/* ============================================================
   GYRE — système de design
   Généré à partir de « Gyre — Système.dc.html », recoupé avec
   « Gyre — Écrans session.dc.html » et « Gyre — Écrans pilotage.dc.html ».
   Jetons + classes de composants, chargé par toutes les pages.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Familjen+Grotesk:wght@500;600;700&family=Chivo:wght@400;500;600;700&family=Spline+Sans+Mono:wght@400;500;600&display=swap');

:root{
  /* --- couches brutes — les seuls hex du système (Tabl. 1) --- */
  --ivoire:#EFE4CE; --loge:#E4D7BB; --reserve:#F5EEDE; --filet:#C9B896;
  --encre:#221E18; --encre-2:#3A342B; --sepia:#4C4437; --sepia-clair:#635A4B;
  --sepia-negatif:#A2977E; --ivoire-negatif:#C0B6A0;
  --flamme:#8A4B2A; --maths:#5C9C86; --maths-fonce:#2F5E52; --physique:#1E3A54;
  /* Cycle de couleurs pour les matières au-delà de maths/physique (phase
     multi-comptes — voir gySubjectClass()/js/gyre-chrome.js). Choisies dans
     la même famille désaturée que le reste de la palette : ni néon, ni
     réutilisation de --flamme (réservée aux états d'erreur/urgence). */
  --cycle-1:#B8863B; --cycle-1-fonce:#7A5824;
  --cycle-2:#7D5A7D; --cycle-2-fonce:#4F3A4F;
  --cycle-3:#4A6670;
  --cycle-4:#75823F; --cycle-4-fonce:#4A5227;

  /* --- couche sémantique — que des var() (Tabl. 2) --- */
  --surface-page:var(--ivoire); --surface-bloc:var(--loge); --surface-creux:var(--reserve);
  --trait:var(--filet); --trait-fort:var(--encre);
  --texte:var(--encre); --texte-2:var(--sepia); --texte-3:var(--sepia-clair);
  --texte-negatif:var(--ivoire); --texte-negatif-2:var(--ivoire-negatif); --texte-negatif-3:var(--sepia-negatif);
  --maths-texte:var(--maths-fonce); --physique-texte:var(--physique);
  --cycle-1-texte:var(--cycle-1-fonce); --cycle-2-texte:var(--cycle-2-fonce);
  --cycle-3-texte:var(--cycle-3); --cycle-4-texte:var(--cycle-4-fonce);
  --action:var(--encre); --action-active:var(--encre-2); --urgence:var(--flamme);

  /* --- typo (Fig. 1) --- */
  --f-display:'Familjen Grotesk',sans-serif; --f-texte:'Chivo',sans-serif;
  --f-mono:'Spline Sans Mono',ui-monospace,monospace;
  --t-64:64px; --t-40:40px; --t-34:34px; --t-30:30px; --t-22:22px; --t-19:19px;
  --t-15:15px; --t-14:14px; --t-13:13px; --t-12:12px; --t-11:11px; --t-10:10.5px;
  --t-math-display:24px;

  /* --- espacement, rayons, traits (Fig. 2) --- */
  --e-0:2px; --e-1:4px; --e-2:8px; --e-3:12px; --e-4:16px; --e-5:22px; --e-6:28px; --e-7:44px;
  --r-bloc:6px; --r-controle:3px; --r-loge:999px;
  --trait-1:1px; --trait-2:2px; --trait-3:3px;

  /* --- durées, courbes (Fig. 3) --- */
  --t-instant:90ms; --t-court:180ms; --t-long:620ms;
  --courbe:cubic-bezier(.2,.8,.2,1); --courbe-sortie:cubic-bezier(.4,0,1,1);

  /* --- largeurs de conteneur (Fig. 5 — cf. section LARGEURS ci-dessous) --- */
  --largeur-donnees:1440px; --largeur-prose:760px; --largeur-session:900px;

}

@media (prefers-reduced-motion:reduce){
  :root{ --t-instant:0ms; --t-court:0ms; --t-long:0ms; }
}

/* ============================================================
   BASE
   ============================================================ */
*{ box-sizing:border-box; }
body{ margin:0; background:var(--surface-page); color:var(--texte); font-family:var(--f-texte); }
a{ color:var(--texte); }
a:hover{ color:var(--urgence); }
.hidden{ display:none !important; }

/* ============================================================
   LARGEURS DE CONTENEUR
   Trois familles, une par nature d'écran, jamais un plafond choisi
   page par page :

     --largeur-donnees — les écrans denses : tableau de bord, chapitre,
       liste d'exercices, statistiques, planning, erreurs, exercice, et
       les trois écrans d'ingestion (dépôt, relecture, liste des
       imports — la relecture rejoint cette famille plutôt que de
       garder un plafond à elle : sa contrainte de lecture porte sur la
       colonne de propositions à l'intérieur du conteneur, jamais sur
       le conteneur lui-même, qui suit --largeur-donnees comme les
       autres). 1440px jusqu'à 1600px de viewport ; au-delà, la largeur
       gagnée sert à afficher plus de colonnes/figures, pas à dilater
       les colonnes existantes — le plafond monte donc à 1680px pour
       leur en laisser la place (cf. règle ci-dessous). Un plafond plus
       étroit tasserait ces pages artificiellement (grilles à colonnes,
       tableaux à colonnes fixes, jauges dépliées).
     --largeur-prose (760px) — guide, seul écran de lecture continue.
       Reste étroit volontairement à toutes les largeurs de viewport,
       pour la lisibilité d'un seul flux de texte.
     --largeur-session (900px) — révision, QCM. Centrage étroit voulu
       (un geste, une carte à la fois) : ne jamais élargir au-delà, à
       aucune largeur de viewport.

   Au-delà du plafond, l'espace va aux marges (margin:0 auto), jamais
   au contenu. Chaque page applique le jeton de sa famille sur son
   conteneur .xxx-container ; --largeur-prose est aussi la valeur par
   défaut de `main` ci-dessous (guide.html n'a donc rien à surcharger).

   Paliers — mêmes trois valeurs partout, jamais réinventées par page
   (design/desktop-redesign-brief*.md ; tranché à trois plutôt que
   cinq après vérification sur les treize maquettes, cf.
   design/…RECAP-passe-desktop.md) :
     1600px — au-delà, composition dense : --largeur-donnees monte à
       1680px et chaque écran peut faire apparaître une colonne/figure
       supplémentaire plutôt que d'étirer les existantes.
     1000px — entre 1000 et 1600px, composition à une colonne (c'est la
       tranche qui couvre le cas quotidien de la demi-fenêtre ~936px) ;
       en dessous de 1000px, la même composition continue, resserrée.
       Remplace l'ancien palier à 1100px, propre à dashboard.css et
       jamais repris ailleurs — il disparaît.
     600px  — palier mobile, issu de la refonte mobile. Inchangé : rien
       en dessous de 600px ne bouge dans la passe desktop.

   Note de transition : au moment d'écrire ceci (fondations système de
   la passe desktop), plusieurs feuilles par page utilisent encore un
   palier à 800px hérité d'avant cette règle (chapter.css, erreurs.css,
   stats.css, planning.css, exercise.css…). Ce n'est pas un désaccord —
   800px n'est plus un palier canonique, mais son retrait page par page
   attend que chaque écran soit effectivement retravaillé (lots
   suivants) plutôt que d'être renuméroté à la volée sans revoir la
   composition qui l'accompagne.
   ============================================================ */

/* Famille dense : 1680px au-delà de 1600px de viewport, 1440px en
   dessous (valeur de base posée dans :root ci-dessus). Un seul point de
   vérité — toute page utilisant --largeur-donnees hérite de
   l'escalade sans rien déclarer elle-même. */
@media (min-width:1600px){
  :root{ --largeur-donnees:1680px; }
}

/* En-tête/fil d'Ariane pleine largeur, contenu centré en dessous — motif
   partagé par les sept écrans de consultation. Les écrans de session
   (barre seule) et exercise.html (grille 2 colonnes) débordent ce défaut
   dans leur propre feuille de style. */
main{ max-width:var(--largeur-prose); margin:0 auto; padding:var(--e-7) var(--e-4); }
.katex{ font-size:1em; }
.katex-display{ margin:0; }

/* Repli KaTeX mobile (design/mobile-redesign-brief.md §2) : une formule
   display trop large scrolle horizontalement plutôt que de déborder ou
   de casser la mise en page ; une formule inline se réduit légèrement
   pour limiter le risque de débordement dans une phrase. */
@media (max-width:600px){
  .katex{ font-size:.92em; }
  .katex-display{
    overflow-x:auto;
    overflow-y:hidden;
    padding-bottom:var(--e-1);
  }
  .katex-display > .katex{ font-size:1em; }
}

/* iOS Safari détecte les nombres qui ressemblent à des téléphones et les
   relie en bleu souligné (x-apple-data-detectors), même avec le meta
   format-detection=telephone=no posé sur chaque page — le cache du
   Service Worker peut servir une version de la page plus ancienne que
   ce meta tag. Filet de sécurité au niveau du système : le lien détecté
   hérite du texte environnant plutôt que du bleu par défaut, quelle que
   soit la page ou la version d'HTML servie (retour de test réel :
   erreurs.html, compteur "N×" toujours bleu malgré le meta tag). */
a[x-apple-data-detectors]{
  color:inherit !important;
  text-decoration:inherit !important;
}

/* Espace entre l'en-tête et le contenu : le padding vertical par défaut
   de main (e-7 = 44px, pensé pour desktop) laisse un vide disproportionné
   en haut des écrans qui n'ont pas leur propre composition dense
   (planning/erreurs/guide/QCM/flashcards/liste d'exercices — dashboard/
   chapitre/exercice s'en sortent déjà, ils annulent ce padding eux-mêmes).
   Resserré une première fois à e-4, encore signalé trop grand au retour
   de test suivant — redescendu à e-2 ici. */
@media (max-width:600px){
  main{ padding-top:var(--e-2); padding-bottom:var(--e-4); }
}

@keyframes gyRise{ from{ opacity:0; transform:translateY(8px); } }
.gy-rise{ animation:gyRise var(--t-long) var(--courbe) backwards; }

@keyframes gyFill{
  0%,18%{ background:var(--surface-creux); border-color:var(--trait); }
  28%,72%{ background:var(--encre); border-color:var(--encre); }
  82%,100%{ background:var(--surface-creux); border-color:var(--trait); }
}
.gy-load span{ animation:gyFill 1.8s var(--courbe) infinite; }
.gy-load span:nth-child(1){ animation-delay:0s; }
.gy-load span:nth-child(2){ animation-delay:.09s; }
.gy-load span:nth-child(3){ animation-delay:.18s; }
.gy-load span:nth-child(4){ animation-delay:.27s; }
.gy-load span:nth-child(5){ animation-delay:.36s; }
.gy-load span:nth-child(6){ animation-delay:.45s; }
.gy-load span:nth-child(7){ animation-delay:.54s; }
.gy-load span:nth-child(8){ animation-delay:.63s; }
.gy-load span:nth-child(9){ animation-delay:.72s; }

@media (prefers-reduced-motion: reduce){
  .gy-load, .gy-rise{ animation:none !important; }
}

/* ============================================================
   TYPOGRAPHIE UTILITAIRE
   ============================================================ */
/* Légende en petites capitales, mono 10,5px (Fig. 4 du système) */
.gy-legend{
  font-family:var(--f-mono);
  font-size:var(--t-10);
  letter-spacing:.2em;
  text-transform:uppercase;
  color:var(--texte-2);
  margin:0 0 var(--e-3) 0;
  display:block;
}
/* Étiquette de navigation / fil d'Ariane / chip de matière, mono 11px */
.gy-eyebrow{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--texte-2);
}
.gy-nums{ font-variant-numeric:tabular-nums; }

.gy-page-title{
  font-family:var(--f-display);
  font-weight:700;
  font-size:var(--t-40);
  letter-spacing:-.03em;
  line-height:1.04;
  margin:0 0 var(--e-3);
}
.gy-page-subtitle{
  font-size:var(--t-15);
  line-height:1.8;
  color:var(--texte-2);
  max-width:64ch;
  margin:0 0 var(--e-6);
}

/* Raccourci clavier inline (Espace, 1 2 3, Entrée) */
.gy-kbd{
  font-family:var(--f-mono);
  font-size:var(--t-10);
  color:var(--texte-3);
  border:var(--trait-1) solid var(--trait);
  border-radius:var(--r-controle);
  padding:1px var(--e-1);
}

/* ============================================================
   BOUTONS
   Un seul bouton primaire par écran (§5 « Faire »).
   ============================================================ */
.gy-btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:var(--e-2);
  font-family:var(--f-texte);
  font-size:var(--t-15);
  font-weight:600;
  border-radius:var(--r-controle);
  padding:var(--e-3) var(--e-5);
  cursor:pointer;
  text-decoration:none;
  transition:background var(--t-instant) var(--courbe), border-color var(--t-instant) var(--courbe), color var(--t-instant) var(--courbe);
}
.gy-btn:focus-visible{
  outline:var(--trait-2) solid var(--action);
  outline-offset:var(--e-0);
}
.gy-btn:disabled{ opacity:.4; cursor:not-allowed; }

.gy-btn-primary{
  color:var(--texte-negatif);
  background:var(--action);
  border:var(--trait-1) solid var(--action);
}
.gy-btn-primary:hover:not(:disabled){ background:var(--action-active); border-color:var(--action-active); }

.gy-btn-secondary{
  color:var(--texte);
  background:transparent;
  border:var(--trait-1) solid var(--trait);
}
.gy-btn-secondary:hover:not(:disabled){ border-color:var(--trait-fort); }

.gy-btn-ghost{
  color:var(--texte-2);
  background:transparent;
  border:none;
  text-decoration:underline;
  text-underline-offset:4px;
  text-decoration-color:var(--trait);
  padding:var(--e-2) var(--e-1);
}
.gy-btn-ghost:hover:not(:disabled){ color:var(--texte); }

.gy-btn-danger{
  color:var(--flamme);
  background:transparent;
  border:var(--trait-1) solid var(--flamme);
}
.gy-btn-danger:hover:not(:disabled){ background:var(--flamme); color:var(--texte-negatif); }

/* Variante « monumentale » des écrans de session : Voir la réponse,
   Valider, Question suivante, Choisir une photo — toujours seule,
   centrée, sous le même geste clavier. Ramenée à --e-7 (pas de jeton
   dédié) : à revoir concrètement à l'étape 3 sur review/quiz/exercise —
   si --e-7 paraît trop serré une fois rendu, on ajoutera --e-8:64px comme
   cran général plutôt qu'une variable à usage unique. */
.gy-btn-session{
  font-size:var(--t-22);
  padding:var(--e-4) var(--e-7);
}

/* ============================================================
   CHIPS / TAGS
   ============================================================ */
.gy-chip-matiere{
  display:inline-flex;
  align-items:center;
  gap:7px;
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.06em;
  padding:5px var(--e-3);
  border-radius:var(--r-loge);
  border:var(--trait-1) solid var(--trait);
}
.gy-chip-matiere .gy-chip-dot{ width:7px; height:7px; border-radius:var(--r-loge); }
.gy-chip-matiere.gy-maths{ border-color:var(--maths); color:var(--maths-texte); }
.gy-chip-matiere.gy-maths .gy-chip-dot{ background:var(--maths); }
.gy-chip-matiere.gy-physique{ border-color:var(--physique); color:var(--physique-texte); }
.gy-chip-matiere.gy-physique .gy-chip-dot{ background:var(--physique); }
/* Matières au-delà de maths/physique — cycle de couleurs, voir gySubjectClass()
   (js/gyre-chrome.js) : une matière donnée retombe toujours sur la même classe. */
.gy-chip-matiere.gy-cycle-1{ border-color:var(--cycle-1); color:var(--cycle-1-texte); }
.gy-chip-matiere.gy-cycle-1 .gy-chip-dot{ background:var(--cycle-1); }
.gy-chip-matiere.gy-cycle-2{ border-color:var(--cycle-2); color:var(--cycle-2-texte); }
.gy-chip-matiere.gy-cycle-2 .gy-chip-dot{ background:var(--cycle-2); }
.gy-chip-matiere.gy-cycle-3{ border-color:var(--cycle-3); color:var(--cycle-3-texte); }
.gy-chip-matiere.gy-cycle-3 .gy-chip-dot{ background:var(--cycle-3); }
.gy-chip-matiere.gy-cycle-4{ border-color:var(--cycle-4); color:var(--cycle-4-texte); }
.gy-chip-matiere.gy-cycle-4 .gy-chip-dot{ background:var(--cycle-4); }
/* Matière sans couleur dédiée (aucun subjectId) — traitement neutre à
   l'encre/sepia, jamais utilisé pour une matière connue mais différente de
   maths/physique (design/gyre-design-dna.json ; design/multi-comptes.md §3). */
.gy-chip-matiere.gy-neutre{ border-color:var(--sepia); color:var(--texte-2); }
.gy-chip-matiere.gy-neutre .gy-chip-dot{ background:var(--sepia); }

/* Petit point + libellé, utilisé en tête de carte de contenu (session) */
.gy-matiere-tag{
  display:flex;
  align-items:center;
  gap:var(--e-2);
  opacity:.7;
}
.gy-matiere-tag .gy-chip-dot{ width:var(--e-2); height:var(--e-2); border-radius:var(--r-loge); }
.gy-matiere-tag.gy-maths .gy-chip-dot{ background:var(--maths); }
.gy-matiere-tag.gy-maths span{ color:var(--maths-texte); }
.gy-matiere-tag.gy-physique .gy-chip-dot{ background:var(--physique); }
.gy-matiere-tag.gy-physique span{ color:var(--physique-texte); }
.gy-matiere-tag.gy-cycle-1 .gy-chip-dot{ background:var(--cycle-1); }
.gy-matiere-tag.gy-cycle-1 span{ color:var(--cycle-1-texte); }
.gy-matiere-tag.gy-cycle-2 .gy-chip-dot{ background:var(--cycle-2); }
.gy-matiere-tag.gy-cycle-2 span{ color:var(--cycle-2-texte); }
.gy-matiere-tag.gy-cycle-3 .gy-chip-dot{ background:var(--cycle-3); }
.gy-matiere-tag.gy-cycle-3 span{ color:var(--cycle-3-texte); }
.gy-matiere-tag.gy-cycle-4 .gy-chip-dot{ background:var(--cycle-4); }
.gy-matiere-tag.gy-cycle-4 span{ color:var(--cycle-4-texte); }
.gy-matiere-tag.gy-neutre .gy-chip-dot{ background:var(--sepia); }
.gy-matiere-tag.gy-neutre span{ color:var(--texte-2); }

.gy-tag{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.06em;
  padding:5px var(--e-3);
  border-radius:var(--r-controle);
}
.gy-tag-outline{ border:var(--trait-1) solid var(--trait); color:var(--texte-2); }
.gy-tag-fort{ border:var(--trait-1) solid var(--trait-fort); color:var(--texte); }
.gy-tag-urgence{ background:var(--urgence); color:var(--texte-negatif); }

.gy-breakdown-chips{ display:flex; flex-wrap:wrap; gap:var(--e-1); }
.gy-breakdown-chip{
  font-family:var(--f-mono);
  font-size:var(--t-10);
  padding:3px var(--e-2);
  border-radius:var(--r-controle);
  border:var(--trait-1) solid var(--trait);
  color:var(--texte-2);
}
.gy-breakdown-chip.gy-maths{ border-color:var(--maths); color:var(--maths-texte); }
.gy-breakdown-chip.gy-physique{ border-color:var(--physique); color:var(--physique-texte); }
.gy-breakdown-chip.gy-cycle-1{ border-color:var(--cycle-1); color:var(--cycle-1-texte); }
.gy-breakdown-chip.gy-cycle-2{ border-color:var(--cycle-2); color:var(--cycle-2-texte); }
.gy-breakdown-chip.gy-cycle-3{ border-color:var(--cycle-3); color:var(--cycle-3-texte); }
.gy-breakdown-chip.gy-cycle-4{ border-color:var(--cycle-4); color:var(--cycle-4-texte); }
.gy-breakdown-chip.gy-neutre{ border-color:var(--sepia); color:var(--texte-2); }

/* ============================================================
   CARTES / BLOCS
   ============================================================ */
.gy-card{
  background:var(--surface-bloc);
  border:var(--trait-1) solid var(--trait);
  border-radius:var(--r-bloc);
  padding:24px 26px;
}
/* Carte de contenu (formule, citation) : filet d'emphase à l'encre,
   jamais à la couleur d'une matière (§ Faire / Ne pas faire). */
.gy-card-emphase{
  background:var(--surface-creux);
  border:var(--trait-1) solid var(--trait);
  border-left:var(--trait-3) solid var(--trait-fort);
  border-radius:var(--r-controle);
  padding:var(--e-5);
}
/* Bloc figure numéroté : Fig. n — / Tabl. n — (consultation uniquement,
   absent des écrans de session — cf. §5 « Faire »). */
.gy-figure{ background:var(--surface-page); border:var(--trait-1) solid var(--trait); border-radius:var(--r-controle); padding:18px; }

/* ============================================================
   JAUGE DE LOGES
   Huit loges (apprentissage + sept intervalles de scheduler.js),
   seule unité de progression d'un chapitre : jamais pour une
   matière ni pour une session (cf. « Ne pas faire »).
   ============================================================ */
.gy-gauge{ display:flex; gap:var(--e-1); }
.gy-gauge span{ flex:1; border-radius:var(--r-loge); }
.gy-gauge.gy-gauge-l span{ height:14px; }
.gy-gauge.gy-gauge-m{ gap:var(--e-0); }
.gy-gauge.gy-gauge-m span{ height:9px; }
.gy-gauge.gy-gauge-s{ gap:var(--e-0); }
.gy-gauge.gy-gauge-s span{ height:7px; }
.gy-gauge span{ background:var(--surface-creux); border:var(--trait-1) solid var(--trait); }
.gy-gauge span.gy-on{ border-color:transparent; }
.gy-gauge.gy-maths span.gy-on{ background:var(--maths); }
.gy-gauge.gy-physique span.gy-on{ background:var(--physique); }

/* Barre de progression de session (flashcards/QCM), pas une jauge de loges */
.gy-session-bar{ display:flex; gap:var(--e-0); }
.gy-session-bar span{ height:var(--e-1); flex:1; background:var(--surface-creux); }
.gy-session-bar span.gy-done{ background:var(--trait-fort); }

/* ============================================================
   EN-TÊTE D'APPLICATION / FIL D'ARIANE
   Identique sur les sept écrans de consultation ; remplacé par la
   barre de session sur review/quiz/exercice en cours.
   ============================================================ */
.gy-app-header{
  display:flex;
  align-items:center;
  justify-content:space-between;
  padding:calc(var(--e-4) + env(safe-area-inset-top)) var(--e-6) var(--e-4);
  border-bottom:var(--trait-1) solid var(--trait-fort);
  background:var(--surface-page);
}
.gy-app-header-brand{ display:flex; align-items:center; gap:var(--e-3); text-decoration:none; }
.gy-app-header-brand .gy-wordmark{
  font-family:var(--f-display);
  font-weight:700;
  font-size:var(--t-22);
  letter-spacing:-.035em;
  color:var(--texte);
}
.gy-app-header-descriptor{
  font-family:var(--f-mono);
  font-size:var(--t-10);
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--texte-3);
}
.gy-app-header-nav{ display:flex; align-items:center; gap:6px; }
.gy-app-header-nav a{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.1em;
  text-transform:uppercase;
  padding:6px 12px;
  border-radius:var(--r-controle);
  border:var(--trait-1) solid transparent;
  color:var(--texte-2);
  text-decoration:none;
}
.gy-app-header-nav a:hover{ color:var(--texte); }
.gy-app-header-nav a.gy-actif{
  background:var(--surface-bloc);
  border-color:var(--trait-fort);
  color:var(--texte);
}
/* Guide : lien secondaire, pas un onglet — les exercices sont toujours
   liés à un chapitre, il n'y a pas de destination « Exercices » globale. */
.gy-app-header-nav .gy-secondaire{
  margin-left:var(--e-2);
  padding-left:var(--e-4);
  border-left:var(--trait-1) solid var(--trait);
  border-radius:0;
  text-decoration:underline;
  text-decoration-color:var(--trait);
  text-underline-offset:3px;
}
.gy-app-header-nav .gy-secondaire:hover{ text-decoration-color:var(--texte); }

.gy-session-bar-nav{
  display:flex;
  align-items:center;
  justify-content:space-between;
  padding:calc(var(--e-3) + env(safe-area-inset-top)) var(--e-6) var(--e-3);
}
.gy-session-exit{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--texte-3);
  text-decoration:none;
}
.gy-session-exit:hover{ color:var(--texte); }
.gy-session-progress{ display:flex; align-items:center; gap:var(--e-3); }
.gy-session-count{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  color:var(--texte-3);
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
}
.gy-session-progress .gy-session-bar{ width:220px; gap:var(--e-0); }

/* Barre de progression et compteur trop petits en plein écran (design/
   desktop-redesign-brief-vague2.md, écrans review/quiz/exercise, tous
   trois montés par mountSessionBar) : une piste de 220px et un compteur
   à 11px se perdent en haut d'un viewport de 1792px. Déclaré une seule
   fois ici plutôt que dans les trois feuilles par page — c'est le même
   composant partagé. */
@media (min-width:1600px){
  .gy-session-progress .gy-session-bar{ width:440px; }
  .gy-session-progress .gy-session-bar span{ height:var(--e-2); }
  .gy-session-count{ font-size:var(--t-15); color:var(--texte); }
}

/* Nav retour/accueil existante de l'appli, rejouée avec les jetons Gyre —
   aucune maquette ne couvre ce motif précis (les écrans de consultation
   Gyre utilisent un en-tête complet + fil d'Ariane, pas un « Retour »),
   donc structure et comportement inchangés, seules les valeurs migrent. */
.gy-page-nav{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--e-3);
  margin-bottom:var(--e-3);
}
.gy-nav-back{
  display:inline-flex;
  align-items:center;
  gap:var(--e-2);
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--texte-2);
  text-decoration:none;
  padding:var(--e-2) var(--e-3);
  border-radius:var(--r-controle);
  border:var(--trait-1) solid var(--trait);
}
.gy-nav-back:hover{ border-color:var(--trait-fort); color:var(--texte); }
.gy-nav-home{
  width:32px;
  height:32px;
  border-radius:var(--r-loge);
  display:flex;
  align-items:center;
  justify-content:center;
  border:var(--trait-1) solid var(--trait);
  color:var(--texte-2);
  flex-shrink:0;
}
.gy-nav-home:hover{ border-color:var(--trait-fort); color:var(--texte); }

.gy-breadcrumb{
  display:flex;
  align-items:center;
  gap:var(--e-3);
  padding:var(--e-3) var(--e-6);
  border-bottom:var(--trait-1) solid var(--trait);
}
.gy-breadcrumb .gy-separateur{ font-family:var(--f-mono); font-size:var(--t-11); color:var(--texte-3); }
.gy-breadcrumb a, .gy-breadcrumb .gy-lien, .gy-breadcrumb .gy-courant{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.1em;
  text-transform:uppercase;
  text-decoration:none;
}
.gy-breadcrumb a, .gy-breadcrumb .gy-lien{ color:var(--texte-2); cursor:pointer; }
.gy-breadcrumb a:hover, .gy-breadcrumb .gy-lien:hover{ color:var(--texte); }
.gy-breadcrumb .gy-courant{ color:var(--texte); }

/* ============================================================
   ÉTATS : vide, erreur, chargement, toast
   ============================================================ */
.gy-empty{
  border:var(--trait-1) dashed var(--trait);
  border-radius:var(--r-controle);
  padding:26px 22px;
  text-align:center;
}
.gy-empty h2, .gy-empty p{ margin:0; }
.gy-empty h2{ font-family:var(--f-display); font-weight:600; font-size:var(--t-30); margin-bottom:var(--e-2); }
.gy-empty p{ font-size:var(--t-15); color:var(--texte-2); }

.gy-error{
  border:var(--trait-1) solid var(--flamme);
  border-left:var(--trait-3) solid var(--flamme);
  border-radius:var(--r-controle);
  padding:16px;
  background:var(--surface-creux);
}
.gy-error .gy-legend{ color:var(--flamme); }
.gy-error p{ font-size:var(--t-13); line-height:1.7; color:var(--texte-2); margin:0 0 var(--e-3); }

/* Placement (aucun dans le CSS d'origine — premier usage réel, câblé par
   gyToast() dans js/gyre-chrome.js) : fixé EN BAS, centré, jamais en haut.
   - Le haut est à #gyreSyncBanner (sticky top:0, pleine largeur, z 900)
     dès qu'une synchro coince — un toast ancré en haut s'y ferait couvrir.
   - En bas vivent les barres d'actions de session fixées bottom:0
     (.review-actions/.quiz-actions, ~56px + marges + safe-area, z 500,
     max-width:600px) et .gy-tabs (56px). Le décalage bottom ci-dessous
     reprend leur réserve exacte pour passer AU-DESSUS d'elles, + e-2.
   - #gyreAccountControl est fixé en bas À DROITE : le toast centré, borné
     à 420px et à e-4 des bords, ne le recouvre pas.
   - z 800 : sous les modales (z 1000), au-dessus du reste. */
.gy-toast{
  position:fixed;
  left:var(--e-4);
  right:var(--e-4);
  bottom:calc(56px + var(--e-4) + var(--e-1) + env(safe-area-inset-bottom) + var(--e-2));
  z-index:800;
  max-width:420px;
  margin:0 auto;
  display:flex;
  align-items:center;
  gap:14px;
  background:var(--encre);
  border-radius:var(--r-controle);
  padding:13px 16px;
}
.gy-toast span{ flex:1; font-size:var(--t-13); color:var(--texte-negatif); }
.gy-toast a{ font-family:var(--f-mono); font-size:var(--t-11); color:var(--sepia-negatif); cursor:pointer; }

/* Bandeau persistant (js/storage.js) : une écriture a échoué et n'est pas
   rejouée sans qu'on le sache — ou (mode hors ligne, design/prompt-mode-
   hors-ligne-2026-08-10.md) les données affichées viennent d'un instantané
   local daté. Sticky plutôt que fixed : premier enfant de <body>, il
   pousse le contenu au lieu de le recouvrir, puis reste visible au scroll
   — jamais en conflit avec les barres d'actions fixées en bas des écrans
   de session (elles vivent à l'autre bout de l'écran).
   Colonne plutôt qu'une seule ligne : hors ligne et une synchro en attente
   sont deux faits vrais en même temps (toute écriture hors ligne échoue en
   transitoire), affichés l'un sous l'autre plutôt que de se disputer la
   même ligne — voir renderSyncBanner(). */
.gy-sync-banner{
  position:sticky;
  top:0;
  z-index:900;
  display:flex;
  flex-direction:column;
  gap:var(--e-1);
  background:var(--surface-creux);
  border-bottom:var(--trait-1) solid var(--flamme);
  padding:var(--e-2) var(--e-4);
}
.gy-sync-banner-line{ display:flex; align-items:baseline; flex-wrap:wrap; gap:var(--e-3); }
.gy-sync-banner .gy-legend{ color:var(--flamme); margin:0; white-space:nowrap; }
.gy-sync-banner p{ margin:0; font-size:var(--t-13); color:var(--texte-2); }

.gy-data-error-screen{
  min-height:100vh;
  display:flex;
  align-items:center;
  justify-content:center;
  padding:var(--e-4);
  background:var(--surface-page);
}
.gy-data-error-screen .gy-error{ max-width:420px; width:100%; }
.gy-data-error-screen .gy-btn{ margin-top:var(--e-3); }

/* ============================================================
   MODALE
   Voile à 42 % d'encre, dialogue au filet plein — jamais d'ombre portée.
   ============================================================ */
.gy-modal-overlay{
  position:fixed;
  inset:0;
  background:rgba(34,30,24,.42);
  display:flex;
  align-items:center;
  justify-content:center;
  z-index:1000;
}
.gy-modal{
  position:relative;
  background:var(--surface-page);
  border:var(--trait-1) solid var(--encre);
  border-radius:var(--r-bloc);
  padding:22px;
  max-width:420px;
  width:90%;
}
/* Mobile : reste centrée comme sur desktop (jamais une feuille plein
   écran), juste plus étroite — marge fixe sur l'échelle e plutôt qu'un
   pourcentage, pour une respiration constante quelle que soit la
   largeur exacte du viewport (design/mobile-redesign-brief-phase2.md). */
@media (max-width:600px){
  .gy-modal{ width:calc(100% - var(--e-6) * 2); padding:var(--e-4); }
}
.gy-modal h2{ font-family:var(--f-display); font-weight:600; font-size:var(--t-19); margin:0 0 6px; }
.gy-modal p{ font-size:var(--t-13); line-height:1.7; color:var(--texte-2); margin:0 0 var(--e-4); }
.gy-modal-subtitle{
  display:block;
  font-family:var(--f-mono);
  font-size:var(--t-11);
  color:var(--texte-2);
  margin:0 0 var(--e-4);
}
.gy-modal-choices{ display:flex; flex-direction:column; gap:var(--e-2); }
.gy-modal-choice{
  display:block;
  width:100%;
  text-align:left;
  font-family:var(--f-texte);
  border-radius:var(--r-controle);
  padding:13px 16px;
  cursor:pointer;
}
.gy-modal-choice .gy-modal-choice-title{ display:block; font-size:var(--t-15); font-weight:600; }
.gy-modal-choice .gy-modal-choice-subtitle{ display:block; font-family:var(--f-mono); font-size:var(--t-10); margin-top:3px; }
.gy-modal-choice.gy-modal-choice-primary{ background:var(--action); border:var(--trait-1) solid var(--action); color:var(--texte-negatif); }
.gy-modal-choice.gy-modal-choice-primary .gy-modal-choice-subtitle{ color:var(--sepia-negatif); }
.gy-modal-choice.gy-modal-choice-primary:hover{ background:var(--action-active); }
.gy-modal-choice:not(.gy-modal-choice-primary){ background:transparent; border:var(--trait-1) solid var(--trait); color:var(--texte); }
.gy-modal-choice:not(.gy-modal-choice-primary) .gy-modal-choice-subtitle{ color:var(--texte-3); }
.gy-modal-choice:not(.gy-modal-choice-primary):hover{ border-color:var(--trait-fort); }
.gy-modal-close{
  position:absolute;
  top:14px;
  right:14px;
  width:40px;
  height:40px;
  border-radius:var(--r-loge);
  display:flex;
  align-items:center;
  justify-content:center;
  background:none;
  border:none;
  color:var(--texte-2);
  cursor:pointer;
}
.gy-modal-close:hover{ background:var(--surface-creux); }

/* ============================================================
   HERO CHAPITRE (titre + méta | Fig. 1 jauge à loges)
   Un seul bloc, réutilisé par chapter.html (une fois par carte) et
   stats.html (niveau chapitre) — la composition de la maquette
   « Écran chapitre », détachée de sa page d'origine.
   ============================================================ */
.chap-hero{
  display:flex;
  align-items:flex-start;
  justify-content:space-between;
  gap:var(--e-7);
  padding:var(--e-6);
  border-bottom:var(--trait-1) solid var(--trait);
}
.chap-hero-main{ max-width:660px; }
.chap-title{
  font-family:var(--f-display);
  font-weight:700;
  font-size:var(--t-40);
  line-height:1.04;
  letter-spacing:-.03em;
  margin:0 0 var(--e-3);
}
.chap-meta{
  font-family:var(--f-mono);
  font-size:var(--t-12);
  color:var(--texte-2);
  margin:0 0 var(--e-5);
}
.chap-hero-gauge{
  flex:none;
  width:330px;
  border-left:var(--trait-1) solid var(--trait);
  padding-left:var(--e-6);
}

/* ============================================================
   COURBE D'ÉVOLUTION (Fig. — 30 jours)
   Un tracé (chapter.html, stats.html) ou trois superposés (index.html) —
   même motif, partagé pour ne pas le redéfinir par écran.
   ============================================================ */
.dash-courbe-svg{ width:100%; height:150px; }
.dash-courbe-svg line{ stroke:var(--trait); }
.dash-courbe-svg line.dash-axe-fort{ stroke:var(--trait-fort); }
.dash-trace-physique{ stroke:var(--physique); stroke-width:1.6; }
.dash-trace-maths{ stroke:var(--maths); stroke-width:1.6; }
.dash-trace-global{ stroke:var(--encre); stroke-width:2.2; }
.dash-courbe-axis{
  display:flex;
  justify-content:space-between;
  font-family:var(--f-mono);
  font-size:var(--t-10);
  color:var(--texte-3);
  margin-top:var(--e-2);
}
.dash-courbe-legend{
  display:flex;
  gap:var(--e-4);
  font-family:var(--f-mono);
  font-size:var(--t-10);
  color:var(--texte-2);
  margin-top:var(--e-3);
}
.dash-legend-swatch{
  display:inline-block;
  width:var(--e-4);
  height:var(--e-0);
  vertical-align:middle;
  margin-right:var(--e-2);
  background:currentColor;
}
.dash-courbe-legend .dash-trace-global{ background:var(--encre); }
.dash-courbe-legend .dash-trace-maths{ background:var(--maths); }
.dash-courbe-legend .dash-trace-physique{ background:var(--physique); }
.dash-fig-empty{ font-size:var(--t-13); color:var(--texte-3); margin:var(--e-4) 0 0; }

/* ============================================================
   FILTRES — MENUS DÉROULANTS
   Un champ = un bouton qui ouvre un menu d'options, chacune avec son
   nombre de résultats. Motif partagé par erreurs.html (type/matière/
   chapitre) et exercise-list.html (état/difficulté) — même contrôle,
   déclaré une seule fois plutôt que dupliqué par écran (design/desktop-
   redesign-brief-vague2.md : « une seule fonctionnalité, trois rendus »,
   ici deux écrans desktop qui doivent se ressembler). Densité souris par
   défaut (36px/32px), 44px sous pointeur grossier via .gy-cible
   (gyre-system.css § CIBLE TACTILE).
   ============================================================ */
.gy-filter-bar{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:var(--e-3);
}
.gy-filter-field{ position:relative; flex:1 1 220px; min-width:0; }
.gy-filter-btn{
  width:100%;
  min-height:36px;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--e-3);
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.06em;
  padding:0 var(--e-3);
  border-radius:var(--r-controle);
  cursor:pointer;
  background:var(--surface-page);
  border:var(--trait-1) solid var(--trait);
  color:var(--texte);
  text-align:left;
}
.gy-filter-btn:hover{ border-color:var(--trait-fort); }
.gy-filter-btn.gy-actif{ background:var(--surface-bloc); border-color:var(--trait-fort); }
.gy-filter-btn-inner{ display:flex; align-items:baseline; gap:var(--e-2); min-width:0; }
.gy-filter-legend{ flex:none; font-size:var(--t-10); letter-spacing:.14em; text-transform:uppercase; color:var(--texte-3); }
.gy-filter-valeur{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--texte-2); }
.gy-filter-btn.gy-actif .gy-filter-valeur{ color:var(--texte); }
.gy-filter-caret{ flex:none; color:var(--texte-3); font-size:var(--t-10); }
.gy-filter-menu{
  position:absolute;
  top:calc(100% + var(--e-1));
  left:0;
  min-width:100%;
  max-height:320px;
  overflow-y:auto;
  z-index:5;
  background:var(--surface-page);
  border:var(--trait-1) solid var(--trait-fort);
  border-radius:var(--r-controle);
  padding:var(--e-1);
}
.gy-filter-option{
  width:100%;
  min-height:32px;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--e-4);
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.04em;
  white-space:nowrap;
  padding:0 var(--e-3);
  border:none;
  border-radius:var(--r-controle);
  cursor:pointer;
  background:transparent;
  color:var(--texte-2);
  text-align:left;
}
.gy-filter-option:hover{ background:var(--surface-creux); }
.gy-filter-option.gy-actif{ background:var(--surface-bloc); color:var(--texte); }
.gy-filter-reset{
  margin-left:auto;
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.06em;
  text-transform:uppercase;
  background:transparent;
  border:none;
  color:var(--texte-3);
  text-decoration:underline;
  text-decoration-color:var(--trait);
  text-underline-offset:4px;
  cursor:pointer;
  padding:0;
  white-space:nowrap;
}
.gy-filter-reset:hover{ color:var(--texte-2); }
.gy-filter-count{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  color:var(--texte-2);
  font-variant-numeric:tabular-nums;
  white-space:nowrap;
}
@media (min-width:1600px){
  .gy-filter-field{ flex:0 1 300px; max-width:340px; }
}
/* Sous 600px (erreurs.html à trois champs, exercise-list.html à deux) :
   la base 220px force chaque champ sur sa propre ligne dès deux champs
   présents, un bandeau de filtres prenant alors plus de hauteur que
   l'écran n'en a à offrir avant le contenu utile. Base réduite pour que
   les champs se répartissent sur une seule ligne plutôt que d'empiler
   verticalement, hauteur de contrôle resserrée en suivant — le rehaussement
   tactile 44px (.gy-cible, § CIBLE TACTILE) reste appliqué par-dessus,
   inchangé, sous pointeur grossier. */
@media (max-width:600px){
  .gy-filter-bar{ gap:var(--e-2); }
  .gy-filter-field{ flex:1 1 108px; }
  .gy-filter-btn{ min-height:32px; padding:0 var(--e-2); font-size:var(--t-10); }
  .gy-filter-legend{ display:none; }
  .gy-filter-count{ font-size:var(--t-10); }
  .gy-filter-reset{ font-size:var(--t-10); }
}

/* ============================================================
   TABLEAU (listes d'exercices, d'erreurs, de chapitres…)
   ============================================================ */
.gy-table-head{
  font-family:var(--f-mono);
  font-size:var(--t-10);
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--texte-3);
  padding-bottom:var(--e-2);
  border-bottom:var(--trait-1) solid var(--trait-fort);
}
.gy-table-row{
  padding:var(--e-3) 0;
  border-bottom:var(--trait-1) solid var(--trait);
  align-items:center;
}

/* ============================================================
   REPLI .chap-hero (chapter.html + stats.html, vue chapitre)
   Repli de layout, pas une règle de cible tactile — anciennement
   regroupé avec le repli des cibles tactiles ci-dessous parce que les
   deux vivaient sous le même @media(max-width:800px), sans lien de
   fond entre eux. Séparés ici ; ce palier à 800px est celui hérité
   (cf. § LARGEURS, note de transition) et n'est pas retouché en dehors
   de ce lot — chapter.html et stats.html revoient leur composition
   dense dans les lots suivants, ce repli sera reconsidéré à ce moment.
   ============================================================ */
@media (max-width:800px){
  /* .chap-hero est défini ci-dessus, partagé par chapter.html et
     stats.html (vue chapitre) — le repli doit vivre ici, pas dans
     chapter.css, sinon stats.html (qui ne charge pas chapter.css) garde
     une rangée à largeur fixe (330px + 660px) et déborde horizontalement
     sous 800px. Bug réel trouvé sur téléphone : stats.html scrollait
     horizontalement à cause de ça. */
  .chap-hero{ flex-wrap:wrap; }
  .chap-hero-gauge{ border-left:none; padding-left:0; border-top:var(--trait-1) solid var(--trait); padding-top:var(--e-4); width:auto; }
}

@media (max-width:600px){
  /* chapter.html empile plusieurs .chap-hero d'affilée (une par chapitre
     de la liste) — à la densité desktop (padding e-6, titre 40px), la
     liste devient très difficile à parcourir au pouce (retour de test
     réel). Resserré ici, ce qui profite aussi à la vue chapitre unique
     de stats.html (même composant partagé). */
  .chap-hero{ padding:var(--e-4); gap:var(--e-4); }
  .chap-title{ font-size:var(--t-30); margin-bottom:var(--e-2); }
  .chap-meta{ margin-bottom:var(--e-3); }
}

/* ============================================================
   NAVIGATION MOBILE
   Palier ajouté pour la déclinaison mobile (design/gyre-design-dna.json,
   design/mobile-redesign-brief*.md) : la rangée de liens desktop cède la
   place à une barre d'onglets fixée en bas (Bord/Planning/Stats/Erreurs)
   + un lien Guide secondaire dans un en-tête réduit à 44px. Un seul
   palier, défini une fois ici — jamais réinventé par page.
   ============================================================ */
.gy-mobile-back-bar{ display:none; }
.gy-tabs{ display:none; }

@media (max-width:600px){
  .gy-app-header{
    padding:calc(var(--e-2) + env(safe-area-inset-top)) var(--e-4) var(--e-2);
  }
  .gy-app-header-descriptor{ display:none; }
  .gy-app-header-nav a:not(.gy-secondaire){ display:none; }
  .gy-app-header-nav .gy-secondaire.gy-actif{
    background:none;
    border-color:transparent;
    color:var(--texte);
    text-decoration-color:var(--texte);
  }
  /* Sous-écran : la barre Retour remplace la ligne wordmark+nav plutôt
     que de s'y ajouter — cf. mobileBack dans gyre-chrome.js. */
  .gy-app-header-hasback{ display:none; }
  .gy-breadcrumb{ display:none; }

  .gy-mobile-back-bar{
    display:flex;
    align-items:center;
    height:var(--e-7);
    padding:0 var(--e-4);
    border-bottom:var(--trait-1) solid var(--trait);
  }
  .gy-mobile-back-bar .gy-session-exit{ flex:none; }
  .gy-mobile-back-context{
    flex:1;
    text-align:right;
    font-family:var(--f-mono);
    font-size:var(--t-10);
    letter-spacing:.16em;
    text-transform:uppercase;
    color:var(--texte-2);
  }

  /* Espace réservé en bas de page pour ne pas laisser le tab bar fixé
     recouvrir le dernier contenu — appliqué seulement sur les pages qui
     montent réellement la barre (mountAppTabs ajoute cette classe).
     e-1 de marge au-dessus de la safe-area : e-5 mangeait trop d'écran
     (retour de test réel — la barre + son padding dépassait 100px). */
  body.gy-has-tabs{
    padding-bottom:calc(56px + var(--e-1) + env(safe-area-inset-bottom));
  }
  .gy-tabs{
    display:grid;
    grid-template-columns:repeat(4,minmax(0,1fr));
    position:fixed;
    left:0; right:0; bottom:0;
    background:var(--surface-page);
    border-top:var(--trait-1) solid var(--trait-fort);
    padding-bottom:calc(var(--e-1) + env(safe-area-inset-bottom));
    z-index:500;
  }
  .gy-tab{
    height:56px;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:var(--e-2);
    text-decoration:none;
  }
  .gy-tab-label{
    font-family:var(--f-mono);
    font-size:var(--t-10);
    letter-spacing:.16em;
    text-transform:uppercase;
    color:var(--texte-3);
  }
  .gy-tab-rule{ width:24px; height:var(--trait-2); background:transparent; }
  .gy-tab.gy-actif .gy-tab-label{ color:var(--texte); }
  .gy-tab.gy-actif .gy-tab-rule{ background:var(--trait-fort); }
  .gy-tab:focus-visible{ outline:var(--trait-2) solid var(--action); outline-offset:calc(-1 * var(--e-0)); }

  /* .gy-session-bar-nav (quiz/review/exercise) — jamais eu de repli
     mobile : la piste de progression à largeur fixe (220px) plus le
     libellé Quitter plus le compteur débordaient et se chevauchaient
     sur 375px (bug réel trouvé en testant sur téléphone). */
  .gy-session-bar-nav{
    padding-left:var(--e-4);
    padding-right:var(--e-4);
    gap:var(--e-2);
  }
  .gy-session-exit{ flex:none; }
  .gy-session-progress{ flex:1; min-width:0; justify-content:flex-end; }
  .gy-session-progress .gy-session-bar{ width:auto; flex:1; min-width:44px; max-width:90px; }
}

/* ============================================================
   CIBLE TACTILE — mécanisme canonique, déclaré une seule fois
   Calé sur le dispositif de pointage, pas sur la largeur de viewport
   (design/…RECAP-passe-desktop.md, « Corrections post-vérification »
   §1 : une largeur de fenêtre ne dit rien du doigt vs. la souris — un
   Mac en demi-fenêtre à 936px reste piloté à la souris). Densité
   souris par défaut partout ; relevée à 44px minimum ici, sous
   pointeur grossier réel, quelle que soit la page. Deux usages :
     - .gy-cible : classe utilitaire, à poser sur toute cible qui n'est
       pas déjà un .gy-btn/.gy-tab dimensionné pour le tactile (champ de
       filtre, option de menu, bouton de matière à l'ingestion…) — la
       page fixe sa propre taille « souris » par défaut, ce bloc la
       recale sous pointeur grossier sans qu'elle ait à redéclarer la
       media query.
     - .gy-kbd et les liens/bouton d'en-tête ci-dessous : cibles déjà
       nommées par le système, mêmes règles, même bloc plutôt que
       répétées par écran.
   ============================================================ */
@media (hover:none) and (pointer:coarse){
  .gy-cible{ min-height:44px; min-width:44px; }
  .gy-app-header-nav a{ padding:var(--e-3) var(--e-4); }
  .gy-nav-home{ width:44px; height:44px; }
  .gy-kbd{ display:none; }
}

/* ============================================================
   CHARGEMENT INITIAL — attente courte et indéterminée
   Gabarit inerte (bandeau résumé, pas animé) + .gy-load (déjà défini
   plus haut dans ce fichier, jamais câblé avant ce lot) pour
   l'indicateur central — design/…États vides et chargement, écran
   « chargement ». data.js interroge neuf tables à chaque changement
   de page ; sans ceci la zone de contenu resterait vide pendant ce
   temps. Visible par défaut dans le HTML de chaque écran concerné,
   masqué au dataReady (JS de l'écran).
   ============================================================ */
.gy-loading-head{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--e-7); flex-wrap:wrap; padding-bottom:var(--e-6); margin-bottom:var(--e-6);
  border-bottom:var(--trait-1) solid var(--trait);
}
.gy-loading-head-main{ max-width:720px; width:100%; }
.gy-loading-bar{ border-radius:var(--r-controle); background:var(--surface-bloc); }
.gy-loading-head-main .gy-loading-bar:nth-child(1){ width:120px; height:var(--e-3); }
.gy-loading-head-main .gy-loading-bar:nth-child(2){ width:60%; height:var(--e-7); margin-top:var(--e-3); }
.gy-loading-head-main .gy-loading-bar:nth-child(3){ width:40%; height:var(--e-3); margin-top:var(--e-3); }
.gy-loading-head-side{ display:none; flex:none; width:280px; border-left:var(--trait-1) solid var(--trait); padding-left:var(--e-6); }
.gy-loading-head-side .gy-loading-bar:nth-child(1){ width:100%; height:var(--e-3); }
.gy-loading-head-side .gy-loading-bar:nth-child(2){ width:70%; height:var(--e-7); margin-top:var(--e-3); }
@media (min-width:1000px){ .gy-loading-head-side{ display:block; } }
.gy-loading-body{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--e-4); padding:var(--e-7) 0; min-height:320px;
}
.gy-loading-gauge{ width:360px; max-width:100%; }
.gy-loading-gauge span{ height:var(--e-5); background:var(--encre); border-color:var(--encre); }
.gy-loading-label{ font-family:var(--f-mono); font-size:var(--t-11); letter-spacing:.14em; text-transform:uppercase; color:var(--texte-3); margin:0; }

/* ============================================================
   ÉTAT VIDE — compte neuf
   Registre repris du message existant de planning.html : expliquer le
   mécanisme plutôt que constater l'absence, mener au dépôt d'un cours.
   Un seul gabarit partagé (js/gyre-chrome.js, gyEmptyState), posé sur
   bord/chapitres/planning/stats/erreurs plutôt que rédigé cinq fois —
   design/…États vides et chargement. Le cas du chapitre sans carte
   (jauge bloquée à 0/8) est traité séparément dans chapter.js : ce
   n'est pas un compte vide, ce gabarit ne s'y applique pas.
   ============================================================ */
.gy-empty-head{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--e-7); flex-wrap:wrap; padding-bottom:var(--e-7); margin-bottom:var(--e-6);
  border-bottom:var(--trait-1) solid var(--trait-fort);
}
.gy-empty-main{ max-width:680px; }
.gy-empty-title{
  font-family:var(--f-display); font-weight:700; font-size:var(--t-30);
  line-height:1.04; letter-spacing:-.03em; margin:0 0 var(--e-4);
}
.gy-empty-texte{ font-size:var(--t-19); line-height:1.75; color:var(--texte-2); margin:0 0 var(--e-6); }
.gy-empty-actions{ display:flex; gap:var(--e-3); flex-wrap:wrap; }
.gy-empty-figure{ display:none; flex:none; width:380px; border-left:var(--trait-1) solid var(--trait); padding-left:var(--e-6); }
.gy-empty-figure-gauge span{ height:var(--e-5); }
.gy-empty-figure-texte{ font-family:var(--f-mono); font-size:var(--t-11); line-height:1.8; color:var(--texte-3); margin:var(--e-4) 0 0; }
.gy-empty-steps{ display:grid; grid-template-columns:minmax(0,1fr); }
.gy-empty-step{ padding:var(--e-6) 0; border-top:var(--trait-1) solid var(--trait); }
.gy-empty-step-n{ font-family:var(--f-mono); font-size:var(--t-11); letter-spacing:.14em; color:var(--texte-3); margin:0 0 var(--e-3); }
.gy-empty-step-titre{ font-family:var(--f-display); font-weight:600; font-size:var(--t-22); letter-spacing:-.02em; margin:0 0 var(--e-2); }
.gy-empty-step-texte{ font-size:var(--t-13); line-height:1.8; color:var(--texte-2); margin:0; }
@media (min-width:1600px){
  .gy-empty-title{ font-size:var(--t-40); }
  .gy-empty-figure{ display:block; }
  .gy-empty-steps{ grid-template-columns:repeat(3,minmax(0,1fr)); }
  .gy-empty-step{ padding:var(--e-6); border-top:none; border-right:var(--trait-1) solid var(--trait); }
  .gy-empty-step:last-child{ border-right:none; }
}

/* ============================================================
   MENU DE COMPTE — ajout localisé (phase multi-comptes)
   Élément autonome en position fixe, indépendant de .gy-app-header et
   .gy-tabs ci-dessus : ne touche à aucune règle existante, pour ne pas
   interférer avec la refonte desktop en cours sur ce fichier (voir
   design/multi-comptes.md §6). Sélecteurs neufs uniquement.
   Un seul point d'entrée fixe (#gyreAccountControl) qui ouvre un menu,
   plutôt qu'un bouton par fonctionnalité : la phase 4 y ajoute feedback,
   consommation et mode debug dans .gy-account-dropdown, sans multiplier
   les éléments position:fixed dans le même coin.
   ============================================================ */
/* Bas-droite, pas haut-droite : #gyreSyncBanner (css ci-dessus, z-index 900)
   est sticky en haut de page et prend toute la largeur dès qu'un souci de
   synchro survient — un contrôle ancré en haut se retrouverait couvert et
   inatteignable pile quand un testeur pourrait avoir besoin de se
   déconnecter/reconnecter pour en sortir. Observé en testant le scénario 4
   du plan de test (design/multi-comptes.md) : le bouton disparaissait sous
   le bandeau dès qu'une écriture échouait. */
#gyreAccountControl{
  position:fixed;
  bottom:calc(var(--e-2) + env(safe-area-inset-bottom));
  right:var(--e-2);
  z-index:300;
}
/* Arrivé hier soir avec la fusion multi-comptes, jamais repris depuis
   (design/plan-2-jours-2026-08.md §3 : "à styler proprement... arrivé en
   production hier soir seulement"). Deux écarts par rapport au reste du
   chrome (nav, onglets, lien Quitter des barres de session) : la police
   texte au lieu de mono, et un fond surface-page — identique à celui de
   la page qu'il survole en position fixe, donc pratiquement invisible
   partout où il n'y a pas de contenu juste derrière pour le faire
   ressortir par contraste. Aligné sur le vocabulaire mono/majuscules déjà
   utilisé par .gy-session-exit et .gy-tab-label, fond surface-bloc (loge)
   pour rester lisible quel que soit ce qu'il survole. */
.gy-account-btn{
  font-family:var(--f-mono);
  font-size:var(--t-11);
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--texte-2);
  background:var(--surface-bloc);
  border:var(--trait-1) solid var(--trait);
  border-radius:var(--r-controle);
  padding:var(--e-2) var(--e-3);
  cursor:pointer;
}
.gy-account-btn:hover{ border-color:var(--trait-fort); color:var(--texte); }

/* Ouvre vers le HAUT (bottom:100%), pas vers le bas : le point d'ancrage
   est collé au bord inférieur du viewport, un menu qui s'ouvrirait vers le
   bas sortirait de l'écran. */
.gy-account-dropdown{
  position:absolute;
  bottom:calc(100% + var(--e-1));
  right:0;
  min-width:160px;
  background:var(--surface-page);
  border:var(--trait-1) solid var(--encre);
  border-radius:var(--r-bloc);
  padding:var(--e-1);
  display:flex;
  flex-direction:column;
}
.gy-account-dropdown.hidden{ display:none; }
.gy-account-menu-item{
  font-family:var(--f-texte);
  font-size:var(--t-13);
  color:var(--texte);
  background:none;
  border:none;
  border-radius:var(--r-controle);
  padding:var(--e-2);
  text-align:left;
  cursor:pointer;
}
.gy-account-menu-item:hover{ background:var(--surface-creux); }
.gy-account-quota{
  font-family:var(--f-texte);
  font-size:var(--t-11);
  color:var(--texte-2);
  padding:var(--e-1) var(--e-2) 0;
  margin:0;
}

/* Panneau feedback — repliable dans le menu, pas un nouvel élément fixe
   (phase 4, design/multi-comptes.md § Questions ouvertes). */
.gy-feedback-panel{
  display:flex;
  flex-direction:column;
  gap:var(--e-1);
  padding:var(--e-1);
  border-top:var(--trait-1) solid var(--trait);
  margin-top:var(--e-1);
}
.gy-feedback-panel.hidden{ display:none; }
.gy-feedback-textarea{
  font-family:var(--f-texte);
  /* 16px minimum, pas var(--t-13) : iOS Safari zoome automatiquement sur
     tout champ de saisie sous 16px, et le zoom résiduel rend ensuite la
     page déplaçable latéralement (un seul bug, deux symptômes, constatés
     sur iPhone dans la modale « Ajouter au chapitre » qui partage cette
     classe). Ne PAS répondre par maximum-scale=1 sur le viewport : ça
     casse le zoom à deux doigts et iOS l'ignore de toute façon. */
  font-size:16px;
  color:var(--texte);
  background:var(--surface-page);
  border:var(--trait-1) solid var(--trait);
  border-radius:var(--r-controle);
  padding:var(--e-1);
  resize:vertical;
  min-width:220px;
}
.gy-feedback-actions{ display:flex; justify-content:flex-end; gap:var(--e-1); }
.gy-feedback-status{ font-size:var(--t-11); color:var(--texte-2); margin:0; }
.gy-feedback-status.hidden{ display:none; }

@media (max-width:600px){
  /* Au-dessus de .gy-tabs (56px + inset, z-index 500 ci-dessus) plutôt que
     dessous : même raisonnement que ci-dessus, ne jamais finir masqué par
     un autre élément fixe. */
  #gyreAccountControl{
    bottom:calc(56px + env(safe-area-inset-bottom) + var(--e-2));
    right:var(--e-1);
  }
  .gy-account-btn{ padding:3px var(--e-1); font-size:var(--t-11); }
}
