/*
 * console.css : l'habillage de l'espace d'administration.
 *
 * --- Réécrite de zéro le 2026-08-24 ---------------------------------------
 * La version précédente habillait des écrans qui étaient chacun un document :
 * un titre, un chapeau, des paragraphes, et la navigation elle-même écrite en
 * prose au milieu du contenu. C'est ce qui produisait un tableau de bord de
 * 1 187 mots et quinze boutons primaires identiques sur l'écran du contenu.
 *
 * Cette version habille une APPLICATION : un cadre permanent à gauche qui porte
 * la navigation et l'identité du site, et à droite un écran qui ne contient plus
 * que sa propre matière. Le cadre n'est plus du contenu.
 *
 * --- Les quatre règles de cette feuille -----------------------------------
 *  1. UN SEUL ACCENT. Le marine #1F4789 porte les actions et l'état actif. Le
 *     bleu de la charte #0097D7 ne sert plus qu'à la pastille de marque et aux
 *     pictogrammes : il fait 3,28:1 sur blanc, il ne peut pas porter de texte.
 *  2. AUCUNE OPACITÉ SUR DU TEXTE. Les gris sont des couleurs pleines, mesurées
 *     et annotées. Un rgba() sur du texte donne un contraste qui dépend du fond
 *     du jour, donc un contraste qu'on ne peut pas garantir.
 *  3. LE TRAIT AVANT L'OMBRE. Un filet d'un pixel sépare mieux que trois ombres
 *     empilées, et il ne salit pas le fond. L'ombre existe mais elle est teintée
 *     et presque invisible : elle dit « ceci flotte », rien de plus.
 *  4. LE MOUVEMENT SERT OU N'EST PAS. 140 ms sur un survol, 180 ms sur une
 *     apparition, jamais « transition: all », jamais d'animation sur une action
 *     répétée cent fois par jour. Tout s'arrête sous prefers-reduced-motion.
 *
 * Les six couleurs de la charte JCI v1.4 sont écrites ici en dur, et c'est le
 * seul endroit du projet où c'est permis : l'administration n'est pas un site de
 * JCE, elle n'a donc pas de charte modifiable. Les sites publics tirent leurs
 * couleurs de leurs réglages.
 *
 * Les polices ne sont pas chargées : la pile système, qui ne coûte aucune requête
 * et qui donne SF sur un Mac, Segoe sur Windows. C'est-à-dire la police que
 * l'utilisateur lit déjà toute la journée.
 */

:root {
  /* -- Couleurs de la charte, telles quelles ------------------------------ */
  --bleu:      #0097D7;   /* 3,28:1 sur blanc : marque et pictogrammes, jamais de texte */
  --nuit:      #130F2D;
  --blanc:     #FFFFFF;
  --marine:    #1F4789;   /* 9,06:1 avec du blanc : c'est lui qui porte les actions */
  --sarcelle:  #57BCBC;
  --jaune:     #EFC40F;

  /* -- Encres, en couleurs pleines et mesurées sur blanc ------------------ */
  --encre:     #14142B;   /* 15,8:1 - le texte courant et les titres */
  --encre-2:   #5A5F73;   /*  6,3:1 - le texte secondaire, les chapeaux */
  --encre-3:   #676D80;   /*  5,2:1 sur blanc, 5,0:1 sur le gris du cadre.
                           * Mesuré à l'écran plutôt que calculé sur blanc : l'ancien
                           * #6E7488 tenait 4,71:1 sur du blanc pur mais retombait
                           * juste sous 4,5 sur le fond du cadre de gauche, qui n'est
                           * pas blanc. Un seuil qui ne tient que sur une surface
                           * n'est pas tenu. */
  /* Ces deux-là existaient en rgba() et sont employés dans quarante écrans. Ils
   * sont conservés comme alias vers les couleurs pleines : le nom reste, le
   * contraste devient garanti. */
  --encre-70:  #5A5F73;
  --encre-50:  #676D80;

  /* -- Surfaces ---------------------------------------------------------- */
  --fond:      #F5F6F8;   /* le fond de l'application, gris froid très clair */
  --carte:     #FFFFFF;
  --rail:      #FBFBFC;   /* le cadre de gauche, à peine détaché du fond */
  --trait:     #E5E7EC;   /* le filet : décoratif, jamais porteur d'information */
  --trait-fort:#D3D7E0;
  --survol:    #F0F1F4;   /* le fond d'une ligne survolée ou d'un onglet inactif */

  /* -- États ------------------------------------------------------------- */
  --ok:        #1F6B6B;   /* 6,1:1 - dérivé du sarcelle, assombri pour porter du texte */
  --ok-fond:   #EEF7F7;
  --attention: #6B5600;   /* 7,1:1 - dérivé du jaune, même raison */
  --att-fond:  #FBF6E4;
  --danger:    #B3261E;   /* 6,5:1 - la septième couleur, et la seule qui manque à la
                           * charte : « détruire ne ressemble jamais à enregistrer »
                           * ne peut pas se dire avec du marine. */
  --danger-fond:#FCEFEE;
  --info-fond: #EEF4FB;

  /* -- Géométrie --------------------------------------------------------- */
  --rayon:     14px;      /* les conteneurs */
  --rayon-s:   10px;      /* les commandes : plus serré que son contenant */
  --rayon-xs:  7px;
  --rail-l:    248px;
  --largeur:   800px;     /* une colonne de lecture, pas un écran entier */

  /* Ombres teintées de nuit, pas du noir : une ombre grise sur un fond froid
   * fait sale. Deux niveaux, et le second ne sert qu'à ce qui flotte vraiment. */
  --ombre:     0 1px 2px rgba(20, 20, 43, .04), 0 1px 3px rgba(20, 20, 43, .03);
  --ombre-flot:0 12px 32px rgba(20, 20, 43, .12), 0 2px 6px rgba(20, 20, 43, .06);

  /* -- Mouvement --------------------------------------------------------- */
  /* Les courbes natives de CSS sont trop molles : elles démarrent lentement, donc
   * l'interface a l'air de réfléchir avant de répondre. Celle-ci démarre vite. */
  --sortie:    cubic-bezier(.23, 1, .32, 1);
  --court:     140ms;
  --moyen:     180ms;

  --police: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--fond);
  color: var(--encre);
  font: 400 15px/1.55 var(--police);
  -webkit-font-smoothing: antialiased;
  /* Les chiffres alignés partout : une colonne de dates ou de compteurs qui
   * danse d'une ligne à l'autre se relit deux fois. */
  font-variant-numeric: tabular-nums;
}

/* -- Le squelette : un cadre à gauche, un écran à droite -------------------- */
/*
 * Une grille de deux colonnes plutôt que deux blocs flottants : le cadre garde sa
 * largeur quoi qu'il arrive au contenu, et le contenu ne peut pas passer dessous.
 */
.appli {
  display: grid;
  grid-template-columns: var(--rail-l) minmax(0, 1fr);
  min-height: 100dvh;
}

.rail {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 18px 14px 14px;
  background: var(--rail);
  border-right: 1px solid var(--trait);
  /* Le cadre reste à l'écran quand l'écran de droite fait quatre hauteurs : c'est
   * toute sa raison d'être. Un menu qu'il faut remonter chercher n'est pas un
   * cadre, c'est un contenu. */
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* L'identité du site en cours d'édition. Elle est ici et nulle part ailleurs :
 * quelqu'un qui gère deux JCE doit savoir laquelle il modifie sans lire le titre. */
.rail-marque {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px 14px;
  color: var(--encre);
  text-decoration: none;
  font-weight: 600;
  letter-spacing: -.01em;
}
.marque-pastille {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--bleu);
  flex: none;
}
.rail-marque span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rail-nav { display: flex; flex-direction: column; gap: 1px; }

/* Un intertitre de groupe. En minuscules et en gris : il classe, il ne s'annonce
 * pas. Les capitales espacées d'une interface d'admin sur trois crient plus fort
 * que les entrées qu'elles surmontent. */
.rail-titre {
  margin: 18px 0 6px;
  padding: 0 10px;
  font-size: .78rem;
  font-weight: 500;
  color: var(--encre-3);
}
.rail-nav > .rail-titre:first-child { margin-top: 4px; }

.rail-lien {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--rayon-s);
  color: var(--encre-2);
  font-size: .94rem;
  font-weight: 500;
  text-decoration: none;
  transition: background-color var(--court) var(--sortie),
              color var(--court) var(--sortie);
}
.rail-lien svg { width: 18px; height: 18px; flex: none; color: var(--encre-3); }
/* Le survol n'existe que là où il y a un vrai pointeur : sur un écran tactile il
 * se déclenche au doigt et reste allumé sur l'entrée qu'on vient de quitter. */
@media (hover: hover) and (pointer: fine) {
  .rail-lien:hover { background: var(--survol); color: var(--encre); }
  .rail-lien:hover svg { color: var(--encre-2); }
}
/* L'écran où l'on se trouve. Fond plein et non pas un simple gras : à neuf
 * entrées, un gras se cherche, un aplat se voit sans lire. */
.rail-lien--actif,
.rail-lien--actif:hover {
  background: var(--marine);
  color: var(--blanc);
}
.rail-lien--actif svg, .rail-lien--actif:hover svg { color: rgba(255, 255, 255, .82); }

/* Un compteur collé à une entrée : les demandes qui attendent. Il ne s'affiche
 * que s'il y a quelque chose, sinon c'est un zéro qui occupe de la place. */
.rail-compteur {
  margin-left: auto;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--danger);
  color: var(--blanc);
  font-size: .75rem;
  font-weight: 600;
  text-align: center;
}
.rail-lien--actif .rail-compteur { background: rgba(255, 255, 255, .22); }

.rail-pied {
  margin-top: auto;
  padding: 14px 10px 4px;
  border-top: 1px solid var(--trait);
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: .85rem;
}
.rail-pied .qui {
  color: var(--encre-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rail-pied .version { color: var(--encre-3); font-size: .78rem; }

/* Sur un téléphone, le cadre passe en bandeau horizontal qui défile. Pas de menu
 * escamotable : il demanderait un script, et un menu qui ne s'ouvre pas parce que
 * le script a échoué enferme l'utilisateur dehors. */
@media (max-width: 900px) {
  .appli { grid-template-columns: minmax(0, 1fr); }
  .rail {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border-right: none;
    border-bottom: 1px solid var(--trait);
    overflow-x: auto;
  }
  .rail-marque { padding: 0 8px 0 4px; }
  .rail-nav { flex-direction: row; gap: 4px; }
  .rail-titre, .rail-pied .version { display: none; }
  .rail-lien { white-space: nowrap; padding: 7px 10px; }
  .rail-pied {
    margin: 0 0 0 auto;
    padding: 0 0 0 12px;
    border-top: none;
    border-left: 1px solid var(--trait);
  }
  .rail-pied .qui { display: none; }
}

/* -- L'anneau de focus, une fois pour toutes ------------------------------ */
/*
 * Posé sur tout ce qui peut recevoir le focus, et pas classe par classe : une
 * règle par composant se traduit toujours, tôt ou tard, par un composant écrit
 * plus tard qui n'en a pas. Le contour par défaut du navigateur existe, mais il
 * est fin, il change d'un navigateur à l'autre, et il disparaît sous un fond
 * coloré.
 *
 * :focus-visible et non :focus : le clic à la souris ne dessine rien, seul le
 * clavier le fait. C'est ce qui permet un anneau franc sans qu'il apparaisse à
 * chaque clic.
 */
:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--marine);
  outline-offset: 2px;
  border-radius: var(--rayon-xs);
}
/* Sur l'entrée active du cadre, qui est un aplat marine, l'anneau marine serait
 * invisible : il passe en blanc, à l'intérieur. */
.rail-lien--actif:focus-visible {
  outline-color: var(--blanc);
  outline-offset: -3px;
}

/* Le lien d'évitement. Invisible jusqu'à ce qu'on tabule dessus. */
.saut { position: absolute; left: -9999px; z-index: 60; }
.saut:focus {
  left: 12px; top: 12px;
  padding: 10px 14px;
  background: var(--marine);
  color: var(--blanc);
  border-radius: var(--rayon-s);
  text-decoration: none;
}

/* -- L'écran -------------------------------------------------------------- */

.corps {
  width: 100%;
  max-width: var(--largeur);
  margin: 0 auto;
  padding: 40px 32px 72px;
}
body.large .corps { max-width: 1240px; }
/* L'éditeur : aucune marge, il occupe tout.
 *
 * Et le cadre de gauche s'efface. C'est le seul écran où il le fait, pour deux
 * raisons : l'éditeur est déjà une mise en page à deux volets, et lui retirer
 * 248 pixels écrase l'aperçu, qui est la moitié de son intérêt ; et on n'y entre
 * pas pour naviguer mais pour faire une chose et repartir. Sa propre barre porte
 * « Toutes les pages », donc la sortie reste à un clic.
 */
body.pleine .corps { max-width: none; padding: 0; }
body.pleine .appli { grid-template-columns: minmax(0, 1fr); }
body.pleine .rail { display: none; }

/* L'en-tête d'écran : le nom de l'écran à gauche, LA seule action primaire à
 * droite. C'est le gabarit qui la porte, donc aucun écran ne peut en avoir deux. */
.entete-ecran {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 28px;
}
.entete-ecran > div { min-width: 0; }
.entete-ecran h1 { margin: 0; }
.entete-ecran .chapeau { margin: 6px 0 0; }
.entete-ecran-actions { display: flex; align-items: center; gap: 10px; flex: none; }

/* Le fil d'Ariane : d'où l'on vient, en une ligne. Toujours présent sur un écran
 * de deuxième niveau, parce qu'un cul-de-sac oblige à repasser par le cadre. */
.fil {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 14px;
  font-size: .88rem;
  color: var(--encre-3);
}
.fil a { color: var(--encre-2); text-decoration: none; }
.fil a:hover { color: var(--encre); text-decoration: underline; }

/* -- Typographie ---------------------------------------------------------- */

h1 {
  margin: 0 0 8px;
  font-size: 1.65rem;
  line-height: 1.2;
  font-weight: 600;
  /* Le resserrement va avec la taille : un titre à 26 px avec l'espacement d'un
   * paragraphe a l'air distendu. */
  letter-spacing: -.022em;
  text-wrap: balance;
}
h2 {
  margin: 40px 0 14px;
  font-size: 1.1rem;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -.012em;
  text-wrap: balance;
}
h3 {
  margin: 26px 0 8px;
  font-size: .98rem;
  font-weight: 600;
  letter-spacing: -.006em;
}
p { margin: 0 0 14px; text-wrap: pretty; }
a { color: var(--marine); text-decoration-thickness: 1px; text-underline-offset: 2px; }
strong { font-weight: 600; }

/* Le chapeau : une seule phrase, et une largeur de lecture. Au-delà de 62
 * caractères l'oeil perd sa ligne en revenant à gauche. */
.chapeau { margin: 0 0 24px; max-width: 62ch; color: var(--encre-2); }
.note    { color: var(--encre-3); font-size: .89rem; }
.note--espacee { margin-top: 20px; }
.aide--erreur { color: var(--danger); }
code, .code { font-family: var(--mono); font-size: .88em; }

/* -- Cartes --------------------------------------------------------------- */
/*
 * Une carte n'existe que si l'élévation dit quelque chose : « ceci est un objet
 * distinct du fond ». Le filet fait le travail, l'ombre le confirme à peine.
 */
.carte {
  background: var(--carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: 24px;
}
.carte + .carte { margin-top: 16px; }
/* Une carte qui n'est qu'un contenant de lignes : les lignes portent déjà leur
 * séparation, la carte n'a pas à rajouter de marge intérieure en haut. */
.carte--lignes { padding: 6px 24px; }

.centre { max-width: 420px; margin: 8vh auto; }

/* Une pile d'objets de même nature, à intervalle constant. Remplace les
 * margin-bottom posés à la main écran par écran. */
.pile { display: flex; flex-direction: column; gap: 16px; }
.pile--serree { gap: 8px; }

.deux-colonnes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0 20px;
}

/* -- Boutons -------------------------------------------------------------- */
/*
 * Trois niveaux, et un seul par écran peut être primaire. Le quatrième niveau est
 * un lien : quand une action n'a pas besoin d'un bouton, elle n'en prend pas.
 */
.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 9px 16px;
  border: 1px solid transparent;
  border-radius: var(--rayon-s);
  background: var(--marine);
  color: var(--blanc);
  font: inherit;
  font-size: .94rem;
  font-weight: 500;
  letter-spacing: -.005em;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  /* Jamais « all » : la liste explicite évite d'animer par accident une propriété
   * de mise en page, qui coûte un recalcul complet à chaque image. */
  transition: background-color var(--court) var(--sortie),
              border-color var(--court) var(--sortie),
              transform var(--court) var(--sortie),
              box-shadow var(--court) var(--sortie);
}
.bouton svg { width: 17px; height: 17px; }
@media (hover: hover) and (pointer: fine) {
  .bouton:hover { background: #1A3C74; }
}
/* L'enfoncement. C'est le détail qui fait qu'une interface a l'air d'avoir
 * entendu le clic : sans lui, le bouton reste inerte jusqu'à ce que la page
 * change, et l'utilisateur clique deux fois. */
.bouton:active { transform: scale(.98); }
.bouton:focus-visible { outline: 2px solid var(--marine); outline-offset: 2px; }
/* Un bouton éteint. Il reste lisible, il ne fait que cesser d'appeler le clic :
 * un bouton à 30 % d'opacité ne se lit plus, et l'utilisateur croit à une panne
 * d'affichage plutôt qu'à une action indisponible. Son motif est dans son title. */
.bouton:disabled {
  background: var(--survol);
  border-color: var(--trait);
  color: var(--encre-3);
  cursor: default;
  box-shadow: none;
}
.bouton:disabled:active { transform: none; }

/* Secondaire : la même forme, sans l'aplat. C'est le niveau par défaut de toute
 * action qui n'est pas celle que l'utilisateur est venu faire. */
.bouton--sobre, .bouton--fantome {
  background: var(--carte);
  border-color: var(--trait-fort);
  color: var(--encre);
  box-shadow: 0 1px 1px rgba(20, 20, 43, .03);
}
@media (hover: hover) and (pointer: fine) {
  .bouton--sobre:hover, .bouton--fantome:hover { background: var(--survol); }
}
/* Détruire ne ressemble jamais à enregistrer : c'est le seul endroit de
 * l'administration où le rouge apparaît, et il n'apparaît que là. */
.bouton--danger {
  background: var(--carte);
  border-color: rgba(179, 38, 30, .32);
  color: var(--danger);
}
@media (hover: hover) and (pointer: fine) {
  .bouton--danger:hover { background: var(--danger-fond); border-color: var(--danger); }
}
.bouton--danger:focus-visible { outline-color: var(--danger); }
/* L'aplat rouge, et il n'existe qu'à UN endroit : le bouton qui confirme une
 * destruction dans une boîte de dialogue. Ailleurs, détruire se dit en contour
 * (.bouton--danger) : un aplat rouge posé dans un écran de travail attire l'œil
 * sur ce qu'on ne veut surtout pas cliquer par erreur. Ici c'est l'inverse : on est
 * venu pour ça, et il doit se distinguer d'« Annuler » sans hésitation.
 * #B3261E porte du blanc à 6,5:1. */
.bouton--detruire {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--blanc);
}
@media (hover: hover) and (pointer: fine) {
  .bouton--detruire:hover { background: #96201A; border-color: #96201A; }
}
.bouton--detruire:focus-visible { outline-color: var(--danger); }

/* ── Une boîte de dialogue ───────────────────────────────────────────────────
 *
 * Rendue par le serveur, pas ouverte par un script : l'adresse porte la demande de
 * confirmation, donc la protection existe même si le script ne s'est pas chargé.
 * C'est ce qui la distingue d'un `confirm()` du navigateur, qui ne se met pas en
 * forme et ne peut pas dire ce que l'action coûte vraiment.
 */
.voile {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 20px;
  /* Le voile est sombre et transparent : il ne cache pas ce qu'on est en train de
   * décider, il le met en retrait. Une boîte posée sur un écran net donne
   * l'impression qu'on peut encore cliquer derrière. */
  background: rgba(20, 20, 43, .45);
  animation: voile-entree var(--moyen) var(--sortie);
}
.modale {
  width: min(420px, 100%);
  padding: 22px 24px 20px;
  background: var(--carte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-flot);
  animation: modale-entree var(--moyen) var(--sortie);
}
.modale-titre {
  margin: 0 0 8px;
  font-size: 1.08rem;
  font-weight: 600;
  letter-spacing: -.014em;
}
.modale-texte { margin: 0; color: var(--encre-2); font-size: .93rem; }
/* « Annuler » à gauche, l'action à droite, et l'action destructrice est la seule
 * en aplat : la main va à droite, mais l'œil a lu les deux. */
.modale-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
}

/* Elle arrive de très peu, et depuis 96 % de sa taille : rien dans le monde réel
 * n'apparaît à partir de rien, et une boîte qui surgit d'un point donne
 * l'impression d'un ressort. */
@keyframes modale-entree {
  from { opacity: 0; transform: translateY(6px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
@keyframes voile-entree {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.bouton--bloc { width: 100%; }
.bouton--s { padding: 6px 12px; font-size: .875rem; border-radius: var(--rayon-xs); }
.bouton--s svg { width: 15px; height: 15px; }

/* Un bouton d'icône seule : les flèches de l'éditeur, la duplication, le retrait.
 * Discret au repos, il ne se révèle qu'au survol de sa ligne. */
.bouton--picto {
  padding: 6px;
  width: 30px; height: 30px;
  background: transparent;
  border-color: transparent;
  color: var(--encre-3);
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
  .bouton--picto:hover { background: var(--survol); color: var(--encre); }
}

.actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 24px; }
/* Un champ qui suit une rangée de boutons a besoin de sa propre respiration : sans
 * elle, son étiquette se colle au bas des boutons et paraît leur appartenir. */
.actions + .champ, .actions + form.champ { margin-top: 18px; }

.lien-discret { color: var(--encre-2); font-size: .89rem; }
.lien-sobre { color: var(--marine); font-size: .89rem; }

/* -- Formulaires ---------------------------------------------------------- */

.champ { margin-bottom: 18px; }
.champ label {
  display: block;
  margin-bottom: 6px;
  font-size: .89rem;
  font-weight: 500;
  color: var(--encre);
}
/* L'aide d'un champ passe SOUS le champ et en petit : au-dessus, elle se lit
 * avant l'étiquette et double la hauteur de chaque ligne du formulaire. */
.champ .aide {
  display: block;
  margin-top: 6px;
  color: var(--encre-3);
  font-size: .84rem;
}

/* ── L'infobulle : le petit « i » ─────────────────────────────────────────────
 *
 * L'aide d'un champ ne s'écrit plus sous le champ. Elle se range derrière un
 * cercle de seize pixels et n'apparaît qu'au survol, au focus, ou au doigt.
 *
 * Trois points de construction :
 *  · la bulle est ancrée à la LIGNE de l'étiquette, pas à l'icône, et elle
 *    s'étire d'un bord à l'autre. Ancrée à l'icône, elle sortirait du panneau de
 *    l'éditeur, qui fait 260 px et qui défile en masquant ce qui dépasse ;
 *  · `visibility` en plus de `opacity` : sans elle, la bulle invisible reste
 *    cliquable et intercepte le clic destiné au champ qu'elle recouvre ;
 *  · fond encre et texte blanc, soit 15,8:1. Une bulle claire sur un formulaire
 *    clair ne se détacherait pas de ce qu'elle recouvre.
 */
.champ-tete {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
}
.champ-tete label { margin-bottom: 0; }
/* Le titre d'un bloc de réglages porte sa bulle comme une étiquette de champ
 * porte la sienne : même geste, même endroit, un seul mécanisme à comprendre. */
.carte-tete { margin-bottom: 12px; }
.carte-tete h2 { margin: 0; }
.carte-tete .info-bulle { top: calc(100% + 5px); }
.champ-tete--case { margin-bottom: 0; }
.champ-tete--case .case { margin: 0; }

/* L'astérisque d'un champ indispensable. Il se voit sans rien survoler : ce qui
 * conditionne la mise en ligne ne peut pas être caché dans une bulle. */
.requis {
  margin-left: 3px;
  color: var(--marine);
  font-weight: 600;
}

.info-i {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 17px;
  height: 17px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--encre-3);
  cursor: help;
  transition: color var(--court) var(--sortie);
}
.info-i svg { width: 15px; height: 15px; display: block; }
.info-i:hover, .info-i:focus-visible { color: var(--marine); }

.info-bulle {
  position: absolute;
  z-index: 40;
  top: calc(100% + 7px);
  left: 0;
  right: 0;
  padding: 9px 12px;
  border-radius: var(--rayon-xs);
  background: var(--encre);
  color: var(--blanc);
  font-size: .84rem;
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
  box-shadow: var(--ombre-flot);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  pointer-events: none;
  transition: opacity var(--court) var(--sortie),
              transform var(--court) var(--sortie),
              visibility var(--court) linear;
}
/* La pointe. Un carré tourné plutôt qu'une bordure biseautée : il suit la couleur
 * du fond de la bulle sans qu'on ait à la répéter. */
.info-bulle::before {
  content: "";
  position: absolute;
  top: -3px;
  left: 4px;
  width: 8px;
  height: 8px;
  border-radius: 1px;
  background: var(--encre);
  transform: rotate(45deg);
}
.info-i:hover + .info-bulle,
.info-i:focus-visible + .info-bulle,
.info-i:focus + .info-bulle {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* Le geste, et non l'explication du geste : « Ouvrir Médias » remplace trois
 * lignes qui disaient d'y aller. */
.champ-ailleurs {
  display: inline-block;
  margin-top: 8px;
  color: var(--marine);
  font-size: .84rem;
}

/* Un champ composé par le moteur : la valeur se lit, l'étiquette dit d'où elle
 * vient, et le pourquoi est dans la bulle. */
.champ--calcule .champ-label { font-size: .89rem; font-weight: 500; }
.champ--calcule .champ-calcule-valeur {
  margin: 0;
  padding: 9px 12px;
  border: 1px dashed var(--trait-fort);
  border-radius: var(--rayon-s);
  color: var(--encre-2);
  font-size: .91rem;
}

@media (prefers-reduced-motion: reduce) {
  .info-bulle { transition: none; }
}

input[type=text], input[type=email], input[type=password], input[type=url],
input[type=number], input[type=date], input[type=time], input[type=search],
input[type=tel], input[type=file], select, textarea {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--trait-fort);
  border-radius: var(--rayon-s);
  background: var(--blanc);
  color: var(--encre);
  font: inherit;
  font-size: .94rem;
  transition: border-color var(--court) var(--sortie),
              box-shadow var(--court) var(--sortie);
}
input::placeholder, textarea::placeholder { color: var(--encre-3); }
/* Le halo plutôt que le contour épais : il marque le champ actif sans décaler
 * d'un pixel la ligne du formulaire, ce que faisait l'ancien outline. */
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--marine);
  box-shadow: 0 0 0 3px rgba(31, 71, 137, .16);
}
textarea { min-height: 96px; resize: vertical; line-height: 1.55; }
select {
  appearance: none;
  padding-right: 34px;
  /* La flèche est dessinée dans le fond : un SVG en data-uri ne demande aucune
   * requête et la politique de sécurité du contenu l'autorise. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23676D80' stroke-width='1.7' stroke-linecap='round'%3E%3Cpath d='M4 6.5 8 10.5l4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 16px;
}
input[type=checkbox], input[type=radio] { accent-color: var(--marine); }

input.code {
  font-family: var(--mono);
  font-size: 1.5rem;
  letter-spacing: .4em;
  text-align: center;
}

/* La barre d'enregistrement d'un formulaire long. Elle suit le défilement, son
 * fond est OPAQUE, et elle porte un filet en haut : sans le filet elle a l'air
 * posée au hasard sur le texte qui passe dessous. */
.reglages-form .carte + .carte { margin-top: 16px; }
.reglages-form .carte h2 { margin-top: 0; font-size: 1rem; }
.reglages-form .carte h2 .note { font-weight: 400; margin-left: 8px; }
.reglages-form .carte h3 { font-size: .94rem; margin: 22px 0 10px; }
.reglages-form .actions {
  position: sticky;
  bottom: 0;
  margin-top: 20px;
  padding: 14px 0;
  background: var(--fond);
  border-top: 1px solid var(--trait);
}

/* -- Listes et tableaux --------------------------------------------------- */

table { width: 100%; border-collapse: collapse; font-size: .93rem; }
th {
  text-align: left;
  padding: 0 12px 9px 0;
  border-bottom: 1px solid var(--trait);
  color: var(--encre-3);
  font-size: .82rem;
  font-weight: 500;
  /* Pas de capitales forcées : « ADRESSE » ne se lit pas plus vite que
   * « Adresse », il pèse seulement plus lourd dans l'image de la page. */
  text-transform: none;
  letter-spacing: 0;
}
td {
  padding: 12px 12px 12px 0;
  border-bottom: 1px solid var(--trait);
  vertical-align: middle;
}
tr:last-child td { border-bottom: none; }
td .note { display: block; margin-top: 2px; }

/* Une ligne dont le survol éclaire toute la largeur : ça donne la cible. */
@media (hover: hover) and (pointer: fine) {
  .table--lignes tbody tr { transition: background-color var(--court) var(--sortie); }
  .table--lignes tbody tr:hover { background: var(--survol); }
}
.table--lignes td:first-child { padding-left: 10px; border-radius: var(--rayon-xs) 0 0 var(--rayon-xs); }
.table--lignes td:last-child  { padding-right: 10px; text-align: right; border-radius: 0 var(--rayon-xs) var(--rayon-xs) 0; }

/* Une liste d'objets : le nom, un état, une action. Remplace les tableaux à
 * quatre colonnes dont trois étaient de l'explication. */
.rangee {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--trait);
}
.rangee:last-child { border-bottom: none; }
/* Ces trois-là sont tantôt des paragraphes, tantôt des span à l'intérieur d'une
 * rangée-lien : un <p> dans un <a> n'est pas du HTML valide. D'où le display posé
 * explicitement plutôt que laissé à la balise. */
.rangee__corps { display: block; flex: 1; min-width: 0; }
/* Un titre posé directement dans la rangée, sans corps autour : il prend la place
 * que le corps aurait prise, sinon le chevron se colle au dernier mot au lieu de
 * tenir le bord droit, et la ligne n'a plus de bord droit à suivre. */
.rangee > .rangee__titre { flex: 1; min-width: 0; }
.rangee__titre { display: block; margin: 0; font-size: .97rem; font-weight: 500; color: var(--encre); }
.rangee__titre a { color: var(--encre); text-decoration: none; }
.rangee__titre a:hover { text-decoration: underline; }
.rangee__meta { display: block; margin: 2px 0 0; color: var(--encre-3); font-size: .86rem; }
.rangee__actions { display: flex; align-items: center; gap: 8px; flex: none; }

/* Une rangée qui EST un lien. Toute la ligne est cliquable, ce qui remplace le
 * bouton « Ouvrir » qu'on répétait quinze fois : la cible passe de 60 pixels de
 * large à toute la largeur de la carte, et l'écran perd quinze boutons primaires.
 *
 * Les marges négatives font mordre le survol jusqu'aux bords de la carte : un
 * survol qui s'arrête à trois pixels du bord a l'air d'un défaut d'alignement. */
a.rangee {
  color: inherit;
  text-decoration: none;
  margin: 0 -12px;
  padding: 14px 12px;
  border-radius: var(--rayon-xs);
  transition: background-color var(--court) var(--sortie);
}
a.rangee:not(:last-child) { border-radius: var(--rayon-xs) var(--rayon-xs) 0 0; }
@media (hover: hover) and (pointer: fine) {
  a.rangee:hover { background: var(--survol); }
}
a.rangee:focus-visible { outline: 2px solid var(--marine); outline-offset: -2px; }
/* Le chevron dit « ça mène ailleurs » sans ajouter un mot. Il est dessiné dans le
 * fond : aucune requête, et la politique de sécurité du contenu l'autorise. */
a.rangee::after {
  content: "";
  width: 16px; height: 16px;
  flex: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23676D80' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") no-repeat center;
  background-size: 16px;
}
/* Les commandes d'une ligne n'apparaissent qu'au survol de la ligne. Sur une
 * liste de huit sections à quatre commandes, c'est trente-deux boutons qui
 * cessent d'être là en permanence. Au clavier et au doigt, elles restent
 * visibles : la révélation au survol serait un piège pour eux. */
@media (hover: hover) and (pointer: fine) {
  .rangee__actions--survol { opacity: 0; transition: opacity var(--court) var(--sortie); }
  .rangee:hover .rangee__actions--survol,
  .rangee:focus-within .rangee__actions--survol { opacity: 1; }
}

.liste-controle { list-style: none; padding: 0; margin: 12px 0 0; }
.liste-controle li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--trait);
  font-size: .93rem;
}
.liste-controle li:last-child { border-bottom: none; }
.liste-controle a { white-space: nowrap; }

/* -- Étiquettes d'état ---------------------------------------------------- */
/*
 * Une pastille de couleur et un mot, sans aplat de fond : trois pastilles
 * colorées sur une ligne de tableau font un sapin de Noël, alors que le point
 * suffit à dire l'état.
 */
.etiquette {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  font-size: .85rem;
  font-weight: 500;
  color: var(--encre-2);
  white-space: nowrap;
}
.etiquette::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.etiquette--ok        { color: var(--ok); }
.etiquette--tiede,
.etiquette--attention { color: var(--attention); }
/* La pastille d'un avertissement prend le jaune de la charte, pas l'encre olive
 * qui porte son texte : à sept pixels, #6B5600 se lit comme du vert. Une pastille
 * n'est pas du texte, elle n'a donc pas de seuil de contraste à tenir. */
.etiquette--tiede::before,
.etiquette--attention::before { background: var(--jaune); }
.etiquette--info      { color: var(--marine); }
.etiquette--neutre    { color: var(--encre-3); }
/* Une étiquette qui nomme une catégorie et non un état : pas de pastille, un
 * fond gris très clair. Elle classe, elle n'alerte pas. */
.etiquette--genre {
  padding: 2px 8px;
  border-radius: var(--rayon-xs);
  background: var(--survol);
  color: var(--encre-2);
  font-weight: 400;
}
.etiquette--genre::before { display: none; }

/* -- Encarts -------------------------------------------------------------- */
/*
 * Un encart interrompt la lecture : il n'y en a qu'un par écran, et il porte
 * toujours ce qu'il faut faire.
 */
.encart {
  margin: 0 0 22px;
  padding: 13px 16px;
  border: 1px solid var(--trait);
  border-left: 3px solid var(--trait-fort);
  border-radius: var(--rayon-s);
  background: var(--carte);
  font-size: .93rem;
}
.encart p { margin: 0; }
.encart p + p { margin-top: 6px; }
.encart--ok        { background: var(--ok-fond);     border-color: rgba(31, 107, 107, .22);
                     border-left-color: var(--ok); }
.encart--attention { background: var(--att-fond);    border-color: rgba(107, 86, 0, .2);
                     border-left-color: var(--jaune); }
.encart--info      { background: var(--info-fond);   border-color: rgba(31, 71, 137, .18);
                     border-left-color: var(--marine); }
.encart--erreur    { background: var(--danger-fond); border-color: rgba(179, 38, 30, .22);
                     border-left-color: var(--danger); color: #7C1B15; }
.encart--erreur a  { color: #7C1B15; }

/* -- Vide, et ce n'est pas une erreur ------------------------------------- */
/*
 * Un écran vide qui n'affiche rien laisse croire à une panne. Il dit donc ce
 * qu'on y met, et porte l'action qui l'y met.
 */
.vide {
  padding: 40px 24px;
  text-align: center;
  border: 1px dashed var(--trait-fort);
  border-radius: var(--rayon);
  background: var(--carte);
}
.vide svg { width: 26px; height: 26px; color: var(--encre-3); margin-bottom: 10px; }
.vide h3 { margin: 0 0 6px; font-size: 1rem; }
.vide p { margin: 0 auto 16px; max-width: 46ch; color: var(--encre-2); font-size: .93rem; }

/* -- Onglets -------------------------------------------------------------- */
/*
 * De vrais liens, pas des panneaux masqués en JavaScript : chaque onglet a son
 * adresse, donc il se met en favori et revient au bon endroit après un
 * enregistrement.
 *
 * Ils sont posés sur un rail gris plutôt que découpés dans un trait : à neuf
 * onglets sur deux lignes, les découpes se lisaient comme des dossiers empilés.
 */
.onglets {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin: 0 0 26px;
  padding: 3px;
  border-radius: var(--rayon-s);
  background: var(--survol);
}
.onglet {
  padding: 7px 13px;
  border-radius: var(--rayon-xs);
  color: var(--encre-2);
  font-size: .9rem;
  font-weight: 500;
  text-decoration: none;
  transition: background-color var(--court) var(--sortie),
              color var(--court) var(--sortie);
}
@media (hover: hover) and (pointer: fine) {
  .onglet:hover { color: var(--encre); }
}
.onglet--actif {
  background: var(--carte);
  color: var(--encre);
  box-shadow: 0 1px 2px rgba(20, 20, 43, .06);
}
.onglets--sobres { font-size: .88rem; }

/* -- Indicateurs ---------------------------------------------------------- */

.indicateurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
  margin: 0 0 24px;
}
/* La marge est annulée AVEC la même spécificité que `.carte + .carte`, qui la pose
 * seize pixels plus haut : `.carte--indicateur` seul perdait, et la deuxième carte
 * d'une grille d'indicateurs se retrouvait poussée de seize pixels vers le bas. Elle
 * gardait la hauteur de la rangée, donc son fond s'arrêtait plus haut que celui de
 * la première : à l'écran, la première paraissait plus grande. Vu à l'écran. */
.carte--indicateur,
.carte + .carte--indicateur { margin: 0; padding: 18px 20px; }
.indicateur__libelle {
  margin: 0;
  font-size: .86rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--encre-3);
}
/* Le chiffre porte l'écran : grand, resserré, aligné avec les autres chiffres de
 * la grille grâce aux chiffres tabulaires posés sur body. */
.indicateur__valeur {
  margin: 4px 0 0;
  font-size: 2.1rem;
  line-height: 1.05;
  font-weight: 600;
  letter-spacing: -.03em;
  color: var(--encre);
}
.indicateur__unite { margin: 3px 0 0; font-size: .9rem; color: var(--encre-2); }
.indicateur__evolution { margin: 8px 0 0; font-size: .87rem; color: var(--encre-2); }

.carte--controle h2 { display: flex; align-items: center; gap: 10px; margin-top: 0; font-size: 1rem; }
.carte--controle + .carte--controle { margin-top: 14px; }

.source + .source { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--trait); }
.source p { margin: 0; }
.source p + p { margin-top: 3px; }
.chiffres-cles p { margin: 0 0 8px; font-size: 1rem; }
.chiffres-cles p:last-child { margin-bottom: 0; }

.diag { width: 100%; font-size: .92rem; }
.diag td:last-child { text-align: right; font-weight: 500; }
.diag .non { color: var(--attention); }

/* -- Secrets à recopier --------------------------------------------------- */

.secret {
  font-family: var(--mono);
  font-size: 1.02rem;
  letter-spacing: .06em;
  background: var(--survol);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-s);
  padding: 13px 15px;
  word-break: break-all;
  user-select: all;
}
.codes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px;
  padding: 0;
  margin: 12px 0 0;
}
.codes li {
  list-style: none;
  font-family: var(--mono);
  background: var(--survol);
  border: 1px solid var(--trait);
  border-radius: var(--rayon-xs);
  padding: 9px 12px;
  text-align: center;
}

/* -- L'écran de démarrage ------------------------------------------------- */

.demarrage { list-style: none; padding: 0; margin: 0; }
.demarrage__point { padding: 14px 0; border-bottom: 1px solid var(--trait); }
.demarrage__point:last-child { border-bottom: none; }
.demarrage__titre {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0;
  font-size: .97rem;
}
.demarrage__titre a { font-weight: 500; }
.demarrage__etat {
  display: inline-block;
  width: 1.1em;
  flex: 0 0 1.1em;
  color: var(--ok);
  font-weight: 600;
}
/* Ce qui est fait s'estompe, et son explication disparaît : il n'y a plus rien à
 * y faire, donc plus rien à y lire. */
.demarrage__point--fait .demarrage__titre span { color: var(--encre-3); }
.demarrage__point--fait .demarrage__detail { display: none; }
/* Le premier point non fait. C'est toute la fonction de cet écran, donc il a
 * droit à un fond et à une marge négative qui le fait dépasser de la liste. */
.demarrage__point--maintenant {
  margin: 0 -14px;
  padding: 16px 14px;
  border-radius: var(--rayon-s);
  background: var(--info-fond);
  border-bottom-color: transparent;
}
.demarrage__detail { margin: 5px 0 0; font-size: .9rem; color: var(--encre-2); }
.demarrage-bilan { margin: 0 0 12px; font-size: .97rem; }
/* Un titre à l'intérieur d'une carte : la marge du haut appartient à l'espace
 * entre deux blocs, pas à l'intérieur d'un bloc. */
.sans-marge { margin-top: 0; }
/* Le repli des points déjà connus : il vient après une action, donc il se sépare
 * d'un filet plutôt que d'une marge, sinon il a l'air de flotter. */
.replie-liste {
  margin: 22px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--trait);
}
.replie-liste .demarrage__point { padding: 9px 0; }

/* Une barre de progression : neuf points, et savoir où l'on en est sans compter
 * les coches. */
.jauge {
  height: 5px;
  border-radius: 999px;
  background: var(--survol);
  overflow: hidden;
  margin: 0 0 16px;
}
.jauge > i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--marine);
  /* La largeur arrive en variable posée par le PHP, pas en style en ligne : la
   * politique de sécurité du contenu interdit le second. */
  width: var(--part, 0%);
  transition: width var(--moyen) var(--sortie);
}
/* La part remplie arrive en classe et non en attribut « style » : la politique de
 * sécurité du contenu interdit le style en ligne, et un attribut posé quand même
 * ne s'applique pas du tout. Vingt et une classes par pas de cinq pour cent, ce
 * qui suffit à lire une progression et évite d'en générer cent. */
.jauge--p0{--part:0%}    .jauge--p5{--part:5%}    .jauge--p10{--part:10%}
.jauge--p15{--part:15%}  .jauge--p20{--part:20%}  .jauge--p25{--part:25%}
.jauge--p30{--part:30%}  .jauge--p35{--part:35%}  .jauge--p40{--part:40%}
.jauge--p45{--part:45%}  .jauge--p50{--part:50%}  .jauge--p55{--part:55%}
.jauge--p60{--part:60%}  .jauge--p65{--part:65%}  .jauge--p70{--part:70%}
.jauge--p75{--part:75%}  .jauge--p80{--part:80%}  .jauge--p85{--part:85%}
.jauge--p90{--part:90%}  .jauge--p95{--part:95%}  .jauge--p100{--part:100%}

/* -- Guides --------------------------------------------------------------- */

/* ── L'aide contextuelle ──────────────────────────────────────────────────────
 *
 * Refaite le 2026-08-25. Au repos, une ligne grise avec un « i » : le même
 * niveau visuel qu'une mention, et non celui d'une alerte. Elle ne s'ouvre que
 * si on la lui demande, et c'est alors seulement qu'elle prend une surface.
 *
 * La flèche native du <details> est retirée : le « i » dit déjà qu'il y a
 * quelque chose à ouvrir, et deux marqueurs pour un seul geste font deux fois
 * plus de bruit pour la même information.
 */
.aide-bloc { margin: 0 0 16px; font-size: .88rem; }
.aide-bloc > summary {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 3px 0;
  cursor: pointer;
  color: var(--encre-3);
  list-style: none;
  transition: color var(--court) var(--sortie);
}
.aide-bloc > summary::-webkit-details-marker { display: none; }
.aide-bloc > summary::marker { content: ""; }
.aide-bloc > summary:hover { color: var(--marine); }
.aide-i { width: 15px; height: 15px; flex: none; }
.aide-bloc[open] > summary { color: var(--marine); font-weight: 500; }
.aide-corps {
  margin-top: 8px;
  padding: 12px 14px;
  border-radius: var(--rayon-s);
  background: var(--info-fond);
  color: var(--encre-2);
  line-height: 1.55;
}
.aide-corps > p { margin: 0 0 9px; }
.aide-corps > p:last-child { margin-bottom: 0; }
.aide-corps .aide-verifier { color: var(--encre); }
.aide-bloc kbd {
  font-family: var(--mono);
  font-size: .8rem;
  padding: 1px 6px;
  border: 1px solid var(--trait-fort);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--carte);
  color: var(--encre);
}

/* ── Un repli : ce qui a déjà une bonne valeur ────────────────────────────────
 *
 * Les options d'une section et ses couleurs. Elles ont toutes un défaut qui
 * convient, c'est même leur définition : les afficher d'office fait chercher le
 * titre au milieu de quinze champs. Le compteur dit combien il y a derrière,
 * pour qu'on sache si ça vaut le clic.
 */
.repli {
  margin: 0 0 14px;
  border: 1px solid var(--trait);
  border-radius: var(--rayon-s);
  background: var(--carte);
}
.repli > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  cursor: pointer;
  font-size: .89rem;
  font-weight: 500;
  color: var(--encre);
  list-style: none;
  border-radius: var(--rayon-s);
}
.repli > summary::-webkit-details-marker { display: none; }
.repli > summary::marker { content: ""; }
/* Le chevron dessiné dans le fond, et qui pivote : il dit lequel des deux gestes
 * le clic va faire, le même mouvement que le sélecteur de page de la barre. */
.repli > summary::after {
  content: "";
  width: 15px;
  height: 15px;
  margin-left: auto;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23676D80' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5 8 10.5l4-4'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform var(--court) var(--sortie);
}
.repli[open] > summary::after { transform: rotate(180deg); }
.repli-compte {
  min-width: 19px;
  padding: 1px 6px;
  border-radius: 9px;
  background: var(--survol);
  color: var(--encre-3);
  font-size: .76rem;
  font-weight: 600;
  text-align: center;
}
.repli-corps {
  padding: 4px 12px 14px;
  border-top: 1px solid var(--trait);
  margin-top: -1px;
}
.repli-corps > .champ:last-child { margin-bottom: 0; }

@media (hover: hover) and (pointer: fine) {
  .repli > summary:hover { background: var(--survol); }
}
@media (prefers-reduced-motion: reduce) {
  .repli > summary::after { transition: none; }
}

/* ── Une grille de tuiles ─────────────────────────────────────────────────────
 *
 * Ce qui remplace les listes de barres blanches identiques. Une tuile porte un
 * pictogramme, un nom court, et deux ou trois mentions en pied. On la reconnaît
 * à sa forme et à son dessin avant de lire son nom, ce qu'une ligne de texte ne
 * permet jamais : neuf lignes de dix mots ne sont pas une liste, c'est un
 * paragraphe.
 *
 * `auto-fill` avec un minimum de 232 px : trois colonnes sur un écran large,
 * deux sur un portable, une sur un téléphone, sans point de rupture écrit.
 */
.grille-cartes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 12px;
  margin: 0 0 28px;
}

.tuile {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  background: var(--carte);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--court) var(--sortie),
              box-shadow var(--court) var(--sortie),
              transform var(--court) var(--sortie);
}
.tuile-picto {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: var(--rayon-s);
  background: var(--info-fond);
  /* Le bleu de la charte, et c'est l'emploi qui lui reste : un pictogramme n'est
   * pas du texte, il n'a pas à tenir 4,5:1. */
  color: var(--bleu);
}
.tuile-picto .picto { width: 21px; height: 21px; }
.tuile-titre {
  font-size: .97rem;
  font-weight: 600;
  color: var(--encre);
  line-height: 1.35;
}
.tuile-resume {
  font-size: .87rem;
  color: var(--encre-2);
  line-height: 1.45;
}
/* Le pied est poussé en bas : sur une grille, des mentions qui flottent à des
 * hauteurs différentes d'une tuile à l'autre font une grille de travers. */
.tuile-pied {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: 6px;
}
.tuile-pied .jauge { flex: 1; min-width: 46px; margin: 0; }
.tuile-action {
  margin-left: auto;
  color: var(--marine);
  font-size: .87rem;
  font-weight: 500;
}
/* Un parcours terminé s'estompe : il reste relançable, il cesse d'appeler. */
.tuile--faite .tuile-picto { background: var(--ok-fond); color: var(--ok); }

@media (hover: hover) and (pointer: fine) {
  .tuile:hover {
    border-color: var(--trait-fort);
    box-shadow: var(--ombre);
    transform: translateY(-1px);
  }
}
.tuile:focus-visible { outline: 2px solid var(--marine); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .tuile, .tuile:hover { transition: none; transform: none; }
}

/* Une tuile de catalogue : elle porte une ACTION et non un lien, donc elle n'est
 * pas elle-même cliquable et elle ne se soulève pas au survol. Une tuile qui a
 * l'air cliquable et qui ne l'est qu'à un endroit est un piège. */
.tuile--catalogue { gap: 6px; }
.tuile-tete {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
}
.tuile-tete .info-bulle { top: calc(100% + 6px); }
.tuile-adresse code { font-family: var(--mono); font-size: .78rem; color: var(--encre-3); }
.tuile--catalogue .etiquette { margin: 0; }
.tuile-forme { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.tuile--catalogue .tuile-pied { padding-top: 12px; gap: 10px; }
/* Une page déjà en ligne s'estompe : elle reste dans le catalogue parce qu'on vient
 * aussi ici pour retirer, mais elle cesse d'appeler le clic. */
.tuile--faite.tuile--catalogue .tuile-titre { color: var(--encre-2); }

/* ── La bande « la prochaine chose à faire » ──────────────────────────────── */
.suite {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  margin: 0 0 28px;
  border: 1px solid var(--trait);
  border-left: 3px solid var(--marine);
  border-radius: var(--rayon);
  background: var(--carte);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--court) var(--sortie);
}
.suite-picto { display: inline-flex; color: var(--bleu); flex: none; }
.suite-picto .picto { width: 22px; height: 22px; }
.suite-corps { display: block; flex: 1; min-width: 0; }
.suite-sur-titre {
  display: block;
  color: var(--encre-3);
  font-size: .8rem;
}
.suite-titre { display: block; font-weight: 600; color: var(--encre); }
.suite::after {
  content: "";
  width: 16px; height: 16px; flex: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%231F4789' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3.5 10.5 8 6 12.5'/%3E%3C/svg%3E") no-repeat center / 16px;
}
@media (hover: hover) and (pointer: fine) {
  .suite:hover { background: var(--survol); }
}
.suite:focus-visible { outline: 2px solid var(--marine); outline-offset: 2px; }

/* ── La fiche d'un guide ──────────────────────────────────────────────────────
 *
 * Les étapes le long d'un fil vertical numéroté. Une liste ordonnée nue laissait
 * la personne compter les paragraphes pour savoir où elle en était ; le fil dit
 * combien il en reste sans qu'on ait à les compter.
 */
.guide-tete { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 18px; }
.guide-tete h1 { margin: 0 0 8px; }
.guide-tete-picto {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: var(--rayon-s);
  background: var(--info-fond);
  color: var(--bleu);
}
.guide-tete-picto .picto { width: 26px; height: 26px; }
.guide-puces { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; }

.guide-cadre { margin-bottom: 20px; }
.guide-quand { margin: 0 0 8px; color: var(--encre-2); font-size: .92rem; }
.guide-pourquoi { margin: 0; color: var(--encre); }

.guide-fil { list-style: none; margin: 0 0 20px; padding: 0; }
.guide-etape {
  position: relative;
  display: flex;
  gap: 14px;
  padding: 0 0 22px;
}
/* Le fil relie un numéro au suivant, et s'arrête au dernier : une ligne qui
 * dépasse sous la dernière étape laisse croire qu'il en manque une. */
.guide-etape:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 30px;
  bottom: 2px;
  width: 2px;
  background: var(--trait);
}
.guide-numero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--marine);
  color: var(--blanc);
  font-size: .84rem;
  font-weight: 600;
}
.guide-etape-corps { flex: 1; min-width: 0; padding-top: 3px; }
.guide-faire { margin: 0; line-height: 1.6; }
.guide-voir, .guide-piege {
  display: block;
  margin: 8px 0 0;
  padding: 9px 12px;
  border-left: 3px solid var(--trait);
  border-radius: 0 var(--rayon-xs) var(--rayon-xs) 0;
  background: var(--survol);
  font-size: .89rem;
  color: var(--encre-2);
  line-height: 1.5;
}
/* L'intitulé sur sa propre ligne, en capitales de petite taille : « Le piège : »
 * au fil du texte se lisait comme le début de la phrase. */
.guide-voir strong, .guide-piege strong {
  display: block;
  margin-bottom: 2px;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.guide-voir  { border-left-color: var(--ok); background: var(--ok-fond); }
.guide-voir strong { color: var(--ok); }
.guide-piege { border-left-color: var(--jaune); background: var(--att-fond); }
.guide-piege strong { color: var(--attention); }

.guide-verif { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; }
.guide-verif .aide-verifier { margin: 0; flex: 1; min-width: 240px; }

/* ── L'écran de la charte ─────────────────────────────────────────────────────
 *
 * Un écran qui existe pour MONTRER une charte l'écrivait en tableaux de chiffres.
 * Ce qui suit habille les quatre vues : la carte d'une couleur, le spécimen
 * typographique à sa vraie taille, et le verdict d'un contrôle.
 */

/* Une carte par couleur : l'aplat d'abord, parce que c'est lui qu'on est venu
 * voir, et le code en dessous. `auto-fill` à 210 px : trois par rangée sur un
 * écran large, une sur un téléphone. */
.couleurs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px;
  margin: 0 0 26px;
}
.couleur {
  overflow: hidden;
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  background: var(--carte);
}
/* L'aplat est un SVG et non un fond CSS : la politique de sécurité du contenu
 * interdit le style en ligne, et un attribut `fill` de SVG n'est pas du CSS.
 * `preserveAspectRatio: none` le laisse s'étirer à la largeur de la carte. */
.couleur-aplat {
  display: block;
  width: 100%;
  height: 92px;
  /* Le blanc de la charte sur une carte blanche serait invisible : un filet
   * intérieur en dessous de l'aplat lui rend son bord. */
  box-shadow: inset 0 -1px 0 var(--trait);
}
.couleur-corps { padding: 12px 14px 14px; }
.couleur-tete {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
}
.couleur-tete h2 { margin: 0; font-size: .97rem; }
.couleur-tete .info-bulle { top: calc(100% + 6px); }
.couleur-code { margin: 4px 0 10px; }
.couleur-code code { font-family: var(--mono); font-size: .85rem; color: var(--encre-2); }
.couleur-declinaisons {
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 0 0 10px;
}
.couleur-declinaisons .note { margin-left: 6px; font-size: .78rem; }
.declinaison svg { display: block; border-radius: 5px; }
.couleur .etiquette { margin: 0; }

/* Le verdict d'un contrôle de couleur : la pastille en grand à gauche, les trois
 * phrases à droite. Trois listes à puces les mettaient sur le même rang que le
 * reste de l'écran, alors qu'elles répondent à une question qu'on vient de poser. */
.verdict {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--trait);
}
.verdict-pastille svg { display: block; border-radius: var(--rayon-xs); box-shadow: 0 0 0 1px var(--trait); }
.verdict-corps { flex: 1; min-width: 0; }
.verdict-titre { margin: 0 0 8px; font-weight: 500; }
.verdict-titre--ok        { color: var(--ok); }
.verdict-titre--attention { color: var(--attention); }
.verdict-ligne {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0 0 6px;
  font-size: .9rem;
  color: var(--encre-2);
}
.verdict-ligne code { font-family: var(--mono); font-size: .82rem; }
.verdict-ligne svg { border-radius: 4px; }
/* L'étiquette du fond testé est dessinée à la couleur du fond testé : « sur nuit »
 * écrit sur du nuit vaut mieux que « sur nuit » écrit en gris. */
.verdict-fond {
  flex: none;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: .78rem;
  font-weight: 500;
}
.verdict-fond--blanc { background: var(--blanc); color: var(--encre); box-shadow: inset 0 0 0 1px var(--trait-fort); }
.verdict-fond--nuit  { background: var(--nuit);  color: var(--blanc); }

/* ── Les spécimens typographiques ────────────────────────────────────────────
 *
 * La taille est portée par une CLASSE et non par un attribut « style » : la
 * politique de sécurité du contenu interdit le second. Les cinq niveaux sont une
 * liste fermée, écrite dans charte_typographie() ; cinq classes suffisent, et
 * elles suffiront encore dans dix ans.
 *
 * Les valeurs sont celles de la charte, en pixels, telles que le site les rend.
 */
.specimens { margin: 0 0 26px; }
.specimen {
  padding: 20px 22px;
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  background: var(--carte);
}
.specimen + .specimen { margin-top: 12px; }
.specimen-mesures { margin: 0 0 10px; color: var(--encre-3); font-size: .8rem; }
.specimen-mesures strong { color: var(--encre-2); font-weight: 600; }
.specimen-texte {
  margin: 0;
  color: var(--encre);
  font-family: "Plus Jakarta Sans", var(--police);
  /* Un titre de 64 px sur un écran de téléphone déborderait : il rétrécit avec la
   * fenêtre au lieu de pousser une barre de défilement horizontale. */
  overflow-wrap: break-word;
}
.specimen--titre               { font-size: clamp(2rem, 7vw, 4rem);   line-height: 1.2; font-weight: 700; }
.specimen--sous_titre          { font-size: clamp(1.4rem, 4vw, 2rem); line-height: 1.3; font-weight: 500; }
.specimen--sous_titre_section  { font-size: 1.7rem; line-height: 1.4; font-weight: 600; }
.specimen--paragraphe          { font-size: 1.3rem; line-height: 1.4; font-weight: 400; }
.specimen--citation {
  font-size: clamp(1.4rem, 4vw, 2rem);
  line-height: 1.3;
  font-weight: 400;
  font-family: Arvo, Georgia, serif;
}

/* Une graisse se juge sur un dessin de lettre, pas sur son nombre. */
.graisse-specimen {
  flex: none;
  width: 44px;
  font-family: "Plus Jakarta Sans", var(--police);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--encre);
}
.graisse--200 { font-weight: 200; }  .graisse--300 { font-weight: 300; }
.graisse--400 { font-weight: 400; }  .graisse--500 { font-weight: 500; }
.graisse--600 { font-weight: 600; }  .graisse--700 { font-weight: 700; }
.graisse--800 { font-weight: 800; }
.graisse .etiquette, .graisse .note { flex: none; }

/* Un champ et son bouton sur la même ligne : une recherche, un code couleur. */
.ligne-champ { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.ligne-champ input { flex: 1; min-width: 160px; width: auto; }
.ligne-champ input[type=text] { flex: 1; min-width: 160px; width: auto; }
.ligne-champ .bouton { flex: none; }

/* Quatre petits champs qui tiennent ensemble : ils forment un seul réglage. */
.grille-champs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0 14px;
  align-items: end;
}
.grille-champs .actions { grid-column: 1 / -1; margin-top: 6px; }

/* Une étiquette qui existe pour les lecteurs d'écran et pour eux seuls : le champ
 * qu'elle nomme porte déjà son intitulé dans son texte de substitution. */
.visuellement-cache {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* -- Pictogrammes de la charte (écran de démonstration) ------------------- */

.pictos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 6px;
  list-style: none;
  padding: 0;
}
.pictos li {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .5rem .7rem;
  border: 1px solid var(--trait);
  border-radius: var(--rayon-xs);
  font-size: .87rem;
}
.pictos svg { width: 1.35rem; height: 1.35rem; flex: none; }
.pictos span { flex: 1; }
.pictos code { font-size: .7rem; color: var(--encre-3); }
.carte--motif { display: grid; place-items: center; padding: 2rem; }
.carte--motif svg { max-width: 420px; }

/* ── La porte : les écrans qu'on voit avant d'être entré ──────────────────────
 *
 * Connexion, second facteur, invitation, installateur. Une carte centrée dans la
 * hauteur de l'écran, et rien d'autre : ni barre du haut, ni cadre, ni pied de
 * page. Une barre qui portait le nom de la plateforme au-dessus d'une carte qui
 * portait le même nom l'écrivait deux fois, et la version du moteur n'aide en rien
 * quelqu'un qui essaie de se connecter.
 *
 * `min-height: 100dvh` et non `100vh` : sur un téléphone, la barre d'adresse du
 * navigateur mange la différence, et la carte se retrouvait coupée en bas.
 */
body.porte { background: var(--fond); }
body.porte .corps {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100dvh;
  max-width: none;
  padding: 32px 20px;
}
.porte-carte {
  width: 100%;
  max-width: 400px;
  padding: 30px 30px 26px;
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  background: var(--carte);
  box-shadow: var(--ombre);
}
/* La marque au-dessus du titre, en petit : elle dit OÙ l'on est, le titre dit ce
 * qu'on y fait. L'inverse ferait lire deux fois le même mot. */
.porte-marque {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 22px;
  color: var(--encre-2);
  font-size: .88rem;
  font-weight: 500;
}
.porte-carte h1 { margin: 0 0 6px; font-size: 1.4rem; }
.porte-carte .chapeau { margin: 0 0 22px; font-size: .93rem; }
.porte-carte .champ:last-of-type { margin-bottom: 22px; }
.porte-carte h3 { font-size: .95rem; margin: 22px 0 8px; }
.porte-pied {
  margin: 22px 0 0;
  color: var(--encre-3);
  font-size: .8rem;
}
/* Un écran de porte qui explique une manipulation (l'activation du second facteur,
 * l'installateur) déborde de 400 px : il prend la largeur d'une colonne de lecture. */
body.porte .porte-carte--large { max-width: 620px; }

@media (max-width: 460px) {
  .porte-carte { padding: 24px 20px 22px; border-radius: var(--rayon-s); }
}

/* -- L'ancien en-tête, gardé pour les écrans sans session ----------------- */
/*
 * Connexion, second facteur, invitation, installateur : ces quatre écrans n'ont
 * pas de cadre de navigation, parce qu'il n'y a encore rien à naviguer. Ils
 * gardent donc un en-tête simple, et il est clair plutôt que nuit : une barre
 * sombre sur une page de connexion vide en fait un mur.
 */
.tete { background: var(--carte); border-bottom: 1px solid var(--trait); }
.tete .dedans {
  max-width: 1240px;
  margin: 0 auto;
  padding: 13px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.marque {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--encre);
  font-weight: 600;
  text-decoration: none;
  letter-spacing: -.01em;
}
.tete-nav { display: flex; align-items: center; gap: 14px; }
.tete .qui { color: var(--encre-2); font-size: .89rem; }

.pied {
  border-top: 1px solid var(--trait);
  color: var(--encre-3);
  font-size: .84rem;
}
.pied .dedans {
  max-width: 1240px;
  margin: 0 auto;
  padding: 16px 24px;
  display: flex;
  gap: 16px;
  justify-content: space-between;
}
.pied-mode { color: var(--encre-3); }

@media (max-width: 640px) {
  .corps { padding: 26px 18px 48px; }
  .carte { padding: 18px; }
  .carte--lignes { padding: 4px 18px; }
  .centre { margin: 4vh auto; }
  h1 { font-size: 1.45rem; }
  .entete-ecran { margin-bottom: 22px; }
}

/* -- La visite guidée ----------------------------------------------------- */
/*
 * LA règle du point 13.1 : « la visite ne bloque jamais l'écran, ne masque rien,
 * n'empêche aucune action ». D'où l'absence de voile : l'estompage vient d'une
 * OMBRE PORTÉE immense sur l'élément mis en évidence. Une ombre ne capte pas les
 * clics, donc l'écran entier reste utilisable sans qu'aucune ligne de code n'ait
 * à s'en occuper.
 */
.visite-cible {
  position: relative;
  z-index: 40;
  border-radius: var(--rayon-s);
  /* Pas de transition sur cette ombre : une mise en évidence qui s'installe en
   * fondu est illisible dans un onglet en arrière-plan, où le navigateur suspend
   * les animations. Elle apparaît d'un coup. */
  box-shadow: 0 0 0 3px var(--marine),
              0 0 0 9999px rgba(20, 20, 43, .5);
}
.visite-bulle {
  position: fixed;
  z-index: 50;
  width: min(360px, calc(100vw - 32px));
  padding: 18px 20px 16px;
  background: var(--carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-flot);
}
.visite-bulle--centre { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.visite-compteur {
  margin: 0 0 6px;
  font-size: .8rem;
  color: var(--encre-3);
  letter-spacing: 0;
  text-transform: none;
}
.visite-titre { margin: 0 0 8px; font-weight: 600; font-size: 1.02rem; letter-spacing: -.01em; }
.visite-texte { margin: 0; font-size: .93rem; line-height: 1.55; color: var(--encre-2); }
.visite-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 16px; }
.visite-fermer {
  position: absolute;
  top: 8px; right: 10px;
  border: none;
  background: none;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--encre-3);
  cursor: pointer;
  padding: 4px 8px;
  border-radius: var(--rayon-xs);
}
.visite-fermer:hover { color: var(--encre); background: var(--survol); }

/* -- Impression ----------------------------------------------------------- */

@media print {
  /* Sur papier, le cadre de navigation, les actions et les bulles ne servent à
   * rien : ils prennent la moitié de la première page et ne sont pas cliquables. */
  .rail, .tete, .pied, .actions, .onglets, .visite-bulle,
  .entete-ecran-actions { display: none; }
  /* Sur papier, un repli fermé serait une information perdue : le lecteur du PDF
   * ne peut pas l'ouvrir. Tout ce qui est replié se déplie donc à l'impression,
   * ce qui est aussi ce qui rend l'export du point 14.3 complet. */
  details:not([open]) > *:not(summary) { display: block; }
  details > summary::after, .repli > summary::after { display: none; }
  .appli { display: block; }
  body { background: #FFF; }
  .corps { max-width: none; padding: 0; }
  .carte { break-inside: avoid; border: 1px solid #DDD; box-shadow: none; }
  .indicateurs { grid-template-columns: repeat(2, 1fr); }
  .visite-cible { box-shadow: none; }
  h2 { break-after: avoid; }
}

/* ── Un choix en segments ────────────────────────────────────────────────────
 *
 * Trois boutons collés, à la façon d'un sélecteur de période : à trois choix, une
 * liste déroulante demande deux clics pour montrer ce qu'un coup d'œil suffirait à
 * lire, et elle cache deux options sur trois.
 *
 * Ce sont de VRAIS boutons radio, seulement habillés. Le bouton natif n'est pas
 * masqué par `display: none`, qui le retirerait de l'ordre de tabulation : il est
 * rendu transparent et étalé sur toute l'étiquette, donc le clavier le trouve, le
 * lecteur d'écran l'annonce, et le formulaire l'envoie sans une ligne de script.
 */
.segments { border: none; margin: 0 0 18px; padding: 0; min-width: 0; }
.champ-legende {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  margin-bottom: 6px;
  font-size: .89rem;
  font-weight: 500;
  color: var(--encre);
}
.champ-legende .info-bulle { top: calc(100% + 5px); }
.segments-choix {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  border-radius: var(--rayon-s);
  background: var(--survol);
}
.segment {
  position: relative;
  margin: 0;
  padding: 0;
  border-radius: var(--rayon-xs);
  cursor: pointer;
}
.segment input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.segment span {
  display: block;
  padding: 6px 13px;
  border-radius: var(--rayon-xs);
  font-size: .88rem;
  font-weight: 500;
  color: var(--encre-2);
  white-space: nowrap;
  transition: background-color var(--court) var(--sortie),
              color var(--court) var(--sortie);
}
.segment:has(input:checked) span {
  background: var(--carte);
  color: var(--encre);
  box-shadow: 0 1px 2px rgba(20, 20, 43, .06);
}
.segment:has(input:focus-visible) span { outline: 2px solid var(--marine); outline-offset: 1px; }
@media (hover: hover) and (pointer: fine) {
  .segment:hover span { color: var(--encre); }
}
@media (prefers-reduced-motion: reduce) {
  .segment span { transition: none; }
}

/* Ce qu'un champ PRODUIT, écrit sous lui : « le site écrira Trésorière ». C'est une
 * conséquence et non une consigne, donc elle ne se replie pas derrière un « i » et
 * ne s'affiche que s'il y a une différence à montrer. */
.champ-effet {
  margin: 6px 0 0;
  font-size: .84rem;
  color: var(--encre-2);
}
.champ-effet strong { color: var(--encre); font-weight: 600; }

/* ── Une fonction du bureau, repliée ─────────────────────────────────────────
 *
 * La question qu'on se pose devant cet écran est « qui porte quoi », et elle se
 * répond en une ligne. Les six champs de la fonction sont derrière.
 */
.fonction-nom { font-weight: 500; color: var(--encre); }
.fonction-qui {
  margin-left: auto;
  margin-right: 10px;
  color: var(--encre-3);
  font-size: .88rem;
  font-weight: 400;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* La seule couleur de l'écran : la fonction qui ne mène à aucune adresse valide. */
.fonction-alerte {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--jaune);
}
/* Un intertitre à l'intérieur d'un formulaire : il sépare deux sujets sans prendre
 * le poids d'un titre de carte. */
.sous-titre {
  margin: 20px 0 10px;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--encre-3);
}

/* La bulle se place par rapport à la ligne qui la porte. Plutôt que de déclarer
 * `position: relative` sur chacune des quinze lignes concernées, et d'oublier la
 * seizième dans six mois, la règle est posée une fois : est un repère tout élément
 * qui porte une bulle en enfant direct. Le sélecteur ne remonte pas plus haut, donc
 * il ne touche ni le corps du document ni les cartes. */
:has(> .info-bulle) { position: relative; }

/* Un état en trois points : acquis, manquant, invérifiable. Le mot est dans le
 * `title` et dans l'`aria-label`, et il ne s'écrit que si l'état n'est pas acquis.
 * « En place » répété trois fois de suite n'apprend rien. */
.etat-point {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
}
.etat-point--ok     { background: var(--ok); }
.etat-point--manque { background: var(--jaune); }
.etat-point--flou   { background: none; box-shadow: inset 0 0 0 1.5px var(--trait-fort); }
.courriel-controle .rangee__titre { flex: none; }

/* ── Un chiffre du réseau ────────────────────────────────────────────────────
 *
 * Trois colonnes sur une ligne : ce que la valeur est, ce qui s'affiche
 * aujourd'hui, et le champ qui permet de dire autre chose. L'ordre du point 3.1
 * tient dans la lecture de gauche à droite : on lit avant de pouvoir contredire.
 */
.reseau-ligne { gap: 16px; }
.reseau-ligne .rangee__meta code { font-family: var(--mono); font-size: .78rem; }
.reseau-valeur {
  flex: none;
  width: 160px;
  text-align: right;
  font-weight: 500;
  color: var(--encre);
  font-size: .93rem;
}
.reseau-valeur .etiquette { display: block; margin: 3px 0 0; font-weight: 400; }
/* Une valeur de diagnostic qui ne convient pas. Elle porte la couleur, et le nom du
 * contrôle reste noir : c'est la valeur qu'on va corriger, pas le contrôle. */
.reseau-valeur--non { color: var(--attention); }
/* Le champ de surcharge est étroit et discret : c'est l'exception, pas le geste
 * qu'on vient faire. Un champ pleine largeur en ferait le sujet de la ligne.
 *
 * Le sélecteur passe par la balise : la règle générale des champs de saisie est
 * `input[type=text]`, dont la spécificité est celle d'une classe PLUS celle d'un
 * attribut. Une classe seule perdait, et le champ reprenait 100 % de largeur en
 * débordant de la carte. Vu à l'écran. */
input.reseau-champ { flex: none; width: 190px; font-size: .89rem; }
input.reseau-champ::placeholder { font-size: .85rem; }

@media (max-width: 760px) {
  .reseau-ligne { flex-wrap: wrap; }
  .reseau-valeur { width: auto; text-align: left; }
  input.reseau-champ { width: 100%; }
}

/* Le titre d'un habillage et son « i ». La bulle s'ancre sous la ligne du titre et
 * s'étire à la largeur de la vignette : ancrée à l'icône, elle sortirait de la
 * carte, qui est étroite. */
.modele-tete { display: flex; align-items: center; gap: 6px; }
.modele-tete h2 { margin: 0; }
.modele-tete .info-bulle { top: calc(100% + 6px); }

/* ── Les promesses d'un écran de décision ────────────────────────────────────
 *
 * L'écran de bascule d'habillage existe pour rassurer, et c'est la seule chose
 * qu'il fait : sans la phrase « rien n'est touché », personne n'ose cliquer. Les
 * phrases restent donc en toutes lettres, mais chacune sur une ligne, cochée. Une
 * liste à puces se lit comme une notice ; des lignes cochées se lisent comme un
 * contrat.
 */
.promesses {
  list-style: none;
  margin: 0 0 24px;
  padding: 0;
}
.promesses li {
  position: relative;
  padding: 0 0 12px 28px;
  color: var(--encre-2);
  line-height: 1.55;
}
.promesses li:last-child { padding-bottom: 0; }
.promesses li strong { color: var(--encre); }
.promesses li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 4px;
  width: 16px;
  height: 16px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%231F6B6B' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5 6.5 11.5 12.5 4.5'/%3E%3C/svg%3E") no-repeat center / 16px;
}

/* « 4 → 7 sections » : un avant et un après sur la même ligne, ce qu'un tableau à
 * trois colonnes disait en trois cellules dont deux d'en-tête. */
.compo-delta {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: none;
  font-size: .9rem;
  color: var(--encre-2);
}

/* ── Une redirection ─────────────────────────────────────────────────────────
 *
 * Elle se lit comme elle se dit : « ceci mène à cela ». Deux adresses séparées par
 * une flèche, et non quatre colonnes de tableau dont deux de gris.
 */
.redirection-trajet {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.redirection-trajet code {
  font-family: var(--mono);
  font-size: .85rem;
  color: var(--encre);
  padding: 1px 6px;
  border-radius: 5px;
  background: var(--survol);
}
.redirection-fleche {
  width: 14px;
  height: 14px;
  flex: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23676D80' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 8h11M10 4.5 13.5 8 10 11.5'/%3E%3C/svg%3E") no-repeat center / 14px;
}
.redirection-suivie { flex: none; font-size: .86rem; text-align: right; white-space: nowrap; }
.redirection .etiquette { margin: 0; }

/* ── La barre de création, et la barre de filtre ──────────────────────────────
 *
 * Deux cartes empilées portaient l'une un champ de titre et un bouton, l'autre un
 * champ de recherche, un menu et un bouton : quatre lignes de formulaire avant la
 * première fiche. Elles tiennent chacune sur une ligne.
 *
 * La création garde l'aplat marine et le filtre passe en contour : sur cet écran,
 * une seule des deux est ce qu'on est venu faire.
 */
.barre-creer, .barre-filtre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
}
.barre-creer { padding: 12px 14px; border: 1px solid var(--trait); border-radius: var(--rayon); background: var(--carte); }
.barre-filtre { margin-bottom: 18px; }
.barre-creer input[type=text],
.barre-filtre input[type=search] { flex: 1; min-width: 200px; width: auto; }
.barre-filtre select { flex: none; width: auto; min-width: 170px; }
.barre-creer .bouton, .barre-filtre .bouton { flex: none; }

/* Le point d'état d'une fiche. Trois couleurs pour trois états qu'on distingue
 * sans lire ; le mot est dans le `title` et dans l'`aria-label`, parce que « en
 * ligne » écrit vingt fois de suite ne distingue plus rien. */
.etat-point--brouillon { background: none; box-shadow: inset 0 0 0 1.5px var(--marine); }

/* ── Une demande ─────────────────────────────────────────────────────────────
 *
 * Le point plein est le seul repère de la liste : plein, la demande attend ;
 * creux, elle est traitée. Le mot est dans le `title` et dans l'`aria-label`,
 * parce qu'un point de huit pixels ne porte pas de mot, et qu'une colonne
 * « statut » répétée quarante fois n'est pas une information.
 */
.demande-point {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--marine);
}
.demande-point--faite { background: none; box-shadow: inset 0 0 0 1.5px var(--trait-fort); }

/* Un champ de demande : son intitulé à gauche sur une colonne fixe, sa valeur à
 * droite. Le tableau à deux colonnes disparaît, et le message long se replie sans
 * pousser la colonne des intitulés. */
.rangee--champ { align-items: flex-start; gap: 20px; }
.rangee-label {
  flex: none;
  width: 168px;
  color: var(--encre-3);
  font-size: .87rem;
  padding-top: 1px;
}
.rangee-valeur { flex: 1; min-width: 0; color: var(--encre); overflow-wrap: break-word; }

@media (max-width: 620px) {
  .rangee--champ { flex-direction: column; gap: 3px; }
  .rangee-label { width: auto; }
}

/* Un repli qui ouvre sur ce qui détruit. Il ne prend pas le rouge au repos : un
 * aplat rouge posé dans un écran de travail attire l'œil sur ce qu'on ne veut
 * surtout pas cliquer par erreur. Il le prend une fois ouvert, quand on est venu
 * pour ça. */
.repli--danger > summary { color: var(--encre-2); }
.repli--danger[open] { border-color: rgba(179, 38, 30, .3); }
.repli--danger[open] > summary { color: var(--danger); }

/* ── Une ligne de journal ────────────────────────────────────────────────────
 *
 * Deux cents lignes dans un tableau de quatre colonnes, dont trois de gris : c'est
 * un mur, pas une liste. La ligne porte le dessin de sa famille à gauche, ce qui
 * s'est passé au milieu, et depuis quand à droite.
 *
 * La date exacte est dans le `title` de la durée : on cherche « avant-hier » dix
 * fois pour une fois qu'on cherche « le 12 à 14 h 03 ».
 */
.journal-ligne { padding: 11px 0; }
.journal-picto {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: var(--rayon-xs);
  background: var(--survol);
  color: var(--encre-3);
}
.journal-picto .picto { width: 16px; height: 16px; }
/* La seule couleur du journal, et elle ne marque que ce qui a détruit quelque
 * chose : c'est la ligne qu'on cherche six mois plus tard. */
.journal-picto--detruit { background: var(--danger-fond); color: var(--danger); }
.journal-ligne .rangee__titre { font-size: .93rem; font-weight: 500; }
.journal-ligne .rangee__meta code { font-family: var(--mono); font-size: .8rem; }
.journal-quand {
  flex: none;
  color: var(--encre-3);
  font-size: .84rem;
  white-space: nowrap;
  /* Le pointillé dit qu'il y a une date exacte derrière, sans ajouter un mot. */
  border-bottom: 1px dotted var(--trait-fort);
  cursor: help;
}

/* ── Une répartition, en barres ───────────────────────────────────────────────
 *
 * Les pages les plus vues, les provenances, les sorties immédiates : trois
 * tableaux de nombres devenus trois fois le même dessin. Un nom, une valeur, une
 * barre sous les deux.
 *
 * La barre passe SOUS et non à côté : à côté, elle serait rognée par le nom d'une
 * page longue, et deux blocs côte à côte ne partageraient plus la même échelle.
 * En dessous, elle occupe toute la largeur de la carte, donc toutes les barres
 * d'un bloc se comparent sur la même règle.
 *
 * La plus longue vaut cent pour cent : on lit un RAPPORT entre pages, ce qu'une
 * colonne de nombres oblige à calculer de tête.
 */
.barre {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 4px 14px;
  padding: 11px 0;
  border-bottom: 1px solid var(--trait);
}
.barre:last-child { border-bottom: none; }
.barre-nom {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-size: .93rem;
  color: var(--encre);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.barre-nom code { font-family: var(--mono); font-size: .85rem; color: var(--encre-2); }
.barre-valeur {
  font-size: .93rem;
  font-weight: 600;
  color: var(--encre);
  white-space: nowrap;
}
.barre-valeur .note { font-weight: 400; }
.barre .jauge {
  display: block;
  grid-column: 1 / -1;
  margin: 3px 0 0;
  height: 4px;
}
/* Une part qu'on préférerait basse : le jaune de la charte plutôt que le marine.
 * Sur une barre, le jaune passe : ce n'est pas du texte, il n'a pas à tenir 4,5:1. */
.jauge--attention > i { background: var(--jaune); }
/* Une ligne isolée sous une liste de barres : pas de barre, donc pas de place
 * réservée pour elle. */
.barre--seule { border-top: 1px solid var(--trait); border-bottom: none; }
/* La bulle d'un « i » posé dans une barre s'ancre sous la ligne, comme partout. */
.barre .info-bulle { top: calc(100% - 4px); }

/* Un intertitre entre deux grilles d'indicateurs. Il classe, il ne s'annonce pas :
 * même poids qu'un titre de groupe du cadre de gauche. */
.titre-section {
  margin: 30px 0 12px;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--encre-3);
}
.titre-section:first-of-type,
.titre-section--premier { margin-top: 4px; }
/* Le compte d'une exception, collé à l'intertitre qui la porte : « 5 encore au
 * texte du réseau ». Il est en ambre parce que c'est une tâche, et il n'apparaît
 * que s'il y a quelque chose à compter. */
.titre-section-note {
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--att-fond);
  color: var(--attention);
  font-size: .72rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
}

/* Le dessin d'un objet dans une rangée. Sans fond ni cadre, contrairement à celui
 * d'une tête de carte : à seize lignes, seize pastilles colorées feraient une
 * colonne de confettis, alors qu'un tracé gris se lit comme une initiale. */
.rangee-picto {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 20px;
  color: var(--encre-3);
  /* Aligné sur la PREMIÈRE ligne et non sur le milieu de la rangée : sur une ligne
   * dont le détail fait trois lignes de texte, un dessin centré dérive au milieu du
   * paragraphe et ne désigne plus le titre. */
  align-self: flex-start;
  margin-top: 2px;
}
.rangee-picto .picto { width: 19px; height: 19px; }
/* Un nombre au bout d'une rangée. Largeur fixe et chiffres tabulaires, hérités du
 * corps : la colonne s'aligne, donc deux lignes se comparent sans être lues. */
.rangee-compte {
  flex: none;
  min-width: 34px;
  text-align: right;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--encre);
  letter-spacing: -.01em;
}
@media (hover: hover) and (pointer: fine) {
  a.rangee:hover .rangee-picto { color: var(--marine); }
}

/* La tête d'un indicateur : son nom, et son « i » s'il a quelque chose à dire.
 * `position: relative` porte la bulle, qui s'étire d'un bord à l'autre de la carte. */
.indicateur__tete {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
}
.indicateur__tete .indicateur__libelle { margin: 0; }
.indicateur__tete .info-bulle { top: calc(100% + 6px); }

/* Une évolution : le nombre porte la couleur, le reste de la phrase reste gris.
 * Une phrase entière en vert se lit comme une alerte, or une hausse de visites
 * n'appelle aucun geste. */
.delta { font-weight: 600; }
.delta--hausse { color: var(--ok); }
.delta--baisse { color: var(--attention); }
.delta--stable { color: var(--encre-2); }

.indicateur__lien {
  display: inline-block;
  margin-top: 10px;
  color: var(--marine);
  font-size: .87rem;
  font-weight: 500;
}

/* ── Les graphiques ──────────────────────────────────────────────────────────
 *
 * Écrits en SVG par includes/graphes.php. Ce qui suit ne fait que les poser dans
 * la page : les couleurs, elles, sont des attributs du dessin, parce que la
 * politique de sécurité du contenu interdit le style en ligne.
 */

/* ── L'anneau ──────────────────────────────────────────────────────────────── */
.anneau {
  display: block;
  width: 100%;
  max-width: 190px;
  height: auto;
  margin: 0 auto;
}
/* Le chiffre du centre. En unités du VIEWBOX et non en pixels : il grandit et
 * rétrécit avec l'anneau, donc il reste centré à toutes les tailles de carte. */
.anneau-valeur {
  font: 600 28px/1 var(--police);
  font-variant-numeric: tabular-nums;
  fill: var(--encre);
  letter-spacing: -1px;
}
.anneau-libelle { font: 400 11px/1 var(--police); fill: var(--encre-3); }
/* Un arc grossit au survol pour se désigner, sans bouger de place : le trait
 * s'épaissit vers l'intérieur ET l'extérieur, donc le cercle reste centré. */
@media (hover: hover) and (pointer: fine) {
  .anneau circle[stroke-dasharray] { transition: stroke-width var(--court) var(--sortie); }
  .anneau circle[stroke-dasharray]:hover { stroke-width: 22; cursor: help; }
}
@media (prefers-reduced-motion: reduce) {
  .anneau circle { transition: none; }
}

/* ── Les courbes ────────────────────────────────────────────────────────────
 *
 * Le dessin vient du serveur. Ce qui suit lui donne sa place, et pose la bulle de
 * survol que `assets/js/courbes.js` remplit.
 */
.carte--courbes { padding: 20px 22px 16px; }
.courbes-cadre { position: relative; margin: 4px 0 6px; }
.courbes {
  display: block;
  width: 100%;
  /* Le rapport du viewBox est conservé, donc la hauteur suit la largeur : les
   * dates et les valeurs de l'axe ne sont jamais comprimées. Le plancher évite
   * qu'une courbe devienne un trait de trente pixels sur un téléphone. */
  height: auto;
  min-height: 180px;
  overflow: visible;
}
.courbes-axe  { font: 400 11px/1 var(--police); fill: var(--encre-3); font-variant-numeric: tabular-nums; }
.courbes-date { font: 400 11px/1 var(--police); fill: var(--encre-3); }

/* Le fil vertical et les points d'un seau ne se voient qu'au survol de sa bande.
 * Six séries de trente points feraient cent quatre-vingts pastilles en permanence,
 * et la courbe disparaîtrait sous ses propres points. */
.courbes-fil, .courbes-point { opacity: 0; transition: opacity var(--court) var(--sortie); }
.courbes-seau--actif .courbes-fil,
.courbes-seau--actif .courbes-point { opacity: 1; }
.courbes-bande { cursor: crosshair; }
.courbes-bande:focus { outline: none; }
.courbes-bande:focus-visible + * { outline: 2px solid var(--marine); }

/* La bulle. Sa position arrive en propriétés personnalisées posées par le script :
 * la politique de sécurité du contenu interdit un attribut `style` dans le
 * document, et une propriété personnalisée écrite par CSSOM n'en est pas un. */
.courbes-bulle {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  min-width: 168px;
  padding: 10px 12px;
  border-radius: var(--rayon-s);
  background: var(--encre);
  color: var(--blanc);
  font-size: .84rem;
  box-shadow: var(--ombre-flot);
  pointer-events: none;
  transform: translate(calc(var(--x, 0px) - 50%), calc(var(--y, 0px) - 8px));
  transition: transform var(--court) var(--sortie);
}
.courbes-bulle[hidden] { display: none; }
.courbes-bulle-titre {
  margin: 0 0 7px;
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(255, 255, 255, .16);
  font-weight: 600;
}
.courbes-bulle-liste { list-style: none; margin: 0; padding: 0; }
.courbes-bulle-liste li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 2px 0;
}
.courbes-bulle-pastille {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 3px;
  background: var(--teinte, #FFF);
}
.courbes-bulle-nom {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: rgba(255, 255, 255, .82);
}
.courbes-bulle-valeur { flex: none; font-weight: 600; font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .courbes-bulle, .courbes-fil, .courbes-point { transition: none; }
}

/* ── Le sélecteur de période ────────────────────────────────────────────────
 *
 * Il commande TOUT l'écran, donc il est en haut, seul sur sa ligne, et il ne
 * ressemble à aucun autre réglage de la page.
 */
.periodes {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0 0 20px;
}
.periodes .info-bulle { top: calc(100% + 6px); max-width: 520px; }
/* Un segment qui est un LIEN et non un bouton radio : le choix est dans l'adresse,
 * donc il se met en favori et le retour du navigateur y revient. */
.segment--lien { text-decoration: none; }
.segment--actif span {
  background: var(--carte);
  color: var(--encre);
  box-shadow: 0 1px 2px rgba(20, 20, 43, .06);
}
.segment--lien:focus-visible span { outline: 2px solid var(--marine); outline-offset: 1px; }

.periode-plage { display: flex; align-items: center; gap: 8px; }
.periode-plage input[type=date] { width: auto; font-size: .86rem; padding: 6px 9px; }
.periode-a { color: var(--encre-3); }

/* La période affichée, rappelée dans la tête du graphique : sans elle, une courbe
 * mise en favori sur douze mois se lit comme une courbe de trente jours. */
.controle-periode {
  margin-left: auto;
  flex: none;
  color: var(--encre-3);
  font-size: .84rem;
}

@media (max-width: 620px) {
  .periodes { gap: 10px; }
  .periode-plage { width: 100%; }
  .periode-plage input[type=date] { flex: 1; min-width: 0; }
}

/* ── La légende d'un graphique ──────────────────────────────────────────────
 *
 * Elle porte le nom et le chiffre de chaque part. Ce n'est pas un ornement : un
 * graphique dont la couleur est le seul porteur d'information est illisible pour
 * une personne sur douze.
 */
.legende { list-style: none; margin: 0; padding: 0; }
/* La ligne est le conteneur, et l'éventuel lien qu'elle porte s'étale dedans :
 * une part de légende est tantôt cliquable (elle mène à son contrôle) tantôt non
 * (elle ne mène nulle part), et les deux doivent se dessiner pareil. */
.legende-ligne {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--trait);
}
.legende-ligne:last-child { border-bottom: none; }
.legende-ligne > a {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  margin: -8px -10px;
  padding: 8px 10px;
  border-radius: var(--rayon-xs);
  color: inherit;
  text-decoration: none;
}
.legende-pastille { flex: none; width: 11px; }
.legende-pastille svg { display: block; width: 11px; height: 11px; }
.legende-nom {
  flex: 1;
  min-width: 0;
  font-size: .89rem;
  color: var(--encre);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.legende-valeur {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: .93rem;
  font-weight: 600;
  color: var(--encre);
}
/* Le pourcentage est plus petit et gris : c'est le nombre brut qu'on lit d'abord,
 * la part sert à le situer. Largeur fixe, sinon la colonne danse d'une ligne à
 * l'autre entre « 4 % » et « 43 % ». */
.legende-part {
  min-width: 38px;
  text-align: right;
  font-size: .82rem;
  font-weight: 400;
  color: var(--encre-3);
}
@media (hover: hover) and (pointer: fine) {
  .legende-ligne > a:hover { background: var(--survol); }
  .legende-ligne > a:hover .legende-nom { color: var(--marine); }
}

/* La tête de l'écran de santé : l'anneau à gauche, sa légende à droite. Sous
 * 760 px ils s'empilent, l'anneau d'abord : c'est lui qu'on est venu voir. */
.sante-tete {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: 14px;
  margin: 0 0 24px;
}
.sante-anneau { display: grid; place-items: center; padding: 22px; }
.sante-legende { display: flex; flex-direction: column; padding: 18px 20px; }
/* La ligne de contexte est poussée en bas et détachée : c'est une mention, pas une
 * part du graphique. */
.sante-quand {
  margin: auto 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--trait);
  color: var(--encre-3);
  font-size: .84rem;
}
@media (max-width: 760px) {
  .sante-tete { grid-template-columns: 1fr; }
}

/* ── Un contrôle de la santé ─────────────────────────────────────────────────
 *
 * Une carte dont la tête porte quatre choses et pas une de plus : le dessin du
 * contrôle, son nom, son « pourquoi » derrière un « i », et le nombre de points.
 * Le paragraphe d'explication qui suivait chaque titre faisait à lui seul quatre
 * lignes par contrôle, douze fois par écran.
 */
.controle + .controle { margin-top: 14px; }
.controle-tete {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
.controle-tete h2 { margin: 0; font-size: 1rem; }
.controle-picto {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: var(--rayon-xs);
}
.controle-picto .picto { width: 18px; height: 18px; }
.controle-picto--info      { background: var(--info-fond); color: var(--bleu); }
.controle-picto--attention { background: var(--att-fond);  color: var(--attention); }
/* Le nombre est poussé à droite, en gris : c'est une quantité, pas une alerte.
 * L'alerte, s'il y en a une, est déjà dans la couleur du pictogramme. */
.controle-compte {
  margin-left: auto;
  flex: none;
  min-width: 22px;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--survol);
  color: var(--encre-2);
  font-size: .82rem;
  font-weight: 600;
  text-align: center;
}
/* La bulle du « i » d'une tête de carte s'ancre sous la LIGNE entière, comme
 * celle d'un champ : ancrée à l'icône, elle sortirait de la carte. */
.controle-tete .info-bulle { top: calc(100% + 6px); }

.rangee--note { color: var(--encre-3); font-size: .86rem; }
/* Une rangée qui ne mène nulle part et ne demande rien : pas de chevron, pas de
 * survol, et le nom en gris. Elle est là pour être comptée, pas cliquée. */
.rangee--calme { padding: 9px 0; }
.rangee--calme .rangee__titre { font-weight: 400; color: var(--encre-2); font-size: .92rem; }
.controle-coche {
  flex: none;
  width: 16px;
  height: 16px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%231F6B6B' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5 6.5 11.5 12.5 4.5'/%3E%3C/svg%3E") no-repeat center / 16px;
}
/* Un repli qui contient une liste et non un formulaire : il prend la largeur de
 * l'écran et non celle d'une colonne de panneau. */
.repli--large { margin-top: 18px; }
.repli--large .repli-corps { padding: 2px 14px 6px; }

/* Un indicateur dont la valeur est un mot et non un nombre : « il y a 3 jours »
 * à 2,1 rem déborde de sa carte. */
.indicateur__valeur--texte { font-size: 1.35rem; letter-spacing: -.01em; }
/* Le dénominateur d'un rapport : plus petit et gris, sinon « 11/12 » se lit comme
 * un seul nombre de quatre chiffres. */
.indicateur__sur { font-size: 1.1rem; font-weight: 500; color: var(--encre-3); }

/* ── La planche-contact des médias ────────────────────────────────────────────
 *
 * Une bibliothèque d'images se REGARDE. L'ancien écran empilait une carte par
 * fichier avec son formulaire, ses trois recadrages et leur légende d'usage :
 * douze fichiers faisaient 2 569 mots, et retrouver une photo demandait de lire
 * douze fois la même chose.
 *
 * Trois points de construction :
 *  · le rectangle est le même pour tous, image ou document, et il est tenu par
 *    `aspect-ratio` plutôt que par une hauteur : une grille dont une case se
 *    déforme au premier PDF n'est plus une grille ;
 *  · `auto-fill` avec un minimum de 168 px, donc six colonnes sur un grand écran
 *    et deux sur un téléphone, sans point de rupture écrit ;
 *  · rien n'est écrit sous une image sauf son nom de fichier, et la seule couleur
 *    de la planche est la pastille de ce qui manque.
 */
.planche {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.planche-note {
  margin: 0 0 20px;
  padding: 9px 12px 9px 30px;
  position: relative;
  border-radius: var(--rayon-s);
  background: var(--att-fond);
  color: var(--attention);
  font-size: .89rem;
}
/* Le point plutôt qu'un pictogramme d'alerte : c'est une remarque, pas une panne. */
.planche-note::before {
  content: "";
  position: absolute;
  left: 12px;
  top: 15px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--jaune);
}

.media-case {
  display: flex;
  flex-direction: column;
  gap: 7px;
  text-decoration: none;
  color: inherit;
  border-radius: var(--rayon-s);
}
.media-cadre {
  position: relative;
  display: block;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border: 1px solid var(--trait);
  border-radius: var(--rayon-s);
  background: var(--carte);
  transition: border-color var(--court) var(--sortie),
              box-shadow var(--court) var(--sortie);
}
.media-image {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
/* Un document n'a pas d'image : il prend le même rectangle et le pictogramme de sa
 * nature, centré. Sans ce repli, la grille perdrait une case sur deux. */
.media-doc {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background: var(--info-fond);
  color: var(--bleu);
}
.media-doc .picto { width: 34px; height: 34px; }
.media-doc--grand { aspect-ratio: 3 / 2; border-radius: var(--rayon-s); }
.media-doc--grand .picto { width: 56px; height: 56px; }

/* Ce qui manque, et rien d'autre. Le libellé est dans le `title` et dans
 * l'`aria-label` : une pastille de huit pixels ne porte pas de mot. */
.media-manque {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--jaune);
  box-shadow: 0 0 0 2px var(--blanc);
}
.media-nom {
  display: block;
  font-size: .82rem;
  color: var(--encre-3);
  /* Un nom de fichier long est coupé et non replié : deux lignes sous une vignette
   * décalent toute la rangée suivante. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (hover: hover) and (pointer: fine) {
  .media-case:hover .media-cadre {
    border-color: var(--trait-fort);
    box-shadow: var(--ombre);
  }
  .media-case:hover .media-nom { color: var(--encre); }
}
.media-case:focus-visible { outline: 2px solid var(--marine); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .media-cadre { transition: none; }
}

/* ── La fiche d'un fichier ──────────────────────────────────────────────────
 *
 * L'image à gauche, ce qu'on peut en dire à droite. Sous 900 px la colonne de
 * droite passe dessous : côte à côte, à cette largeur, aucune des deux ne tient.
 */
.media-fiche {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}
.media-fiche-vue { position: sticky; top: 28px; }
.media-grande {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  background: var(--carte);
}
.media-meta { margin: 10px 0 0; color: var(--encre-3); font-size: .86rem; }
.media-fiche-cote > .encart { margin-top: 0; }
.media-ancrage {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}
.media-ancrage label { font-size: .89rem; font-weight: 500; flex: none; }
.media-ancrage select { width: auto; flex: 1; min-width: 180px; }
/* La suppression est séparée par un filet et repoussée : elle ne se trouve pas
 * sous le doigt de quelqu'un qui vient d'enregistrer une légende. */
.media-detruire {
  margin-top: 28px;
  padding-top: 18px;
  border-top: 1px solid var(--trait);
}

@media (max-width: 900px) {
  .media-fiche { grid-template-columns: 1fr; gap: 20px; }
  .media-fiche-vue { position: static; }
}

/* -- Galerie des habillages ----------------------------------------------- */
/*
 * Chaque vignette est un CADRE, et dedans la vraie page d'accueil du site rendue
 * dans l'habillage candidat. Pas une capture d'écran : une capture montrerait le
 * site de quelqu'un d'autre, et l'équipe découvrirait le sien après la bascule.
 *
 * L'échelle est une variable et la largeur du cadre en découle, au lieu d'être
 * réglée à la main des deux côtés. Sans ce lien, la vignette et son contenu
 * divergent au premier ajustement, et il reste une bande blanche que personne ne
 * comprend.
 *
 * Le rendu se fait à 1320 px de large, la mesure d'un écran d'ordinateur : c'est la
 * composition qu'on veut juger. Rendre le cadre à sa taille réelle donnerait la
 * version téléphone du site, qui ne dit rien du choix qu'on est en train de faire.
 */
.modeles {
  --vignette-large: 1320px;
  --vignette-haute: 1080px;
  /* 0,26 et pas 0,3 : à 0,3 deux vignettes seulement tenaient par rangée, donc
     cinq habillages sur trois rangées, et comparer le premier au cinquième
     demandait de défiler. À 0,26 il en tient trois, et les cinq se voient sur
     deux rangées. Mesuré sur un écran de 1600 px, avec les cinq en face de soi. */
  --vignette-echelle: .26;
  display: grid;
  grid-template-columns: repeat(auto-fill, calc(var(--vignette-large) * var(--vignette-echelle)));
  justify-content: center;
  gap: 28px;
  margin-top: 8px;
}

.modele-carte {
  display: flex;
  flex-direction: column;
  background: var(--carte);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  overflow: hidden;
}
/* L'habillage en service se signale par un filet, pas par un aplat : un aplat de
 * couleur derrière une vignette fausserait la couleur de la vignette elle-même. */
.modele-carte--actif { border-color: var(--marine); box-shadow: 0 0 0 1px var(--marine), var(--ombre); }

.modele-vignette {
  position: relative;
  width: calc(var(--vignette-large) * var(--vignette-echelle));
  height: calc(var(--vignette-haute) * var(--vignette-echelle));
  overflow: hidden;
  background: var(--survol);
  border-bottom: 1px solid var(--trait);
}
.modele-vignette iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--vignette-large);
  height: var(--vignette-haute);
  border: 0;
  transform: scale(var(--vignette-echelle));
  transform-origin: 0 0;
  /* Le cadre ne prend pas les clics : c'est le lien posé par-dessus qui les
   * reçoit, et une vignette n'est pas un endroit où naviguer. */
  pointer-events: none;
}

/* Le lien couvre toute la vignette. Son libellé n'apparaît qu'au survol, mais il
 * reste dans l'arbre d'accessibilité : un lecteur d'écran l'annonce toujours. */
.modele-vignette-lien {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(20, 20, 43, 0);
  transition: background var(--court) var(--sortie);
}
.modele-vignette-lien span {
  padding: 8px 14px;
  border-radius: var(--rayon-xs);
  background: var(--blanc);
  color: var(--marine);
  font-size: .87rem;
  font-weight: 600;
  opacity: 0;
  transition: opacity var(--court) var(--sortie);
}
.modele-vignette-lien:hover,
.modele-vignette-lien:focus-visible { background: rgba(20, 20, 43, .32); }
.modele-vignette-lien:hover span,
.modele-vignette-lien:focus-visible span { opacity: 1; }

.modele-corps { display: flex; flex-direction: column; gap: 8px; padding: 18px 20px 20px; flex: 1; }
.modele-corps h2 { margin: 0; font-size: 1.15rem; }
.modele-registre {
  margin: 0;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--encre-3);
}
.modele-pour { margin: 0; font-size: .87rem; color: var(--encre-2); }
.modele-pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: auto;
  padding-top: 12px;
}

/* L'aperçu de l'écran de confirmation : presque grandeur réelle, parce qu'on y
 * décide. Une vignette suffit à comparer six habillages ; elle ne suffit pas à en
 * choisir un. */
.modele-plein {
  --vignette-large: 1320px;
  --vignette-haute: 1300px;
  --vignette-echelle: .7;
  position: relative;
  width: 100%;
  max-width: calc(var(--vignette-large) * var(--vignette-echelle));
  height: calc(var(--vignette-haute) * var(--vignette-echelle));
  margin: 12px 0;
  overflow: hidden;
  border: 1px solid var(--trait);
  border-radius: var(--rayon-s);
  background: var(--survol);
}
.modele-plein iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--vignette-large);
  height: var(--vignette-haute);
  border: 0;
  transform: scale(var(--vignette-echelle));
  transform-origin: 0 0;
}

.liste-points { margin: 12px 0 0; padding-left: 20px; }
.liste-points li { margin-bottom: 8px; font-size: .93rem; }
.liste-points li:last-child { margin-bottom: 0; }

/* Sous 1000 px, deux vignettes ne tiennent plus côte à côte : l'échelle descend
 * plutôt que la grille ne déborde. */
@media (max-width: 1000px) {
  .modeles { --vignette-echelle: .24; }
  .modele-plein { --vignette-echelle: .5; }
}
@media (max-width: 620px) {
  .modeles { --vignette-echelle: .2; }
  .modele-plein { --vignette-echelle: .3; }
}

/* -- Mouvement réduit ----------------------------------------------------- */
/*
 * « Mouvement réduit » ne veut pas dire « aucun retour ». Les changements de
 * couleur restent, parce qu'ils portent de l'information ; les déplacements et
 * les mises à l'échelle partent, parce qu'ils ne portent que du plaisir.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
  }
  .bouton:active { transform: none; }
}

/* ─── L'éditeur de la page de vente ────────────────────────────────────────── */
/* Vingt sections repliées, cent onze textes et dix-huit listes. Tout tient dans
   un seul formulaire : un bouton par section aurait permis de corriger un titre
   ici, un autre là, puis de perdre le premier en enregistrant le second. */
.vitrine-section {
  border: 1px solid var(--line, #E4E7EE);
  border-radius: 12px;
  margin-bottom: 10px;
  background: #fff;
}
.vitrine-section[open] { box-shadow: 0 1px 2px rgba(19,15,45,.05); }
.vitrine-section > summary {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 18px; cursor: pointer;
  font-weight: 650; font-size: .95rem;
  list-style: none;
}
.vitrine-section > summary::-webkit-details-marker { display: none; }
/* Le chevron est dessiné plutôt qu'écrit : le triangle natif ne se colore pas
   et ne s'aligne pas de la même façon d'un navigateur à l'autre. */
.vitrine-section > summary::before {
  content: ''; flex: 0 0 auto;
  width: 8px; height: 8px; margin-right: 2px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(-45deg); transition: transform .18s;
}
.vitrine-section[open] > summary::before { transform: rotate(45deg); }
.vitrine-section > summary:hover { background: #F5F7FA; border-radius: 12px; }
.vitrine-section__nom { flex: 1; }
.vitrine-section__jeton {
  font-size: .74rem; font-weight: 700; letter-spacing: .02em;
  padding: 3px 9px; border-radius: 999px;
  background: #EDF6FB; color: #007CB0;
}
.vitrine-section__corps { padding: 4px 18px 20px; border-top: 1px solid #F0F2F6; }
.vitrine-section__corps .champ:last-of-type { margin-bottom: 8px; }

/* Les listes : un tableau, une ligne par entrée. Le cadre défile de côté quand
   une liste a six colonnes, et la page ne défile jamais avec lui. */
.vitrine-liste {
  border: 1px solid #E4E7EE; border-radius: 10px;
  padding: 14px 16px 10px; margin: 18px 0;
}
.vitrine-liste > legend {
  font-size: .82rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: #6B7285;
  padding-inline: 6px;
}
.vitrine-liste__cadre { overflow-x: auto; margin-top: 10px; }
.vitrine-liste__table { width: 100%; border-collapse: collapse; font-size: .86rem; }
.vitrine-liste__table th {
  text-align: left; font-size: .74rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: #6B7285;
  padding: 4px 6px 8px; white-space: nowrap;
}
.vitrine-liste__table td { padding: 3px 6px; vertical-align: top; }
.vitrine-liste__table input { width: 100%; min-width: 150px; }
.vitrine-liste__rang { width: 28px; color: #9AA0AE; font-size: .78rem; padding-top: 12px; }

/* Le bouton d'enregistrement reste atteignable après deux cents champs : il est
   collé en bas de la fenêtre, pas au bout du formulaire. */
.vitrine-editeur__pied {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 20px; padding: 14px 18px;
  background: rgba(255,255,255,.94);
  backdrop-filter: blur(8px);
  border: 1px solid #E4E7EE; border-radius: 12px;
  box-shadow: 0 -2px 16px rgba(19,15,45,.06);
}
.vitrine-editeur__pied .aide { margin: 0; }

.pour-lecteur-ecran {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
