﻿/* Back-office Militants PPA-CI.
   Sans cadre ni dependance : trois ecrans ne justifient pas une chaine de
   construction.

   La palette est celle du parti, reprise au code hexadecimal pres de
   « Synchron/lib/core/theme/couleurs_ppaci.dart », elle-meme transposee de
   Sentinelle. Les applications du parti doivent se reconnaitre entre elles :
   qui passe de l'une a l'autre ne doit pas avoir l'impression de changer
   d'organisation. Toute evolution se repercute des trois cotes. */

:root {
  /* Foret : couleur structurante, en-tetes et actions principales. */
  --foret-100: #d9e6f6;
  --foret-700: #1f3e70;
  --foret-800: #1a3459;
  --foret-900: #12294a;

  /* Azur : l'accent, ce sur quoi on clique. */
  --azur-100: #d0ecfb;
  --azur-600: #0e88c6;
  --azur-700: #0d6fa2;

  /* Mandarine : ce qui attend. */
  --mandarine-100: #ffe6cc;
  --mandarine-600: #e06f00;

  --actif: #1e7a4c;
  --suspendu: #b3362a;

  --ivoire: #eef2f7;
  --papier: #ffffff;

  --encre-100: #e6eaf1;
  --encre-200: #d0d7e2;
  --encre-400: #8390a4;
  --encre-600: #4b5768;
  --encre-900: #171c25;

  --rayon: 8px;
  --rayon-large: 12px;
  --ombre: 0 1px 2px rgba(18, 41, 74, .06), 0 4px 14px rgba(18, 41, 74, .06);
}

* { box-sizing: border-box; }

/* « hidden » cache, quoi qu'en dise le reste de la feuille.
   L'attribut ne tient que par un « display: none » du navigateur, que la
   moindre regle d'auteur emporte : « .connexion { display: grid } » laissait
   ainsi le formulaire de connexion visible par-dessus l'application une fois
   connecte. Pose ici une fois pour toutes, plutot qu'a corriger a chaque
   nouvelle classe qui declare un display. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--ivoire);
  color: var(--encre-900);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

h1, h2, h3 { margin: 0; font-weight: 650; letter-spacing: -.01em; }
h1 { font-size: 22px; }
h2 { font-size: 19px; }
h3 { font-size: 17px; }

code, .mono { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: .92em; }

/* ------------------------------------------------------- chargement */

/* Le ruban avance seul, sans connaitre la progression reelle : une requete HTTP
   ne dit pas ou elle en est. Il ralentit a mesure qu'il approche du bout et ne
   l'atteint jamais tout seul — c'est l'arrivee de la reponse qui le termine,
   ce qui evite une barre pleine devant un ecran encore vide. */
.ruban {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 100%;
  z-index: 20;
  background: transparent;
  overflow: hidden;
}

.ruban::after {
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--azur-600), var(--mandarine-600));
  transform-origin: left;
  animation: ruban-avance 12s cubic-bezier(.15, .8, .25, 1) forwards;
}

.ruban.fini::after {
  animation: none;
  transform: scaleX(1);
  transition: transform .18s ease-out, opacity .32s ease-out .12s;
  opacity: 0;
}

@keyframes ruban-avance {
  0%   { transform: scaleX(.02); }
  25%  { transform: scaleX(.5); }
  60%  { transform: scaleX(.78); }
  100% { transform: scaleX(.94); }
}

/* Un bouton qui travaille le dit, et refuse d'etre represse. Sans quoi deux
   clics sur « Marquer transmis » lancent deux requetes, dont la seconde
   repond que le lot est deja transmis — une erreur pour un geste correct. */
button.occupe {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}

button.occupe::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--papier);
  animation: tourne .6s linear infinite;
}

/* Sur fond clair, le disque emprunte l'encre : blanc sur blanc ne se verrait
   pas. */
.discret.occupe::after, .lien.occupe::after { color: var(--encre-600); }

@keyframes tourne { to { transform: rotate(360deg); } }

/* Qui a demande moins d'animation n'en recoit pas : le ruban se pose et reste,
   le disque disparait. L'information demeure, le mouvement non. */
@media (prefers-reduced-motion: reduce) {
  .ruban::after { animation: none; transform: scaleX(.6); }
  button.occupe::after { animation: none; border-top-color: currentColor; opacity: .4; }
}

/* ------------------------------------------------------------- connexion */

.connexion {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
}

.carte-connexion {
  width: 100%;
  max-width: 360px;
  background: var(--papier);
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon-large);
  box-shadow: var(--ombre);
  padding: 28px;
  display: flex;
  flex-direction: column;
}

.carte-connexion h1 { color: var(--foret-700); }
.carte-connexion .sous-titre { margin: 2px 0 22px; color: var(--encre-600); }
.carte-connexion label { font-size: 13px; font-weight: 600; margin-bottom: 5px; }
.carte-connexion input { margin-bottom: 16px; }
.carte-connexion button { margin-top: 6px; }

/* ------------------------------------------------------------ structure */

/* L'en-tete porte le foret, comme la barre de titre de l'application mobile :
   c'est a cela qu'on reconnait un ecran du parti. */
.barre {
  display: flex;
  align-items: center;
  gap: 28px;
  padding: 0 24px;
  height: 58px;
  background: var(--foret-700);
  color: #fff;
}

.marque strong { color: #fff; }

/* Le compte, replie. « position: relative » porte le menu : il se pose sous le
   bouton, quelle que soit la largeur de la barre. */
.compte {
  position: relative;
  margin-left: auto;
  font-size: 13.5px;
}

.compte-bouton {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: 1px solid transparent;
  border-radius: var(--rayon);
  background: none;
  color: rgba(255, 255, 255, .88);
  font: inherit;
  cursor: pointer;
}

.compte-bouton:hover,
.compte-bouton[aria-expanded="true"] {
  border-color: rgba(255, 255, 255, .22);
  background: rgba(255, 255, 255, .1);
}

/* Le chevron, dessine plutot qu'ecrit : un caractere ne se retourne pas
   proprement et depend des polices installees. */
.chevron {
  width: 7px;
  height: 7px;
  margin-top: -3px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform .15s ease;
}

.compte-bouton[aria-expanded="true"] .chevron {
  margin-top: 2px;
  transform: rotate(-135deg);
}

/* Le menu deroule. Aligne a droite : il descend du bouton, qui touche le bord
   de la fenetre — aligne a gauche, il en sortirait. */
.menu-compte {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  min-width: 200px;
  padding: 6px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--papier);
  box-shadow: var(--ombre);
}

.menu-nom {
  margin: 0;
  padding: 8px 10px 10px;
  border-bottom: 1px solid var(--encre-100);
  color: var(--foret-900);
  font-weight: 600;
}

.menu-action {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--rayon);
  background: none;
  color: var(--encre-600);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.menu-action:hover { background: var(--ivoire); color: var(--encre-900); }

/* ------------------------------------------------------- navigation laterale */

/* Deux colonnes : les modules a gauche, l'ecran a droite. La colonne reste en
   place quand le contenu defile — on garde sous les yeux ou l'on se trouve. */
.corps { display: flex; align-items: flex-start; }

.lateral {
  flex: none;
  width: 232px;
  align-self: stretch;
  padding: 18px 10px;
  background: var(--papier);
  border-right: 1px solid var(--encre-200);
  min-height: calc(100vh - 58px);
  position: sticky;
  top: 0;
}

.module + .module { margin-top: 2px; }

/* Le module se deplie ou se replie. Un bouton, pas un lien : il n'ouvre pas
   d'adresse a lui, il commande l'accordeon. */
.module > button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-radius: var(--rayon);
  background: none;
  color: var(--encre-600);
  font-size: 14px;
  text-align: left;
}

.module > button:hover { background: var(--ivoire); color: var(--encre-900); }

/* Le module ouvert est en gras, comme son sous-module courant : c'est ce qui
   dit ou l'on est sans avoir a relire l'adresse. */
.module.ouvert > button { color: var(--foret-900); font-weight: 700; }

/* Le chevron tourne avec l'ouverture : la fleche vers le bas annonce ce qui
   est deja deplie. */
/* Dessine, et non ecrit : le chevron etait un « › » typographique, qui ne se
   centre pas dans sa boite et dont la rotation part de travers. */
.module .chevron {
  flex: none;
  display: flex;
  align-items: center;
  color: var(--encre-400);
  transition: transform .16s ease;
}

.module .chevron svg { width: 14px; height: 14px; display: block; }

.module.ouvert .chevron { transform: rotate(90deg); }

.sous-modules { padding: 2px 0 6px 12px; }

.sous-modules a {
  display: block;
  padding: 7px 12px;
  border-left: 2px solid var(--encre-100);
  color: var(--encre-600);
  font-size: 13.5px;
}

.sous-modules a:hover { color: var(--encre-900); border-left-color: var(--encre-200); }

.sous-modules a.actif {
  color: var(--foret-900);
  font-weight: 700;
  border-left-color: var(--azur-600);
  background: var(--ivoire);
}

main { flex: 1; min-width: 0; padding: 26px 24px 60px; max-width: 1180px; }

.champ-region { max-width: 320px; }

.entete-vue {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 18px;
}

.entete-vue h2 { margin-right: auto; color: var(--foret-900); }

.vide { color: var(--encre-400); text-align: center; padding: 44px 0; }

/* Les actions que porte un ecran entier, a cote de son titre. */
.actions-entete { display: flex; align-items: center; gap: 8px; }

/* Les criteres d'une recherche, poses en grille : ils se lisent d'un coup
   d'oeil, et l'ecran ne se replie pas en une colonne de sept champs. */
.criteres {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 0 16px;
  padding: 18px 18px 6px;
  margin-bottom: 20px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon-large);
  background: var(--ivoire);
}

/* Les boutons descendent avec les champs : leur cellule les pose en bas, a la
   hauteur des controles de la meme rangee. */
.criteres-actions {
  display: flex;
  align-items: flex-end;
  gap: 10px;
}

.criteres-actions .principal { flex: 1; }

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 18px;
  color: var(--encre-600);
  font-size: 13.5px;
}

/* Le nom d'un militant ouvre sa fiche : il se donne pour ce qu'il est. */
.lien-fiche {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--azur-600);
  cursor: pointer;
  text-align: left;
}

.lien-fiche:hover { text-decoration: underline; }

/* Un intertitre dans la liste des champs d'une fiche : il traverse les deux
   colonnes de la grille. */
.champs-fiche dt.groupe {
  grid-column: 1 / -1;
  margin-top: 14px;
  color: var(--foret-900);
  border-bottom: 1px solid var(--encre-100);
  padding-bottom: 4px;
}

.champs-fiche dt.groupe:first-child { margin-top: 0; }

/* -------------------------------------------------------------- controles */

input, select {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--papier);
  color: var(--encre-900);
  font: inherit;
}

input:focus, select:focus {
  outline: 2px solid var(--azur-600);
  outline-offset: -1px;
  border-color: transparent;
}

button { font: inherit; cursor: pointer; }

/* Ou l'on se trouve au clavier. Les champs portaient deja leur lisere ; les
   boutons et les liens n'avaient rien, et parcourir l'ecran a la tabulation
   revenait a avancer les yeux fermes. « focus-visible » plutot que « focus » :
   il ne parait pas sous la souris, ou il n'apprendrait rien. */
button:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--azur-600);
  outline-offset: 2px;
  border-radius: var(--rayon);
}



/* Un bouton ferme se voit ferme : sans cela on clique, rien ne se passe, et on
   croit l'ecran casse. Le titre dit alors ce qui manque. */
button:disabled {
  opacity: .45;
  cursor: not-allowed;
  filter: grayscale(.35);
}

.principal:disabled:hover { background: var(--azur-600); }
select:disabled { background: var(--ivoire); color: var(--encre-400); cursor: not-allowed; }

.principal {
  padding: 9px 16px;
  border: 0;
  border-radius: var(--rayon);
  background: var(--azur-600);
  color: #fff;
  font-weight: 600;
}

.principal:hover { background: var(--azur-700); }

.discret {
  padding: 6px 12px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--papier);
  color: var(--encre-600);
  font-size: 13.5px;
  font-weight: 550;
}

.discret:hover { border-color: var(--azur-600); color: var(--azur-700); }

/* Le bouton d'une action sans retour. Il porte la couleur des comptes
   suspendus, celle que ce service donne deja a ce qui ferme : on ne clique pas
   dessus par megarde. */
.destructif {
  padding: 9px 16px;
  border: 0;
  border-radius: var(--rayon);
  background: var(--suspendu);
  color: #fff;
  font-weight: 600;
}

.destructif:hover { background: #8f2b21; }
.destructif:disabled:hover { background: var(--suspendu); }

/* Le releve du lot, entre l'avertissement et la confirmation : il lui faut
   respirer des deux cotes, sinon le champ semble en faire partie. */
#recap-lot { margin-bottom: 20px; }

/* Ce qu'on perd, dit avant de le perdre. */
.avertissement {
  margin: -6px 0 18px;
  padding: 11px 13px;
  border-radius: var(--rayon);
  background: #f8e3e0;
  color: var(--suspendu);
  font-size: 13.5px;
}

.lien {
  border: 0;
  background: none;
  color: inherit;
  opacity: .8;
  text-decoration: underline;
  padding: 4px;
  font-size: 13.5px;
}

.lien:hover { opacity: 1; }

/* -------------------------------------------------------- fil d'Ariane */

.fil {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px;
  margin: -4px 0 14px;
  font-size: 13.5px;
}

.fil .separateur { color: var(--encre-400); padding: 0 2px; }
.fil .ici { color: var(--encre-900); font-weight: 600; padding: 4px 2px; }

/* Un dossier ouvert se clique, comme dans un explorateur de fichiers. Un
   bouton plutot qu'un lien : il n'y a pas d'adresse a copier derriere, et un
   lien mort se remarque a la premiere tentative. */
.entree {
  border: 0;
  background: none;
  padding: 4px 2px;
  color: var(--azur-700);
  font: inherit;
  font-weight: 550;
  text-align: left;
}

/* Toute la cellule, et non le seul mot : viser huit caracteres au milieu
   d'une ligne large de mille pixels demande une precision que rien ne
   justifie. Le survol ne souligne que le texte, la boite ne se voit pas. */
.entree { display: block; width: 100%; }

.entree:hover { text-decoration: underline; }

/* ---------------------------------------------------------------- table */

.table {
  width: 100%;
  border-collapse: collapse;
  background: var(--papier);
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  overflow: hidden;
  box-shadow: var(--ombre);
}

/* L'entete reste sous les yeux quand la liste defile : une section du Nawa
   porte quatre-vingt-huit comites, et arrive au bas de la page on ne sait plus
   quelle colonne dit quoi. */
.table th {
  position: sticky;
  top: 0;
  z-index: 1;
  text-align: left;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--encre-400);
  font-weight: 650;
  padding: 11px 14px;
  border-bottom: 1px solid var(--encre-200);
  background: #f8fafc;
}

.table td { padding: 12px 14px; border-bottom: 1px solid var(--encre-100); }
.table tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: #f8fafc; }

.nombre { text-align: right; font-variant-numeric: tabular-nums; }
.actions { text-align: right; white-space: nowrap; }

/* L'entete suit sa colonne. « .table th » l'emportait sur « .nombre » d'une
   specificite, et le titre restait a gauche pendant que les chiffres se
   rangeaient a droite : dans une colonne large, les deux finissaient a trois
   cents pixels l'un de l'autre. */
.table th.nombre { text-align: right; }

/* Chaque colonne a la largeur de ce qu'elle porte, sauf celle du nom, qui
   prend ce qui reste. Sans cela le nom s'etalait sur toute la table et
   repoussait les nombres au loin, seuls contre le bord.

   « width: 1% » demande le minimum : un tableau donne alors a la colonne la
   largeur de son contenu, et distribue le surplus aux colonnes sans largeur. */
.table th.nombre, .table th.actions { width: 1%; white-space: nowrap; }

/* La valeur ne se casse pas non plus : reduite au minimum, la colonne pliait
   « 2.0 Mo » sur deux lignes. */
.table td.nombre { white-space: nowrap; }

/* Les deux tableaux du decoupage ne portent que quatre colonnes, toutes
   courtes : un nom, deux nombres, deux boutons. Etales sur toute la page, ils
   laissaient neuf cents pixels de vide entre le nom et son compte, et l'oeil
   devait traverser la ligne pour les relier. */
#table-regions, #table-sfed { max-width: 880px; }
.actions button + button { margin-left: 6px; }

/* Les actions ne paraissent qu'au survol de leur ligne. Deux boutons repetes
   sur onze sections font vingt-deux boutons, et le nom — la seule chose qu'on
   lit en parcourant — se noie au milieu. La place reste reservee : sans quoi
   la ligne s'elargirait sous le curseur.

   « focus-within » les rend au clavier, ou il n'y a pas de survol pour les
   appeler. */
.table .actions button {
  opacity: 0;
  transition: opacity .12s ease;
}

.table tr:hover .actions button,
.table tr:focus-within .actions button { opacity: 1; }

/* Sur un ecran tactile, rien ne survole : elles restent la. */
@media (hover: none) {
  .table .actions button { opacity: 1; }
}

/* Ce qui detruit se distingue de ce qui corrige — au moment de le viser, pas
   avant : un rouge permanent sur chaque ligne crierait sans rien dire. */
.danger:hover { border-color: var(--suspendu); color: var(--suspendu); }

/* Sauf au fond d'une fenetre de confirmation, ou il n'y a plus rien a menager :
   on a lu l'avertissement et retape le nom. Le bouton dit ce qu'il fait. */
.principal.danger,
.principal.danger:hover {
  border-color: var(--suspendu);
  background: var(--suspendu);
  color: #fff;
}

.puce {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}

.puce.actif { background: #ddefe6; color: var(--actif); }
.puce.revoque { background: #f8e3e0; color: var(--suspendu); }

/* Un lot qui n'a pas encore ete depouille. La mandarine, celle que ce service
   donne deja a ce qui attend — l'icone d'archive la porte trois colonnes plus
   a gauche, sur la meme ligne. */
.puce.attente { background: var(--mandarine-100); color: var(--mandarine-600); }

/* Un lot deja sorti du depot. Discret a dessein : ce n'est ni un succes ni une
   alerte, seulement un fait dont on se souvient mal tout seul. */
.puce.exporte { background: var(--encre-100); color: var(--encre-600); }

/* Dans la fenetre de fusion, la date de sortie suit le nom du lot plutot que de
   le pousser sur une seconde ligne. */
.colonne-choix .puce.exporte { margin-left: auto; font-size: 11.5px; }

.faible { color: var(--encre-400); }

/* La ligne qu'on vient d'ecrire, remontee en tete de sa liste. Le lisere la
   designe sans la crier : elle est deja premiere, il ne reste qu'a confirmer
   que c'est bien elle. Il tient jusqu'au changement d'ecran, le temps qu'on la
   voie. */
.table tbody tr.neuf,
.table tbody tr.neuf:hover { background: var(--azur-100); }
.table tbody tr.neuf td:first-child { box-shadow: inset 3px 0 0 var(--azur-600); }

/* Un lot porte son archive devant son nom : c'est ce qui distingue d'un coup
   d'oeil un fichier a telecharger du reste de la page. La mandarine — celle
   qui marque ce qui attend — plutot que l'encre : l'icone doit se voir sans
   qu'on la cherche. */
.lot { display: inline-flex; align-items: center; gap: 10px; }

.lot .icone { flex: none; display: flex; color: var(--mandarine-600); }
.lot .icone svg { width: 17px; height: 17px; display: block; }

/* Le nom d'un lot s'ouvre : il garde sa chasse fixe — c'est un numero de
   dossier, « 002-01-001 », et les chiffres doivent s'aligner d'une ligne a
   l'autre. */
.lot .entree { display: inline; width: auto; padding: 0; }

/* ------------------------------------------------------- fiche d'un militant */

/* Plus large que les autres fenetres : une fiche porte une photo et une
   vingtaine de champs, que quatre cent quarante pixels casseraient en
   colonnes d'un mot. */
#modale-fiche { width: min(760px, calc(100vw - 32px)); }
/* Vingt-deux champs et une photo depassent un ecran d'ordinateur portable :
   la fenetre defile plutot que de sortir par le bas, ou le bouton « Fermer »
   deviendrait inatteignable.

   Voir « un seul ascenseur » plus bas : c'est le corps qui defile, jamais le
   dialogue. */
#modale-fiche .fiche {
  padding: 0 var(--modale-x, 26px) var(--modale-y, 26px);
  overflow-y: auto;
}

/* La photo a cote des champs tant qu'il y a la place, au-dessus sinon. Le
   reploiement se decide sur la largeur disponible et non sur celle de l'ecran :
   un point de rupture ne dit rien d'une fenetre posee dans un ecran large mais
   etroite elle-meme, et c'est la que les champs se reduisaient a une colonne
   d'un mot. Voir « flex: 1 1 300px » plus bas — c'est lui qui decide. */
.fiche-corps { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 24px; }

.fiche-photo { flex: none; width: 172px; }

/* Le format d'une photo d'identite, et « cover » plutot que « contain » : les
   telephones ne cadrent pas tous pareil, et une image laissee libre ferait
   sauter la mise en page d'une fiche a l'autre. */
.fiche-photo img {
  display: block;
  width: 172px;
  height: 215px;
  object-fit: cover;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--ivoire);
}

.photo-vide {
  display: grid;
  place-items: center;
  width: 172px;
  height: 215px;
  margin: 0;
  border: 1px dashed var(--encre-200);
  border-radius: var(--rayon);
  color: var(--encre-400);
  font-size: 13.5px;
}

/* Sous la photo, a sa largeur : ce sont des gestes sur elle, pas sur la fiche. */
.gestes-photo {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  margin-top: 10px;
}

.gestes-photo .lien { padding-left: 0; padding-right: 0; font-size: 13px; }

/* ------------------------------------------------------- recadrage photo */

/* Plus large que les autres fenetres : on y travaille sur une image, et un
   cadre de quatre cents pixels se manie mal au doigt. Le corps defile si la
   hauteur manque, jamais la fenetre elle-meme. */
#modale-recadrage { width: min(640px, calc(100vw - 20px)); }

.recadrage {
  padding: 0 var(--modale-x, 26px) var(--modale-y, 26px);
  overflow-y: auto;
  max-height: calc(100dvh - 40px);
}

/* Une hauteur fixee : Cropper.js s'adapte a son conteneur, et sans elle une
   photo en portrait deborderait de l'ecran d'un telephone. */
.zone-recadrage {
  height: min(60dvh, 480px);
  background: var(--ivoire);
  border-radius: var(--rayon);
  overflow: hidden;
}

.zone-recadrage img { display: block; max-width: 100%; }

.rotations { display: flex; justify-content: center; gap: 10px; margin-top: 12px; }

/* Au doigt, les poignees de Cropper.js — cinq pixels — ne se saisissent pas :
   on attrape la photo au lieu du cadre. Elles grossissent sur un ecran tactile,
   decalees de moitie pour rester centrees sur le bord. */
@media (pointer: coarse) {
  .cropper-point { width: 16px; height: 16px; opacity: .9; }
  .cropper-point.point-e  { right: -8px;  margin-top: -8px; }
  .cropper-point.point-w  { left: -8px;   margin-top: -8px; }
  .cropper-point.point-n  { top: -8px;    margin-left: -8px; }
  .cropper-point.point-s  { bottom: -8px; margin-left: -8px; }
  .cropper-point.point-ne { right: -8px;  top: -8px; }
  .cropper-point.point-nw { left: -8px;   top: -8px; }
  .cropper-point.point-sw { left: -8px;   bottom: -8px; }
  .cropper-point.point-se { right: -8px;  bottom: -8px; width: 16px; height: 16px; }
}

/* Un telephone couche : large, mais court. La fenetre n'a plus que trois cents
   pixels de haut, et un portrait de deux cent quinze en mangeait les deux
   tiers. Il rapetisse pour laisser passer les champs, qui sont ce qu'on est
   venu lire. */
@media (max-height: 560px) {
  .fiche-photo,
  .fiche-photo img,
  .photo-vide { width: 104px; }

  .fiche-photo img,
  .photo-vide { height: 130px; }

  .photo-vide { font-size: 12px; }

  /* La fenetre respire moins haut : sur trois cents pixels, vingt-six de marge
     en haut et en bas sont deux lignes de perdues. */
  #modale-fiche .fiche { --modale-y: 16px; }
}

/* Deux colonnes : l'intitule, puis la valeur. « max-content » les cale sur le
   plus long des intitules, sans quoi chaque ligne partirait d'un cran
   different. */
.champs-fiche {
  /* Trois cents pixels au moins, faute de quoi la photo passe au-dessus : en
     deca, l'intitule et sa valeur ne tiennent plus cote a cote et chaque ligne
     se casse en deux. */
  flex: 1 1 300px;
  min-width: 0;
  margin: 0;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 7px 18px;
  align-items: baseline;
}

.champs-fiche dt {
  color: var(--encre-400);
  font-size: 12.5px;
  text-align: right;
}

.champs-fiche dd { margin: 0; min-width: 0; overflow-wrap: break-word; }

/* ------------------------------------------- plusieurs comites d'un coup */

/* La zone a plusieurs lignes se tient comme les autres champs, mais garde ses
   retours a la ligne — c'est par eux qu'on separe les comites. */
#st-libelles {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--papier);
  color: var(--encre-900);
  font: inherit;
  resize: vertical;
}

#st-libelles:focus { outline: 2px solid var(--azur-600); outline-offset: -1px; }

/* La liste de confirmation. Numerotee : c'est le numero que chacun prendra, et
   le voir avant d'ecrire evite de decouvrir apres coup qu'on a decale la
   section entiere. Elle defile passe une vingtaine d'entrees plutot que de
   pousser le pied de la fenetre hors de l'ecran. */
.liste-comites {
  max-height: 340px;
  overflow-y: auto;
  margin: 0 0 4px;
  padding: 10px 10px 10px 34px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--ivoire);
}

.liste-comites li { padding: 3px 0; color: var(--encre-900); }
.liste-comites li::marker { color: var(--encre-400); font-size: 12.5px; }

/* Sans numero de depart, la liste ne prejuge pas de ceux qu'ils prendront : le
   service les attribuera a la suite des existants, et afficher « 1, 2, 3 »
   annoncerait des numeros qui ne seront pas les leurs. */
.liste-comites.sans-numero { list-style: disc; }

/* -------------------------------------------- correction d'un militant */

/* Large, comme la fiche : une vingtaine de champs en colonne unique
   demanderaient de defiler sur trois ecrans pour corriger une lettre.

   L'inscription porte les memes champs, plus la photo : meme largeur, meme
   corps qui defile. */
#modale-correction,
#modale-inscription { width: min(760px, calc(100vw - 32px)); }

/* Le corps defile, l'entete et le pied tiennent : « Enregistrer » doit rester
   sous la main quel que soit le champ qu'on regarde. En bloc, jamais en
   « flex » — voir « un seul ascenseur » plus bas. */
#modale-correction .correction-corps,
#modale-inscription .correction-corps {
  max-height: calc(100vh - 260px);
  max-height: calc(100dvh - 260px);
  overflow-y: auto;
}

/* Les trois groupes de la correction. Sans eux, dix-huit champs se suivent
   sans qu'on sache lequel touche a quoi. */
.groupe-champs {
  margin: 22px 0 10px;
  color: var(--foret-900);
  font-size: 14px;
  border-bottom: 1px solid var(--encre-100);
  padding-bottom: 5px;
}

.groupe-champs:first-of-type { margin-top: 0; }

/* ------------------------------------ actions de l'ecran des militants */

/* La loupe garde sa place en clair : revenir aux criteres est le geste le plus
   frequent de l'ecran, et le ranger dans un menu l'aurait mis a deux clics. */
.bouton-icone {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--papier);
  color: var(--encre-600);
  cursor: pointer;
}

.bouton-icone:hover { background: var(--ivoire); color: var(--encre-900); }
.bouton-icone svg { width: 18px; height: 18px; display: block; }

.menu-porteur { position: relative; }

/* Le meme dessin que le bouton de compte, en sombre sur clair : c'est le meme
   geste — un intitule qui ne change pas, et ce qu'il ouvre en dessous. */
.bouton-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 38px;
  padding: 0 12px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--papier);
  color: var(--encre-900);
  font: inherit;
  cursor: pointer;
}

.bouton-actions:hover,
.bouton-actions[aria-expanded="true"] { background: var(--ivoire); }

.bouton-actions[aria-expanded="true"] .chevron {
  margin-top: 2px;
  transform: rotate(-135deg);
}

/* Aligne a droite : il descend d'un bouton pose contre le bord de la vue. */
.menu-deroule {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  min-width: 210px;
  padding: 6px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--papier);
  box-shadow: var(--ombre);
}

/* Les entrees se suivent sans marge haute : dans le menu du compte, celle-ci
   separait le nom du bouton ; ici il n'y a que des actions. */
.menu-deroule .menu-action { margin-top: 0; }
.menu-deroule .menu-action + .menu-action { margin-top: 2px; }

/* ------------------------------------------------ le choix des colonnes */

/* Dans le formulaire de recherche, et replie : vingt-deux cases depliees
   reprendraient la place que les criteres ont justement cessé d'occuper. Le
   resume dit combien sont retenues, ce qui suffit a savoir s'il faut ouvrir. */
.bloc-colonnes {
  grid-column: 1 / -1;
  margin: 0 0 16px;
}

.bloc-colonnes > summary {
  padding: 8px 2px;
  color: var(--encre-900);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.bloc-colonnes > summary:hover { color: var(--foret-900); }
.bloc-colonnes > summary .fine { font-weight: 400; }

.colonnes-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 4px 0 10px;
}

/* En colonnes : vingt-deux intitules en une seule file demanderaient de defiler
   pour savoir ce qui existe, et c'est justement la question qu'on se pose. */
.liste-colonnes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 2px 16px;
  padding: 12px 14px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon-large);
  background: var(--papier);
}

.colonne-choix {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 4px;
  border-radius: var(--rayon);
  font-size: 14px;
  cursor: pointer;
}

.colonne-choix:hover { background: var(--ivoire); }
.colonne-choix input { width: auto; }

/* --------------------------------------------- apercu avant impression */

#modale-impression { width: min(860px, calc(100vw - 32px)); }

.apercu-impression {
  max-height: calc(100vh - 220px);
  max-height: calc(100dvh - 220px);
  overflow: auto;
  padding: 22px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  /* Blanc franc, et non l'ivoire du reste : c'est du papier qu'on regarde. */
  background: #fff;
}

.feuille-entete { margin-bottom: 14px; }

.feuille-entete h1 {
  margin: 0;
  font-size: 17px;
  color: var(--encre-900);
}

.feuille-sous-titre {
  margin: 4px 0 0;
  color: var(--encre-600);
  font-size: 12.5px;
}

.feuille-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}

.feuille-table th,
.feuille-table td {
  padding: 5px 7px;
  border: 1px solid var(--encre-200);
  text-align: left;
  vertical-align: top;
}

.feuille-table th {
  background: var(--ivoire);
  font-weight: 600;
}

/* Designees par leur cle et non par leur rang : les colonnes se choisissent,
   et un style pose sur « la quatrieme » viserait autre chose des qu'on en
   decoche une. */
.feuille-table td[data-cle="numero"],
.feuille-table td[data-cle="identifiant"] {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.feuille-table td[data-cle="dateNaissance"],
.feuille-table td[data-cle="telephone"] { white-space: nowrap; }

/* Sur l'ecran, cette zone n'existe pas. */
#zone-impression { display: none; }

@media print {
  /* Tout disparait sauf la feuille — la barre, la colonne de gauche, les
     fenetres ouvertes. Un dialogue ouvert est dans le « top layer », que les
     navigateurs impriment chacun a leur facon : on n'imprime donc jamais
     depuis lui, mais depuis ce bloc ordinaire. */
  body > * { display: none !important; }
  body > #zone-impression { display: block !important; }

  @page { margin: 14mm; }

  body { background: #fff; color: #000; }

  .feuille-entete h1 { font-size: 15pt; color: #000; }
  .feuille-sous-titre { color: #333; font-size: 9pt; }

  .feuille-table { font-size: 9pt; }

  /* L'en-tete se repete en haut de chaque page : sans lui, la deuxieme feuille
     est une grille de valeurs dont on ne sait plus ce qu'elles nomment. */
  .feuille-table thead { display: table-header-group; }
  .feuille-table tr { break-inside: avoid; page-break-inside: avoid; }

  .feuille-table th, .feuille-table td {
    border: 1px solid #999;
    padding: 3pt 4pt;
  }

  .feuille-table th { background: #eee !important; -webkit-print-color-adjust: exact; }
}

/* ----------------------------------------- listes deroulantes filtrantes */

/* Le « select » reste la source de verite ; il ne se voit plus, c'est le champ
   qui le manœuvre. « display:none » plutot que l'attribut « hidden » : rien ne
   doit le rendre a nouveau visible par inadvertance. */
.combo { position: relative; }
.combo select { display: none; }

/* Le champ se donne l'air d'une liste deroulante — meme cadre, meme chevron —
   parce qu'il en est une : le montrer comme un champ de texte ferait croire
   qu'on peut y ecrire ce qu'on veut. */
.combo-champ {
  width: 100%;
  padding-right: 30px;
  background-image:
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 15px;
  cursor: pointer;
}

.combo-champ:focus { cursor: text; }
.combo-champ:disabled { cursor: default; opacity: 0.65; }

/* Posee en « fixed » par le script : sans cela, le corps defilant des fenetres
   de correction et d'inscription la couperait a trois options. */
.combo-liste {
  position: fixed;
  z-index: 60;
  margin: 0;
  padding: 4px;
  list-style: none;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  box-shadow: 0 10px 28px rgba(17, 24, 39, 0.16);
}

.combo-liste [role="option"] {
  padding: 7px 10px;
  border-radius: var(--rayon);
  font-size: 14px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Le curseur du clavier, distinct du choix courant : on parcourt la liste sans
   avoir encore rien choisi, et confondre les deux ferait croire que c'est fait. */
.combo-liste .combo-actif { background: var(--encre-100); }
.combo-liste .combo-choisie { font-weight: 600; color: var(--foret-900); }

/* L'entree qui ne designe rien — « Toutes les fédérations », « Aucune » : un
   retour en arriere, pas un choix parmi les autres. */
.combo-liste .combo-vide { color: var(--encre-600); font-style: italic; }

.combo-rien {
  padding: 7px 10px;
  color: var(--encre-400);
  font-size: 13.5px;
}

/* Une case a cocher se lit avec son intitule, pas au-dessus de lui. */
.champ-case label { display: flex; align-items: center; gap: 8px; font-weight: 500; }
.champ-case input { width: auto; }

/* La photo cherchee sur le disque. L'apercu n'est pas un ornement : il est le
   seul moyen de voir qu'on a pris le bon fichier avant de l'envoyer, et une
   photo d'identite ressemble beaucoup a une autre dans une liste de noms. */
.champ-photo input[type="file"] { width: 100%; }

#n-photo-apercu {
  display: block;
  margin-top: 10px;
  /* Bornee en hauteur, jamais deformee : une photo au format portrait et une
     au format paysage doivent rester lisibles au meme endroit. */
  max-height: 160px;
  max-width: 100%;
  border-radius: var(--rayon);
  border: 1px solid var(--encre-200);
}

.champ-photo .lien { margin-top: 6px; padding-left: 0; }

/* ------------------------------------------------ decision sur un doublon */

/* La plus large des fenetres : deux fiches cote a cote et quinze lignes de
   comparaison. Sous cette largeur, les valeurs se plient et deux colonnes
   inegales cassent l'alignement qui fait tout l'interet de l'ecran. */
#modale-conflit { width: min(860px, calc(100vw - 32px)); }

#modale-conflit .conflit {
  padding: 0 var(--modale-x, 26px) var(--modale-y, 26px);
  overflow-y: auto;
}

/* ------------------------------------------------------- un seul ascenseur */

/* Les deux fenetres qui defilent bornent leur hauteur elles-memes et s'ecretent.

   Sans cela, elles defilaient a deux etages : le dialogue a une hauteur maximale
   par defaut plus courte que celle qu'on donnait au corps, si bien qu'il prenait
   son propre ascenseur. L'entete du corps avait beau coller, l'ascenseur
   exterieur la faisait sortir par le haut — et l'on voyait le contenu passer
   au-dessus du titre.

   « dvh » et non « vh » : sur un telephone, « vh » compte l'ecran entier, barre
   d'adresse et barre d'outils comprises, et la fenetre deborderait de la zone
   visible. Les deux declarations : la seconde ecrase la premiere la ou l'unite
   existe, et les navigateurs qui l'ignorent gardent « vh ».

   EN BLOC, ET NON EN « FLEX ». Le corps etait un enfant flexible en « flex: 1 »,
   donc de base zero, dans un conteneur de hauteur automatique. Chromium en
   deduit la hauteur du contenu ; Safari s'en tient a la base et rend zero. Sur
   un iPhone, la fenetre se reduisait alors a une bande blanche de quelques
   pixels — le contenu etait la, ecrete par le « overflow: hidden » du dialogue.

   La hauteur automatique d'un bloc, elle, ne se discute pas : c'est celle de son
   contenu, partout. Le corps reprend la borne du dialogue par « inherit » et
   defile dedans ; l'entete y colle comme avant. */
#modale-fiche,
#modale-conflit {
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  overflow: hidden;
}

#modale-fiche .fiche,
#modale-conflit .conflit {
  max-height: inherit;
  overflow-y: auto;
}

/* Le compte des doublons, a droite du titre. Il dit ou l'on en est dans une
   file qu'on ne voit pas : sans lui, trancher le troisieme d'une serie de huit
   ressemble a trancher le dernier. */
/* Colle au titre, et c'est lui qui pousse la croix au bord : « Doublon 2 sur 5 »
   se lit d'un trait. Le titre rend donc sa marge automatique — sinon les deux
   se partageraient la place et le compte flotterait au milieu de l'entete. */
#modale-conflit .entete-modale h3 { margin-right: 0; }

.compte-conflits {
  flex: none;
  margin-right: auto;
  padding: 3px 10px;
  border-radius: 20px;
  background: var(--ivoire);
  color: var(--encre-600);
  font-size: 12.5px;
  font-weight: 600;
}

/* Le motif du doublon. En jaune et non en rouge : ce n'est pas une perte, c'est
   une question. */
#conflit-motif {
  background: var(--mandarine-100);
  color: #8a4400;
}

/* Le cadre de la comparaison. Sans « overflow » ici : un conteneur qui defile,
   fut-ce sur un seul axe, devient le repere de tout ce qui colle dedans — et
   l'entete du tableau n'aurait plus la fenetre par rapport a quoi se caler. Le
   defilement lateral n'est pose que sous 640 pixels, ou il devient necessaire ;
   l'entete y perd son maintien, ce qui est le moindre des deux maux sur un
   ecran de telephone. */
.conflit-tableau { margin-top: 4px; }

/* Pour la meme raison, le tableau ne s'ecrete pas. L'ecretage n'etait la que
   pour arrondir ses coins ; les cellules d'entete les portent desormais
   elles-memes. */
.conflit-table { overflow: visible; }
.conflit-table thead th:first-child { border-top-left-radius: var(--rayon); }
.conflit-table thead th:last-child { border-top-right-radius: var(--rayon); }

/* L'intitule, en retrait : c'est le repere de la ligne, pas une valeur de
   plus. Sa colonne prend le minimum, les deux autres se partagent le reste a
   parts egales — une comparaison ou l'une des deux colonnes serait plus large
   suggererait qu'elle compte davantage. */
.conflit-table th.conflit-intitule, .conflit-table td.conflit-intitule {
  width: 1%;
  white-space: nowrap;
  color: var(--encre-400);
  font-size: 12.5px;
}

.conflit-table th:nth-child(2), .conflit-table th:nth-child(3) { width: 45%; }

.conflit-table td { padding: 8px 14px; }

/* L'entete du tableau se cale sous celle de la fenetre, qui ne defile pas non
   plus : a « top: 0 », elle glisserait dessous et disparaitrait. Sa hauteur est
   mesuree a l'ouverture — le CSS ne sait pas la lire — et posee sur la fenetre
   par montrerConflit(). Sans elle, arrive au bas d'une comparaison de quinze
   lignes, on ne sait plus laquelle des deux colonnes est le lot. */
.conflit-table thead th { top: var(--haut-entete, 0px); }

/* ------------------------------------------------- les deux visages, en tete */

/* Deux fiches rapprochees par leur empreinte portent forcement les memes nom,
   prenoms et date de naissance : c'est la photo qui dit s'il s'agit de deux
   fois la meme personne. Elle ouvre donc la comparaison, dans la colonne de
   chacun — l'alignement vient de la table, sans rien a caler a la main. */
.conflit-table tbody tr.ligne-photos td { padding: 14px; vertical-align: top; }

/* Le cadre est pose avant l'image, a sa taille definitive : sans lui, la ligne
   grandirait a l'arrivee de la premiere photo et l'ecran sauterait sous le
   curseur, juste au-dessus des boutons de decision. */
.cadre-photo {
  display: grid;
  place-items: center;
  width: 152px;
  height: 190px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--ivoire);
  overflow: hidden;
}

/* Le format d'une photo d'identite, et « cover » plutot que « contain » : les
   telephones ne cadrent pas tous pareil, et deux images laissees libres se
   compareraient a des echelles differentes. */
.cadre-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cadre-photo p {
  margin: 0;
  padding: 0 10px;
  color: var(--encre-400);
  font-size: 12.5px;
  text-align: center;
}

/* Les couleurs disent le doublon, pas le tableau.

   Ce qui « concorde » est ce qui accuse : deux fiches qui portent le meme
   telephone, la meme section et la meme carte d'electeur sont la meme personne,
   et chaque ligne de plus resserre l'etau. D'ou la mandarine, celle que ce
   service donne a ce qui attend qu'on s'en occupe.

   Ce qui « differe » est ce qui disculpe : c'est par la qu'on voit deux
   homonymes nes le meme jour, et que « Garder les deux » devient la bonne
   reponse. D'ou le vert.

   Nom, prenoms et date de naissance sont concordants par construction — c'est
   l'empreinte qui a rapproche les deux fiches — donc la mandarine domine
   toujours. Elle est pour cela plus pale que le vert : ce qu'on cherche des
   yeux est la ligne rare, et une couleur qui couvre les trois quarts du tableau
   ne doit pas crier plus fort qu'elle. */
.conflit-table tbody tr.pareil,
.conflit-table tbody tr.pareil:hover { background: #fff3e5; }

.conflit-table tbody tr.pareil td:first-child {
  box-shadow: inset 3px 0 0 var(--mandarine-600);
}

.conflit-table tbody tr.ecart,
.conflit-table tbody tr.ecart:hover { background: #ddefe6; }

.conflit-table tbody tr.ecart td:first-child {
  box-shadow: inset 3px 0 0 var(--actif);
}

/* Les trois issues se rangent comme les colonnes qu'elles designent : celle qui
   fait gagner le lot sous le lot, celle qui fait gagner la base sous la base, et
   « les deux » entre les deux. On choisit alors du meme geste qu'on a compare,
   sans avoir a relire quel bouton designe quelle colonne.

   Toutes trois de meme poids : aucune n'est recommandee, et une seule en bleu
   plein dirait le contraire de ce que cet ecran demande — regarder, puis
   trancher. */
#modale-conflit .pied { justify-content: space-between; }

/* Le releve de fin, entre le titre et le rappel : il lui faut respirer des deux
   cotes, comme celui de la suppression. */
#fin-recap { margin-bottom: 18px; }

/* Une precision discrete a cote d'un titre ou d'un intitule de colonne. */
th .fine, h2 .fine, h3 .fine {
  color: var(--encre-400);
  font-weight: 500;
  margin-left: 6px;
}

/* --------------------------------------------------------------- modales */

dialog {
  border: 0;
  border-radius: var(--rayon-large);
  padding: 0;
  box-shadow: 0 12px 48px rgba(18, 41, 74, .28);
  width: min(440px, calc(100vw - 32px));
}

dialog::backdrop { background: rgba(18, 41, 74, .46); }
/* La marge d'une fenetre passe par une variable : l'entete l'annule pour
   s'etendre bord a bord, et elle retrecit sous 640 pixels. Deux valeurs ecrites
   a la main auraient fini par diverger, et l'entete deborderait de trois pixels
   sur un cote. */
dialog form { padding: 0 var(--modale-x, 26px) var(--modale-y, 26px); }
dialog h3 { color: var(--foret-900); }

/* Le titre et la croix sur une meme ligne. La croix ferme sans rien valider,
   comme « Annuler » — c'est le geste qu'on cherche en haut a droite avant de
   lire le pied de la fenetre.

   L'entete ne defile pas avec le contenu : elle reste en haut, quoi qu'on
   descende. Une fiche porte vingt-deux champs et une comparaison de doublon en
   porte davantage ; passe le premier ecran, on ne sait plus de quoi on lit les
   valeurs, ni ou l'on ferme.

   Elle s'etend bord a bord parce qu'un fond qui s'arrete a la marge laisserait
   le texte remonter par les cotes. Sur les cotes, une marge negative annule
   celle de la fenetre ; en haut, non — un « margin-top » negatif decalerait le
   point de collage d'autant, et le contenu defilerait dans la bande ainsi
   laissee au-dessus. La marge haute est donc retiree de la fenetre et donnee a
   l'entete, qui la porte comme rembourrage.

   Ses coins suivent ceux de la fenetre : sans cela, un fond carre recouvrirait
   les arrondis du haut. */
.entete-modale {
  position: sticky;
  top: 0;
  z-index: 2;

  display: flex;
  align-items: baseline;
  gap: 14px;

  margin: 0 calc(-1 * var(--modale-x, 26px)) 16px;
  padding: var(--modale-y, 26px) var(--modale-x, 26px) 14px;

  background: var(--papier);
  border-bottom: 1px solid var(--encre-100);
  border-radius: var(--rayon-large) var(--rayon-large) 0 0;
}

.entete-modale h3 { margin-right: auto; }

.fermer {
  flex: none;
  border: 0;
  background: none;
  color: var(--encre-400);
  font-size: 22px;
  line-height: 1;
  padding: 2px 6px;
  border-radius: var(--rayon);
}

.fermer:hover { background: var(--ivoire); color: var(--encre-900); }

/* Ou le compte va atterrir, sous le titre. En italique : c'est un rappel de ce
   que l'ecran a deja decide, pas un champ de plus a remplir. */
.rappel {
  margin: -14px 0 20px;
  font-style: italic;
  color: var(--encre-600);
  font-size: 13.5px;
}

.champ { margin-bottom: 15px; }
.champ label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px; }
.champ small { display: block; margin-top: 5px; color: var(--encre-400); font-size: 12.5px; }

.pied { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
.pied .lien { color: var(--encre-600); }

.erreur {
  margin: 4px 0 0;
  padding: 9px 12px;
  border-radius: var(--rayon);
  background: #f8e3e0;
  color: var(--suspendu);
  font-size: 13.5px;
}

/* -------------------------------------------------------------- messages */

.messages {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 10;
}

.message {
  padding: 11px 18px;
  border-radius: var(--rayon);
  background: var(--foret-900);
  color: #fff;
  font-size: 14px;
  box-shadow: 0 6px 24px rgba(18, 41, 74, .34);
}

.message.echec { background: var(--suspendu); }

/* Le perimetre par lequel on entre dans les comptes : un champ qui le porte,
   un bouton qui l'ouvre. */
.perimetre { max-width: 620px; margin-bottom: 22px; }

.perimetre > label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 5px;
}

/* Le bouton vit dans le champ : c'est un seul controle, celui par lequel on
   choisit. La bordure et le fond sont portes par la ligne, l'input n'en a plus
   — sans quoi on verrait deux cadres imbriques. */
.perimetre-ligne {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px;
  border: 1px solid var(--encre-200);
  border-radius: var(--rayon);
  background: var(--ivoire);
}

/* Le liseré du champ suit le clavier : il se pose sur la ligne entiere,
   bouton compris, puisque c'est elle qui a l'air d'un champ. */
.perimetre-ligne:focus-within {
  outline: 2px solid var(--azur-600);
  outline-offset: -1px;
  border-color: transparent;
}

/* Grise, et non desactive : le champ se lit, se selectionne et se copie, mais
   ne se saisit pas. Une federation tapee a la main ne designerait rien. */
.perimetre-ligne input {
  flex: 1;
  min-width: 0;
  padding: 6px 8px;
  border: 0;
  background: none;
  color: var(--encre-900);
  font-weight: 600;
  cursor: default;
}

.perimetre-ligne input:focus { outline: none; }
.perimetre-ligne input::placeholder { font-weight: 400; color: var(--encre-400); }

.perimetre-ligne .discret {
  flex: none;
  white-space: nowrap;
  background: var(--papier);
}

.perimetre small {
  display: block;
  margin-top: 6px;
  color: var(--encre-400);
  font-size: 12.5px;
}

/* ------------------------------------------------------------ etroit */

/* Trois paliers, chacun pour ce qui casse a cette largeur-la :
   900 px, la mise en page a deux colonnes ; 640 px, les tableaux ;
   480 px, les fenetres et les boutons.

   Le premier etait a 640 : entre les deux, une tablette ou une fenetre reduite
   de moitie gardait 232 pixels de navigation devant un tableau de sept
   colonnes, et tout s'ecrasait sans que rien ne s'adapte. */

@media (max-width: 900px) {
  /* La navigation passe au-dessus de l'ecran : 232 pixels de colonne sur une
     tablette ne laissent plus rien au contenu. */
  /* « align-items: flex-start » tient la colonne laterale a sa hauteur tant
     qu'elle est a cote ; l'une sous l'autre, il empechait « main » de prendre
     la largeur — toute la page se rangeait alors sur deux cents pixels. */
  .corps { flex-direction: column; align-items: stretch; }

  .lateral {
    width: 100%;
    min-height: 0;
    position: static;
    border-right: 0;
    border-bottom: 1px solid var(--encre-200);
    padding: 8px 10px;
  }

  /* Un bandeau de deux lignes plutot qu'un accordeon empile : les modules
     l'un sous l'autre poussaient le contenu sous la ligne de flottaison, et
     l'ecran s'ouvrait sur sa navigation.

     « display: contents » fait remonter le bouton et ses sous-modules dans le
     meme rang, ou l'ordre les separe : les modules d'abord, les sous-modules
     du seul module ouvert en dessous. */
  .lateral { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
  .module { display: contents; }

  .module > button {
    order: 1;
    width: auto;
    flex: 0 0 auto;
    padding: 7px 12px;
  }

  .sous-modules {
    order: 2;
    flex-basis: 100%;
    display: flex;
    gap: 6px;
    padding: 2px 0 2px 2px;
  }

  /* Le lisere passe dessous : a l'horizontale, un trait a gauche ne marque
     plus la colonne dont il souligne l'entree. */
  .sous-modules a {
    border-left: 0;
    border-bottom: 2px solid var(--encre-100);
    border-radius: var(--rayon) var(--rayon) 0 0;
    padding: 6px 12px;
  }

  .sous-modules a.actif { border-left: 0; border-bottom-color: var(--azur-600); }

  .barre { flex-wrap: wrap; height: auto; padding: 12px 16px; gap: 12px; }
  main { padding: 20px 16px 50px; }

  /* Passe a la ligne, le compte n'a plus rien a pousser : « margin-left: auto »
     l'envoyait seul contre le bord droit. */
  .compte { margin-left: 0; }

  /* Le titre et son bouton l'un sous l'autre : cote a cote, le bouton ecrase
     le titre ou passe a la ligne de travers. Le bouton garde sa largeur — etire
     d'un bord a l'autre, « Ajouter » pese plus lourd que le titre. */
  .entete-vue { flex-direction: column; align-items: flex-start; gap: 12px; }
  .entete-vue h2 { margin-right: 0; }

  /* Le champ du perimetre s'etale — il porte deux noms et un bouton. Le choix
     de la region reste court : un menu de deux entrees etire sur toute la
     largeur ne se lit pas mieux. */
  .perimetre { max-width: none; }

  /* Un message long tenait sur une seule ligne, et debordait des deux cotes. */
  .messages { left: 16px; right: 16px; transform: none; }
  .message { text-align: center; }
}

@media (max-width: 640px) {
  main { padding: 16px 12px 50px; }

  /* Sur un telephone, un menu deroulant se vise mieux large. */
  .champ-region { max-width: none; }

  /* La photo passe au-dessus, et les champs sur une seule colonne : cote a
     cote, il ne reste a la valeur qu'une dizaine de caracteres, et « Monsieur »
     se coupe en deux. */
  .fiche-corps { flex-direction: column; align-items: center; gap: 18px; }
  .champs-fiche { width: 100%; grid-template-columns: 1fr; gap: 2px 0; }
  .champs-fiche dt { text-align: left; }
  .champs-fiche dd { margin-bottom: 10px; }
  #modale-fiche .fiche { --modale-y: 22px; --modale-x: 18px; }
  #modale-conflit .conflit { --modale-y: 20px; --modale-x: 18px; }

  /* Le tableau devient une carte par ligne. Sept colonnes ne tiennent pas dans
     la largeur d'un telephone, et les faire defiler de cote n'est pas s'y
     adapter : on lit une ligne en la suivant du regard, pas en la poussant.

     Chaque cellule porte alors le nom de sa colonne — pose par
     etiqueterColonnes() — puisqu'il n'y a plus d'entete au-dessus pour le
     dire. */
  .table, .table tbody, .table tr, .table td { display: block; }

  /* Sauf la comparaison d'un doublon : empilee, elle ne compare plus rien.
     Elle defile lateralement dans son cadre.

     Ecrite « .table.conflit-table » et non « .conflit-table » seul : les regles
     qui suivent portent sur « .table », et a specificite egale la derniere
     ecrite l'emporte — l'entete serait repasse a « display: none » trois lignes
     plus bas. */
  .conflit-tableau { overflow-x: auto; }

  .table.conflit-table {
    display: table;
    min-width: 520px;
    background: var(--papier);
    border: 1px solid var(--encre-200);
    border-radius: var(--rayon);
  }

  .table.conflit-table thead { display: table-header-group; }
  .table.conflit-table tbody { display: table-row-group; }

  .table.conflit-table tr {
    display: table-row;
    margin: 0;
    padding: 0;
    border: 0;
    box-shadow: none;
  }

  .table.conflit-table td { display: table-cell; }
  .table thead { display: none; }

  .table {
    border: 0;
    background: none;
    box-shadow: none;
    overflow: visible;
  }

  .table tr {
    background: var(--papier);
    border: 1px solid var(--encre-200);
    border-radius: var(--rayon);
    box-shadow: var(--ombre);
    padding: 4px 0;
  }

  .table tr + tr { margin-top: 10px; }
  .table tbody tr:hover { background: var(--papier); }

  .table td {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 6px 14px;
    border-bottom: 0;
    text-align: left;
  }

  .table td[data-colonne]::before {
    content: attr(data-colonne);
    flex: none;
    width: 38%;
    color: var(--encre-400);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 650;
    line-height: 1.6;
  }

  /* Les nombres reviennent a gauche : alignes a droite, ils se retrouvaient
     loin de leur etiquette, seuls au bord de la carte. */
  .table .nombre { text-align: left; }

  /* En carte, les actions ne se cachent plus : il n'y a pas de ligne a
     survoler, et « hover: none » ne repond que du pointeur — une tablette a
     souris les laisserait invisibles. */
  .table .actions button { opacity: 1; }

  /* Les actions ferment la carte, d'un bord a l'autre. */
  .table td.actions {
    justify-content: flex-end;
    gap: 6px;
    padding: 8px 14px 6px;
    margin-top: 2px;
    border-top: 1px solid var(--encre-100);
  }

  .table td.actions button { margin: 0; }

  /* Une cellule vide ne merite pas sa ligne dans la carte. */
  .table td:empty { display: none; }
}

@media (max-width: 480px) {
  /* Une fenetre presque pleine largeur : sur un telephone, les marges d'un
     dialogue de bureau prennent la moitie de l'ecran. */
  dialog { width: calc(100vw - 20px); }
  dialog form { --modale-y: 20px; --modale-x: 18px; }

  /* Les boutons empiles, l'action principale en haut du pouce. */
  .pied { flex-direction: column-reverse; gap: 8px; }
  .pied button { width: 100%; }

  /* Empilees, mais dans le sens de la lecture : le premier bouton en haut. Le
     « column-reverse » general remonte l'action principale, qui est la derniere
     ecrite ; ici il n'y en a pas, et inverser l'ordre romprait la
     correspondance avec les colonnes. */
  #modale-conflit .pied { flex-direction: column; }

  .connexion { padding: 16px; }
  .carte-connexion { padding: 22px 18px; }


  /* L'etiquette au-dessus de sa valeur : a 38 % de largeur, « Comites de
     base » se casse en trois lignes et la valeur pend a cote. */
  .table td { flex-direction: column; align-items: stretch; gap: 1px; }
  .table td[data-colonne]::before { width: auto; }
  .table td.actions { flex-direction: row; }
}
