/* -------------------------------------------------------------------------
   Extranet Evidra — feuille de style

   Charte MESURÉE sur le logo retenu (losange), pas estimée à l'œil :
     bleu de marque  #252C47  — contraste 13,73:1 sur blanc, niveau AAA

   Le contraste étant très élevé, cette teinte peut porter du texte sans être
   doublée d'une variante plus sombre.

   Pas de dépendance, pas de police distante : tout ce qui vient d'un serveur
   tiers est un point de panne et une fuite d'information sur les visiteurs.
   ---------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   TYPOGRAPHIE — Inter, hébergée localement
   ---------------------------------------------------------------------------
   Le site tournait en `system-ui` — un choix honnête, pas une absence : j'avais
   d'abord annoncé « aucune police déclarée », c'était faux, elle l'était par le
   raccourci `font:` que ma recherche sur `font-family` ne voyait pas.

   ⭐ CE QUE `system-ui` COÛTE RÉELLEMENT : la police change avec la MACHINE —
   Segoe UI sur Windows, San Francisco sur Mac, Roboto sur Android. Les
   chasses diffèrent, donc les largeurs de colonnes et les retours à la ligne
   aussi : un tableau réglé au pixel ici déborde ailleurs. Une police servie
   par le site rend le rendu identique partout, et c'est ce qui distingue une
   interface de produit d'une page qui s'habille chez l'hôte.

   ⭐ HÉBERGÉE, comme Leaflet l'est déjà. Un lien vers Google Fonts ou rsms.me
      dirait à un tiers qui consulte l'extranet et quand — et le harnais
      l'interdit à juste titre.

   ⚠️ L'ITALIQUE N'EST PAS TÉLÉCHARGÉE : 388 Ko pour UN seul usage dans tout le
      projet (.acces-hors). L'oblique synthétisée par le navigateur suffit
      largement à ce cas.

   ⚠️ `font-display: swap` : le texte s'affiche immédiatement dans la police de
      secours puis bascule. Sans lui, un extranet consulté sur un réseau de
      salon reste muet le temps du chargement. */
@font-face {
  font-family: 'InterVariable';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/inter/InterVariable.woff2') format('woff2');
}

:root {
  --marque:        #252c47;
  --marque-clair:  #3d4870;
  --titre:         #171c2e;

  /* ⭐⭐⭐ L'ENCRE DE MARQUE, SÉPARÉE DU FOND DE MARQUE — et c'est ce qui rend
     le mode sombre possible. `--marque` servait à DEUX choses opposées : le
     FOND de l'en-tête, des boutons pleins et de la jauge (5 usages), et
     l'ENCRE des liens, des onglets actifs et des contours de focus (24
     usages). Sur fond clair, une seule couleur suffisait aux deux.

     En sombre, elles partent en sens contraire : le fond doit rester sombre,
     l'encre doit s'éclaircir pour rester lisible. Redéfinir `--marque` aurait
     donc éclairci les fonds de boutons — et leur texte blanc serait devenu
     illisible sur eux. Deux rôles dans un seul jeton, c'est une collision, et
     elle ne se voit qu'au moment où les deux doivent diverger.

     ⚠️ En clair, --accent VAUT --marque : aucun pixel ne change. */
  --accent:        var(--marque);

  --texte:         #1b1d21;
  --texte-doux:    #5a6068;
  --bord:          #dcdfe4;
  --fond:          #ffffff;
  --fond-doux:     #f7f8fa;

  --succes:        #0a7c2f;
  --succes-fond:   #e8f6ec;
  --succes-texte:  #0a5c24;
  --erreur:        #a11122;
  --erreur-fond:   #fdeaea;
  --erreur-texte:  #7d0d1a;
  --info:          #33506e;
  --info-fond:     #eef2f7;
  --info-texte:    #26405a;
  --alerte:        #8a5a00;
  --alerte-fond:   #fdf3e0;
  --alerte-texte:  #6b4600;

  /* ⭐⭐⭐ LA BORDURE D'UN CHAMP DE SAISIE N'EST PAS UNE BORDURE DÉCORATIVE.
     C'est le SEUL indicateur qui dit où l'on peut écrire — donc un composant
     d'interface au sens de WCAG 2.2 § 1.4.11, qui exige 3:1.

     --bord vaut #dcdfe4, soit 1,34:1 sur blanc — recalculé le 10/08, luminances
     relatives 0,7359 contre 1 : (1 + 0,05) / (0,7359 + 0,05) = 1,34. Très
     au-dessous du seuil. Sur un écran de portable un peu lavé, ou en plein
     jour dans un hall d'exposition, les champs disparaissent purement et
     simplement.

     ⚠️ UN JETON À PART, ET SURTOUT PAS --bord ASSOMBRI : celui-ci sert à une
        vingtaine de séparateurs, cadres et filets de tableaux, qui sont
        décoratifs et que 1.4.11 ne vise pas. Les assombrir tous alourdirait
        l'écran sans rien corriger d'obligatoire.

     #8c9096 = 3,21:1 (vérifié par le même calcul), gris légèrement bleuté pour
     rester dans la famille du #252c47. */
  --bord-champ:    #8c9096;

  /* Fond des pastilles neutres (clos, archive, retire) : etait ecrit en dur,
     donc reste clair en mode sombre — texte clair sur fond clair, 1,91:1. */
  /* ⚠️ LA HAUTEUR DE L'EN-TÊTE, mesurée (77 px). Elle sert à caler l'en-tête
     de tableau collant JUSTE SOUS la barre du site, qui est elle-même
     collante : sans cette valeur, les intitulés de colonnes se glisseraient
     dessous et disparaîtraient. ⚠️ Si le rembourrage de `.entete` change, cette
     valeur doit suivre — c'est le prix d'une hauteur que le CSS ne sait pas
     mesurer tout seul. */
  --hauteur-entete: 4.8rem;
  --neutre-fond:   #eceef1;
  --bouton-fond:        var(--marque);
  --bouton-fond-survol: var(--marque-clair);
  --bouton-encre:       #fff;
  --rayon:         8px;
  /* ⭐ REMAPPÉ SUR OPEN PROPS plutôt que réglé au jugé. Les ombres d'Open Props
     sont calibrées par palier (élévation croissante) et teintées d'un gris
     neutre : deux ombres du site ne peuvent plus diverger parce que l'une a
     été retouchée un soir. Le repli garde la valeur d'origine, pour le cas où
     la feuille de jetons ne serait pas déposée. */
  --ombre:         var(--shadow-2, 0 1px 3px rgba(16, 41, 74, .08), 0 1px 2px rgba(16, 41, 74, .04));
  --ombre-relief:  var(--shadow-3, 0 6px 16px rgba(16, 41, 74, .12));
}

/* ---------------------------------------------------------------------------
   MODE SOMBRE
   ---------------------------------------------------------------------------
   ⭐⭐⭐ TOUT PASSE PAR LES JETONS, PAS UNE RÈGLE N'EST RÉÉCRITE. Le thème ne
   redéfinit que des variables : les ~3 400 lignes de mise en forme ne
   connaissent pas son existence. Un mode sombre qui duplique des règles finit
   par diverger de son jumeau clair — on corrige un écran, on oublie l'autre.

   ⚠️ DEUX DÉCLENCHEURS, ET ILS NE SE VALENT PAS :
      · `prefers-color-scheme` suit le réglage du système — c'est le défaut, et
        pour la plupart des gens ce sera le seul.
      · `[data-theme]` est posé sur <html> par le choix explicite de la
        personne, et il DOIT l'emporter : quelqu'un qui bascule à la main dit
        quelque chose de plus précis que son système d'exploitation.

   ⚠️ La CARTE reste claire dans les deux thèmes : les tuiles viennent de l'IGN
      et ne sont pas les nôtres. Les teinter par un filtre CSS fausserait des
      couleurs qui portent du sens (l'eau, les forêts, les routes) — on ne
      maquille pas une donnée cartographique pour l'assortir à une interface. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) { color-scheme: dark; }
}
:root[data-theme="sombre"] { color-scheme: dark; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) {
    --texte:         #e7eaf3;
    --texte-doux:    #a6aec2;
    --bord:          #2c3346;
    --bord-champ:    #79839b;
    --fond:          #171b28;
    --fond-doux:     #0f121c;

    --marque:        #1e2437;
    --marque-clair:  #2c3450;
    --titre:         #f1f4fb;
    --accent:        #a9bcff;

    --succes:        #3fbf6a;  --succes-fond: #122a1c;  --succes-texte: #9fe5b6;
    --erreur:        #ef6b7a;  --erreur-fond: #2e1519;  --erreur-texte: #f7b4bb;
    --info:          #8fb0e0;  --info-fond:   #16203a;  --info-texte:   #c3d6f5;
    --alerte:        #d9a441;  --alerte-fond: #2c2210;  --alerte-texte: #f0d49a;
    --neutre-fond:   #262c3d;
    --bouton-fond:        #a9bcff;
    --bouton-fond-survol: #c3d0ff;
    --bouton-encre:       #141a2e;
  }
}
/* Le choix explicite, qui l'emporte sur le système — mêmes valeurs, une seule
   source : elles sont recopiées ici parce que CSS ne permet pas de partager un
   bloc de déclarations entre deux sélecteurs. ⚠️ Toute retouche va DANS LES
   DEUX. */
:root[data-theme="sombre"] {
  --texte:         #e7eaf3;
  --texte-doux:    #a6aec2;
  --bord:          #2c3346;
  --bord-champ:    #79839b;
  --fond:          #171b28;
  --fond-doux:     #0f121c;

  --marque:        #1e2437;
  --marque-clair:  #2c3450;
  --titre:         #f1f4fb;
  --accent:        #a9bcff;

  --succes:        #3fbf6a;  --succes-fond: #122a1c;  --succes-texte: #9fe5b6;
  --erreur:        #ef6b7a;  --erreur-fond: #2e1519;  --erreur-texte: #f7b4bb;
  --info:          #8fb0e0;  --info-fond:   #16203a;  --info-texte:   #c3d6f5;
  --alerte:        #d9a441;  --alerte-fond: #2c2210;  --alerte-texte: #f0d49a;
  --neutre-fond:   #262c3d;
  --bouton-fond:        #a9bcff;
  --bouton-fond-survol: #c3d0ff;
  --bouton-encre:       #141a2e;
}

* { box-sizing: border-box; }

/* ⚠️ INDISPENSABLE, et pas une précaution de style. L'attribut « hidden » ne
   pose qu'un « display: none » de feuille par défaut : la MOINDRE règle qui
   donne un display à l'élément l'emporte, et l'élément reste affiché. C'est
   arrivé le 07/08 sur les boutons radio de la fiche d'un lieu — le groupe
   « avant » était masqué, mais sa règle « display: flex » gagnait, et les
   boutons apparaissaient deux fois. */
[hidden] { display: none !important; }

body {
  margin: 0;
  /* ⚠️ La pile de secours reste EN ENTIER derrière Inter : si la police ne se
     charge pas — réseau de salon, cache vidé, fichier absent du dépôt — on
     retombe exactement sur le rendu d'avant, jamais sur du Times. */
  font: 16px/1.55 'InterVariable', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Ligatures et formes contextuelles d'Inter, plus la taille optique : c'est
     ce qui rend les chiffres et les majuscules d'interface nets à petite
     taille (recommandation de l'auteur de la police). */
  font-feature-settings: 'liga' 1, 'calt' 1;
  font-variation-settings: 'opsz' 20;
  -webkit-font-smoothing: antialiased;
  color: var(--texte);
  background: var(--fond-doux);

  /* Le pied de page doit rester EN BAS de la fenêtre même quand la page est
     peu remplie. Sans cela, il se cale sous le contenu et laisse une grande
     zone vide en dessous — ce qui donne l'impression d'une page tronquée.
     Constaté sur le rendu réel du 07/08 : aucun contrôle HTML ne voit ça. */
  min-height: 100vh;
  /* ⚠️ « dvh » suit la barre d'adresse mobile, qui se rétracte au défilement :
     avec « vh » seul, la page fait toujours la hauteur BARRE DÉPLOYÉE, et l'on
     hérite d'un défilement de quelques dizaines de pixels sur une page qui tient
     pourtant dans l'écran. Écrit APRÈS « vh », qui reste le recours des
     navigateurs anciens. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* ⚠⚠ LES ANCRES DOIVENT S'ARRÊTER SOUS L'EN-TÊTE.
   Avec une barre fixe, un lien vers « #donnees » place le titre visé exactement
   sous elle : il disparaît, et l'on croit que le lien n'a rien fait. C'est le
   sommaire des mentions légales qui en dépend, et tout futur lien interne.
   La valeur couvre la hauteur de l'en-tête plus une respiration. */
/* ---------------------------------------------------------------------------
   TRANSITIONS DE NAVIGATION
   ---------------------------------------------------------------------------
   ⭐⭐⭐ CE SITE EST UNE APPLICATION MULTIPAGE : chaque clic recharge vraiment la
   page. Le navigateur efface donc l'écran avant de peindre le suivant — c'est
   le blanc qui sépare deux écrans, et c'est ce qui fait qu'une application PHP
   « fait site » là où une application JavaScript « fait logiciel ».

   `@view-transition` supprime cette coupure SANS changer un octet
   d'architecture : le navigateur garde l'image de la page quittée, peint la
   nouvelle par-dessus et fond l'une dans l'autre. Aucun JavaScript, aucune
   dépendance, aucun rendu déplacé côté client — cf. [affichage écrit deux
   fois], le piège qu'on ne veut surtout pas rouvrir.

   ⚠️ CE N'EST PAS UNE ANIMATION DÉCORATIVE, C'EST UN REPÈRE : l'en-tête et le
      pied sont NOMMÉS ci-dessous, donc le navigateur les reconnaît d'une page
      à l'autre et ne les refait pas apparaître. Ils restent immobiles pendant
      que le contenu change — exactement ce qu'on vient de gagner en découplant
      l'ossature. Sans ces noms, toute la page fondrait d'un bloc et la barre
      clignoterait à chaque clic.

   ⚠️ Un navigateur qui ne connaît pas la règle l'ignore et l'on retrouve le
      comportement actuel, à l'identique. Rien à détecter, rien à charger. */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: evidra-sortie .16s ease-in both; }
  ::view-transition-new(root) { animation: evidra-entree .22s ease-out both; }
}
/* ⚠️ ET SI LE MOUVEMENT EST REFUSÉ, ON NE LAISSE PAS L'ANIMATION PAR DÉFAUT
   s'appliquer : on la coupe. `navigation: auto` reste, mais la bascule devient
   instantanée — la coupure blanche disparaît quand même, sans rien qui bouge. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

@keyframes evidra-sortie { to { opacity: 0; } }
@keyframes evidra-entree {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

html {
  scroll-padding-top: 5.5rem;
  /* ⭐ LA PLACE DE LA BARRE DE DÉFILEMENT EST RÉSERVÉE EN PERMANENCE.
     Mesuré le 13/08 : une page qui défile réduit la fenêtre de 15 px, et tout
     ce qui est aligné à droite — le menu — se décalait d'autant selon que la
     page tenait ou non à l'écran. Le gros du saut venait du régime de largeur
     (409 px, corrigé), ce reliquat-ci vient du navigateur lui-même et frappe
     n'importe quel site. */
  scrollbar-gutter: stable;
}

a { color: var(--accent); }
a:hover { color: var(--marque-clair); }

/* --- Ossature ---------------------------------------------------------- */

/* --- Les barres fixes ---------------------------------------------------- *
   ⚠️ « sticky » ET NON « fixed », et ce n'est pas un synonyme.

      Un élément « fixed » sort du flux : il faudrait alors compenser sa hauteur
      par une marge sur le contenu, en la maintenant à la main à chaque
      changement de l'en-tête — et le jour où le menu passe sur deux lignes, le
      haut de la page se retrouve caché dessous. « sticky » reste dans le flux :
      il occupe sa place, puis se colle au bord quand on défile. Rien à
      compenser, rien à maintenir.

   ⚠️ Le corps de page est en flex-column : « sticky » y fonctionne, mais il
      cesserait net si un ancêtre recevait « overflow: hidden ». À ne pas
      ajouter sur body ou main sans vérifier ces deux barres.
   ------------------------------------------------------------------------ */
.entete {
  /* Nommé pour la transition de page : le navigateur reconnaît cette barre
     d'un écran à l'autre et ne la refait pas apparaître. Elle reste immobile
     pendant que le contenu se remplace. */
  view-transition-name: evidra-entete;
  background: var(--marque);
  color: #fff;
  padding: .85rem 0;

  position: sticky;
  top: 0;
  /* Au-dessus du contenu et des tuiles, mais SOUS les panneaux : ceux-ci sont
     des <dialog> modaux, qui vivent dans la couche supérieure du navigateur et
     qu'aucun z-index ne peut de toute façon dépasser. */
  z-index: 40;
}
/* Deux régimes de largeur — voir la justification dans app/gabarits/base.php.
   En résumé : le TEXTE se lit mal au-delà d'environ 90 caractères par ligne,
   les TABLEAUX profitent au contraire de toute la place disponible. */
:root {
  --largeur-page: 68rem;
  /* ⚠️ DÉLIBÉRÉMENT HORS DE PORTÉE DE .page-large : c'est ce qui rend
     l'ossature immobile. Elle vaut le plafond du régime large, pour que la
     barre reste alignée sur le contenu des écrans de travail — ceux où l'on
     passe le plus de temps. */
  --largeur-ossature: 120rem;
}
.page-large {
  --largeur-page: 120rem;
}

/* ⭐⭐⭐ L'OSSATURE NE SUIT PAS LE CONTENU — corrigé le 13/08, signalé par
   l'auteur : « les menus du header ne sont jamais à la même place selon les
   pages ».

   Les trois conteneurs partageaient `max-width: var(--largeur-page)`. Comme
   cette variable change de valeur d'un écran à l'autre (68 rem en lecture,
   120 rem au travail), l'en-tête et le pied se redimensionnaient AVEC le
   contenu. Mesuré à 1920 px : le logo sautait de 409 px, le menu de 423 px,
   le pied de 409 px — à chaque navigation.

   ⚠️ ET LE SAUT TOMBAIT SUR LE PARCOURS LE PLUS FRÉQUENT : les deux entrées
      du menu sont dans deux régimes opposés — « Accueil » est un écran de
      travail, « Mes dossiers » une page de lecture. Un aller-retour entre
      elles déplaçait toute la barre.

   ⇒ Une barre de navigation est un repère : sa géométrie ne doit dépendre que
     de la FENÊTRE, jamais de la page affichée. Seul `.principal` garde les
     deux régimes — c'est le contenu qu'ils concernent, et eux seuls. */
.entete-interieur,
.pied-interieur {
  max-width: var(--largeur-ossature);
  margin: 0 auto;
  padding: 0 1.25rem;

  /* ⭐ CENTRÉ ET SUR UNE SEULE LIGNE — demandé par l'auteur le 13/08.
     Les deux mentions se suivent au centre tant qu'elles tiennent, et
     repassent l'une sous l'autre quand la place manque : c'est le même
     réglage qui sert les deux cas, sans seuil à choisir ni media query à
     tenir d'accord avec le reste. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .2rem 1.4rem;
  text-align: center;
}
.pied-interieur p { margin: 0; }

.principal {
  /* La largeur suit la fenêtre et s'arrête au plafond : sur un écran étroit
     la page occupe tout, sur un écran large elle se centre. */
  max-width: var(--largeur-page);
  margin: 0 auto;
  padding: 0 1.25rem;
}
.entete-interieur {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

/* Nom composé en CSS plutôt qu'en image : net à toutes les tailles, lisible
   par les lecteurs d'écran, et modifiable sans repasser par un graphiste. */
.marque {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  color: #fff;
  text-decoration: none;
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: .16em;
}
.marque img { height: 30px; width: auto; display: block; }
.marque:hover { color: #fff; }
.marque-sous {
  display: block;
  font-size: .7rem;
  letter-spacing: .02em;
  font-weight: 400;
  opacity: .8;
}

.nav { display: flex; align-items: center; gap: 1.1rem; font-size: .92rem; }
.nav a, .nav button {
  color: #fff;
  text-decoration: none;
  opacity: .92;
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  padding: 0;
}
.nav a:hover, .nav button:hover { opacity: 1; text-decoration: underline; }
.nav-qui { opacity: .75; font-size: .86rem; }

/* --- La bascule clair / sombre ------------------------------------------- */
.theme-bascule {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* ⚠️ PAS de soulignement au survol, hérité de .nav button : une icône
     soulignée fait un trait qui ne veut rien dire. */
  text-decoration: none !important;
  border-radius: 6px;
  /* La zone reste confortable au doigt sans grossir la barre : le bouton fait
     la hauteur d'une ligne de menu, la règle des 44 px s'y ajoute sur écran
     tactile comme pour les autres commandes de la barre. */
  padding: .2rem;
}
.theme-icone { width: 18px; height: 18px; display: block; }

/* ⚠️ LES DEUX ICÔNES SONT LIVRÉES, LE CSS CHOISIT — même condition exactement
   que celle des couleurs, sinon l'icône finirait par annoncer un thème et la
   page en afficher un autre. Écrire ces deux règles séparément est ce qui
   garantit qu'elles ne peuvent pas diverger : elles se lisent l'une sous
   l'autre. */
.theme-icone-sombre { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) .theme-icone-clair  { display: none; }
  :root:not([data-theme="clair"]) .theme-icone-sombre { display: block; }
}
:root[data-theme="sombre"] .theme-icone-clair  { display: none; }
:root[data-theme="sombre"] .theme-icone-sombre { display: block; }
/* Le choix explicite « clair » l'emporte, y compris sur un système sombre. */
:root[data-theme="clair"] .theme-icone-clair  { display: block; }
:root[data-theme="clair"] .theme-icone-sombre { display: none; }

/* Annoncé aux lecteurs d'écran, invisible à l'œil. `display: none` et
   `visibility: hidden` retireraient l'élément de l'arbre d'accessibilité :
   il ne serait plus lu du tout. */
.annonce-invisible {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Occupe l'espace restant, ce qui repousse le pied en bas de fenêtre. */
.principal { flex: 1 0 auto; padding-top: 1.75rem; padding-bottom: 3rem; width: 100%; }

.pied {
  view-transition-name: evidra-pied;
  flex-shrink: 0;
  border-top: 1px solid var(--bord);
  background: var(--fond);
  /* 1,1rem à l'origine. Le pied est une mention, pas une zone de travail :
     il n'a pas à occuper 14 % de la fenêtre. */
  padding: .75rem 0;
  color: var(--texte-doux);
  font-size: .84rem;

  position: sticky;
  bottom: 0;
  z-index: 30;
}

/* ⚠⚠ EN PAYSAGE SUR TÉLÉPHONE, LE PIED REDEVIENT ORDINAIRE.
   Deux barres fixes sur 380 px de haut, c'est un tiers de l'écran mangé en
   permanence pour une ligne de mentions légales — on ne voit plus assez de
   contenu pour travailler. Le pied reprend alors sa place à la fin de la page.
   Le seuil porte sur la HAUTEUR, pas sur la largeur : c'est la hauteur qui
   manque. */
@media (max-height: 30rem) {
  .pied { position: static; }
}

/* ⚠⚠ LE PIED SE RÉDUIT À SON STRICT NÉCESSAIRE SUR TÉLÉPHONE.
   Signalé à l'usage : « le footer est monstrueux ». Il l'était : deux
   paragraphes, dont une phrase entière sur la confidentialité des documents,
   collés en bas de l'écran EN PERMANENCE puisqu'il est fixe. Sur 800 px de
   haut, cela revenait à sacrifier un dixième de l'écran à un texte qu'on a lu
   une fois pour toutes.
   ⇒ Le LIEN reste — il est obligatoire et doit être atteignable partout. La
     phrase, elle, disparaît : elle n'a jamais rien commandé, et le grand écran
     continue de l'afficher. */
@media (max-width: 60rem) {
  .pied {
    padding: .35rem 0;
    font-size: .78rem;
  }
  .pied-interieur > p:not(.pied-legal) { display: none; }
  /* Le lien perd sa hauteur de 44 px : le pied ferait sinon à lui seul la
     hauteur d'une tuile. Il reste large, donc atteignable. */
  .pied-legal { margin: 0; }
  .pied-legal a { min-height: 0; padding: .2rem 0; }
}

/* --- Titres ------------------------------------------------------------- */

h1 { font-size: 1.5rem; margin: 0 0 .3rem; color: var(--titre); }
h2 { font-size: 1.15rem; margin: 2rem 0 .75rem; color: var(--titre); }
.sous-titre { color: var(--texte-doux); margin: 0 0 1.5rem; }

/* --- Blocs -------------------------------------------------------------- */

.carte {
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: 1.25rem;
  margin-bottom: 1rem;
}

.vide {
  text-align: center;
  color: var(--texte-doux);
  padding: 2.5rem 1rem;
  border: 1px dashed var(--bord);
  border-radius: var(--rayon);
  background: var(--fond);
}

/* --- Messages ----------------------------------------------------------- */

.message {
  padding: .8rem 1rem;
  border-radius: var(--rayon);
  margin-bottom: 1rem;
  border-left: 4px solid;
}
.message-succes { background: var(--succes-fond); border-color: var(--succes); color: var(--succes-texte); }
.message-erreur { background: var(--erreur-fond); border-color: var(--erreur); color: var(--erreur-texte); }
.message-info   { background: var(--info-fond);   border-color: var(--info);   color: var(--info-texte); }
.message-alerte { background: var(--alerte-fond); border-color: var(--alerte); color: var(--alerte-texte); }

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

.champ { margin-bottom: 1.1rem; }
.champ label {
  display: block;
  font-weight: 600;
  font-size: .9rem;
  margin-bottom: .3rem;
  color: var(--titre);
}
.champ input[type=text],
.champ input[type=email],
.champ input[type=password],
.champ input[type=date],
.champ select,
.champ textarea {
  width: 100%;
  padding: .6rem .7rem;
  /* 3,21:1 — cf. --bord-champ. La bordure d'un champ dit où l'on écrit :
     c'est un composant d'interface, pas un filet décoratif. */
  border: 1px solid var(--bord-champ);
  border-radius: 6px;
  font: inherit;
  background: var(--fond);
  color: var(--texte);
}
.champ input:focus,
.champ select:focus,
.champ textarea:focus {
  outline: 2px solid var(--marque-clair);
  outline-offset: 1px;
  border-color: var(--marque-clair);
}
.aide { font-size: .84rem; color: var(--texte-doux); margin-top: .3rem; }

.bouton {
  display: inline-block;
  /* ⚠️⚠️ JETONS PROPRES AU BOUTON PRINCIPAL, et ce n'est pas du zele. Il tirait
     son fond de --marque, qui vaut le bleu nuit de la charte : parfait sur une
     page blanche (12,92:1), invisible sur une page sombre (1,21:1 — mesure du
     13/08 sur le banc). Un bouton d'action qui se confond avec le fond echoue
     a WCAG 1.4.11, qui exige 3:1 pour tout composant d'interface.
     ⇒ En sombre, le rapport s'inverse : encre claire sur fond sombre pour le
       texte, mais fond CLAIR pour le bouton, qui redevient l'element le plus
       visible de l'ecran. C'est la convention des interfaces sombres. */
  background: var(--bouton-fond);
  color: var(--bouton-encre);
  border: 1px solid var(--bouton-fond);
  border-radius: 6px;
  padding: .6rem 1.15rem;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}
.bouton:hover { background: var(--bouton-fond-survol); color: var(--bouton-encre); }
.bouton-discret {
  background: var(--fond);
  color: var(--accent);
  border-color: var(--bord);
}
.bouton-discret:hover { background: var(--fond-doux); color: var(--accent); }

/* --- Saisie d'un mot de passe ------------------------------------------- */

/* L'œil est DANS le champ, à son extrémité : c'est là qu'on le cherche.
   Le champ réserve la place à droite pour que le texte ne passe jamais
   dessous, même en saisie longue. */
.saisie-mdp { position: relative; display: block; }
.saisie-mdp input { width: 100%; padding-right: 3rem; }

.oeil {
  position: absolute;
  top: 50%;
  right: .25rem;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--texte-doux);
  cursor: pointer;
  padding: 0;
}
.oeil:hover { color: var(--accent); background: var(--fond-doux); }
.oeil:focus-visible { outline: 2px solid var(--marque-clair); outline-offset: -2px; }
/* Mot de passe visible : l'icône change ET la teinte, pour que l'état se lise
   même sans distinguer les deux dessins. */
.oeil[aria-pressed="true"] { color: var(--accent); }

@media (pointer: coarse) {
  .oeil { width: 44px; height: 44px; }
  .saisie-mdp input { padding-right: 3.2rem; }
}

.criteres {
  list-style: none;
  margin: 0 0 1rem;
  padding: .7rem .9rem;
  background: var(--fond-doux);
  border: 1px solid var(--bord);
  border-radius: 6px;
  font-size: .88rem;
}
.criteres li {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  padding: .12rem 0;
  color: var(--texte-doux);
}
/* ⚠️ Cette classe s'appelait « marque » — le nom de la marque de l'en-tête,
   qui porte « color: #fff ». Une déclaration directe l'emporte toujours sur un
   héritage : la puce était donc BLANCHE sur fond clair, contraste 1,06:1,
   invisible. Renommée pour qu'aucun composant ne puisse plus la teindre.

   La puce hérite maintenant la couleur du <li>, donc celle de l'état :
   --succes (5,33:1 sur blanc) ou --erreur (8,02:1). Sans cela, l'état d'un
   critère ne passait que par la couleur du libellé — une information portée
   par la seule couleur.

   La largeur est fixe : sans cela, le texte se décale à chaque changement de
   symbole et la liste « saute » pendant la frappe. */
.criteres .critere-marque {
  color: inherit;
  font-size: inherit;
  letter-spacing: normal;
  display: inline-block;
  width: 1em;
  text-align: center;
  font-weight: 700;
}
.criteres .critere-ok        { color: var(--succes); }
.criteres .critere-manque    { color: var(--erreur); }

.accord-ok  { color: var(--succes); font-weight: 600; }
.accord-non { color: var(--erreur); font-weight: 600; }

.bouton:disabled {
  opacity: .5;
  cursor: not-allowed;
  background: var(--texte-doux);
  border-color: var(--texte-doux);
}

/* --- Page de connexion -------------------------------------------------- */

.connexion {
  max-width: 26rem;
  margin: 3.5rem auto;
  padding: 0 1.25rem;
}
.connexion .carte { padding: 1.75rem; }
.connexion-marque {
  text-align: center;
  margin-bottom: 1.5rem;
  color: var(--accent);
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: .18em;
}
.connexion-marque img { height: 56px; width: auto; display: block; margin: 0 auto .6rem; }

/* --- Tableaux ----------------------------------------------------------- */

/* Enveloppés dans .tableau-large : une largeur excessive doit défiler DANS
   son cadre, jamais faire défiler la page entière. */
.tableau-large { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: .93rem; }
th, td { text-align: left; padding: .6rem .7rem; border-bottom: 1px solid var(--bord); }
th { background: var(--fond-doux); color: var(--titre); font-size: .82rem;
     text-transform: uppercase; letter-spacing: .04em; }
tbody tr:hover { background: var(--fond-doux); }

/* --- Étiquettes d'état -------------------------------------------------- */

.etat {
  display: inline-block;
  font-size: .76rem;
  font-weight: 700;
  padding: .15rem .5rem;
  border-radius: 4px;
  letter-spacing: .03em;
}
.etat-actif     { background: var(--succes-fond); color: var(--succes); }
.etat-brouillon { background: var(--info-fond);   color: var(--info); }
.etat-clos      { background: var(--neutre-fond);   color: var(--texte-doux); }

/* --- Bascule entre les vues de l'administration -------------------------- */

.admin-bascule-vues {
  display: flex;
  gap: .25rem;
  margin-bottom: 1.1rem;
  border-bottom: 1px solid var(--bord);
}
.admin-bascule-vues a {
  padding: .55rem 1rem;
  text-decoration: none;
  color: var(--texte-doux);
  font-weight: 600;
  font-size: .95rem;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}
.admin-bascule-vues a:hover { color: var(--accent); }
.admin-bascule-vues a.actif { color: var(--accent); border-bottom-color: var(--accent); }

/* ⭐ LA SAUVEGARDE N'EST PAS UN ONGLET, C'EST UNE ACTION. Elle est donc
   repoussée à droite et n'a pas d'état « actif » : rangée parmi les sections,
   elle ferait croire à un cinquième écran, et l'on chercherait sur quoi elle
   ouvre.
   ⚠️ « margin-left: auto » plutôt qu'un espacement fixe : la barre reste juste
      quel que soit le nombre de sections, y compris celle qu'on ajoutera. */
.admin-bascule-action {
  margin-left: auto;
  font-weight: 500;
  font-size: .88rem;
}
/* Sur petit écran, la barre passe à la ligne : la pousser à droite n'a plus de
   sens et créerait un trou. */
@media (max-width: 700px) {
  .admin-bascule-vues { flex-wrap: wrap; }
  .admin-bascule-action { margin-left: 0; }
}

.admin-bascule-archives {
  display: flex;
  align-items: center;
  gap: .45rem;
  font-size: .85rem;
  color: var(--texte-doux);
  cursor: pointer;
  margin-bottom: .6rem;
}

/* Un compte administrateur se repère d'un coup d'œil dans la liste. */
.marque-admin {
  display: inline-block;
  background: var(--info-fond);
  color: var(--info);
  font-size: .66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  border-radius: 3px;
  padding: 0 .3rem;
  margin-left: .3rem;
  vertical-align: middle;
}

/* Un permalien : on montre le SERVICE, pas l'URL — elles font 200 caractères
   et n'apprennent rien. Le ⚠ signale un domaine inattendu sans rien bloquer. */
.lien-carto {
  display: inline-block;
  font-size: .86rem;
  padding: .1rem .45rem;
  border: 1px solid var(--bord);
  border-radius: 4px;
  text-decoration: none;
  background: var(--fond-doux);
}
.lien-carto:hover { border-color: var(--marque-clair); background: var(--fond); }
.lien-suspect { border-color: var(--alerte); color: var(--alerte); background: var(--alerte-fond); }

.champ textarea { resize: vertical; min-height: 3rem; }

/* Un avertissement, pas un refus : teinte d'alerte mais pas de rouge, et il
   n'empêche jamais de valider. */
.avertissement-service {
  background: var(--alerte-fond);
  border-left: 3px solid var(--alerte);
  border-radius: 0 4px 4px 0;
  padding: .5rem .65rem;
  margin-top: .4rem;
  color: #6b4600;
}

.puces-clients { list-style: none; margin: .4rem 0 0; padding: 0; }
.puces-clients li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .45rem 0;
  border-bottom: 1px solid var(--fond-doux);
}
.puces-clients li:last-child { border-bottom: 0; }

/* --- « À sa place » : ce qu'un contact voit de l'extranet ----------------

   Volontairement pauvre en style. Ce panneau n'imite pas l'écran du contact —
   il en fait l'inventaire — et une ressemblance à moitié tenue serait pire
   qu'aucune : on croirait regarder son écran, et l'on conclurait de travers
   sur ce qui diffère. */
.apercu-qui {
  margin: 0 0 .8rem;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .5rem;
}

.apercu-liste { list-style: none; margin: .4rem 0 0; padding: 0; }
.apercu-liste li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .5rem;
  padding: .45rem 0;
  border-bottom: 1px solid var(--fond-doux);
}
.apercu-liste li:last-child { border-bottom: 0; }

/* --- Administration : liste à gauche, détail à droite -------------------- */

/* Grille plutôt que colonnes flottantes : les deux panneaux défilent
   indépendamment, la liste reste visible pendant qu'on lit le détail. */
.admin {
  display: grid;
  grid-template-columns: minmax(17rem, 22rem) 1fr;
  gap: 1.25rem;
  align-items: start;
}

.admin-colonne {
  position: sticky;
  top: 1rem;
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: .9rem;
  max-height: calc(100vh - 6rem);
  display: flex;
  flex-direction: column;
}
.admin-colonne-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-bottom: .75rem;
}
.admin-colonne-tete h1 { font-size: 1.1rem; margin: 0; }

.admin-recherche {
  width: 100%;
  padding: .5rem .65rem;
  /* Champ de saisie : 3,21:1, cf. --bord-champ. */
  border: 1px solid var(--bord-champ);
  border-radius: 6px;
  font: inherit;
  margin-bottom: .6rem;
}
.admin-recherche:focus { outline: 2px solid var(--marque-clair); outline-offset: 1px; }

.admin-liste { list-style: none; margin: 0; padding: 0; overflow-y: auto; }

.admin-client {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-radius: 6px;
  padding: .55rem .6rem;
  font: inherit;
  cursor: pointer;
  transition: background .12s ease;
}
.admin-client:hover { background: var(--fond-doux); }
.admin-client.actif { background: var(--info-fond); box-shadow: inset 3px 0 0 var(--accent); }
.admin-client-nom { display: block; font-weight: 600; color: var(--titre); }
.admin-client-chiffres {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  font-size: .8rem;
  color: var(--texte-doux);
  margin-top: .12rem;
}

.pastille {
  background: var(--alerte-fond);
  color: var(--alerte);
  border-radius: 10px;
  padding: 0 .4rem;
  font-weight: 600;
}

.admin-vide-liste { color: var(--texte-doux); font-size: .88rem; padding: .5rem .2rem; }

/* --- Détail -------------------------------------------------------------- */

.admin-detail { min-width: 0; transition: opacity .15s ease; }
.admin-detail.chargement { opacity: .45; pointer-events: none; }

.admin-accueil {
  background: var(--fond);
  border: 1px dashed var(--bord);
  border-radius: var(--rayon);
  padding: 3rem 2rem;
  text-align: center;
}
.admin-accueil-titre { font-size: 1.05rem; font-weight: 600; color: var(--titre); margin: 0 0 .4rem; }
.admin-accueil .aide { max-width: 32rem; margin: 0 auto; }

.admin-detail-tete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}
.admin-detail-tete h1 { margin: 0 0 .15rem; }

.admin-bloc {
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: 1rem 1.1rem 1.1rem;
  margin-bottom: 1rem;
}
.admin-bloc-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: .6rem;
}
.admin-bloc-tete h2 { margin: 0; font-size: 1rem; }

.admin-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.admin-table th {
  text-align: left;
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--texte-doux);
  background: none;
  border-bottom: 1px solid var(--bord);
  padding: .3rem .5rem;
}
.admin-table td { padding: .7rem .5rem; border-bottom: 1px solid var(--fond-doux); vertical-align: top; }

/* --- L'en-tête de colonnes reste visible pendant le défilement ------------
   ⭐ Sur une liste longue, l'intitulé des colonnes disparaît au premier
   défilement : on lit alors des valeurs sans savoir de quoi elles parlent, et
   l'on remonte pour vérifier. C'est le geste le plus fréquent sur un écran de
   travail, et il ne coûtait qu'une règle.

   ⚠️ IL SE CALE SOUS LA BARRE DU SITE, qui est collante elle aussi. `top: 0`
      l'aurait glissé DERRIÈRE elle — visible nulle part, et d'autant plus
      troublant que la règle aurait l'air correcte.
   ⚠️ Z-INDEX SOUS CELUI DE LA BARRE (40) : l'en-tête de tableau passe devant
      le contenu et derrière la navigation, dans cet ordre-là.
   ⚠️ FOND OPAQUE OBLIGATOIRE. Le th était en `background: none` : collant, il
      aurait laissé défiler les lignes VISIBLEMENT au travers de ses intitulés.
   ⚠️ ET LA BORDURE PASSE EN OMBRE INTERNE : avec `border-collapse: collapse`,
      la bordure appartient à la grille du tableau et NON à la cellule — elle
      reste donc en place pendant que la cellule se décolle, et l'on voit un
      trait orphelin traverser le tableau.
   ⚠️ Rien de tout cela sous 701 px : le tableau y est replié en fiches et son
      `thead` est masqué. */
@media (min-width: 701px) {
  .admin-table thead th {
    position: sticky;
    top: var(--hauteur-entete);
    z-index: 5;
    background: var(--fond);
    border-bottom: 0;
    box-shadow: inset 0 -1px 0 var(--bord);
  }
}
.admin-table tr:last-child td { border-bottom: 0; }
.alignement-droite { text-align: right; }

.vide-doux { color: var(--texte-doux); font-size: .9rem; margin: .3rem 0 0; }

.etat-attente { background: var(--alerte-fond); color: var(--alerte); }
.etiquette-restreint {
  display: inline-block;
  background: var(--info-fond);
  color: var(--info);
  font-size: .7rem;
  font-weight: 700;
  border-radius: 3px;
  padding: 0 .3rem;
  margin-left: .3rem;
}

.bouton-compact { padding: .3rem .65rem; font-size: .86rem; }

/* Une action irréversible ne doit pas ressembler à une action ordinaire. */
.bouton-danger { background: var(--erreur); border-color: var(--erreur); }
.bouton-danger:hover { background: #8b0e1c; border-color: #8b0e1c; }

.panneau-etroit { width: min(26rem, calc(100vw - 2rem)); }

/* ⭐ UNE TAILLE INTERMÉDIAIRE, née d'un défaut vu à l'écran le 11/08. Le panneau
   ordinaire fait 34 rem : à trois colonnes, chaque champ tombait à 150 px et
   une adresse de courriel y était aussi étroite qu'un prénom. Le panneau large
   (60 rem) était l'excès inverse pour cinq champs. 42 rem donne des colonnes
   qui ressemblent à ce qu'on y écrit. */
.panneau-moyen { width: min(42rem, calc(100vw - 2rem)); }

/* --- Archives ------------------------------------------------------------ */

.admin-bascule {
  display: flex;
  align-items: center;
  gap: .45rem;
  font-size: .85rem;
  color: var(--texte-doux);
  cursor: pointer;
  margin-bottom: .6rem;
}

/* Un client archivé doit se distinguer SANS avoir à lire : teinte atténuée
   et mention explicite. La seule couleur ne suffirait pas. */
/* ⚠️⚠️⚠️ PAS D'OPACITÉ ICI — ELLE RENDAIT ILLISIBLE LA SEULE INFORMATION QUI
   COMPTE. Il y avait « opacity: .62 », et l'opacité s'applique à TOUT le bloc,
   pastille comprise. Mesuré le 10/08, couleurs réellement composées :

     texte  #5a6068 à 62 % sur blanc → #999ca1
     fond   #eceef1 à 62 % sur blanc → #f3f4f6
     pastille « ARCHIVÉ »  : 2,50:1   (WCAG 1.4.3 en exige 4,5)
     chiffres POI/dossiers : 2,75:1

   Or la pastille est justement le SEUL marqueur textuel qui distingue un site
   archivé d'un site en service — et le commentaire de ce bloc revendiquait une
   « mention explicite » précisément parce que la couleur seule ne suffit pas.
   On confondait donc archivé et en service, alors que le nom du site, lui,
   restait parfaitement lisible.

   ⚠️ REMONTER L'OPACITÉ NE MARCHE PAS : il faudrait 0,94 pour repasser le
      seuil, et à 0,94 l'estompage ne se voit plus. Et un enfant ne peut pas
      annuler l'opacité de son parent — elle crée un contexte de composition.

   ⇒ L'estompage passe par une COULEUR, choisie et mesurée : le nom d'une ligne
     archivée descend à --texte-doux, soit 6,35:1 sur blanc. Il paraît en
     retrait, il reste lisible, et la pastille garde ses propres couleurs
     (5,46:1). */
.est-archive .admin-client-nom { color: var(--texte-doux); font-weight: 500; }
.est-archive .admin-client:hover .admin-client-nom { color: var(--texte); }
.marque-archive {
  display: inline-block;
  background: var(--neutre-fond);
  color: var(--texte-doux);
  font-size: .66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  border-radius: 3px;
  padding: 0 .3rem;
  margin-left: .35rem;
  vertical-align: middle;
}
/* Ne sert plus qu'aux dossiers archivés, dans un tableau : pas de mise en
   page particulière, seulement la teinte. Les règles de positionnement
   d'origine visaient la liste des clients, qui ne porte plus ce bouton. */
/* ⚠️⚠️ CETTE RÈGLE ÉTAIT MORTE. Elle s'écrivait « .lien-restaurer » (0,1,0) et
   les boutons concernés portent AUSSI « .lien-discret », défini plus bas dans
   cette feuille avec la même spécificité : à égalité, c'est le dernier déclaré
   qui gagne, donc le gris de .lien-discret l'emportait toujours. Le bouton
   « Restaurer » d'un bloc replié tombait à 3,36:1, sous le seuil de 4,5.
   ⇒ Sélecteur doublé (0,2,0) pour qu'il l'emporte là où il doit : #252c47 sur
     blanc, soit 13,73:1. Aucune couleur nouvelle, juste une règle qui
     s'applique enfin. */
.lien-discret.lien-restaurer { color: var(--accent); }
.lien-discret.lien-restaurer:hover { color: var(--marque-clair); }

.admin-actions-client { display: flex; align-items: center; gap: .9rem; flex-shrink: 0; }

/* Les archives d'un client : repliées par défaut, présentes sans encombrer. */
.archives-repli { margin-top: .8rem; }
.archives-repli summary {
  cursor: pointer;
  font-size: .86rem;
  color: var(--texte-doux);
  padding: .3rem 0;
  user-select: none;
}
.archives-repli[open] summary { margin-bottom: .3rem; }
.archives-repli .admin-table td { opacity: .72; }

/* Deux actions voisines aux effets très différents : on les espace pour que
   le clic ne parte pas sur la mauvaise. */
.admin-table .lien-discret + .lien-discret { margin-left: .9rem; }

/* --- Ce qu'une suppression va détruire ----------------------------------- */
/*
   La liste des pertes, dans le panneau de suppression d'un site. Elle n'est
   pas décorative : c'est elle qui porte la décision. On la rend LISIBLE, pas
   discrète — l'erreur serait d'atténuer visuellement ce qu'on doit lire avant
   d'effacer l'état de référence d'un parc entier.
*/
.liste-impact {
  margin: .9rem 0;
  padding-left: 1.15rem;
  color: var(--texte);
}
.liste-impact li { margin-bottom: .4rem; line-height: 1.45; }

/* --- La fiche d'un site -------------------------------------------------- */
/*
   L'écran des sites est la nouvelle entrée de l'administration : la fiche y
   rassemble tout ce qui se rattache à un lieu — ses POI durables, ses
   campagnes, ses documents, ses accès — sans passer par son client.
*/

.fiche-tete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: .75rem 1.5rem;
  margin-bottom: 1.2rem;
}
.fiche-tete h2 { margin: 0; color: var(--titre); }

.fiche-client { margin: .25rem 0 0; color: var(--texte-doux); font-size: .92rem; }

/* ⭐ LE CLIENT EST UN BOUTON, PAS UN LIEN : il n'emmène nulle part, il ouvre
      un panneau sur place. Un <a> aurait promis un changement de page — et il
      aurait été impossible à atteindre au clavier sans href. Il en garde
      l'apparence, parce que c'est bien un endroit où l'on clique. */
.etiquette-client-lien {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--marque-clair);
  text-decoration: underline;
  cursor: pointer;
}
.etiquette-client-lien:hover { color: var(--titre); }

.fiche-actions { display: flex; flex-wrap: wrap; gap: .4rem; }

.fiche-ligne { margin: .35rem 0; }
.fiche-ligne strong {
  display: inline-block;
  min-width: 8.5rem;
  color: var(--texte-doux);
  font-weight: 600;
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .03em;
}

.fiche-bloc { margin-top: 1.6rem; }
.fiche-bloc h3 {
  margin: 0 0 .5rem;
  font-size: 1rem;
  color: var(--titre);
  border-bottom: 1px solid var(--bord);
  padding-bottom: .35rem;
}

/* Le nombre d'éléments, à côté du titre : on sait s'il y a matière à ouvrir
   la section avant de la parcourir. */
.compteur {
  display: inline-block;
  margin-left: .4rem;
  padding: .05rem .45rem;
  border-radius: 999px;
  background: var(--fond-doux);
  border: 1px solid var(--bord);
  color: var(--texte-doux);
  font-size: .78rem;
  font-weight: 600;
}

@media (max-width: 700px) {
  /* L'intitulé cesse d'être une colonne : sur 390 px, il mangeait la moitié
     de la ligne et la valeur passait à la ligne suivante. C'est bien une
     affaire de LARGEUR — donc une media query de largeur. */
  .fiche-ligne strong { display: block; min-width: 0; }
  .fiche-actions .bouton { flex: 1 1 auto; justify-content: center; }
}

/* --- Choisir un contact à rattacher -------------------------------------- */

.admin-bloc-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

.candidats { list-style: none; margin: .5rem 0 0; padding: 0; }
.candidats li + li { margin-top: .3rem; }

.candidat {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--fond-doux);
  border: 1px solid var(--bord);
  border-radius: 6px;
  padding: .55rem .7rem;
  font: inherit;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}
.candidat:hover { background: var(--fond); border-color: var(--marque-clair); }
.candidat strong { display: block; color: var(--titre); }
.candidat .aide { display: block; }

/* --- Irréversible -------------------------------------------------------- */

/* Ce qui détruit sans retour ne doit ressembler à rien d'autre. */
.lien-danger { color: var(--erreur); }
.lien-danger:hover { color: #7d0d1a; }

.panneau-danger { border-top: 4px solid var(--erreur); }
.panneau-tete-danger h2 { color: var(--erreur); }

.encart-danger {
  background: var(--erreur-fond);
  border-left-color: var(--erreur);
  color: #7d0d1a;
}
.impact-danger { background: var(--erreur-fond); border-left-color: var(--erreur); }

/* Sa propre ligne, discrètement distinguée : on comprend sans lire pourquoi
   elle ne porte pas les mêmes actions que les autres. */
.est-soi-meme { background: var(--fond-doux); }

/* La demande de mot de passe est détachée du reste : c'est une barrière,
   pas un champ de plus dans un formulaire. */
.champ-confirmation {
  margin-top: 1.1rem;
  padding-top: 1rem;
  border-top: 2px solid var(--bord);
}

/* Ce qu'un archivage emporte, énoncé avant de confirmer. */
.impact {
  list-style: none;
  margin: .5rem 0 .8rem;
  padding: .6rem .8rem;
  background: var(--alerte-fond);
  border-left: 3px solid var(--alerte);
  border-radius: 0 6px 6px 0;
  font-size: .92rem;
}
.impact li { padding: .1rem 0; }
.lien-discret {
  background: none;
  border: 0;
  color: var(--texte-doux);
  font: inherit;
  font-size: .86rem;
  cursor: pointer;
  text-decoration: underline;
  padding: 0;
}
.lien-discret:hover { color: var(--erreur); }

/* --- Panneaux (dialog natif) --------------------------------------------- */

.panneau {
  border: 0;
  border-radius: 12px;
  padding: 0;
  width: min(34rem, calc(100vw - 2rem));
  box-shadow: 0 12px 40px rgba(16, 20, 35, .28);
  color: var(--texte);
}
.panneau::backdrop { background: rgba(20, 24, 40, .45); backdrop-filter: blur(2px); }

/* Entrée en fondu : sans transition, le panneau apparaît d'un coup et l'œil
   perd le fil de ce qui vient de se passer. */
@media (prefers-reduced-motion: no-preference) {
  .panneau[open] { animation: panneau-entre .16s ease-out; }
  @keyframes panneau-entre {
    from { opacity: 0; transform: translateY(-6px) scale(.99); }
    to   { opacity: 1; transform: none; }
  }
}

.panneau-forme { display: flex; flex-direction: column; max-height: 85vh; }
.panneau-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--bord);
}
.panneau-tete h2 { margin: 0; font-size: 1.05rem; color: var(--titre); }
.panneau-sous { font-weight: 400; color: var(--texte-doux); font-size: .9rem; }
.panneau-fermer {
  background: none; border: 0; font-size: 1.5rem; line-height: 1;
  color: var(--texte-doux); cursor: pointer; padding: 0 .2rem;
}
.panneau-fermer:hover { color: var(--texte); }

.panneau-corps { padding: 1.15rem 1.25rem; overflow-y: auto; }
.panneau-pied {
  display: flex;
  justify-content: flex-end;
  gap: .6rem;
  padding: .85rem 1.25rem;
  border-top: 1px solid var(--bord);
  background: var(--fond-doux);
}

.requis {
  font-size: .7rem;
  font-weight: 600;
  color: var(--erreur);
  background: var(--erreur-fond);
  border-radius: 3px;
  padding: 0 .3rem;
  margin-left: .3rem;
  /* ⚠️ Il passait à la ligne derrière un intitulé un peu long — « Adresse de
     courriel OBLIGATOIRE » — et le champ dessous descendait d'un cran, ce qui
     désalignait toute la rangée. Vu à l'écran le 11/08. */
  white-space: nowrap;
  text-transform: uppercase;
  letter-spacing: .03em;
}

.deux-colonnes { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }

/* --- Formulaire à deux colonnes ------------------------------------------
   Demandé par l'auteur le 09/08 : « la fiche est trop petite, faut scroller,
   c'est relou. On agrandit tout ça, et tout sur le même écran, d'un seul coup
   d'œil. »

   ⭐ ON N'APPARIE QUE CE QUI VA ENSEMBLE — prénom/nom, courriel/téléphone,
      fonction/société. Deux champs sans rapport côte à côte font zigzaguer le
      regard et se remplissent PLUS lentement qu'en une seule colonne : le
      gain de hauteur ne vaut que si la paire a un sens.

   ⚠️ minmax(0, 1fr) et non 1fr : une colonne « 1fr » ne descend pas sous la
      largeur de son contenu, et un texte d'aide un peu long l'empêcherait
      alors de rétrécir — les deux colonnes déborderaient du panneau.

   ⚠️ Le repli à une colonne est calé sur 700 px, le seuil où les panneaux
      passent en plein écran : en dessous, le panneau EST la fenêtre, donc la
      largeur de l'un vaut celle de l'autre. */
.grille-champs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .75rem 1.5rem;
  align-items: start;
}

.grille-champs > .pleine-largeur { grid-column: 1 / -1; }

/* --- Un formulaire dont les largeurs ANNONCENT le contenu ---------------- */
/*
   ⭐⭐⭐ NÉ D'UN DÉFAUT VU À L'ÉCRAN, PAS D'UNE MESURE. Le panneau de création
   d'un contact réutilisait la grille à trois colonnes ci-dessus. J'avais
   mesuré la HAUTEUR — plus de défilement, donc réputé bon — sans jamais
   regarder le résultat : une adresse de courriel dans 150 px, un téléphone
   suivi de deux tiers de ligne vide, et le badge « obligatoire » qui passait à
   la ligne en désalignant toute la rangée. *Mesurer une chose ne dit rien du
   reste.*

   LE PRINCIPE : la largeur d'un champ doit ressembler à ce qu'on y écrit. Un
   champ trop large invite à en mettre plus que prévu ; un champ trop court fait
   croire qu'on s'est trompé de case. Six colonnes permettent la moitié (3), le
   tiers (2) et les deux tiers (4) sans nombre à virgule.

   ⚠️ Les proportions ne sont PAS un habillage : ce sont elles qui disent qu'un
      courriel est long et qu'un téléphone est court.
*/
.grille-formulaire {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: .9rem 1.25rem;
  align-items: start;
}
/* Par défaut, un champ occupe la moitié de la ligne. */
.grille-formulaire > * { grid-column: span 3; }
.grille-formulaire > .champ-long  { grid-column: span 4; }
.grille-formulaire > .champ-court { grid-column: span 2; }
.grille-formulaire > .pleine-largeur { grid-column: 1 / -1; }

/* Sous 700 px, une seule colonne : à cette largeur, deux champs côte à côte
   font des cases de 130 px que le doigt vise mal et que l'œil parcourt en
   zigzag. */
@media (max-width: 700px) {
  .grille-formulaire > * { grid-column: 1 / -1; }
}

/* ⭐ TROIS COLONNES PLUTÔT QUE DEUX — tranché par l'auteur le 10/08 SUR LES
      CHIFFRES, pas sur une impression : trois colonnes retirent une rangée
      entière et le contenu passe de 627 à 524 px. C'est ce qui fait la
      différence entre « ça tient » et « ça défile encore de 148 px » sur un
      portable 13 pouces.
   ⚠️ Deux colonnes en dessous de 1000 px : à trois, chaque champ tomberait
      sous 220 px et « Responsable sécurité » n'y tiendrait plus. */
@media (max-width: 1000px) {
  .grille-champs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 700px) {
  .grille-champs { grid-template-columns: 1fr; }
}

.plus-tard { margin-top: .3rem; }
.plus-tard summary {
  cursor: pointer;
  color: var(--accent);
  font-size: .9rem;
  padding: .35rem 0;
  user-select: none;
}
.plus-tard[open] summary { margin-bottom: .5rem; }

.case { display: flex; gap: .55rem; align-items: flex-start; margin-top: .3rem; cursor: pointer; }
.case input { margin-top: .3rem; }
.case .aide { display: block; margin-top: .1rem; }

.encart {
  background: var(--info-fond);
  border-left: 3px solid var(--info);
  border-radius: 0 6px 6px 0;
  padding: .65rem .8rem;
  font-size: .9rem;
  margin: .5rem 0 0;
}
.encart-alerte { background: var(--alerte-fond); border-left-color: var(--alerte); }

.identifiant {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin-top: .8rem;
  flex-wrap: wrap;
}
.identifiant-etiquette {
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--texte-doux);
  width: 7rem;
}
.identifiant code {
  flex: 1;
  min-width: 12rem;
  background: var(--fond-doux);
  border: 1px solid var(--bord);
  border-radius: 6px;
  padding: .45rem .6rem;
  font-size: 1rem;
  letter-spacing: .02em;
  user-select: all;
}

/* --- Messages passagers -------------------------------------------------- */

/* ⚠️ Le z-index ci-dessous ne suffit PAS à passer devant un panneau modal :
   celui-ci vit dans la « couche supérieure » du navigateur, hors d'atteinte de
   tout empilement du document. C'est admin-commun.js qui rattache cette zone AU
   PANNEAU quand il y en a un d'ouvert. Le z-index ne sert qu'au reste. */
.admin-messages {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  max-width: min(26rem, calc(100vw - 2rem));
}
.admin-messages .message {
  margin: 0;
  box-shadow: 0 6px 20px rgba(16, 20, 35, .18);
  transition: opacity .5s ease, transform .5s ease;
}
.admin-messages .message-parti { opacity: 0; transform: translateY(8px); }

@media (max-width: 900px) {
  .admin { grid-template-columns: 1fr; }
  .admin-colonne { position: static; max-height: none; }
  .deux-colonnes { grid-template-columns: 1fr; }
}

/* --- Écran d'erreur ----------------------------------------------------- */

.erreur-page { text-align: center; padding: 4rem 1rem; }
.erreur-code { font-size: 3.5rem; font-weight: 700; color: var(--accent); line-height: 1; }

/* --- Petits écrans ------------------------------------------------------ */

/* =========================================================================
   MOBILITÉ

   Le site doit être TOTALEMENT utilisable au doigt, pas seulement lisible.
   Mesuré au banc d'essai (iframe de largeur imposée, Chrome refusant de
   descendre sous ~485 px de fenêtre sous Windows) : à 390 px, la barre de
   navigation débordait et les zones tactiles faisaient 23 px de haut.
   ====================================================================== */

/* --- Zones tactiles : 44 px partout où l'on clique ----------------------- */
@media (pointer: coarse) {
  .nav a,
  .nav button,
  .lien-discret,
  .plus-tard summary,
  .archives-repli summary,
  /* ⭐ AJOUTÉS LE 10/08, APRÈS MESURE au format téléphone : les actions de la
        fiche d'un site (33 px), le bouton « + Site » de la tête de colonne
        (30 px) et le CHAMP DE RECHERCHE (39 px). Le dernier n'est pas neuf —
        il servait déjà aux écrans clients et contacts, où personne ne l'avait
        mesuré. Le contrôle de mobilité du harnais ne le voyait pas : il
        vérifie que la règle des 44 px EXISTE, pas qu'elle couvre tout.
     ⚠️ ICI et non dans une media query de largeur : c'est le DOIGT qui
        dimensionne ces zones, pas la place disponible. Une fenêtre de bureau
        rétrécie garde des commandes compactes, qui se visent très bien à la
        souris. */
  .admin-recherche,
  .admin-colonne-tete .bouton,
  /* ⭐ RAPATRIÉ ICI LE 13/08 : le lien des mentions légales portait ses 44 px
        dans la règle générale, donc aussi sous la souris — c'est ce qui
        gonflait le pied de page à 128 px sur écran d'ordinateur. */
  .pied-legal a,
  .fiche-actions .bouton {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  /* Une case à cocher de 13 px est intouchable : on l'agrandit et on étend
     la zone sensible à son libellé. */
  input[type=checkbox] { width: 22px; height: 22px; }
  .admin-bascule,
  .case { min-height: 44px; }
  .admin-client { padding: .75rem .6rem; }
}

/* --- Menu replié ---------------------------------------------------------- */

.menu-bascule {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: none;
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: 8px;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
}
.menu-bascule[hidden] { display: none; }
.menu-bascule:hover { background: rgba(255, 255, 255, .1); }
.menu-bascule:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* Trois barres dessinées en CSS : pas d'image, pas de police d'icônes. */
.menu-barres,
.menu-barres::before,
.menu-barres::after {
  display: block;
  width: 20px;
  height: 2px;
  background: #fff;
  border-radius: 2px;
  transition: transform .18s ease, opacity .18s ease;
}
.menu-barres { position: relative; }
.menu-barres::before,
.menu-barres::after { content: ''; position: absolute; left: 0; }
.menu-barres::before { top: -6px; }
.menu-barres::after  { top: 6px; }

/* Ouvert : les barres deviennent une croix — l'état se voit sans lire. */
.menu-ouvert .menu-barres { background: transparent; }
.menu-ouvert .menu-barres::before { transform: translateY(6px) rotate(45deg); }
.menu-ouvert .menu-barres::after  { transform: translateY(-6px) rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .menu-barres, .menu-barres::before, .menu-barres::after { transition: none; }
}

@media (max-width: 860px) {
  /* L'en-tête garde UNE SEULE ligne : marque à gauche, bouton à droite.
     L'empilement des entrées de menu donnait un bandeau démesuré — signalé
     par une testeuse sur téléphone le 07/08/2026. */
  .entete-interieur {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
  }
  .entete { padding: .6rem 0; }
  .marque { font-size: 1.15rem; }
  .marque img { height: 26px; }

  /* Le menu se déploie SOUS l'en-tête, sur toute la largeur. */
  .nav {
    order: 3;
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    border-top: 1px solid rgba(255, 255, 255, .18);
    margin-top: .5rem;
    padding-top: .25rem;
  }
  .nav.nav-repliee { display: none; }

  .nav a, .nav button, .nav form {
    width: 100%;
    text-align: left;
    padding: .1rem 0;
    margin: 0;
  }
  .nav a, .nav button {
    min-height: 44px;
    display: flex;
    align-items: center;
  }
  .nav-qui {
    min-height: 0;
    padding: .35rem 0 .15rem;
    opacity: .7;
    font-size: .8rem;
    text-transform: uppercase;
    letter-spacing: .04em;
  }

  h1 { font-size: 1.25rem; }
  .principal { padding-top: 1.1rem; }
}

/* --- Tableaux : en fiches, pas en défilement horizontal ------------------ */
/*
   Un tableau de six colonnes ne se lit pas sur 390 px, même en le faisant
   défiler : on perd l'en-tête dès la deuxième colonne. On le replie donc en
   fiches, chaque cellule portant son intitulé.
*/
@media (max-width: 700px) {
  .admin-table thead { display: none; }
  .admin-table, .admin-table tbody, .admin-table tr, .admin-table td { display: block; width: 100%; }

  .admin-table tr {
    border: 1px solid var(--bord);
    border-radius: var(--rayon);
    padding: .6rem .7rem;
    margin-bottom: .6rem;
    background: var(--fond);
  }
  .admin-table td { border: 0; padding: .2rem 0; }

  /* L'intitulé de colonne revient devant la valeur. */
  .admin-table td[data-col]::before {
    content: attr(data-col);
    display: block;
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--texte-doux);
  }
  .admin-table td:empty { display: none; }

  /* Les actions passent en bas de fiche, espacées pour le doigt. */
  .admin-table td.alignement-droite {
    text-align: left;
    margin-top: .5rem;
    padding-top: .5rem;
    border-top: 1px dashed var(--bord);
    display: flex;
    flex-wrap: wrap;
    gap: .25rem 1.4rem;
  }
  .admin-table .lien-discret + .lien-discret { margin-left: 0; }

  .admin-detail-tete { flex-direction: column; }
  .admin-actions-client { flex-wrap: wrap; gap: .25rem 1.2rem; }

  /* Les panneaux occupent l'écran : un dialogue étroit centré sur un
     téléphone laisse des bords inutiles et rapetisse les champs. */
  .panneau, .panneau-etroit {
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    border-radius: 0;
    margin: 0;
  }
  .panneau-forme { max-height: 100dvh; height: 100%; }
  .panneau-pied { position: sticky; bottom: 0; }
  .panneau-pied .bouton { flex: 1; justify-content: center; text-align: center; }
}

/* =========================================================================
   DOCUMENTS
   ---------
   Dépôt et liste sur le même écran. La file d'attente est visible AVANT
   l'envoi : c'est là que se voient les refus (trop lourd, format non accepté)
   et que se choisit la nature de chaque fichier — pendant qu'il est encore
   temps, et non après trois minutes de transfert.
   ====================================================================== */

.documents { margin-bottom: 1.5rem; }

/* --- La zone de dépôt ---------------------------------------------------- */

.depot {
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  padding: 1.1rem 1.25rem;
  margin-bottom: 1rem;
}

.depot-zone {
  border: 2px dashed var(--bord);
  border-radius: var(--rayon);
  padding: 1.4rem 1rem;
  text-align: center;
  transition: border-color .15s ease, background .15s ease;
}
.depot-zone.depot-survol {
  border-color: var(--marque-clair);
  background: var(--info-fond);
}

.depot-invite { margin: 0 0 .5rem; color: var(--texte); }
.depot-invite strong { color: var(--titre); }
.depot-limites { margin: .6rem 0 0; }

/* Le champ de fichier est retiré de la VUE, jamais du flux : « display:none »
   le rendrait inatteignable au clavier et à un lecteur d'écran, alors que
   c'est lui qui ouvre le sélecteur. */
.depot-champ {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- La file d'attente --------------------------------------------------- */

.depot-attente { margin-top: 1rem; }
.depot-attente-titre {
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--texte-doux);
  margin: 0 0 .5rem;
}

.depot-liste { list-style: none; margin: 0 0 .8rem; padding: 0; }

.depot-item {
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: .6rem .75rem;
  margin-bottom: .5rem;
  background: var(--fond-doux);
}
/* Un fichier refusé se voit d'un coup d'œil dans la liste : sans cela, on
   lance l'envoi et l'on découvre le refus après coup. */
.depot-item-refuse { border-color: var(--erreur); background: var(--erreur-fond); }
.depot-item-fait   { border-color: var(--succes); background: var(--succes-fond); }

.depot-item-tete { display: flex; justify-content: space-between; gap: .75rem; align-items: baseline; }
.depot-item-nom { font-weight: 600; color: var(--titre); word-break: break-word; }
.depot-item-taille { color: var(--texte-doux); font-size: .86rem; white-space: nowrap; }
.depot-item-refus { color: var(--erreur); font-size: .88rem; margin: .35rem 0 0; }
.depot-item-etat  { color: var(--texte-doux); font-size: .86rem; margin: .3rem 0 0; }
.depot-item-nature { display: block; margin-top: .5rem; font-size: .88rem; color: var(--texte-doux); }

.depot-jauge {
  height: 6px;
  background: var(--bord);
  border-radius: 3px;
  overflow: hidden;
  margin-top: .5rem;
}
.depot-jauge-barre { height: 100%; background: var(--marque); transition: width .2s ease; }

.depot-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

/* --- Le tableau ---------------------------------------------------------- */

.documents-table .document-nom { font-weight: 600; color: var(--titre); word-break: break-word; }

/* Un document retiré ou dépassé reste LISIBLE : l'estomper au point de ne
   plus le déchiffrer reviendrait à le supprimer sans le dire. */
.document-obsolete .document-nom { text-decoration: line-through; text-decoration-thickness: 1px; }
.document-retire { background: var(--fond-doux); }
.document-retire .document-nom { color: var(--texte-doux); }

.etiquette {
  display: inline-block;
  font-size: .7rem;
  font-weight: 700;
  border-radius: 3px;
  padding: 0 .3rem;
  margin-left: .3rem;
  white-space: nowrap;
}
.etiquette-livrable { background: var(--succes-fond); color: var(--succes); }
.etiquette-obsolete { background: var(--alerte-fond); color: var(--alerte); }
.etiquette-masque   { background: var(--info-fond);   color: var(--info); }
.etiquette-retire   { background: var(--neutre-fond);   color: var(--texte-doux); }
.etiquette-absent   { background: var(--erreur-fond); color: var(--erreur); }

.select-nature {
  border: 1px solid var(--bord);
  border-radius: 5px;
  padding: .25rem .4rem;
  font: inherit;
  font-size: .88rem;
  background: var(--fond);
  color: var(--texte);
  max-width: 100%;
}
.select-nature:focus-visible { outline: 2px solid var(--marque-clair); outline-offset: 1px; }

.bascule-ligne { display: inline-flex; align-items: center; gap: .4rem; cursor: pointer; }
.bascule-mot { font-size: .88rem; color: var(--texte-doux); }

/* --- Mobilité ------------------------------------------------------------ */

@media (pointer: coarse) {
  /* Un sélecteur de 26 px et une case de 13 px ne se visent pas au doigt. */
  .select-nature,
  .bascule-ligne,
  .depot-actions .bouton,
  .depot-invite .bouton {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  .depot-item .lien-discret { min-height: 44px; display: inline-flex; align-items: center; }
}

@media (max-width: 700px) {
  .depot { padding: .8rem .9rem; }
  .depot-zone { padding: 1rem .6rem; }
  /* Les deux boutons occupent la largeur : côte à côte sur 390 px, ils
     deviennent deux cibles de 90 px collées l'une à l'autre. */
  .depot-actions .bouton { flex: 1; justify-content: center; text-align: center; }
  .depot-item-tete { flex-direction: column; gap: .1rem; }
  .select-nature { width: 100%; }
}

/* --- Vue d'ensemble des documents dans l'écran d'administration ---------- */

.documents-filtres {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem 1.25rem;
  margin-bottom: .75rem;
}
.documents-filtre {
  display: flex;
  align-items: center;
  gap: .4rem;
  font-size: .86rem;
  color: var(--texte-doux);
}

@media (max-width: 700px) {
  /* Empilés : côte à côte sur 390 px, les deux sélecteurs deviennent
     illisibles avant d'être intouchables. */
  .documents-filtres { flex-direction: column; align-items: stretch; gap: .5rem; }
  .documents-filtre { flex-direction: column; align-items: flex-start; gap: .2rem; }
}

/* --- Actions en icônes, à emplacement fixe ------------------------------- */
/*
   Trois emplacements, toujours les mêmes, dans le même ordre : télécharger,
   marquer obsolète, retirer. Une action indisponible LAISSE SA PLACE VIDE
   plutôt que de décaler les suivantes.

   C'est la seule façon de viser sans lire : si la corbeille se déplace d'une
   ligne à l'autre selon les droits, on la cherche à chaque fois — et sur
   téléphone, on finit par cliquer à côté. Les libellés partent en infobulle.
*/
.actions-icones {
  display: inline-grid;
  grid-template-columns: repeat(3, 2rem);
  gap: .25rem;
  justify-items: center;
}

.bouton-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 5px;
  background: none;
  color: var(--texte-doux);
  cursor: pointer;
  transition: color .12s ease, background .12s ease, border-color .12s ease;
}
.bouton-icone:hover {
  color: var(--accent);
  background: var(--fond-doux);
  border-color: var(--bord);
}
.bouton-icone:focus-visible { outline: 2px solid var(--marque-clair); outline-offset: 1px; }
.bouton-icone svg { width: 1rem; height: 1rem; display: block; }

/* L'action destructrice ne se signale qu'au survol : rouge en permanence,
   elle attire l'œil plus que le document lui-même. */
.bouton-icone-danger:hover { color: var(--erreur); border-color: var(--erreur); background: var(--erreur-fond); }

/* L'emplacement d'une action indisponible. Il occupe la place, sans rien
   afficher : une icône grisée se clique quand même, et un clic sans effet
   fait douter de tous les autres. */
.bouton-icone-vide { display: block; width: 2rem; height: 2rem; }

/* Marque un état déjà actif — obsolète, par exemple : le bouton sert alors à
   le DÉFAIRE, et doit se distinguer de celui qui l'appliquerait. */
.bouton-icone.actif { color: var(--alerte); background: var(--alerte-fond); border-color: var(--alerte); }

/* --- Colonne Livrable ---------------------------------------------------- */

.marque-livrable {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--succes-fond);
  color: var(--succes);
}
.marque-livrable svg { width: .9rem; height: .9rem; }
.sans-valeur { color: var(--bord); }

/* Une date affichée au jour, l'heure en infobulle : le pointillé annonce
   qu'il y a quelque chose à survoler, sinon personne ne le découvre. */
.date-detail {
  border-bottom: 1px dotted var(--bord);
  cursor: help;
}

@media (pointer: coarse) {
  /* 44 px au doigt. La grille s'élargit d'autant, l'alignement tient. */
  .actions-icones { grid-template-columns: repeat(3, 44px); gap: .15rem; }
  .bouton-icone { width: 44px; height: 44px; }
  .bouton-icone-vide { width: 44px; height: 44px; }
  /* L'infobulle n'existe pas au doigt : l'heure passe sous la date. */
  .date-detail { border-bottom: 0; cursor: default; }
  .date-heure { display: block; }
}

/* Sur téléphone l'heure est écrite, faute de survol possible. */
.date-heure { display: none; font-size: .8rem; color: var(--texte-doux); }
@media (max-width: 700px) {
  .date-heure { display: block; }
  .date-detail { border-bottom: 0; cursor: default; }
  /* Les actions restent groupées et alignées en bas de fiche. */
  .admin-table td.alignement-droite .actions-icones { justify-content: flex-start; }
}

/* =========================================================================
   BASE DE LIEUX
   ====================================================================== */

.lieux { margin-bottom: 1.5rem; }

.lieux-barre {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: .75rem;
}
.lieux-filtres {
  display: flex;
  align-items: flex-end;
  gap: .75rem 1.25rem;
  flex-wrap: wrap;
}
.lieux-recherche { width: 16rem; max-width: 100%; }
.lieux-reste { margin: 0; white-space: nowrap; }

/* Saisie directe dans le tableau : c'est le geste du traitement, il doit
   tenir en une frappe sans ouvrir de fiche. */
.champ-en-ligne {
  border: 1px solid var(--bord);
  border-radius: 5px;
  padding: .25rem .4rem;
  font: inherit;
  font-size: .86rem;
  background: var(--fond);
  color: var(--texte);
  width: 100%;
  min-width: 8rem;
}
.champ-en-ligne:focus-visible { outline: 2px solid var(--marque-clair); outline-offset: 1px; }

.lieu-waze { display: flex; align-items: center; gap: .25rem; }

.coordonnées { font-variant-numeric: tabular-nums; font-size: .88rem; }

/* Deux emplacements au lieu de trois — l'alignement tient quand même. */
.actions-deux { grid-template-columns: repeat(2, 2rem); }

/* La ligne que le serveur vient de refuser pour conflit. Signalée le temps
   d'un redessin : « quelqu'un a modifié ce lieu » laisse sinon chercher
   lequel parmi cent. */
.lieu-en-conflit {
  background: var(--alerte-fond);
  box-shadow: inset 3px 0 0 var(--alerte);
}

.lieux-bloc-traitement {
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: .9rem 1rem .2rem;
  margin-top: 1.2rem;
}
.lieux-bloc-traitement legend {
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--texte-doux);
  padding: 0 .4rem;
}

@media (pointer: coarse) {
  .champ-en-ligne { min-height: 44px; }
  .actions-deux { grid-template-columns: repeat(2, 44px); }
}

@media (max-width: 700px) {
  .lieux-barre { flex-direction: column; align-items: stretch; }
  .lieux-filtres { flex-direction: column; align-items: stretch; }
  .lieux-recherche { width: 100%; }
  .lieux-barre .bouton { width: 100%; justify-content: center; }
}

/* --- Fiche d'un lieu : les caractéristiques en avant / attendu ----------- */
/*
   Trois colonnes : l'intitulé, la valeur d'avant, la valeur attendue. C'est la
   mise en regard qui fait la prestation — « c'est X, ça doit devenir Y » — et
   deux champs empilés l'un sous l'autre la perdent entièrement.
*/
.caracs { margin: 1.2rem 0; }

.caracs-entete,
.caracs-ligne {
  display: grid;
  grid-template-columns: 12rem 1fr 1fr;
  gap: .5rem .75rem;
  align-items: start;
}

.caracs-entete {
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--texte-doux);
  padding-bottom: .3rem;
  border-bottom: 1px solid var(--bord);
  margin-bottom: .6rem;
}

.caracs-ligne { margin-bottom: .55rem; }
.caracs-ligne input,
.caracs-ligne textarea,
.caracs-ligne select {
  width: 100%;
  padding: .4rem .5rem;
  border: 1px solid var(--bord);
  border-radius: 5px;
  font: inherit;
  font-size: .9rem;
  background: var(--fond);
  color: var(--texte);
}
.caracs-ligne textarea { resize: vertical; min-height: 2.4rem; }
.caracs-ligne input:focus,
.caracs-ligne textarea:focus,
.caracs-ligne select:focus {
  outline: 2px solid var(--marque-clair);
  outline-offset: 1px;
}

.caracs-libelle {
  font-size: .88rem;
  font-weight: 600;
  color: var(--titre);
  padding-top: .45rem;
}

/* La fiche d'un lieu porte trente champs : elle a besoin de plus de place que
   les panneaux ordinaires. */
.panneau-large { width: min(60rem, calc(100vw - 3rem)); }

/* ⭐ UN PANNEAU LARGE A AUSSI DROIT À PLUS DE HAUTEUR. Les 85vh communs
      laissaient 479 px de contenu visible sur une fenêtre de 720 px — la fiche
      d'un contact en demande 524. Mesuré le 10/08 : à 92vh elle tient tout
      entière, et c'était la demande (« tout sur le même écran, d'un seul coup
      d'œil »).
   ⚠️ Visé sur .panneau-large SEULEMENT : les petits panneaux de confirmation
      n'ont rien à y gagner, et rogner la marge autour d'eux ferait paraître le
      dialogue collé aux bords.
   ⚠️⚠️ ET BORNÉ AU-DESSUS DE 700 px. « .panneau-large .panneau-forme » pèse
      0,2,0 : sans cette borne il l'emporterait sur le « .panneau-forme »
      (0,1,0) de la règle plein écran mobile, et un panneau large cesserait
      d'occuper tout l'écran du téléphone — un sélecteur plus précis ne
      remplace pas, il SE SUPERPOSE. */
@media (min-width: 701px) {
  .panneau-large .panneau-forme { max-height: 92vh; }
}

/* Chaque demande a sa teinte : sur une liste, la nature de la demande se
   repère avant même d'être lue. */
.etat-demande-ajout        { background: var(--succes-fond); color: var(--succes); }
.etat-demande-modification { background: var(--info-fond);   color: var(--info); }
.etat-demande-fermeture    { background: var(--alerte-fond); color: var(--alerte); }
.etat-demande-suppression  { background: var(--erreur-fond); color: var(--erreur); }

@media (max-width: 700px) {
  /* Trois colonnes sur 390 px sont illisibles : chaque caractéristique
     devient un bloc, l'intitulé au-dessus de ses deux champs. */
  .caracs-entete { display: none; }
  .caracs-ligne {
    grid-template-columns: 1fr;
    gap: .25rem;
    padding-bottom: .7rem;
    margin-bottom: .7rem;
    border-bottom: 1px solid var(--bord);
  }
  .caracs-libelle { padding-top: 0; }
  /* ⚠️ Sans en-tête de colonne, chaque champ doit dire lequel il est — et ce
     n'est PAS faisable en CSS : un input est un élément remplacé, ::before n'y
     rend rien. L'indication passe donc par le placeholder, posé en PHP, qui
     fonctionne partout et sert aussi sur grand écran. */
  .panneau-large { width: 100vw; max-width: 100vw; }
}

.lieux-actions { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
@media (max-width: 700px) {
  .lieux-actions { flex-direction: column; align-items: stretch; }
  .lieux-actions .bouton { width: 100%; text-align: center; }
}

/* --- Boutons radio dans la fiche d'un lieu ------------------------------ */
/*
   Deux valeurs qui s'excluent : autant les montrer toutes les deux plutôt que
   de les cacher derrière une liste qu'il faut ouvrir pour savoir ce qu'elle
   contient. La troisième option — « non renseigné » — n'est pas un ornement :
   un bouton radio ne se décoche pas, et sans elle une valeur choisie par
   erreur ne pourrait plus jamais être retirée.
*/
.radios { display: flex; flex-wrap: wrap; gap: .15rem .9rem; padding-top: .3rem; }

.radio-option {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  font-size: .88rem;
  cursor: pointer;
  white-space: nowrap;
}
.radio-option input { margin: 0; }

/* Le nom d'un lieu OUVRE sa fiche. Un bouton, pas un texte cliquable : il
   s'atteint au clavier et s'annonce comme une action. Il en garde pourtant
   l'apparence — un bouton dessiné au milieu d'un tableau alourdirait chaque
   ligne. */
.lien-nom {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--titre);
  text-align: left;
  cursor: pointer;
  border-bottom: 1px solid transparent;
}
.lien-nom:hover { color: var(--accent); border-bottom-color: var(--marque-clair); }
.lien-nom:focus-visible { outline: 2px solid var(--marque-clair); outline-offset: 2px; }

/* La note explicative de la demande : elle se lit AVANT de remplir. */
.lieu-note { margin: 0 0 1.1rem; font-size: .9rem; }

.actions-une { grid-template-columns: 2rem; }

@media (pointer: coarse) {
  .radio-option { min-height: 44px; }
  .radio-option input { width: 22px; height: 22px; }
  .lien-nom { min-height: 44px; display: inline-flex; align-items: center; }
  .actions-une { grid-template-columns: 44px; }
}

@media (max-width: 700px) {
  .caracs-une-colonne .caracs-entete,
  .caracs-une-colonne .caracs-ligne { grid-template-columns: 1fr; }
}

/* --- Champs sans objet pour la demande en cours -------------------------- */
/*
   GRISÉS ET INACCESSIBLES, mais toujours là. Un ajout n'a pas d'« avant », une
   fermeture ne demande pas de nouveaux horaires — ces champs ne doivent pas se
   remplir, mais les faire DISPARAÎTRE donnait l'impression que les valeurs
   avaient été perdues. On les montre, on les verrouille.

   ⚠️ Le verrouillage lui-même est en JavaScript, et il n'est pas décoratif :
      readonly sur les champs de saisie (qui restent transmis), disabled plus
      un champ caché sur les radios et les listes (qui ne le seraient pas).
      Sans cela, changer de demande effacerait la saisie en silence.
*/
.champ-inactif {
  opacity: .45;
  background: var(--fond-doux);
  cursor: not-allowed;
}
.champ-inactif:focus { outline: none; }

/* La ligne entière quand aucun de ses deux champs ne sert. */
.carac-inactive .caracs-libelle { opacity: .45; }

/* Les boutons radio d'un groupe verrouillé : le curseur le dit avant le clic. */
.champ-inactif .radio-option { cursor: not-allowed; }

/* --- Import des lieux ---------------------------------------------------- */
/*
   Un bloc dépliable DANS l'écran, et non une page à part : on garde sous les
   yeux la liste qu'on est en train de compléter. Pas d'assistant en trois
   pages — on télécharge le modèle, on le renvoie, on voit ce qui va se passer,
   on confirme.
*/
.import {
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  margin-bottom: 1rem;
}
.import-titre {
  cursor: pointer;
  padding: .8rem 1.1rem;
  font-weight: 600;
  color: var(--titre);
  list-style-position: inside;
}
.import[open] .import-titre { border-bottom: 1px solid var(--bord); }
.import-corps { padding: 1rem 1.1rem 1.2rem; }

.import-etapes { margin: .8rem 0 0; padding-left: 1.2rem; }
.import-etapes > li { margin-bottom: 1rem; }
.import-liens { display: flex; gap: .5rem; flex-wrap: wrap; margin: .5rem 0 .3rem; }

.import-resume { margin: .6rem 0; }
.import-erreurs { margin: .4rem 0 0; padding-left: 1.1rem; }
.import-erreurs li { margin-bottom: .2rem; }

.import-apercu { margin-top: 1rem; border-top: 1px solid var(--bord); padding-top: 1rem; }
.import-actions { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .9rem; }

@media (pointer: coarse) {
  .import-titre { min-height: 44px; display: flex; align-items: center; }
}
@media (max-width: 700px) {
  .import-liens, .import-actions { flex-direction: column; align-items: stretch; }
  .import-liens .bouton, .import-actions .bouton { width: 100%; text-align: center; }
}

/* --- Carte des sites ----------------------------------------------------- */
/*
   Leaflet est HÉBERGÉ dans /assets/leaflet/, pas appelé chez un tiers : un
   script distant est un point de panne qu'on ne maîtrise pas, et il apprend à
   son hébergeur qui visite l'extranet et quand.

   Les tuiles viennent de la Géoplateforme de l'IGN. L'attribution posée par
   carte.js est EXIGÉE par ses conditions d'usage — ne pas la masquer.
*/
.carte-sites-cadre { margin-bottom: 1.5rem; }

.carte-sites {
  height: 22rem;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
  background: var(--fond-doux);
  z-index: 0; /* reste sous les panneaux et les messages */
}
.carte-sites-fiche { height: 16rem; }

.carte-sites-absente {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--texte-doux);
  margin: 0;
}

/* La même information qu'à la carte, en texte : c'est le seul chemin pour qui
   navigue au clavier ou au lecteur d'écran. Ce n'est pas un doublon. */
.carte-sites-liste {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: .4rem 1.4rem;
  margin: .6rem 0 0;
  padding: 0;
  font-size: .9rem;
}
.carte-sites-liste .aide { display: inline; margin-left: .3rem; }

.site-liens { display: flex; gap: .5rem; flex-wrap: wrap; margin: 0 0 1.2rem; }

@media (pointer: coarse) {
  .carte-sites-liste a { min-height: 44px; display: inline-flex; align-items: center; }
}
@media (max-width: 700px) {
  .carte-sites { height: 16rem; }
  .site-liens { flex-direction: column; }
  .site-liens .lien-carto { text-align: center; }
}

/* --- Attribution nominative des accès ------------------------------------
   ⚠️ PAS DE TABLEAU ICI, à dessein. Un dossier compte une à cinq personnes :
      cinq colonnes d'en-tête au-dessus d'une seule ligne étalaient trois mots
      sur toute la largeur de l'écran, avec des vides partout. Une liste de
      lignes serrées se lit d'un coup d'œil, et tient telle quelle sur un
      téléphone — sans le repli en fiches qu'exigent les vrais tableaux.
   ------------------------------------------------------------------------ */
/* ⚠️ CE BLOC NE SUIT PAS LA LARGEUR DE LA PAGE, et c'est délibéré.
      L'écran d'un dossier est en « page-large » (120rem = 1920 px) parce que
      les tableaux de lieux et de documents en profitent. Pas celui-ci : une à
      cinq personnes étalées sur 1920 px, c'est un nom collé à gauche et son
      droit échoué à l'autre bout, séparés par un mètre de vide. Le geste doit
      rester à portée de l'œil de l'information qu'il modifie. */
.acces { margin-bottom: 1.5rem; max-width: 58rem; }

/* Le bandeau qui expliquait dans quel SENS le geste allait agir a disparu avec
   le modèle de droits qui le rendait nécessaire (08/08/2026). Un écran qu'il
   faut lire avant d'y toucher est un écran mal conçu. */

.acces-carte { padding: 0; }

.acces-liste { list-style: none; margin: 0; padding: 0; }

.acces-ligne {
  display: grid;
  /* pastille · qui · droit · dernière visite · geste
     ⚠️ LARGEURS FIXES, et non « auto » : chaque ligne est une grille
        INDÉPENDANTE. En auto, la colonne « droit » se calait sur le contenu de
        SA ligne, si bien que les sélecteurs se retrouvaient à trois abscisses
        différentes selon la longueur du nom au-dessus. L'œil lit une liste par
        colonnes ; des colonnes qui zigzaguent la rendent illisible. */
  grid-template-columns: 2.1rem minmax(9rem, 19rem) 12.5rem auto 1fr;
  align-items: center;
  gap: .2rem .9rem;
  padding: .7rem .9rem;
  border-bottom: 1px solid var(--bord);
}
.acces-ligne:last-child { border-bottom: 0; }

.acces-pastille {
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  background: var(--marque);
  color: #fff;
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .02em;
  flex: none;
}

.acces-qui { display: flex; flex-direction: column; min-width: 0; }
.acces-nom { font-weight: 600; }
/* Un courriel long ne doit pas pousser la ligne au-delà de l'écran. */
.acces-mail {
  color: var(--texte-doux);
  font-size: .86rem;
  overflow-wrap: anywhere;
}

.acces-droit { display: flex; flex-direction: column; gap: .15rem; }
.acces-droit-fige { font-size: .92rem; }
.acces-droit .select-nature { width: 100%; }

/* Une personne sans accès reste DANS la liste — c'est un résultat, pas un vide
   à cacher : on vient ici pour savoir qui voit le dossier ET qui ne le voit
   pas. Elle s'efface visuellement, sans disparaître. */
.acces-ligne-sans { background: var(--fond-doux); }
.acces-ligne-sans .acces-nom { font-weight: 500; }
.acces-ligne-sans .acces-pastille { background: var(--texte-doux); opacity: .55; }
.select-eteint { color: var(--texte-doux); }
.acces-hors { color: var(--texte-doux); font-style: italic; }

.acces-vu {
  color: var(--texte-doux);
  font-size: .82rem;
  white-space: nowrap;
}

/* Le bouton « Retirer » et la barre « Donner accès à » ont disparu le
   08/08/2026 : régler quelqu'un sur « Aucun accès » remplace les deux, et
   supprime un geste dont le sens s'inversait selon le dossier. */

.acces-vide,
.acces-note {
  margin: 0;
  padding: .9rem;
  color: var(--texte-doux);
  font-size: .9rem;
}
.acces-note {
  border-top: 1px solid var(--bord);
  background: var(--fond-doux);
  border-radius: 0 0 var(--rayon) var(--rayon);
}
.acces-note strong { color: var(--texte); }

@media (max-width: 760px) {
  /* Zones tactiles : la ligne s'empile, le menu passe en pleine largeur. */
  .acces-ligne {
    grid-template-columns: auto 1fr;
    gap: .35rem .7rem;
    padding: .8rem;
  }
  .acces-droit,
  .acces-vu { grid-column: 2; }
  /* 44 px de haut : le menu est la SEULE commande de cet écran sur téléphone,
     et un menu qu'on rate au doigt donne un droit qu'on ne voulait pas. */
  .acces-droit .select-nature { min-height: 44px; }
}

/* --- L'événement d'un dossier --------------------------------------------
   ⚠️ LA PHASE EST CALCULÉE PAR LE SERVEUR (Evenement::etat), jamais ici : le
      CSS ne fait que la vêtir. Une règle de dates qui vivrait aussi dans la
      feuille de style donnerait deux vérités le jour de la bascule.
   ------------------------------------------------------------------------ */
.bandeau-evenement {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .35rem .75rem;
  margin: -.4rem 0 1.1rem;
  padding: .55rem .85rem;
  border-left: 3px solid var(--bord);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  background: var(--fond-doux);
  max-width: 58rem;
}
.bandeau-nom { font-weight: 650; }
.bandeau-phase { font-size: .92rem; }
.bandeau-dates { color: var(--texte-doux); font-size: .86rem; }

/* Trois phases, trois couleurs — et la plus voyante est celle qui appelle une
   action : un événement qui ouvre bientôt. Un événement terminé s'efface, il
   n'a plus rien à réclamer. */
.bandeau-avant    { border-left-color: var(--alerte); background: var(--alerte-fond); }
.bandeau-avant .bandeau-phase { color: var(--alerte); font-weight: 650; }
.bandeau-en-cours { border-left-color: var(--succes); background: var(--succes-fond); }
.bandeau-en-cours .bandeau-phase { color: var(--succes); font-weight: 650; }
.bandeau-termine  { border-left-color: var(--bord); }
.bandeau-termine .bandeau-phase,
.bandeau-sans-date .bandeau-phase { color: var(--texte-doux); }

/* Dans la liste des dossiers de l'administration. */
.etiquette-evenement {
  display: inline-block;
  margin-top: .2rem;
  padding: .08rem .45rem;
  border-radius: 999px;
  background: var(--info-fond);
  color: var(--info);
  font-size: .76rem;
  font-weight: 600;
}

/* --- Régler l'événement depuis la fiche du dossier -----------------------
   ⚠️ CES QUATRE CLASSES ONT ÉTÉ LIVRÉES SANS UNE LIGNE DE STYLE le 08/08.
      Le bloc s'affichait — donc rien ne paraissait cassé —, mais le bouton
      était un bouton brut de navigateur au milieu d'un bandeau soigné.
      « Le bouton moche », dixit l'auteur. Un contrôle surveille désormais
      qu'aucune classe de gabarit ne soit orpheline.
   ------------------------------------------------------------------------ */
.bandeau-regler {
  margin-left: auto;
  border: 1px solid var(--bord);
  background: var(--fond);
  color: var(--texte-doux);
  border-radius: var(--rayon);
  padding: .3rem .7rem;
  font: inherit;
  font-size: .84rem;
  cursor: pointer;
  white-space: nowrap;
}
.bandeau-regler:hover { border-color: var(--accent); color: var(--accent); }
.bandeau-regler:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.evenement-forme {
  max-width: 58rem;
  margin: -.6rem 0 1.2rem;
  padding: .9rem 1rem;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--fond-doux);
}
.evenement-forme .aide { margin: .5rem 0 0; }

/* Le nom prend la place qu'il faut, les deux dates restent compactes : une
   date occupe une largeur connue, un nom de salon non. */
.evenement-champs {
  display: grid;
  grid-template-columns: minmax(12rem, 1fr) 10rem 10rem;
  gap: .75rem;
  align-items: end;
}
.evenement-champs .champ { margin: 0; }

.evenement-gestes {
  display: flex;
  justify-content: flex-end;
  gap: .6rem;
  margin-top: .9rem;
}

@media (max-width: 760px) {
  .evenement-champs { grid-template-columns: 1fr; }
  /* Zone tactile : sur téléphone, ces trois commandes sont les seules de
     l'écran, et un bouton raté enregistre autre chose que ce qu'on voulait. */
  .bandeau-regler { width: 100%; margin: .4rem 0 0; padding: .6rem; }
  .evenement-gestes { flex-direction: column-reverse; }
  .evenement-gestes .bouton { width: 100%; text-align: center; padding: .65rem; }
}

/* Le droit qui vient d'AILLEURS, et qui ne se retire pas sur cet écran.
   ⚠️ Sans cette mention, régler quelqu'un sur « Aucun accès » afficherait un
      menu éteint pendant qu'il continue d'entrer par le site : on croirait la
      porte fermée. C'est le mensonge d'écran le plus coûteux — celui qu'on ne
      peut pas voir. */
.acces-ailleurs {
  color: var(--alerte);
  font-size: .78rem;
  line-height: 1.35;
}

/* --- L'écran des POI ------------------------------------------------------
   ⚠️ Écrit EN MÊME TEMPS que le gabarit, pas après. Le 08/08, un bloc livré
      sans une ligne de style s'affichait sans paraître cassé — seul un bouton
      brut trahissait le manque, et c'est l'auteur qui l'a vu.
   ------------------------------------------------------------------------ */

/* L'origine et la récidive se lisent AVANT le nom du geste : un stand créé par
   un exposant n'appelle pas le même traitement qu'un hall du parc. */
.etiquette-tiers {
  background: var(--alerte-fond);
  color: var(--alerte);
}
.etiquette-recidive {
  background: var(--erreur-fond);
  color: var(--erreur);
  font-variant-numeric: tabular-nums;
}
.etiquette-retour {
  background: var(--info-fond);
  color: var(--info);
}

/* L'état de référence : affiché, jamais saisi. Il doit donc RESSEMBLER à du
   texte, pas à un champ — sans quoi on essaie d'y écrire, et l'on ne comprend
   pas pourquoi rien ne se passe. */
.carac-reference {
  display: block;
  padding: .45rem .1rem;
  color: var(--texte-doux);
  font-size: .92rem;
  line-height: 1.4;
  overflow-wrap: anywhere;
  min-height: 1.2rem;
}
.carac-reference:empty::before { content: '—'; }

/* Le bloc du prestataire — visuellement séparé de ce que le client remplit. */
.bloc-prestataire {
  margin: 1.2rem 0 0;
  padding: .9rem 1rem;
  border: 1px solid var(--bord);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  background: var(--fond-doux);
}
.bloc-prestataire legend {
  padding: 0 .4rem;
  font-size: .82rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--accent);
}

/* Une case à cocher qui tient sur la ligne de son libellé. */
.case-en-ligne {
  display: flex;
  align-items: center;
  gap: .45rem;
  /* 44 px de zone tactile : la case seule fait 13 px, on clique le libellé. */
  min-height: 44px;
  cursor: pointer;
}
.case-en-ligne input { width: 1.05rem; height: 1.05rem; flex: none; }

/* L'aperçu du QR-Code, dans la fiche d'un POI.
   ⚠️ Une taille FIXE et modeste : un QR-Code se vérifie d'un coup d'œil, il n'a
      pas à occuper la moitié du panneau. Et « image-rendering: pixelated »
      garde ses modules nets à l'agrandissement — un QR-Code lissé devient
      illisible pour un téléphone. */
.qr-apercu {
  display: flex;
  align-items: flex-end;
  gap: .8rem;
  margin: .4rem 0 .6rem;
}
.qr-apercu img {
  width: 128px;
  height: 128px;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: .3rem;
  image-rendering: pixelated;
}

/* --- L'aperçu d'import : un vocabulaire À PART -----------------------------
   ⚠️ NÉ D'UNE CONFUSION SIGNALÉE LE 09/08. L'aperçu réutilisait les couleurs
      et les mots des TYPES DE DEMANDE : « Création » en vert d'ajout, « Mise à
      jour » en bleu de modification. Or ces deux notions n'ont RIEN à voir :

        * le TYPE dit ce qu'on demande à EVIDRA de faire dans Waze ou Google
          Maps — ajouter, modifier, fermer, supprimer un point ;
        * l'EFFET D'IMPORT dit ce que le fichier fait à la LISTE — ajouter une
          ligne, en compléter une existante.

      Un POI importé comme « création » s'affichait ensuite « Modification » en
      bleu, parce que c'était son type de demande. Deux bleus, deux sens, et
      l'on s'y perd — dixit l'auteur, qui avait raison.

      ⇒ Des teintes NEUTRES, volontairement hors de la palette des types.
   ------------------------------------------------------------------------ */
.etat-import-neuf,
.etat-import-maj {
  background: var(--fond-doux);
  color: var(--texte-doux);
  border: 1px solid var(--bord);
}
.etat-import-neuf { border-left: 3px solid var(--accent); }
.etat-import-maj  { border-left: 3px solid var(--bord); }

/* La zone de dépôt de l'import, pendant le survol d'un fichier glissé.
   ⚠️ Un retour VISIBLE est indispensable : sans lui, on ne sait pas si la zone
      accepte, et l'on relâche à côté — le navigateur ouvre alors le fichier à
      la place de la page, et la saisie en cours est perdue. */
.import-survol {
  outline: 2px dashed var(--accent);
  outline-offset: -6px;
  background: var(--info-fond);
}

/* --- Mentions légales ---------------------------------------------------- *
   Une page de LECTURE, et non un écran de travail : elle garde donc la
   largeur confortable (~68 rem) et n'est pas inscrite dans la liste des
   écrans larges de base.php.

   ⚠️ Le tableau des informations est un <dl>, pas un tableau HTML. Sur
      téléphone, un tableau à deux colonnes se replie mal — l'intitulé et sa
      valeur se retrouvent sur deux lignes de largeurs différentes. Le <dl>
      en grille bascule proprement d'une à deux colonnes, et c'est le même
      balisage dans les deux cas.
   ------------------------------------------------------------------------ */
.mentions { max-width: 46rem; }

.mentions section { margin-bottom: 2.25rem; }
.mentions p       { margin: 0 0 .8rem; line-height: 1.6; }
.mentions ul      { margin: 0 0 .9rem; padding-left: 1.15rem; line-height: 1.6; }
.mentions li      { margin-bottom: .45rem; }
.mentions h3      { font-size: .98rem; margin: 1.4rem 0 .45rem; color: var(--titre); }

/* Le sommaire : des ancres, pas une navigation d'application. Il évite de
   faire défiler toute la page pour atteindre « Vos droits », qui est la seule
   section que quelqu'un vient chercher dans l'urgence. */
.mentions-sommaire {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem .5rem;
  margin: 1rem 0 2rem;
  padding: .75rem;
  background: var(--fond-doux);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
}
.mentions-sommaire a {
  /* 44 px de haut : la cible se vise au doigt, pas seulement à la souris. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 .7rem;
  border-radius: var(--rayon);
  background: var(--fond);
  border: 1px solid var(--bord);
  font-size: .88rem;
  text-decoration: none;
}
.mentions-sommaire a:hover,
.mentions-sommaire a:focus-visible { background: var(--info-fond); }

/* Le saut d'ancre place la section pile sous l'en-tête ; sans cela, le titre
   visé se retrouve masqué par la barre du haut. */
.mentions section[id] { scroll-margin-top: 1.5rem; }

.mentions-liste { margin: 0; }
.mentions-ligne {
  display: grid;
  grid-template-columns: 14rem 1fr;
  gap: .35rem 1rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--bord);
}
.mentions-ligne:last-child { border-bottom: 0; }
.mentions-ligne dt { color: var(--texte-doux); font-size: .88rem; }
.mentions-ligne dd { margin: 0; }

/* Sur téléphone, deux colonnes de 14 rem ne tiennent pas : l'intitulé passe
   au-dessus de sa valeur. */
@media (max-width: 40rem) {
  .mentions-ligne { grid-template-columns: 1fr; gap: .1rem; padding: .6rem 0; }
}

/* Le lien légal du pied de page. */
.pied-legal { margin: 0; }
.pied-legal a {
  display: inline-flex;
  align-items: center;
  color: var(--texte-doux);
}
/* ⚠️⚠️ LE « min-height: 44px » A ÉTÉ DÉPLACÉ DANS @media (pointer: coarse) —
   il était ici, donc appliqué AUSSI à la souris. Mesuré le 13/08 à 1920x900 :
   le pied faisait 128 px, soit 14,2 % de la fenêtre et 1,66 fois l'en-tête,
   pour deux lignes de texte. Une hauteur de confort TACTILE imposée à un
   pointeur fin, exactement comme le `touch-action` hors media query corrigé le
   10/08. ⇒ Le doigt dimensionne, pas la place disponible. */

/* --- QR-Code : la vignette du tableau et son agrandissement --------------- *
   Signalé à l'usage le 09/08 : le tableau ne proposait qu'un lien
   « Télécharger », et l'aperçu de l'image n'existait QUE dans la zone de dépôt,
   réservée au prestataire. Un client ne pouvait voir son QR-Code nulle part.

   ⚠️ « image-rendering: pixelated » est INDISPENSABLE ici : un QR-Code lissé
      par l'interpolation du navigateur devient illisible pour un téléphone.
      C'est la même raison qu'au-dessus, et il faut le redire sur chaque règle —
      la propriété ne s'hérite pas d'un autre sélecteur.
   ------------------------------------------------------------------------ */
.qr-vignette {
  /* 44 px : la cible se vise au doigt. La vignette elle-même fait 32 px, le
     reste est de la marge cliquable — cf. la règle de mobilité du projet. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: var(--rayon);
}
.qr-vignette img {
  width: 32px;
  height: 32px;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--bord);
  border-radius: 4px;
  padding: 2px;
  image-rendering: pixelated;
}
.qr-vignette:hover img,
.qr-vignette:focus-visible img { border-color: var(--accent); }

.qr-vue-corps { text-align: center; }

.qr-vue-image img {
  /* Aussi grand que le panneau le permet : on le scanne à l'écran, parfois de
     loin, parfois sur un téléphone tenu à bout de bras. */
  width: min(20rem, 100%);
  height: auto;
  aspect-ratio: 1;
  object-fit: contain;
  background: #fff;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: .6rem;
  image-rendering: pixelated;
}

/* Le lien téléphonique des mentions : une cible qu'on vise au doigt.
   Cette page se consulte surtout depuis un téléphone — c'est même le seul
   endroit du site où l'on cherche « comment les joindre ». Un lien de la
   hauteur d'une ligne de texte se rate une fois sur deux. */
.mentions-ligne dd a[href^="tel:"] {
  display: inline-block;
  padding: .55rem 0;
  margin: -.55rem 0;
}

/* Ce qu'on retire de la VUE sans le retirer de la PAGE.
   ⚠️ PAS de « display:none » ni de « visibility:hidden » : l'un et l'autre
      retirent l'élément de l'ordre de tabulation et du lecteur d'écran — or
      c'est exactement pour EUX qu'on le garde. On le réduit à un point
      transparent, ce qui le laisse lisible et focusable.

   ⚠️⚠️ CETTE RÈGLE MANQUAIT. « visuellement-cache » était employée dans
      pois.js pour nommer les cases « Modifié » et « Confirmé à l'intention
      des lecteurs d'écran — et n'était définie NULLE PART. Le mot destiné à
      être entendu s'affichait donc à côté de chaque case. Le contrôle des
      classes sans style ne regardait que les GABARITS, pas le JS : il est
      désormais étendu à celui-ci. */
.visuellement-cache {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.visuellement-cache:focus-visible + .bouton {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Un <label> habillé en bouton doit se comporter comme un bouton au survol. */
label.bouton { cursor: pointer; }

/* --- La clôture du cycle -------------------------------------------------- *
   Le détail des POI concernés : replié, mais présent. Personne ne relit douze
   noms à chaque clôture, et pourtant il faut pouvoir vérifier avant de
   trancher — c'est le seul moment où l'on a la liste sous les yeux.
   ------------------------------------------------------------------------ */
.cloture-detail {
  margin: .8rem 0 .4rem;
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--fond-doux);
}
.cloture-detail > summary {
  /* 44 px : la cible se vise au doigt, comme tout ce qui se clique ici. */
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 .8rem;
  cursor: pointer;
  font-size: .9rem;
}
.cloture-liste {
  margin: 0;
  padding: 0 .8rem .8rem 2rem;
  max-height: 16rem;
  /* Douze POI passent ; deux cents ne doivent pas pousser les boutons de
     décision hors de l'écran. */
  overflow-y: auto;
}
.cloture-liste li { margin-bottom: .35rem; line-height: 1.5; }
.cloture-liste .aide { display: inline; margin-left: .3rem; }

/* Les deux décisions : chacune porte son explication sous son titre, et la
   zone cliquable couvre le tout — viser un bouton radio de 13 px au doigt est
   un exercice d'adresse.

   ⚠️ CE BLOC N'EST PAS DANS UN « .champ », et c'est délibéré :
      « .champ label » pèse 0,1,1 en spécificité et écrase « .case »
      (0,1,0), ce qui fait perdre au label sa disposition en flex — le bouton
      radio se retrouve seul au-dessus de son propre libellé. */
.cloture-choix {
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: .3rem .9rem .9rem;
  margin: 0 0 .4rem;
}
.cloture-choix legend {
  padding: 0 .35rem;
  font-size: .9rem;
  font-weight: 600;
  color: var(--titre);
}
.cloture-choix .case {
  /* La zone cliquable couvre le titre ET son explication : viser un bouton
     radio de 13 px au doigt est un exercice d'adresse. */
  padding: .5rem .4rem;
  border-radius: var(--rayon);
}
.cloture-choix .case:hover { background: var(--fond-doux); }
/* Le chevron « > » importe : sans lui, la regle attrape AUSSI le strong
   imbrique dans l explication (« Le dossier reste ouvert »), qui se retrouve
   seul sur sa ligne au milieu d une phrase. */
.cloture-choix .case > span > strong { display: block; color: var(--titre); }
.cloture-choix .case .aide { margin-top: .15rem; }

/* Un champ qui n'est qu'une case à cocher.
   ⚠️ PAS « .champ » : `.champ label { display: block }` pèse 0,1,1 et écrase
      `.case` (0,1,0), ce qui retire au label sa disposition en flex — la case
      se retrouve seule AU-DESSUS de son libellé. Piège constaté à la capture
      sur l'écran de clôture le 09/08, et qui se reproduit partout où l'on pose
      une case dans un formulaire. */
.champ-coche { margin-bottom: 1.1rem; }
.champ-coche .case > span { display: block; }
.champ-coche .case strong { display: block; color: var(--titre); font-size: .9rem; }
.champ-coche .case .aide { display: block; margin-top: .15rem; }

/* Le contact qui commande et qui paie.
   ⚠️ Teinte NEUTRE et non un vert de validation : « client » n'est pas un
      état d'avancement, c'est une qualité. Emprunter la couleur du succès
      ferait lire « tout va bien » là où il n'y a rien à valider. */
.etiquette-client {
  background: var(--info-fond);
  color: var(--info);
  border: 1px solid var(--info);
}

/* La fonction et la société, sous le nom dans la liste des contacts. */
.admin-client-qui {
  display: block;
  font-size: .82rem;
  color: var(--texte-doux);
  font-weight: 400;
  margin-top: .1rem;
}

/* =========================================================================
   L'ACCUEIL — la mosaïque de tuiles
   -------------------------------------------------------------------------
   Références montrées par l'auteur le 09/08 : le tableau de bord de Dendreo
   (mosaïque, poignée de déplacement, gros chiffres, listes compactes) et un
   portail Jahia (carte « qui je suis »). Ce qu'il en retient : moderne.

   ⚠️ CE QU'ON N'A PAS COPIÉ : leur palette. La charte EVIDRA est mesurée
      (#252C47, contraste 13,73:1 AAA) et elle tient ; emprunter le corail de
      Dendreo produirait un écran qui ne ressemble plus au reste du site. Ce
      qu'on reprend, c'est la STRUCTURE — grille, respiration, hiérarchie —
      pas les couleurs de quelqu'un d'autre.
   ========================================================================= */

.accueil-tete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.2rem;
}
.accueil-tete h1 { margin-bottom: .15rem; }
/* ⚠️ UNE LIGNE, PAS UN ENCART. Un bandeau coloré pleine largeur en tête d'un
   écran qu'on ouvre dix fois par jour est un bruit permanent — et il donne à
   la page l'air d'un logiciel qui s'excuse. Ces deux gestes ont besoin d'être
   dits une fois ; ils n'ont pas besoin d'être criés. */
.accueil-aide {
  margin: -.4rem 0 1rem;
  font-size: .85rem;
  color: var(--texte-doux);
}

/* LA GRILLE — DOUZE COLONNES FIXES, ET DES COORDONNÉES EXPLICITES.

   ⭐⭐⭐ CE QUI A CHANGE, ET POURQUOI. La grille était en « auto-fit », donc en
        FLUX : les tuiles se placaient a la suite, et leur nombre de colonnes
        variait avec la largeur de la fenetre — cinq colonnes mesurées sur
        l'écran de l'auteur, alors que les largeurs s'arrêtaient a trois. Une
        tuile n'avait aucune position propre, seulement un rang.

        Il l'a éprouvé en voulant mettre une tuile SOUS une autre : « c'est
        impossible, on ne peut mettre aucune tuile a gauche d'une ligne
        donnée ». C'était exact, et c'était structurel.

   ⚠️ `grid-auto-rows` FIXE LA HAUTEUR D'UNE LIGNE : c'est ce qui rend le
      redimensionnement prévisible. Une tuile de 4 lignes fait exactement le
      double d'une tuile de 2, et deux tuiles côte à côte s'alignent au pixel.
   ------------------------------------------------------------------------ */
.tuiles {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-auto-rows: 92px;
  gap: 1rem;
}

/* La position vient des variables posées par le serveur. « span » plutôt qu'une
   ligne de fin : la tuile occupe w colonnes a partir de x, quoi qu'il arrive. */
/* ⚠️⚠️ « .tuile[data-x] » ET NON « .tuile » — CE N'EST PAS UN DÉTAIL, C'EST CE
        QUI ÉVITE UN ÉCRAN VIDE.

        Écrite sur « .tuile » tout court, cette règle s'applique AUSSI aux
        tuiles dépourvues de coordonnées — et « var(--x, 0) » les envoie alors
        toutes en colonne 1, ligne 1. Elles se superposent EXACTEMENT, et l'on
        n'en voit plus qu'une : la dernière dessinée.

        C'est arrivé en vrai le 09/08, à la mise en ligne : le CSS neuf était
        déposé, le gabarit non. L'auteur : « J'ai tout perdu. J'ai plus que la
        tuile Mes Dossiers ! » — et son utilisatrice n'avait plus que la carte,
        c'est-à-dire chacun la dernière tuile de sa propre liste.

        ⇒ Avec le sélecteur d'attribut, une tuile sans coordonnées est placée
          AUTOMATIQUEMENT par la grille : la page reste utilisable, seulement
          moins bien rangée. Une mise en ligne partielle doit DÉGRADER, jamais
          effacer. */
.tuile[data-x] {
  grid-column: calc(var(--x, 0) + 1) / span var(--w, 4);
  grid-row: calc(var(--y, 0) + 1) / span var(--h, 3);
}

/* Le comportement de secours : sans coordonnées, la tuile prend une largeur
   raisonnable et se place à la suite des autres. */
.tuile:not([data-x]) { grid-column: span 4; }

/* ⚠️⚠️ AU TELEPHONE, LA GRILLE REPREND LA MAIN — et c'est précisément pour
   cela que les coordonnées passent par des VARIABLES et non par un style en
   ligne : ici, on les ignore. Une tuile posee en colonne 9 deborderait sinon
   de l'écran, sans rien pour la rattraper.

   Les tuiles s'empilent alors dans l'ordre du document, trié par ligne puis par
   colonne côté serveur — donc dans l'ordre de lecture. */
/* ⚠️⚠️ AU TÉLÉPHONE, LES TUILES PRENNENT TOUTE LA LARGEUR — MAIS GARDENT LEUR
        LIGNE, ET C'EST TOUTE LA DIFFÉRENCE.

        La première version écrivait aussi « grid-row: auto » : les coordonnées
        étaient alors totalement ignorées, la grille empilait dans l'ordre du
        document, et le geste ne pouvait plus rien changer. J'avais donc bloqué
        déplacement et redimensionnement sous ce seuil — autrement dit, SUPPRIMÉ
        LA FONCTION sur téléphone, alors que l'auteur avait demandé dès le
        départ qu'elle y marche « comme un widget Android ».

        En gardant « grid-row », la ligne continue de commander l'ordre vertical
        et la hauteur : on peut donc toujours déplacer une tuile vers le haut ou
        vers le bas, et l'agrandir en hauteur. Seule la largeur n'a plus de sens
        — une colonne sur douze ferait trente pixels.
   ------------------------------------------------------------------------ */
@media (max-width: 60rem) {
  .tuiles {
    grid-template-columns: 1fr;
    /* ⚠️⚠️ « minmax » ET NON UNE HAUTEUR FIXE : sur une seule colonne, une
       liste de huit dossiers ne tient pas dans trois lignes de 92 px, et
       « overflow: hidden » la COUPERAIT sans rien dire — on perdrait des
       entrées sans qu'aucun signe ne l'indique. La ligne garde donc sa hauteur
       minimale, mais grandit si le contenu l'exige.
       ⚠️ Conséquence assumée : le pas vertical n'est plus constant, donc la
          ligne visée pendant un déplacement est approchée. L'aimantation
          rattrape — sur un seul rang, « y » ne sert plus que d'ordre. */
    grid-auto-rows: minmax(92px, auto);
  }
  /* ⚠⚠ « grid-row: auto » EST INDISPENSABLE, et son absence a fait
     DISPARAÎTRE des tuiles. Deux tuiles côte à côte sur grand écran partagent
     la même LIGNE (même y, colonnes différentes). Ramenées toutes deux à la
     pleine largeur mais gardant leur ligne, elles se retrouvent EXACTEMENT
     l'une sur l'autre — et l'on n'en voit plus qu'une. Vu à la capture :
     « Mon espace » et « À traiter » avaient purement disparu.
     ⇒ Sur un seul rang, c'est l'ordre du DOCUMENT qui commande, et le serveur
       le trie déjà par ligne puis colonne — donc dans l'ordre de lecture. */
  .tuile[data-x],
  .tuile:not([data-x]) {
    grid-column: 1 / -1;
    grid-row: auto;
  }
}

/* La carte occupe toute la hauteur que la tuile lui laisse : c'est la tuile qui
   commande, plus une hauteur écrite a part.
   ⚠️ « min-height: 0 » est indispensable : un enfant de flex refuse par défaut
      de rétrécir sous la taille de son contenu, et déborde de la tuile. */
/* ⭐⭐⭐ LE CORPS DÉFILE — signalé par l'auteur le 13/08 : « on ne voit que
   2 sites sur 4 ». Mesuré sur l'accueil, à la taille PAR DÉFAUT des tuiles et
   sans que personne n'ait rien redimensionné : « Mes sites » disposait de
   479 px pour 567 px de contenu — 88 px purement coupés par le `overflow:
   hidden` de la tuile, et deux sites invisibles. « À traiter » et « Mes
   dossiers » en perdaient 11 et 9.

   ⚠️ LE RAISONNEMENT D'ORIGINE NE TENAIT QUE POUR UN GESTE VOLONTAIRE. Il
      disait : celui qui rétrécit une tuile voit ce qu'il perd et peut la
      ragrandir. Mais ici personne n'avait rien rétréci — c'est le réglage
      d'usine qui cachait la moitié du contenu, et RIEN ne l'annonçait. Une
      information tronquée en silence se lit comme une information complète :
      « j'ai 2 sites » au lieu de « j'en vois 2 sur 4 ».

   ⇒ La tuile garde son `overflow: hidden` (sans lui elle déborderait sur sa
     voisine), et c'est le CORPS qui défile. L'ascenseur est à la fois le moyen
     d'atteindre le reste et le signe qu'il y a un reste. */
.tuile-corps {
  display: flex;
  flex-direction: column;

  /* ⚠️⚠️ CE `flex: 1 1 auto` MANQUAIT, ET C'EST TOUT LE DÉFAUT SIGNALÉ PAR
     L'AUTEUR : « la carte ne change jamais de taille, quelle que soit la
     dimension de la tuile ». Mesuré : en agrandissant la tuile de 524 à
     848 px, le CORPS restait à 387 — donc la carte à 355.

     ⭐ Et l'erreur de diagnostic était toute prête : Leaflet ne voit pas son
       cadre changer, il y a une fonction faite exprès pour l'en avertir, tout
       désignait `invalidateSize()`. Elle était appelée, et correctement. Le
       conteneur, lui, n'avait jamais grandi : il n'y avait rien à
       revalider. ⇒ Mesurer la CHAÎNE au complet — tuile, corps, carte — plutôt
       que le seul maillon qu'on soupçonne.

     Sans cette ligne, un enfant de flex prend la hauteur de son CONTENU et
     ignore la place disponible. La tuile grandissait donc autour d'un corps
     qui ne bougeait pas. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;

  /* ⚠️⚠️ AUCUNE BARRE GRISE. Signalé par l'auteur : « très moche pour du 2026,
     et presque ridiculement inutile ». Il a raison — sur « Mes dossiers » elle
     s'affichait pour 11 px. Une barre permanente sur une carte de 200 px de
     haut coûte plus en laideur qu'elle ne rapporte en information.
     Le contenu reste atteignable : molette, doigt, clavier. Ce qui disparaît,
     c'est le rail, pas le défilement. */
  scrollbar-width: none;

  /* ⭐ CE QUI REMPLACE LA BARRE : une ombre de défilement. Elle n'apparaît que
     s'il reste du contenu de ce côté et s'efface quand on atteint le bout —
     c'est le dégradé fixé au conteneur qui masque celui, attaché au contenu,
     qui défile avec lui. Technique CSS pure, sans une ligne de JavaScript et
     sans rien à mesurer : le navigateur s'en charge.
     ⚠️ Elle est INDISPENSABLE sur Mac et iOS, où la barre est de toute façon
        invisible tant qu'on ne fait pas défiler : sans elle, on croit voir
        tout le contenu — exactement le défaut qu'on vient de corriger. */
  background:
    linear-gradient(var(--fond) 30%, transparent) top / 100% 24px no-repeat local,
    linear-gradient(transparent, var(--fond) 70%) bottom / 100% 24px no-repeat local,
    radial-gradient(farthest-side at 50% 0, rgba(16, 41, 74, .13), transparent) top / 100% 10px no-repeat scroll,
    radial-gradient(farthest-side at 50% 100%, rgba(16, 41, 74, .13), transparent) bottom / 100% 10px no-repeat scroll;
}
.tuile-corps::-webkit-scrollbar { width: 0; height: 0; }
.carte-sites-tuile { flex: 1 1 auto; min-height: 12rem; }

/* ---------------------------------------------------------------------------
   LA TUILE DES SITES — la carte à côté de la liste, à la forme des données
   ---------------------------------------------------------------------------
   ⭐⭐⭐ LA RÉPARTITION EST CALCULÉE, PAS CHOISIE. Arbitrage de l'auteur le
   13/08 : « je ne pense pas qu'il y ait une bonne répartition magique, il faut
   qu'elle soit intelligente en fonction des ratios ». Exact — et le rapport
   qui compte n'est pas esthétique, il est GÉOGRAPHIQUE.

   Le cadre recevait une largeur fixe, donc une forme sans rapport avec les
   points à montrer. Mesuré sur quatre sites réels : cadre 3,4:1 pour une
   étendue de 0,68 — cinq fois trop large. `fitBounds` doit alors faire tenir
   5,7° de latitude dans 262 px de haut : il dézoome jusqu'à l'Europe, et la
   largeur se remplit de vide.

   ⇒ `--ratio-carte` est calculé PAR LE SERVEUR à partir des coordonnées (voir
     Tuiles::contenu), et le cadre l'adopte en `aspect-ratio`. La forme du cadre
     épouse celle des sites : alignés du nord au sud, le cadre est haut ; d'est
     en ouest, il est large. Vérifié : l'écart tombe de 5× à 1×.

   ⚠️ AVEC DES BORNES, ET ELLES SONT INDISPENSABLES. Sans plancher, le calcul
      donnait 144 px de large sur ce jeu de données : le cadrage devenait
      parfait et la carte ne montrait plus rien — une boîte bleue vide. Un
      ratio juste sur un objet illisible ne sert personne : le plancher prime
      sur la forme. */
@media (min-width: 861px) {
  .tuile-sites {
    display: grid;
    grid-template-columns: auto minmax(11rem, 1fr);
    gap: 1rem;
    align-items: stretch;
  }
  .tuile-sites .carte-sites-tuile {
    aspect-ratio: var(--ratio-carte, 1.6);
    height: 100%;
    width: auto;
    min-height: 0;
    /* ⚠️ LES TROIS BORNES SE RÉPONDENT, et l'ordre dans lequel le navigateur
       les applique compte. Mesuré : avec le seul plancher de largeur, le ratio
       0,68 réclamait 240 × 355 px dans une tuile qui n'en fait que 262 de haut
       — la carte débordait et se mettait à défiler dans son propre cadre.
       `max-height` la ramène à la hauteur disponible ; le plancher de largeur
       l'emporte alors sur la forme, ce qui est le bon ordre de priorité : une
       carte bien proportionnée mais illisible ne sert à rien. */
    max-height: 100%;
    /* 18 rem : en deçà, la carte cesse de montrer quoi que ce soit — mesuré à
       15 rem, où elle n'était plus qu'une vignette. 45 % au plus : au-delà,
       elle réclame tant de largeur que fitBounds doit dézoomer pour la
       remplir, et l'on revoit l'Europe entière. */
    min-width: 18rem;
    max-width: 45%;
  }

  /* ⭐⭐⭐ LA LISTE COMBLE CE QUE LA CARTE NE PREND PAS — et c'est ce qui a
     manqué à mes deux premières tentatives. Une carte bien cadrée sur des
     sites verticaux occupe peu de largeur ; sur une tuile large, il restait
     1 279 px de vide (mesuré) et l'auteur l'a vu tout de suite : « c'est pas
     mieux ». Étirer la carte pour remplir ramenait le dézoom.
     ⇒ C'est la LISTE qui s'étale, en autant de colonnes que la place autorise.
       Chaque colonne fait au moins 16 rem — en dessous, un nom de site et son
       client se coupent en deux lignes.
     ⚠️ `break-inside: avoid` est indispensable : sans lui, un site se scinde
        entre le bas d'une colonne et le haut de la suivante, nom d'un côté et
        client de l'autre.
     ⚠️ Vérifié qu'il n'y a PAS de débordement horizontal — le multi-colonnes
        en hauteur contrainte crée d'ordinaire des colonnes vers la droite
        plutôt que de faire défiler. Éprouvé avec les 6 sites que le serveur
        envoie au maximum, sur 4, 6 et 12 colonnes de grille : 6/6 visibles,
        aucun débordement. */
  .tuile-sites .tuile-liste {
    margin: 0;
    overflow-y: auto;
    columns: 16rem;
    column-gap: 2rem;
  }
  .tuile-sites .tuile-liste li { break-inside: avoid; }
}

/* --- « Mon compte » : deux blocs côte à côte plutôt qu'une colonne ---------
   Les cartes gardent chacune une largeur de lecture confortable ; c'est leur
   MISE EN REGARD qui occupe l'écran. `align-items: start` est indispensable :
   sans lui, la carte la plus courte s'étirerait à la hauteur de l'autre et
   afficherait un grand vide sous son contenu. */
.compte-grille { display: grid; gap: 1.25rem; }
@media (min-width: 900px) {
  /* ⭐ TROIS COLONNES — arbitrage de l'auteur : les coordonnées, puis les champs
     du mot de passe, puis les explications. Le bloc du mot de passe en occupe
     donc DEUX, et se partage lui-même en saisie et en aide.
     ⚠️ Une version à deux colonnes égales donnait 923 px au bloc du mot de
        passe pour 416 px de contenu — 462 px de carte vide, invisibles dans les
        chiffres et flagrants à l'écran. Ranger le « blabla » à côté des champs
        remplit cet espace au lieu de le rétrécir. */
  .compte-grille {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr);
    align-items: start;
  }

  /* ⚠️ ET L'INTITULÉ SE BORNE. Sans cette largeur, les deux colonnes du tableau
     clé/valeur se partagent l'espace et la valeur part loin de son propre
     intitulé : on lit « NOM » d'un côté et le nom de l'autre. */
  .compte-grille th { width: 12rem; }
}

/* --- Le bloc mot de passe : saisir d'un côté, comprendre de l'autre -------- */
.bloc-mdp-saisie > .bouton { margin-top: .5rem; }

/* ⚠️ LA COLONNE D'EXPLICATIONS RESTE LISIBLE. Elle est faite de PHRASES, et le
   texte se lit mal au-delà d'une petite centaine de caractères par ligne —
   c'est la règle qui vaut pour toute la page, et elle ne cesse pas de valoir
   parce qu'on a placé ce texte dans une colonne large. */
.bloc-mdp-aide { max-width: 34rem; }

@media (min-width: 900px) {
  .bloc-mdp-deux {
    display: grid;
    grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
  }
  /* ⚠️ Les critères ne portent plus de marge haute ici : ils ne suivent plus
     les champs, ils sont à côté. Une marge pensée pour un empilement laisse un
     décalage inexpliqué en tête de colonne. */
  .bloc-mdp-deux .criteres { margin-top: 0; }
}

/* ⛔ PAS DE CARTE SUR TÉLÉPHONE — arbitrage de l'auteur. La liste porte la même
   information et se lit, elle. carte.js ne la construit même pas : la masquer
   seulement aurait laissé Leaflet réclamer ses tuiles à l'IGN pour rien. */
@media (max-width: 860px) {
  .carte-sites-tuile { display: none; }
}


.tuile {
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: 12px;
  box-shadow: var(--ombre);

  /* ⚠️⚠️ CE `display: flex` N'EST PAS DE LA MISE EN FORME, C'EST CE QUI REND
     L'ASCENSEUR POSSIBLE. Sans lui la tuile est un bloc ordinaire : son corps
     prend sa hauteur NATURELLE, ne dépasse donc jamais de lui-même, et un
     `overflow-y: auto` posé dessus ne trouve rien à faire défiler — c'est la
     tuile qui coupait, en amont. Mesuré : la règle seule ne produisait aucun
     ascenseur. En colonne, la tête garde sa taille, le corps reçoit le reste
     et se retrouve enfin CONTRAINT. */
  display: flex;
  flex-direction: column;

  /* « hidden » COUPE ce qui dépasse : sans lui, une tuile trop courte
     déborderait sur sa voisine. Il reste donc — mais il ne cache plus rien,
     puisque le corps défile désormais à l'intérieur. */
  overflow: hidden;
  /* Le focus des tuiles sert au rangement au clavier : il doit se voir. */
  outline-offset: 2px;
}
.tuile:focus-visible { outline: 2px solid var(--accent); }

/* ⭐ RELIEF AU SURVOL — la carte se soulève légèrement, comme dans les tableaux
   de bord du domaine. La courbe et la durée viennent d'Open Props plutôt que
   d'un chiffre choisi au hasard.
   ⚠️ ENCADRÉ PAR prefers-reduced-motion : une animation n'est jamais
      gratuite, elle peut déclencher un malaise chez qui l'a signalé à son
      système. Ici seule l'ombre bouge, mais la règle vaut d'être tenue
      partout — sinon on la tient nulle part. */
@media (prefers-reduced-motion: no-preference) {
  .tuile { transition: box-shadow var(--animation-fade-in, .3s) var(--ease-3, ease); }
  .tuile:hover { box-shadow: var(--ombre-relief); }
}

/* ⭐ CHIFFRES À CHASSE FIXE — mais SEULEMENT sur le grand nombre.
   Le « 1 » est plus étroit que le « 8 » : deux nombres l'un sous l'autre ne
   s'alignent pas, et l'œil ne peut plus comparer une colonne d'un coup.

   ⚠️⚠️ JE L'AVAIS POSÉ SUR LES LISTES ET LES CELLULES DE TABLEAU, ET C'ÉTAIT
      UN DÉFAUT — vu au rendu, mesuré ensuite : « TEST01-DO » s'affichait
      « TEST01 - DO ». En chasse fixe, le TRAIT D'UNION prend lui aussi la
      largeur d'un chiffre (345 px → 377 px sur ce seul mot, soit +9 %), et il
      paraît entouré d'espaces. Or ces listes et ces colonnes portent des CODES
      de dossier, pas des quantités.
      ⇒ Le réglage ne vaut que là où l'on compare des nombres entre eux. Le
        jour où une colonne sera vraiment numérique, elle portera sa propre
        classe. (Contrôlé : ni `calt` ni `opsz` n'y étaient pour quelque
        chose — je les soupçonnais tous les deux.) */
.tuile-chiffre { font-variant-numeric: tabular-nums; }

.tuile-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .75rem 1rem;
  border-bottom: 1px solid var(--bord);
  background: var(--fond-doux);
}
.tuile-tete h2 {
  margin: 0;
  font-size: .82rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--texte-doux);
}
.tuile-corps { padding: 1rem; }

/* --- La pastille d'icône de tuile ----------------------------------------
   Le repère que l'œil trouve avant d'avoir lu le titre. Posée dans un cadre
   plutôt que nue : une icône seule sur un fond de tête de tuile flotte, alors
   qu'un carré discret lui donne un poids égal à celui du titre en face.
   ⚠️ `flex: none` : sans lui, la pastille se laisse écraser par un titre long
      et devient un rectangle aplati. */
.tuile-icone {
  display: grid;
  place-items: center;
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 7px;
  background: var(--fond);
  border: 1px solid var(--bord);
  color: var(--texte-doux);
}
.tuile-icone svg { width: 1rem; height: 1rem; }

/* ⚠️ L'ÉTAT PASSE PAR LA COULEUR *ET* PAR LE CHIFFRE, jamais par la couleur
   seule : un écran d'alerte qui ne se distingue que par une teinte est
   illisible pour qui ne perçoit pas cette teinte (WCAG 1.4.1). Ici l'ambre ne
   fait que doubler un nombre déjà écrit en grand. */
.tuile-icone-alerte {
  color: var(--alerte);
  border-color: var(--alerte);
  background: var(--alerte-fond);
}

/* Le gros chiffre : ce qu'on lit de loin, avant tout le reste. */
.tuile-chiffre {
  font-size: 2.6rem;
  font-weight: 700;
  line-height: 1;
  margin: 0 0 .2rem;
  color: var(--titre);
  font-variant-numeric: tabular-nums;
}
/* ⚠️ La couleur d'attention ne s'applique QUE s'il y a quelque chose à faire.
   Un « 0 » en rouge ferait chercher un problème qui n'existe pas. */
.tuile-chiffre-attention { color: var(--alerte); }

.tuile-nom { font-weight: 600; font-size: 1.05rem; margin: 0 0 .2rem; }
.tuile-contact { margin: .7rem 0 .3rem; font-size: .9rem; }

.tuile-liste { list-style: none; margin: .6rem 0 0; padding: 0; }
.tuile-liste li {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .35rem .5rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--bord);
}
.tuile-liste li:last-child { border-bottom: 0; }
.tuile-liste .aide { font-size: .82rem; }
.tuile-ev-nom { font-weight: 600; }

/* Le compteur en bout de ligne : aligné à droite, chiffres de largeur fixe
   pour que les nombres se lisent en colonne. */
.tuile-compte {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--alerte);
}
.tuile-liste-dossiers li { align-items: center; }

/* ⚠⚠ PAS DE « height » ICI. Cette règle en portait un (15 rem), et comme le
   sélecteur était déjà défini plus haut avec « flex: 1 1 auto », c'est la
   hauteur fixe qui l'emportait : agrandir la tuile n'agrandissait PAS la carte,
   qui restait à 15 rem avec du vide en dessous. Une seconde définition ne
   remplace pas la première, elle se superpose à elle. */
.carte-sites-tuile {
  border-radius: var(--rayon);
  border: 1px solid var(--bord);
}

/* Les étiquettes d'événement. Le vocabulaire des couleurs est celui du reste
   du site : alerte pour ce qui approche, info pour ce qui court, neutre pour
   ce qui est passé ou sans date. */
.etiquette-ev-avant     { background: var(--alerte-fond); color: #6b4600; border: 1px solid var(--alerte); }
.etiquette-ev-en-cours  { background: var(--succes-fond); color: #0a5c24; border: 1px solid var(--succes); }
.etiquette-ev-termine,
.etiquette-ev-sans-date { background: var(--fond-doux);   color: var(--texte-doux); border: 1px solid var(--bord); }


@media (prefers-reduced-motion: no-preference) {
  .tuile { transition: box-shadow .15s ease, border-color .15s ease; }
}

/* ⚠️ LES LIENS DES TUILES AU DOIGT — mesuré, pas supposé : à 390 px ils
   faisaient 25 px de haut, là où la règle du projet demande 44. Une liste de
   liens serrés à 25 px se rate une fois sur deux, et l'on ouvre le dossier
   voisin.

   Le grandissement ne s'applique QUE sur petit écran : à la souris, 25 px se
   visent très bien, et étirer les lignes sur un grand écran ferait une tuile
   deux fois plus haute pour la même information. */
@media (max-width: 48rem) {
  .tuile-liste a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .tuile-liste li { padding: .15rem 0; }
  /* Le lien isole d une tuile (« Mon compte ») suit la meme regle : il est
     seul, donc moins risque a viser, mais c est une ACTION et rien ne
     justifie de la traiter autrement que les autres. */
  .tuile-corps .lien-discret {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* --- Déplacer et redimensionner : le modèle de la fenêtre ---------------- *
   On ATTRAPE la barre de titre pour déplacer, on TIRE le coin pour
   redimensionner — comme une fenêtre, comme un widget Android. Aucun mode à
   activer, aucun bouton dans le titre.

   ⚠️⚠️ `touch-action: none` EST POSÉ AU MILLIMÈTRE : sur la barre de titre et
        la poignée, et NULLE PART AILLEURS. Sans lui, le navigateur prend le
        glissement du doigt pour un défilement et ne nous envoie plus rien —
        le geste serait impossible au toucher. Posé sur la tuile entière, il
        empêcherait de faire défiler la page depuis le corps d'une tuile, et
        l'écran deviendrait impraticable au téléphone.
   ------------------------------------------------------------------------ */

.tuile { position: relative; }

/* ⚠⚠⚠ AU DOIGT, LES GESTES N'EXISTENT QU'EN MODE ÉDITION.

   Signalé à l'usage : « les tuiles se déplacent systématiquement quand la
   souris passe sur le titre. Ingérable. » Sur téléphone, la barre de titre
   occupe toute la largeur de la tuile : en la rendant saisissable en
   permanence, on ne pouvait plus FAIRE DÉFILER la page dès que le doigt s'y
   posait — c'est-à-dire presque toujours.

   ⇒ Un APPUI LONG bascule en mode édition, comme sur un écran d'accueil
     Android. Hors de ce mode, `touch-action` reste libre et la page défile
     normalement, où que le doigt se pose.

   ⚠ À LA SOURIS, RIEN DE TOUT CELA : le pointeur ne défile pas la page en
     appuyant, il n'y a donc aucun conflit à arbitrer et le geste reste direct.
     C'est la règle @media (hover: hover) qui les sépare. */
@media (hover: hover) and (pointer: fine) {
  .tuile-tete, .tuile-tete * { touch-action: none; }
}
@media (hover: none) {
  /* Hors édition : le doigt défile, rien ne s'attrape. */
  .tuile-tete, .tuile-tete * { touch-action: auto; }
  .tuile-poignee { display: none; }

  .accueil-edition .tuile-tete,
  .accueil-edition .tuile-tete * { touch-action: none; }
  .accueil-edition .tuile-poignee { display: block; opacity: 1; }

  /* On doit VOIR qu'on est en édition, sinon le mode surprend au geste suivant. */
  .accueil-edition .tuile {
    border-color: var(--marque-clair);
    box-shadow: 0 0 0 2px var(--marque-clair);
  }
}

/* La sortie du mode, en bas de l'écran : un mode dans lequel on ne sait pas
   comment entrer se devine ; un mode dont on ne sait pas sortir enferme. */
.accueil-sortie {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 4.5rem;
  z-index: 50;
  box-shadow: 0 6px 18px rgba(16, 41, 74, .25);
}

/* ⚠️⚠️ `touch-action` N'EST PAS HÉRITÉ — ET LES ENFANTS SONT VISÉS EXPRESSÉMENT.
   Signalé à l'usage : « sur mobile, le déplacement ne fonctionne pas ». La
   règle était bien posée sur la barre de titre — mais le doigt n'atterrit pas
   sur la barre : il atterrit sur le <h2> QU'ELLE CONTIENT, qui garde
   `touch-action: auto`. Le navigateur y voyait donc un début de défilement,
   avalait le geste, et ne nous envoyait plus rien.
   ⇒ La barre ET tout ce qu'elle contient.

   ⚠️⚠️⚠️ LA RÈGLE QUI SUIVAIT CE COMMENTAIRE A ÉTÉ RETIRÉE LE 10/08 — ELLE
   RENDAIT L'ACCUEIL INUTILISABLE AU DOIGT. C'était une TROISIÈME déclaration
   de « .tuile-tete, .tuile-tete * { touch-action: none } », hors de toute
   media query. Même spécificité que les deux autres, mais déclarée APRÈS :
   elle l'emportait donc partout, y compris sur le « touch-action: auto » que
   @media (hover: none) rétablit hors mode édition.

   Effet réel : une bande de 38 px par tuile, sur toute sa largeur, où le doigt
   ne faisait RIEN — il ne faisait pas défiler la page (le navigateur avalait
   le geste) et ne déplaçait pas la tuile non plus (le JS refuse le geste hors
   mode édition, tuiles.js). Zone morte pure.

   Les deux régimes ci-dessus suffisent, et ils visent bien « .tuile-tete * ».
   ⇒ Un sélecteur défini deux fois ne remplace pas, il SE SUPERPOSE. Ici,
     trois fois — et c'est la seule sans garde qui gagnait. */
/* ⚠⚠ LE CURSEUR EST FOURNI PAR NOUS, PAS PAR LE SYSTÈME.
   Signalé à l'usage : « le curseur de déplacement est blanc, donc blanc sur
   blanc, on n'y voit rien ». C'est exact et on n'y peut rien autrement : le
   curseur « grab » est dessiné par le système, sa couleur nous échappe, et sur
   Windows il est clair — invisible sur une barre de titre claire.

   ⇒ Une croix de déplacement en SVG, tracée en FONCÉ AVEC LISERÉ BLANC : elle
     se voit sur fond clair comme sur fond sombre, ce qu'aucune couleur unie ne
     permet. Les deux nombres après l'adresse sont le point chaud, au centre.
   ⚠ Le « # » doit être écrit %23 dans une adresse de données : sinon le
     navigateur y voit le début d'une ancre et l'image ne charge pas.
   ⚠ « grab » reste en secours : si le SVG échoue, on garde un curseur qui dit
     au moins qu'on peut attraper. */
.tuile-tete {
  cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><g fill="none" stroke="%23ffffff" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18M3 12h18M12 3l-3 3M12 3l3 3M12 21l-3-3M12 21l3-3M3 12l3-3M3 12l3 3M21 12l-3-3M21 12l-3 3"/></g><g fill="none" stroke="%23252c47" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v18M3 12h18M12 3l-3 3M12 3l3 3M12 21l-3-3M12 21l3-3M3 12l3-3M3 12l3 3M21 12l-3-3M21 12l-3 3"/></g></svg>') 12 12, grab;
  /* Le titre ne doit pas se sélectionner quand on attrape la tuile : le
     navigateur surlignerait le texte au lieu de suivre le geste, et l'on
     croirait que le déplacement ne marche pas. */
  user-select: none;
  -webkit-user-select: none;
  /* Au toucher, la surbrillance bleue du « tap » clignote à chaque prise. */
  -webkit-tap-highlight-color: transparent;
}
.tuile-tete:active { cursor: grabbing; }

/* LA POIGNÉE. Discrète au repos, franche dès qu'on approche.
   ⚠️ 44 px de zone tactile, comme toute cible du projet — mais un dessin plus
      petit : c'est la ZONE qui doit être atteignable au doigt, pas le trait
      qui doit être gros. */
.tuile-poignee,
.tuile-poignee::after { touch-action: none; }
.tuile-poignee {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: nwse-resize;
  touch-action: none;
  opacity: 0;
  transition: opacity .12s ease;
}
/* Le chevron lui-même : deux traits, dessinés en dégradé plutôt qu'en SVG —
   rien à charger, et la couleur suit les jetons du thème. */
.tuile-poignee::after {
  content: '';
  position: absolute;
  right: 7px;
  bottom: 7px;
  width: 12px;
  height: 12px;
  border-right: 2px solid var(--texte-doux);
  border-bottom: 2px solid var(--texte-doux);
  border-bottom-right-radius: 3px;
}
.tuile:hover .tuile-poignee,
.tuile:focus-within .tuile-poignee,
.tuile-poignee:focus-visible { opacity: 1; }

/* ⚠️ AU TOUCHER, LA POIGNÉE EST TOUJOURS VISIBLE : il n'y a pas de survol sur
   un écran tactile, et une poignée qui n'apparaît qu'au survol y serait
   introuvable — donc la fonction n'existerait pas. */
@media (hover: none) {
  .tuile-poignee { opacity: .55; }
}

/* Pendant le geste. La tuile attrapée s'efface un peu pour qu'on voie où elle
   va se poser ; le curseur reste « en train de tirer » sur toute la page, même
   si le pointeur sort de la poignée. */
.tuile-attrapee { opacity: .5; }
.accueil-deplace, .accueil-deplace * { cursor: grabbing !important; }
.accueil-redimensionne, .accueil-redimensionne * { cursor: nwse-resize !important; }
/* ⚠️ Pendant un redimensionnement, le pointeur balaie le texte des tuiles
   voisines : sans cela, le navigateur le sélectionne au passage et l'écran se
   couvre de surlignage bleu. */
.accueil-deplace, .accueil-redimensionne { user-select: none; }

/* La transition ne s'applique PAS pendant le geste : une tuile qui met 150 ms
   à suivre le doigt donne l'impression d'un logiciel qui rame. */
@media (prefers-reduced-motion: no-preference) {
  .tuile { transition: min-height .12s ease; }
  .accueil-redimensionne .tuile { transition: none; }
}

/* --- Ce qu'on montre PENDANT le geste ------------------------------------ *
   ⭐⭐⭐ LA GRILLE NE BOUGE PLUS PENDANT QU'ON TIRE. C'était tout le défaut
        signalé à l'essai : « tu empiles, y'a tout qui bouge, on voit pas la
        modif en live ». On agissait sur le flux à chaque pixel, et les cinq
        tuiles sautaient à chaque fois.

        Désormais on montre l'INTENTION — un cadre à la taille visée, un
        emplacement vide là où la tuile va se poser — et le document ne change
        qu'au relâchement, une seule fois.
   ------------------------------------------------------------------------ */

/* ⚠️⚠️ LE CADRE FANTÔME EST DÉFINI PLUS BAS, DANS LA GRILLE — et il l'était
        AUSSI ici, en « position: fixed », d'une version précédente. Les deux
        blocs portaient le même sélecteur : le « fixed » l'emportait sur le
        placement en grille, sortait le cadre du flux, et comme le script ne
        pose plus de coordonnées en pixels, il se réduisait à ses bordures —
        MESURÉ À 4 × 4 PIXELS en production.
        Résultat : rien à l'écran, alors que tout paraissait correct dans le
        code. Signalé deux fois par l'auteur avant que je pense à mesurer le
        cadre lui-même plutôt qu'à relire ma logique.
        ⇒ Un sélecteur ne doit être défini qu'UNE fois : la seconde définition
          ne remplace pas la première, elle se superpose à elle.

   La tuile détachée, qui suit le pointeur.
   ⚠️ « position: fixed » et non « absolute » : les coordonnées du pointeur sont
      relatives à la FENÊTRE. En absolute, la tuile dériverait dès que la page
      est défilée, et le décalage grandirait avec le défilement. */
.tuile-flottante {
  position: fixed !important;
  z-index: 70;
  margin: 0;
  box-shadow: 0 12px 32px rgba(16, 41, 74, .22);
  transform: rotate(.6deg);
  opacity: .96;
  cursor: grabbing;
}

/* L'emplacement laissé libre : il occupe exactement la place de la tuile, ce
   qui empêche la grille de se refermer et de tout décaler pendant le geste. */
.tuile-place {
  border: 2px dashed var(--bord);
  border-radius: 12px;
  background: var(--fond-doux);
}

/* Le fantôme occupe une case de la grille, comme une tuile — mêmes variables,
   donc exactement la place où la tuile ira. Un cadre posé en absolu par-dessus
   pourrait montrer une position différente de la réalité. */
.tuile-fantome {
  grid-column: calc(var(--x, 0) + 1) / span var(--w, 4);
  grid-row: calc(var(--y, 0) + 1) / span var(--h, 3);
  /* Un TRAIT, donc de l'encre : il doit s'éclaircir en mode sombre, sinon le
     fantôme devient invisible sur le fond de la grille. Et son remplissage
     passe par color-mix plutôt qu'un rgba figé, pour suivre le thème. */
  border: 2px dashed var(--accent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  pointer-events: none;

  /* ⚠⚠ SANS z-index, LE FANTÔME EST INVISIBLE PENDANT UN REDIMENSIONNEMENT.
     Signalé à l'usage : « il faudrait voir le cadre en cours de
     redimensionnement, pour prévisualiser ». Il était bien dessiné — mais
     DERRIÈRE la tuile : contrairement au déplacement, où la tuile se détache
     et flotte ailleurs, le redimensionnement la laisse en place. Le fantôme
     occupait donc la même case qu'une tuile opaque, et l'on n'en voyait que le
     débord — c'est-à-dire rien tant qu'on rétrécissait.
     ⇒ Il passe devant, et son fond translucide laisse lire ce qu'il couvre. */
  z-index: 5;
}
/* Repliée sur une colonne, la grille ignore les coordonnées : le fantôme n'a
   plus de sens, et il occuperait une ligne vide au milieu des tuiles. */
@media (max-width: 60rem) {
  .tuile-fantome { display: none; }
}

/* =========================================================================
   L'ÉCHELLE SUR PETIT ÉCRAN
   -------------------------------------------------------------------------
   Demandé à l'usage : « la version mobile, c'est énorme ! Réduisons tout, les
   polices... tout ! » Constat juste — l'habillage avait été réglé sur un grand
   écran, et transposé tel quel.

   ⭐⭐⭐ ON RÉDUIT LA RACINE, PAS CHAQUE RÈGLE. Toute la feuille est écrite en
        `rem` (423 occurrences) : changer la taille de référence fait suivre
        d'un coup les titres, les marges, les rayons, les paddings — dans les
        bonnes proportions, et sans toucher à quoi que ce soit d'autre.
        Réduire règle par règle aurait demandé cent retouches, dont on aurait
        oublié la moitié.

   ⚠️⚠️ SAUF LES CHAMPS DE SAISIE, QUI RESTENT À 16 px. En dessous, iOS ZOOME
        AUTOMATIQUEMENT sur le champ au moment où on le touche — la page se
        décale, on perd le contexte, et il faut repincer pour revenir. C'est un
        comportement du système, qu'aucune balise viewport ne désactive
        proprement. 16 px est le seuil, il n'est pas négociable.

   ⚠️ Les zones tactiles restent en PIXELS (44 px) et ne bougent donc pas :
      c'est le doigt qui les dimensionne, pas la typographie.
   ========================================================================= */

@media (max-width: 60rem) {
  html { font-size: 15px; }
  body { font-size: 15px; }

  /* Les espacements se resserrent PLUS que le texte : c'est le blanc qui
     occupe la place sur un petit écran, pas les lettres. */
  .tuiles { gap: .6rem; grid-auto-rows: minmax(72px, auto); }
  .tuile-corps { padding: .7rem; }
  .tuile-tete { padding: .5rem .7rem; }
  .principal { padding: 1rem .75rem; }
  .carte { padding: .8rem; }

  /* Le gros chiffre reste gros — c'est ce qu'on lit de loin, et c'est
     justement ce qui doit rester lisible d'un coup d'œil sur un téléphone. */
  .tuile-chiffre { font-size: 2.1rem; }

  h1 { font-size: 1.25rem; }
  h2 { font-size: 1.05rem; }
  .accueil-tete { margin-bottom: .8rem; }
}

@media (max-width: 30rem) {
  html { font-size: 14.5px; }
  body { font-size: 14.5px; }
  .principal { padding: .8rem .6rem; }
}

/* ⚠⚠ RIEN NE DOIT POUSSER LA PAGE PLUS LARGE QUE L'ÉCRAN. Vu à la capture :
   la ligne d'aide et les noms de sites débordaient à droite, et toute la page
   se décalait avec eux. Une seule ligne trop longue suffit à rendre un écran
   entier bancal, et le défaut ne se voit pas sur un grand écran. */
@media (max-width: 60rem) {
  .accueil, .tuile, .tuile-corps { min-width: 0; }
  .tuile-liste li { flex-wrap: wrap; }
  /* Un nom sans espace (une adresse, une référence) se coupe plutôt que de
     pousser : mieux vaut un mot brisé qu'une page qui glisse. */
  .tuile-liste a, .tuile-nom, .accueil-aide { overflow-wrap: anywhere; }
}

/* ⚠️⚠️⚠️ LE SEUIL iOS — ET LA RÈGLE NE GAGNAIT PAS.
   Un champ sous 16 px déclenche le zoom automatique de Safari : la page saute
   dès qu'on touche le champ, et ne revient pas au relâchement.

   Le commentaire d'origine disait « posée après tout le reste pour qu'il
   l'emporte ». C'est faux, et c'est le défaut : l'ordre de déclaration ne
   départage que des règles de MÊME spécificité. « input, select, textarea »
   pèse (0,0,1) et perdait contre tous les sélecteurs de champ réels —
   « .champ input » (0,1,1), « .admin-recherche » (0,1,0) — qui posent
   « font: inherit » et héritent donc de la racine, réduite sur petit écran.
   Zoom mesuré par l'audit du 10/08 : ×1,10 sur l'écran de connexion, jusqu'à
   ×1,25 sur les sélecteurs de nature.

   ⚠️ « !important » EST JUSTIFIÉ ICI, et c'est le seul emploi avec [hidden] :
      il ne s'agit pas d'un choix de style qu'on impose, mais d'un comportement
      du système d'exploitation qu'on n'a pas le droit de déclencher. Lister à
      la main tous les sélecteurs de champ du projet reviendrait à en oublier
      un le jour où l'on en ajoute — et ce jour-là, personne ne le verrait.

   ⚠️ « pointer: coarse » EN PLUS de la largeur : un iPad en paysage fait
      1024 px, soit 64 rem — au-delà du seuil, alors que c'est bien un doigt
      qui touche l'écran et bien Safari qui zoome. */
@media (max-width: 60rem), (pointer: coarse) {
  input, select, textarea { font-size: 16px !important; }
}

/* ---------------------------------------------------------------------------
   PROSPECTS — les demandes venues du site public
   ---------------------------------------------------------------------------
   ⚠️ CE SONT DES MESSAGES, PAS DES LIGNES DE TABLEAU. Leur longueur va de deux
   lignes à trente ; un tableau à six colonnes aurait tronqué le seul contenu
   qui compte, et il aurait fallu ouvrir chaque ligne pour savoir de quoi il
   s'agit. D'où des fiches dépliables, qui donnent l'essentiel replié et le
   message entier une fois ouvertes.
   ------------------------------------------------------------------------ */

.pros-entete { margin-bottom: 1rem; }
.pros-entete h1 { margin: 0 0 .2rem; }
.pros-sous-titre { margin: 0; color: var(--texte-doux); font-size: .95rem; }

/* Le compte sur l'onglet de la bascule. */
.bascule-compte {
  display: inline-block;
  margin-left: .4rem;
  min-width: 1.3rem;
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--erreur);
  color: #fff;
  font-size: .78rem;
  font-weight: 700;
  line-height: 1.35;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.pros-liste { display: grid; gap: .75rem; margin-top: 1.25rem; }

.pros-fiche {
  background: var(--fond);
  border: 1px solid var(--bord);
  border-left: 4px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: var(--ombre);
}
/* ⭐ LE LISERÉ DIT L'ÉTAT AVANT MÊME QU'ON LISE. Il double la pastille, il ne
   la remplace pas : la couleur seule ne serait pas perceptible par tout le
   monde, et elle ne se prononce pas. */
.pros-statut-nouveau { border-left-color: var(--erreur); }
.pros-statut-lu      { border-left-color: var(--alerte); }
.pros-statut-traite  { border-left-color: var(--succes); }
.pros-statut-rejete  { border-left-color: var(--bord); opacity: .72; }

.pros-fiche > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem .75rem;
  /* 44 px de zone tactile : cette page se consulte au téléphone comme le
     reste du site. */
  min-height: 44px;
  padding: .6rem .9rem;
  cursor: pointer;
  list-style-position: inside;
}
.pros-fiche > summary:hover { background: var(--fond-doux); }
.pros-qui { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; }
.pros-orga { color: var(--texte-doux); font-size: .9rem; }
/* Poussée à droite, elle reste alignée d'une fiche à l'autre. */
.pros-date { margin-left: auto; color: var(--texte-doux); font-size: .85rem; }

.pros-corps { padding: 0 .9rem 1rem; border-top: 1px solid var(--bord); }
.pros-coord { margin: .8rem 0; font-size: .95rem; }

.pros-message {
  margin: 0 0 1rem;
  padding: .85rem 1rem;
  background: var(--fond-doux);
  border-radius: var(--rayon);
  /* ⚠️ Un message peut contenir une adresse très longue sans espace : sans
     cette règle, elle élargirait la fiche et ferait défiler tout l'écran. */
  overflow-wrap: anywhere;
}

.pros-gestes { display: grid; gap: .6rem; }
.pros-note-champ { display: grid; gap: .3rem; font-size: .9rem; font-weight: 600; }
.pros-note-champ textarea {
  font: 16px/1.5 'InterVariable', system-ui, sans-serif;
  font-weight: 400;
  color: var(--texte);
  background: var(--fond);
  border: 1px solid var(--bord-champ);
  border-radius: var(--rayon);
  padding: .5rem .65rem;
  resize: vertical;
}
.pros-boutons { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; }
.pros-statut-choix select {
  font: 16px/1.5 'InterVariable', system-ui, sans-serif;
  color: var(--texte);
  background: var(--fond);
  border: 1px solid var(--bord-champ);
  border-radius: var(--rayon);
  min-height: 44px;
  padding: .35rem .6rem;
}
/* ⚠️ SÉPARÉE DES AUTRES GESTES, ET VOLONTAIREMENT DISCRÈTE : c'est le seul
   irréversible de l'écran. Voisine du bouton « Enregistrer » et de même
   apparence, elle se cliquerait un jour à sa place. */
.pros-supprimer { margin-top: 1rem; padding-top: .75rem; border-top: 1px solid var(--bord); }

.pastille-nouveau { background: var(--erreur-fond); color: var(--erreur-texte); }
.pastille-lu      { background: var(--alerte-fond); color: var(--alerte-texte); }
.pastille-traite  { background: var(--succes-fond); color: var(--succes-texte); }
.pastille-rejete  { background: var(--neutre-fond); color: var(--texte-doux); }
