/* Socle commun à tous les pays, habillé à l'identité visuelle Logimaths.

   Deux écarts assumés par rapport à css/style.css du site, tous deux imposés
   par le cyrillique :

   1. POLICES. Quicksand et Baloo 2 ne couvrent pas le cyrillique (subsets
      Google : latin, latin-ext, vietnamese, + devanagari pour Baloo). Une
      interface kirghize et russe rendue avec elles tomberait entièrement sur
      la police de secours du système. On les garde en tête de pile pour le
      latin (mot-symbole, exemples anglais) et on ajoute Nunito derrière, qui
      porte cyrillique et cyrillique-ext et reste une ronde humaniste proche.
   2. SURFACES. Le site pose des rgba blanc translucides et les surcharge en
      fin de fichier pour le mode clair. Ici tout passe par des variables qui
      s'inversent, pour qu'aucune surface ne devienne invisible en clair.

   Tout le reste vient du site : palette, --radius 20px, dégradé signature
   rose → bleu ciel, boutons en gélule, sombre par défaut et clair via
   html[data-theme="light"]. */

:root {
  /* ⛔ À NE PAS RETIRER. C'est ce qui rend lisibles les menus déroulants.

     La liste d'options d'un <select> est dessinée par le NAVIGATEUR, dans une
     fenêtre à lui, hors de la page : ni le fond de la page ni une règle
     `option { background: … }` ne l'atteignent — Chrome sous Windows ignore
     purement et simplement la seconde. Sans cette déclaration, il dessine sa
     fenêtre en thème clair, et notre `color: var(--blanc)` y écrit en blanc
     sur blanc : liste vide à l'écran, seule la ligne survolée visible.

     `color-scheme` est le seul mécanisme qui parle à ces widgets-là. Il vaut
     aussi pour les barres de défilement et les sélecteurs de date. */
  color-scheme: dark;

  --encre:      #0D0A25;   /* fond */
  --blanc:      #FFFFFF;   /* texte */
  --indigo:     #241B4F;
  --rose:       #E13280;
  --bleu-ciel:  #357ECF;
  --lavande:    #ADA7CC;

  --juste:      #3ED598;
  --faux:       #FF7A8A;

  --surface:      linear-gradient(135deg, rgba(224, 41, 123, .20) 0%, rgba(46, 116, 194, .16) 100%);
  --surface-bord: rgba(255, 255, 255, .12);
  --surface-plein: rgba(255, 255, 255, .05);

  --radius: 20px;
  --shadow: 0 12px 32px rgba(13, 10, 37, .35);
  --page-bg: radial-gradient(ellipse 1400px 1000px at 50% 30%, #2b2264 0%, #1a1442 45%, #0D0A25 100%);
}

html[data-theme="light"] {
  color-scheme: light;

  --encre:      #FFFFFF;
  --blanc:      #0D0A25;
  --indigo:     #DCD5F2;
  --rose:       #DF2076;
  --bleu-ciel:  #2E74C2;
  --lavande:    #6C6590;

  --juste:      #0E7A55;
  --faux:       #C8253B;

  /* ⚠️ THÈME CLAIR : les surfaces sont BLANCHES, pas teintées.

     Elles portaient le même dégradé rose→bleu que le sombre, à 10 % sur un
     fond déjà très pâle : cartes, blocs et boutons se fondaient dans la page,
     on ne voyait plus où commençait une section. En clair, ce qui sépare
     n'est pas la teinte, c'est la LUMIÈRE — une surface blanche posée sur un
     fond lavande, avec un filet net et une ombre douce. C'est ce que fait le
     site sur ses cartes.

     Le fond de page est donc assombri d'un cran (le blanc pur de la fin du
     dégradé annulait le contraste en bas d'écran) et le filet passe de 14 à
     22 % : en dessous, il disparaissait sur un écran de téléphone en plein
     jour, et c'est exactement la situation d'une salle de classe à Karakol. */
  /* Dégradé de marque, très pâle : le blanc pur faisait des cartes des
     rectangles muets. Il suit la même diagonale rose → bleu que le thème
     sombre, mais à quelques pour cent — assez pour que la surface vive sous
     la lumière, pas assez pour manger la lisibilité du texte noir dessus.
     Contrôlé au rendu, pas au style calculé. */
  --surface:      linear-gradient(135deg, #FFFFFF 0%, #FDF4FA 45%, #F1F5FE 100%);
  --surface-bord: rgba(36, 27, 79, .22);
  --surface-plein: #FFFFFF;

  --shadow: 0 10px 26px rgba(36, 27, 79, .12);
  --page-bg: radial-gradient(ellipse 1400px 1000px at 50% 30%, #EFE9FC 0%, #E4DCF6 45%, #F2EDFB 100%);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--encre);
  background-image: var(--page-bg);
  background-attachment: fixed;
  color: var(--blanc);
  font-family: "Quicksand", "Nunito", system-ui, sans-serif;
  font-weight: 500;
  font-size: 16px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3 {
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 12px;
  color: var(--blanc);
}

:focus-visible {
  outline: 3px solid var(--bleu-ciel);
  outline-offset: 3px;
}

/* ---------- bandeau d'avertissement traduction ---------- */

.bandeau {
  padding: .6rem 1rem;
  background: var(--rose);
  color: #FFFFFF;
  font-size: .85rem;
  font-weight: 700;
  text-align: center;
}

/* ---------- en-tête ---------- */

.entete {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .7rem 1rem;
  /* Même dégradé signature que l'en-tête du site, très atténué. */
  background:
    linear-gradient(120deg, rgba(224, 41, 123, .10) 0%, rgba(46, 116, 194, .08) 100%),
    rgba(13, 10, 37, .85);
  border-bottom: 1px solid var(--surface-bord);
  /* Pas de backdrop-filter : sur un en-tête collant, le flou se recalcule à
     chaque image pendant le défilement et fait saccader les Android modestes,
     qui sont précisément la cible ici. Le fond à 85 % suffit. */
}

html[data-theme="light"] .entete {
  background:
    linear-gradient(120deg, rgba(224, 41, 123, .08) 0%, rgba(46, 116, 194, .06) 100%),
    rgba(255, 255, 255, .88);
}

/* Anneau du logo : mêmes valeurs que .logo-ring / .logo-ring-sm du site. */
.logo-ring {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 6px solid var(--rose);
  box-sizing: border-box;
  flex-shrink: 0;
  overflow: hidden;
}

.logo-ring img { width: 86%; height: 86%; object-fit: contain; }
.logo-ring-sm { width: 44px; height: 44px; border-width: 3px; }

/* Le badge complet (anneau rose + cercle blanc + mot, tout dans le PNG,
   geometrie mesuree sur l'oeuvre) remplace l'ecrin .logo-ring des en-tetes.
   En theme clair, la variante a l'encre prend le relais par `content:` —
   aucun JavaScript a ecrire ni a maintenir. */
.logo-badge-img { width: 46px; height: 46px; display: block; flex: none; }
html[data-theme="light"] .logo-badge-img {
  content: url("../assets/barsbilim-badge-clair.png");
}

.entete-titre { flex: 1; min-width: 0; }

/* Mot-symbole : même construction que .wordmark du site, le second mot en
   rose. Baloo 2 suffit ici, le nom de marque est en caractères latins. */
.marque {
  display: block;
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-weight: 800;
  font-size: 1.35rem;
  letter-spacing: -.01em;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wm-accent { color: var(--rose); }

html[data-theme="light"] .marque { -webkit-text-stroke: .5px currentColor; }

.sous-titre {
  display: block;
  margin-top: 2px;
  font-size: .78rem;
  color: var(--lavande);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Boutons ronds de l'en-tête : valeurs reprises telles quelles de
   .theme-toggle du site (38 px, filet à 20 %, fond à 6 %, survol à 14 %). */
.retour, .theme {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .2);
  background: rgba(255, 255, 255, .06);
  color: var(--blanc);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}

.retour:hover, .theme:hover {
  background: rgba(255, 255, 255, .14);
  transform: translateY(-1px);
}

html[data-theme="light"] .retour,
html[data-theme="light"] .theme {
  border-color: rgba(28, 22, 51, .2);
  background: rgba(28, 22, 51, .06);
}

html[data-theme="light"] .retour:hover,
html[data-theme="light"] .theme:hover { background: rgba(28, 22, 51, .12); }

.theme svg { display: block; }

/* Sélecteur de langue : liste déroulante. Des segments côte à côte
   tomberaient sous les 40 px de large dès la troisième langue, ce qui n'est
   plus tapable ; la liste garde la même taille quel que soit leur nombre. */
.langues {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
}

.langues select {
  height: 38px;
  padding: 0 1.5rem 0 .8rem;
  -webkit-appearance: none;
  appearance: none;
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: 999px;
  background: rgba(255, 255, 255, .06);
  color: var(--blanc);
  font-family: inherit;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .02em;
  cursor: pointer;
}

.langues select:hover { background: rgba(255, 255, 255, .14); }

html[data-theme="light"] .langues select {
  border-color: rgba(28, 22, 51, .2);
  background: rgba(28, 22, 51, .06);
}

html[data-theme="light"] .langues select:hover { background: rgba(28, 22, 51, .12); }

/* La flèche est décorative : elle ne doit jamais intercepter le tap. */
.langues::after {
  content: "▾";
  position: absolute;
  right: .6rem;
  color: var(--lavande);
  font-size: .7rem;
  pointer-events: none;
}

/* Le menu déroulant est dessiné par le système : sans couleur explicite, il
   sort en texte sombre sur fond sombre sur certains Android. */
.langues option {
  background: #1a1442;
  color: #FFFFFF;
}

html[data-theme="light"] .langues option {
  background: #FFFFFF;
  color: #0D0A25;
}

/* ---------- vue ---------- */

.vue {
  flex: 1;
  width: 100%;
  max-width: 44rem;
  margin: 0 auto;
  padding: 1.2rem 1rem 3rem;
}

h1 { font-size: 1.7rem; }
h2 { font-size: 1.2rem; margin: 1.8rem 0 .7rem; }

.chapeau {
  margin: 0 0 1.3rem;
  color: var(--lavande);
  font-size: .95rem;
}

/* ---------- grandes cartes de classe (accueil) ----------
   Reprises de .levels-grid / .level-card du site : même dégradé rose → bleu
   ciel à 34 % / 30 %, filet blanc à 18 %, rayon 20 px, survol qui soulève de
   6 px. La mascotte est posée sur le bord de la dernière carte, comme le
   post-bac sur logimaths.fr. */

.vue-large { max-width: 60rem; }

.entete-accueil { text-align: center; margin-bottom: 34px; }

.surtitre {
  font-weight: 600;
  font-size: .8rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bleu-ciel);
  margin-bottom: 10px;
}

.entete-accueil h1 { font-size: 2.1rem; }

.entete-accueil .chapeau {
  max-width: 620px;
  margin: 0 auto;
  font-size: 1.05rem;
}

/* La mascotte est perchée sur le coin de la grille, jamais sur une carte : le
   professeur peut créer autant de classes qu'il veut, donc la dernière carte
   n'est plus forcément en haut à droite. */
.levels-zone { position: relative; }

/* 3 par rangée, puis 2 sur téléphone. Le nombre de classes est libre : la
   grille empile les rangées toute seule, rien à changer quand une 4e arrive. */
.levels-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
}

.level-card {
  position: relative;
  display: block;
  width: 100%;
  padding: 36px 28px;
  text-align: center;
  background: linear-gradient(135deg, rgba(224, 41, 123, .34) 0%, rgba(46, 116, 194, .30) 100%);
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  color: var(--blanc);
  font-family: inherit;
  cursor: pointer;
  transition: transform .2s ease, border-color .15s ease;
}

.level-card:hover { transform: translateY(-6px); border-color: var(--rose); }

/* ⚠️ Les cartes de classes gardent le dégradé de marque : c'est leur
   identité, et c'est la seule surface colorée de l'accueil. Mais en clair
   il doit être DENSE — à 34 % sur un fond lavande, le texte blanc passait
   mal et la carte se confondait avec la page. */
html[data-theme="light"] .level-card {
  background: linear-gradient(135deg, rgba(214, 46, 120, .92) 0%, rgba(46, 116, 194, .88) 100%);
  border-color: rgba(36, 27, 79, .10);
  color: #FFFFFF;
}

html[data-theme="light"] .level-card h3,
html[data-theme="light"] .level-card p,
html[data-theme="light"] .level-note { color: #FFFFFF; }
html[data-theme="light"] .level-card:hover { box-shadow: 0 18px 36px rgba(36, 27, 79, .18); }

.level-icon { font-size: 2.5rem; margin-bottom: 12px; line-height: 1; }

.level-card h3 {
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-size: 1.3rem;
  font-weight: 700;
  margin: 0 0 12px;
}

.level-card p { color: var(--lavande); margin: 0; }

.level-note {
  display: inline-block;
  margin-top: 16px;
  padding: .25rem .75rem;
  border-radius: 999px;
  background: var(--rose);
  color: #FFFFFF;
  font-size: .78rem;
  font-weight: 700;
}

/* La carte qui porte la mascotte doit la laisser déborder vers le haut. */
.level-card-mascotte { overflow: visible; }

/* La mascotte déborde vers le bas de « hauteur − |top| », et ce débordement
   doit rester au-dessus de l'icône de la classe (36 px sous le bord de la
   carte, 22 px sur téléphone).
   ⚠️ |top| est borné par le HAUT : l'en-tête collant est à z-index 5, et
   au-delà de |top| ≈ 157 px la tête passe derrière lui et se fait couper au
   chargement (mesuré, pas estimé).
   ⚠️ Le repère de `top` est la CARTE, pas .levels-zone : les deux ont le même
   haut sur grand écran, ce qui masque la différence — sur téléphone la carte
   descend, d'où la valeur propre au téléphone plus bas.
   ⛔⛔ CES VALEURS SONT LIÉES À L'IMAGE, via sa PROPORTION. L'actuelle fait
   1,807 de haut pour 1 de large, soit 235 px à 130 px de large, et déborde
   donc de 90 px dans la carte : il FAUT le `padding-top` ci-dessous pour
   descendre l'icône sous ses pattes. Erreur déjà commise le 2026-08-25 —
   changer `mascotte` dans config.json sans refaire ce calcul a remis l'icône
   sous le léopard, recouverte de 58 px. Changer l'image = refaire les deux. */
.level-card-mascot {
  position: absolute;
  top: -145px;
  right: 20px;
  width: 130px;
  pointer-events: none;
  /* Les cartes qui suivent dans le document se peindraient par-dessus et
     couperaient le chat à mi-corps : ce plan explicite le fait passer devant. */
  z-index: 2;
}

/* Le contenu de la carte porteuse démarre sous les pattes du léopard, au lieu
   des 36 px des autres cartes. La rangée grandit d'autant : c'est voulu, la
   mascotte occupe visuellement cette place. */
.level-card-mascotte { padding-top: 104px; }

/* Téléphone : deux par rangée. Le texte de l'en-tête occupe alors toute la
   largeur, donc la mascotte n'a plus de vide à droite où se poser : on lui
   réserve sa hauteur au-dessus de la grille. */
@media (max-width: 860px) {
  .levels-grid { grid-template-columns: repeat(2, 1fr); gap: 14px; }
  /* Doit rester supérieur au |top| de la mascotte ci-dessous, sinon son
     débordement mord sur le texte de l'en-tête. */
  .levels-zone { margin-top: 200px; }
  /* Ici l'en-tête collant n'est plus une contrainte (la grille est repoussée) :
     on remonte davantage la mascotte, ce qui réduit son débordement et permet
     de moins décaler le contenu de la carte, précieux sur petit écran. */
  .level-card-mascot { top: -190px; }
  .level-card { padding: 22px 14px; }
  /* ⛔ APRÈS `.level-card` : à spécificité égale c'est la dernière règle qui
     gagne, et le raccourci `padding` ci-dessus remet padding-top à 22 px.
     Placée avant, cette ligne était inerte — l'icône restait recouverte. */
  .level-card-mascotte { padding-top: 62px; }
  .level-icon { font-size: 2rem; }
  .level-card h3 { font-size: 1.05rem; }
  .level-card p { font-size: .84rem; }
  .level-note { font-size: .72rem; padding: .2rem .55rem; }
  .entete-accueil h1 { font-size: 1.7rem; }
}

/* En dessous de 360 px, deux colonnes deviennent illisibles : on repasse à une. */
@media (max-width: 360px) {
  .levels-grid { grid-template-columns: 1fr; }
}

/* ---------- cartes cliquables ---------- */

.liste { display: grid; gap: .8rem; }

.carte {
  display: flex;
  align-items: center;
  gap: .9rem;
  width: 100%;
  padding: 1rem 1.1rem;
  text-align: left;
  background: var(--surface);
  border: 2px solid var(--surface-bord);
  border-radius: var(--radius);
  color: var(--blanc);
  font-family: inherit;
  font-size: 1rem;
  cursor: pointer;
  transition: transform .15s ease, border-color .15s ease;
}

.carte:hover { transform: translateY(-4px); border-color: var(--rose); }
.carte:active { transform: translateY(-1px); }

.carte-corps { flex: 1; min-width: 0; }

/* Titre et note sont des <span> : sans display block, ils se collent sur une
   seule ligne (« Memory des pairesRetrouve chaque mot… »). Vu sur le hub des
   jeux, vrai pour toutes les cartes. */
.carte-titre {
  display: block;
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-weight: 700;
  font-size: 1.1rem;
  margin-bottom: .2rem;
}

.carte-note { display: block; font-size: .84rem; color: var(--lavande); }

.pastille {
  flex: none;
  min-width: 2.7rem;
  padding: .25rem .55rem;
  border-radius: 999px;
  background: var(--rose);
  color: #FFFFFF;
  font-size: .78rem;
  font-weight: 700;
  text-align: center;
}

.chevron { flex: none; color: var(--lavande); font-size: 1.2rem; }

/* ---------- contenu d'une leçon ---------- */

.bloc {
  padding: 1.1rem 1.2rem;
  margin: 0 0 .9rem;
  background: var(--surface);
  border: 2px solid var(--surface-bord);
  border-radius: var(--radius);
}

.bloc p:first-child { margin-top: 0; }
.bloc p:last-child  { margin-bottom: 0; }

.bloc-piege {
  background: linear-gradient(160deg, rgba(255, 0, 0, .16) 0%, rgba(224, 41, 123, .16) 100%);
  border-color: var(--rose);
}

.bloc-etiquette {
  display: block;
  margin-bottom: .55rem;
  font-family: "Quicksand", "Nunito", system-ui, sans-serif;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bleu-ciel);
}

.bloc-piege .bloc-etiquette { color: var(--rose); }

ul.puces { margin: .4rem 0; padding-left: 1.2rem; }
ul.puces li { margin: .35rem 0; }
ul.puces li::marker { color: var(--rose); }

.mot-en { font-weight: 700; }
.mot-tr { color: var(--lavande); }

table.vocab {
  width: 100%;
  border-collapse: collapse;
  font-size: .95rem;
}

table.vocab th,
table.vocab td {
  padding: .5rem .5rem;
  text-align: left;
  border-bottom: 1px solid var(--surface-bord);
}

table.vocab th {
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--lavande);
}

table.vocab tr:last-child td { border-bottom: 0; }

/* ---------- QCM ---------- */

.progression {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin-bottom: 1.1rem;
  font-size: .82rem;
  font-weight: 600;
  color: var(--lavande);
}

.jauge {
  flex: 1;
  height: 8px;
  border-radius: 999px;
  background: var(--surface-bord);
  overflow: hidden;
}

.jauge span {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--rose) 0%, var(--bleu-ciel) 100%);
  transition: width .25s ease;
}

.enonce {
  margin: 0 0 1.1rem;
  padding: 1.2rem;
  background: var(--surface);
  border: 2px solid var(--surface-bord);
  border-radius: var(--radius);
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-size: 1.3rem;
  font-weight: 600;
}

.consigne {
  margin: 0 0 .8rem;
  color: var(--lavande);
  font-size: .92rem;
}

/* ---------- textes à trou ---------- */

.trous {
  margin: 0 0 1rem;
  padding: 1.2rem;
  background: var(--surface);
  border: 2px solid var(--surface-bord);
  border-radius: var(--radius);
  font-size: 1.1rem;
  /* Interligne généreux : les champs dépassent de la ligne de texte, sans quoi
     les lignes se chevauchent dès qu'il y a plusieurs trous. */
  line-height: 2.4;
}

.trou {
  width: 7rem;
  padding: .2rem .5rem;
  margin: 0 .15rem;
  border: 0;
  border-bottom: 2px solid var(--rose);
  border-radius: 6px 6px 0 0;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: .95rem;
  font-weight: 650;
  text-align: center;
}

.trou:focus { outline: 2px solid var(--bleu-ciel); outline-offset: 1px; }

/* Corrigé : la bonne réponse remplace la saisie fausse. Corriger sans montrer
   la réponse n'apprend rien. */
.trou-juste { border-bottom-color: var(--juste); color: var(--juste); }
.trou-faux  { border-bottom-color: var(--faux);  color: var(--faux); }

/* Safari grise les champs désactivés : on garde le contraste du corrigé. */
.trou:disabled { opacity: 1; -webkit-text-fill-color: currentColor; }

.choix { display: grid; gap: .6rem; }

.choix-ligne { display: flex; flex-wrap: wrap; }

.choix button {
  padding: .9rem 1.1rem;
  text-align: left;
  background: var(--surface-plein);
  border: 2px solid var(--surface-bord);
  border-radius: var(--radius);
  color: var(--blanc);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color .15s ease, transform .15s ease;
}

/* Propositions courtes : côte à côte, CENTRÉES, à parts égales. `min-width`
   fait replier proprement quand quatre choix ne tiennent pas sur un petit
   écran — jamais de texte coupé pour gagner une ligne.
   ⚠️ APRÈS `.choix button` : à spécificité égale c'est la dernière règle qui
   gagne, et le `text-align: left` général écrasait le centrage. Vérifié au
   rendu, pas à la règle. */
.choix-ligne button {
  flex: 1 1 0;
  min-width: 5.2rem;
  text-align: center;
}

.choix button:hover:not(:disabled) { border-color: var(--rose); transform: translateY(-2px); }
.choix button:disabled { cursor: default; }

.choix button.juste {
  border-color: var(--juste);
  color: var(--juste);
  font-weight: 700;
}

.choix button.faux {
  border-color: var(--faux);
  color: var(--faux);
}

.retour-reponse {
  margin: 1.1rem 0 0;
  padding: 1rem 1.1rem;
  border-radius: var(--radius);
  background: var(--surface);
  border: 2px solid var(--surface-bord);
  font-size: .95rem;
}

.retour-reponse b { color: var(--bleu-ciel); }

/* Les quatre cases. Un miroir, pas une note : discrètes, jamais alarmantes.
   « fragile » et « incompris » ne sont pas rouges — ce ne sont pas des fautes
   supplémentaires, ce sont des indications de révision. */
.cas {
  display: inline-block;
  padding: .1rem .5rem;
  margin-left: .3rem;
  border-radius: 999px;
  font-size: .74rem;
  font-weight: 700;
}

.cas-maitrise   { background: var(--juste); color: var(--encre); }
.cas-fragile    { border: 1px solid var(--bleu-ciel); color: var(--bleu-ciel); }
.cas-etourderie { border: 1px solid var(--lavande); color: var(--lavande); }
.cas-incompris  { border: 1px solid var(--rose); color: var(--rose); }

/* ---------- boutons ---------- */

.bouton {
  display: block;
  width: 100%;
  margin-top: 1.1rem;
  padding: .95rem 1.75rem;
  background: var(--rose);
  border: 2px solid var(--rose);
  border-radius: 999px;
  color: #FFFFFF;
  font-family: "Quicksand", "Nunito", system-ui, sans-serif;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(224, 41, 123, .4);
  transition: transform .15s ease, box-shadow .15s ease;
}

.bouton:hover { transform: translateY(-2px); box-shadow: 0 12px 26px rgba(224, 41, 123, .48); }
.bouton:active { transform: translateY(0); }

.bouton-doux {
  background: transparent;
  border-color: var(--bleu-ciel);
  color: var(--bleu-ciel);
  box-shadow: none;
}

.bouton-doux:hover { box-shadow: 0 8px 20px rgba(46, 116, 194, .3); }

/* ---------- résultat ---------- */

.score {
  padding: 1.8rem 1rem;
  text-align: center;
  background: var(--surface);
  border: 2px solid var(--rose);
  border-radius: var(--radius);
  box-shadow: 0 12px 40px rgba(224, 41, 123, .18);
}

.score-chiffre {
  display: block;
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-size: 3rem;
  font-weight: 800;
  color: var(--rose);
  letter-spacing: -.03em;
  line-height: 1;
  margin-bottom: .4rem;
}

/* ---------- bandeau de mise à jour ---------- */

/* Posé par js/version.js quand une version plus récente est en ligne. En bas :
   en haut il recouvrirait l'en-tête collant, et sur un téléphone le pouce
   atteint le bas. ⚠️ Il ne recharge JAMAIS tout seul — un rechargement surpris
   au milieu d'une série effacerait le travail en cours. */
.maj-bandeau {
  position: fixed;
  left: 50%;
  bottom: calc(1rem + env(safe-area-inset-bottom));
  z-index: 200;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: .6rem;
  width: min(30rem, calc(100vw - 1.6rem));
  padding: .7rem .9rem;
  background: var(--indigo);
  border: 1px solid var(--rose);
  border-radius: 16px;
  box-shadow: 0 14px 34px rgba(13, 10, 37, .45);
  color: var(--blanc);
  font-size: .9rem;
  font-weight: 600;
}

html[data-theme="light"] .maj-bandeau {
  background: #FFFFFF;
  box-shadow: 0 14px 34px rgba(36, 27, 79, .22);
}

.maj-bandeau span { flex: 1; min-width: 0; }

.maj-bouton {
  flex: none;
  padding: .4rem .9rem;
  border: 0;
  border-radius: 999px;
  background: var(--rose);
  color: #FFFFFF;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

.maj-plus-tard {
  flex: none;
  padding: .4rem .3rem;
  border: 0;
  background: none;
  color: var(--lavande);
  font: inherit;
  cursor: pointer;
}

/* En application installée, la barre du bas occupe déjà ce coin. */
@media (display-mode: standalone) {
  .maj-bandeau { bottom: calc(4.8rem + env(safe-area-inset-bottom)); }
}

html.lm-standalone .maj-bandeau { bottom: calc(4.8rem + env(safe-area-inset-bottom)); }

/* ⚠️ Sur téléphone, l'en-tête porte désormais sept éléments : retour, logo,
   titre, langue, badge, thème, hamburger. À 430 px le mot-symbole tombait à
   « Cours … ». On resserre les écarts et la taille du titre plutôt que de
   retirer une commande — chacune sert. Mesuré à 430 px : le titre entier
   tient. */
@media (max-width: 480px) {
  .entete { gap: .45rem; padding: .6rem .7rem; }
  .marque { font-size: 1.08rem; }
  .sous-titre { font-size: .7rem; }
  .logo-ring-sm { width: 38px; height: 38px; }
}

/* ---------- filet dégradé sous l'en-tête ---------- */

/* Repris de `.site-header::after` de logimaths.fr : un trait de 3 px en
   dégradé rose → bleu ciel, qui remplace le filet translucide d'un pixel. Il
   sépare franchement l'en-tête du corps de page, ce qu'un filet à 12 % ne
   faisait pas — surtout en thème clair, où il disparaissait.

   ⚠️ `.entete` est déjà positionné (collant), il porte donc l'ancrage sans
   qu'on ait à y toucher. */
.entete::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--rose), var(--bleu-ciel));
  opacity: .75;
}

/* ---------- badge « connecté » ---------- */

/* Avatar à l'initiale + point vert, convention que tout le monde connaît
   (YouTube, Google). Le mot dit où le clic emmène — « Mon compte » — parce
   que l'avatar et le point disent DÉJÀ que la session est ouverte. Repris de
   logimaths.fr, à l'identique. */
.lm-connecte {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  padding: 4px 12px 4px 4px;
  border-radius: 999px;
  border: 1px solid rgba(34, 197, 94, .42);
  background: rgba(34, 197, 94, .12);
  color: var(--blanc);
  font-family: inherit;
  font-size: .8rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.lm-connecte:hover {
  background: rgba(34, 197, 94, .2);
  border-color: rgba(34, 197, 94, .65);
}

.lm-avatar {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--rose), var(--bleu-ciel));
  color: #FFFFFF;
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-weight: 800;
  font-size: .85rem;
}

/* Le point de statut bat doucement. ⚠️ Seules l'échelle et la couleur bougent,
   sur un élément de 9 px : rien à recalculer par image ailleurs dans la page. */
.lm-dot {
  position: absolute;
  right: -2px;
  bottom: -2px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #22C55E;
  box-shadow: 0 0 0 2px var(--encre), 0 0 5px 1px rgba(34, 197, 94, .6);
  animation: lmDotPulse 1.5s ease-in-out infinite;
}

@keyframes lmDotPulse {
  0%, 100% {
    background: #22C55E;
    transform: scale(1);
    box-shadow: 0 0 0 2px var(--encre), 0 0 4px 1px rgba(34, 197, 94, .55);
  }
  50% {
    background: #57FF8F;
    transform: scale(1.22);
    box-shadow: 0 0 0 2px var(--encre), 0 0 13px 4px rgba(34, 197, 94, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .lm-dot { animation: none; }
}

/* Sous 470 px, l'avatar seul : l'initiale et le point vert disent l'essentiel,
   et la place manque à côté du titre, du sélecteur de langue et du hamburger.
   Même seuil que sur le site. */
@media (max-width: 469px) {
  .lm-connecte {
    padding: 3px;
    background: none;
    border: 0;
  }
  .lm-connecte .lm-txt { display: none; }
}

/* ---------- navigation : hamburger et barre du bas ---------- */

/* Le bouton et le panneau sont posés par js/menu.js, appelé par les deux
   coquilles. ⚠️ Contrairement à logimaths.fr, le hamburger s'affiche à TOUTES
   LES LARGEURS : le site a une barre de navigation sur grand écran, pas nous. */

.lm-burger {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  flex: none;
  width: 38px;
  height: 38px;
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: 50%;
  background: rgba(255, 255, 255, .06);
  color: var(--blanc);
  cursor: pointer;
  transition: background .18s ease;
}

html[data-theme="light"] .lm-burger {
  border-color: rgba(36, 27, 79, .16);
  background: rgba(36, 27, 79, .05);
}

.lm-burger:hover { background: rgba(255, 255, 255, .14); }
html[data-theme="light"] .lm-burger:hover { background: rgba(36, 27, 79, .1); }
.lm-burger.is-open { background: rgba(224, 41, 123, .22); }

.lm-burger-bar {
  display: block;
  width: 17px;
  height: 2.2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform .26s cubic-bezier(.22, 1, .36, 1), opacity .16s ease;
}

.lm-burger.is-open .lm-burger-bar:nth-child(1) { transform: translateY(7.2px) rotate(45deg); }
.lm-burger.is-open .lm-burger-bar:nth-child(2) { opacity: 0; }
.lm-burger.is-open .lm-burger-bar:nth-child(3) { transform: translateY(-7.2px) rotate(-45deg); }

/* ⚠️ z-index 4, JUSTE SOUS l'en-tête (5) et non au-dessus : `.entete` est
   collant, donc positionné, donc il crée un contexte d'empilement — le panneau
   qu'il contient ne peut pas dépasser 5 vu de l'extérieur, quel que soit son
   z-index. Un voile à 98 serait passé PAR-DESSUS le menu qu'il accompagne.
   À 4, il assombrit la page et laisse l'en-tête et son panneau éclairés. */
.lm-menu-voile {
  position: fixed;
  inset: 0;
  z-index: 4;
  background: rgba(13, 10, 37, .55);
  opacity: 0;
  visibility: hidden;
  transition: opacity .24s ease, visibility .24s;
}

.lm-menu-voile.is-open { opacity: 1; visibility: visible; }

/* Panneau : on le garde dans le flux et on anime l'opacité et la translation —
   `display: none` ne se transitionne pas.

   ⛔ LE PANNEAU DOIT DÉFILER. Il est ancré à un en-tête COLLANT : ce qui
   dépasse en bas n'est PAS rattrapable en faisant défiler la page, puisque
   l'en-tête reste épinglé et le panneau avec lui (mesuré : 495 px de
   défilement ne déplaçaient le bas du panneau que de 13 px). Sans
   `max-height` + `overflow-y`, les dernières entrées étaient donc
   définitivement hors d'atteinte sur un écran bas — à 620 px de haut, 9 des
   13 entrées du professeur seulement, et « Se déconnecter » en faisait
   partie. Une entrée fait 60 px : 13 entrées réclament 874 px de fenêtre,
   qu'un portable 1366 × 768 n'a pas.
   ⚠️ `dvh` et non `vh` : sur mobile la barre d'adresse fait varier la hauteur
   réelle, et `vh` fige la valeur barre rétractée — le bas resterait coupé.
   `overscroll-behavior: contain` empêche la page de partir derrière le menu
   une fois arrivé en bout de course.
   ⚠️ La marge de 7rem tient compte du BANDEAU de relecture, qui pousse
   l'en-tête vers le bas tant qu'une traduction n'est pas validée : le haut du
   panneau descend alors de 67 à 94 px. Calculée sur le seul en-tête, la
   soustraction laissait 6 px du panneau hors écran. */
.lm-menu {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 99;
  width: min(21rem, calc(100vw - 1.2rem));
  max-height: calc(100dvh - 7rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: .7rem;
  background: linear-gradient(180deg, var(--indigo) 0%, var(--encre) 100%);
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 0 0 22px 22px;
  box-shadow: 0 22px 44px rgba(0, 0, 0, .5);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  transition: opacity .2s ease, transform .28s cubic-bezier(.22, 1, .36, 1), visibility .28s;
}

html[data-theme="light"] .lm-menu {
  background: linear-gradient(180deg, #FFFFFF 0%, #F4F1FB 100%);
  border-color: rgba(36, 27, 79, .12);
  box-shadow: 0 22px 44px rgba(36, 27, 79, .22);
}

.lm-menu.is-open { opacity: 1; visibility: visible; transform: none; }

.lm-menu button {
  display: flex;
  align-items: center;
  gap: .7rem;
  width: 100%;
  margin: 3px 0;
  padding: .6rem .7rem;
  border: 0;
  border-radius: 14px;
  background: rgba(255, 255, 255, .05);
  color: var(--blanc);
  font-family: inherit;
  font-size: .98rem;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
  opacity: 0;
  transform: translateY(-6px);
}

html[data-theme="light"] .lm-menu button { background: rgba(36, 27, 79, .05); }

.lm-menu.is-open button {
  animation: lmMenuEntree .3s cubic-bezier(.22, 1, .36, 1) forwards;
  animation-delay: calc(var(--i, 0) * 32ms);
}

@keyframes lmMenuEntree { to { transform: none; opacity: 1; } }

.lm-menu button[aria-current="page"] {
  background: linear-gradient(135deg, rgba(224, 41, 123, .28), rgba(46, 116, 194, .22));
  border: 1px solid rgba(224, 41, 123, .4);
}

/* Entrée d'appoint (déconnexion, lien vers un autre espace) : même ligne, mais
   détachée du bloc principal par un filet. */
.lm-menu button.lm-menu-appoint {
  margin-top: .6rem;
  border-top: 1px solid var(--surface-bord);
  border-radius: 0 0 14px 14px;
  background: none;
  color: var(--lavande);
  font-weight: 600;
}

.lm-menu-ic {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 11px;
  background: linear-gradient(135deg, rgba(224, 41, 123, .28), rgba(46, 116, 194, .24));
}

.lm-menu-ic svg { width: 21px; height: 21px; display: block; }
.lm-menu-ic svg .p { fill: var(--rose); }
/* Opacité, et non une couleur plus claire : en thème clair --lavande est un
   violet sombre, et une grande forme secondaire pleine mangeait la
   silhouette de l'icône. Vérifié à l'écran, pas au style calculé. */
.lm-menu-ic svg .s { fill: var(--lavande); opacity: .5; }
.lm-menu-lbl { flex: 1; min-width: 0; }
.lm-menu-chevron { color: var(--lavande); font-size: 1.2rem; line-height: 1; }

/* --- barre du bas, application installée seulement --- */

/* Elle ne s'affiche QUE dans l'app : sur le web, le hamburger fait le travail
   et une barre fixe mangerait de la hauteur pour rien. */
.lm-tabbar {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 100;
  background: var(--encre);
  border-top: 1px solid var(--surface-bord);
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: 0 -2px 14px rgba(0, 0, 0, .22);
}

.lm-tabbar button {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px 2px 7px;
  border: 0;
  background: none;
  color: var(--lavande);
  font-family: inherit;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.lm-tabbar button span:last-child {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lm-tabbar .lm-ic {
  display: grid;
  place-items: center;
  width: 56px;
  height: 30px;
  border-radius: 999px;
}

.lm-tabbar svg { width: 25px; height: 25px; display: block; }
.lm-tabbar svg .p { fill: var(--rose); transition: fill .25s ease; }
.lm-tabbar svg .s { fill: var(--lavande); transition: fill .25s ease; }

/* Onglet actif : la pastille « verre rose » du site. ⚠️ Sans backdrop-filter —
   c'est l'effet le plus coûteux sur WebKit, et il l'est même au repos. */
.lm-tabbar button[aria-current="page"] { color: var(--blanc); font-weight: 700; }

.lm-tabbar button[aria-current="page"] .lm-ic {
  background: linear-gradient(165deg, rgba(255, 255, 255, .30) 0%, rgba(224, 41, 123, .62) 45%, rgba(178, 24, 93, .58) 100%);
  border: 1px solid rgba(255, 255, 255, .34);
  box-shadow: inset 0 1px 2px rgba(255, 255, 255, .5),
              inset 0 -2px 5px rgba(13, 10, 37, .22),
              0 3px 10px rgba(224, 41, 123, .38);
}

html[data-theme="light"] .lm-tabbar button[aria-current="page"] .lm-ic {
  background: linear-gradient(165deg, rgba(255, 255, 255, .42) 0%, rgba(224, 41, 123, .78) 45%, rgba(178, 24, 93, .72) 100%);
  box-shadow: inset 0 1px 2px rgba(255, 255, 255, .6),
              inset 0 -2px 5px rgba(36, 27, 79, .14),
              0 3px 10px rgba(224, 41, 123, .28);
}

.lm-tabbar button[aria-current="page"] svg .p { fill: #FFFFFF; }
.lm-tabbar button[aria-current="page"] svg .s { fill: rgba(255, 255, 255, .5); }

@media (prefers-reduced-motion: no-preference) {
  .lm-tabbar svg { transition: transform .38s cubic-bezier(.33, 0, .15, 1); }
  .lm-tabbar button[aria-current="page"] svg { transform: scale(1.18); }
}

/* En application : la barre du bas remplace le hamburger, et le pied de page
   se dégage de dessous elle. */
/* ⚠️ Le dégagement se pose sur le CORPS, pas sur le pied : rembourrer le pied
   agrandit sa boîte VERS LE BAS, donc son propre bas reste sous la barre. Sur
   le corps, c'est tout le document qui s'arrête au-dessus. Mesuré : avec le
   rembourrage sur `.pied`, le bas du pied restait sous la barre fixe. */
@media (display-mode: standalone) {
  .lm-tabbar { display: flex; }
  .lm-burger, .lm-menu { display: none; }
  body { padding-bottom: calc(4.4rem + env(safe-area-inset-bottom)); }
}

html.lm-standalone .lm-tabbar { display: flex; }
html.lm-standalone .lm-burger,
html.lm-standalone .lm-menu { display: none; }
html.lm-standalone body { padding-bottom: calc(4.4rem + env(safe-area-inset-bottom)); }

@media (prefers-reduced-motion: reduce) {
  .lm-menu, .lm-menu-voile, .lm-burger-bar { transition: none; }
  .lm-menu.is-open button { animation: none; transform: none; opacity: 1; }
}

/* ---------- séries d'automatismes ---------- */

/* Rappel : AUCUN chronomètre à l'écran, ni ici ni ailleurs pendant la série.
   Le score courant, oui — il ne mesure pas le temps. */
.serie-score {
  font-weight: 800;
  color: var(--blanc);
  white-space: nowrap;
}

/* Le temps n'apparaît qu'au bilan, une fois la série finie. */
.serie-chiffres {
  display: flex;
  justify-content: center;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .5rem 1.2rem;
  margin-top: .9rem;
  font-size: .88rem;
  color: var(--lavande);
}

.serie-chiffres span:first-child {
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--bleu-ciel);
}

.serie-stop { display: block; margin: 1.4rem auto 0; }

/* Erreurs rendues à la fin, toutes ensemble. */
.rappel {
  margin-top: .8rem;
  padding: 1rem 1.1rem;
  background: var(--surface);
  border: 2px solid var(--surface-bord);
  border-radius: var(--radius);
}

.rappel-enonce { margin: 0 0 .5rem; font-weight: 700; }

.rappel-ligne { margin: .25rem 0; font-size: .92rem; }

.rappel-ligne b { color: var(--lavande); font-weight: 700; }

.rappel-attendu { color: var(--juste); }

.rappel-attendu b { color: var(--juste); }

/* ---------- ligne de compte (pied de l'accueil) ---------- */

/* Les deux entraînements côte à côte : `.bouton` est en pleine largeur par
   défaut, ce qui les empilerait tout seuls sur deux lignes même sur un écran
   large. */
.compte-ligne-entrainements .bouton {
  width: auto;
  margin-top: 0;
}

.compte-ligne {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: 2rem;
  font-size: .9rem;
  color: var(--lavande);
}

.lien {
  padding: 0;
  border: 0;
  background: none;
  color: var(--rose);
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}

/* ---------- champs de formulaire (connexion élève) ---------- */

/* 26rem et pas 22 : « logimathofficiel@gmail.com » debordait du champ de
   connexion admin (constate a l'ecran, 2026-08-26). */
.champ { display: block; max-width: 26rem; margin: 0 auto .9rem; }

.champ span {
  display: block;
  margin-bottom: .3rem;
  font-size: .82rem;
  font-weight: 600;
  color: var(--lavande);
}

.champ input {
  width: 100%;
  padding: .8rem .9rem;
  border: 1px solid var(--surface-bord);
  border-radius: 12px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: 1rem;
}

html[data-theme="light"] .champ input {
  background: #FFFFFF;
  border-color: rgba(36, 27, 79, .25);
}

form .bouton { max-width: 26rem; margin-left: auto; margin-right: auto; }

/* ---------- radar de compétences et courbe ----------
   Aucune couleur figée : tout suit les variables, donc les deux thèmes aussi.
   Les remplissages sont translucides pour que les deux tracés superposés
   restent lisibles l'un à travers l'autre. */

.radar, .courbe {
  display: block;
  width: 100%;
  max-width: 26rem;
  margin: 0 auto;
  overflow: visible;
}

.courbe { max-width: 34rem; }

.rad-grille {
  fill: none;
  stroke: var(--surface-bord);
  stroke-width: 1;
}

.rad-rayon { stroke: var(--surface-bord); stroke-width: 1; }
.rad-rayon-vide { stroke-dasharray: 3 4; }

.rad-nom {
  fill: var(--lavande);
  font-family: "Quicksand", "Nunito", system-ui, sans-serif;
  font-size: 9.5px;
  font-weight: 700;
}

/* Un axe sans donnée s'efface, il ne se dessine pas à zéro. */
.rad-nom-vide { opacity: .45; font-style: italic; }

.rad-echelle {
  fill: var(--lavande);
  font-size: 8px;
  text-anchor: middle;
  opacity: .7;
}

.rad-actuel {
  fill: var(--rose);
  fill-opacity: .28;
  stroke: var(--rose);
  stroke-width: 2;
  stroke-linejoin: round;
}

.rad-actuel-pt { fill: var(--rose); }

/* Halo pulsant du tracé mesuré, repris de la matrice de compétences du site.
   ⚠️ Plus FIN que là-bas : 4 unités contre 7, qui empâtaient la figure au
   point de fermer les angles aigus du polygone. Le tracé porte 2.

   SEULE l'opacité s'anime : le compositeur la gère seul, donc aucun travail
   par image et aucun repeint de forme. `will-change` est posé une fois pour
   toutes — le poser pendant l'animation recréerait la couche à chaque cycle. */
.rad-halo {
  fill: none;
  stroke: var(--rose);
  stroke-width: 4;
  stroke-linejoin: round;
  opacity: 0;
  will-change: opacity;
}

.rad-halo-pt { fill: none; }

/* L'animation ne part qu'au premier passage à l'écran : la classe est posée
   par Graphes.anime(), jamais au chargement. */
.radar.bat .rad-halo { animation: radHalo 1.6s ease-in-out infinite; }

@keyframes radHalo { 0%, 100% { opacity: 0; } 50% { opacity: .5; } }

@media (prefers-reduced-motion: reduce) {
  .radar.bat .rad-halo { animation: none; }
}

.rad-debut {
  fill: var(--bleu-ciel);
  fill-opacity: .16;
  stroke: var(--bleu-ciel);
  stroke-width: 2;
  stroke-dasharray: 4 3;
  stroke-linejoin: round;
}

.rad-debut-pt { fill: var(--bleu-ciel); }

.crb-grille { stroke: var(--surface-bord); stroke-width: 1; }

.crb-echelle, .crb-date {
  fill: var(--lavande);
  font-family: "Quicksand", "Nunito", system-ui, sans-serif;
  font-size: 10px;
}

.crb-ligne {
  fill: none;
  stroke: var(--rose);
  stroke-width: 2.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.crb-qcm { fill: var(--rose); }
.crb-note { fill: var(--bleu-ciel); }

.crb-reference {
  stroke: var(--bleu-ciel);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
}

.crb-reference-nom {
  fill: var(--bleu-ciel);
  font-family: "Quicksand", "Nunito", system-ui, sans-serif;
  font-size: 9px;
  font-weight: 700;
}

/* ---------- Coach Luna ----------
   ⛔ TEXTE UNIQUEMENT, jamais d'avatar ni d'illustration : règle posée par le
   propriétaire et refusée quatre fois sur logimaths.fr. L'étiquette suffit à
   identifier la voix. */
.luna {
  padding: 1.1rem 1.2rem;
  margin: 1.2rem 0;
  background: var(--surface);
  border: 2px solid var(--bleu-ciel);
  border-radius: var(--radius);
}

.luna-nom {
  display: block;
  margin-bottom: .5rem;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bleu-ciel);
}

.luna p { margin: .4rem 0; }
.luna p:first-of-type { margin-top: 0; }
.luna p:last-child { margin-bottom: 0; }

/* Bloc d'observations du professeur : factuel, sans persona. */
.observations { margin: 1.2rem 0; }

.observations li { margin: .4rem 0; }
.observations li::marker { color: var(--bleu-ciel); }

.stat-grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: .7rem;
  margin: 1rem 0;
}

.stat {
  padding: .8rem;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--surface-bord);
  border-radius: 12px;
}

.stat b {
  display: block;
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-size: 1.5rem;
  color: var(--rose);
  line-height: 1.1;
}

.stat span { font-size: .76rem; color: var(--lavande); }

.rang {
  flex: none;
  min-width: 2.2rem;
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-size: 1.1rem;
  font-weight: 800;
  color: var(--lavande);
  text-align: center;
}

.ecart-plus { color: var(--juste); font-weight: 700; }
.ecart-moins { color: var(--faux); font-weight: 700; }

/* Légende, en accord avec les formes du graphique. */
.legende {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: .7rem;
  font-size: .8rem;
  color: var(--lavande);
}

.legende span::before {
  content: "";
  display: inline-block;
  width: .7rem;
  height: .7rem;
  margin-right: .35rem;
  border-radius: 50%;
  vertical-align: -1px;
}

.legende .lg-actuel::before { background: var(--rose); }
.legende .lg-debut::before  { background: var(--bleu-ciel); }
.legende .lg-note::before   { background: var(--bleu-ciel); border-radius: 2px; }

/* Tableau de secours sous le radar : un graphique n'est pas lisible par un
   lecteur d'écran, et certains chiffres se lisent mieux en clair. */
.chiffres {
  width: 100%;
  max-width: 26rem;
  margin: 1rem auto 0;
  border-collapse: collapse;
  font-size: .88rem;
}

.chiffres td {
  padding: .35rem .4rem;
  border-bottom: 1px solid var(--surface-bord);
}

.chiffres td:last-child { text-align: right; font-weight: 700; }

/* En-tête de la table de comparaison du professeur. Elle a trois colonnes —
   compétence, réussite, écart — et sans titres on lisait deux notes côte à
   côte au lieu d'un taux et d'un écart. */
.chiffres th {
  padding: .35rem .4rem;
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--lavande);
  text-align: left;
  border-bottom: 1px solid var(--surface-bord);
}

.chiffres th:not(:first-child) { text-align: right; }

/* Le volume accompagne le pourcentage sans lui disputer la vedette : c'est un
   complément qui empêche de lire « 100 % » sans voir qu'il porte sur quatre
   réponses, pas une seconde valeur. */
.chiffres .volume {
  font-size: .74rem;
  font-weight: 500;
  color: var(--lavande);
  white-space: nowrap;
}
.chiffres tr:last-child td { border-bottom: 0; }
.chiffres .sans { color: var(--lavande); font-style: italic; font-weight: 500; }

/* ---------- boîte de signalement ---------- */

.signaler {
  display: block;
  margin: 1.2rem auto 0;
  font-size: .85rem;
  font-weight: 600;
  color: var(--lavande);
}

.signaler:hover { color: var(--rose); }

.modale {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(13, 10, 37, .72);
}

.modale-boite {
  width: 100%;
  max-width: 22rem;
  padding: 1.4rem;
  text-align: center;
  background: var(--encre);
  background-image: var(--page-bg);
  border: 2px solid var(--surface-bord);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.modale-boite h2 { margin-top: 0; }
.modale-boite .bouton { margin-top: 0; }
.modale-boite .lien { margin-top: 1rem; }

/* ---------- pied ---------- */

.pied {
  padding: 1rem;
  text-align: center;
  font-size: .78rem;
  color: var(--lavande);
  border-top: 1px solid var(--surface-bord);
}

/* ---------- apparition au premier passage à l'écran ---------- */

.entre {
  opacity: 0;
  transform: translateY(8px);
  animation: entre .32s ease forwards;
}

/* Écran à illustration : le texte et le formulaire d'un côté, l'image de
   l'autre. La colonne de saisie était large et vide, l'illustration l'occupe
   sans rien pousser hors de l'écran.
   ⚠️ `column-reverse` sur téléphone : dans le HTML l'image vient APRÈS le
   texte — c'est ce qu'il faut pour un lecteur d'écran, une image décorative ne
   devant pas précéder le titre — mais à l'affichage empilé elle doit passer
   au-dessus. L'inversion se fait donc ici, pas dans le balisage. */
.ecran-illustre {
  display: flex;
  align-items: center;
  gap: 30px;
}

.ecran-illustre-texte { flex: 1; min-width: 0; }

.ecran-illus {
  flex: none;
  width: 190px;
  max-width: 38%;
  height: auto;
}

@media (max-width: 700px) {
  .ecran-illustre { flex-direction: column-reverse; gap: 14px; }
  .ecran-illus { width: 150px; max-width: 60%; }
}

@keyframes entre {
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .entre { animation: none; opacity: 1; transform: none; }
  * { transition: none !important; }
}


/* ---------- Luna : lignes d'action ---------- */

/* ⛔ TEXTE SEUL. Pas d'avatar, pas d'illustration, pas de personnage : règle
   du propriétaire, refusée quatre fois sur logimaths.fr. Les icônes de gauche
   sont des formes du module de navigation — elles disent le TYPE d'action,
   pas qui parle. */

.luna-titre {
  margin: .2rem 0 .9rem;
  font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
  font-size: 1.1rem;
  font-weight: 700;
}

.luna-ligne {
  display: flex;
  align-items: center;
  gap: .7rem;
  width: 100%;
  margin: .45rem 0;
  padding: .7rem .8rem;
  border: 1px solid var(--surface-bord);
  border-radius: 14px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: .95rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: border-color .15s ease, transform .15s ease;
}

.luna-ligne:hover {
  border-color: var(--rose);
  transform: translateY(-1px);
}

/* Un progrès n'est pas une consigne : sa ligne ne se clique pas, et rien ne
   laisse croire le contraire — ni curseur, ni flèche, ni survol. */
.luna-ligne-fixe {
  cursor: default;
  background: none;
  border-style: dashed;
}

.luna-ligne-fixe:hover { border-color: var(--surface-bord); transform: none; }

.luna-ic {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 10px;
  background: linear-gradient(135deg, rgba(224, 41, 123, .28), rgba(46, 116, 194, .24));
}

.luna-ic svg { width: 19px; height: 19px; display: block; }
.luna-ic svg .p { fill: var(--rose); }
.luna-ic svg .s { fill: var(--lavande); opacity: .5; }

.luna-txt { flex: 1; min-width: 0; }

.luna-fleche {
  flex: none;
  color: var(--rose);
  font-size: 1.1rem;
  font-weight: 800;
}

/* ---------- en-tête sur petit écran ----------
   ⚠️ EN FIN DE FICHIER, et pas à côté des règles d'en-tête : à
   spécificité égale, c'est la dernière déclaration qui l'emporte.
   Placées plus haut, ces règles étaient écrasées par `.lm-burger` et
   `.theme` — mesuré, le bouton restait à 38 px. */

/* ⚠️ Sur téléphone, l'en-tête porte désormais sept éléments : retour, logo,
   titre, langue, badge, thème, hamburger. À 430 px le mot-symbole tombait à
   « Cours … ». On resserre les écarts et la taille du titre plutôt que de
   retirer une commande — chacune sert. Mesuré à 430 px : le titre entier
   tient. */
@media (max-width: 480px) {
  .entete { gap: .45rem; padding: .6rem .7rem; }
  .marque { font-size: 1.08rem; }
  .sous-titre { font-size: .7rem; }
  .logo-ring-sm { width: 38px; height: 38px; }
}

/* Sous 400 px — les Android d'entrée de gamme, très répandus à Karakol — il
   faut encore de la place : le sélecteur de langue se resserre et le titre
   perd deux points. Mesuré à 360 px, le mot-symbole entier tient. */
@media (max-width: 400px) {
  /* Le nom de l'école quitte l'en-tête : il est déjà au pied de page, et
     mieux vaut le perdre là que de couper le nom de l'application. */
  .sous-titre { display: none; }
  .marque { font-size: 1.02rem; white-space: normal; line-height: 1.05; }
  .langues select { height: 34px; padding: 0 1.15rem 0 .5rem; font-size: .72rem; }
  .retour, .theme { width: 34px; height: 34px; }
  .lm-burger { width: 34px; height: 34px; }
}



/* ---------- pastilles de réglage (séries) ---------- */

/* Trois listes déroulantes natives demandaient six gestes avant de commencer
   et rendaient chacune à sa façon selon le téléphone. Les pastilles montrent
   tous les choix d'un coup, un geste chacun, pareil partout. */
.pastilles-titre {
  margin: .9rem 0 .4rem;
  font-size: .82rem;
  font-weight: 700;
  color: var(--lavande);
}

.pastilles-titre:first-of-type { margin-top: .4rem; }

.pastilles { display: flex; flex-wrap: wrap; gap: .45rem; }

.pastille {
  padding: .5rem .95rem;
  border: 1px solid var(--surface-bord);
  border-radius: 999px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: .9rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}

.pastille:hover { border-color: var(--rose); }

.pastille-on {
  background: var(--rose);
  border-color: var(--rose);
  color: #FFFFFF;
  font-weight: 700;
}

.bloc-serie .bouton { margin-top: 1.3rem; }

/* La carte de Luna sur l'écran des séries : son bouton part sans réglage. Un
   bouton désactivé le temps que la proposition arrive — jamais un bouton qui
   ne fait rien au clic. */
.luna-serie .bouton { margin-top: .9rem; }
.luna-serie .bouton:disabled { opacity: .5; cursor: wait; }

/* Note de format sous la carte de Luna : dire une fois comment la séance se
   déroule, sans encombrer la carte elle-même. */
.serie-note { text-align: center; font-size: .85rem; margin-top: .9rem; }
.doux { color: var(--lavande); }

/* ---------- les jeux (js/jeux.js) ----------
   Préfixe jx- : aucun risque de collision avec le socle, prof.css ou
   admin.css — vérifié avant d'écrire, règle du projet. Couleurs uniquement
   par variables : les deux thèmes doivent tenir sans exception. */

.jx-record { display: block; color: var(--rose); font-weight: 700; margin-top: .15rem; }

/* Memory : la carte se retourne d'un tap. Le dos et la face occupent la même
   case ; la bascule est un simple fondu-écrasement, lisible aussi quand les
   animations sont réduites. */
.jx-memory {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: .5rem;
  margin: 1rem 0;
}

.jx-carte {
  position: relative;
  min-height: 64px;
  border: 1px solid var(--surface-bord);
  border-radius: 14px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  cursor: pointer;
  padding: .3rem;
  overflow: hidden;
}

.jx-carte-dos,
.jx-carte-face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: .3rem;
  transition: opacity .22s ease, transform .22s ease;
  overflow-wrap: anywhere;
  text-align: center;
}

.jx-carte-dos { font-size: 1.3rem; font-weight: 800; color: var(--lavande); }
.jx-carte-face { font-size: .85rem; font-weight: 700; opacity: 0; transform: scaleX(0); }

.jx-carte.est-vue .jx-carte-dos,
.jx-carte.est-trouvee .jx-carte-dos { opacity: 0; transform: scaleX(0); }
.jx-carte.est-vue .jx-carte-face,
.jx-carte.est-trouvee .jx-carte-face { opacity: 1; transform: none; }

.jx-carte.est-vue { border-color: var(--rose); }
.jx-carte.est-trouvee { border-color: var(--juste); opacity: .55; }

/* Tuiles de mots (phrase en désordre) et jetons de lettres (épeleur). */
.jx-reponse,
.jx-banc {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  align-items: center;
  margin: .8rem 0;
}

.jx-reponse {
  min-height: 3rem;
  padding: .55rem;
  border: 1px dashed var(--surface-bord);
  border-radius: 14px;
}

.jx-reponse.est-juste { border-color: var(--juste); }
.jx-reponse.est-faux { border-color: var(--faux); }

.jx-vide { color: var(--lavande); }

.jx-tuile,
.jx-lettre {
  padding: .5rem .8rem;
  border: 1px solid var(--surface-bord);
  border-radius: 12px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}

.jx-tuile:hover, .jx-lettre:hover { border-color: var(--rose); }

.jx-lettre { min-width: 2.6rem; text-transform: uppercase; text-align: center; }

/* Cases à remplir (épeleur, montgolfière). */
.jx-cases {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  justify-content: center;
  margin: 1rem 0;
}

.jx-case {
  width: 2.2rem;
  height: 2.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-bottom: 3px solid var(--surface-bord);
  font-size: 1.25rem;
  font-weight: 800;
  text-transform: uppercase;
  color: var(--blanc);
}

.jx-cases.est-juste .jx-case { border-color: var(--juste); }
.jx-cases.est-faux .jx-case { border-color: var(--faux); }
.jx-case.est-revele { color: var(--faux); }

/* Mot courant (tri, bingo en mode texte). */
.jx-mot-courant {
  margin: 1rem 0;
  padding: 1rem;
  text-align: center;
  font-size: 1.5rem;
  font-weight: 800;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--surface-bord);
}

/* Bingo : neuf cases, marquées au fil des mots entendus. */
.jx-bingo {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .5rem;
  margin: 1rem 0;
}

.jx-bingo-case {
  min-height: 62px;
  border: 1px solid var(--surface-bord);
  border-radius: 14px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: .95rem;
  font-weight: 700;
  cursor: pointer;
  overflow-wrap: anywhere;
  padding: .3rem;
}

.jx-bingo-case.est-marquee {
  background: var(--rose);
  border-color: var(--rose);
}

.jx-bingo-case.est-marquee .mot-en { color: #FFFFFF; }
.jx-bingo-case.faux { border-color: var(--faux); }

.jx-ecouter { display: inline-flex; align-items: center; gap: .5rem; }
.jx-ecouter svg { flex: none; }

/* Montgolfière : l'enveloppe descend d'un cran par erreur (transform posé par
   le JS sur le groupe). La transition rend la chute lisible. */
.jx-ballon {
  display: block;
  width: min(190px, 55vw);
  margin: .4rem auto 0;
}

.jx-ballon g { transition: transform .45s ease; }
.jx-enveloppe { fill: var(--rose); opacity: .85; }
.jx-nacelle { fill: var(--bleu-ciel); }
.jx-corde { stroke: var(--lavande); stroke-width: 2; }
.jx-sol { stroke: var(--surface-bord); stroke-width: 3; stroke-linecap: round; }

/* Clavier ABC de la montgolfière. */
.jx-clavier {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: .35rem;
  margin: 1rem 0;
}

.jx-touche {
  min-height: 44px;
  border: 1px solid var(--surface-bord);
  border-radius: 10px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: 1.05rem;
  font-weight: 700;
  text-transform: uppercase;
  cursor: pointer;
}

.jx-touche:disabled { opacity: .3; cursor: default; }

/* Mots mêlés : la grille se parcourt au doigt. touch-action: none, sinon le
   glissement fait défiler la page au lieu de tracer. */
.jx-mm-grille {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  gap: 2px;
  margin: 1rem 0;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

.jx-mm-case {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: var(--surface-plein);
  border: 1px solid var(--surface-bord);
  font-size: clamp(.8rem, 3.4vw, 1.05rem);
  font-weight: 700;
  text-transform: uppercase;
  color: var(--blanc);
}

.jx-mm-case.est-choisie { background: var(--bleu-ciel); border-color: var(--bleu-ciel); color: #FFFFFF; }
.jx-mm-case.est-mot { background: var(--rose); border-color: var(--rose); color: #FFFFFF; }

.jx-mm-indices li.jx-mm-trouve { text-decoration: line-through; opacity: .6; }

/* Jeu de l'oie : plateau en serpentin, le pion monte vers l'arrivée. */
.jx-oie {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: .4rem;
  margin: 1rem 0;
}

.jx-oie-case {
  position: relative;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  background: var(--surface-plein);
  border: 1px solid var(--surface-bord);
  font-size: .85rem;
  font-weight: 700;
  color: var(--lavande);
}

.jx-oie-case.est-fin { background: var(--surface); color: var(--blanc); }
.jx-oie-case.est-ici { border-color: var(--rose); color: var(--blanc); }

.jx-pion {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--rose);
  border: 2px solid var(--blanc);
}

.jx-de { display: inline-flex; align-items: center; gap: .6rem; }
.jx-de svg { flex: none; }

/* Histoire en morceaux : les phrases sont des cartes larges, numérotées une
   fois choisies. */
.jx-recit { flex-direction: column; align-items: stretch; }

.jx-phrase {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  text-align: left;
  padding: .6rem .8rem;
  border: 1px solid var(--surface-bord);
  border-radius: 12px;
  background: var(--surface-plein);
  color: var(--blanc);
  font-family: inherit;
  font-size: .95rem;
  cursor: pointer;
}

.jx-phrase:hover { border-color: var(--rose); }

.jx-phrase-n {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--rose);
  color: #FFFFFF;
  font-size: .8rem;
  font-weight: 800;
}

.jx-ordre { margin: .8rem 0; }

/* Pictogrammes du memory (data/pictos.json). */
/* Dessins Noto : assez grands pour se reconnaître d'un coup d'œil. */
.jx-pict { width: 44px; height: 44px; object-fit: contain; }

/* Tuiles déjà placées : elles se REPRENNENT (tap) et se DÉPLACENT (glisser).
   La main ouverte l'annonce, la main fermée accompagne le geste, et
   touch-action: none empêche le glissement de faire défiler la page. */
.jx-reponse [data-jx-rep],
.jx-reponse [data-jx-histrep] {
  cursor: grab;
  touch-action: none;
}

.jx-tuile.est-levee,
.jx-phrase.est-levee {
  cursor: grabbing;
  position: relative;
  z-index: 6;
  border-color: var(--rose);
  box-shadow: var(--shadow);
}

/* Les boutons juste/faux des jeux réutilisent les classes des QCM (.choix
   button.juste / .faux) : mêmes couleurs, mêmes réflexes. */

@media (prefers-reduced-motion: reduce) {
  .jx-carte-dos, .jx-carte-face, .jx-ballon g { transition: none; }
}

/* Sous 400 px, les grilles denses gagnent un peu d'air en resserrant. */
@media (max-width: 400px) {
  .jx-memory { gap: .35rem; }
  .jx-carte { min-height: 56px; }
  .jx-clavier { grid-template-columns: repeat(6, 1fr); }
  .jx-case { width: 1.9rem; height: 2.3rem; font-size: 1.1rem; }
}

/* Interrupteur des bruitages (écran des jeux). L'icône et le libellé changent
   ensemble ; le bouton reste un simple lien, pas une commande de premier plan. */
.jx-sons {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  margin: .2rem 0 .6rem;
}
.jx-sons svg { flex: none; }
