/* ==========================================================================
   GeXpert — feuille de style unique
   ICT-GE · Genève — refonte « Registre » (fond bleu-ardoise pâle)
   PHP 8.5 / Apache · 100 % CSS, aucun script, aucune ressource externe
   Remplace app/assets/style.css. Aucun nom de classe existant n'a changé.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Jetons
   -------------------------------------------------------------------------- */
:root {
  /* Encres */
  --ink:            #14171c;   /* texte principal — 16.6:1 sur --paper */
  --ink-2:          #3c444f;   /* texte courant secondaire — 9.4:1 */
  --muted:          #6b7480;   /* texte tertiaire — 4.7:1 sur --paper */
  --faint:          #8b939e;   /* désactivé, jamais porteur de sens seul */

  /* Surfaces */
  --paper:          #ffffff;   /* panneaux, cartes, tableaux */
  --paper-2:        #fafbfc;   /* en-têtes de tableau, lignes alternées */
  --surface:        #e9eff5;   /* fond de page — bleu-ardoise pâle */
  --surface-2:      #f1f5f9;   /* creux : champs en lecture seule, zones inertes */
  --line:           #d3dde6;   /* filet structurant */
  --line-soft:      #eceff3;   /* filet interne, séparateur de ligne */

  /* Action */
  --accent:         #1b4fd8;   /* 6.7:1 sur --paper */
  --accent-ink:     #163fae;   /* survol / actif */
  --accent-soft:    #eaeefb;
  --accent-line:    #c3cdf2;

  /* États — réservés : jamais décoratifs */
  --ok:             #0e7c4a;
  --ok-ink:         #0b6640;   /* texte sur --ok-soft */
  --ok-soft:        #e6f4ed;
  --ok-line:        #bfe0cf;
  --warn:           #b3261e;
  --warn-ink:       #9f1f18;   /* texte sur --warn-soft — 6.8:1 */
  --warn-soft:      #fdeceb;
  --warn-line:      #e6c9c6;
  /* Ambre franc plutôt que brun : 5.02:1 sur blanc, 4.85:1 sur la ligne
     alternée, 4.61:1 sur --wait-soft — au-dessus de 4.5:1 partout où il sert.
     Un orange plus vif (#c2620a) tombait à 3.82:1 sur la pastille. */
  --wait:           #b45309;   /* ambre : en attente / à activer */
  --wait-soft:      #fff4e0;
  --wait-line:      #ecd9b4;

  /* Deux tons de plus, pour les tuiles du tableau de bord seulement
     (2026-08-12, choix de l'utilisateur) : cinq chiffres côte à côte se
     distinguent mal avec quatre couleurs, dont deux proches. Ils reprennent les
     teintes déjà éprouvées des pastilles de métier — 5,4:1 et 7,4:1 sur blanc.
     Ils ne portent **aucun état** : ce sont des repères, pas des alertes. */
  --turquoise:      #0b6b6b;
  --turquoise-soft: #e0f2f2;
  --mauve:          #6b2fb3;
  --mauve-soft:     #f0e9fa;

  /* Gris des barres de titre teintées du tableau de bord. --muted y tombe à
     4.14:1 sur --warn-soft et 4.38:1 sur --accent-soft ; celui-ci tient 5.24:1
     et 5.17:1. Réservé à cet usage — sur blanc, --muted reste le bon gris. */
  --muted-sur-teinte: #5c646f;

  /* Échelle d'espacement (base 4) — utilisée partout dans ce fichier.
     Les marges inline restées dans le PHP continuent de fonctionner. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 20px; --s6: 24px; --s7: 32px; --s8: 48px;

  /* Rayons */
  --radius:    6px;   /* panneaux, cartes, champs */
  --radius-sm: 4px;   /* boutons de tableau, pastilles carrées */
  --radius-pill: 999px;

  /* Élévation — très discrète, jamais deux niveaux côte à côte */
  --lift-1: 0 1px 2px rgba(20, 23, 28, .05);
  --lift-2: 0 4px 16px rgba(20, 23, 28, .10);

  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, Helvetica, Arial, sans-serif;
}

/* --------------------------------------------------------------------------
   2. Réglages de base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* `hidden` doit vraiment cacher. Le navigateur ne le pose qu'en `display: none`
   de feuille par défaut, que n'importe quelle règle d'auteur emporte : une barre
   `.form-actions` en `display: flex` restait affichée alors qu'un script venait
   de la marquer `hidden`. C'est ce qui laissait à l'écran le bouton « Déposer
   les pièces choisies », pourtant devenu inutile depuis que le dépôt est
   immédiat (2026-08-08). */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  /* La grille de 32 px est abandonnée : elle bruitait les tableaux denses.
     Le fond est désormais un aplat, la structure vient des filets. */
  background: var(--surface);
  color: var(--ink);
  font: 15px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

h1, h2, h3 { margin: 0; letter-spacing: -.015em; }
h1 { font-size: 24px; font-weight: 650; }
h2 { font-size: 17px; font-weight: 620; margin-top: var(--s7); }
h3 { font-size: 15px; font-weight: 600; }
h2:first-child, h3:first-child { margin-top: 0; }

p { margin: 0 0 var(--s3); }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-ink); text-decoration: underline; }

hr, .divider {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--s5) 0;
}

.eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}
.small { font-size: 13px; color: var(--muted); }
.mono  { font-family: var(--mono); }

/* --------------------------------------------------------------------------
   3. Gabarit public — .shell / .card
   -------------------------------------------------------------------------- */
.shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s6);
  padding: var(--s8) var(--s5);
}

/* Sceau du collège, discret, en pied des pages publiques.
   Aucune modification du HTML nécessaire. */
.shell::after {
  content: "";
  display: block;
  width: 28px;
  height: 28px;
  background: url("logo-college.png") no-repeat center / contain;
  opacity: .5;
}

.card {
  width: 100%;
  max-width: 420px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--lift-1);
  padding: var(--s6) var(--s7) var(--s6);
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}
.card--wide { max-width: 900px; }

/* Logo GeXpert en tête de carte : le h1 de la carte porte le logo et son
   texte reste dans l'arbre d'accessibilité. */
.card > h1:first-child,
.card > .card-brand {
  /* Le logo fait 400 × 123 : à 200 px de large il occupe 62 px de haut.
     En dessous, le bas de l'image — la toque — est rogné. */
  min-height: 62px;
  margin: 0 0 calc(var(--s1) * -1);
  font-size: 0;
  /* Centré (2026-08-13, demande de l'utilisateur) : la carte de connexion est
     elle-même centrée dans la page, un logo calé à gauche y penchait. */
  background: url("logo-gexpert.png") no-repeat center / 200px auto;
}

.steps {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}
.steps > * { display: flex; align-items: center; gap: var(--s2); }
.steps > * + *::before { content: "›"; color: var(--faint); }

.linkline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--s3);
  font-size: 13px;
}
.linkline > * + *::before {
  content: "";
  display: inline-block;
  width: 1px;
  height: 12px;
  margin-right: var(--s3);
  background: var(--line);
  vertical-align: -2px;
}

/* --------------------------------------------------------------------------
   4. Gabarit applicatif — .app-shell / .sidebar / .app-content
   -------------------------------------------------------------------------- */
/* Sa boîte est normalisée avec les autres cases pilotes (section « Formulaires »). */

.app-shell {
  display: flex;
  align-items: flex-start;
  min-height: 100vh;
}

.sidebar {
  position: sticky;
  top: 0;
  flex: none;
  width: 236px;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border-right: 1px solid var(--line);
}

/* Logo de l'application. Le texte « GeXpert » du HTML reste présent pour les
   lecteurs d'écran (font-size: 0 ne le retire pas de l'arbre). */
.sidebar-brand {
  display: block;
  /* 184 px de large → 57 px de haut pour un logo de 400 × 123. Le conteneur
     doit suivre, sinon l'image est rognée par le bas. */
  min-height: 58px;
  margin: var(--s4) var(--s5) var(--s3);
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
  font-size: 0;
  background: url("logo-gexpert.png") no-repeat left center / 184px auto;
}

/* Session de travail, rappelée sous le logo : elle doit se lire sans la
   chercher, c'est ce qui évite de se tromper d'année. */
.sidebar-session {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0 var(--s5) var(--s3);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-2);
}

.sidebar-session-label {
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}

.sidebar-session-value {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

.sidebar-session-value--none { color: var(--warn); }

/* **Resserré pour tenir d'un seul tenant** (2026-08-20). Cinq groupes et une
   quinzaine d'entrées ne tenaient pas sur un écran de portable : le bandeau
   défilait, et chaque navigation le ramenait à son sommet — il fallait
   redescendre pour l'entrée suivante (signalé par l'utilisateur).

   Plutôt que de mémoriser sa position, on lui évite de défiler : vingt pixels
   entre les groupes en deviennent douze, et chaque entrée gagne quatre pixels
   de hauteur. Une centaine de pixels regagnés, sans rien retirer du menu.

   Sous 860 px le bandeau devient un tiroir plein écran : la place ne manque
   plus, et les entrées reprennent leur confort — on les touche du doigt. */
.sidebar-nav {
  flex: 1;
  min-height: 0;          /* piège Flexbox : ne pas retirer */
  overflow-y: auto;
  padding: var(--s2) var(--s3) var(--s4);
  display: flex;
  flex-direction: column;
  /* Le gap se resserre : c'est le filet qui sépare désormais, et l'espace
     au-dessus de lui suffit à respirer. */
  gap: var(--s2);
}

.sidebar-group { display: flex; flex-direction: column; gap: 2px; }
/* **Un filet sépare les groupes** (2026-08-20, troisième essai). Foncer les
   intitulés les faisait rivaliser avec la page ouverte ; les éclaircir les
   rendait indistincts des entrées. La casse et l'espacement ne suffisent pas
   quand tout est du texte gris de taille voisine.
   La structure se marque donc par un **trait**, pas par de l'encre : un
   intitulé n'a plus à crier pour qu'on voie où commence son groupe, et la
   page ouverte reste seule à parler en bleu. */
.sidebar-group + .sidebar-group {
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}

/* Trois niveaux, trois poids, et ils ne doivent pas se disputer l'œil
   (2026-08-20, deux allers-retours avec l'utilisateur) :

   1. **la page ouverte** domine — c'est la seule chose qu'on cherche en
      arrivant, et elle parle en bleu ;
   2. **les entrées** viennent ensuite, en encre courante ;
   3. **les intitulés de groupe** ferment la marche : ils se distinguent par
      la casse et l'espacement, pas par la force de l'encre.

   Ils étaient d'abord en `--faint`, la teinte du désactivé — invisibles. Puis
   en `--ink-2`, où ils rivalisaient avec la page ouverte. `--muted` les rend
   lisibles (4,7:1) sans qu'ils prennent le pas sur elle. */
.sidebar-group-label {
  padding: 0 var(--s2) var(--s1);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}

.sidebar-link {
  display: block;
  padding: 6px var(--s3);
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  color: var(--ink-2);
  transition: background-color .12s ease, color .12s ease;
}
.sidebar-link:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
/* La page ouverte **parle en bleu** : elle avait le fond de l'accent mais
   l'encre de tout le monde, et se laissait rattraper par le premier intitulé
   un peu appuyé. Le texte prend la couleur de l'accent, le filet de gauche
   s'épaissit, la graisse monte d'un cran — trois signaux qui vont dans le même
   sens plutôt qu'un fond qu'on remarque à peine. */
.sidebar-link.active {
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-weight: 650;
  box-shadow: inset 3px 0 0 var(--accent);
}

.sidebar-user {
  padding: var(--s3) var(--s4) var(--s4);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.sidebar-user-name { font-size: 13px; font-weight: 560; color: var(--ink); }
.sidebar-user-role { font-size: 11px; color: var(--muted); }

.sidebar-logout {
  display: block;
  margin-top: var(--s1);
  padding: var(--s2) var(--s3);
  /* Neutre, pas rouge : se déconnecter ne détruit rien. Le rouge reste
     réservé aux erreurs et aux actions irréversibles. */
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  font-size: 12.5px;
  color: var(--ink-2);
  text-align: center;
}
.sidebar-logout:hover { background: var(--surface-2); text-decoration: none; }

/* Sceau du collège en pied de menu, avec sa raison sociale. */
/* La version, sous le nom du collège : plus discrète encore que lui — on ne
   la lit qu'en la cherchant. */
.sidebar-version {
  margin: 4px 0 0;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--muted);
  opacity: .75;
}

.sidebar-college {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: var(--s3) 0 0;
  padding-top: var(--s3);
  border-top: 1px solid var(--line-soft);
  font-size: 11px;
  line-height: 1.35;
  color: var(--muted);
}
.sidebar-college::before {
  content: "";
  flex: none;
  width: 26px;
  height: 26px;
  background: url("logo-college.png") no-repeat center / contain;
  opacity: .55;
}

.sidebar-burger {
  display: none;
  position: fixed;
  top: var(--s3);
  left: var(--s3);
  z-index: 20;
  width: 38px;
  height: 38px;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--lift-1);
  font-size: 16px;
  color: var(--ink);
  cursor: pointer;
}

.app-content {
  flex: 1;
  min-width: 0;
  padding: var(--s6) var(--s7) var(--s8);
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}

/* --------------------------------------------------------------------------
   5. Entête de page — .topbar
   -------------------------------------------------------------------------- */
.topbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s6);
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--line);
}
.topbar h1 { font-size: 24px; }
.topbar nav,
.topbar .topbar-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}

/* --------------------------------------------------------------------------
   6. Panneaux — hiérarchie à trois niveaux
   -------------------------------------------------------------------------- */
.panel {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s5);
}

/* Un panneau à en-tête : le premier h2/.eyebrow devient une barre à filet.
   C'est ce qui remplace la pile de panneaux de même poids. */
.panel > h2:first-child,
.panel > .eyebrow:first-child,
.panel > .panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin: calc(var(--s5) * -1) calc(var(--s5) * -1) var(--s5);
  padding: var(--s3) var(--s5);
  border-bottom: 1px solid var(--line-soft);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Niveau 1 : le panneau porteur de la page (frise, dossier). */
.panel--primary { box-shadow: var(--lift-1); }
/* Niveau 3 : contexte, historiques, notes. */
.panel--quiet { background: var(--surface-2); border-style: dashed; }

.panel + .panel { margin-top: 0; }     /* l'espacement vient du gap parent */

/* … sauf dans un formulaire, qui est un enfant unique de `.app-content` : ses
   panneaux se touchaient, faute que le `gap` du parent ne les sépare. */
form > .panel + .panel { margin-top: var(--s5); }
.panel > :last-child { margin-bottom: 0; }

/* Grille de deux colonnes pour les pages denses (dossier candidat). */
.panel-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: var(--s5);
  align-items: start;
}
.panel-col { display: flex; flex-direction: column; gap: var(--s5); }

/* --------------------------------------------------------------------------
   7. Panneaux repliables — case à cocher masquée, aucun script
   -------------------------------------------------------------------------- */


.toggle-panel-label,
.toggle-experts-bouton {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  font-size: 12.5px;
  color: var(--accent);
  cursor: pointer;
  transition: background-color .12s ease;
}
.toggle-panel-label:hover,
.toggle-experts-bouton:hover { background: var(--accent-soft); }

.toggle-panel-body {
  display: none;
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
}
.toggle-panel-input:checked ~ .toggle-panel-body { display: block; }

/* Repère d'ouverture porté par le libellé lui-même. */
.toggle-panel-input:checked ~ .toggle-panel-label,
.toggle-panel-input:checked ~ * .toggle-panel-label,
.toggle-panel-input:checked ~ .toggle-experts-bouton {
  background: var(--accent-soft);
}

/* Contractuel — vérifié mot pour mot dans le CSS servi.

   **La modification se fait sur place** (2026-08-20). Le formulaire vivait en
   pied de carte : modifier un expert demandait de descendre bien plus bas que
   la ligne qu'on voulait changer. Chaque place porte désormais deux blocs au
   même endroit — la ligne qu'on lit, le champ qu'on remplit — et la case à
   cocher échange l'un pour l'autre. Rien n'est déplacé, rien ne saute : le
   champ occupe exactement l'emplacement du nom qu'il remplace. */
/* `.field.college-modif` et non `.college-modif` seul : `.field { display:
   flex }` est déclarée deux cents lignes plus bas, à poids égal, et l'emportait
   donc — les listes déroulantes restaient visibles en permanence, sous chaque
   nom d'expert (signalé par l'utilisateur, 2026-08-20). Deux classes valent
   mieux qu'une : la règle gagne quel que soit l'ordre du fichier. */
.college-modif,
.field.college-modif { display: none; }
#toggle-experts:checked ~ .panel .college-lecture { display: none; }
#toggle-experts:checked ~ .panel .college-modif { display: block; }
#toggle-experts:checked ~ .panel .toggle-experts-bouton { background: var(--accent-soft); }
/* Le bouton dit ce qu'il fera : « Modifier » quand on lit, « Annuler » quand
   on modifie. Deux libellés dans le même bouton, un seul visible. */
#toggle-experts:checked ~ .panel .quand-replie { display: none; }
#toggle-experts:checked ~ .panel .quand-deplie { display: inline; }
/* Le champ de modification se resserre : quatre places de suite ne doivent pas
   creuser la carte plus que les quatre lignes qu'elles remplacent. La colonne
   du dossier est étroite, et un champ pleine hauteur y paraît énorme
   (2026-08-20). Réduit **ici seulement** : les formulaires de l'application
   gardent leur taille, on ne rapetisse pas une saisie qu'on remplit longtemps
   pour un choix qu'on fait en un clic. */
.college-modif.field + .college-modif.field { margin-top: var(--s3); }
.college-modif.field { gap: 2px; }
.college-modif label { font-size: 11.5px; }
.college-modif .champ-select { padding: 6px 9px; font-size: 13.5px; }

/* Une place vide se lit comme les autres : elle a sa ligne, son rôle, et un
   tiret là où il y aurait des initiales. Elle n'apparaissait nulle part
   auparavant — seul le décompte la signalait (2026-08-20). */
.college-ligne--vide .college-nom--vide { color: var(--muted); font-style: italic; }
.college-pastille--vide {
  background: var(--surface-2);
  color: var(--faint);
  border: 1px dashed var(--line);
}

/* Le report des dates d'examen. Il était replié à l'intérieur de la carte
   « Examen » ; il vit dans une **fenêtre** depuis le 2026-09-08 — six champs
   ouverts sous une carte d'appoint prenaient la moitié de la colonne, pour un
   geste qu'on fait deux fois par dossier. Plus de dépli, donc plus de règle
   d'affichage : la modale s'en charge. */
.panel--report-dates { margin: 0; }
/* Le carré de la date ouvre la fenêtre : c'est le chemin le plus court — on
   regarde la date, on clique dessus, on la change. Vide, il l'ouvre aussi. */
.date-puce--ouvre { cursor: pointer; }
.date-puce--ouvre:hover { border-color: var(--accent-line); background: var(--accent-soft); }
.date-puce--ouvre:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dates-ouvrir { width: 100%; margin-top: 14px; display: block; text-align: center; }

/* L'acquittement de la prise de contact, replié de la même façon (2026-08-14).
   **Sa propre classe, et pas celle des dates** : le sélecteur de frère général
   atteint tous les panneaux suivants, si bien qu'un formulaire partagé se
   serait ouvert à deux endroits — et le bouton « Corriger la date » n'ouvrait
   rien du tout, faute d'une règle ancrée sur son propre identifiant. */
/* Il vit dans une **fenêtre** depuis le 2026-09-08, comme celui des dates de
   l'examen : deux cartes voisines qui portent chacune une date n'ont aucune
   raison de se manipuler autrement, et le carré ouvre les deux. Plus de dépli,
   donc plus de règle d'affichage ni de bascule. */
.panel--contact-form { margin: 0; }

/* La séance du collège se corrige derrière son bouton (B7, 2026-08-14) : même
   dessin que les deux précédents, et sa propre règle pour la même raison. */
.panel--seance-form {
  display: none;
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
}
#toggle-seance:checked ~ .panel .panel--seance-form { display: block; }
#toggle-seance:checked ~ .panel .toggle-seance-bouton { background: var(--accent-soft); }

/* Le pointage : trente lignes, trois colonnes de cases. La cellule entière est
   cliquable — sur trente lignes, viser un rond de 13 px se paie. */
.table--pointage td:not(:first-child),
.table--pointage th:not(:first-child) { width: 110px; }
.pointage-case {
  display: block;
  padding: var(--s2) 0;
  cursor: pointer;
  border-radius: 6px;
}
.pointage-case:hover { background: var(--surface-2); }
.pointage-case input { cursor: pointer; }
/* La ligne prend la couleur de ce qui est déjà pointé : sur trente lignes, on
   cherche les exceptions, pas la règle. */
.pointage-ligne--absent { background: var(--warn-soft); }
.pointage-ligne--excuse { background: var(--surface-2); }

/* La colonne « Précision » porte deux champs, et la case cochée décide lequel
   paraît (2026-08-14) : les heures siégées pour un présent arrivé en retard, le
   motif pour un excusé. Toujours visibles, ils feraient soixante cases vides
   sur trente lignes ; c'est `:has()` qui les révèle, comme le masquage des
   champs « expert » du formulaire de compte. */
.pointage-heures, .pointage-motif { display: none; align-items: center; gap: var(--s2); }
.pointage-ligne:has(input[value="present"]:checked) .pointage-heures { display: flex; }
.pointage-ligne:has(input[value="excuse"]:checked) .pointage-motif { display: flex; }
.pointage-precision input[type="number"] { width: 76px; }
.pointage-precision input[type="text"] { width: 100%; min-width: 160px; }
.pointage-precision input { font-size: 12px; padding: 4px 8px; }
.pointage-unite { font-size: 11px; color: var(--muted); white-space: nowrap; }
/* `td.` et non `.` seule : la règle des colonnes ci-dessus porte un
   `:not(:first-child)` qui pèse autant, et la dernière déclarée l'emporterait
   sur une spécificité inférieure. */
.table--pointage td.pointage-precision { width: auto; min-width: 220px; }

/* Masquage des champs « expert » quand le rôle choisi est Formateur.
   Contractuel — vérifié dans admin.php et admin-edit.php. */
#create-account-grid:has(#role option[value="formateur"]:checked) .expert-only { display: none; }
#edit-account-grid:has(#role option[value="formateur"]:checked) .expert-only { display: none; }

/* --------------------------------------------------------------------------
   8. Boutons
   -------------------------------------------------------------------------- */
button, .btn {
  display: block;
  width: 100%;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: #fff;
  font: 560 14px/1.2 var(--sans);
  text-align: center;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
button:hover, .btn:hover {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  color: #fff;
  text-decoration: none;
}

.btn--ghost {
  background: var(--paper);
  border-color: var(--accent-line);
  color: var(--accent);
}
.btn--ghost:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }

.btn--danger {
  background: var(--paper);
  border-color: var(--warn-line);
  color: var(--warn);
}
.btn--danger:hover { background: var(--warn-soft); border-color: var(--warn); color: var(--warn-ink); }

/* **Deux boutons qui portent un état** (2026-09-17). Le vert et le rouge très
   pâle des cartes, appliqués à un bouton : il ne s'agit pas d'une alerte ni
   d'une action dangereuse, mais de ce que le bouton mène à voir — une grille
   achevée, ou une grille qui attend. Les teintes sont celles des `evt-ok` et
   `evt-warn` de la même page, pour qu'un écran ne parle pas deux langues. */
.btn--complet {
  background: var(--ok-soft);
  border-color: var(--ok-line);
  color: var(--ok-ink);
}
.btn--complet:hover { background: var(--ok); border-color: var(--ok); color: #fff; }

.btn--incomplet {
  background: var(--warn-soft);
  border-color: var(--warn-line);
  color: var(--warn-ink);
}
.btn--incomplet:hover { background: var(--warn); border-color: var(--warn); color: #fff; }

.btn--inline {
  display: inline-block;
  width: auto;
  padding: var(--s1) var(--s3);
  font-size: 13px;
  line-height: 1.5;
}

.btn--neutral {
  background: var(--paper);
  border-color: var(--line);
  color: var(--ink-2);
}
.btn--neutral:hover { background: var(--surface-2); border-color: var(--faint); color: var(--ink); }

button:disabled, .btn[aria-disabled="true"] {
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--faint);
  cursor: not-allowed;
}

/* Barre de filtres : l'actif en plein, les autres à contour. */
.filtres {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
}
.filtres + .filtres { margin-top: var(--s2); }
.filtre-actif { /* alias sémantique : mêmes règles que le bouton plein */ }

/* --------------------------------------------------------------------------
   9. Formulaires
   -------------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: var(--s1); }
.field + .field { margin-top: var(--s4); }

/* Des champs en grille se passent de cette marge : c'est `gap` qui les espace,
   et la règle ci-dessus décalait le second champ de chaque rangée d'un demi-cran
   vers le bas — « Prénom » et « Nom » ne s'alignaient plus (signalé par
   l'utilisateur sur la fenêtre de modification d'un candidat, 2026-08-12).

   La classe ne pose que le remède : la grille et ses colonnes restent écrites
   là où elles sont, chaque formulaire n'ayant pas le même nombre de colonnes. */
.grille-champs .field + .field { margin-top: 0; }

/* Le rythme du formulaire ne venait que de .field + .field : tout ce qui
   suit un champ sans en être un — un bouton, une rangée d'actions — se
   retrouvait collé au champ précédent. */
.field + button,
.field + .btn,
/* Les actions ne collent jamais à ce qui précède, quel que soit le dernier
   élément du formulaire : champ, grille, paragraphe d'explication ou message
   d'avertissement. La marge est portée par la barre elle-même plutôt que par
   un sélecteur d'adjacence, qui ne couvrait que le cas du champ. */
.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;   /* un « Annuler » en label garde la ligne d'un bouton */
  gap: var(--s2);
  margin-top: var(--s4);
}

/* Lot E de la campagne de tests (2026-11-01) : la correction des experts
   (T10), la validation de la note (T11), la reprise de l'entretien (T13). */
.eval-correction {
  margin: var(--s2) 0 0;
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--wait);
  background: var(--surface-2);
  font-size: 13px;
}
.eval-correction-motif { display: block; margin-top: 2px; color: var(--ink-2); }
.eval-corriger { margin-top: var(--s2); font-size: 13px; }
.eval-corriger > summary { cursor: pointer; color: var(--muted); }
.eval-corriger .eval-niveaux { margin: var(--s2) 0; }
.eval-reprise { margin: 2px 0 0; color: var(--muted); }
.validation-liste { list-style: none; margin: 0; padding: 0; }
.validation {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s2);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line-soft);
}
.validation:last-child { border-bottom: 0; }
.validation-qui { font-weight: 560; }
.preparation-questions { margin: 0; }
.preparation-questions dt { color: var(--muted); margin-top: var(--s2); }
.preparation-questions dd { margin: 2px 0 0; }

/* Les boutons des longues pages restent en bas de l'écran (T28, campagne de
   tests, 2026-11-01) : « on a tendance à les oublier sur les longues pages ».
   `sticky` et non `fixed` : la barre reste dans son formulaire, colle au bas
   de la fenêtre tant qu'on le parcourt, et reprend sa place à la fin — elle ne
   flotte jamais par-dessus la suite de la page. Dans une fenêtre modale, c'est
   la fenêtre qui défile : elle colle au bas de celle-ci. Sans script. */
.form-actions--collantes {
  position: sticky;
  bottom: 0;
  z-index: 5;
  margin-left: calc(-1 * var(--s3));
  margin-right: calc(-1 * var(--s3));
  padding: var(--s3);
  border-top: 1px solid var(--line-soft);
  background: var(--paper);
  box-shadow: 0 -6px 12px -10px rgba(20, 23, 28, .25);
}
/* La grille d'évaluation porte déjà son bouton dans le panneau qui suit le
   défilement, sur grand écran. Sa barre du bas ne colle donc que là où ce
   panneau redevient statique — sous 900px, comme lui. */
@media (min-width: 901px) {
  .form-actions--collantes-etroit { position: static; box-shadow: none;
                                    border-top: 0; margin-left: 0; margin-right: 0;
                                    padding: 0; }
}

label { font-size: 13px; font-weight: 560; color: var(--ink-2); }

input, select, textarea {
  width: 100%;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  color: var(--ink);
  font: 14px/1.4 var(--sans);
  transition: border-color .12s ease, box-shadow .12s ease;
}
/* ---------- Les cases à cocher qui pilotent le CSS
   Bandeau latéral, panneaux repliables, blocs métier, onglets, filtres : le
   projet se passe de script en cachant une case et en la lisant par sélecteur
   de voisinage. Ces cases ne doivent occuper **aucune** place.

   `position: absolute` n'y suffit pas : la règle ci-dessus leur donne
   `width: 100%`, et une case absolue aussi large que la page débordait de 4 px
   sur la droite. La barre horizontale ainsi provoquée rognait 15 px de hauteur
   de fenêtre, que le `min-height: 100vh` du shell ne savait pas rendre — d'où
   une barre verticale, elle aussi, sur **toutes** les pages, même les plus
   vides (relevé et mesuré le 2026-08-11).

   Elles gardent 1 px et ne sont pas masquées par `display: none` : l'anneau de
   focus du clavier se pose dessus, et `display: none` les retirerait de
   l'arbre d'accessibilité. */
/* **Sans `top` ni `left`** (2026-08-20). Épinglées en 0,0, ces cases se
   posaient en haut de la page ; cliquer leur libellé les met au point, et le
   navigateur y faisait défiler — on perdait sa place à chaque « Modifier les
   experts », à chaque onglet, à chaque bloc replié (signalé par
   l'utilisateur).

   Une position absolue sans décalage laisse l'élément **là où il serait dans
   le flux** : hors de la mise en page, mais à sa place. Le défilement ne bouge
   plus, et l'anneau de focus reste au bon endroit pour le clavier. */
.sidebar-toggle-input,
.toggle-panel-input,
.metier-bascule,
.tdb-onglet-input,
.tdb-metier-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
}
.sidebar-toggle-input,
.toggle-panel-input,
.metier-bascule { pointer-events: none; }

input::placeholder, textarea::placeholder { color: var(--faint); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
input[readonly], input:disabled, select:disabled {
  background: var(--surface-2);
  color: var(--muted);
}
.field-hint { font-size: 12px; color: var(--muted); }
.field-error { font-size: 12px; color: var(--warn-ink); }

.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s4);
}

/* Les séances de la session : « Séance du collège d'experts » tient sur une
   ligne, « Séance d'information des formateurs TPI » sur deux, et les champs
   de date se retrouvaient à des hauteurs différentes (signalé par
   l'utilisateur, 2026-08-10). L'intitulé absorbe l'espace libre — les champs
   reposent alors sur la même ligne, quelle que soit la longueur des mots. */
.grille-seances .field > label { margin-bottom: auto; }

/* Chaque séance porte maintenant un nom et une date (2026-08-14) : les deux
   champs vont ensemble, sur une carte discrète, sinon la grille alignerait le
   nom de l'une avec la date de l'autre. */
/* Quatre séances tiennent sur une ligne dès que l'écran le permet ; en dessous,
   la grille retombe à deux, puis à une. */
.grille-seances { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* La corbeille se pose au pied de la carte, poussée en bas pour que les
   séances d'une même ligne l'alignent quelle que soit leur hauteur. */
.seance-retrait { margin: auto 0 0; }

/* La feuille d'indemnités : les séances constatées au pointage du Chef Expert
   ne se modifient pas, et se lisent comme telles — grisées, sans champ. */
.ligne-verrouillee { background: var(--surface-2); color: var(--muted); }
.ligne-verrouillee td { color: var(--muted); }

/* La feuille visée des enseignants : une bande sous le tableau, et non un
   panneau dans le panneau — c'est une pièce jointe, pas une section. */
.bloc-vise {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--line-soft);
}
.bloc-vise-form { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }

/* Le champ de dépôt **passe au vert dès qu'un fichier est choisi**, avant même
   d'avoir cliqué (2026-08-17, demande de l'utilisateur). Aucun script : le
   champ porte `required`, il est donc `:invalid` tant qu'il est vide et
   `:valid` dès qu'un fichier est là — c'est exactement l'information
   cherchée. */
.bloc-vise-form .piece-champ {
  margin: 0;
  padding: var(--s2);
  font-size: 12px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--paper);
}
.bloc-vise-form .piece-champ:valid {
  background: var(--ok-soft);
  border-color: var(--ok-line);
}

/* La ligne du document déposé : son nom à gauche, la corbeille au bout — elle
   porte sur ce qui est là, pas sur ce qu'on va déposer. */
.ligne-document {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
}

/* « Valider la ligne », **en fin de ligne** : la saisie se termine par le
   temps, à droite ; ramener la souris au début pour valider ne se justifiait
   pas (2026-08-14). La ligne de saisie se distingue par un filet tireté. */
.btn-valider-ligne { padding: 4px var(--s3); font-size: 12px; white-space: nowrap; }
.ligne-ajout td { border-top: 1px dashed var(--line); }

.seance-champ {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-2);
}

/* Grille en lecture seule (profil) */
.readonly-grid {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: var(--s2) var(--s4);
  font-size: 14px;
}
.readonly-grid > dt { font-size: 13px; color: var(--muted); }
.readonly-grid > dd { margin: 0; color: var(--ink); }

/* --------------------------------------------------------------------------
   10. Alertes
   -------------------------------------------------------------------------- */
[class^="alert"], [class*=" alert"] {
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  font-size: 13.5px;
  color: var(--ink-2);
}
.alert--ok    { border-color: var(--ok-line);   background: var(--ok-soft);   color: var(--ok-ink); }
.alert--error { border-color: var(--warn-line); background: var(--warn-soft); color: var(--warn-ink); }
.alert--info  { border-color: var(--accent-line); background: var(--accent-soft); color: var(--accent-ink); }
/* Un lien dans une alerte garde la couleur de l'alerte : le bleu du lien sur le
   rose d'une alerte rouge tirait l'œil hors du message qu'il sert. */
[class^="alert"] a, [class*=" alert"] a { color: inherit; text-decoration: underline; }

/* --------------------------------------------------------------------------
   11. Tableaux — le cœur du travail quotidien
   -------------------------------------------------------------------------- */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

/* Un tableau posé directement dans un panneau va bord à bord. */
.panel > table { margin: 0 calc(var(--s5) * -1) calc(var(--s5) * -1); width: calc(100% + var(--s5) * 2); }
/* …sauf s'il est suivi de quelque chose : la marge basse négative sert à
   coller au bord du panneau, et faisait passer le contenu suivant par-dessus
   la dernière ligne du tableau (signalé par l'utilisateur, 2026-08-17). */
.panel > table:not(:last-child) { margin-bottom: var(--s4); }

th {
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--line-soft);
  background: var(--paper-2);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: left;
  white-space: nowrap;
}
td {
  padding: var(--s3);
  border-bottom: 1px solid var(--line-soft);
  color: var(--ink-2);
  vertical-align: middle;
}
th:first-child, td:first-child { padding-left: var(--s5); }
th:last-child,  td:last-child  { padding-right: var(--s5); text-align: right; }

tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--paper-2); }

/* La première colonne porte l'identité : elle est plus lourde. */
td:first-child { color: var(--ink); font-weight: 560; }

/* Dates, heures, versions : chiffres alignés. */
td.num, td .mono, .col-date { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* Ligne courante d'un dossier (version en cours de validation). */
/* Un seul filet, à gauche de la ligne — pas un par cellule. */
tr.is-courante td { background: var(--accent-soft); }
tr.is-courante td:first-child { box-shadow: inset 2px 0 0 var(--accent); }

/* ---------- Dossier candidat ---------- */

.dossier-entete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  margin-bottom: var(--s5);
}

/* Quand l'en-tête porte plus d'un bouton (2026-09-17, le PDF de la grille à
   côté du retour au dossier) : côte à côte, et qui passe à la ligne sur un
   écran étroit passe avec les autres. */
.dossier-entete-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
}

.dossier-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

.dossier-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }

/* Tableau des versions : la première colonne porte l'identité de la version,
   les cellules s'alignent en haut parce que les lignes sont hautes. */
.table--versions td { vertical-align: top; }
.table--versions th:nth-child(1), .table--versions td:nth-child(1) { width: 42%; }
.table--versions th:nth-child(2), .table--versions td:nth-child(2) { width: 22%; }
.table--versions th:nth-child(3), .table--versions td:nth-child(3) { width: 20%; }
.table--versions .version-fichier { color: var(--muted); word-break: break-word; }
.table--versions .version-commentaire { font-style: italic; }

/* Les deux dernières colonnes serrées l'une contre l'autre : « Dossier » et
   « Actions » ne portent que des boutons, elles n'ont aucune raison de prendre
   de la largeur. Sans `width: 1%`, le navigateur répartit l'espace restant
   entre toutes les colonnes et le crayon se retrouve à l'autre bout de la
   ligne, loin du bouton qu'il accompagne. La place regagnée revient aux
   colonnes de texte, qui en manquaient. */
.table--actions-serrees th:last-child,
.table--actions-serrees td:last-child,
.table--actions-serrees th:nth-last-child(2),
.table--actions-serrees td:nth-last-child(2) {
  width: 1%;
  white-space: nowrap;
  text-align: left;
}
/* Le dernier `td` reste collé au bord droit du panneau : le padding de fin de
   ligne s'applique toujours, seul l'alignement du contenu change. */
.table--actions-serrees td:last-child { padding-left: var(--s2); }

/* Action secondaire réduite à son picto (le crayon « Modifier » de la liste des
   candidats). Sans cadre au repos : c'est là tout le propos, elle ne doit plus
   peser autant que l'action principale de la ligne. Le cadre revient au survol
   et au focus, pour qu'on voie qu'elle est cliquable.

   La cible garde 32px de côté : un picto de 16px ne se vise pas au doigt. */
.btn-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--muted);
  transition: color .12s ease, background-color .12s ease, border-color .12s ease;
}
.btn-icone svg { width: 16px; height: 16px; }
.btn-icone:hover {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: var(--accent-line);
}
.btn-icone:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  color: var(--accent);
}
/* Le retrait d'un fichier : **picto et mot**, dans un cadre rouge pâle visible
   au repos (2026-08-08). Deux essais successifs — corbeille grise, puis
   corbeille rouge — sont restés introuvables sur le fond vert d'une ligne
   pourvue : un dessin de 16px, seul, ne se voit pas. Le mot se lit. */
.btn-retrait {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  /* `button` est en `display: block; width: 100%` dans tout le projet — sans
     cette largeur, le bouton prenait toute la ligne et écrasait le nom du
     fichier sur une colonne d'une lettre par ligne (vu le 2026-08-08). */
  width: auto;
  padding: 3px 9px 3px 7px;
  border: 1px solid var(--warn-line);
  border-radius: var(--radius-sm);
  background: var(--warn-soft);
  color: var(--warn-ink);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.6;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
.btn-retrait svg { width: 13px; height: 13px; }
.btn-retrait:hover { background: var(--warn); border-color: var(--warn); color: #fff; }
.btn-retrait:focus-visible { outline: 2px solid var(--warn); outline-offset: 2px; }

/* Identifiants fraîchement créés, à recopier : cadre blanc à l'intérieur du
   panneau vert, intitulé au-dessus de la valeur. Ce n'est pas un `<table>` :
   `.panel > table` va bord à bord par des marges négatives, ce qui faisait
   traverser le vert par deux bandes blanches débordant des coins arrondis. */
.identifiants {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s3) var(--s6);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
}
.identifiants > div { min-width: 0; }
.identifiants .eyebrow { margin: 0 0 2px; }
.identifiant-valeur {
  margin: 0;
  font-family: var(--mono);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
  /* Un mot de passe temporaire n'a pas d'espace où se couper : sans ceci il
     élargit la grille au lieu de passer à la ligne. */
  overflow-wrap: anywhere;
}
.identifiant-note { grid-column: 1 / -1; margin: 0; }

/* Dates de l'examen : intitulé au-dessus, date en dessous, en colonnes côte à
   côte. Elles vivaient dans un tableau pleine largeur où la valeur se lisait à
   l'autre bout de la ligne, loin de ce qu'elle datait. */
.examen-dates {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s3) var(--s6);
}
.examen-dates > div { min-width: 0; }
.examen-dates .eyebrow { margin: 0 0 2px; }
.examen-date {
  margin: 0;
  font-size: 19px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* Grille de notation : le libellé prend la place, le poids et la note tiennent
   en bout de ligne. La dernière ligne — la note finale — reprend le fond de
   `.is-courante`, déjà utilisé pour désigner la ligne qui compte. */
.table--notes th:nth-child(2), .table--notes td:nth-child(2) { width: 15%; }
.table--notes th:nth-child(3), .table--notes td:nth-child(3) { width: 22%; }
.table--notes label { display: block; margin: 0; font-weight: 500; }
.table--notes input[type="text"] {
  padding: 8px 10px;
  font: inherit;
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}

/* Pièces du dossier d'examen : le libellé et ses fichiers à gauche, le champ de
   dépôt en bout de ligne. Pas d'en-tête de tableau — les deux colonnes se
   passent de nom, et l'intitulé de groupe au-dessus dit déjà de quoi il s'agit. */
.table--pieces td { vertical-align: top; }
.table--pieces th:nth-child(2), .table--pieces td:nth-child(2) { width: 34%; }
.piece-fichiers { margin: 4px 0 0; padding-left: 18px; font-size: 14px; }
.piece-fichiers li { margin-bottom: 2px; }
.piece-retraits { list-style: none; margin: 0; padding: 0; }
.piece-retraits li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 6px 0;
  border-top: 1px solid var(--line);
  font-size: 14px;
}
.piece-retraits form { margin: 0; }

/* Les experts en pleine largeur : deux colonnes tant que la place le permet,
   pour qu'une équipe de quatre tienne sur deux lignes. */
.avis-liste--grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  column-gap: var(--s6);
}

/* Panneau d'action : ce que la personne connectée doit faire maintenant. */
.panel--action {
  border-color: #ccd8f7;
  background: var(--accent-soft);
}

/* ---------- Tuiles de chiffres clés (page d'accueil) ----------
   Un nombre, un libellé, rien d'autre. Aucune couleur ne porte de sens :
   c'est le chiffre qui informe, pas la teinte. */
.tuiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s3);
  margin-bottom: var(--s5);
}

.tuile {
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}

.tuile-valeur {
  display: block;
  font-size: 28px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--ink);
  /* Chiffres proportionnels : à cette taille, tabular-nums fait flotter les
     nombres courts. Le tabulaire reste réservé aux colonnes de tableau. */
  font-variant-numeric: proportional-nums;
}

/* ---------- Bloc métier du tableau de bord (Chef Expert, Administrateur)
   Une section entière par métier. Le cadre et la barre de titre disent que
   tout ce qui suit appartient à ce métier — un simple intertitre se lisait
   comme posé là. Repliable, ouvert par défaut, sans script. */
.metier-bloc {
  margin-bottom: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  overflow: hidden;
}



.metier-entete {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}
.metier-entete:hover { background: var(--surface); }

/* Replié, la barre n'a plus rien à séparer. */
.metier-bascule:not(:checked) ~ .metier-entete { border-bottom-color: transparent; }

.metier-compte {
  margin-left: auto;
  font-size: 13px;
  font-weight: 400;
  color: var(--muted);
}

.metier-chevron {
  font-size: 12px;
  color: var(--muted);
  transition: transform .15s ease;
}
.metier-bascule:not(:checked) ~ .metier-entete .metier-chevron { transform: rotate(-90deg); }

.metier-corps { display: none; padding: var(--s4); }
.metier-bascule:checked ~ .metier-corps { display: block; }

/* Les tuiles n'ont pas besoin de leur marge basse à l'intérieur du bloc. */
.metier-corps .tuiles { margin-bottom: var(--s4); }

/* Le dernier panneau du bloc s'arrête au bord : sa marge doublerait la
   respiration du corps. Les précédents la gardent — l'Expert en a deux. */
.metier-corps .panel:last-child { margin-bottom: 0; }

/* Deux panneaux dans un même bloc ne se touchent pas : collés, ils se lisaient
   comme un seul tableau à deux en-têtes. La règle générale `.panel + .panel`
   met la marge à zéro parce que l'espacement vient d'ordinaire du `gap` du
   parent — il n'y en a pas ici. */
.metier-corps .panel + .panel { margin-top: var(--s4); }

/* ---------- Ce qui informe
   Bleu très pâle sur la barre de titre d'un panneau qui n'est là que pour le
   suivi — rien n'y attend celui qui le lit. Il ne reste que chez le Chef
   Expert et l'Administrateur : les deux autres teintes (`--attente` rouge,
   `--note` verte) sont parties avec les listes de l'Expert le 2026-08-09, la
   file d'actions marquant désormais ses groupes d'une pastille.

   La teinte ne prend que la barre de titre (demande de l'utilisateur,
   2026-08-06) : le tableau reste blanc, comme partout ailleurs dans
   l'application. Les coins hauts suivent l'arrondi du panneau, sinon l'aplat
   déborderait sur le rayon. */
.panel--suivi > h2:first-child {
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
  background: var(--accent-soft);
  border-bottom-color: var(--accent-line);
  /* Le gris du titre est repris un cran plus sombre : --muted ne tient pas
     4.5:1 sur ce fond. Redéfini sur la barre, il vaut aussi pour le décompte
     qu'elle porte à droite. */
  --muted: var(--muted-sur-teinte);
}

/* Le total qui situe la valeur : « 4 sur 28 ». Nettement plus discret que le
   premier nombre, sinon les deux se disputent le regard. */
.tuile-total {
  font-size: 15px;
  font-weight: 400;
  color: var(--muted);
}

.tuile-libelle {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--muted);
}

/* Étiquette réservée aux lecteurs d'écran : la grille du calendrier de session
   nomme ses colonnes par une pastille de métier, ce qui ne suffit pas à un
   champ de saisie sorti de son contexte visuel. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- En-têtes de colonne triables
   Le tri est un lien : il tient sans script, se met en favori et s'ouvre dans
   un onglet comme n'importe quel autre. La flèche n'est portée que par la
   colonne active — une flèche grise sur chaque en-tête ne dirait plus
   laquelle commande. */
th a.tri {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: inherit;
  text-decoration: none;
}
th a.tri:hover { color: var(--ink); text-decoration: underline; }
th a.tri:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}
.tri-fleche { font-size: 8px; color: var(--accent); }

/* ---------- Tableau de bord de l'Expert ----------
   Refonte du 2026-08-09, sur maquette de l'utilisateur : un en-tête, trois
   chiffres, puis deux colonnes — la file de travail à gauche, le contexte à
   droite. Les blocs par métier de l'Expert ont disparu ; le métier reste
   écrit sur chaque ligne, et la colonne de droite reste rangée par métier.

   Tout est en tokens : la maquette proposait ses propres hex (#b3261e,
   #9a5b00, #1c6b3a), qui sont à un cheveu de --warn, --wait et --ok. Reprendre
   les tokens vaut mieux qu'ajouter trois couleurs qui ne servent qu'ici. */

/* En-tête sur fond blanc, comme la maquette : il se détache du fond de page
   bleu-ardoise et coiffe la bande de chiffres au lieu de flotter dessus. */
.tdb-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  margin-bottom: var(--s4);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}

.tdb-entete-qui {
  display: flex;
  align-items: center;
  gap: var(--s3);
}

/* Les initiales, ou la photo déposée sur « Mon profil » depuis le 2026-08-11.
   Sans photo, le rond garde son fond d'encre : un rond vide serait pire que
   pas de rond du tout. */
.tdb-avatar {
  width: 34px;
  height: 34px;
  flex: none;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--ink);
  color: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 620;
  letter-spacing: .02em;
}
/* Le fond d'encre se retire sous une photo : il déborderait dans les coins. */
.tdb-avatar--photo { background: var(--surface-2); }

.tdb-titre {
  margin: 0;
  font-size: 17px;
  font-weight: 620;
  letter-spacing: -.01em;
  color: var(--ink);
}

.tdb-meta {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--muted);
}

/* Le calendrier de session : trois colonnes étalées sur 1600 px mettaient
   les champs de date à l'autre bout de l'écran. La table se borne, la
   première colonne prend le reste. */
.table--calendrier { max-width: 760px; }
.table--calendrier th:not(:first-child),
.table--calendrier td:not(:first-child) { width: 180px; }

/* ---------- Le sélecteur de session
   Maquette 1c de l'utilisateur (2026-08-09) : une barre segmentée, un segment
   par session, **sans bouton de validation** — le choix s'applique au clic.

   Ce sont des liens ordinaires vers la même page : aucun script n'est en jeu.
   Un `<select>` qui s'appliquerait de lui-même en aurait demandé un ; trois
   sessions dans une barre n'en demandent aucun.

   La pastille verte marque la session **ouverte**, pas celle qu'on regarde —
   la sélection se lit déjà au fond blanc du segment. On consulte souvent une
   session clôturée : les deux informations doivent rester distinctes. */
/* La barre ne s'étire pas : sur un écran large, deux sessions occupaient
   1500 px et leurs libellés flottaient au milieu du vide. Elle est bornée, et
   l'intitulé au-dessus suit la même largeur pour rester aligné avec elle.
   Pas de marge propre — l'espacement vient du `gap` de `.app-content`. */
.selecteur-tete,
.selecteur-segments { max-width: 620px; }

.selecteur-tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s3);
}
.selecteur-tete .eyebrow { margin: 0; }
.selecteur-note { font-size: 12px; color: var(--faint); }

.selecteur-segments {
  display: flex;
  gap: var(--s2);
  padding: 5px;
  border-radius: var(--radius);
  background: var(--surface-2);
}

.selecteur-segment {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 40px;
  padding: 0 var(--s3);
  border-radius: var(--radius);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  text-align: center;
}
.selecteur-segment:hover { background: var(--paper-2); color: var(--accent); }

.selecteur-segment--actif {
  background: var(--paper);
  box-shadow: var(--lift-1);
  color: var(--ink);
  font-weight: 620;
}
.selecteur-segment--actif:hover { background: var(--paper); color: var(--ink); }

/* La pastille redouble le libellé « en cours » du bandeau latéral, elle ne le
   remplace pas : une couleur seule ne porte jamais l'information. D'où aussi
   son `title`. */
.selecteur-point {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--ok);
}

/* Sous 620px les segments s'empilent : quatre sessions sur une ligne de
   téléphone donneraient des libellés coupés. */
@media (max-width: 620px) {
  .selecteur-segments { flex-direction: column; }
}

/* ---------- Les trois dates du collège, en rangée hiérarchisée
   Maquette 3a de l'utilisateur (2026-08-09). Elles ont d'abord vécu en bas de
   la colonne de droite, puis en trois vignettes carrées dans l'en-tête ; elles
   forment maintenant leur propre bande, la plus proche en vedette.

   L'ordre est **chronologique** — c'est ce qui donne son sens à la vedette.
   Ce sont les repères **communs** du collège : les trois sont servies à tout
   le monde, y compris pour un métier où l'on ne siège pas. À ne pas confondre
   avec la ligne « ce qui vous presse » de l'en-tête, qui se lit dans les
   dossiers de la personne. */
/* Elles vivent **dans** l'en-tête, à droite de l'identité (2026-08-10) : la
   bande séparée qu'elles occupaient un temps ajoutait un étage à la page. */
.tdb-echeances { min-width: 0; }

.tdb-echeances-rangee {
  display: flex;
  align-items: center;
  gap: var(--s4);
}

/* L'intitulé **ouvre la rangée** (2026-08-10) : posé au-dessus et aligné à
   droite, il flottait dans le coin de l'écran, « un peu perdu ». Devant les
   cartes, il les annonce, et l'œil le rencontre avant elles.
   Deux lignes voulues : une étiquette étroite laisse la place aux dates. */
.tdb-echeances-titre {
  max-width: 78px;
  margin: 0;
  flex: none;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--faint);
}

/* Quand tout est passé, la rangée est vide : sans un mot, on croirait à un
   écran qui a perdu ses dates. */
.tdb-echeances-vide {
  margin: 0;
  font-size: 12.5px;
  color: var(--muted);
}

/* Toutes les cartes ont le même dessin — pastille de date, intitulé, délai — et
   ne diffèrent que par la taille. Les suivantes ont eu un temps une autre
   disposition, date en clair au-dessus du titre : trois dessins pour une même
   information ne se comparaient pas d'un coup d'œil (2026-08-10).
   Elles restent séparées d'un filet, pas d'un fond : trois blocs colorés se
   disputeraient la vedette qu'on vient de désigner. */
.tdb-echeance {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
}
.tdb-echeance + .tdb-echeance {
  padding-left: var(--s4);
  border-left: 1px solid var(--line-soft);
}

.tdb-echeance-texte { min-width: 0; }

/* La pastille de date ne porte pas l'état de l'échéance : sa teinte dit « c'est
   la prochaine », ce qui est de la hiérarchie, pas une alerte. L'état, lui,
   reste écrit sous le titre, en toutes lettres et coloré par `evt-*`. C'est
   aussi pourquoi seule la vedette porte le bleu d'accent : les suivantes
   gardent le même dessin en gris, la hiérarchie se lit sans un mot de plus. */
.tdb-echeance-pastille {
  flex: none;
  border-radius: var(--radius);
  text-align: center;
}
.tdb-echeance-jour {
  display: block;
  font-weight: 620;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.tdb-echeance-mois {
  display: block;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.tdb-echeance-delai { font-size: 11px; color: var(--muted); }

.tdb-echeance-vedette .tdb-echeance-pastille {
  width: 52px;
  padding: 6px 0;
  background: var(--accent-soft);
}
.tdb-echeance-vedette .tdb-echeance-jour  { font-size: 20px; color: var(--accent); }
.tdb-echeance-vedette .tdb-echeance-mois  { font-size: 10px; color: var(--accent-ink); }
.tdb-echeance-vedette .tdb-echeance-titre {
  font-size: 13.5px;
  font-weight: 620;
  color: var(--ink);
}
.tdb-echeance-vedette .tdb-echeance-delai { font-weight: 560; }

.tdb-echeance-suite .tdb-echeance-pastille {
  width: 40px;
  padding: 4px 0;
  background: var(--surface-2);
}
.tdb-echeance-suite .tdb-echeance-jour  { font-size: 15px; color: var(--ink-2); }
.tdb-echeance-suite .tdb-echeance-mois  { font-size: 9px;  color: var(--muted); }
.tdb-echeance-suite .tdb-echeance-titre {
  font-size: 12px;
  font-weight: 560;
  color: var(--ink-2);
}

/* Sous 1100px la rangée passe pleine largeur et s'enroule ; sous 620px elle
   s'empile, chaque carte séparée d'un filet horizontal. */
@media (max-width: 1100px) {
  .tdb-echeances { width: 100%; }
  .tdb-echeances-rangee { flex-wrap: wrap; }
  .tdb-echeances-titre { max-width: none; width: 100%; }
}
@media (max-width: 620px) {
  .tdb-echeances-rangee { flex-direction: column; align-items: stretch; }
  .tdb-echeance + .tdb-echeance {
    padding: var(--s2) 0 0;
    border-left: none;
    border-top: 1px solid var(--line-soft);
  }
}

/* ---------- Les trois chiffres de tête ---------- */

.tdb-tuiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s4);
  margin-bottom: var(--s5);
}

/* Le Formateur n'en a que deux : il ne communique aucune note. Elles se
   partagent la rangée à parts égales plutôt que de laisser une troisième
   colonne vide, qui se lirait comme un chiffre manquant.
   Le Responsable en a quatre : il suit le cycle de bout en bout. */
.tdb-tuiles--deux   { grid-template-columns: repeat(2, 1fr); }
.tdb-tuiles--quatre { grid-template-columns: repeat(4, 1fr); }
/* Le Chef Expert en a sept depuis le 2026-08-19 : il conduit les examens des
   deux métiers, et sept chiffres sur une rangée seraient illisibles. Ils se
   lisent sur deux rangées — ce qui bloque le cycle, puis le collège.
   Le Responsable les a rejoints le 2026-09-04 : cinq chiffres tenaient sur une
   rangée, la tuile des contacts en retard en fait six (R02), et six à la file
   sont six chiffres à l'étroit. Deux rangées de trois pour l'un comme pour
   l'autre. */
.tdb-tuiles--trois  { grid-template-columns: repeat(3, 1fr); }
/* L'Expert en a cinq depuis le 2026-11-01 (T20) : ses énoncés en correction
   chez le formateur. Une rangée sur grand écran, trois colonnes plus bas. */
.tdb-tuiles--cinq   { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 1280px) {
  .tdb-tuiles--cinq { grid-template-columns: repeat(3, 1fr); }
}

/* Sous 1000px, quatre colonnes donnent des chiffres à l'étroit : elles passent
   à deux avant de s'empiler complètement plus bas. */
@media (max-width: 1000px) {
  .tdb-tuiles--quatre,
  .tdb-tuiles--cinq,
  .tdb-tuiles--trois  { grid-template-columns: repeat(2, 1fr); }
}

.tdb-tuile {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}

.tdb-tuile-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
  /* Deux lignes réservées : « Énoncés validés par les Experts » en prend deux
     quand les autres n'en prennent qu'une, et les chiffres de la rangée ne se
     lisaient plus sur la même ligne (2026-08-12). */
  min-height: 2.4em;
}

.tdb-tuile-chiffre {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
}

.tdb-tuile-valeur {
  font-size: 30px;
  font-weight: 620;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.tdb-tuile-unite { font-size: 13px; color: var(--muted); }
.tdb-tuile-precision { font-size: 12px; color: var(--faint); }

/* La barre dit toujours la même chose : la part du tout qui vous attend. Elle
   se remplit donc quand le travail s'accumule — trois barres qui se liraient
   dans deux sens différents ne diraient plus rien. La couleur redouble le
   libellé, elle ne le remplace jamais. */
.tdb-jauge {
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  overflow: hidden;
}
.tdb-jauge-part { height: 100%; }

/* Le ton neutre, pour un chiffre qui n'est ni une charge ni un aboutissement :
   « 17 énoncés déposés sur 24 » est une avancée, pas une alerte. Bleu d'accent
   plutôt qu'ambre — chez le Responsable, une seule tuile doit tirer l'œil, et
   c'est celle qui l'attend. */
/* Une tuile qui mène quelque part se **teinte** au survol, de sa propre
   couleur et en pastel. Le soulignement du lien, lui, est retiré : sur une
   tuile entière, il barrait l'intitulé sans rien apprendre — c'est le bloc qui
   est cliquable, pas le mot (demande de l'utilisateur, 2026-08-11).
   Le `text-decoration` doit être répété sur `:hover` : la règle générale
   `a:hover` le rétablirait sinon. Le halo de focus vient de `:focus-visible`. */
.tuile--lien,
.tdb-tuile--lien {
  text-decoration: none;
  color: inherit;
  transition: background-color .12s, border-color .12s;
}
.tuile--lien:hover,
.tdb-tuile--lien:hover {
  text-decoration: none;
  color: inherit;
}

/* Chacune prend le ton qu'elle porte déjà : bleu pour une avancée, rouge pour
   ce qui attend, vert pour ce qui est fait. Les tuiles des blocs métier n'ont
   pas de ton à elles — le bleu d'accent leur va, c'est la couleur des liens.

   Le fond seul, sans toucher à la bordure : une bordure pleine `--warn` ou
   `--ok` cernait la tuile d'un trait vif là où l'on voulait un aplat doux. */
.tuile--lien:hover                        { background: var(--accent-soft); }
.tdb-tuile--lien.tdb-tuile--neutre:hover  { background: var(--accent-soft); }
.tdb-tuile--lien.tdb-tuile--urgent:hover  { background: var(--warn-soft); }
.tdb-tuile--lien.tdb-tuile--attente:hover { background: var(--wait-soft); }
.tdb-tuile--lien.tdb-tuile--fait:hover    { background: var(--ok-soft); }
.tdb-tuile--lien.tdb-tuile--turquoise:hover { background: var(--turquoise-soft); }
.tdb-tuile--lien.tdb-tuile--mauve:hover     { background: var(--mauve-soft); }

.tdb-tuile--neutre  .tdb-tuile-valeur { color: var(--accent); }
.tdb-tuile--neutre  .tdb-jauge-part   { background: var(--accent); }
.tdb-tuile--urgent  .tdb-tuile-valeur { color: var(--warn); }
.tdb-tuile--urgent  .tdb-jauge-part   { background: var(--warn); }
.tdb-tuile--attente .tdb-tuile-valeur { color: var(--wait); }
.tdb-tuile--attente .tdb-jauge-part   { background: var(--wait); }
.tdb-tuile--fait    .tdb-tuile-valeur { color: var(--ok); }
.tdb-tuile--fait    .tdb-jauge-part   { background: var(--ok); }
.tdb-tuile--turquoise .tdb-tuile-valeur { color: var(--turquoise); }
.tdb-tuile--turquoise .tdb-jauge-part   { background: var(--turquoise); }
.tdb-tuile--mauve     .tdb-tuile-valeur { color: var(--mauve); }
.tdb-tuile--mauve     .tdb-jauge-part   { background: var(--mauve); }

/* ---------- Les deux colonnes ---------- */

.tdb-colonnes {
  display: grid;
  grid-template-columns: 1.62fr 1fr;
  gap: var(--s5);
  align-items: start;
}

/* Les panneaux du tableau de bord n'ont pas la marge intérieure du cas
   courant : leurs lignes vont bord à bord, comme un tableau. */
.panel.tdb-file { padding: 0; overflow: hidden; }

.tdb-file-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line-soft);
}
.tdb-file-tete h2 {
  margin: 0;
  font-size: 14px;
  font-weight: 620;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}

.tdb-file-vide {
  margin: 0;
  padding: var(--s5) var(--s4);
  font-size: 13px;
  color: var(--muted);
}

/* Le compte de la file, là où l'Expert a ses onglets. Le Formateur n'en a pas :
   un seul groupe d'actions n'a rien à trier. Reste le nombre, qui manquerait
   sinon — les onglets le portaient. */
.tdb-file-compte {
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  font-size: 12px;
  font-weight: 620;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* Les onglets filtrent la file sans rien recharger : quatre boutons radio
   devant, les groupes derrière, des sélecteurs de voisinage pour cacher ce
   qu'on n'a pas demandé. Aucun script, comme partout ailleurs.
   Chaque onglet doit être nommé dans les règles ci-dessous : sans `:has()`,
   il n'existe pas de version générique. */
/* Sa boîte est normalisée avec les autres cases pilotes, section
   « Formulaires » : invisible, mais focalisable — l'onglet reste au clavier. */

.tdb-onglets {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius);
  background: var(--surface-2);
}

.tdb-onglet {
  padding: 5px 11px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
}
.tdb-onglet:hover { color: var(--accent); }

.tdb-onglet-input--tout:checked    ~ .tdb-file-tete .tdb-onglet--tout,
.tdb-onglet-input--enonces:checked ~ .tdb-file-tete .tdb-onglet--enonces,
.tdb-onglet-input--notes:checked   ~ .tdb-file-tete .tdb-onglet--notes {
  background: var(--paper);
  box-shadow: var(--lift-1);
  color: var(--ink);
  font-weight: 620;
}

.tdb-onglet-input--tout:focus-visible    ~ .tdb-file-tete .tdb-onglet--tout,
.tdb-onglet-input--enonces:focus-visible ~ .tdb-file-tete .tdb-onglet--enonces,
.tdb-onglet-input--notes:focus-visible   ~ .tdb-file-tete .tdb-onglet--notes {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.tdb-onglet-input--enonces:checked ~ .tdb-groupes .tdb-groupe:not(.tdb-groupe--enonces),
.tdb-onglet-input--notes:checked   ~ .tdb-groupes
  .tdb-groupe:not(.tdb-groupe--notes):not(.tdb-groupe--notation):not(.tdb-groupe--validation) {
  display: none;
}

/* ---------- Le filtre par métier
   Second jeu de commandes, indépendant des onglets : ceux-là choisissent un
   groupe, celui-ci un métier. Les deux filtres se composent.

   **Des cases à cocher, et elles se cumulent** (2026-08-11) : TPI seul, puis
   TPI et TPP, puis TPP seul, comme sur la liste des candidats. Le bouton
   « Tous » a disparu — n'en cocher aucune revient au même. La pastille garde
   sa forme arrondie, qui plaisait ; c'est sa teinte active qui suit désormais
   celle de la liste, pleine et bleue, pour qu'on voie ce qui est retenu.

   Le cumul se dit en deux temps : dès qu'une case est cochée on masque toutes
   les lignes, puis on ré-affiche celles des métiers retenus. Chaque métier est
   nommé — un troisième demanderait ses propres règles, comme les pastilles
   `.tag--tpi` / `.tag--tpp`. */
.tdb-metiers-filtre {
  display: flex;
  gap: var(--s1);
  margin-left: auto;
}

.tdb-filtre {
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-2);
  background: var(--paper);
  cursor: pointer;
  white-space: nowrap;
}
.tdb-filtre:hover { border-color: var(--accent-line); color: var(--accent); }

.tdb-metier-input--tpi:checked  ~ .tdb-file-tete .tdb-filtre--tpi,
.tdb-metier-input--tpp:checked  ~ .tdb-file-tete .tdb-filtre--tpp {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
  font-weight: 620;
}

.tdb-metier-input--tpi:focus-visible  ~ .tdb-file-tete .tdb-filtre--tpi,
.tdb-metier-input--tpp:focus-visible  ~ .tdb-file-tete .tdb-filtre--tpp {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Une case cochée, quelle qu'elle soit : tout se masque… */
.tdb-file:has(.tdb-metier-input:checked) .tdb-action { display: none; }
/* … puis les métiers retenus reparaissent, chacun le sien. */
.tdb-file:has(.tdb-metier-input--tpi:checked) .tdb-action--tpi,
.tdb-file:has(.tdb-metier-input--tpp:checked) .tdb-action--tpp { display: grid; }

/* Un groupe vidé de toutes ses lignes garderait un titre suivi de rien. Il ne
   se retire que lorsqu'**un seul** métier est retenu : les deux cochés, plus
   rien n'est masqué. */
.tdb-file:has(.tdb-metier-input--tpi:checked):not(:has(.tdb-metier-input--tpp:checked))
  .tdb-groupe:not(:has(.tdb-action--tpi)),
.tdb-file:has(.tdb-metier-input--tpp:checked):not(:has(.tdb-metier-input--tpi:checked))
  .tdb-groupe:not(:has(.tdb-action--tpp)) {
  display: none;
}

/* ---------- Un groupe de la file ---------- */

.tdb-groupe-tete {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  background: var(--paper-2);
  border-bottom: 1px solid var(--line-soft);
}

.tdb-groupe-point {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
}
.tdb-groupe-point--enonces { background: var(--wait); }
.tdb-groupe-point--notes   { background: var(--ok); }
.tdb-groupe-point--notation { background: var(--mauve); }
.tdb-groupe-point--entretiens { background: var(--accent); }
.tdb-groupe-point--validation { background: var(--ok); }

.tdb-groupe-titre {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.tdb-groupe-sous { font-size: 12px; color: var(--muted); }

/* Les quatre colonnes s'alignent d'une ligne à l'autre, et d'un groupe à
   l'autre : le gabarit vit sur `.tdb-groupes`, groupes et lignes le reprennent
   par `subgrid`.

   Chaque `.tdb-action` était une grille à elle seule, si bien que la largeur
   d'une pastille décalait la ligne voisine — « dans 165 jours » contre « dans
   7 jours », et l'entreprise ne commençait plus au même endroit (signalé par
   l'utilisateur, 2026-08-10). `display: contents` aurait aligné aussi, mais la
   ligne y aurait perdu son fond, son filet et sa marge intérieure.

   La première déclaration de chaque paire est le repli des navigateurs sans
   `subgrid` : ils gardent le comportement d'avant, décalage compris, plutôt
   qu'une colonne unique où tout s'empilerait. */
.tdb-groupes {
  display: grid;
  grid-template-columns: 1.5fr 1fr auto auto;
  column-gap: var(--s4);
}

.tdb-groupe,
.tdb-action {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1.5fr 1fr auto auto;
  grid-template-columns: subgrid;
  column-gap: var(--s4);
}

.tdb-groupe-tete,
.tdb-file-vide { grid-column: 1 / -1; }

.tdb-action {
  align-items: center;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line-soft);
}
.tdb-action:last-child { border-bottom: none; }

/* Le nom porte la pastille de nature du Formateur : en ligne avec lui, elle
   est lue avant tout le reste — c'est ce qu'on lui demande. */
.tdb-action-nom {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

/* « Dépôt » ou « Correction ». Bleu contre rouge pâle : la seconde demande de
   reprendre un travail déjà rendu, la première non. Le mot reste écrit — la
   couleur ne porte jamais l'information seule. */
.tdb-action-nature {
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  font-weight: 620;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}
.tdb-action-nature--depot      { background: var(--accent-soft); color: var(--accent); }
.tdb-action-nature--correction { background: var(--warn-soft);   color: var(--warn-ink); }
.tdb-action-nature--notation   { background: var(--wait-soft);   color: var(--wait); }
.tdb-action-nature--validation { background: var(--ok-soft);     color: var(--ok); }
.tdb-action-sous { margin-top: 2px; font-size: 12px; color: var(--muted); }
.tdb-action-entreprise { font-size: 13px; color: var(--ink-2); }
.tdb-action-detail { margin-top: 2px; font-size: 12px; color: var(--faint); }
.tdb-action-lien { text-align: right; }

.tdb-pastille {
  display: inline-block;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.tdb-pastille--error   { background: var(--warn-soft); color: var(--warn-ink); }
.tdb-pastille--warn    { background: var(--wait-soft); color: var(--wait); }
.tdb-pastille--neutral { background: var(--surface-2); color: var(--ink-2); }
.tdb-pastille--ok      { background: var(--ok-soft);   color: var(--ok-ink); }

/* ---------- La colonne de droite ---------- */

.tdb-contexte > .eyebrow { margin: 0 0 var(--s4); }

.tdb-metier + .tdb-metier { margin-top: var(--s4); }

.tdb-metier-tete {
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.tdb-metier-nom { flex: 1; font-size: 13px; color: var(--ink-2); }
.tdb-metier-nombre {
  font-size: 19px;
  font-weight: 620;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.tdb-metier-options {
  margin: var(--s1) 0 0;
  font-size: 12px;
  color: var(--faint);
}

/* La liste défile plutôt que d'être tronquée : un expert peut suivre des
   dizaines de candidats, et couper la liste sans le dire ferait croire qu'il
   n'en a que six. */
/* `.tdb-liste-candidats` est la même boîte sans le bloc métier autour : c'est
   l'encart du Formateur, dont les candidats se lisent d'une traite — il n'y a
   rien à séparer chez quelqu'un qui en suit trois. */
.tdb-metier-liste,
.tdb-liste-candidats {
  margin-top: var(--s2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden auto;
  max-height: 340px;
}

.tdb-candidat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--line-soft);
  text-decoration: none;
  color: inherit;
}
.tdb-candidat:last-child { border-bottom: none; }
.tdb-candidat:hover { background: var(--paper-2); }

.tdb-candidat-qui { min-width: 0; }
.tdb-candidat-nom,
.tdb-candidat-entreprise {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tdb-candidat-nom { font-size: 13px; font-weight: 600; color: var(--ink); }
.tdb-candidat-entreprise { font-size: 12px; color: var(--muted); }

.tdb-candidat-place {
  flex: none;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-size: 11px;
  color: var(--ink-2);
  white-space: nowrap;
}

/* Chez le Responsable, la place est tenue par la note reçue : un chiffre, pas
   une pastille. Pas de fond — sa teinte vient de `evt-*`, la même qu'ailleurs
   dans l'application, et un aplat de plus la brouillerait. */
.tdb-candidat-note {
  flex: none;
  font-size: 14px;
  font-weight: 620;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Chez le Formateur, cette place porte l'étape du dossier — et elle se teinte,
   parce que « à corriger » et « validé » ne se lisent pas de la même façon.
   Le libellé reste écrit : la couleur ne dit jamais rien à elle seule.
   Le ton neutre garde le fond gris de la règle ci-dessus, il n'a pas de
   variante à lui. */
.tdb-candidat-place--ok    { background: var(--ok-soft);   color: var(--ok-ink); }
.tdb-candidat-place--warn  { background: var(--wait-soft); color: var(--wait); }
.tdb-candidat-place--error { background: var(--warn-soft); color: var(--warn-ink); }

.tdb-contexte-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
  color: var(--muted);
}
.tdb-contexte-total strong { color: var(--ink); }

/* La colonne de droite empile ses panneaux : la grille des colonnes ne compte
   que deux enfants, le second panneau retomberait sous la file sans coquille. */
.tdb-cote {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}

/* Sous 1000px les deux colonnes se superposent : la file d'abord, elle porte
   le travail. Sous 620px chaque ligne d'action se déplie en quatre. */
@media (max-width: 1000px) {
  .tdb-colonnes { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
  .tdb-tuiles { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  /* Les trois niveaux redeviennent des grilles ordinaires à une colonne : il
     n'y a plus rien à aligner entre les lignes quand chacune s'empile. */
  .tdb-groupes,
  .tdb-groupe,
  .tdb-action { grid-template-columns: 1fr; }
  .tdb-action { gap: var(--s2); }
  .tdb-action-lien { text-align: left; }
  .tdb-onglets { width: 100%; overflow-x: auto; }
}

/* ---------- Fenêtre modale ----------
   Une case à cocher masquée, un label pour ouvrir, deux pour fermer (le fond
   et la croix) : l'ouverture et la fermeture ne demandent aucun script. Le
   confort clavier (Échap, focus piégé), lui, en demande — il est ajouté par
   layout_footer() sur les pages qui portent une modale.
   C'est ici que passent toutes les créations : compte, candidat, session. */
.modale-input { display: none; }
.modale { display: none; }

/* `+` et non `~` (2026-08-09). Le sélecteur de voisinage général atteignait
   **toutes** les modales suivantes : sur une page qui en porte vingt-quatre —
   une par règle de rappel —, en ouvrir une les affichait toutes, empilées. On
   croyait alors fermer la sienne alors qu'on cliquait sur la croix de la
   dernière, qui ne faisait que l'ouvrir à son tour. `layout_modale_ouvrir()`
   pose toujours la case juste avant sa modale : l'adjacence est exacte. */
.modale-input:checked + .modale {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 50;
  padding: var(--s5);
  overflow-y: auto;
  align-items: flex-start;
  justify-content: center;
}

.modale-fond {
  position: fixed;
  inset: 0;
  background: rgba(20, 23, 28, .45);
  cursor: pointer;
}

.modale-boite {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 560px;
  margin: auto;
  box-shadow: 0 24px 64px rgba(20, 23, 28, .28);
}

/* Les formulaires de création à trois colonnes ont besoin d'air. */
.modale-boite--large { max-width: 860px; }

.modale-fermer {
  position: absolute;
  top: 6px;
  right: var(--s3);
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
  color: var(--muted);
}
.modale-fermer:hover { color: var(--ink); }

/* L'historique des avis reste replié tant qu'on ne le demande pas. */
.toggle-historique-input { display: none; }
.toggle-historique-input ~ .panel--historique { display: none; }
.toggle-historique-input:checked ~ .panel--historique { display: block; }

.table-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
  justify-content: flex-end;
}

.table-vide {
  padding: var(--s6);
  text-align: center;
  font-size: 13.5px;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   12. Pastilles — trois familles disjointes, libellé toujours écrit
   -------------------------------------------------------------------------- */
.tag {
  display: inline-block;
  padding: 3px var(--s2);
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* -- Statuts de compte ---------------------------------------------------- */
.tag--active   { color: var(--ok);    background: var(--ok-soft); }
.tag--pending  { color: var(--wait);  background: var(--wait-soft); }
.tag--disabled { color: var(--muted); background: var(--surface-2); }

/* -- Mentions : un fait, ni un statut ni un rôle -------------------------- */
/* « Enseignant » dans l'annuaire du collège (2026-08-21) : ce n'est pas un
   titre, c'est une règle d'assignation. Une teinte à elle, pour qu'on ne la
   lise pas comme un compte désactivé. */
.tag--mention { color: var(--ink-2); background: var(--surface-2); }

/* -- Rôles : cinq déclarations distinctes, aucune commune à un statut ----- */
.tag--admin,
.tag--role-admin       { color: #1b4fd8; background: #eaeefb; }
.tag--role-chef        { color: #4c2fa8; background: #e7e4fb; }
.tag--role-responsable { color: #a3246b; background: #fdeaf2; }
.tag--role-expert      { color: #2f5d7c; background: #e8eff5; }
.tag--role-formateur   { color: #5a564a; background: #efeee9; } /* neutre chaud : externe au collège */

/* -- Métiers ------------------------------------------------------------- */
.tag--tpi { color: #6b2fb3; background: #f0e9fa; }
.tag--tpp { color: #0b6b6b; background: #e0f2f2; }

/* -- Option de filière, et domaine d'expertise ---------------------------- */
/* **La même notion des deux côtés** — ce sur quoi porte le travail, ce sur quoi
   porte l'expertise — donc **la même pastille partout dans l'application**
   (2026-08-13, demande de l'utilisateur). Elle a vécu trois jours en aplat
   sombre, jugé « trop terne » : le bleu pastel de l'accent la rend lisible sans
   crier. Il ne dit aucun état — le vert, l'ambre et le rouge restent aux
   statuts —, et se distingue franchement du mauve du TPI, sa voisine de
   toujours dans l'en-tête du dossier.
   Mêmes valeurs que `.tag--role-admin`, et c'est voulu : deux familles
   disjointes peuvent partager une teinte, elles ne se rencontrent pas. */
/* **Deux teintes** depuis le 2026-08-21 : la pastille disait « il y a un
   domaine » sans dire lequel. Deux bleus de la même famille n'ont pas suffi —
   il fallait encore lire le mot pour les départager (remarque de
   l'utilisateur). Bleu pâle pour le développement, jaune pâle pour
   l'infrastructure : deux familles franchement distinctes, qui se reconnaissent
   du coin de l'œil dans une liste de quatre-vingt-dix lignes.
   Ni vert ni rouge, comme toujours : ce n'est pas un état. Le jaune voisine
   dans l'en-tête du dossier avec l'orangé du dual — deux pâles chauds, mais
   l'un tire au sable et l'autre à la terre cuite. */
.tag--option-dev   { color: #1b4fd8; background: #eaeefb; }
.tag--option-infra { color: #6b5200; background: #fdf3cf; }

/* -- Filière : dual ou plein temps --------------------------------------- */
/* Qui engage le candidat — une entreprise, ou l'école (2026-08-18). Deux
   pastilles et non une : la nuance se lit d'un coup d'œil dans une liste de
   quatre-vingt-dix lignes, ce qu'un libellé de même couleur ne donnerait pas.

   Deux teintes chaudes, franchement distinctes du mauve des métiers et du bleu
   de l'option, avec lesquelles elles voisinent dans l'en-tête du dossier. Ni
   vert ni rouge : ce n'est pas un état, aucune des deux filières ne vaut mieux
   que l'autre. */
/* Rouge pâle pour le plein temps, turquoise pâle pour le dual (2026-08-21,
   demande de l'utilisateur). Le rose et l'orangé se ressemblaient trop ; le
   rouge et l'orangé aussi — « une très faible différence ». Deux familles
   opposées règlent la question : on ne les confond plus même du coin de l'œil.

   Le rouge est ici une famille, pas une alarme : il ne partage ni la teinte ni
   le fond de `--warn`, qui reste aux refus et aux erreurs. Le turquoise, lui,
   tire au bleu là où celui du TPP tire au vert — les deux se croisent sur la
   même ligne, l'un en colonne Métier, l'autre en colonne Filière. */
.tag--dual        { color: #0b7285; background: #dcf1f5; }
.tag--plein-temps { color: #9b2c22; background: #fceae7; }

/* --------------------------------------------------------------------------
   12 bis. Dossier candidat — en-tête, deux colonnes, cartes
   Remanié le 2026-08-07 sur une maquette de l'utilisateur : tout s'empilait
   auparavant sur une seule colonne et la page ne se lisait plus.
   -------------------------------------------------------------------------- */
.dossier-tete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s5);
  padding: var(--s5) var(--s6);
  margin-bottom: var(--s5);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.dossier-identite { display: flex; gap: var(--s4); align-items: flex-start; min-width: 0; }
.dossier-identite h1 { margin: 7px 0 0; font-size: 27px; line-height: 1.1; }

/* Pastille d'initiales : elle donne un point d'ancrage visuel à l'en-tête, et
   la même mécanique sert au fil de discussion et au collège. */
.dossier-avatar {
  flex: none;
  width: 56px;
  height: 56px;
  border-radius: 14px;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 19px;
  font-weight: 600;
}

/* `align-items: center` : la pastille de métier est un `.tag`, avec ses propres
   hauteur et graisse. Sans cet alignement, son texte flottait au-dessus de
   celui des étiquettes voisines. */
.dossier-etiquettes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  margin: var(--s3) 0 0;
}
.dossier-etiquettes .tag { display: inline-flex; align-items: center; min-height: 24px; }
.etiquette {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 11.5px;
  font-weight: 500;
}
/* Les adresses de l'en-tête du dossier sont cliquables depuis le 2026-09-04
   (R11). Elles restent de la couleur de l'étiquette — un bleu de lien dans une
   pastille grise ferait un bouton là où il n'y a qu'un renseignement — et ne se
   soulignent qu'au survol, où l'on découvre qu'on peut écrire. */
.etiquette a { color: inherit; text-decoration: none; }
.etiquette a:hover,
.etiquette a:focus-visible { text-decoration: underline; }

/* Deux colonnes : le travail à gauche, ce qui l'encadre à droite. */
.dossier-grille {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 308px;
  gap: var(--s5);
  align-items: start;
}
.dossier-colonne,
.dossier-aside { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }

/* La carte : même boîte que `.panel`, mais son titre est une étiquette discrète
   plutôt qu'une barre. Sur une page qui en empile six, la barre pleine largeur
   répétée six fois pesait plus que ce qu'elle annonçait. */
.panel--carte { margin: 0; }
.panel--carte > h2:first-child {
  display: flex;
  flex-wrap: wrap;
  /* **Centré, et non aligné sur la ligne de base** (2026-09-18, remarque de
     l'utilisateur : « peux-tu centrer le label PRISE DE CONTACT, j'ai
     l'impression qu'il est vers le bas »). L'étiquette fait 10 px, ce qu'elle
     porte à droite en fait 12 ou 15 : alignées par la base, la petite paraît
     tomber au pied de la grande. Le centre les met d'accord, et c'est vrai de
     toutes les cartes — celle des dates portait déjà le même écart. */
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  margin: 0 0 var(--s4);
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}
.carte-appoint {
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
  font-family: var(--sans);
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}

/* **L'action d'une carte, en pied** (2026-09-17, demande de l'utilisateur sur
   la check-list de la rencontre : « je veux qu'il soit positionné de la même
   manière que le bouton Modifier les experts »).

   C'est la forme que le projet donne déjà à ce geste — `.dates-ouvrir` et le
   bouton des experts la portent en toutes lettres depuis des mois. Nommée une
   fois, elle cesse d'être recopiée en style en ligne à chaque carte.

   Elle vient de loin : le bouton a d'abord vécu dans le `<h2>`, qui est une
   étiquette en petites capitales espacées — deux propriétés qui **s'héritent**
   et que le raccourci `font` de `.btn` ne remet pas à zéro, si bien que le
   libellé partait en capitales assez larges pour remplir une colonne de 308 px.
   Le sortir du titre a réglé cela ; le descendre en pied lui donne sa place. */
.carte-bouton {
  display: block;
  width: 100%;
  margin-top: 14px;
  text-align: center;
  font-family: var(--sans);
  font-weight: 560;
  letter-spacing: 0;
  text-transform: none;
}

/* Jeton d'état : « Brouillon — non transmise », « Validé ». Pastille arrondie,
   jamais décorative — elle dit un état, comme les classes evt-*. */
.jeton {
  display: inline-block;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  font-weight: 600;
  white-space: nowrap;
}
.jeton--ok      { background: var(--ok-soft);   color: var(--ok-ink); }
.jeton--attente { background: var(--wait-soft); color: var(--wait); }
.jeton--error   { background: var(--warn-soft); color: var(--warn-ink); }
.jeton--neutre  { background: var(--surface-2); color: var(--muted); }

/* Jauge de complétude des pièces. */
.jauge {
  display: inline-block;
  width: 110px;
  height: 5px;
  border-radius: 3px;
  background: var(--line);
  overflow: hidden;
  vertical-align: middle;
}
.jauge-part { display: block; height: 100%; background: var(--accent); }

/* Un bouton qui se lit comme un lien : pour les actions de retrait, à
   l'intérieur d'une ligne de texte. */
.btn-lien {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 11.5px;
  color: var(--muted);
  text-decoration: underline;
  cursor: pointer;
}
.btn-lien:hover { color: var(--warn); }

/* ---------- Versions et leur fil de discussion ---------- */

.versions { display: flex; flex-direction: column; }
.version { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: var(--s4); }
.version-piste { display: flex; flex-direction: column; align-items: center; }
.version-puce {
  width: 11px;
  height: 11px;
  margin-top: 5px;
  border-radius: var(--radius-pill);
  background: var(--line);
}
.version--courante .version-puce { background: var(--ok); }
.version-trait { flex: 1; width: 1.5px; margin: 5px 0; background: var(--line-soft); }
.version-corps { padding-bottom: var(--s5); min-width: 0; }
.version:last-child .version-corps { padding-bottom: 0; }

.version-entete { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); }
.version-numero { font-size: 13.5px; }
.version-etat { font-size: 11px; font-weight: 600; }
.version-meta { margin: 3px 0 0; font-size: 11.5px; color: var(--muted); }

/* Le fichier et son lien de téléchargement, ensemble et toujours au même
   endroit. Le lien vivait auparavant en bout de ligne d'en-tête, poussé au bord
   droit par `margin-left: auto` : il se retrouvait à un demi-écran du document
   qu'il désigne, et changeait de hauteur selon la longueur de l'en-tête. */
.version-fichier {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2);
  margin: var(--s2) 0 0;
  font-size: 11.5px;
}
/* `word-break: break-all` a disparu le 2026-09-04 : il coupait le nom au milieu
   d'un mot pour le faire tenir, là où la grille le tronque proprement d'une
   ellipse. Les deux ensemble ne donnaient ni l'un ni l'autre.
   Le `min-width: 0` est ce qui rend la troncature possible dans la grille :
   sans lui, une cellule refuse de descendre sous la largeur de son contenu. */
.version-nomfichier {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--mono);
  color: var(--ink-2);
}
.version-taille { color: var(--muted); }
.version-taille::before { content: "· "; }
.version-lien { font-weight: 500; }

/* Carte repliée une fois l'énoncé validé : le cycle est terminé, la page se
   concentre sur l'évaluation et les documents. **Tout le corps** disparaît, pas
   seulement les versions antérieures — sur un dossier qui n'en compte qu'une, le
   premier essai donnait un bouton qui semblait ne rien faire (2026-08-07).
   Le titre et son bouton restent, et le contenu est toujours dans la page. */
/* Une carte en alerte (2026-09-04, R06) : le dossier d'inscription tant qu'il
   est incomplet. Le rouge très pâle des récusations — assez léger pour que le
   texte reste lisible, assez net pour qu'on repère la carte en parcourant la
   colonne. Le titre reprend l'encre foncée du rouge : `--muted` tombe à 4,14:1
   sur ce fond, `--warn-ink` y tient 6,8:1. */
.panel--alerte {
  border-color: var(--warn-line);
  background: var(--warn-soft);
}
.panel--alerte > h2:first-child { color: var(--warn-ink); }
.panel--alerte .carte-corps { color: var(--ink); }
/* Son pendant d'avant l'échéance (Q5, campagne de tests, 2026-10-31) : un
   dossier incomplet est une étape normale tant que la date du dépôt n'est pas
   passée. Le bleu d'information le signale sans alarmer ; le rouge doux de
   R06 ne vient qu'après. */
.panel--info {
  border-color: var(--accent-line);
  background: var(--accent-soft);
}
.panel--info > h2:first-child { color: var(--accent-ink); }
.panel--info .carte-corps { color: var(--ink); }

.panel--repliable .carte-corps { display: none; }
.panel--repliable > h2:first-child { margin-bottom: 0; }
#toggle-versions:checked ~ .panel .carte-corps { display: block; }
#toggle-versions:checked ~ .panel > h2:first-child { margin-bottom: var(--s4); }
#toggle-versions:checked ~ .panel .toggle-versions-bouton { background: var(--accent-soft); }
.quand-deplie { display: none; }
#toggle-versions:checked ~ .panel .quand-replie { display: none; }
#toggle-versions:checked ~ .panel .quand-deplie { display: inline; }

/* Ce que la fenêtre de validation dit en s'ouvrant : ce qui reste à cocher, ou
   qu'il n'y a plus qu'à confirmer. La phrase change, la place ne bouge pas. */
.checklist-etat {
  margin: 0 0 var(--s4); padding: var(--s3);
  font-size: 13.5px; line-height: 1.5; color: var(--ink-2);
  background: var(--surface-2); border-radius: var(--radius);
}
/* En lecture, il n'y a pas de case : c'est le document qu'on lit. Le retrait
   qui alignait le texte sur la case n'a plus lieu d'être. */
.checklist--lecture li { padding-left: 0; }
.checklist--lecture li > span:first-child { font-weight: 560; }

/* L'adresse d'abonnement au calendrier (R27, 2026-09-08). Elle paraît une
   seule fois, et elle est longue : on la sélectionne d'un coup, elle ne se
   recopie pas à la main. `word-break` plutôt qu'un défilement horizontal —
   une adresse coupée en deux se relit, une adresse qu'il faut faire défiler
   se copie mal. */
/* Pourquoi un bouton du jeu de test est grisé (2026-09-09). À côté de lui, pas
   au-dessus : c'est le bouton qu'on regarde quand on se demande pourquoi il ne
   répond pas. */
.jdt-refus { font-size: 13px; color: var(--wait); align-self: center; }

.ics-lien {
  margin: 0 0 var(--s4); padding: var(--s3);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius);
}
.ics-lien code {
  font-family: var(--mono); font-size: 12.5px; line-height: 1.5;
  word-break: break-all; user-select: all;
}

.checklist-etat--complete {
  background: var(--ok-soft); color: var(--ok-ink);
  border: 1px solid var(--ok-line);
}

/* Le dossier d'inscription se replie de la même façon (B14, 2026-08-19), mais
   sur sa propre bascule : les deux cartes se suivent, et un sélecteur commun
   les ouvrirait ensemble. Il se replie **quand il est complet** — un dossier
   qui ne demande rien n'a pas à occuper une page déjà longue. */
#toggle-inscription:checked + .panel .carte-corps { display: block; }
#toggle-inscription:checked + .panel > h2:first-child { margin-bottom: var(--s4); }
#toggle-inscription:checked + .panel .quand-replie { display: none; }
#toggle-inscription:checked + .panel .quand-deplie { display: inline; }

/* Ce que l'application sait déjà, et ce que le formateur a écrit : des paires
   étiquette/valeur, en deux colonnes tant que la place le permet. */
.inscription-connu {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s2) var(--s4);
  margin: 0 0 var(--s3);
}
.inscription-connu dt {
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.inscription-connu dd { margin: 2px 0 0; font-size: 13.5px; color: var(--ink); }
/* L'essentiel du dossier, dans sa carte (T30, 2026-10-31) : une adresse longue
   se coupe plutôt que de pousser la colonne, et le travail prend toute la
   largeur — c'est la seule ligne qui porte une phrase. */
.inscription-resume dd { overflow-wrap: anywhere; }
.inscription-resume > div:last-child:not(:first-child):not(:nth-child(2)) { grid-column: 1 / -1; }

/* Un champ prérempli depuis un autre dossier en plein temps : la mention se
   tient sous le champ, en gris, pour qu'on sache d'où vient ce qu'on n'a pas
   tapé — sans en faire un avertissement (2026-08-21). */
.inscription-repris { display: block; margin-top: 4px; color: var(--muted); }

/* L'astérisque des champs obligatoires (2026-09-04, précision de R06). Il tient
   au libellé sans le pousser — d'où la marge à gauche seule — et prend l'encre
   foncée du rouge, qui tient 6,8:1 sur le papier comme sur les fonds teintés.
   `abbr` porte l'infobulle « Champ obligatoire » ; le soulignement pointillé
   que les navigateurs lui ajoutent d'office n'a rien à faire sur un astérisque
   d'une seule lettre. */
.champ-requis {
  margin-left: 2px;
  color: var(--warn-ink);
  font-weight: 600;
  text-decoration: none;
  border: 0;
  cursor: help;
}

/* Les encarts dont les champs tiennent sur une ligne — les coordonnées de la
   société, la date du TPP. Le même quadrillage que la liste au-dessus, pour
   que les deux encarts se lisent d'un seul mouvement (2026-08-19).
   `.field + .field` pose une marge haute quand ils s'empilent : elle n'a plus
   lieu d'être quand la grille les range côte à côte. */
.inscription-ligne {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s3) var(--s4);
}
.inscription-ligne .field + .field { margin-top: 0; }
.version-depot { margin: var(--s2) 0 0; font-size: 12.5px; font-style: italic; color: var(--ink-2); }

/* Les pièces d'un lot (2026-09-04, R04/R05). Trois lignes là où il y en avait
   une : le nom de la pièce ouvre chacune, sans quoi trois fichiers à la suite
   ne diraient pas lequel est l'énoncé.

   **Cinq colonnes alignées** depuis le soir même, à la demande de
   l'utilisateur : « il serait mieux que le nom du fichier, le poids, la version
   et le lien Télécharger soient dans un tableau invisible aligné ». Les tailles
   et les liens tombaient auparavant où le nom du fichier les laissait, et l'œil
   n'avait aucun repère d'une ligne à l'autre.

   Une grille et non un `<table>` : ce ne sont pas des données tabulaires mais
   trois lignes de même forme, et un tableau leur imposerait une sémantique — et
   un en-tête — dont elles n'ont pas besoin. `display: contents` sur la ligne
   laisse ses cellules entrer dans la grille du parent. */
/* Les colonnes **se serrent contre le nom** (2026-09-04, second regard de
   l'utilisateur : « les informations sur le poids, la version, etc. doivent se
   trouver plus proche du nom du document »).

   Le premier essai donnait `1fr` à la colonne du nom : elle absorbait toute la
   largeur de la carte, et la taille, la version et le lien se retrouvaient
   plaqués au bord droit — à un demi-écran du fichier qu'ils décrivent. La
   colonne se dimensionne maintenant sur **le plus long des noms**, plafonnée à
   46 caractères ; `justify-content: start` laisse le vide à droite plutôt que
   de l'insérer entre les colonnes.

   Le plafond est ce qui rend la troncature utile : au-delà, le nom se coupe
   d'une ellipse et garde son infobulle, au lieu d'étirer la grille. */
.version-pieces {
  display: grid;
  grid-template-columns: max-content minmax(0, 46ch) max-content max-content max-content;
  justify-content: start;
  align-items: baseline;
  gap: 3px var(--s3);
  margin: var(--s2) 0;
}
.version-pieces .version-fichier { display: contents; }
.version-piece {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Un nom trop long se coupe plutôt que de pousser les colonnes — voir
   `.version-nomfichier` plus haut, où la troncature est déclarée une fois pour
   toutes. C'est `minmax(0, 1fr)` sur la colonne qui la rend possible : sans le
   `0`, la grille refuse de descendre sous la largeur du contenu. */
.version-taille, .version-telecharger { white-space: nowrap; }
.version-neuve  { font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
/* Une pièce reprise du lot précédent le dit : sans cette mention, on croirait
   le formateur avoir tout redéposé à chaque fois. */
.version-repris {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--muted);
  font-style: italic;
  cursor: help;
}
/* Ce qui manque pour que le lot parte, en tête de la carte des versions. */
.carte-manque { font-weight: 600; }

.fil { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s3); }
.fil-entree {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 10px 12px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--paper-2);
}
/* Un avis n'est pas un commentaire : il porte une décision. Le filet de gauche
   le distingue sans le sortir du fil. */
.fil-entree--avis { box-shadow: inset 2px 0 0 var(--line); }

/* Une teinte par personne (2026-08-07) : on repère qui a écrit sans lire les
   noms. Six fonds très pâles, attribués sur l'identifiant, donc stables d'un
   dossier à l'autre.

   **Aucune ne porte de sens** — ce ne sont pas des états. Les couleurs qui
   signifient quelque chose restent les `evt-*` et les jetons, et elles ne
   figurent pas dans cette liste. Toutes sont assez claires pour que le texte
   du fil (`--ink-2`, 9.4:1 sur blanc) y reste très au-dessus de 4.5:1.

   **L'ordre compte** : les teintes sont attribuées dans l'ordre d'apparition
   des personnes, donc deux voisines de cette liste se retrouvent souvent côte à
   côte à l'écran. Elles sont rangées pour que les teintes successives soient les
   plus éloignées possible — bleu, ambre, violet, vert, rose, gris. Le premier
   essai plaçait le vert et le sarcelle à deux rangs d'écart, et un expert s'est
   retrouvé avec la même couleur qu'un responsable (2026-08-07). */
.fil-entree--t1 { background: #eef3fd; border-color: #dbe5f8; }  /* bleu   */
.fil-entree--t2 { background: #fdf6ea; border-color: #f3e6cd; }  /* ambre  */
.fil-entree--t3 { background: #f4f0fc; border-color: #e3daf6; }  /* violet */
.fil-entree--t4 { background: #ecf7f0; border-color: #d3e9dc; }  /* vert   */
.fil-entree--t5 { background: #fdeef3; border-color: #f5dae4; }  /* rose   */
.fil-entree--t6 { background: #f2f4f7; border-color: #e0e5ec; }  /* gris   */
/* La pastille d'initiales reprend le fond de sa ligne, en un cran plus soutenu. */
.fil-entree--t1 .fil-pastille { background: #dbe5f8; }
.fil-entree--t2 .fil-pastille { background: #f3e6cd; }
.fil-entree--t3 .fil-pastille { background: #e3daf6; }
.fil-entree--t4 .fil-pastille { background: #d3e9dc; }
.fil-entree--t5 .fil-pastille { background: #f5dae4; }
.fil-entree--t6 .fil-pastille { background: #e0e5ec; }
.fil-pastille {
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9.5px;
  font-weight: 600;
}
.fil-corps { min-width: 0; flex: 1; }
.fil-auteur { margin: 0; font-size: 11.5px; font-weight: 600; color: var(--ink); }
.fil-qualite { font-weight: 400; color: var(--muted); }
.fil-marque { margin-left: var(--s2); font-size: 11px; font-weight: 600; }
.fil-texte { margin: 4px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--ink-2); }
.fil-retrait { flex: none; margin: 0; }

/* La portée d'un commentaire (2026-09-04, R10), sur la ligne de son auteur et
   seulement quand elle n'est pas publique : marquer « Public » les trois quarts
   du fil n'apprendrait rien. Ce qui compte, c'est de savoir qu'un mot ne
   s'adresse pas à tout le monde — avant de répondre en croyant que si.
   Le mauve du dossier, qui ne dit ni une faute ni un succès : une audience. */
.fil-portee {
  margin-left: var(--s2);
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--mauve-soft, var(--surface-2));
  color: var(--ink-2);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .02em;
  cursor: help;
}

/* Ce qu'un commentaire enrichi a le droit de contenir (R07). La liste suit
   celle de `COMMENTAIRE_BALISES` : ce que le serveur laisse passer, la feuille
   sait le dessiner, et rien de plus. */
.fil-riche { margin: 4px 0 0; }
/* Un message long se coupe à cinq lignes, avec un dégradé qui dit qu'il
   continue (2026-09-04, demande de l'utilisateur). C'est `assets/fil.js` qui
   pose la classe — lui seul sait si le texte dépasse vraiment, et un
   « Afficher plus… » sous un message de trois mots serait pire que rien.
   Sans script, aucune classe n'est posée et le message paraît en entier. */
.fil-riche--replie { overflow: hidden; position: relative; }
.fil-riche--replie::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 22px;
  background: linear-gradient(to bottom, transparent, var(--paper));
  pointer-events: none;
}
.fil-plus { margin-top: 2px; font-size: 11.5px; }
.fil-riche p { margin: 0 0 6px; }
.fil-riche p:last-child { margin-bottom: 0; }
.fil-riche ul, .fil-riche ol { margin: 4px 0 6px; padding-left: 20px; }
.fil-riche li { margin: 2px 0; }
.fil-riche a { color: var(--accent); }

/* ---------- Ce qui se replie dans la carte des versions ---------- */

/* **Seule la version courante est dépliée** (2026-09-04, sur copie d'écran de
   l'utilisateur : « les anciennes versions devraient être repliées pour ne voir
   que le label Version X »). Un dossier de trois versions montrait trois fois
   les pièces, trois fils et trois éditeurs — plusieurs écrans pour un seul
   travail en cours.

   `+` et non `~` : le voisinage général atteindrait toutes les versions
   suivantes, et en déplier une les déplierait toutes. La leçon a déjà été payée
   sur les modales (section 7 bis). */
.version-bascule { display: none; }
.version-bascule + .version .version-detail { display: none; }
.version-bascule:checked + .version .version-detail { display: block; }
/* Classes propres à la version. Les `.quand-replie` communes sont visées par
   `#toggle-versions:checked ~ .panel .quand-replie`, qui balaie **tout** le
   panneau : le bouton d'une version restait sur « Masquer » quelle que soit la
   sienne. Deux bascules imbriquées ne peuvent pas partager leurs classes
   d'état — le sélecteur de la plus large gagne toujours. */
.version-si-deplie { display: none; }
.version-bascule:checked + .version .version-si-replie { display: none; }
.version-bascule:checked + .version .version-si-deplie { display: inline; }

/* Une version repliée s'efface d'un ton : c'est de l'histoire, pas du travail
   en cours. Elle garde sa pastille et son état — ce qu'on cherche en parcourant
   un historique, c'est où ça a coincé. */
.version--ancienne .version-numero { font-weight: 560; color: var(--ink-2); }
.version-resume { font-size: 11.5px; color: var(--muted); }
.version-plier {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--accent);
  cursor: pointer;
  white-space: nowrap;
}
.version-plier:hover { text-decoration: underline; }

/* L'éditeur ne s'ouvre qu'à la demande (même retour) : « le champ texte enrichi
   ne devrait être visible que lorsqu'on appuie sur le bouton Commentaire ».
   Le bouton s'efface quand le formulaire paraît — c'est le même geste, et deux
   commandes pour une seule intention se lisent mal. */
.fil-bascule { display: none; }
.fil-bascule ~ .fil-forme { display: none; }
.fil-bascule:checked ~ .fil-forme { display: flex; }
.fil-bascule:checked ~ .fil-commenter { display: none; }
/* `align-self` parce que `.fil` est une colonne flex : ses enfants s'étirent
   par défaut, et le bouton prenait toute la largeur de la carte malgré son
   `width: auto` — un `inline-block` étiré reste étiré (constaté sur copie
   d'écran, 2026-09-04). */
.fil-commenter { align-self: flex-start; margin-top: var(--s2); }
.fil-annuler { margin-right: var(--s3); }

/* La demande de corrections se replie de la même façon (2026-09-04, dernier
   temps de l'allègement) : un champ de texte large, ouvert en permanence sous
   chaque dossier, était le dernier pavé d'une carte qu'on venait d'épurer.

   Deux gestes qui se ressemblent — on écrit quelque chose à quelqu'un — se
   prennent de la même façon : même bouton discret, même effacement quand le
   champ paraît, même « Annuler ». */
.corr-bascule { display: none; }
.corr-bascule ~ .corr-panneau { display: none; }
.corr-bascule:checked ~ .corr-panneau { display: block; }
.corr-bascule:checked ~ .corr-ouvrir { display: none; }
.corr-ouvrir { margin-top: var(--s3); }
.corr-annuler { margin-right: var(--s3); white-space: nowrap; }

.fil-forme { display: flex; gap: 9px; margin: 2px 0 0; }
.fil-forme input[type="text"] {
  flex: 1;
  min-width: 0;
  padding: 10px 11px;
  font: inherit;
  font-size: 12.5px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
}

/* Le formulaire du fil est passé en colonne le 2026-09-04 : la zone de saisie
   au-dessus, la portée et le bouton en pied. Une ligne unique ne tenait plus —
   le champ a grandi (R07) et le sélecteur de portée s'y est ajouté (R10). */
.fil-forme--riche { flex-direction: column; gap: var(--s2); }
.fil-forme--riche textarea {
  width: 100%;
  padding: 10px 11px;
  font: inherit;
  font-size: 12.5px;
  line-height: 1.5;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  resize: vertical;
}
.fil-forme-pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
}
/* Renoncer et valider sont les deux issues du même geste : elles se tiennent
   côte à côte, dans leur propre boîte. Trois éléments directs dans un
   `space-between` en laissent forcément un au milieu — « Annuler » flottait
   entre la portée et le bouton, sans rattachement visible (copie d'écran de
   l'utilisateur, 2026-09-04). */
.fil-forme-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
  margin-left: auto;
}
.fil-portee-choix {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  margin: 0;
  font-size: 11.5px;
  font-weight: 400;
  color: var(--muted);
}
/* « Visible par » se cassait en deux lignes et désalignait le menu : deux mots
   qui nomment un champ ne se coupent pas. */
.fil-portee-choix > span { white-space: nowrap; }
.fil-portee-choix select {
  padding: 6px 9px;
  font: inherit;
  font-size: 11.5px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  color: var(--ink);
}

/* ---------- L'éditeur des commentaires (R07, 2026-09-04) ---------- */

/* Il n'existe que si le script s'est chargé : sans lui, la zone de texte reste
   visible et fait le travail. Rien ici n'est donc nécessaire à l'usage — c'est
   de l'agrément, et cela se voit dans le dessin : une barre discrète posée sur
   un cadre qui reprend celui du champ qu'il remplace. */
.editeur {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  overflow: hidden;
}
.editeur-barre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  padding: 5px 6px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
}
/* `display` et `width` sont repris explicitement : la règle générale des
   boutons (§ 8) les pose en `block` sur toute la largeur — c'est pourquoi
   l'application écrit `style="width:auto"` partout ailleurs. Sans ces deux
   lignes, chacun des sept outils prenait sa propre ligne et la barre se lisait
   en colonne (constaté par l'utilisateur, 2026-09-04). */
.editeur-bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: auto;
  min-width: 28px;
  height: 28px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.editeur-bouton:hover,
.editeur-bouton:focus-visible {
  border-color: var(--line);
  background: var(--paper);
  color: var(--ink);
}
/* Chaque bouton porte sa propre mise en forme : « G » en gras, « I » en
   italique. C'est l'usage, et cela vaut mieux qu'une icône à deviner. */
.editeur-bouton--gras      { font-weight: 700; }
.editeur-bouton--italique  { font-style: italic; }
.editeur-bouton--souligne  { text-decoration: underline; }

/* Le compteur ne paraît qu'à l'approche du plafond : un chiffre qui descend de
   vingt mille à dix-neuf mille neuf cent quatre-vingts n'intéresse personne. */
.editeur-compteur {
  margin-left: auto;
  font-size: 10.5px;
  color: var(--muted);
}
.editeur-compteur--trop { color: var(--warn-ink); font-weight: 600; }

.editeur-zone {
  min-height: 84px;
  max-height: 380px;
  overflow-y: auto;
  padding: 10px 11px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink);
}
.editeur-zone:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* L'invite du champ d'origine, rendue à la main : `::placeholder` ne s'applique
   pas à un `contenteditable`. Elle disparaît dès qu'un enfant existe. */
.editeur-zone:empty::before {
  content: attr(data-invite);
  color: var(--muted);
}
.editeur-zone--vide { outline: 2px solid var(--warn); outline-offset: -2px; }
.editeur-zone p { margin: 0 0 6px; }
.editeur-zone ul, .editeur-zone ol { margin: 4px 0 6px; padding-left: 20px; }
.editeur-zone a { color: var(--accent); }

/* L'avis se rend sous la version qu'il juge, en deux formulaires côte à côte —
   valider à gauche, demander des corrections à droite. Il occupait auparavant
   un panneau bleu pleine largeur en tête de page, qui pesait plus lourd que le
   dossier lui-même (2026-08-07). */
.version-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
}
.version-actions form { margin: 0; }
/* `align-items: center` : sans lui les enfants s'étirent sur la hauteur du
   champ, et le texte d'« Annuler » se collait en haut de sa boîte au lieu de
   s'aligner sur le bouton (constaté sur copie d'écran, 2026-09-04). */
.version-corrections {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  min-width: 240px;
  gap: var(--s2);
}
/* Une zone de plusieurs lignes depuis le 2026-10-31 (T03) : elle prend toute
   la largeur, « Annuler » et « Envoyer » passent dessous, à droite. */
.version-corrections textarea {
  flex: 1 1 100%;
  min-width: 0;
  resize: vertical;
  padding: 9px 11px;
  font: inherit;
  font-size: 12.5px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
}
.version-note { margin: var(--s2) 0 0; font-size: 11.5px; color: var(--muted); }

/* ---------- Grille d'évaluation ---------- */

.eval { display: flex; flex-direction: column; }
.eval-ligne {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 58px 78px;
  gap: var(--s3);
  align-items: center;
  padding: 11px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 12.5px;
}
.eval-ligne label { margin: 0; font-weight: 500; }
.eval-pas { display: block; font-size: 11px; font-weight: 400; color: var(--muted); }
.eval-poids { font-family: var(--mono); font-size: 11.5px; color: var(--muted); text-align: right; }
.eval-champ {
  width: 100%;
  padding: 7px 0;
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
}
.eval-champ--lecture { border-color: transparent; background: none; }
.eval-ligne--total { border-bottom: 0; padding-bottom: 0; }
.eval-total { font-family: var(--mono); font-size: 20px; font-weight: 700; text-align: center; }

/* La note finale porte ses propres verts et rouges, plus clairs que ceux des
   états (2026-08-07 : « c'est trop sombre »). `--ok` et `--warn` sont calibrés
   pour du petit texte et pour des aplats ; sur un nombre de 20 px ils
   s'alourdissent. Ceux-ci tiennent 5.0:1 et 5.3:1 sur blanc — au-dessus du
   seuil du projet, tout en respirant nettement plus. */
.eval-total.evt-ok      { color: #15803d; }
.eval-total.evt-error   { color: #cc2b2b; }
.eval-total.evt-neutral { color: var(--faint); }

.eval-actions {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
}
.eval-actions input[type="text"] {
  flex: 1;
  min-width: 0;
  padding: 10px 11px;
  font: inherit;
  font-size: 12.5px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
}
.eval-actions .btn, .eval-actions button { flex: none; }

.examen-saisie {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--s3);
  margin-bottom: var(--s3);
}

/* ---------- Pièces du dossier d'examen ---------- */

/* L'appel du protocole de visite, en tête de sa propre ligne : sous le nom de
   la pièce, avant les visites déjà consignées. Un bouton à la mesure de la
   ligne — celui du formulaire ferait deux fois la hauteur du reste. */
.piece-appel { margin: 6px 0 4px; }
.piece-appel .btn {
  width: auto;
  padding: 2px var(--s2);
  font-size: 11px;
  line-height: 1.6;
}

.pieces-groupe {
  margin: var(--s4) 0 var(--s2);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink-2);
}
.pieces { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); }
.piece {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper-2);
}
/* Une ligne sans fichier se dessine en pointillé : elle attend quelque chose,
   elle ne contient rien. Une ligne pourvue passe au vert pâle (2026-08-07) —
   avec le seul texte du fichier, on ne voyait pas qu'on venait d'en déposer un.
   C'est ici une couleur qui **dit** quelque chose : la pièce est là. */
.piece--vide { border-style: dashed; background: none; }
.piece--pourvue { background: var(--ok-soft); border-color: var(--ok-line); }
.piece-fichier {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
}
.pieces-formats { margin: var(--s3) 0 0; color: var(--muted); }
.piece-vignette {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
  background: var(--ok-soft);
  color: var(--ok-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 600;
}
.piece-vignette--vide {
  background: var(--surface-2);
  color: var(--faint);
  font-family: var(--sans);
  font-size: 15px;
}
.piece-corps { min-width: 0; flex: 1; }
.piece-nom { margin: 0; font-size: 12px; font-weight: 600; }
.piece-requis { color: var(--wait); font-weight: 500; }
.piece-etat { margin: 2px 0 0; font-size: 11px; color: var(--muted); word-break: break-word; }
/* Le champ prend toute la largeur de sa ligne depuis le 2026-09-04 : à 11 px et
   sur une largeur libre, le nom du fichier choisi — que le navigateur écrit
   lui-même à côté du bouton — se tronquait au point de ne plus se voir.
   C'est ce que l'utilisateur a signalé le soir de la livraison : « rien ne se
   passe, il ne me montre pas le document ». */
.piece-champ { display: block; width: 100%; margin-top: 6px; font-size: 12px; }

/* Les lignes de dépôt d'un lot (R04/R05) : trois cadres qui se suivent, avec de
   l'air entre eux — collés, on ne voyait pas qu'il y en avait plusieurs. */
.depot-pieces { display: flex; flex-direction: column; gap: var(--s3); }
.depot-compte { font-style: italic; }

/* Le dépôt partagé : les autres candidats qui recevront le même énoncé
   (2026-08-21). Un cadre discret sous le fichier — c'est une extension du
   dépôt, pas une seconde décision de même poids. */
.depot-partage {
  margin-top: var(--s4);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.depot-partage legend {
  padding: 0 6px;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.depot-partage p { margin: 0 0 var(--s2); }
.depot-partage-ligne {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 4px 0;
  font-size: 13px;
  cursor: pointer;
}
.depot-partage-ligne input { width: auto; margin: 0; }

/* Ligne d'ajout d'une règle de rappel : tout tient sur un rang tant que la
   place le permet, et passe à la ligne sans rien casser quand elle manque. */
.rappel-ajout {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--s3);
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
}
.rappel-ajout .champ-select, .rappel-ajout input[type="number"] { width: auto; }
.rappel-case {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-bottom: 9px;
  font-size: 13px;
  white-space: nowrap;
}
.rappel-case input { width: auto; }

/* En-tête d'une modale de message : de quelle règle il s'agit. Sur vingt-quatre
   règles qui se ressemblent, c'est ce qui dit laquelle on tient. */
.modele-entete {
  grid-template-columns: 130px 1fr;
  gap: var(--s2) var(--s4);
  padding: var(--s3) var(--s4);
  margin-bottom: var(--s4);
  background: var(--surface-2);
}

/* Onglets en CSS pur : deux boutons radio devant, les panneaux derrière, et
   des sélecteurs de voisinage pour montrer celui qui va avec. Aucun script —
   la règle du projet vaut ici comme ailleurs (section 8). */
.onglet-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;      /* invisible mais focalisable : l'onglet reste au clavier */
}
.onglets {
  display: flex;
  gap: var(--s2);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s4);
}
.onglet {
  padding: 8px 14px;
  margin-bottom: -1px;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  color: var(--muted);
  font-size: 13px;
  font-weight: 560;
  cursor: pointer;
}
.onglet:hover { color: var(--accent); }
.onglet-panneau { display: none; }
/* Chaque paire d'onglets ajoute son nom aux trois listes ci-dessous : le
   sélecteur doit nommer la classe pour relier l'input à son label et à son
   panneau, il n'y a pas de version générique sans `:has()`. Paires en place :
   redaction/champs (le message d'une règle), calendrier/rappels (l'écran du
   Responsable), apercu/motdepasse (la carte « Votre sécurité » du profil). */
/* `~ *` et non `~ .onglets` : depuis que le profil loge ses onglets dans la
   barre de titre de sa carte (2026-08-11), la rangée n'est plus le frère
   direct des boutons radio mais leur nièce. Les classes nommées ici sont assez
   précises pour que « n'importe quel frère suivant » ne rattrape rien d'autre. */
.onglet-input--redaction:checked ~ * .onglet--redaction,
.onglet-input--champs:checked ~ * .onglet--champs,
.onglet-input--apercu:checked ~ * .onglet--apercu,
.onglet-input--motdepasse:checked ~ * .onglet--motdepasse,
.onglet-input--calendrier:checked ~ * .onglet--calendrier,
.onglet-input--rappels:checked ~ * .onglet--rappels,
.onglet-input--seances:checked ~ * .onglet--seances {
  background: var(--paper);
  border-color: var(--line);
  color: var(--ink);
}
.onglet-input--redaction:checked ~ .onglet-panneau--redaction,
.onglet-input--champs:checked ~ .onglet-panneau--champs,
.onglet-input--apercu:checked ~ .onglet-panneau--apercu,
.onglet-input--motdepasse:checked ~ .onglet-panneau--motdepasse,
.onglet-input--calendrier:checked ~ .onglet-panneau--calendrier,
.onglet-input--rappels:checked ~ .onglet-panneau--rappels,
.onglet-input--seances:checked ~ .onglet-panneau--seances { display: block; }
.onglet-input--redaction:focus-visible ~ * .onglet--redaction,
.onglet-input--champs:focus-visible ~ * .onglet--champs,
.onglet-input--apercu:focus-visible ~ * .onglet--apercu,
.onglet-input--motdepasse:focus-visible ~ * .onglet--motdepasse,
.onglet-input--calendrier:focus-visible ~ * .onglet--calendrier,
.onglet-input--rappels:focus-visible ~ * .onglet--rappels,
.onglet-input--seances:focus-visible ~ * .onglet--seances {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* La variante segmentée : même mécanique, l'habillage du sélecteur de session
   (§ 9 bis). Elle sert quand les onglets vivent dans une barre de titre plutôt
   qu'au-dessus de leur panneau — un soulignement y flotterait sans rien
   souligner. */
.onglets--segmente {
  gap: 3px;
  padding: 3px;
  border-bottom: none;
  border-radius: var(--radius);
  margin-bottom: 0;
  background: var(--surface-2);
}
.onglets--segmente .onglet {
  margin-bottom: 0;
  padding: 6px 13px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
}
/* Quatre classes : il faut passer devant la règle générique ci-dessus, qui en
   compte trois. */
.onglet-input--apercu:checked ~ * .onglets--segmente .onglet--apercu,
.onglet-input--motdepasse:checked ~ * .onglets--segmente .onglet--motdepasse {
  border-color: transparent;
  box-shadow: var(--lift-1);
  color: var(--accent);
  font-weight: 620;
}

/* L'aperçu du message : on le montre tel qu'il partira, sauts de ligne compris,
   d'où le `pre`. Il se replie sur les longues lignes plutôt que de pousser une
   barre de défilement horizontale sous le panneau. */
.rappel-apercu {
  margin: 0;
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font: 13px/1.6 var(--mono);
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---------- Colonne d'appoint ---------- */

.date-ligne { display: flex; align-items: center; gap: 11px; margin-bottom: var(--s3); }
.date-ligne:last-of-type { margin-bottom: 0; }
.date-puce {
  flex: none;
  width: 40px;
  height: 44px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.date-puce strong { font-size: 15px; line-height: 1; }
.date-puce span { font-family: var(--mono); font-size: 8.5px; color: var(--muted); }
.date-nom { display: block; font-size: 12.5px; font-weight: 600; }
.date-detail { display: block; margin-top: 2px; font-size: 11.5px; color: var(--muted); }

/* Ce qui manque au calendrier de l'examen (2026-09-08, demande de
   l'utilisateur : « une couleur rouge très claire et douce qui montre que les
   dates d'examen, heures, soutenance, etc. ne sont pas encore remplies »).

   **Le rouge y dit un manque, pas une faute** — c'est le deuxième endroit de
   l'application où il ne dit pas une faute, après la récusation. Le choix est
   celui de l'utilisateur, et il se défend : une date d'examen absente n'est pas
   une attente qui se résoudra d'elle-même, c'est un trou qui bloque la
   validation de l'énoncé et, pour l'heure de début, son envoi au candidat. Le
   ton le plus pâle de la palette (`--warn-soft`, 6,8:1 avec `--warn-ink`) : on
   doit le repérer en parcourant la colonne, pas le recevoir comme une alarme. */
.panel--incomplet > h2:first-child { color: var(--warn); }
.panel--incomplet > h2:first-child::after {
  content: "";
  flex: 1 1 auto;
  height: 2px;
  background: var(--warn-line);
  border-radius: 1px;
}

/* La ligne d'une date qu'on attend encore : la puce garde sa place — le
   calendrier ne se réorganise pas quand une date arrive — et se creuse. */
.date-ligne--vide .date-puce {
  background: var(--warn-soft);
  border-color: var(--warn-line);
  border-style: dashed;
}
.date-ligne--vide .date-puce strong { color: var(--warn-line); }
.date-ligne--vide .date-nom { color: var(--muted); }
.date-ligne--vide .date-detail { color: var(--warn-ink); }

.dates-manque {
  margin: 0 0 var(--s3);
  padding: var(--s3);
  font-size: 12.5px; line-height: 1.45; color: var(--warn-ink);
  background: var(--warn-soft);
  border: 1px solid var(--warn-line);
  border-radius: var(--radius);
}
.dates-manque:last-child { margin-bottom: 0; }
/* Les renseignements facultatifs : le même rouge, sans le cadre. Ils ne
   bloquent rien, et un encadré de plus dans une carte étroite ferait deux
   alarmes pour deux poids différents. */
.dates-manque--doux {
  margin-top: var(--s3);
  padding: 0; border: 0; background: none;
  color: var(--muted);
}
.dates-manque--doux .small { display: block; margin-top: 2px; color: var(--warn-ink); }

/* Le champ qu'on n'a pas encore rempli, dans le formulaire de report. Assez
   pâle pour qu'un formulaire entièrement vide reste lisible — c'est l'état
   normal d'un dossier qui commence. */
.champ-vide { background: var(--warn-soft); border-color: var(--warn-line); }
.champ-vide:focus { background: var(--paper); }

.college-ligne { display: flex; align-items: center; gap: var(--s3); margin-bottom: 11px; }
.college-ligne:last-child { margin-bottom: 0; }
.college-ligne--hors {
  padding-top: 11px;
  border-top: 1px solid var(--line-soft);
}
/* Le Formateur n'est pas du collège : il a son propre intertitre dans la même
   carte, plutôt qu'une ligne de plus sous les experts. */
.college-intertitre {
  margin: var(--s4) 0 var(--s3);
  padding-top: var(--s3);
  border-top: 1px solid var(--line-soft);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}
.college-intertitre + .college-ligne { border-top: 0; padding-top: 0; }

/* Le compte des places, quand il en manque. Rouge pâle : c'est un oubli qui se
   rattrape, pas un état du dossier — et il vaut mieux le voir ici, là où la
   place se pourvoit, que sur le seul tableau de bord du Responsable. */
.college-manque {
  margin: 0 0 var(--s3);
  padding: var(--s2) var(--s3);
  border-radius: var(--radius-sm);
  background: var(--warn-soft);
  color: var(--warn-ink);
  font-weight: 500;
}

/* Le premier intertitre suit la barre de titre du panneau ou le bouton du
   formulaire : son filet y ferait double emploi. Le second, lui, sépare
   vraiment deux demi-journées et le garde. */
.college-intertitre--premier {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}
.college-pastille {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
}
.college-pastille--moi  { background: var(--accent-soft); color: var(--accent); }
.college-pastille--hors { color: var(--faint); }
.college-corps { flex: 1; min-width: 0; }
.college-nom { display: block; font-size: 12.5px; font-weight: 600; }
.college-role { display: block; margin-top: 2px; font-size: 11px; color: var(--muted); }

.champ-select {
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}

/* --------------------------------------------------------------------------
   13. Frise d'avancement
   -------------------------------------------------------------------------- */
/* Cinq étapes depuis B4 : la communication de la note prolonge le cycle au-delà
   de l'énoncé validé.

   **Chaque barre vit dans sa propre cellule** (2026-08-07). Auparavant le trait
   de liaison était un pseudo-élément qui remontait `left: -100%`, soit une
   colonne entière vers la gauche : dès que la frise passait sur deux rangées, le
   trait de la première étape de la seconde rangée traversait la page jusque dans
   le bandeau de navigation. Une barre contenue dans sa cellule ne peut plus
   déborder, quel que soit le nombre d'étapes ou de rangées. */
.frise {
  margin: 0;
  padding: var(--s5) 0 var(--s2);
  list-style: none;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--s5) 0;
}

.frise-etape {
  position: relative;
  padding-right: var(--s3);
}

.frise-barre {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: var(--line);
}
.frise-etape--done    .frise-barre { background: var(--ok); }
.frise-etape--refuse  .frise-barre { background: var(--warn); }
.frise-etape--current .frise-barre { background: var(--accent-line); }

.frise-pastille {
  position: absolute;
  top: -4px;
  left: 0;
  width: 11px;
  height: 11px;
  border-radius: var(--radius-pill);
  background: var(--line);
}
.frise-etape--done    .frise-pastille { background: var(--ok); }
.frise-etape--refuse  .frise-pastille { background: var(--warn); }
.frise-etape--current .frise-pastille {
  background: var(--paper);
  border: 3px solid var(--accent);
  width: 13px;
  height: 13px;
  top: -5px;
}

/* Libellé sur deux niveaux : nom d'abord, état ensuite. Plus de retours à la
   ligne intempestifs, les cinq colonnes gardent la même hauteur. */
.frise-nom {
  display: block;
  margin-top: 12px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
  text-wrap: balance;
}
.frise-detail { display: block; margin-top: 3px; font-size: 11.5px; color: var(--muted); }
.frise-etape--done    .frise-detail { color: var(--muted); }
.frise-etape--current .frise-nom    { color: var(--accent); }
.frise-etape--current .frise-detail { color: var(--ink-2); font-weight: 560; }
.frise-etape--refuse  .frise-detail { color: var(--warn-ink); }

/* La pré-validation, sous l'étape du Responsable et nulle part ailleurs
   (2026-09-04, R03). C'est une **mention**, pas une étape : elle ne prend ni
   pastille, ni barre, ni place dans la numérotation.

   Elle **remplace** le détail de l'étape au lieu de s'y ajouter — le premier
   essai empilait « En cours » et « Pré-validé », deux états contradictoires
   pour une seule étape (copie d'écran de l'utilisateur, le jour même). Elle
   occupe donc la ligne du détail, avec le même retrait.

   L'ambre : quelqu'un a regardé, personne n'a tranché. C'est exactement ce que
   la pré-validation dit — un travail commencé, pas un dossier avancé. */
.frise-preval {
  display: inline-block;
  margin-top: 3px;
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .02em;
  white-space: nowrap;
  background: var(--wait-soft);
  color: var(--wait);
  cursor: help;
}
.frise-etape--todo    .frise-nom,
.frise-etape--todo    .frise-detail { color: var(--faint); }

/* L'échéance de l'étape, sous son état. Elle porte sa propre couleur — rouge
   une fois dépassée, ambre quand elle approche — et prime donc sur le
   grisé des étapes à venir : une échéance qui s'annonce reste lisible même
   sur une étape qu'on n'a pas encore atteinte. */
.frise-echeance { display: block; font-size: 12px; margin-top: 2px; }
.frise-etape--todo .frise-echeance.evt-neutral { color: var(--faint); }

/* --------------------------------------------------------------------------
   14. Avis des experts
   -------------------------------------------------------------------------- */
.avis-liste { margin: 0; padding: 0; list-style: none; }
.avis-liste > li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 14px;
}
.avis-liste > li:last-child { border-bottom: 0; }

.avis-marque {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-size: 12.5px;
}
.avis-marque::before {
  content: "";
  width: 18px;
  height: 18px;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
}
.avis-marque--ok            { color: var(--ok-ink); }
.avis-marque--ok::before    { content: "✓"; background: var(--ok-soft); color: var(--ok); }
.avis-marque--attente       { color: var(--wait); }
.avis-marque--attente::before { content: "⋯"; background: var(--wait-soft); color: var(--wait); }
.avis-marque--refuse        { color: var(--warn-ink); }
.avis-marque--refuse::before { content: "✗"; background: var(--warn-soft); color: var(--warn); }
/* L'expert n'a pas encore été sollicité : rien ne se joue de son côté, donc
   ni ambre ni couleur d'état — le même gris que les étapes à venir. */
.avis-marque--neutre        { color: var(--muted); }
.avis-marque--neutre::before { content: "–"; background: var(--surface-2); color: var(--faint); }

/* États de texte du journal et des historiques */
.evt-ok      { color: var(--ok-ink); }
.evt-error   { color: var(--warn-ink); }
.evt-warn    { color: var(--wait); }
.evt-info    { color: var(--accent-ink); }   /* Q5 : ce qui manque, avant l'échéance */
.evt-neutral { color: var(--muted); }

/* --------------------------------------------------------------------------
   15. Double authentification — écrans de référence
   -------------------------------------------------------------------------- */
.code-input {
  width: 100%;
  padding: var(--s3) var(--s3) var(--s3) calc(var(--s3) + .34em);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
  font-family: var(--mono);
  font-size: 30px;
  letter-spacing: .34em;
  text-align: center;
  color: var(--ink);
}
.code-input:focus {
  background: var(--paper);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.countdown {
  height: 4px;
  border-radius: 2px;
  background: var(--line-soft);
  overflow: hidden;
}
.countdown > * { display: block; height: 100%; background: var(--accent); }
.countdown-label { font-size: 11.5px; color: var(--muted); }

.secret-box {
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-family: var(--mono);
  font-size: 15px;
  letter-spacing: .06em;
  word-break: break-all;
  color: var(--ink);
}

.qr-wrap {
  align-self: center;
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper);
  line-height: 0;
}
.qr-wrap img { display: block; width: 180px; height: 180px; }

.codes-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s2);
  font-family: var(--mono);
  font-size: 14px;
}
.codes-grid > * {
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--paper-2);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   15 bis. L'écran des candidats
   Maquette « Écran candidats », variante 1a (2026-08-10) : la barre de filtres
   et le tableau ne font plus qu'une carte. Ils vivaient dans deux panneaux
   séparés, et l'on filtrait en haut pour lire en bas sans les voir ensemble.
   -------------------------------------------------------------------------- */
/* `overflow: hidden` pour que le tableau ne dépasse pas des coins arrondis de
   la carte ; c'est le conteneur du tableau, lui, qui défile quand sept colonnes
   ne tiennent pas dans la fenêtre. Sans lui, c'était la page entière qui
   partait vers la droite (signalé par l'utilisateur, 2026-08-10). */
.liste-candidats { padding: 0; overflow: hidden; }
.liste-tableau { overflow-x: auto; }

/* Une adresse e-mail longue ne doit pas élargir sa colonne à elle seule. */
.liste-candidats .mono,
.liste-experts .mono,
.liste-comptes .mono { overflow-wrap: anywhere; }

.filtres-barre {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s4);
  border-bottom: 1px solid var(--line-soft);
}

/* La loupe est dessinée en CSS : un cercle et sa poignée. Deux pseudo-éléments
   valent mieux qu'un fichier de plus, et le projet n'admet aucune icône
   distante. */
.filtre-recherche {
  position: relative;
  flex: 1 1 280px;
  min-width: 220px;
  max-width: 340px;
}
.filtre-recherche::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 12px;
  width: 11px;
  height: 11px;
  border: 1.5px solid var(--muted);
  border-radius: 50%;
}
.filtre-recherche::after {
  content: "";
  position: absolute;
  left: 23px;
  top: 23px;
  width: 5px;
  height: 1.5px;
  background: var(--muted);
  transform: rotate(45deg);
}
.filtre-recherche input {
  width: 100%;
  box-sizing: border-box;
  height: 38px;
  padding: 0 var(--s3) 0 34px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  font-size: 14px;
  background: var(--paper);
}

/* Les pastilles de métier se cumulent : aucun « Tous », n'en retenir aucune
   revient au même. La forme est celle du sélecteur de session — segments dans
   une gouttière —, pour qu'un même geste se reconnaisse d'un écran à l'autre. */
.filtre-segments {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius);
  background: var(--surface-2);
}
.filtre-segment {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 var(--s3);
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
}
.filtre-segment:hover { background: var(--paper); color: var(--accent); text-decoration: none; }

/* Retenu : **plein**, en bleu d'accent, avec une coche. Le fond blanc et son
   ombre légère ne suffisaient pas — on ne savait pas lequel était actif
   (signalé par l'utilisateur, 2026-08-11). Un aplat contre un fond gris ne
   laisse aucun doute, et la coche le dit sans la couleur, pour qui ne la
   distingue pas. */
.filtre-segment--actif,
.filtre-segment--actif:hover {
  background: var(--accent);
  color: var(--paper);
  font-weight: 620;
}
.filtre-segment-coche { font-size: 11px; line-height: 1; }

/* « Mes candidats » n'est pas un métier : un filet le sépare des pastilles TPI
   et TPP, dans la même barre. C'est un filtre enregistré, disponible en tout
   temps, pas une troisième valeur de la même question. */
.filtre-segment--mien {
  margin-right: var(--s2);
  padding-right: var(--s4);
  border-right: 1px solid var(--line);
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}
.filtre-segment--mien.filtre-segment--actif { border-right-color: var(--accent); }

.filtre-select {
  height: 38px;
  max-width: 250px;
  padding: 0 var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--paper);
}

.filtre-plus {
  height: 38px;
  display: inline-flex;
  align-items: center;
  padding: 0 var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-2);
  background: var(--paper);
  cursor: pointer;
  white-space: nowrap;
}
.filtre-plus:hover { border-color: var(--accent-line); color: var(--accent); }
.toggle-panel-input:checked ~ .filtre-plus {
  border-color: var(--accent-line);
  background: var(--accent-soft);
  color: var(--accent);
}

/* Les quatre filtres rares, sur toute la largeur sous la barre. Repliés par la
   même mécanique que les autres panneaux du projet : une case et son label. */
.filtres-avances {
  display: none;
  width: 100%;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s4);
  margin-top: var(--s2);
  padding-top: var(--s4);
  border-top: 1px dashed var(--line);
}
.toggle-panel-input:checked ~ .filtres-avances { display: grid; }

/* Le bouton s'efface quand le script applique au changement : il reste, et
   fonctionne, sur une page sans script. C'est le script qui pose la classe. */
.filtres-barre.filtres-auto .filtre-appliquer { display: none; }
.filtre-appliquer {
  height: 38px;
  width: auto;
  padding: 0 var(--s4);
}

.filtres-actifs {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  background: var(--paper-2);
  border-bottom: 1px solid var(--line-soft);
}
.filtres-actifs-titre {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--faint);
}

.filtre-puce {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  height: 26px;
  padding: 0 6px 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--paper);
  font-size: 12.5px;
  color: var(--ink-2);
  text-decoration: none;
}
.filtre-puce:hover { border-color: var(--accent-line); color: var(--accent); text-decoration: none; }
.filtre-puce-croix {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--surface-2);
  font-size: 11px;
  line-height: 1;
}
.filtre-raz { font-size: 12.5px; }

.liste-compte {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  font-size: 14px;
  color: var(--ink);
}
.liste-tri { color: var(--faint); }
.liste-vide { margin: 0; padding: var(--s5) var(--s4); font-size: 13px; color: var(--muted); }

/* Le tableau va bord à bord dans sa carte, comme la file du tableau de bord. */
.liste-candidats table,
.liste-experts table,
.liste-comptes table { margin: 0; }
.liste-candidats table th:first-child,
.liste-experts table th:first-child,
.liste-comptes table th:first-child,
.liste-candidats table td:first-child,
.liste-experts table td:first-child,
.liste-comptes table td:first-child { padding-left: var(--s4); }
.liste-candidats table th:last-child,
.liste-experts table th:last-child,
.liste-comptes table th:last-child,
.liste-candidats table td:last-child,
.liste-experts table td:last-child,
.liste-comptes table td:last-child { padding-right: var(--s4); }

/* Une ligne masquée par la recherche instantanée. La classe est posée par le
   script ; sans lui, c'est le serveur qui filtre et rien n'est masqué. */
.liste-candidats tr.ligne-filtree,
.liste-experts tr.ligne-filtree,
.liste-comptes tr.ligne-filtree { display: none; }

/* La rangée de titre qui coiffe un groupe — « Mes candidats », puis « Les
   autres candidats », à l'entrée par défaut de qui siège quelque part. Elle
   n'a pas de bordure haute : c'est son fond gris qui la détache, et un filet
   de plus hacherait le tableau. */
.liste-candidats tr.liste-groupe td {
  padding: var(--s2) var(--s4);
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted-sur-teinte);
}
.liste-groupe-compte {
  margin-left: var(--s2);
  font-weight: 600;
  letter-spacing: 0;
  color: var(--faint);
}

@media (max-width: 720px) {
  .filtre-recherche { flex: 1 1 100%; }
  .filtre-select { max-width: none; flex: 1 1 200px; }
}

/* --------------------------------------------------------------------------
   15 ter. Le journal d'un dossier
   Une chronologie à trois colonnes : quand, quoi, qui. Le fil vertical relie
   les points comme la frise relie ses étapes — c'est la même idée, une suite
   d'évènements dont on suit l'ordre, à ceci près que celle-ci n'a pas de fin
   connue. D'où le fil porté par chaque entrée plutôt que par la liste : rien
   ne dépasse sous la dernière.
   -------------------------------------------------------------------------- */
.jdd-liste {
  list-style: none;
  margin: 0;
  padding: 0;
}

.jdd-entree {
  position: relative;
  display: grid;
  grid-template-columns: auto 74px 1fr auto;
  gap: var(--s3);
  align-items: baseline;
  padding: var(--s3) 0 var(--s3) var(--s2);
}
.jdd-entree + .jdd-entree { border-top: 1px solid var(--line-soft); }

/* Le point et son fil. Le fil part du point et descend jusqu'au bas de
   l'entrée ; la dernière n'en a pas, il ne mènerait nulle part. */
.jdd-point {
  position: relative;
  top: 1px;
  width: 9px;
  height: 9px;
  flex: none;
  border-radius: 50%;
  background: var(--line);
}
.jdd-point--ok      { background: var(--ok); }
.jdd-point--error   { background: var(--warn); }
.jdd-point--warn    { background: var(--wait); }
.jdd-point--neutral { background: var(--line); }

.jdd-entree:not(:last-child) .jdd-point::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 13px;
  width: 1px;
  height: calc(100% + var(--s5));
  background: var(--line-soft);
}

.jdd-quand {
  display: flex;
  flex-direction: column;
  font-variant-numeric: tabular-nums;
}
.jdd-date  { font-size: 13px; font-weight: 560; color: var(--ink-2); }
.jdd-heure { font-size: 11px; color: var(--faint); }

.jdd-corps { min-width: 0; }
.jdd-quoi  { font-size: 13.5px; font-weight: 560; color: var(--ink); }

/* Le détail peut être un commentaire d'avis, parfois long : il va à la ligne
   plutôt que d'étirer la colonne, et garde les retours à la ligne écrits. */
.jdd-detail {
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--muted);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.jdd-qui {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  min-width: 0;
}
.jdd-nom  { font-size: 12.5px; font-weight: 560; color: var(--ink-2); }
.jdd-role { font-size: 11px; color: var(--faint); }

/* Sous 720px la colonne de l'auteur passe sous le texte : à trois colonnes et
   une date, il ne restait rien pour l'évènement lui-même. */
@media (max-width: 720px) {
  .jdd-entree { grid-template-columns: auto 1fr; }
  .jdd-quand  { flex-direction: row; gap: var(--s2); align-items: baseline; }
  .jdd-corps,
  .jdd-qui    { grid-column: 2; }
  .jdd-qui    { align-items: flex-start; text-align: left; margin-top: 2px; }
}

/* --------------------------------------------------------------------------
   15 bis. Mon profil
   Maquette 1a de l'utilisateur (2026-08-11) : une colonne d'identité à gauche,
   les sections empilées à droite. La page était jusque-là quatre panneaux de
   même poids où l'on ne trouvait son nom nulle part.
   -------------------------------------------------------------------------- */
.profil-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  flex-wrap: wrap;
  padding: var(--s4) var(--s6);
  margin-bottom: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}
.profil-entete-titre { display: flex; align-items: baseline; gap: var(--s3); }
.profil-entete-titre h1 { margin: 0; font-size: 20px; letter-spacing: -.01em; }
.profil-entete-sous { font-size: 13px; color: var(--muted); }
.profil-entete-repere { display: flex; align-items: center; gap: var(--s3); }

/* Les deux pastilles d'identité de la page. Elles portent la photo quand il y
   en a une, les initiales sinon — d'où le fond qui disparaît sous l'image
   (modificateur `--photo`), sans quoi un portrait sombre se poserait sur un
   rond bleu qui dépasserait sur les bords. */
.profil-pastille,
.profil-avatar {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 620;
}
.profil-pastille { width: 34px; height: 34px; font-size: 13px; }
.profil-avatar   { width: 76px; height: 76px; font-size: 25px;
                   border: 1px solid var(--accent-line); }
.profil-pastille--photo,
.profil-avatar--photo { background: var(--surface-2); border-color: var(--line); }
.profil-pastille img,
.profil-avatar img,
.tdb-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;   /* recadre plutôt que déformer */
  display: block;
}

.profil-grille {
  display: grid;
  grid-template-columns: 312px minmax(0, 1fr);
  align-items: start;
  gap: var(--s5);
}
.profil-colonne  { display: flex; flex-direction: column; gap: var(--s4);
                   position: sticky; top: var(--s6); }
.profil-sections { display: flex; flex-direction: column; gap: var(--s5); }

.profil-carte-identite {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s3);
  text-align: center;
  margin-bottom: 0;
}
.profil-identite-nom { display: flex; flex-direction: column; gap: 3px; }
.profil-nom  { font-size: 18px; font-weight: 700; }
.profil-role { font-size: 13px; color: var(--muted); }
.profil-etiquettes { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }

.profil-photo-form { width: 100%; display: flex; flex-direction: column; gap: var(--s2); }

/* Le champ de fichier est masqué derrière son étiquette : la carte ne porte
   qu'un bouton. Même précaution que les cases qui pilotent le CSS (§ 3) — 1 px,
   pas `display: none`, pour que l'anneau de focus se pose quelque part et que
   le champ reste atteignable au clavier. */
.profil-photo-fichier {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
}
.profil-photo-bouton {
  display: block;
  width: 100%;
  text-align: center;
  cursor: pointer;
}
.profil-photo-fichier:focus-visible + .profil-photo-bouton {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* « Retirer la photo » n'est pas une action qu'on propose, c'est une action
   qu'on rend possible : du texte, pas un second bouton en vis-à-vis du
   premier. */
.profil-photo-retirer {
  width: auto;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
  text-decoration: underline;
  cursor: pointer;
}
.profil-photo-retirer:hover { background: none; color: var(--warn); }

.profil-carte-faits { padding: 2px var(--s5); margin-bottom: 0; }
.profil-fait {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 11px 0;
}
.profil-fait + .profil-fait { border-top: 1px solid var(--line-soft); }
.profil-fait-label  { font-size: 13px; color: var(--muted); }
.profil-fait-ok     { font-size: 13px; font-weight: 600; color: var(--ok); }
.profil-fait-manque { font-size: 13px; font-weight: 600; color: var(--warn); }
.profil-note { margin: 0; color: var(--faint); }

.profil-section { margin-bottom: 0; }
.profil-section-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  margin-bottom: var(--s4);
}
.profil-section-tete h2 { margin: 0; }
.profil-mention {
  padding: 4px 9px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
}

/* Les informations en lecture seule. Deux colonnes de couples libellé/valeur —
   la grille vient d'un `<style>` en tête de page, elle vit ici depuis la
   refonte. */
.ro-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s4) var(--s7); }
.ro-item .label { font-size: 12px; color: var(--faint); margin-bottom: 3px; }
.ro-item .value { font-size: 15px; font-weight: 600; }
.ro-item--pleine {
  grid-column: 1 / -1;
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
}
.profil-email { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); }

.profil-champs {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s4) var(--s6);
}
.profil-champs .field + .field { margin-top: 0; }  /* la grille suffit à espacer */
.field--pleine { grid-column: 1 / -1; }
.profil-champs .field .small { margin: 2px 0 0; color: var(--faint); }

.profil-signature { display: flex; flex-direction: column; gap: var(--s2); }
.sig-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 104px;
  padding: 10px;
  border: 1.5px dashed var(--line);
  border-radius: var(--radius);
  background: var(--paper-2);
  text-align: center;
}
.sig-preview img { max-height: 84px; max-width: 100%; }
.sig-empty { font-size: 13px; color: var(--muted); }
.profil-case {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s2);
  font-size: 13px;
  font-weight: 500;
}
.profil-case input { width: auto; }

/* Les lignes de l'aperçu de sécurité : ce que c'est à gauche, où l'on en est
   à droite. */
.profil-ligne {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding: var(--s4) 0;
}
.profil-ligne + .profil-ligne { border-top: 1px solid var(--line-soft); }
.profil-ligne-quoi { display: flex; flex-direction: column; gap: 2px; }
.profil-ligne-titre { font-size: 14.5px; font-weight: 600; }
.profil-ligne-quoi .small { color: var(--faint); }
.profil-ligne-quoi-faire { display: flex; align-items: center; gap: var(--s3); }
.profil-ligne-form { margin: 0; }
.profil-demande-faite { margin: 0 0 var(--s2); color: var(--muted); }

/* --------------------------------------------------------------------------
   15 ter. La liste consolidée
   Premier temps de la répartition des experts (2026-08-11) : le Responsable y
   arrête le numéro, le sujet, les technologies et la date souhaitée de chaque
   travail. Un tableau qui se saisit — d'où des champs à même les cellules.
   -------------------------------------------------------------------------- */
.filtres-barre--cadrage {
  display: flex;
  align-items: center;
  gap: var(--s4);
  flex-wrap: wrap;
}

.travaux-etat {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.travaux-etat-titre  { font-weight: 620; color: var(--ink); }
.travaux-etat-manque { color: var(--wait); }
.travaux-etat-prete  { color: var(--ok); font-weight: 560; }

/* Les champs vont bord à bord dans leur cellule : un tableau de saisie où
   chaque champ garde ses marges se lit comme une grille de mots croisés. */
.table--travaux td input {
  height: 34px;
  padding: 0 var(--s2);
  font-size: 13.5px;
}
/* Le tableau ne se comprime pas : neuf colonnes dont trois champs de saisie ne
   tiennent pas sur une fenêtre étroite, et les colonnes d'identité s'y
   hachaient mot à mot (« Aubert / Paul / En / validation »). Il défile dans sa
   carte, comme la liste des candidats — ce qui est trop large défile chez lui. */
.table--travaux { min-width: 1080px; }
.table--travaux td { vertical-align: top; }
.table--travaux td:nth-child(2),
.table--travaux td:nth-child(3) { white-space: nowrap; }
.travaux-numero { font-size: 15px; font-weight: 700; color: var(--ink); }
.travaux-aide { margin: 0; padding: 0 var(--s4) var(--s4); color: var(--faint); }
/* Le numéro repris dans la liste des candidats : la même référence, la même
   allure, pour qu'on la reconnaisse d'un écran à l'autre. */
.liste-numero { font-size: 14px; font-weight: 700; color: var(--ink); white-space: nowrap; }
/* Le métier devant le numéro, en retrait : c'est lui qui lève l'ambiguïté,
   mais c'est le numéro qu'on lit. */
.liste-numero-metier { font-size: 10.5px; font-weight: 600; color: var(--faint); }

/* --------------------------------------------------------------------------
   15 quinquies. L'assignation des collèges
   Troisième temps de la répartition (2026-08-12) : une ligne par travail, une
   colonne par place. Le tableau est large par nature — il défile chez lui.
   -------------------------------------------------------------------------- */
.table--assignation { min-width: 900px; }
.table--assignation td { vertical-align: top; }
/* La dernière colonne est une place comme les autres : la règle générale la
   range à droite, ce qui décalait « Expert n°2 » loin de son menu. */
.table--assignation th:last-child,
.table--assignation td:last-child { text-align: left; }
.assign-select { height: 34px; width: 100%; max-width: none; font-size: 13px; }

/* Le rôle de la place, dans l'en-tête : plus discret que le numéro, qui est ce
   qu'on lit. */
.assign-referent { font-weight: 500; text-transform: none; color: var(--faint); }

/* Un travail que personne n'a demandé, et un expert qui porte deux fois sa
   part : deux signaux d'attention, jamais deux refus. */
.assign-personne { font-size: 12px; color: var(--wait); }
.assign-penche   { color: var(--wait); }

/* La charge de chaque expert, lue d'un coup d'œil (2026-08-26).
   La liste est triée du plus chargé au moins chargé, et deux teintes très
   douces reprennent ce que la console dit à droite : l'ambre pour qui n'a pas
   ses deux candidats — il reste des places à lui donner —, le rouge pour une
   répartition fautive. Portées par les cellules et non par la rangée : le
   survol du tableau repeint les `td`, et une teinte posée sur le `tr`
   disparaîtrait sous le curseur. */
tbody tr.assign-ligne--manque  td { background: var(--wait-soft); }
tbody tr.assign-ligne--fautive td { background: var(--warn-soft); }
tbody tr.assign-ligne--manque:hover  td { background: var(--wait-line); }
tbody tr.assign-ligne--fautive:hover td { background: var(--warn-line); }

/* La jauge : une barre par expert, rapportée au plus chargé. Elle occupe une
   colonne à elle, sans en-tête — la colonne « Travaux » d'à côté porte déjà
   le chiffre, et le répéter en titre ne dirait rien de plus. */
.assign-jauge-tete { width: 22%; }
.assign-jauge-cell { padding-right: 18px; }

.assign-jauge {
  position: relative;
  display: block;
  min-width: 70px;
  height: 8px;
  border-radius: 4px;
  background: var(--line-soft);
}

/* Le bout arrondi, la barre ancrée à gauche : c'est la longueur qui parle, et
   elle part toujours du même bord. La largeur minimale garde visible l'expert
   qui ne porte qu'un travail — une barre de deux pixels se lirait « zéro ». */
.assign-jauge-barre {
  display: block;
  min-width: 5px;
  height: 100%;
  border-radius: 4px;
  background: var(--accent);
}
tr.assign-ligne--manque  .assign-jauge-barre { background: var(--wait); }
tr.assign-ligne--fautive .assign-jauge-barre { background: var(--warn); }

/* Le repère de la répartition idéale. Il déborde de deux pixels en haut et en
   bas : au ras de la barre, il se confondrait avec elle dès qu'elle le
   dépasse, et c'est justement là qu'on le cherche. */
.assign-jauge-cible {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 2px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--faint);
}

/* --------------------------------------------------------------------------
   15 quater. Les souhaits des experts
   Second temps de la répartition (2026-08-11) : le Responsable ouvre sa liste,
   les experts cochent ce qu'ils aimeraient expertiser.
   -------------------------------------------------------------------------- */
.souhaits-bandeau {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
  font-size: 13.5px;
}
.souhaits-bandeau--ouvert { background: var(--ok-soft); }
.souhaits-bandeau .small { color: var(--muted-sur-teinte); }

/* La console de suivi de l'assignation (2026-08-13) : la charge à gauche, les
   anomalies à droite. Le tableau garde la part large — il porte jusqu'à quatre
   colonnes de places —, la console tient dans le reste. Sous 1200 px, les deux
   s'empilent : à moitié moins large, le tableau deviendrait illisible. */
.assign-monitoring {
  display: grid;
  gap: var(--s4);
  align-items: start;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
}
@media (max-width: 1200px) {
  .assign-monitoring { grid-template-columns: 1fr; }
}
.assign-console { padding-bottom: var(--s4); }
.assign-console-vide {
  margin: 0 var(--s4) var(--s4);
  padding: var(--s3);
  border-radius: var(--radius);
  background: var(--ok-soft);
  color: var(--ok-ink);
  font-size: 13.5px;
}
.assign-anomalie { padding: var(--s3) var(--s4); border-top: 1px solid var(--line-soft); }
/* Une famille sans anomalie reste affichée, en retrait : une console qui
   n'affiche que ce qui va mal ne dit pas si le reste a été regardé. */
.assign-anomalie--vide { opacity: .55; }
.assign-anomalie-tete {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s2); margin: 0 0 2px;
}
.assign-anomalie-titre { font-weight: 600; font-size: 14px; }
.assign-anomalie-regle { margin: 0; color: var(--muted); }
.assign-anomalie-liste { margin: var(--s2) 0 0; padding-left: var(--s5); }
.assign-anomalie-liste li { margin-bottom: 2px; }
.assign-anomalie-liste .small { color: var(--muted); }

/* Le décompte des souhaits reçus par travail. Zéro se signale : il faudra quand
   même lui trouver un collège, et c'est celui-là qu'on confiera aux experts
   les moins chargés. */
.travaux-souhaits { text-align: center; font-weight: 700; font-family: var(--mono); }
.travaux-souhaits--zero { color: var(--wait); }

/* Qui a demandé ce travail (2026-08-13). Le chiffre seul pose la question ; la
   bulle y répond au survol, et au clavier par le focus — aucun script.
   Elle s'ouvre **vers la gauche** : la colonne est la dernière du tableau, et
   `.liste-tableau` rogne ce qui dépasse (overflow-x: auto). Pour la même
   raison, la dernière ligne l'ancre par le bas : ancrée par le haut, elle
   sortirait sous le tableau et serait coupée. */
/* La prise de contact (2026-08-14) : tant que la rencontre n'est pas planifiée,
   le panneau porte le ton d'attente — l'ambre, pas le rouge. Rien n'est en
   faute : il reste quelque chose à faire, et ce sont les experts du collège
   qui le voient. */
/* **La prise de contact se signale comme les dates de l'examen** (2026-09-20,
   demande de l'utilisateur : « l'entête de l'encadré Prise de contact devrait
   être comme celui du dessus Dates de l'examen […] le titre devrait être rouge
   pour dire que ce n'est pas renseigné, et gris lorsque la date est
   renseignée »).

   Elle avait sa teinte à elle — un aplat orangé sur le titre, un filet orangé
   autour de la carte — pour dire ce que la carte voisine dit avec
   `.panel--incomplet` : une date qui manque. Deux manières de dire le même
   manque en faisaient deux manques différents. Il n'en reste qu'une, et c'est
   celle des dates ; la carte n'a plus de règle propre.

   Ce qui reste ici : la phrase qui explique quoi faire, à l'intérieur. */
.contact-attendu { margin: 0; font-size: 13.5px; color: var(--ink-2); }
/* Le meme rouge que le titre et que le carre vide : c'est un manque, et il se
   dit d'une seule couleur dans toute la carte (2026-09-20). */
.contact-attendu strong { display: block; color: var(--warn); }

/* La récusation, sur l'écran des souhaits et sur celui de l'assignation. Le
   rouge, très pâle (demande de l'utilisateur, 2026-08-13) : c'est le seul
   endroit où il ne dit pas une faute mais un empêchement — « je ne peux pas
   juger celui-là ». Assez léger pour que la ligne reste lisible, assez net
   pour qu'on la repère en parcourant quatre-vingt-dix travaux. */
/* Le texte s'éteint avec la ligne (2026-08-14) : récusé, ce travail ne se
   choisit plus, et il doit se lire comme désactivé plutôt que comme une ligne
   ordinaire posée sur du rouge. `--muted-sur-teinte` est fait pour ça — le gris
   atténué qui tient encore 5,24:1 sur `--warn-soft`, là où `--muted` tombe à
   4,14:1. Il a été créé pour les barres de titre teintées du tableau de bord ;
   c'est le même besoin, on l'y étend. */
.souhaits-ligne--recusee { background: var(--warn-soft); }
.souhaits-ligne--recusee td,
.souhaits-ligne--recusee .souhaits-sujet { color: var(--muted-sur-teinte); }
.souhaits-recuse-marque { color: var(--warn-line); }
.souhaits-recuse { display: block; font-size: 12.5px; font-weight: 600; color: var(--warn-ink); }
.souhaits-recusation { text-align: right; white-space: nowrap; }
.assign-recuses { font-size: 12px; font-weight: 600; color: var(--warn-ink); }

.souhaits-bulle { position: relative; display: inline-block; }
.souhaits-bulle-cible { cursor: help; border-bottom: 1px dotted var(--faint); }
.souhaits-bulle-cible:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.souhaits-bulle-info {
  position: absolute; right: calc(100% + 10px); top: 0; z-index: 30;
  display: grid; gap: 2px; width: max-content; max-width: 260px;
  padding: var(--s3); text-align: left;
  font: 400 13px/1.5 var(--sans); color: var(--ink);
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--lift-2);
  opacity: 0; visibility: hidden; transition: opacity .12s ease;
}
.liste-tableau tbody tr:last-child .souhaits-bulle-info { top: auto; bottom: 0; }
/* Ouverte vers la **droite** quand la cible est dans les premières colonnes
   (2026-08-13) : vers la gauche, elle sortait du tableau — que `.liste-tableau`
   rogne — et paraissait passer derrière le bandeau latéral. */
.souhaits-bulle--droite .souhaits-bulle-info { right: auto; left: calc(100% + 10px); }
.souhaits-bulle:hover .souhaits-bulle-info,
.souhaits-bulle:focus-within .souhaits-bulle-info { opacity: 1; visibility: visible; }
.souhaits-bulle-titre { font-weight: 600; color: var(--muted); }
.souhaits-bulle-niveau { color: var(--wait); }

.souhaits-compte { font-size: 13px; color: var(--muted); }
.souhaits-compte--court { color: var(--wait); font-weight: 620; }
/* La campagne close, sur l'écran de l'expert : la liste reste, la saisie
   s'arrête. Le ton d'attente plutôt que le rouge — rien n'est en faute, le
   temps a passé. */
.souhaits-close { color: var(--wait); font-weight: 560; }

/* La date limite se pose dans le même geste que l'ouverture. */
.souhaits-ouvrir { display: flex; align-items: center; gap: var(--s2); margin: 0; }
.souhaits-ouvrir input[type="date"] { width: auto; height: 34px; font-size: 13px; }

/* Un expert qui n'a rien demandé : c'est lui qu'on relance, ou pour qui on
   saisit. */
.travaux-souhaits--zero { color: var(--wait); }
.souhaits-regle { margin: 0 0 var(--s3); padding: 0 var(--s4); color: var(--muted); }

.table--souhaits td { vertical-align: middle; }
.souhaits-case { width: auto; }
/* L'étiquette couvre le sujet entier : on coche en cliquant sur ce qu'on lit,
   pas sur une case de 13 pixels à l'autre bout de la ligne. */
.souhaits-sujet { display: block; font-size: 14px; font-weight: 560; color: var(--ink); cursor: pointer; }
.souhaits-sujet:hover { color: var(--accent); }
.table--souhaits tr.souhaits-ligne--retenue td { background: var(--accent-soft); }

/* Le parcours en trois temps des souhaits (2026-09-07, R24). L'engagement
   n'est pas une case de plus : c'est un palier, et il se voit comme tel —
   un encadré à part, au-dessus de la liste, qui dit ce qui s'ouvrira ensuite.
   Le ton d'attente plutôt que l'accent : rien n'est encore fait, rien n'est
   en faute. */
.souhaits-engagement {
  margin: 0 var(--s4) var(--s4);
  padding: var(--s4);
  background: var(--wait-soft);
  border: 1px solid var(--wait-line);
  border-radius: var(--radius);
}
.souhaits-engagement .small { color: var(--muted-sur-teinte); }
.souhaits-engagement-titre {
  margin: 0 0 var(--s2);
  font-weight: 620;
  color: var(--wait);
}
.souhaits-engagement form { margin: var(--s3) 0 0; }
/* La reprise (Q18) : un candidat est entré dans la liste. Même encadré, mais
   il ne raconte plus le parcours — l'expert le connaît, il l'a déjà fait. */
.souhaits-engagement--reprise .souhaits-engagement-titre { color: var(--warn-ink); }
.souhaits-engage { color: var(--ok); font-weight: 560; }

/* La charge déclarée (R22). Une ligne de phrase, et non un champ dans un
   formulaire : « Je peux assumer [7] candidats au maximum ». Elle se lit d'un
   trait, ce qui est la seule façon de faire comprendre l'unité sans la définir.

   **Le chiffre se lit de loin** (2026-09-07, remarque de l'utilisateur). C'est
   le seul nombre que l'expert écrive de sa main sur cet écran — tout le reste
   se coche — et celui dont dépendra la répartition : il porte donc la taille et
   la couleur de l'accent, dans un champ deux fois plus haut que les autres.
   L'enregistrement se tient au bout de la même ligne, là où l'on vient de
   taper. */
.souhaits-charge {
  margin: 0 var(--s4) var(--s4); padding: var(--s4);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius);
}
.souhaits-charge-ligne {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
}
.souhaits-charge label { margin: 0; font-size: 15px; font-weight: 560; }
.souhaits-charge-unite { font-size: 15px; font-weight: 560; }
.souhaits-charge-valeur {
  width: 4.5em; height: 46px; padding: 0 var(--s2);
  font: 620 24px/1 var(--sans); text-align: center; color: var(--accent);
  background: var(--paper); border: 2px solid var(--accent-line);
}
.souhaits-charge-valeur:disabled { color: var(--muted); border-color: var(--line); }
/* Poussé à droite : la phrase se lit d'abord, le geste vient après. Il
   retombe sous elle quand la ligne se replie, et garde alors sa place à
   gauche plutôt que de flotter seul au bord. */
.souhaits-charge-bouton { width: auto; margin-left: auto; }
@media (max-width: 640px) { .souhaits-charge-bouton { margin-left: 0; } }
.souhaits-charge-aide { margin: var(--s3) 0 0; color: var(--muted); }

/* Le retour en place (2026-09-07). Enregistrer ou se récuser ramenait en haut
   de la page, et il fallait retrouver sa ligne parmi quatre-vingt-dix : les
   redirections portent désormais une ancre — la liste après un enregistrement,
   la ligne après une récusation. `scroll-margin-top` évite que la cible ne
   colle au bord haut de la fenêtre, où elle se lit mal. */
.souhaits-liste, .table--souhaits tr[id] { scroll-margin-top: var(--s6); }

/* La check-list de la rencontre (R16/R17, 2026-09-08). Une liste de cases dans
   une fenêtre, et le seul endroit de l'application où l'on coche une déclaration
   plutôt qu'un choix : les lignes sont donc espacées, et l'étiquette porte tout
   le texte — on coche en cliquant sur ce qu'on lit. */
.checklist { margin: 0 0 var(--s3); padding: 0; list-style: none; }
.checklist li {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) 0; border-bottom: 1px solid var(--line);
}
.checklist li:last-child { border-bottom: 0; }
.checklist-case { width: auto; margin: 2px 0 0; flex: none; }
.checklist label { margin: 0; font-weight: 400; line-height: 1.45; cursor: pointer; }
/* Les deux temps de la rencontre, et les sections du TPI. Le titre de partie
   porte un filet : c'est un changement de situation — le candidat sort de la
   pièce —, pas un simple regroupement. */
.checklist-partie {
  margin: var(--s5) 0 var(--s2); padding-bottom: var(--s2);
  font-size: 14px; font-weight: 620; color: var(--accent);
  border-bottom: 2px solid var(--accent-line);
}
.checklist-partie:first-of-type { margin-top: 0; }
.checklist-section {
  margin: var(--s4) 0 0; font-size: 13px; font-weight: 620;
  color: var(--muted); text-transform: uppercase; letter-spacing: .03em;
}
/* La précision : elle se lit, elle ne se coche pas. En retrait sous le point,
   dans le gris du texte secondaire — assez lisible pour être lue, assez
   discrète pour qu'on voie d'abord la case et son intitulé. */
.checklist-detail {
  display: block; margin-top: 3px;
  font-size: 12.5px; line-height: 1.45; color: var(--muted);
}
/* Le troisième bouton, quand il n'est plus disponible : ce n'est pas un refus
   qu'on cache, c'est une place déjà prise, et l'écran dit par qui. */
.checklist-absent-pris { color: var(--muted); }

/* La trace, sous la version : qui a attesté, qui n'était pas là. */
.checklist-trace {
  margin: 0 0 var(--s3); padding: var(--s3) var(--s4);
  list-style: none; background: var(--surface-2); border-radius: var(--radius);
  font-size: 13.5px;
}
.checklist-trace li { padding: 2px 0; color: var(--ink-2); }
.checklist-trace .small { color: var(--muted); }
.checklist-trace--absent { color: var(--wait); }

/* Le moment souhaité, sous la date : un TPP en plein temps tient sur une
   demi-journée, et l'expert dit laquelle (R21). */
.souhaits-moment { width: auto; height: 30px; margin-top: 4px; font-size: 12.5px; }

/* L'œil sur l'énoncé (R20). Deux liens et non un : lire n'est pas emporter,
   et les deux gestes se journalisent séparément. */
.souhaits-oeil { white-space: nowrap; }
.souhaits-oeil a { display: block; }

/* La surveillance des consultations (Q19). Une lecture suivie d'une récusation
   n'est pas une faute — c'est le cas qu'on veut voir sans le chercher. */
.consultations-apres { background: var(--warn-soft); }
.consultations-apres td { color: var(--muted-sur-teinte); }
.consultations-marque { font-size: 12.5px; font-weight: 600; color: var(--warn-ink); }

/* --------------------------------------------------------------------------
   16. Accessibilité
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
input:focus-visible, button:focus-visible, a:focus-visible, label:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.sidebar-toggle-input:focus-visible + .app-shell .sidebar-burger,
.toggle-panel-input:focus-visible ~ .toggle-panel-label,
.toggle-panel-input:focus-visible ~ .toggle-experts-bouton,
.toggle-panel-input:focus-visible ~ * .toggle-panel-label,
.toggle-panel-input:focus-visible ~ .panel .toggle-seance-bouton,
.toggle-panel-input:focus-visible ~ .panel .toggle-experts-bouton,
.metier-bascule:focus-visible ~ .metier-entete {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   17. Responsive — 860 / 560 / 520, tous en max-width
   -------------------------------------------------------------------------- */
/* Sous 1240px, la colonne d'appoint du dossier passe sous la colonne
   principale : à 308px elle devient trop étroite pour ses pastilles de date. */
@media (max-width: 1240px) {
  .dossier-grille { grid-template-columns: minmax(0, 1fr); }
}

/* Même raison sur le profil : sous 1000px, les deux colonnes de couples
   libellé/valeur se coincent contre la colonne d'identité. Celle-ci repasse
   au-dessus et cesse d'être collante — une carte qui suit le défilement n'a
   plus de sens quand elle occupe toute la largeur. */
@media (max-width: 1000px) {
  .profil-grille { grid-template-columns: minmax(0, 1fr); }
  /* Elle cesse d'être collante — une carte qui suit le défilement n'a plus de
     sens quand elle occupe toute la largeur — et cesse de s'étirer : un rond
     de 76 px centré sur 900 px de large ne fait pas une carte d'identité. */
  .profil-colonne { position: static; max-width: 420px; }
}

/* Entre 860 et 1100px, cinq colonnes deviennent cinq colonnes étroites où les
   libellés se hachent : on passe à trois. Chaque barre vivant dans sa propre
   cellule, une rangée de plus ne pose plus aucun problème de trait. */
@media (max-width: 1100px) {
  .frise { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pieces { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 860px) {
  .sidebar-burger { display: flex; }

  /* Le bandeau devient un tiroir : la hauteur ne manque plus, et les entrées
     reprennent le confort qu'on leur a retiré sur un portable — trente pixels
     de haut se touchent mal du doigt. */
  .sidebar-nav { gap: var(--s5); }
  .sidebar-link { padding: var(--s2) var(--s3); }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 15;
    width: 264px;
    transform: translateX(-100%);
    box-shadow: var(--lift-2);
    transition: transform .18s ease;
  }
  .sidebar-toggle-input:checked ~ .app-shell .sidebar { transform: translateX(0); }

  .app-content { padding: 64px var(--s4) var(--s7); }

  .panel-grid { grid-template-columns: minmax(0, 1fr); }

  .frise { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dossier-tete { flex-direction: column; align-items: stretch; }
  .eval-actions { flex-wrap: wrap; }
}

/* --------------------------------------------------------------------------
   Tous les tableaux défilent sur un écran étroit — 2026-08-19
   --------------------------------------------------------------------------
   Signalé par l'utilisateur : « sur smartphone, beaucoup de listes dépassent
   et débordent ». Huit listes seulement étaient enveloppées d'un
   `.liste-tableau` ; les vingt-sept autres poussaient la page entière vers la
   droite, et l'on perdait le bandeau, les boutons, tout.

   Un tableau ne peut pas défiler sur lui-même : il lui faut un conteneur. Sauf
   à lui donner `display: block`, ce qui en fait un bloc qui défile pendant que
   ses lignes se rangent dans une boîte de tableau anonyme — la mise en page ne
   change pas, seul le débordement est capté. `min-width: 100%` lui rend la
   largeur qu'il aurait autrement, sans quoi un tableau à deux colonnes se
   recroquevillerait sur son contenu.

   **La règle est ici plutôt que sur chaque écran** : une liste ajoutée demain
   en hérite sans que personne y pense, et c'est bien la leçon des vingt-sept
   qui débordaient. Au-delà de 900 px, rien ne change : les tableaux tiennent,
   et le rendu de bureau reste celui qui a été réglé. */
@media (max-width: 900px) {
  main table {
    display: block;
    overflow-x: auto;
    min-width: 100%;
    /* Le doigt fait défiler ; sur les navigateurs qui la montrent, la barre
       reste discrète. */
    -webkit-overflow-scrolling: touch;
  }
  /* Un mot sans espace — une adresse e-mail, un nom de fichier — ne doit pas
     élargir sa colonne à lui seul au point de tout faire défiler. */
  main table td, main table th { overflow-wrap: anywhere; }
}

@media (max-width: 560px) {
  .codes-grid { grid-template-columns: minmax(0, 1fr); }
  .steps { font-size: 10px; }
  /* Le profil passe à une colonne partout : deux couples libellé/valeur côte à
     côte sur 300px ne se lisent plus. */
  .ro-grid,
  .profil-champs { grid-template-columns: minmax(0, 1fr); }
  .profil-section-tete { flex-wrap: wrap; }
  .profil-ligne { flex-direction: column; align-items: flex-start; gap: var(--s2); }
  .readonly-grid { grid-template-columns: minmax(0, 1fr); gap: 2px var(--s4); }
  .readonly-grid > dd { margin-bottom: var(--s3); }
  .card { padding: var(--s5); }
}

@media (max-width: 520px) {
  /* Frise en une colonne : les libellés longs n'ont plus aucune contrainte. La
     barre reste en tête de chaque étape, elle en devient le filet de gauche. */
  .frise { grid-template-columns: minmax(0, 1fr); gap: var(--s4) 0; }
  .frise-etape { padding-right: 0; }

  /* Tableaux denses en cartes empilées. Les en-têtes de colonne disparaissent :
     chaque cellule porte son intitulé via data-label côté PHP si présent. */
  .table--cartes thead { display: none; }
  .table--cartes tr {
    display: block;
    margin-bottom: var(--s3);
    padding: var(--s3) var(--s4);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
  }
  .table--cartes td {
    display: flex;
    justify-content: space-between;
    gap: var(--s3);
    padding: var(--s1) 0;
    border: 0;
    text-align: left;
  }
  .table--cartes td::before {
    content: attr(data-label);
    font-size: 11px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .table--cartes td:last-child { justify-content: flex-start; padding-top: var(--s2); }

  button, .btn { padding: var(--s3) var(--s4); min-height: 44px; }
  .btn--inline { min-height: 36px; }
}

/* --------------------------------------------------------------------------
   18. Mouvement réduit
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   19. Impression — listes et dossiers imprimés en séance
   -------------------------------------------------------------------------- */
@media print {
  body { background: #fff; }
  .sidebar, .sidebar-burger, .table-actions, .btn, button { display: none !important; }
  .app-content { padding: 0; }
  .panel { border-color: #bbb; box-shadow: none; break-inside: avoid; }
}

/* --------------------------------------------------------------------------
   19. Agenda de l'expert — 2026-08-20
   --------------------------------------------------------------------------
   Une grille mensuelle, « style calendrier Outlook, par carré, qu'on peut
   défiler dans le temps » (demande de l'utilisateur). Sans une ligne de
   script : les flèches sont des liens vers le mois voisin.

   Sous 900 px, la grille cède la place à une liste chronologique du même mois
   — sept colonnes sur un téléphone ne se lisent pas. Ce n'est pas un repli
   dégradé : c'est la lecture qui convient à la largeur, et rien n'est caché
   d'un côté qui ne soit visible de l'autre.
   -------------------------------------------------------------------------- */
.agenda-tete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  margin-bottom: var(--s5);
}
.agenda-navigation { display: flex; gap: var(--s2); }

.agenda-grille {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.agenda-entete {
  padding: 6px 8px;
  background: var(--surface-2);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
}
.agenda-jour {
  display: flex;
  flex-direction: column;
  gap: 3px;
  /* Une hauteur plancher plutôt qu'une hauteur fixe : une journée chargée
     s'étire, les autres gardent le carré. */
  min-height: 96px;
  padding: 4px 5px 6px;
  background: var(--surface);
}
/* Les jours des mois voisins restent lisibles mais s'effacent : ils sont là
   pour que la semaine soit entière, pas pour être lus. */
.agenda-jour--hors { background: var(--surface-2); }
.agenda-jour--hors .agenda-numero { color: var(--faint); }
.agenda-jour--aujourdhui { box-shadow: inset 0 0 0 2px var(--accent); }
.agenda-jour--conflit { background: var(--warn-soft); }

.agenda-numero {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
}
/* Le point d'exclamation du conflit : la couleur seule ne dirait rien à qui ne
   la distingue pas, et une infobulle donne le compte. */
.agenda-conflit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--warn);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
}

/* La barre de gauche porte la couleur forte, et elle est épaisse : c'est elle
   qui identifie la nature d'un coup d'œil, pas le fond, qui reste clair pour
   que le texte se lise (2026-08-20). */
.agenda-evt {
  display: block;
  padding: 2px 5px 2px 6px;
  border-radius: 3px;
  border-left: 5px solid currentColor;
  font-size: 11.5px;
  line-height: 1.35;
  text-decoration: none;
  /* Un nom long ne doit pas élargir la colonne : il se coupe, l'infobulle le
     donne en entier. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
a.agenda-evt:hover { text-decoration: underline; }

/* Six natures, six teintes — elles étaient huit avant que l'utilisateur
   n'écarte les séances des formateurs et les visites ; l'examen y est revenu
   pour le TPP seul, qui tient en une journée quand un TPI court sur des
   semaines. Un agenda
   n'est utile que s'il ne porte que ce qui appelle une présence ou une date à
   tenir. Aucune ne code une gravité, sauf l'ambre du conflit, qui, lui,
   appelle une décision. */
.agenda-evt--examen       { color: #6b2fb3; background: #f0e9fa; }
.agenda-evt--soutenance   { color: #1b4fd8; background: #eaeefb; }
.agenda-evt--rencontre    { color: #15803d; background: #e6f4ec; }
.agenda-evt--seance       { color: #0b6b6b; background: #e0f2f2; }
.agenda-evt--distribution { color: #8a4b12; background: #fbeee0; }
.agenda-evt--echeance     { color: #a3246b; background: #fdeaf2; }

.agenda-legende {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s4);
  margin-top: var(--s4);
  font-size: 12px;
  color: var(--muted);
}
.agenda-legende-item { display: inline-flex; align-items: center; gap: 6px; }
/* La pastille garde le **fond pastel** de l'événement — c'est ce qu'on voit
   dans la grille, et la légende doit lui ressembler pour qu'on fasse le lien.
   Elle a d'abord été posée en aplat plein de la couleur forte : trop dur, et
   plus du tout la même chose que les cases (2026-08-20).

   Ce qui la rend lisible n'est donc pas la saturation mais le **contour** :
   un trait de la couleur franche autour du pastel. Douce de loin, distincte
   de près. */
.agenda-pastille {
  width: 15px;
  height: 15px;
  flex: none;
  border-radius: 4px;
  border: 2px solid currentColor;
}
.agenda-legende { color: var(--ink-2); font-size: 12.5px; }

/* La liste : cachée tant que la grille tient, seule quand elle ne tient plus. */
.agenda-liste { display: none; }
.agenda-liste-jour {
  display: flex;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-top: 1px solid var(--line);
}
.agenda-liste-jour--conflit { background: var(--warn-soft); }
.agenda-liste-date {
  flex: none;
  width: 3.2rem;
  font-size: 11px;
  text-transform: uppercase;
  color: var(--muted);
}
.agenda-liste-date strong { display: block; font-size: 18px; color: var(--ink); }
.agenda-liste-evt { padding: 2px 0; }
.agenda-tag--examen       { color: #6b2fb3; background: #f0e9fa; }
.agenda-tag--soutenance   { color: #1b4fd8; background: #eaeefb; }
.agenda-tag--rencontre    { color: #15803d; background: #e6f4ec; }
.agenda-tag--seance       { color: #0b6b6b; background: #e0f2f2; }
.agenda-tag--distribution { color: #8a4b12; background: #fbeee0; }
.agenda-tag--echeance     { color: #a3246b; background: #fdeaf2; }

@media (max-width: 900px) {
  .agenda-grille, .agenda-legende { display: none; }
  .agenda-liste { display: block; }
}

/* ── La grille d'évaluation, composée critère par critère (2026-09-17) ──────
   Soixante-deux ou soixante-cinq critères, groupés par domaine. Ce n'est pas
   une liste de cases comme la check-list : on n'y déclare rien, on y compare.
   Chaque critère est donc une carte — sa case, son code, sa question, et son
   détail replié — plutôt qu'une ligne de plus. */
.grille-domaine {
  display: flex; align-items: baseline; gap: var(--s3);
  margin: var(--s5) 0 var(--s3); padding-bottom: var(--s2);
  font-size: 14px; font-weight: 620; color: var(--accent);
  border-bottom: 2px solid var(--accent-line);
}
.grille-domaine:first-of-type { margin-top: 0; }
.grille-domaine .small { margin-left: auto; color: var(--muted); font-weight: 400; }

.grille-criteres { display: grid; gap: var(--s3); }
.grille-critere {
  padding: var(--s3) var(--s4);
  background: var(--surface-2); border: 1px solid var(--line);
  border-left: 3px solid transparent; border-radius: var(--radius);
}
/* Ce qui est retenu se voit d'un coup d'œil, sans relire les cases : le filet
   de gauche suffit, et il laisse la carte lisible. Le vert dit « dans la
   grille », pas « bien choisi ». */
.grille-critere--retenu { border-left-color: var(--ok); background: var(--ok-soft); }

.grille-critere-tete {
  display: flex; align-items: flex-start; gap: var(--s3); margin: 0;
}
.grille-case { width: auto; margin: 3px 0 0; flex: none; }
.grille-critere-tete label {
  margin: 0; font-weight: 400; line-height: 1.45; cursor: pointer;
}
.grille-critere-tete .mono {
  margin-right: 6px; font-weight: 620; color: var(--ink-2);
}
/* L'étiquette dit d'un mot pourquoi la case est verrouillée. Elle est à droite,
   où l'œil la trouve sans la chercher au milieu du titre. */
.grille-etiquette {
  flex: none; margin-left: auto; padding: 1px 8px;
  font-size: 11.5px; letter-spacing: .02em; text-transform: uppercase;
  color: var(--muted); background: var(--surface); border: 1px solid var(--line);
  border-radius: 999px;
}
.grille-etiquette--obli { color: var(--accent); border-color: var(--accent-line); }

.grille-question {
  margin: var(--s2) 0 0 26px;
  font-size: 13px; line-height: 1.45; font-style: italic; color: var(--muted);
}

/* Le détail se replie. Quarante items dépliés par domaine feraient une page
   qu'on ne parcourt plus — et `<details>` est du HTML, pas du script. */
.grille-detail { margin: var(--s2) 0 0 26px; font-size: 12.5px; }
.grille-detail summary { color: var(--accent); cursor: pointer; }
.grille-items, .grille-niveaux {
  margin: var(--s2) 0 0; padding-left: var(--s4); color: var(--ink-2);
}
.grille-items li, .grille-niveaux li { padding: 2px 0; line-height: 1.45; }
/* L'item déterminant est en gras dans le texte : le barème l'exige en plus du
   compte, et un item qui manque ne coûte pas le même point selon lequel c'est. */
.grille-item--determinant { color: var(--ink); }
.grille-niveaux { list-style: none; padding-left: 0; color: var(--muted); }
.grille-niveaux .mono {
  display: inline-block; width: 52px; font-weight: 620; color: var(--ink-2);
}

/* Le compteur des choix (2026-09-17). Deux colonnes : la liste, et lui —
   `sticky` plutôt que `fixed`, pour qu'il reste dans la carte et s'empile
   proprement sur un écran étroit au lieu de flotter par-dessus le texte. */
.grille-compo {
  display: grid; grid-template-columns: 1fr 250px;
  gap: var(--s4); align-items: start;
}
/* Il vient avant la liste dans le document — sur un écran étroit, c'est le
   compte qu'on veut lire en premier —, et la grille le renvoie à droite. */
.grille-flotteur { grid-column: 2; grid-row: 1; position: sticky; top: var(--s4); }
.grille-compo-liste { grid-column: 1; grid-row: 1; min-width: 0; }
@media (max-width: 900px) {
  .grille-compo { grid-template-columns: 1fr; }
  .grille-flotteur, .grille-compo-liste { grid-column: 1; grid-row: auto; }
  .grille-flotteur { position: static; }
}

.grille-flotteur {
  padding: var(--s3) var(--s4);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius); font-size: 13px;
}
/* Le compte atteint : la même teinte que partout ailleurs pour « c'est fait ».
   Au-delà, elle retombe — le serveur refusera, et un compteur vert sur une
   grille impossible serait un mensonge. */
.grille-flotteur--complet {
  background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok-ink);
}
.grille-flotteur-total {
  margin: 0 0 var(--s3); padding-bottom: var(--s2);
  line-height: 1.4; border-bottom: 1px solid var(--line);
}
.grille-flotteur--complet .grille-flotteur-total { border-bottom-color: var(--ok-line); }
.grille-flotteur-total strong { font-size: 15px; }

.grille-flotteur-domaines { margin: 0; padding: 0; list-style: none; }
.grille-flotteur-domaines li {
  display: flex; align-items: baseline; gap: 8px; padding: 3px 0;
}
.grille-flotteur-domaines .mono { flex: none; width: 28px; font-weight: 620; }
/* Le libellé s'efface avant le chiffre : c'est le chiffre qu'on vient lire. */
.grille-flotteur-libelle {
  flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
  font-size: 12px; color: var(--muted);
}
.grille-flotteur-compte { flex: none; font-variant-numeric: tabular-nums; }
.grille-flotteur-offerts { color: var(--muted); }
/* Elle ne se lit qu'au compte atteint, et c'est la classe du cadre qui la
   montre — pas de `hidden` à basculer, pas d'état en double. */
.grille-flotteur-plein { display: none; }
.grille-flotteur--complet .grille-flotteur-plein {
  display: block; margin: var(--s3) 0 0; font-size: 12px; line-height: 1.4;
}
/* Une case fermée n'est pas une case absente : elle s'estompe, et la phrase du
   compteur dit pourquoi. */
.grille-critere input.grille-case:disabled:not(:checked) + label { color: var(--muted); }

/* Pleine largeur du compteur : c'est l'action de la colonne, et une largeur
   automatique l'aurait laissé flotter au milieu d'un cadre étroit. */
.grille-flotteur-bouton { width: 100%; margin-top: var(--s3); }
.grille-flotteur-note {
  margin: var(--s3) 0 0; padding-top: var(--s2);
  font-size: 11.5px; line-height: 1.4; color: var(--muted);
  border-top: 1px solid var(--line);
}
.grille-flotteur--complet .grille-flotteur-note,
.grille-flotteur--complet .grille-flotteur-libelle,
.grille-flotteur--complet .grille-flotteur-offerts { color: var(--muted-sur-teinte); }
.grille-flotteur--complet .grille-flotteur-note { border-top-color: var(--ok-line); }

/* ── L'évaluation de la grille (2026-09-18) ─────────────────────────────────
   Quarante-cinq critères qu'on parcourt en notant. La carte d'un critère
   ressemble à celle de la composition — même document, mêmes repères —, mais ce
   qui s'y coche n'est plus « je le retiens » : c'est « il vaut tant ». */
.eval-critere {
  padding: var(--s3) var(--s4);
  margin-bottom: var(--s3);
  background: var(--surface-2); border: 1px solid var(--line);
  border-left: 3px solid transparent; border-radius: var(--radius);
}
/* Un critère évalué se voit d'un coup d'œil, sans relire les boutons : c'est la
   seule question qu'on se pose en descendant la page. */
.eval-critere--fait { border-left-color: var(--ok); background: var(--paper); }

.eval-tete { display: flex; align-items: baseline; gap: var(--s3); margin: 0; }
.eval-tete .mono { flex: none; font-weight: 620; color: var(--ink-2); }
.eval-titre { flex: 1 1 auto; min-width: 0; font-weight: 560; }

/* Les quatre niveaux, en ligne : ils se voient d'un coup, et l'on coche celui
   qu'on vient de lire. Une liste déroulante les aurait cachés derrière un clic
   — quarante-cinq fois. */
.eval-niveaux {
  display: flex; flex-wrap: wrap; gap: var(--s2);
  margin: var(--s3) 0 0 26px;
}
.eval-niveau {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0; padding: 3px 10px;
  font-size: 12.5px; font-weight: 400; line-height: 1.4;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-pill); cursor: pointer;
}
.eval-niveau input { width: auto; margin: 0; }
.eval-niveau--retenu {
  background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok-ink);
  font-weight: 560;
}
/* « Pas encore » n'est pas un niveau : c'est ce qui les efface. Il se tient à
   part, en gris, pour qu'on ne le coche pas en croyant noter zéro. */
.eval-niveau--vide { color: var(--muted); border-style: dashed; }
.eval-niveau:has(input:disabled) { opacity: .6; cursor: default; }

.eval-remarque { margin: var(--s3) 0 0 26px; width: calc(100% - 26px); }
.eval-remarque-lue {
  margin: var(--s3) 0 0 26px; padding-left: var(--s3);
  font-size: 12.5px; color: var(--ink-2); border-left: 2px solid var(--line);
}

/* Sa propre ligne, dans le tableau des deux parts : on cherche la sienne. */
.ligne-moi { background: var(--accent-soft); }

/* ── Le jeton de la check-list (2026-09-18) ────────────────────────────────
   Dans le titre de la carte de la rencontre : un picto, un libellé, et une
   teinte qui répond avant qu'on ait lu — verte si la check-list est attestée,
   rouge pâle sinon. Le picto seul s'apprendrait ; le mot seul se perdrait dans
   un titre. Les deux ensemble se trouvent du premier coup d'œil.

   C'est un `<label>` qui ouvre la fenêtre : la mécanique des modales du projet,
   sans une ligne de script. */
.checklist-jeton {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0; padding: 3px 10px;
  font-family: var(--sans); font-size: 12px; font-weight: 560;
  letter-spacing: 0; text-transform: none; line-height: 1.5;
  border: 1px solid transparent; border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
.checklist-jeton svg { width: 15px; height: 15px; flex: none; }
.checklist-jeton--faite {
  background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok-ink);
}
.checklist-jeton--faite:hover { background: var(--ok); border-color: var(--ok); color: #fff; }
.checklist-jeton--manque {
  background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn-ink);
}
.checklist-jeton--manque:hover { background: var(--warn); border-color: var(--warn); color: #fff; }

/* La sous-note qui vient de la grille : une valeur, pas un champ. Elle garde la
   place et l'alignement de celui qu'elle remplace, pour que la ligne ne bouge
   pas d'un dossier à l'autre. */
.eval-valeur {
  display: inline-block; min-width: 64px;
  font-family: var(--mono); font-size: 15px; font-weight: 620;
  text-align: right; color: var(--ink);
}

/* La remarque exigée (2026-09-18). Sous le niveau maximum, un point a été
   retiré : il s'explique. Le champ se signale dès le clic — découvrir la règle
   au refus, après quarante-cinq critères, c'est remonter la page en cherchant
   lesquels.
   Deux degrés : **due** dit qu'elle est attendue, **manque** qu'elle est encore
   vide. Le second est plus net que le premier, sans crier — c'est un rappel,
   pas une faute. */
.eval-critere--remarque-due .eval-remarque { border-color: var(--warn-line); }
.eval-critere--remarque-manque .eval-remarque {
  background: var(--warn-soft); border-color: var(--warn);
}
.eval-critere--remarque-manque .eval-remarque::placeholder { color: var(--warn-ink); }

/* ── Les pièces du TPP par compétence (T44, campagne de tests, 2026-11-07) ──
   Un groupe repliable par compétence dans la fenêtre de dépôt : vingt-quatre
   lignes dépliées d'un coup noieraient celle qu'on cherche. Le résumé dit ce
   qui est déposé, et ce qui est à corriger. */
.depot-competence {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--s2) var(--s3);
}
.depot-competence > summary { cursor: pointer; font-size: 13px; }
.depot-competence[open] > summary { margin-bottom: var(--s3); }
/* La pièce dont une correction est demandée, dans la fenêtre de dépôt. */
.etiquette--warn {
  min-height: 0;
  padding: 1px 7px;
  margin-left: var(--s2);
  background: var(--warn-soft);
  color: var(--warn-ink);
}
/* Demandée, mais pas redéposée : c'est la première chose à vérifier. */
.version-repris--demandee { color: var(--warn-ink); font-weight: 600; }
/* Les cases des pièces à corriger, dans la demande de corrections. */
.corr-pieces {
  border: 0;
  margin: var(--s2) 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s3);
}
.corr-pieces legend { width: 100%; margin-bottom: var(--s1); padding: 0; }
.corr-piece { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; }
.fil-pieces { margin-top: var(--s1); }

/* La demande de corrections en texte enrichi (2026-11-07) : l'éditeur prend
   toute la largeur, comme la zone de texte qu'il recouvre ; les pièces à
   corriger, « Annuler » et « Envoyer » passent dessous. */
.version-corrections .editeur { flex: 1 1 100%; min-width: 0; }
.version-corrections .corr-pieces { flex: 1 1 100%; }

/* Les remarques de la grille sur plusieurs lignes (2026-11-07) : Entrée va à
   la ligne. La zone grandit à la main, pas en largeur. */
.eval-remarque, .eval-correction-remarque { resize: vertical; min-height: 2.6em; line-height: 1.45; }
.eval-correction-remarque { display: block; width: 100%; margin-top: var(--s2); }

/* Lot J (2026-11-07). L'entretien remplacé par le Chef Expert se relit, en
   retrait ; la documentation des formateurs a son fil d'Ariane. */
.entretien-remplace td { color: var(--muted); }
.entretien-remplacer summary { cursor: pointer; font-size: 13px; color: var(--muted); }
.entretien-remplacer form { display: grid; gap: var(--s2); margin-top: var(--s2); max-width: 640px; }
.doc-fil { display: flex; flex-wrap: wrap; gap: 6px; font-size: 13px; color: var(--muted); }
.doc-dossier { font-weight: 600; }
.doc-metiers { display: flex; gap: var(--s2); margin: var(--s2) 0; }
