/* Jetons de design — vacances-comptes (spec Phase 1 §4, Partie 2).
   Registre Apple/Notion : neutre, un seul accent, bordures fines, ombres
   absentes ou imperceptibles. Point de rupture unique à 720px, mêmes
   gabarits pour toutes les tailles — jamais de feuille mobile séparée. */

:root {
  /* Police : pile système, rien à télécharger */
  --police: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Neutres */
  --gris-000: #ffffff;
  --gris-050: #fafafa;

  /* LE FOND DE L'APPLICATION, distinct de --gris-050 et c'est délibéré.
     Le fond était à #fafafa : trop proche du blanc pour que les cartes s'en
     détachent — elles flottaient sans relief. Valeur reprise de la maquette
     de référence (`recap-maquettes.html`, `--fond`).

     UN JETON À PART, jamais --gris-050 redéfini : ce dernier sert de fond à
     dix surfaces INTERNES (pastilles, bandes, en-têtes, survols) qui, elles,
     doivent rester presque blanches. Les confondre repeindrait l'intérieur
     des cartes en même temps que la page. */
  --fond: #f5f5f2;

  /* Une surface qui porte du CONTENU À COPIER — le message du récap
     aujourd'hui, ce qui viendra demain. Nommée par son rôle et non par sa
     teinte : « gris-150 » aurait invité à s'en servir pour n'importe quel
     fond pâle, et on ne saurait plus lequel on peut bouger.

     Pourquoi pas --gris-050 : posé sur une carte BLANCHE, il est quasi
     invisible — l'encart ne se lisait pas comme un bloc. Et le foncer
     reviendrait à repeindre les dix autres surfaces qui s'en servent et qui,
     elles, doivent rester pâles. */
  --surface-a-copier: #ebebe6;
  --gris-100: #f2f2f2;
  --gris-300: #d8d8d8;
  --gris-500: #8a8a8a;
  --gris-700: #4a4a4a;
  --gris-900: #1c1c1c;

  /* Accent — SEUL endroit où ce hex apparaît dans ce fichier (voir
     tests/test_web_css.py::test_accent_hex_apparait_une_seule_fois).
     Tout le reste en dérive via var(--accent) : changer d'accent reste une
     modification d'une ligne. */
  --accent: #2f7d6e;
  --accent-texte: #ffffff;
  --accent-surface: color-mix(in srgb, var(--accent) 14%, var(--gris-000));
  /* Plus dilué que --accent-surface, et c'est la condition pour que la
     pastille du foyer COURANT (qui, elle, est en --accent-surface) reste
     visible posée dessus. La bande dit « nouveau groupe », pas « c'est
     toi » : deux messages, deux intensités. */
  --accent-bande: color-mix(in srgb, var(--accent) 6%, var(--gris-000));

  /* Sémantique — couleurs de TEXTE uniquement, jamais un aplat. Le signe
     ("+"/"−") porte l'information autant que la teinte. */
  --texte-creancier: #2f7d55;
  --texte-debiteur: #b3432b;
  --texte-anomalie: #b3432b;

  /* Hauteur de l'EN-TÊTE, mesurée sur le rendu réel. UN SEUL nombre
     gouverne trois choses qui doivent s'accorder : le décalage des ancres
     (sans quoi la cible atterrit SOUS l'en-tête collé), la position du
     bandeau de confirmation, et le calage des niveaux collants. Les faire
     dériver l'un de l'autre est le défaut qu'on ne voit qu'à l'usage.
     `tests/test_entete_navigateur.py` compare ce jeton à la hauteur
     RÉELLEMENT rendue, aux deux largeurs : s'ils divergent, il tombe. */
  --hauteur-entete: 130px; /* étroit : titre + barre, deux rangées */

  /* UN SECOND JETON POUR LE CONTEXTE PARAMÈTRES, et pas une barre calibrée
     pour préserver le premier. Deux raisons.

     (1) Un jeton unique serait vrai PAR ACCIDENT même si les deux hauteurs
     coïncidaient aujourd'hui : le premier ajustement de la barre d'onglets
     le rendrait faux sans que rien ne le dise. Le précédent existe —
     `#ligne-N` empruntait `--hauteur-entete` (69px) là où l'obstacle réel
     en faisait 34, et ça marchait par coïncidence jusqu'à ce que quelqu'un
     regarde.

     (2) Calibrer une barre pour préserver un jeton ferait dépendre le design
     d'une contrainte technique, dans le mauvais sens.

     Il est CONSOMMÉ en redéfinissant `--hauteur-entete` sur le corps quand
     la barre des paramètres est là : les deux endroits qui s'en servent
     (décalage des ancres `#foyer-N`/`#ligne-N`, position du bandeau de
     confirmation) n'ont ainsi rien à savoir du contexte.
     `test_cle_le_jeton_css_dit_la_vraie_hauteur` compare CHAQUE valeur à la
     hauteur rendue de SON PROPRE contexte — sinon on remplacerait une
     valeur fausse par deux valeurs invérifiées. */
  --hauteur-entete-parametres: 214px; /* étroit : titre, retour, onglets */

  /* Bordures, rayons, ombres */
  --bordure: 0.5px solid var(--gris-300);
  --rayon: 10px;
  --rayon-lg: 12px;
  --ombre: none;

  /* Échelle typographique (px) */
  --taille-micro: 11px;
  --taille-petite: 12px;
  --taille-corps-sm: 13px;
  --taille-corps: 14px;
  --taille-corps-lg: 15px;
  --taille-titre: 24px;
  --taille-hero: 38px;

  /* Espacement */
  --espace-1: 4px;
  --espace-2: 8px;
  --espace-3: 12px;
  --espace-4: 16px;
  --espace-5: 24px;
  --espace-6: 32px;

  /* Tactile */
  --cible-min: 44px;
}

* {
  box-sizing: border-box;
}

/* `hidden` doit GAGNER, toujours. L'attribut ne pose que `display: none`
   dans la feuille du navigateur : n'importe quelle règle d'auteur qui donne
   un `display` à l'élément l'emporte, et l'élément reste visible pendant
   que le code le croit caché. Vécu sur le panneau des concernés — le JS
   masquait les exemptés des autres comptes, `.concernes-exemptes` était en
   `display: flex`, et l'écran proposait de cocher des cases sans effet.
   `!important` est ici à sa place : c'est l'attribut du DOM qui doit primer
   sur la mise en page, jamais l'inverse. */
[hidden] {
  display: none !important;
}

/* Icônes Lucide, inlinées côté serveur (voir web/icones.py). Elles
   n'apportent aucune couleur : `stroke: currentColor` les fait hériter du
   texte qui les entoure — accent, gris, ou --texte-anomalie. Aucun jeton
   de couleur importé d'ailleurs. La taille suit la typographie (1em), donc
   une icône ne dépasse jamais le texte auquel elle est accolée. */
.icone {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  vertical-align: -0.125em;
}

/* Les icônes de compte portent l'essentiel du repérage sur l'écran le plus
   dense (liste des dépenses). Taille FIXE, pas relative : accolées à une
   étiquette de 11px en capitales, 1em donnait un verre à pied illisible.
   15px reste lisible partout, y compris dans les en-têtes de colonnes du
   tableau de bord. */
.icone-compte {
  width: 15px;
  height: 15px;
}

body {
  margin: 0;
  font-family: var(--police);
  font-size: var(--taille-corps-lg);
  color: var(--gris-900);
  background: var(--fond);
}

/* LE CONTEXTE PARAMÈTRES A SA PROPRE HAUTEUR D'EN-TÊTE. On redéfinit le
   jeton ici plutôt que de le nommer aux points de consommation : le décalage
   des ancres (`#foyer-N`, `#ligne-N` — les deux écrans à ancres SONT dans
   les paramètres) et le bandeau de confirmation continuent d'écrire
   `var(--hauteur-entete)` sans rien savoir du contexte.

   `:has()` est déjà le mécanisme du cadre plein écran de la synthèse et de
   la largeur du bloc d'ajout d'un participant. */
body:has(.nav-parametres) {
  --hauteur-entete: var(--hauteur-entete-parametres);
}

h1 {
  font-size: var(--taille-titre);
  margin: 0 0 var(--espace-4);
}

h2 {
  font-size: var(--taille-corps-lg);
  margin: var(--espace-5) 0 var(--espace-3);
}

/* Boutons — un seul accent (voir --accent ci-dessus), jamais un deuxième
   dérivé pour le bouton secondaire : il se distingue par la bordure, pas
   par une nouvelle teinte. */

.bouton-principal {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--espace-2);
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-4);
  background: var(--accent);
  color: var(--accent-texte);
  border: none;
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: var(--taille-corps-lg);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.bouton-principal:hover,
.bouton-principal:focus-visible {
  opacity: 0.9;
  outline: none;
}

.bouton-secondaire {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Comme `.bouton-principal` : sans lui, « +Ajouter au foyer » se colle à
     son icône. Sans effet sur un bouton sans icône, qui n'a qu'un enfant. */
  gap: var(--espace-2);
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-4);
  background: var(--gris-000);
  color: var(--gris-900);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: var(--taille-corps);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

.bouton-secondaire:hover,
.bouton-secondaire:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.liste-vide {
  color: var(--gris-500);
  font-style: italic;
}

/* La SORTIE du croisement vide n'est pas de la prose : elle reste droite et
   à l'accent, sinon elle se lit comme une partie de la phrase en italique —
   et un lien sans style d'auteur repart au bleu souligné du navigateur, le
   seul endroit de l'app qui l'aurait fait. */
.liste-vide .lien-action {
  font-style: normal;
  margin-left: var(--espace-1);
}

/* En-tête et navigation principale (Phase 2A, Partie 4) */

.entete {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-3);
  padding: var(--espace-3) var(--espace-4);
  background: var(--gris-000);
  border-bottom: var(--bordure);
}

.entete-droite {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
}

.entete-sejour {
  font-size: var(--taille-corps);
  font-weight: 600;
  color: var(--gris-700);
}

.entete-foyer {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  text-decoration: none;
  color: var(--gris-900);
  min-height: var(--cible-min);
  padding: var(--espace-1) var(--espace-2);
  border-radius: var(--rayon);
}

.entete-foyer-nom {
  font-size: var(--taille-corps);
  font-weight: 600;
}

.entete-changer {
  font-size: var(--taille-micro);
  color: var(--gris-500);
}

.entete-parametres {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-2);
  min-height: var(--cible-min);
  padding: var(--espace-1) var(--espace-2);
  border-radius: var(--rayon);
  color: var(--gris-700);
  text-decoration: none;
  font-size: var(--taille-corps);
  font-weight: 600;
}

.entete-parametres .icone {
  width: 20px;
  height: 20px;
}

.entete-parametres--actif {
  color: var(--accent);
  background: var(--accent-surface);
}

/* En étroit, l'engrenage porte seul l'entrée : « Paramètres » en toutes
   lettres ne tient pas à côté du nom du foyer sur 375px. Le libellé reste
   dans le DOM (clip, pas display:none) — un lecteur d'écran lit toujours
   « Paramètres », l'icône seule ne dit rien. */
.entete-parametres-nom {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Barre principale. Troisième enfant de .entete : passe à la ligne en
   étroit (flex-basis: 100%), rejoint la rangée en large. Les marges
   négatives font filer sa bordure jusqu'aux bords, malgré le padding
   horizontal de l'en-tête — sans elles le trait s'arrête 16px trop tôt et
   ressemble à une erreur. */
.nav-principale {
  order: 3;
  flex-basis: 100%;
  display: flex;
  /* Pas de gouttière en étroit : cinq entrées à parts égales occupent la
     largeur, et chaque part EST la zone tactile — l'espacement entre elles
     se prendrait sur le pouce, pas sur le vide. Le padding horizontal
     disparaît pour la même raison ; la bordure, elle, file toujours d'un
     bord à l'autre grâce aux marges négatives. */
  gap: 0;
  margin: var(--espace-3) calc(-1 * var(--espace-4)) calc(-1 * var(--espace-3));
  padding: 0;
  border-top: var(--bordure);
}

/* CINQ entrées sur une ligne de 375px : icône au-dessus d'un libellé court.
   Les quatre d'origine tenaient en toutes lettres ; « Participants » a fait
   casser la barre en deux lignes, et ces 49px de haut se payaient sur TOUS
   les écrans. Toujours pas de défilement horizontal : une entrée qui sort du
   cadre d'un conteneur défilant est invisible, et une navigation dont on
   ignore la fin n'est pas une navigation. */
.nav-lien {
  display: flex;
  flex: 1 1 0; /* cinq parts égales : la barre se répartit, elle ne se replie plus */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  min-height: var(--cible-min);
  padding: var(--espace-1) 2px;
  color: var(--gris-700);
  text-decoration: none;
  font-size: var(--taille-petite);
  font-weight: 600;
  text-align: center;
  box-shadow: inset 0 -2px 0 transparent;
}

.nav-lien-icone {
  width: 22px;
  height: 22px;
}

/* Le libellé LONG reste dans le DOM, hors de vue : l'icône et « Régler » ne
   disent pas à eux seuls « Règlement » à un lecteur d'écran. Le court, lui,
   est décoratif (aria-hidden dans le gabarit) — sans quoi chaque entrée
   serait annoncée deux fois. */
.nav-lien-long {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* L'accent signale la SÉLECTION — exactement son rôle dans le système
   (spec Phase 1, Partie 2). Le soulignement est posé en ombre intérieure
   pour qu'aucune ligne ne bouge entre l'état actif et l'inactif. */
.nav-lien--actif {
  color: var(--accent);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.nav-lien:focus-visible,
.entete-parametres:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--rayon);
}

/* Onglets des quatre écrans de paramétrage. Enroulés sur deux lignes en
   étroit : « Comptes de dépenses » et « Foyers et participants » sont longs
   parce qu'ils doivent l'être (voir web/navigation.py). */
/* Le titre nomme la SECTION, l'onglet actif nomme l'écran : les deux ne
   disent jamais la même chose. La marge basse est resserrée parce qu'il est
   collé à ses onglets — c'est un en-tête en deux étages, pas deux blocs. */
.titre-parametres {
  margin-bottom: var(--espace-3);
}

/* LA BARRE DU CONTEXTE PARAMÈTRES : elle prend la place EXACTE de la barre
   principale (même `order`, mêmes marges négatives, même filet d'un bord à
   l'autre), parce que c'est la même fonction — la navigation visible du
   niveau où l'on se trouve. Un seul niveau à la fois. */
.nav-parametres {
  order: 3;
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--espace-3);
  margin: var(--espace-3) calc(-1 * var(--espace-4)) calc(-1 * var(--espace-3));
  padding: 0 var(--espace-4);
  border-top: var(--bordure);
}

/* LA SORTIE, à gauche et séparée par un filet : elle ne fait pas partie des
   onglets, elle quitte le niveau. Sans ce trait, cinq libellés alignés se
   liraient comme cinq sections. */
.nav-retour {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-min);
  color: var(--gris-700);
  text-decoration: none;
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  white-space: nowrap;
  /* EN ÉTROIT, LE RETOUR PREND SA PROPRE RANGÉE. Sur la même que les
     onglets, il leur volait la largeur et les envoyait sur trois lignes :
     l'en-tête passait à 258px, soit PLUS que les deux barres qu'on venait
     de supprimer. Deux rangées nettes valent mieux qu'une rangée qui
     déborde. */
  flex-basis: 100%;
  border-bottom: var(--bordure);
}

.nav-retour:hover,
.nav-retour:focus-visible {
  color: var(--accent);
}

/* Le jeu ne contient pas de `chevron-left` : on retourne celui de droite.
   Même fichier, donc même géométrie et même graisse que partout ailleurs. */
.icone-retour {
  transform: rotate(180deg);
}

.onglets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-1) var(--espace-4);
  margin-bottom: var(--espace-5);
  border-bottom: var(--bordure);
}

/* Dans la barre, les onglets n'ont plus à porter leur propre filet ni leur
   marge : c'est la barre qui les porte. */
.nav-parametres .onglets {
  flex: 1;
  gap: 0 var(--espace-4);
  margin-bottom: 0;
  border-bottom: none;
}

.onglet {
  display: inline-flex;
  align-items: center;
  min-height: var(--cible-min);
  color: var(--gris-700);
  text-decoration: none;
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  box-shadow: inset 0 -2px 0 transparent;
}

.onglet--actif {
  color: var(--accent);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.onglet:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--rayon);
}

.contenu {
  padding: var(--espace-5) var(--espace-4);
  max-width: 640px;
  margin: 0 auto;
}

/* Pastille foyer : monogramme, jamais une couleur d'identité. Le seul
   traitement différencié est celui du foyer courant (surface, pas
   identité) — voir --accent-surface ci-dessus. */

.pastille-foyer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  min-width: 36px;
  border-radius: 50%;
  border: var(--bordure);
  background: var(--gris-100);
  color: var(--gris-700);
  font-size: var(--taille-petite);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.pastille-foyer--courant {
  background: var(--accent-surface);
  border-color: var(--accent);
  color: var(--gris-900);
}

/* Écran "qui es-tu" */

.grille-foyers {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--espace-3);
}

/* Créer son foyer quand il n'est pas dans la liste. SÉPARÉE par un trait et
   en retrait : présentée comme une ligne de plus de la grille, elle serait
   choisie par erreur — et se déclarer un foyer qui n'est pas le sien est
   exactement ce qu'on veut éviter. Le bouton est de CONTOUR
   (`bouton-secondaire`), jamais d'accent : l'accent appartient au geste
   principal de l'écran, qui est de se reconnaître dans la liste. */
.arrivant {
  margin-top: var(--espace-6);
  padding-top: var(--espace-5);
  border-top: var(--bordure);
}

.arrivant-question {
  margin: 0 0 var(--espace-3);
  font-size: var(--taille-corps-lg);
  font-weight: 600;
  color: var(--gris-900);
}

.form-arrivant {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-3);
}

/* Le champ prend la largeur, le bouton reste à sa taille : une rangée souple
   qui mélange un élément extensible et un élément fixe DOIT pouvoir se
   replier, sinon elle écrase le premier (défaut vécu en Partie 4 sur la
   carte d'un foyer). */
.form-arrivant .champ-parametre {
  flex: 1 1 220px;
  min-width: 0;
}

/* Annoncer la suite : sans cette phrase, on ignore si l'opération prend dix
   secondes ou dix minutes, et c'est ce doute qui fait refermer l'onglet. */
.arrivant-suite {
  margin: var(--espace-3) 0 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.form-premier-foyer {
  display: flex;
  flex-direction: column;
  gap: var(--espace-4);
  max-width: 420px;
}

.bouton-foyer {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  min-height: 56px;
  padding: var(--espace-3) var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre);
  font-family: inherit;
  font-size: var(--taille-corps-lg);
  color: var(--gris-900);
  cursor: pointer;
  text-align: left;
}

.bouton-foyer:hover,
.bouton-foyer:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.bouton-foyer--courant {
  border-color: var(--accent);
}

.bouton-foyer-nom {
  font-weight: 600;
}

.accueil-foyer {
  font-size: var(--taille-corps-lg);
}

/* Tableau de bord (Partie 4) */

.actions-principales {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-3);
  margin-bottom: var(--espace-5);
}

.bandeau-anomalies {
  margin-bottom: var(--espace-5);
  padding: var(--espace-3) var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.bandeau-anomalies-titre {
  display: flex;
  align-items: flex-start;
  gap: var(--espace-2);
  margin: 0 0 var(--espace-2);
  font-weight: 600;
  color: var(--texte-anomalie);
}

.bandeau-anomalies-liste {
  margin: 0;
  padding-left: var(--espace-4);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.bandeau-anomalies-liste li {
  margin-bottom: var(--espace-1);
}

.bandeau-anomalies-liste a {
  color: var(--accent);
  font-weight: 600;
}

.position-personnelle {
  margin: var(--espace-2) 0 var(--espace-5);
}

.position-montant {
  font-size: var(--taille-hero);
  font-weight: 700;
  margin: 0;
}

.position-personnelle--creancier .position-montant {
  color: var(--texte-creancier);
}

.position-personnelle--debiteur .position-montant {
  color: var(--texte-debiteur);
}

/* LE TABLEAU DES SOLDES DANS UNE CARTE. C'était le seul écran dont le
   contenu principal flottait à nu sur le fond — et c'est le premier qu'on
   ouvre, plusieurs fois par jour. Sur l'ancien fond quasi blanc ça passait
   pour « une page » ; sur le fond assombri, il n'était plus posé sur rien.

   MÊME VOCABULAIRE QUE LES CARTES DU RÉCAP, aux mêmes valeurs : pas de
   bordure (le blanc sur le fond neutre suffit), rayon 14, padding 22. Deux
   dialectes de carte dans la même app seraient pires qu'aucune carte. */
.table-soldes-conteneur {
  overflow-x: auto;
  margin: var(--espace-4) 0;
  padding: 22px;
  background: var(--gris-000);
  border-radius: 14px;
}

.table-soldes {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--taille-corps);
}

.table-soldes th,
.table-soldes td {
  padding: var(--espace-2) var(--espace-3);
  text-align: right;
  border-bottom: var(--bordure);
}

.table-soldes th {
  font-size: var(--taille-petite);
  font-weight: 600;
  color: var(--gris-500);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.cellule-foyer,
.cellule-foyer-entete {
  text-align: left;
}

.cellule-foyer-contenu {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  min-height: var(--cible-min);
}

.cellule-foyer-nom {
  font-weight: 600;
}

.ligne-foyer--courant {
  background: var(--accent-surface);
}

/* Ligne du foyer courant : chaque cellule est enveloppée dans le MÊME lien
   (pas un <tr> transformé en <a>, invalide en HTML table) — l'effet perçu
   est celui d'une ligne entièrement cliquable, tout en restant une table
   valide et accessible. Le libellé "Voir mon détail" est un texte visible,
   jamais une zone cliquable muette (piège vécu : le bouton de saisie
   manquant sur ce même écran). */
.ligne-lien {
  display: block;
  color: inherit;
  text-decoration: none;
}

.ligne-lien:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.voir-detail {
  display: block;
  margin-top: 2px;
  font-size: var(--taille-micro);
  font-weight: 600;
  color: var(--accent);
}

.ligne-lien:hover .voir-detail,
.ligne-lien:focus-visible .voir-detail {
  text-decoration: underline;
}

.montant {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.colonne-solde-total {
  font-weight: 600;
}

.texte-creancier {
  color: var(--texte-creancier);
}

.texte-debiteur {
  color: var(--texte-debiteur);
}

/* Décomposition par compte : repliée en étroit (un seul solde par foyer),
   colonnes en large — voir le point de rupture unique ci-dessous. */
.colonne-compte {
  display: none;
}

.table-soldes tfoot .ligne-total td {
  font-weight: 700;
  border-top: 1.5px solid var(--gris-700);
  border-bottom: none;
}

.depense-totale {
  font-size: var(--taille-corps-lg);
  margin: var(--espace-5) 0 var(--espace-3);
}

.lien-discret {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.liens-pied {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2) var(--espace-5);
}

.lien-desactive {
  cursor: default;
}

/* Formulaire de saisie (Partie 3, passe CSS rétroactive) — l'écran qui
   décide si les gens saisissent ou rouvrent Excel. Le montant domine :
   seul champ obligatoire, seul en gros, tout en haut. Le reste est
   secondaire, et le panneau "concernés" reste replié pour qu'une course
   normale tienne sans défiler. */

/* Écart resserré (16px, pas 24) : la cible est que montant + libellé +
   date + compte + payeur + bouton tiennent sur UN écran de téléphone
   (375x667) panneau replié — la saisie en dix secondes se joue là. */
.formulaire-depense {
  display: flex;
  flex-direction: column;
  gap: var(--espace-4);
}

/* Libellé et date côte à côte : deux champs courts qui n'ont aucune raison
   d'occuper deux rangées pleines. */
.rangee-champs {
  display: flex;
  gap: var(--espace-3);
}

.rangee-champs > * {
  flex: 1 1 0;
  min-width: 0;
}

.formulaire-depense fieldset {
  margin: 0;
  padding: 0;
  border: none; /* les bordures natives de fieldset cassent le registre sobre */
}

.etiquette {
  display: flex;
  align-items: center;
  gap: var(--espace-1);
  padding: 0;
  margin-bottom: var(--espace-1);
  font-size: var(--taille-petite);
  font-weight: 600;
  color: var(--gris-500);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.erreur {
  margin: 0;
  padding: var(--espace-3) var(--espace-4);
  border: var(--bordure);
  border-radius: var(--rayon);
  background: var(--gris-000);
  color: var(--texte-anomalie);
  font-weight: 600;
}

/* Confirmation de la saisie précédente, en rafale. HORS DU FLUX
   (position: fixed) : dans le flux, elle poussait "Ajouter et saisir la
   suivante" sous la ligne de flottaison — exactement le lien dont on a
   besoin pour enchaîner. Elle s'efface seule en CSS, sans JS. Couleur de
   texte, pas d'aplat (même règle que créancier/débiteur) : la teinte
   confirme, elle n'alerte pas. */
.confirmation {
  position: fixed;
  /* EN ÉTROIT, LE BANDEAU VIENT DU BAS, et c'est la conséquence directe d'un
     en-tête qui NE COLLE PAS ici.

     Il était posé à `hauteur-entete + 8` : juste au chargement, faux dès
     qu'on avait bougé. Or il apparaît précisément après un enregistrement
     qui renvoie vers une ancre — donc après un DÉFILEMENT. Mesuré sur
     l'écran des foyers en 375px : la page saute à 1109px, l'en-tête est à
     −864 (parti depuis longtemps), et le bandeau reste garé à 222px comme
     s'il était encore là — recouvrant sur 39px la carte qu'on venait
     justement voir. Un décalage appliqué sans obstacle, exactement la
     symétrie du `scroll-margin-top` posé là où rien ne masque.

     Aucune valeur de `top` ne peut être juste aux deux positions de
     défilement tant que l'en-tête n'est pas collant : le bas, lui, est
     toujours libre et toujours visible. En large l'en-tête COLLE, donc
     « sous l'en-tête » y reste vrai en permanence — la règle d'origine y
     est conservée telle quelle. */
  bottom: var(--espace-4);
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  margin: 0;
  padding: var(--espace-2) var(--espace-4);
  border: var(--bordure);
  border-left: 3px solid var(--texte-creancier);
  border-radius: var(--rayon);
  background: var(--gris-000);
  color: var(--texte-creancier);
  font-size: var(--taille-corps);
  font-weight: 600;
  white-space: nowrap;
  animation: confirmation-disparait 3.5s forwards;
}

@keyframes confirmation-disparait {
  0%, 78% { opacity: 1; }
  100% { opacity: 0; visibility: hidden; }
}

/* Mouvement réduit : elle reste affichée plutôt que de disparaître par une
   animation qu'on a demandé de ne pas jouer. Toujours hors du flux, donc
   sans effet sur la cible "un seul écran". */
@media (prefers-reduced-motion: reduce) {
  .confirmation {
    animation: none;
  }
}

/* Montant — la hiérarchie de l'écran tient dans ce bloc. */

.champ-montant {
  text-align: center;
}

.montant-saisie {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--espace-2);
  padding: var(--espace-2) var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon-lg);
}

.montant-saisie:focus-within {
  border-color: var(--accent);
}

.montant-saisie input {
  width: 100%;
  max-width: 260px;
  padding: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--taille-hero);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--gris-900);
  text-align: right;
}

.montant-saisie input::placeholder {
  color: var(--gris-300);
}

.montant-devise {
  font-size: var(--taille-titre);
  font-weight: 600;
  color: var(--gris-500);
}

/* Calculatrice — un auxiliaire de saisie, discret tant qu'on ne l'ouvre pas.
   Le bouton vit DANS le cadre du montant, à droite ; il ne prend pas la
   place du chiffre, qui reste ce qu'on lit en premier sur cet écran. */
.montant-calculatrice {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--cible-min);
  min-height: var(--cible-min);
  padding: 0;
  background: none;
  border: none;
  border-radius: var(--rayon);
  color: var(--gris-500);
  cursor: pointer;
}

.montant-calculatrice .icone {
  width: 22px;
  height: 22px;
}

.montant-calculatrice:hover,
.montant-calculatrice:focus-visible {
  color: var(--accent);
  outline: none;
}

.montant-calculatrice[aria-expanded="true"] {
  background: var(--accent-surface);
  color: var(--accent);
}

.pave-calculatrice {
  margin-top: var(--espace-2);
}

/* Quatre colonnes : les quatre opérateurs sur une rangée, C / ⌫ / = sur la
   suivante, « = » à cheval sur deux colonnes parce que c'est la touche qu'on
   vise sans regarder. */
.pave-touches {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--espace-2);
}

.pave-touche {
  min-height: var(--cible-min);
  padding: var(--espace-2);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: var(--taille-corps-lg);
  font-weight: 600;
  color: var(--gris-900);
  cursor: pointer;
}

.pave-touche:hover,
.pave-touche:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.pave-touche--commande {
  color: var(--gris-700);
}

.pave-touche--egal {
  grid-column: span 2;
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-texte);
}

/* « — » quand l'expression ne se lit pas. Couleur d'anomalie, en TEXTE. */
.pave-etat {
  margin: var(--espace-2) 0 0;
  min-height: 1em;
  text-align: center;
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--texte-anomalie);
}

/* Radios compte / payeur : l'input natif est masqué, c'est le label qui
   porte l'état. :checked doit se voir au premier coup d'œil — surface
   accentuée ET bordure accent, jamais la seule bordure. */

.segmente {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
}

.segmente-option,
.pastille-option {
  position: relative;
  flex: 1 1 auto;
  cursor: pointer;
}

.segmente-option input,
.pastille-option input,
.concernes-foyer input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.segmente-option-texte {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--espace-1);
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-size: var(--taille-corps);
  font-weight: 600;
  text-align: center;
}

.segmente-option input:checked + .segmente-option-texte,
.pastille-option input:checked + .pastille-option-contenu {
  background: var(--accent-surface);
  border-color: var(--accent);
}

.segmente-option input:focus-visible + .segmente-option-texte,
.pastille-option input:focus-visible + .pastille-option-contenu {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.rangee-pastilles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
}

.pastille-option {
  flex: 0 0 auto;
}

/* Pastille compacte ICI seulement : cinq foyers sur deux rangées coûtaient
   123px de haut, contre ~84 ainsi — la différence entre "tient sur un
   écran" et "il faut défiler". On laisse le retour à la ligne plutôt que
   de forcer une rangée unique, qui casserait dès que les vrais noms de
   famille remplaceront les codes F1..F5. La zone tactile reste à 44px via
   le padding vertical du label. */
.pastille-option-contenu {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  min-height: 38px;
  padding: var(--espace-1) var(--espace-3) var(--espace-1) var(--espace-1);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: 999px;
}

.rangee-pastilles .pastille-foyer {
  width: 30px;
  height: 30px;
  min-width: 30px;
}

.pastille-option-nom {
  font-size: var(--taille-corps);
  font-weight: 600;
}

/* Panneau "concernés" : replié, résumé en une ligne cliquable. */

.champ-concernes {
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.concernes-resume {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-3);
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-4);
  cursor: pointer;
  list-style: none; /* le triangle natif est remplacé par .concernes-chevron */
  font-size: var(--taille-corps);
}

.concernes-resume::-webkit-details-marker {
  display: none;
}

.concernes-resume:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--rayon);
}

.concernes-chevron {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--gris-500);
  border-bottom: 1.5px solid var(--gris-500);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

.champ-concernes[open] .concernes-chevron {
  transform: rotate(45deg);
}

.concernes-aide {
  margin: 0;
  padding: 0 var(--espace-4) var(--espace-2);
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

/* Une ligne par FOYER (Phase 2A, Partie 2). Le pavé de dix-huit cases a
   disparu : cinq lignes tiennent sans défilement sur 375px, ce que le
   sélecteur par participant ne faisait pas. */

.concernes-foyers {
  border-top: var(--bordure);
}

.concernes-foyer {
  display: block;
  position: relative; /* ancre le ::after de la coche */
  cursor: pointer;
}

/* `~` ET NON `+`. Le frère ADJACENT exigeait que deux foyers se touchent
   dans le DOM — or le bloc des exemptés (« Inclure Anne-Marie ») s'intercale
   entre eux, et le trait de séparation disparaissait pour le foyer SUIVANT.
   Signalé à l'usage : plus de ligne entre Anne-Marie et Olivier et Charlotte
   et Pierre.

   Le bloc reste dans le DOM même masqué (`[hidden]` ne fait que le cacher),
   et le serveur en rend un PAR COMPTE : le trait manquait donc aussi sur les
   comptes sans exemption. Le frère GÉNÉRAL dit ce qu'on veut vraiment —
   « tout foyer précédé d'un autre foyer » — et tient dans les deux cas, bloc
   présent ou absent, visible ou masqué. */
.concernes-foyer ~ .concernes-foyer {
  border-top: var(--bordure);
}

.concernes-foyer-contenu {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  min-height: var(--cible-min);
  padding: var(--espace-1) var(--espace-4);
  font-size: var(--taille-corps);
}

.concernes-foyer-nom {
  font-weight: 600;
}

/* Le décompte des présents rend visible ce qui serait sinon une surprise :
   cocher un foyer absent ce jour-là ne coûte rien à personne (le calcul
   n'entend que les présents, voir depot/perimetre.py). */
.concernes-foyer-presents {
  margin-left: auto;
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

.concernes-foyer--absent .concernes-foyer-nom {
  color: var(--gris-500);
}

/* Case dessinée : un carré qui se remplit d'accent une fois coché. Le
   pictogramme ✓ est en ::after pour rester lisible en noir et blanc. */
.concernes-foyer-contenu::before {
  content: "";
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 1.5px solid var(--gris-300);
  border-radius: 6px;
  background: var(--gris-000);
}

.concernes-foyer input:checked + .concernes-foyer-contenu::before {
  background: var(--accent);
  border-color: var(--accent);
}

.concernes-foyer input:checked + .concernes-foyer-contenu::after {
  content: "";
  position: absolute;
  width: 5px;
  height: 10px;
  margin-left: 8px;
  border-right: 2px solid var(--accent-texte);
  border-bottom: 2px solid var(--accent-texte);
  transform: translateY(-2px) rotate(45deg);
}

/* ÉTAT INTERMÉDIAIRE : le foyer apporte une partie de ce qu'il peut. La
   case étant DESSINÉE (le carré natif est masqué), `:indeterminate` n'a
   aucun rendu par défaut ici — sans cette règle, « une partie » et « rien »
   se ressemblaient trait pour trait à l'écran alors qu'ils ne disent pas la
   même chose. Vu en capture, pas au test : aucune assertion sur le DOM ne
   pouvait le signaler.

   Un TIRET, jamais une coche pâle : le tiret est la convention des cases
   groupées, et il se lit en noir et blanc comme le ✓. Fond blanc et bordure
   d'accent — l'aplat plein reste réservé au « tout ». */
.concernes-foyer input:indeterminate + .concernes-foyer-contenu::before {
  border-color: var(--accent);
}

.concernes-foyer input:indeterminate + .concernes-foyer-contenu::after {
  content: "";
  position: absolute;
  width: 10px;
  height: 0;
  margin-left: 6px;
  border-top: 2px solid var(--accent);
}

.concernes-foyer input:focus-visible + .concernes-foyer-contenu {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Les exemptés d'un foyer, EN RETRAIT sous lui — un filet vertical plutôt
   qu'un aplat : le décalage dit « ceci dépend de la ligne du dessus » sans
   un mot de plus, et sans peser autant qu'un second niveau de carte.
   Décochés par défaut : l'exemption est la règle, la réinclusion l'exception
   d'une dépense. */
.concernes-exemptes {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  margin: 0 0 var(--espace-2) 34px; /* aligné sous le nom du foyer, pas sous sa case */
  padding-left: var(--espace-3);
  border-left: 2px solid var(--gris-300);
}

/* La raison, dite UNE FOIS pour le foyer : c'est une règle du foyer, pas
   une propriété de chacun de ses membres. Répétée sur chaque ligne, elle
   faisait un mur et donnait à lire une négation par personne. */
.concernes-exemptes-motif {
  margin: 0 0 var(--espace-1);
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

.concernes-exempte {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  min-height: 36px;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
  cursor: pointer;
}

.concernes-exempte input {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}

/* Recoché = il compte à nouveau : la ligne reprend du poids, seul état de
   l'écran qui change un total sans changer une case de foyer. */
.concernes-exempte:has(input:checked) {
  color: var(--gris-900);
  font-weight: 600;
}

/* Libellé, date : champs secondaires, sobres. */

.champ-libelle input,
.champ-date input {
  width: 100%;
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: var(--taille-corps-lg);
  color: var(--gris-900);
}

.champ-libelle input:focus,
.champ-date input:focus {
  outline: none;
  border-color: var(--accent);
}

.facultatif {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}

.bouton-large {
  width: 100%;
  min-height: 52px;
  font-size: var(--taille-corps-lg);
}

/* Deux actions de poids TRÈS différents : "Ajouter la dépense" est le cas
   normal, "et saisir la suivante" le cas du retour de marché. Le second
   est un lien-bouton, pas un second bouton plein — sinon l'écran pose une
   question là où il devrait proposer une évidence. */
.actions-formulaire {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Pas d'écart : les 44px de zone tactile de .lien-action suffisent à
     séparer les deux actions, et les 8px économisés sont exactement ce qui
     fait tenir l'écran sans défilement sur un iPhone SE (667px). */
  gap: 0;
}

.lien-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--cible-min);
  padding: 0 var(--espace-3);
  background: none;
  border: none;
  font-family: inherit;
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.lien-action:hover,
.lien-action:focus-visible {
  text-decoration: none;
}

/* LA SORTIE, et elle est GRISE. `.lien-action` porte deux choses très
   différentes : des actions secondaires qui FONT quelque chose (« Ajouter et
   saisir la suivante », « Modifier ») et des sorties qui font PERDRE la
   saisie (« Annuler », « Fermer »). Les rendre identiques, c'était le piège
   du bouton « Suite » du wizard une seconde fois : la hiérarchie visuelle
   doit refléter la hiérarchie des CONSÉQUENCES.

   `--gris-700` et non `--gris-500` : la sortie doit rester discrète, pas
   douteuse. Le soulignement et les 44px de zone tactile sont conservés — ce
   qui la met en retrait est sa TEINTE, jamais sa cliquabilité.

   Placée juste après `.lien-action`, à ÉGALE spécificité (0,1,0) : c'est
   l'ordre qui tranche. Déplacer ce bloc plus haut le ferait perdre en
   silence — piège déjà rencontré trois fois sur l'écran de synthèse. */
.lien-sortie {
  color: var(--gris-700);
}

/* Liste des dépenses (Partie 3, même passe) */

.bouton-lien {
  text-decoration: none;
}

.liste-depenses {
  list-style: none;
  margin: var(--espace-4) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
}

/* Bandeau de totaux par compte : la valeur est le total, lisible sans
   clic. Pas de couleur d'identité par compte — le budget couleur reste
   réservé au sens (accent, créancier/débiteur, anomalie), comme pour les
   foyers en Partie 2. Un compte se distingue par son libellé. */

/* LES TUILES RESTENT VISIBLES QUAND ON DÉFILE, et ce n'est pas un confort :
   ce sont elles qui portent l'icône ET le nom de chaque poste, donc la
   LÉGENDE de l'icône seule qui figure sur les cartes. Si elles sortent de
   l'écran, le pictogramme des cartes n'a plus de clé de lecture.

   `top: 0` en étroit — l'en-tête n'y colle pas, rien ne les précède — et
   sous l'en-tête en large, où il occupe ces pixels en permanence. Même
   raisonnement que le décalage d'ancre et le bandeau de confirmation :
   `--hauteur-entete` n'est consommé que là où il désigne un obstacle réel.

   Le fond est OPAQUE : sans lui, les cartes défileraient en transparence
   sous les tuiles. */
.bandeau-comptes {
  position: sticky;
  top: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--espace-2);
  margin: var(--espace-4) 0;
  padding: var(--espace-2) 0;
  background: var(--fond);
}

/* QUATRE POSTES OU PLUS : 2×2 plutôt que 3 + 1 ORPHELIN. Le nombre de
   comptes n'est pas figé — Cadeaux en a ajouté un quatrième — et
   `repeat(3, 1fr)` laissait le dernier seul sur sa rangée, le bloc bancal
   déjà corrigé sur les indicateurs du récap.

   `:has()` compte les enfants, ce que ni une classe ni une variable ne
   savent faire depuis le CSS ; c'est déjà le mécanisme du cadre plein écran
   de la synthèse et de la largeur du bloc d'ajout d'un participant. À trois
   comptes, rien ne change : la règle ne s'applique pas. */
.bandeau-comptes:has(> :nth-child(4)) {
  grid-template-columns: repeat(2, 1fr);
}

/* LES PAYEURS. Une rangée de pastilles à monogramme, chacune avec son
   décompte — y compris à ZÉRO : « qui n'a rien avancé » est justement la
   question qu'on se pose au milieu d'un séjour, et une pastille qui
   disparaîtrait à zéro ferait croire que le foyer n'existe pas.

   Elle défile horizontalement, à la différence de la barre de navigation :
   ici la liste est ouverte (un foyer de plus demain) et son ordre est connu
   — on cherche un monogramme précis, pas la fin de la rangée. */
.filtre-payeurs {
  display: flex;
  gap: var(--espace-1);
  overflow-x: auto;
  margin-bottom: var(--espace-3);
  padding-bottom: var(--espace-1);
}

.pastille-payeur {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-min);
  padding: 4px var(--espace-2) 4px 4px;
  border: var(--bordure);
  border-radius: 999px;
  background: var(--gris-000);
  color: var(--gris-700);
  font-size: var(--taille-corps-sm);
  text-decoration: none;
  white-space: nowrap;
}

.pastille-payeur--tous {
  padding-left: var(--espace-3);
  font-weight: 600;
}

.pastille-payeur--actif {
  background: var(--accent-surface);
  border-color: var(--accent);
  color: var(--gris-900);
}

.pastille-payeur .pastille-foyer {
  width: 26px;
  height: 26px;
  min-width: 26px;
  font-size: 9px;
}

.pastille-payeur-nb {
  font-size: var(--taille-micro);
  color: var(--gris-500);
  font-variant-numeric: tabular-nums;
}

.pastille-payeur--actif .pastille-payeur-nb {
  color: var(--gris-700);
}

/* LES CONCERNÉS. Séparés par un filet POINTILLÉ : la ligne ne fait pas
   partie de la dépense, elle la commente. En retrait, comme toute mention
   secondaire — ce qui compte reste le montant. */
.ligne-depense-concernes {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-1);
  margin: var(--espace-2) 0 0;
  padding-top: var(--espace-2);
  border-top: 1px dashed var(--gris-300);
  font-size: var(--taille-petite);
  color: var(--gris-700);
}

/* UN FOYER COCHÉ QUI NE PORTE RIEN. En couleur d'anomalie, comme la phrase
   de la frise qui signale une barre couvrant des membres sans présence :
   c'est la même famille de défaut — la saisie affirme quelque chose que le
   calcul ne fait pas, et c'est CRÉDIBLE, donc invisible sans mention. Le
   calcul, lui, reste juste ; d'où une phrase et pas le bandeau rouge, qui
   est réservé aux dépenses réellement hors des comptes.

   Pas de trait au-dessus : elle suit la ligne des concernés, qu'elle
   complète. Seule, elle en hérite le rôle de séparateur. */
.ligne-depense-muets {
  grid-column: 1 / -1;
  margin: 2px 0 0;
  font-size: var(--taille-petite);
  color: var(--texte-anomalie);
}

.ligne-depense-concernes + .ligne-depense-muets {
  border-top: 0;
}

.ligne-depense-muets:first-of-type {
  margin-top: var(--espace-2);
  padding-top: var(--espace-2);
  border-top: 1px dashed var(--gris-300);
}

.ligne-depense-concernes .pastille-foyer {
  width: 22px;
  height: 22px;
  min-width: 22px;
  font-size: 9px;
}

/* LA RAISON, encore un cran en retrait : c'est elle qui explique un solde
   inattendu, mais on ne la lit qu'après avoir vu QUI. */
.ligne-depense-raison {
  color: var(--gris-500);
}

.carte-compte {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  text-decoration: none;
  color: var(--gris-900);
}

.carte-compte:hover,
.carte-compte:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.carte-compte--actif {
  background: var(--accent-surface);
  border-color: var(--accent);
}

.carte-compte-libelle {
  display: flex;
  align-items: center;
  gap: var(--espace-1);
  font-size: var(--taille-micro);
  font-weight: 600;
  color: var(--gris-500);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.carte-compte-total {
  font-size: var(--taille-corps-lg);
  font-weight: 700;
}

/* LA RÈGLE DU POSTE, sous son total. Elle doit tenir dans une tuile qui, à
   375px, se partage la largeur avec deux autres — d'où la plus petite taille
   de l'échelle et un retour à la ligne autorisé plutôt qu'une troncature :
   une règle coupée en deux ne se devine pas. En retrait, comme toute mention
   secondaire : le total reste ce qu'on vient lire. */
.carte-compte-hors {
  font-size: var(--taille-micro);
  font-weight: 400;
  line-height: 1.3;
  color: var(--gris-500);
  text-transform: none;
  letter-spacing: 0;
}

.carte-compte--actif .carte-compte-hors {
  color: var(--gris-700);
}

/* "Supprimer" sur la MÊME rangée que le montant : lui donner sa propre
   rangée coûtait ~40px par carte, soit un écran entier de défilement sur
   douze dépenses. Le libellé s'ellipse si besoin — le montant, lui, n'est
   jamais tronqué, et la ligne entière reste cliquable pour l'édition. */
.ligne-depense {
  position: relative; /* ancre le lien étiré de .ligne-depense-lien */
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--espace-1) var(--espace-3);
  padding: var(--espace-2) var(--espace-4) var(--espace-3);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

/* La zone tactile de 44px de "Supprimer" impose la hauteur de la rangée :
   sans ce centrage, le montant flottait au-dessus du libellé de l'action. */
.ligne-depense-principal {
  grid-column: 1;
  align-self: center;
}

.ligne-depense-meta {
  grid-column: 1 / -1;
}

.ligne-depense:hover,
.ligne-depense:focus-within {
  border-color: var(--accent);
}

.ligne-depense--anomalie {
  border-color: var(--texte-anomalie);
}

.ligne-depense-principal {
  display: flex;
  align-items: baseline;
  gap: var(--espace-3);
  min-width: 0;
}

/* Lien étiré : cliquable sur toute la carte, tout en restant un vrai lien
   avec un libellé lisible pour le clavier et les lecteurs d'écran. */
.ligne-depense-lien {
  display: flex;
  align-items: baseline;
  gap: var(--espace-3);
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.ligne-depense-lien::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--rayon);
}

.ligne-depense-lien:focus-visible::after {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* LE POSTE RÉDUIT À SON ICÔNE. Ce qui l'autorise : les tuiles du haut
   portent l'icône ET le nom, et elles restent COLLANTES au défilement — ce
   sont elles la légende. Sans ce collage, le pictogramme redeviendrait un
   signe orphelin dès la cinquième dépense.

   Le libellé reste dans le DOM en `.lecteur-ecran` : une icône est
   `aria-hidden`, une cellule qui n'aurait qu'elle serait vide à l'oreille. */
.jeton-poste {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 7px;
  background: var(--gris-100);
  color: var(--gris-700);
  flex-shrink: 0;
}

.jeton-poste .icone-compte {
  width: 14px;
  height: 14px;
}

.ligne-depense-payeur {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
}

.ligne-depense-payeur .pastille-foyer {
  width: 22px;
  height: 22px;
  min-width: 22px;
  font-size: 9px;
}

.ligne-depense-anomalie {
  grid-column: 1 / -1;
  display: flex;
  align-items: flex-start;
  gap: var(--espace-1);
  margin: var(--espace-1) 0 0;
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--texte-anomalie);
}

.ligne-depense-montant {
  font-size: var(--taille-corps-lg);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ligne-depense-libelle {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ligne-depense-meta {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-1) var(--espace-3);
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

.ligne-depense-tracabilite {
  font-style: italic;
}

/* Au-dessus du lien étiré : la suppression doit rester atteignable sans
   déclencher l'ouverture de la dépense. */
.ligne-depense-actions {
  position: relative;
  z-index: 1;
  grid-column: 2;
  grid-row: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--espace-2);
  margin-right: calc(var(--espace-2) * -1); /* aligne le texte, pas la zone tactile */
}

/* Actions en icône seule : cible tactile de 44px maintenue malgré la
   petite taille du dessin (le padding porte la zone, pas le glyphe). */
.action-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--cible-min);
  min-height: var(--cible-min);
  padding: 0;
  background: none;
  border: none;
  font-size: 18px;
  color: var(--gris-500);
  cursor: pointer;
}

.ligne-depense-actions a.action-icone {
  color: var(--accent);
}

.action-icone:hover,
.action-icone:focus-visible {
  color: var(--gris-900);
}

.ligne-depense-actions button.action-icone:hover,
.ligne-depense-actions button.action-icone:focus-visible {
  color: var(--texte-debiteur);
}

/* Écrans de paramétrage (Phase 2A, Partie 1) — édition du séjour et des
   comptes. Même registre que la saisie de dépense : bordures natives des
   fieldset neutralisées, groupes matérialisés par une carte. */

.formulaire-parametres {
  display: flex;
  flex-direction: column;
  gap: var(--espace-4);
}

.formulaire-parametres fieldset {
  margin: 0;
  padding: 0;
  border: none;
}

/* Plus spécifique que `.formulaire-parametres fieldset` (qui remet
   padding/bordure à zéro) plutôt qu'un !important — même choix que pour le
   panneau "concernés" du formulaire de dépense. */
.formulaire-parametres .groupe-parametres {
  padding: var(--espace-3) var(--espace-4) var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.groupe-parametres > .etiquette {
  padding: 0;
  margin-bottom: var(--espace-2);
}

.aide-parametre {
  margin: 0 0 var(--espace-3);
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.aide-parametre:last-child {
  margin: var(--espace-2) 0 0;
}

.champ-parametre {
  display: block;
  min-width: 0; /* sans quoi un champ en flex refuse de se réduire et déborde */
}

.sous-etiquette {
  display: block;
  margin-bottom: var(--espace-1);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.champ-parametre input,
.champ-parametre select {
  width: 100%;
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: var(--taille-corps-lg);
  color: var(--gris-900);
}

.champ-parametre input:focus,
.champ-parametre select:focus {
  outline: none;
  border-color: var(--accent);
}

/* Les trois poids ne tiennent pas côte à côte sur 375px : ils passent en
   colonne en étroit, en rangée dès qu'il y a la place. C'est ce qui
   supprime le débordement horizontal. */
.rangee-champs {
  flex-wrap: wrap;
}

.rangee-champs > * {
  flex: 1 1 140px;
}

/* Refus des bornes : la contrainte qui mord, pas la liste. */

.bloc-conflits {
  padding: var(--espace-3) var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-left: 3px solid var(--texte-anomalie);
  border-radius: var(--rayon);
}

.bloc-conflits-titre {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  margin: 0 0 var(--espace-2);
  font-weight: 600;
  color: var(--texte-anomalie);
}

.bloc-conflits-liste {
  margin: 0;
  padding-left: var(--espace-4);
  font-size: var(--taille-corps);
}

.bloc-conflits-liste li + li {
  margin-top: var(--espace-2);
}

.bloc-conflits-detail {
  display: block;
  margin-top: 2px;
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.bloc-conflits-aide {
  margin: var(--espace-3) 0 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

/* Avertissement de recalcul : lucide, pas alarmant — c'est une
   conséquence attendue, pas une erreur. Texte gris, icône seule en
   couleur d'alerte. */
.avertissement-recalcul {
  display: flex;
  align-items: flex-start;
  gap: var(--espace-2);
  margin: 0;
  padding: var(--espace-3) var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.avertissement-recalcul .icone {
  color: var(--texte-anomalie);
  margin-top: 2px;
}

/* Un avertissement collé au bouton qui l'exécute se lit comme sa légende, et
   il n'en est pas une : c'est ce qu'il faut avoir lu AVANT de cliquer. Le
   sélecteur d'adjacence vise exactement les trois écrans de suppression
   (foyer, participant, dépense), les seuls où l'avertissement précède
   directement les actions — ailleurs il est suivi de champs, et son
   `margin: 0` reste le bon. */
.avertissement-recalcul + .actions-formulaire {
  margin-top: var(--espace-5);
}

/* Écran des comptes : une carte par compte, chacune son formulaire. */

.liste-comptes-parametres {
  list-style: none;
  margin: var(--espace-4) 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espace-3);
}

.carte-compte-parametre {
  padding: var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon-lg);
}

.carte-compte-parametre form {
  display: flex;
  flex-direction: column;
  gap: var(--espace-3);
}

.carte-compte-entete {
  display: flex;
  align-items: flex-end;
  gap: var(--espace-3);
}

.carte-compte-entete .icone-compte {
  width: 22px;
  height: 22px;
  margin-bottom: var(--espace-3);
  color: var(--gris-500);
}

.champ-libelle-compte {
  flex: 1 1 auto;
}

.bascule {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  min-height: var(--cible-min);
  cursor: pointer;
}

.bascule input {
  width: 22px;
  height: 22px;
  accent-color: var(--accent);
}

/* Écran "ton compte" — refondu : du VERDICT à la DIVISION
   ------------------------------------------------------
   L'ordre visuel EST l'argument. Le verdict en grand, puis la soustraction
   qui le produit, puis la division qui produit chaque terme. Un chiffre
   qu'on ne peut pas refaire soi-même demande la confiance que l'Excel
   demandait — c'est cette confiance qu'on remplace par de l'arithmétique
   visible. */

.verdict {
  font-size: var(--taille-hero);
  font-weight: 700;
  line-height: 1.1;
  margin: 0;
}

/* LA PHRASE LA PLUS IMPORTANTE DE L'ÉCRAN, collée sous le verdict : deux
   nombres et une soustraction. Pas en gris clair — elle n'est pas une note
   de bas de page, c'est ce qui rend le verdict refaisable de tête. */
.verdict-calcul {
  margin: var(--espace-2) 0 var(--espace-5);
  font-size: var(--taille-corps-lg);
  color: var(--gris-700);
}

.titre-section {
  font-size: var(--taille-corps-lg);
  margin: 0 0 var(--espace-3);
}

.liste-comptes-detail {
  list-style: none;
  margin: 0 0 var(--espace-6);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
}

.compte-detail {
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.compte-detail[open] {
  border-color: var(--accent);
}

/* Le `<summary>` reste en `block` : en flex, WebKit cesse de le traiter
   comme la commande d'ouverture de son `<details>`. La mise en page vit
   dans `.compte-detail-rangee`. */
.compte-detail-resume {
  display: block;
  padding: var(--espace-3) var(--espace-4);
  cursor: pointer;
  list-style: none;
}

.compte-detail-resume::-webkit-details-marker {
  display: none;
}

.compte-detail-rangee {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  min-height: var(--cible-min);
}

.compte-detail-titre {
  font-weight: 600;
}

.compte-detail-montant {
  margin-left: auto;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.compte-detail-chevron {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--gris-500);
  border-bottom: 1.5px solid var(--gris-500);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

/* `>` obligatoire : une ligne de compte contient des `<details>` de dépense.
   Sans lui, ouvrir le compte retournerait aussi leurs chevrons, restés
   fermés — même piège que sur la frise. */
.compte-detail[open] > .compte-detail-resume .compte-detail-chevron,
.part-depense[open] > .part-depense-resume .compte-detail-chevron {
  transform: rotate(45deg);
}

.compte-detail-corps {
  padding: 0 var(--espace-4) var(--espace-3);
}

.compte-detail-cle {
  margin: 0 0 var(--espace-2);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

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

.part-depense-resume {
  display: block;
  cursor: pointer;
  list-style: none;
}

.part-depense-resume::-webkit-details-marker {
  display: none;
}

.part-depense-rangee {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  min-height: var(--cible-min);
  font-size: var(--taille-corps-sm);
}

.part-depense-libelle {
  overflow-wrap: anywhere;
}

.part-depense-montant {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

/* LA LIGNE OÙ LE SCEPTICISME MEURT. En RETRAIT et sur fond propre : c'est
   une démonstration, pas une ligne de tableau — et une seule dépense la
   porte à la fois, sinon l'écran redevient le tableau qu'on vient de
   quitter. */
.part-explication {
  margin: 0 0 var(--espace-2);
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-050);
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--rayon) var(--rayon) 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.part-repliees {
  display: flex;
  gap: var(--espace-3);
  min-height: var(--cible-min);
  align-items: center;
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.part-repliees-total {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

/* CE QUI DÉTERMINE TA PART */

.determine {
  margin: 0 0 var(--espace-6);
  padding: var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.determine-phrase {
  margin: 0 0 var(--espace-3);
}

.liste-membres-part {
  list-style: none;
  margin: 0 0 var(--espace-3);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  font-size: var(--taille-corps-sm);
}

.liste-membres-part li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--espace-2);
}

.membre-nom {
  font-weight: 600;
}

.membre-dates,
.membre-coef {
  color: var(--gris-500);
}

/* L'ÉCART, et c'est la phrase qui évite qu'on croie l'app fausse. Elle porte
   un liseré d'accent parce qu'elle EXPLIQUE, elle n'alerte pas : ni orange
   ni rouge — une exemption est un état normal, pas un défaut. */
.determine-ecart {
  margin: var(--espace-2) 0 0;
  padding-left: var(--espace-3);
  border-left: 2px solid var(--accent);
  font-size: var(--taille-corps-sm);
}

/* PIED DE CONSERVATION, en français et non en comptable. */
.pied-conservation {
  margin: 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.compte-liens {
  display: flex;
  flex-direction: column;
  gap: var(--espace-1);
  margin-top: var(--espace-5);
}

.compte-liens a {
  display: flex;
  align-items: center;
  min-height: var(--cible-min);
  color: var(--accent);
  font-weight: 600;
}

/* Plan de règlement (Partie 5) */

.liste-filtre {
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
  margin: var(--espace-3) 0;
}

.liste-filtre a {
  color: var(--accent);
  font-weight: 600;
}

.liste-reglement,
.liste-historique {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espace-3);
}

.ligne-reglement {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--espace-3);
  padding: var(--espace-3) var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.ligne-reglement-phrase {
  margin: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-2);
}

.ligne-historique {
  padding: var(--espace-2) var(--espace-4);
  border-bottom: var(--bordure);
}

.ligne-historique-phrase {
  margin: 0;
  color: var(--gris-700);
}

.ligne-historique-meta {
  margin: var(--espace-1) 0 0;
  font-size: var(--taille-micro);
  color: var(--gris-500);
}

/* Poids des repas : saisie en pourcentages entiers, total en direct */

.champ-pourcentage {
  position: relative;
  display: block;
}

.champ-pourcentage input {
  width: 100%;
  padding-right: 2rem; /* laisse la place au % sans le chevaucher */
}

.champ-pourcentage-unite {
  position: absolute;
  right: var(--espace-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--gris-500);
  font-size: var(--taille-corps);
  pointer-events: none;
}

/* Le total est une information de saisie, pas une alerte : neutre quand il
   tombe juste, signalé — en TEXTE, comme les soldes — quand il s'en écarte.
   Pas d'aplat rouge : le budget couleur reste réservé au sens. */
.total-poids {
  margin: var(--espace-2) 0 0;
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--gris-700);
}

.total-poids--ecart {
  color: var(--texte-debiteur);
}

.bouton-principal:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Foyers et participants (Phase 2A, Partie 2) */

.liste-foyers {
  list-style: none;
  margin: 0 0 var(--espace-5);
  padding: 0;
  display: grid;
  gap: var(--espace-4);
}

.carte-foyer {
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon-lg);
  overflow: hidden;
}

/* Un menu ouvert pousse le contenu vers le bas plutôt que de le recouvrir :
   la carte s'allonge, rien n'est rogné par l'`overflow: hidden` ci-dessus. */
.menu-foyer[open] {
  position: static;
}

/* UNE RANGÉE, TROIS CHOSES : la pastille, le nom, le menu. Plus de champ de
   saisie ici — c'est ce qui permet à la rangée de tenir en 375px sans se
   replier. L'ancienne version y serrait étiquette, champ, « Renommer » et
   deux flèches : le champ tombait à ~80px et affichait « Famill ». */
.carte-foyer-entete {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  padding: var(--espace-3) var(--espace-4);
  border-bottom: var(--bordure);
}

.carte-foyer-nom {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: var(--taille-corps-lg);
  font-weight: 600;
  overflow-wrap: anywhere; /* un nom de famille long ne pousse pas le menu dehors */
}

/* LE MENU : renommer, réordonner, supprimer. Trois gestes rares qui
   occupaient chacun un contrôle permanent. `<details>` natif — rien à
   dégrader, il n'y a pas de JS. Pas d'attribut `name` : l'accordéon exclusif
   EMPÊCHE d'ouvrir (bug bloquant de la frise). */
.menu-foyer {
  flex: none;
  position: relative;
}

/* LE `<summary>` N'EST JAMAIS UN CONTENEUR : en flex ou grid, WebKit cesse
   d'en faire la commande d'ouverture de son `<details>`. La mise en page vit
   dans `.menu-foyer-rangee`. */
.menu-foyer-resume {
  display: block;
  min-width: var(--cible-min);
  min-height: var(--cible-min);
  border-radius: var(--rayon);
  color: var(--gris-500);
  cursor: pointer;
  list-style: none; /* pas de triangle : l'icône EST le repère */
}

.menu-foyer-resume::-webkit-details-marker {
  display: none;
}

.menu-foyer-rangee {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--cible-min);
}

.menu-foyer-resume:hover,
.menu-foyer-resume:focus-visible {
  background: var(--gris-100);
  color: var(--gris-900);
  outline: none;
}

.menu-foyer[open] > .menu-foyer-resume {
  background: var(--gris-100);
  color: var(--gris-900);
}

/* DANS LE FLUX, jamais en survol absolu : un menu flottant recouvrirait les
   pastilles du foyer, et sans JS rien ne le refermerait au clic à côté. Il
   pousse la carte vers le bas — le geste est rare, la place est rendue dès
   qu'on referme. */
.menu-foyer-panneau {
  display: flex;
  flex-direction: column;
  gap: var(--espace-3);
  margin-top: var(--espace-3);
  padding: var(--espace-3);
  background: var(--gris-050);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.menu-foyer-renommer {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-2) var(--espace-3);
}

.menu-foyer-renommer .champ-parametre {
  flex: 1 1 180px;
  min-width: 0;
}

.menu-foyer-ordre {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
}

.menu-foyer-ordre .sous-etiquette {
  flex: 1 1 auto;
}

.bouton-icone {
  min-width: var(--cible-min);
  min-height: var(--cible-min);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-size: var(--taille-corps-lg);
  color: var(--gris-700);
  cursor: pointer;
}

.bouton-icone:disabled {
  color: var(--gris-300);
  cursor: default;
}

.bouton-icone:not(:disabled):hover,
.bouton-icone:focus-visible {
  border-color: var(--accent);
  outline: none;
}

/* LES GENS EN PASTILLES. Une liste verticale de six lignes à chevrons
   faisait défiler un foyer sur un demi-écran ; en pastilles il se lit d'un
   regard. Chacune reste un lien vers la fiche — c'est là que se règlent le
   coefficient et les exemptions. */
.pastilles-participants {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
  margin: 0;
  padding: var(--espace-3) var(--espace-4);
}

.pastille-participant {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-min);
  /* Padding droit RÉDUIT : le chevron apporte sa propre respiration, et à
     12px des deux côtés la pastille s'allongeait assez pour qu'un foyer de
     six passe sur une rangée de plus en 375px. */
  padding: var(--espace-2) var(--espace-3);
  padding-right: var(--espace-2);
  background: var(--gris-050);
  border: var(--bordure);
  border-radius: 999px;
  color: var(--gris-900);
  text-decoration: none;
  font-size: var(--taille-corps-sm);
}

.pastille-participant-nom {
  font-weight: 600;
}

/* LE CHEVRON DIT QUE ÇA MÈNE QUELQUE PART, et c'est tout ce qu'il a à dire :
   gris au repos, jamais à l'accent — le budget couleur est réservé au sens,
   et « cliquable » n'est pas un sens, c'est une forme. Il ne prend l'accent
   qu'au survol et au focus, avec la bordure de la pastille : ce n'est plus
   alors une identité, c'est l'état « je vise celle-ci ». */
.pastille-participant .icone-chevron {
  flex: none;
  color: var(--gris-500);
  /* DÉTACHÉ DE CE QUI LE PRÉCÈDE. Sur une pastille marquée, la silhouette
     d'exemption vient juste avant lui : deux glyphes gris à 4px se lisent
     comme un seul symbole. Le chevron dit « ça mène quelque part », la
     silhouette dit « il y a une particularité » — deux natures, deux
     lectures, donc un intervalle qui les sépare. */
  margin-left: var(--espace-1);
}

/* LA MARQUE D'EXEMPTION INDIVIDUELLE, définie UNE FOIS pour les deux endroits
   qui la montrent : la pastille, où elle signale, et la phrase du bas, où
   elle s'explique. Même règle que pour le point du coefficient — deux dessins
   séparés dériveraient au premier ajustement, et personne ne le verrait
   puisque les deux marques ne sont jamais côte à côte à l'écran.

   Gris, comme le chevron : le budget couleur va au SENS, et « il y a une
   particularité ici » est une forme. Le vocabulaire vient de l'écran de
   synthèse — `user` individuel, `users` hérité du foyer — et n'a donc rien de
   neuf à faire apprendre. */
.icone-exempt {
  flex: none;
  color: var(--gris-500);
  vertical-align: middle;
}

.pastille-participant:hover,
.pastille-participant:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.pastille-participant:hover .icone-chevron,
.pastille-participant:focus-visible .icone-chevron {
  color: var(--accent);
}

/* CE QUI DIFFÈRE SE VOIT — même logique que l'écran de synthèse, où la
   valeur choisie porte un trait d'accent sous elle. Ici un point : à la
   taille d'une pastille, un soulignement se confondrait avec la bordure.

   UNE SEULE DÉFINITION POUR LES DEUX ENDROITS QUI MONTRENT CETTE MARQUE : la
   pastille, où elle signale, et la phrase du bas, où elle s'explique. Deux
   dessins séparés dériveraient au premier ajustement, et une légende qui ne
   ressemble plus à ce qu'elle légende est pire que pas de légende.

   Le `title` de la pastille ne suffisait pas : il n'existe pas au doigt, or
   c'est au téléphone qu'on lit cet écran. La phrase du bas est le seul
   endroit où le point s'explique sans coûter la densité qu'on vient de
   gagner. */
.pastille-participant--coef::before,
.puce-coef {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}

.pastille-participant--coef::before {
  content: "";
}

/* Dans une phrase, jamais dans une rangée flex : `inline-block` et calage
   vertical sur le texte qui l'entoure. */
.puce-coef {
  display: inline-block;
  vertical-align: middle;
}

/* L'AJOUT AU BOUT DES GENS, en pointillés : la forme dit « une pastille de
   plus », le pointillé dit qu'elle n'existe pas encore. */
.pastille-ajout-hote {
  display: flex;
}

.pastille-ajout {
  display: block;
  border-radius: 999px;
  cursor: pointer;
  list-style: none;
}

.pastille-ajout::-webkit-details-marker {
  display: none;
}

/* Le conteneur est l'ENFANT, jamais le `<summary>` (WebKit). */
.pastille-ajout-rangee {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-min);
  padding: var(--espace-1) var(--espace-3);
  border: 1px dashed var(--gris-300);
  border-radius: 999px;
  color: var(--accent);
  font-size: var(--taille-corps-sm);
  font-weight: 600;
}

.pastille-ajout:hover .pastille-ajout-rangee,
.pastille-ajout:focus-visible .pastille-ajout-rangee {
  border-color: var(--accent);
  border-style: solid;
}

/* OUVERT, LE FORMULAIRE PREND TOUTE LA LARGEUR. Sans cela il resterait
   comprimé à la largeur de sa pastille — trois champs dans 110px. `:has()`
   est déjà le mécanisme du cadre plein écran de la synthèse. */
.pastilles-participants:has(.ajout-participant[open]) .pastille-ajout-hote {
  flex: 1 1 100%;
}

.ajout-participant[open] {
  width: 100%;
}

.ajout-participant[open] .pastille-ajout-rangee {
  border-style: solid;
  border-color: var(--accent);
}

.ajout-coefficients {
  flex: 1 1 100%;
}

.ajout-coefficients-resume {
  display: block;
  cursor: pointer;
  list-style: none;
}

.ajout-coefficients-resume::-webkit-details-marker {
  display: none;
}

.ajout-coefficients-rangee {
  display: inline-flex;
  align-items: center;
  min-height: var(--cible-min);
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
}

.ajout-coefficients-champs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2) var(--espace-3);
  padding-top: var(--espace-2);
}

.ajout-coefficients-champs .champ-parametre {
  flex: 1 1 120px;
}

.ajout-coefficients-champs .aide-parametre {
  flex: 1 1 100%;
  margin: 0;
}

/* LES EXEMPTIONS DÈS LA CRÉATION : second bloc replié, jumeau du précédent.
   Même montage `<details>`, donc même précaution — le `<summary>` n'est
   JAMAIS le conteneur flex, la mise en page vit dans `-rangee`. */
.ajout-exemptions {
  flex: 1 1 100%;
}

.ajout-exemptions-resume {
  display: block;
  cursor: pointer;
  list-style: none;
}

.ajout-exemptions-resume::-webkit-details-marker {
  display: none;
}

.ajout-exemptions-rangee {
  display: inline-flex;
  align-items: center;
  min-height: var(--cible-min);
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
}

.ajout-exemptions-champs {
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
  padding-top: var(--espace-2);
}

.ajout-exemptions-champs .aide-parametre {
  margin: 0;
}

/* CAS 2 — UN ENFANT SUR UN COMPTE RÉSERVÉ AUX ADULTES. Il y porte déjà un
   poids nul (§4.3 étape 3) : l'exemption n'y changerait rien.

   La qualité se choisit dans CE formulaire, deux champs plus haut : le
   serveur ne peut donc pas la connaître au rendu, et cet écran n'a pas de JS
   (propriété portante depuis la refonte, pas un manque). C'est `:has()` qui
   lit le segment coché — déjà le mécanisme qui donne sa largeur au bloc
   d'ajout ouvert.

   L'atténuation est VISUELLE, la case reste soumissible : l'apparence peut
   dépendre du CSS, jamais la justesse (même règle que la case à trois états
   de l'écran de dépense). Et rien n'est perdu si elle part quand même — une
   exemption posée sur un enfant devient vraie le jour où il passe adulte,
   exactement comme la marque non-buveur qu'on reporte plutôt que d'effacer. */
.note-adultes {
  display: none;
}

.form-ajout-participant:has(input[name="est_adulte"][value="0"]:checked)
  .case-compte--adultes {
  background: var(--gris-050);
  color: var(--gris-500);
}

.form-ajout-participant:has(input[name="est_adulte"][value="0"]:checked)
  .note-adultes {
  display: inline;
}

/* Le formulaire d'ajout, DANS le repli de la pastille. `flex-basis`
   explicite sur chaque enfant, jamais une rangée souple + fixes sans wrap :
   c'est ce défaut-là qui avait tronqué « Famille Bernard » en « Famill ». */
.form-ajout-participant {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--espace-2) var(--espace-3);
  margin-top: var(--espace-2);
  padding: var(--espace-3);
  background: var(--gris-050);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.form-ajout-participant .champ-parametre {
  flex: 1 1 100%;
}

.form-ajout-participant .segmente {
  flex: 1 1 180px;
}

.form-ajout-valider {
  flex: 0 0 auto;
}

/* Une ligne discrète, pas un bloc : l'exemption est rare, mais quand elle
   existe elle doit se lire sans ouvrir quoi que ce soit — c'est elle qui
   explique un solde plus léger que prévu.

   Elle se DÉTACHE néanmoins du contenu qu'elle résume : sans son fond
   propre, une phrase d'état posée en bas d'une carte se lit comme une
   légende, et plus rien n'annonce qu'on peut la changer. Rangée souple
   (jamais de flex sans wrap qui mélange souple et fixe, cf. Partie 4) : en
   375px l'action passe à la ligne sous l'état plutôt que de l'écraser. */
.carte-foyer-exemptions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espace-2);
  min-height: var(--cible-min);
  padding: var(--espace-3) var(--espace-4);
  border-top: var(--bordure);
  /* Pas de rayon à poser ici : `.carte-foyer` est en `overflow: hidden`,
     c'est elle qui découpe le coin. */
  background: var(--gris-050);
  font-size: var(--taille-corps-sm);
  color: var(--gris-900);
  text-decoration: none;
}

/* LA RÈGLE EN GRIS, L'EXCEPTION EN PLEINE ENCRE. « Participe à tous les
   comptes » est le cas de presque tout le monde : il s'atténue. Porté par
   l'ÉTAT seul et non par la ligne entière — sinon le verbe s'atténuerait
   avec lui, et c'est précisément lui qui doit rester lisible. */
.carte-foyer-exemptions--aucune .carte-foyer-exemptions-etat {
  color: var(--gris-500);
}

/* L'ACTION : à l'accent, poussée à droite, le chevron avec elle. */
.carte-foyer-exemptions-action {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  margin-left: auto;
  color: var(--accent);
  font-weight: 600;
}

.carte-foyer-exemptions .icone-chevron {
  flex: none;
}

.carte-foyer-exemptions:hover,
.carte-foyer-exemptions:focus-visible {
  background: var(--gris-100);
  outline: none;
}

.carte-foyer-exemptions:hover .carte-foyer-exemptions-action,
.carte-foyer-exemptions:focus-visible .carte-foyer-exemptions-action {
  text-decoration: underline;
}

/* Case à cocher de paramétrage : une ligne entière est cliquable, et l'état
   coché se voit à la SURFACE (le traitement réservé à la sélection), pas à
   une couleur d'identité. */
.case-a-cocher {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-size: var(--taille-corps);
  cursor: pointer;
}

.case-a-cocher + .case-a-cocher {
  margin-top: var(--espace-2);
}

.case-a-cocher input {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  accent-color: var(--accent);
}

.case-a-cocher:has(input:checked) {
  background: var(--accent-surface);
  border-color: var(--accent);
}

.case-a-cocher-texte {
  font-weight: 600;
}

.case-a-cocher-note {
  margin-left: auto;
  font-size: var(--taille-petite);
  font-weight: 400;
  color: var(--gris-500);
}

/* Réglé au foyer : coché, mais gris et non cliquable — le traitement de
   surface accentué est réservé à ce que CET écran décide. Une case grisée
   qui s'explique vaut mieux qu'une case absente, qui se lirait « il
   participe ». */
.case-a-cocher--herite,
.case-a-cocher--herite:has(input:checked) {
  background: var(--gris-050);
  border-color: var(--gris-300);
  color: var(--gris-500);
  cursor: default;
}

.form-nouveau-foyer {
  display: flex;
  align-items: flex-end;
  gap: var(--espace-3);
  flex-wrap: wrap;
  padding: var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon-lg);
  margin-bottom: var(--espace-4);
}

.form-nouveau-foyer .champ-parametre {
  flex: 1 1 220px;
  min-width: 0;
}

.sous-titre-foyer {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  margin: calc(-1 * var(--espace-3)) 0 var(--espace-4);
  font-weight: 600;
  color: var(--gris-700);
}

/* Suppression : le rouge sémantique existant (--texte-debiteur), en TEXTE
   pour les liens. Le bouton de confirmation est le seul aplat rouge de
   l'app — il est l'action irréversible d'un écran qui ne sert qu'à ça. */
.lien-danger {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-2);
  min-height: var(--cible-min);
  color: var(--texte-debiteur);
  font-size: var(--taille-corps);
  font-weight: 600;
  text-decoration: none;
}

.lien-danger:hover,
.lien-danger:focus-visible {
  text-decoration: underline;
}

.bouton-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-4);
  background: var(--texte-debiteur);
  color: var(--gris-000);
  border: none;
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: var(--taille-corps-lg);
  font-weight: 600;
  cursor: pointer;
}

.bouton-danger:hover,
.bouton-danger:focus-visible {
  opacity: 0.9;
  outline: none;
}

.bloc-consequences {
  padding: var(--espace-4);
  margin-bottom: var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon-lg);
}

.bloc-consequences-titre {
  margin: 0 0 var(--espace-2);
  font-size: var(--taille-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--gris-500);
}

.bloc-consequences ul {
  margin: 0 0 var(--espace-4);
  padding-left: var(--espace-5);
  color: var(--gris-700);
  font-size: var(--taille-corps);
}

.bloc-consequences ul:last-child {
  margin-bottom: 0;
}

.bloc-consequences li + li {
  margin-top: var(--espace-2);
}

/* « supprimé » : une mention, jamais une couleur d'identité — le foyer
   garde son monogramme et sa place dans les soldes. */
.mention-supprime {
  font-size: var(--taille-micro);
  font-weight: 400;
  color: var(--gris-500);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Frise de présence (Phase 2A, Partie 3)
   --------------------------------------
   DEUX écrans, pas deux tailles : `.frise-edition` et `.frise-consultation`
   ne montrent ni le même contenu ni les mêmes possibilités. Le basculement
   se fait au point de rupture unique de l'app (720px), plus bas dans ce
   fichier — `display: none` retire vraiment le bloc caché de l'arbre
   d'accessibilité, ce qui rend « pas d'édition sur mobile » vrai aussi pour
   un lecteur d'écran. */

.frise-edition {
  display: none; /* rétabli au-dessus de 720px */
}

.frise {
  --largeur-etiquette: 128px;
  overflow-x: auto; /* un séjour de trois mois ne casse pas la page */
  padding-bottom: var(--espace-2);
}

.frise-rangee,
.frise-ligne {
  display: grid;
  grid-template-columns: var(--largeur-etiquette) minmax(420px, 1fr);
  align-items: center;
  gap: var(--espace-3);
}

.frise-etiquette {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  min-width: 0;
  font-size: var(--taille-corps-sm);
}

.frise-etiquette-texte {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}

/* La piste porte TROIS colonnes par jour : la barre se dessine au repas,
   alors que le glisser reste à la journée. Une frise au jour plein
   mentirait sur une interruption qui commence à midi. */
.frise-piste {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--nb-creneaux), 1fr);
  align-items: center;
  min-height: 34px;
}

.frise-piste--jours {
  grid-template-columns: repeat(var(--nb-jours), 1fr);
  min-height: 0;
}

.frise-jour {
  padding: 0 0 var(--espace-1);
  text-align: center;
  font-size: var(--taille-micro);
  color: var(--gris-500);
  line-height: 1.2;
}

.frise-jour-mois {
  display: block;
  font-weight: 600;
  color: var(--gris-700);
}

.frise-jour--aujourdhui .frise-jour-numero {
  font-weight: 700;
  color: var(--accent);
}

/* Trait d'aujourd'hui : une règle verticale, pas un aplat — le budget
   couleur reste réservé au sens, et « aujourd'hui » en est un. */
.frise-aujourdhui {
  position: absolute;
  top: 0;
  bottom: 0;
  grid-row: 1;
  border-left: 2px solid var(--accent);
  opacity: 0.45;
  pointer-events: none;
}

.frise-segment {
  grid-row: 1;
  height: 18px;
  background: var(--accent-surface);
  border: 1px solid var(--accent);
  border-radius: 3px;
}

.frise-ligne--detachee .frise-segment {
  height: 12px;
  background: var(--gris-100);
  border-color: var(--gris-500);
}

.frise-ligne--detachee .frise-etiquette-texte {
  font-weight: 400;
  color: var(--gris-700);
}

.frise-poignee {
  grid-row: 1;
  justify-self: center;
  width: 14px;
  height: 26px;
  padding: 0;
  background: var(--gris-000);
  border: 1.5px solid var(--accent);
  border-radius: 4px;
  cursor: ew-resize;
  touch-action: none; /* sans quoi le navigateur fait défiler au lieu de glisser */
}

.frise-poignee:focus-visible,
.frise-poignee--active {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.frise-legende {
  margin: var(--espace-3) 0 0;
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

/* Le formulaire de repli. Ce n'est pas un chemin de secours : c'est le seul
   chemin d'écriture, le glisser ne fait que remplir ses champs.

   Toute la mise en page tient sur DEUX grilles alignées — celle des bornes
   et celle des interruptions — de sorte que « Début » et « Fin » tombent au
   même endroit d'une rangée à l'autre. Aucun fieldset n'est lui-même une
   grille : un <legend> y deviendrait un élément de grille et disloquerait
   l'alignement (même piège que le <p> en flex de la Partie 1). */

.frise-details {
  margin: 0 0 var(--espace-2) var(--largeur-etiquette);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

.frise-details[open] {
  margin-bottom: var(--espace-4);
  border-color: var(--accent);
}

/* DÉFAUT DE CLASSE, à ne pas réintroduire : un <summary> en `display: flex`
   (ou grid) cesse, dans WebKit, d'être la commande d'ouverture de son
   <details> — le clic n'ouvre plus rien, et sur un séjour neuf où chaque
   présence se saisit dans un de ces panneaux, plus rien n'est saisissable.
   Le summary reste donc en `block` et la mise en page vit dans un enfant.
   Jumeau du <legend> en flex (Partie 3) et du <p> en flex (Partie 1) :
   certains éléments perdent leur comportement natif en devenant conteneurs. */
.frise-details-resume {
  display: block;
  padding: var(--espace-2) var(--espace-3);
  cursor: pointer;
  list-style: none; /* le triangle natif est remplacé par .frise-details-chevron */
  font-size: var(--taille-corps-sm);
}

.frise-details-rangee {
  display: flex;
  flex-wrap: wrap;
  /* `center` depuis que l'en-tête porte le monogramme : en `baseline`, une
     pastille de 28px s'aligne sur le texte et dépasse vers le haut. */
  align-items: center;
  gap: var(--espace-2);
  min-height: var(--cible-min);
}

/* Plus petite que sur les cartes : ici elle rattache la ligne à sa famille,
   elle n'est pas le sujet — et une ligne détachée ne porte qu'un prénom,
   c'est le seul rappel du foyer. */
.frise-details-resume .pastille-foyer {
  width: 28px;
  height: 28px;
  min-width: 28px;
  font-size: var(--taille-micro);
}

.frise-details-resume::-webkit-details-marker {
  display: none;
}

.frise-details-resume:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--rayon);
}

.frise-details-qui {
  font-weight: 600;
}

.frise-details-quand {
  color: var(--gris-500);
}

/* Le chevron dit que la ligne s'ouvre ET se referme — sans lui, le résumé
   ne se lisait pas comme une bascule. Même dessin que celui du panneau
   « concernés » de la saisie : une seule idée visuelle dans l'app. */
.frise-details-chevron {
  flex: 0 0 auto;
  margin-left: auto;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--gris-500);
  border-bottom: 1.5px solid var(--gris-500);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

/* `>` obligatoire : le panneau contient maintenant des <details> imbriqués
   (les sections repliées). Sans lui, ouvrir la ligne retournerait AUSSI les
   chevrons de ses sections, qui sont pourtant restées fermées. */
.frise-details[open] > .frise-details-resume .frise-details-chevron {
  transform: rotate(45deg);
}

/* Même gouttière que les sections repliées en dessous : les étiquettes
   « Arrivée », « Qui part de ces dates ? » et « Début » s'alignent toutes
   sur le même bord gauche, d'un bloc à l'autre. */
.form-presence {
  padding: var(--espace-4);
  border-top: var(--bordure);
}

/* AUCUN cadre natif dans ce panneau : ni bordure de fieldset, ni légende
   posée sur le trait. Un `<fieldset>` reste le bon élément (il groupe des
   contrôles pour un lecteur d'écran, et `<legend>` est son intitulé), mais il
   se présente comme les autres blocs de l'app — étiquette AU-DESSUS, pas de
   cadre. La règle porte sur le PANNEAU, pas sur `.form-presence` : depuis la
   refonte, la section « détacher » a son propre formulaire, et le cadre natif
   y réapparaissait. */
.frise-details fieldset {
  min-width: 0;
  margin: 0 0 var(--espace-4);
  padding: 0;
  border: none;
}

.frise-details fieldset:last-of-type {
  margin-bottom: var(--espace-3);
}

.frise-details legend {
  padding: 0;
  margin-bottom: var(--espace-1);
}

/* Bornes : deux colonnes égales tant que la place le permet, une seule
   ensuite. Les deux fieldsets restent en display: block (cf. le legend). */
.rangee-bornes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: var(--espace-3) var(--espace-4);
  margin-bottom: var(--espace-4);
}

.borne-champs {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
  align-items: center;
  gap: var(--espace-2);
}

.borne--active {
  outline: 2px solid var(--accent);
  outline-offset: 6px;
  border-radius: var(--rayon);
}

/* Infobulle de la borne active : « arrivée · 22 juil au dîner ». Vide sans
   JS, donc invisible — le champ date et le select disent déjà tout. */
.borne-etat {
  display: block;
  margin-top: var(--espace-1);
  font-size: var(--taille-petite);
  color: var(--accent);
  font-weight: 600;
}

.borne-etat:empty {
  display: none;
}

/* TOUS les champs de la carte de présence d'un coup — jamais bloc par bloc.
   La règle ciblait `.form-presence` : les dates des sections repliées
   (détachement, repas non pris) restaient donc au rendu NATIF du navigateur,
   bordure épaisse comprise, à trois centimètres de champs identiques. Une
   règle par bloc, c'est un bloc oublié au prochain ajout — celle-ci porte sur
   le panneau, quel que soit le formulaire (ou l'absence de formulaire : les
   interruptions ne sont rattachées au leur que par `form=`). */
.frise-details input[type="date"],
.frise-details select,
.select-repas {
  width: 100%;
  min-height: 38px;
  padding: var(--espace-1) var(--espace-2);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-family: inherit;
  font-size: var(--taille-corps-sm);
  color: var(--gris-900);
}

.frise-details input[type="date"]:focus-visible,
.frise-details select:focus-visible,
.select-repas:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

/* Interruptions : l'en-tête et chaque rangée partagent EXACTEMENT les mêmes
   colonnes, c'est ce qui met « Début » et « Fin » d'aplomb. */
.interruptions-entete,
.interruption {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 5.5rem;
  align-items: center;
  gap: var(--espace-2) var(--espace-3);
}

.interruptions-entete {
  margin-bottom: var(--espace-1);
  font-size: var(--taille-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--gris-500);
}

.interruption + .interruption {
  margin-top: var(--espace-2);
}

.interruption-borne {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
  align-items: center;
  gap: var(--espace-2);
}

.interruption-retirer {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  font-size: var(--taille-petite);
  color: var(--gris-500);
  cursor: pointer;
}

.interruption--vierge .interruption-borne {
  opacity: 0.7; /* la rangée d'ajout s'efface tant qu'elle est vide */
}

.interruption--vierge:focus-within .interruption-borne {
  opacity: 1;
}

.detacher {
  margin: 0 var(--espace-3) var(--espace-3);
}

/* Confirmation détaillée : elle ÉNUMÈRE ce qui a changé (voir
   web/presences.py). Plus longue qu'un « enregistré », donc alignée à
   gauche et pas centrée comme une pastille de statut. */
.confirmation--detaillee {
  text-align: left;
}

/* Consultation — mobile. Une liste, aucune barre, aucun formulaire. */

.aujourdhui-bloc {
  padding: var(--espace-3) var(--espace-4);
  margin-bottom: var(--espace-4);
  background: var(--accent-surface);
  border: 1px solid var(--accent);
  border-radius: var(--rayon-lg);
}

.aujourdhui-titre {
  margin: 0 0 var(--espace-1);
  font-size: var(--taille-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--gris-700);
}

.aujourdhui-qui {
  margin: 0;
  font-size: var(--taille-corps-lg);
  font-weight: 600;
}

.aujourdhui-compte {
  display: block;
  font-size: var(--taille-petite);
  font-weight: 400;
  color: var(--gris-700);
}

.carte-presence {
  padding: var(--espace-3) var(--espace-4);
  margin-bottom: var(--espace-3);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon-lg);
}

.carte-presence-titre {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  margin: 0 0 var(--espace-2);
  font-size: var(--taille-corps-lg);
}

.presence-ligne + .presence-ligne {
  margin-top: var(--espace-3);
  padding-top: var(--espace-3);
  border-top: var(--bordure);
}

.presence-qui {
  margin: 0;
  font-weight: 600;
  font-size: var(--taille-corps);
}

/* Une exception se décale : elle se lit comme une note sous la ligne du
   foyer, pas comme un second foyer. */
.presence-ligne--exception {
  padding-left: var(--espace-3);
  border-left: 2px solid var(--gris-300);
}

/* La phrase de portée : qui cette barre couvre, et ce que le bouton fera.
   Discrète (elle se lit une fois), jamais alarmante — le rouge d'anomalie
   est réservé au tableau de bord, ici on est à l'endroit où l'on corrige. */
.form-presence-portee {
  margin: 0 0 var(--espace-3);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

/* --- Sections repliées du panneau (détacher, repas non pris) -------------
   Deux gestes rares sous le geste fréquent. Fermées par défaut, annoncées
   par une icône, un titre et ce qu'elles permettent : un titre seul
   (« Interruptions ») ne dit pas s'il faut ouvrir. */

.section-repliee {
  border-top: var(--bordure);
}

/* display: block, JAMAIS flex — dans WebKit un <summary> devenu conteneur
   cesse d'ouvrir son <details> (bug bloquant vécu, garde-fou dans
   test_web_css.py). La mise en page vit dans .section-rangee. */
.section-resume {
  display: block;
  padding: var(--espace-3) var(--espace-4);
  cursor: pointer;
  list-style: none;
}

.section-resume::-webkit-details-marker {
  display: none;
}

.section-resume:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.section-rangee {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  min-height: var(--cible-min);
}

.section-icone {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  color: var(--accent);
}

.section-textes {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.section-titre {
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--gris-900);
}

.section-sous-titre {
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

.section-repliee[open] > .section-resume .frise-details-chevron {
  transform: rotate(45deg);
}

.section-repliee[open] > .section-resume {
  border-bottom: var(--bordure);
}

/* Le contenu déplié respire, et s'aligne sur le formulaire du dessus. */
.section-repliee > .form-detacher,
.section-repliee > .interruptions {
  padding: var(--espace-4);
}

/* Les membres à détacher : des pastilles cochables, pas des champs. L'état
   sélectionné se voit d'un coup d'œil (surface accentuée ET bordure accent,
   règle du design system) — l'input natif est masqué, c'est l'étiquette qui
   porte l'état et la zone tactile. */
.choix-membres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2);
}

.choix-membres .pastille-option-contenu {
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-4);
  font-size: var(--taille-corps-sm);
  font-weight: 600;
}

.presence-quand {
  margin: var(--espace-1) 0 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.presence-quand--manquante {
  color: var(--texte-anomalie);
}

.presence-trou {
  margin: var(--espace-1) 0 0;
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

/* Synthèse des participants. DEUX MISES EN PAGE, UN SEUL GABARIT : liste en
   étroit, matrice en large. La matrice exige de la largeur par NATURE — une
   colonne par compte — et le sticky rendait son défilement supportable sans
   le supprimer (253px hors cadre en 375px). En dessous de 720px, les mêmes
   cellules se relisent donc en liste, et la liste ne montre QUE les
   exceptions. Ce sont bien les mêmes `<td>` : deux gabarits, ou deux jeux de
   données, auraient divergé au premier changement de règle.

   Aucune couleur sémantique ici — le budget couleur du projet est réservé
   au sens créancier/débiteur, et une exemption est un état parfaitement
   normal, pas un avertissement. Elle se marque donc par une surface neutre
   et une icône, comme un foyer se marque par un monogramme et jamais par
   une teinte qui lui serait propre. */
.sous-titre {
  margin: calc(-1 * var(--espace-3)) 0 var(--espace-4);
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

/* « Seules les exceptions sont indiquées » : la phrase porte ce que les
   coches disaient en large. Sans elle, une ligne muette se lirait comme une
   donnée manquante. */
.aide-liste {
  margin: 0 0 var(--espace-4);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

/* EN ÉTROIT, LE CADRE UNIQUE DISPARAÎT : chaque foyer devient sa propre
   carte (voir `.synthese-foyer` juste en dessous). Les cinq foyers coulaient
   dans un bloc continu et rien ne disait où l'un finissait. En large, le
   cadre reprend son rôle — c'est lui qui porte le défilement du tableau
   collant, il est restauré dans la requête média. */
.table-defilante {
  margin: var(--espace-4) 0;
}

/* --- Liste (étroit) ------------------------------------------------------
   Les éléments de tableau redeviennent des blocs : plus de colonnes, donc
   plus rien à faire déborder. AUCUN défilement horizontal ne subsiste. */
.table-synthese {
  display: block;
  width: 100%;
}

.table-synthese thead {
  display: none; /* des en-têtes de colonnes sans colonnes ne disent rien */
}

.table-synthese tbody {
  display: block;
}

/* UNE CARTE PAR FOYER, au vocabulaire des autres écrans : blanc sur le fond,
   AUCUNE bordure (le blanc suffit à détacher), rayon 14, padding 22 — les
   mêmes valeurs que `.recap-section` et que le tableau des soldes. Un
   troisième dialecte de carte serait pire qu'aucune carte.

   Elle ne fait pas que séparer : c'est elle qui autorise l'allègement du
   gabarit juste en dessous (une exemption de foyer n'est plus répétée sur
   chaque membre). Sans délimitation, on pourrait lire une ligne de
   participant sans rattacher son en-tête.

   Sélecteur à (0,2,1) : la requête média porte la MÊME spécificité, sinon
   cette règle-ci l'emporterait en large et le tbody cesserait d'être un
   `table-row-group` — quatrième occurrence du piège sur cet écran. */
.table-synthese tbody.synthese-foyer {
  margin-bottom: var(--espace-3);
  padding: 22px;
  background: var(--gris-000);
  border-radius: 14px;
}

.table-synthese th,
.table-synthese td {
  display: block;
  padding: 0;
  border: none;
  text-align: left;
}

/* Une ligne = un participant : le nom à gauche, les coefficients à droite,
   et les exceptions dessous, chacune sur sa ligne. */
.table-synthese tbody tr {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--espace-1) var(--espace-2);
  /* Plus de retrait horizontal : c'est la carte du foyer qui le porte. */
  padding: var(--espace-2) 0;
  border-top: var(--bordure);
}

.table-synthese tbody tr:first-child {
  border-top: none;
}

.cellule-nom {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  color: var(--gris-900);
}

.synthese-foyer-nom a,
.cellule-nom a {
  color: inherit;
  text-decoration: none;
}

.synthese-foyer-nom a:hover,
.cellule-nom a:hover {
  text-decoration: underline;
}

/* « repas 1,00 · nuitée 1,00 » : sans l'en-tête de colonnes, une valeur nue
   ne dit plus de quoi elle est le coefficient. L'étiquette vient de
   `data-libelle`, donc de la donnée déjà présente, pas d'un second gabarit. */
.col-coef {
  font-size: var(--taille-petite);
  color: var(--gris-500);
  white-space: nowrap;
  /* Chiffres de largeur fixe : « 0,60 » et « 1,00 » doivent tomber l'un
     sous l'autre, sinon la colonne ondule et on ne compare plus rien. */
  font-variant-numeric: tabular-nums;
}

.col-coef::before {
  content: attr(data-libelle) " ";
}

.col-coef + .col-coef::before {
  content: "· " attr(data-libelle) " ";
}

/* SEULES LES EXCEPTIONS. Une personne sans mention participe à tout — c'est
   ce que dit la phrase d'introduction, et c'est ce qui rend la liste
   lisible : sur dix-huit participants, trois lignes portent une mention. */
.col-compte {
  flex-basis: 100%;
}

/* Sélecteur préfixé par `.table-synthese` À DESSEIN : `.table-synthese td`
   (0,1,1) l'emporterait sinon sur `.col-compte--participe` (0,1,0), et la
   coche resterait affichée sous chaque nom — vu à la capture. */
.table-synthese .col-compte--participe {
  display: none;
}

/* NE PAS RÉPÉTER CE QUE LE FOYER VIENT DE DIRE. Son en-tête annonce
   « Exempté de : Courses, Alcool » ; chaque membre reportait ensuite les
   deux mêmes pastilles — quatre répétitions d'une règle unique, à deux
   lignes d'intervalle. C'est la règle de tout le projet : la règle du foyer
   se dit une fois, seul ce qui DIFFÈRE se répète (même principe que la
   marque d'exemption individuelle et que les dates de présence).

   Rien n'est perdu : `Etat.EXEMPTE_FOYER` et `bloc.exempte_de` sortent du
   MÊME ensemble (`par_foyer[foyer.id]`, cf. web/synthese.py), donc tout
   compte masqué ici est nommé au-dessus. Un test le prouve plutôt que de
   le supposer.

   Réservé à l'étroit : en large, la lecture en COLONNE est tout l'écran, et
   une cellule vide dans la colonne « Alcool » se lirait « participe ».
   Même sélecteur préfixé que la coche, pour la même raison de spécificité. */
.table-synthese .col-compte--exempte_foyer {
  display: none;
}

.mention-sans-objet {
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

.col-compte--sans_objet .cellule-sans-objet {
  display: none; /* le tiret est un signe de tableau, pas une phrase */
}

/* En-tête de foyer : même rôle qu'en large, en tête de groupe. La ligne
   reste un BLOC — en flex comme les lignes de participants, sa cellule
   unique se dimensionnerait sur son contenu et la bande grise s'arrêterait
   au milieu de l'écran (vu à la capture). */
.table-synthese tbody tr.synthese-foyer-entete {
  display: block;
  padding: 0;
}

/* En étroit, la carte délimite déjà le foyer : la bande grise ferait un
   second signal pour la même chose. L'en-tête redevient le haut de la
   carte, et le trait de la première ligne de membre le sépare d'eux. */
.synthese-foyer-entete th {
  padding: 0 0 var(--espace-2);
  background: none;
  border-top: none;
}

.synthese-foyer-rangee {
  display: flex;
  flex-wrap: wrap; /* la marque passe SOUS le nom plutôt que de l'écraser */
  align-items: center;
  gap: var(--espace-2);
}

.synthese-foyer-nom {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  font-size: var(--taille-corps);
  font-weight: 600;
  color: var(--gris-900);
  text-transform: none;
  letter-spacing: 0;
}

/* Un coefficient PERSONNALISÉ : trait d'accent sous la valeur, et rien
   d'autre. Le mot « propre » a disparu de l'écran — un marqueur visuel n'a
   pas besoin d'être traduit ligne à ligne, la légende le fait une fois.
   `box-shadow` plutôt que `border-bottom` : le trait ne déplace aucune
   ligne de base, donc les colonnes de chiffres restent alignées. */
.col-coef--propre .valeur-coef {
  /* Valeur STANDARD en gris, valeur choisie en texte normal : la
     différence de densité fait la moitié du travail, le trait fait le
     reste. Deux signaux discrets valent mieux qu'un mot. */
  color: var(--gris-900);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.valeur-coef--exemple {
  font-variant-numeric: tabular-nums;
  box-shadow: inset 0 -2px 0 var(--accent);
}

.mention-discrete {
  margin-left: var(--espace-1);
  font-size: var(--taille-micro);
  font-weight: 400;
  color: var(--gris-500);
}

/* LES DATES DU FOYER, sur sa rangée d'en-tête.
   `flex-basis: 100%` en étroit : elles passent SOUS le nom, comme la marque
   d'exemption. En large la rangée est en `width: fit-content` (elle colle à
   gauche dans une cellule à `colspan`), donc rien ne peut être « poussé à
   droite » — les dates suivent simplement le nom, ce qui est aussi le bon
   ordre de lecture : qui, quand, puis l'exception. */
.synthese-foyer-dates {
  flex-basis: 100%;
  font-size: var(--taille-petite);
  font-weight: 400;
  color: var(--gris-500);
  text-transform: none;
  letter-spacing: 0;
}

/* Une personne dont les dates DIVERGENT de celles de son foyer. Même poids
   visuel que « enfant » : c'est une mention, pas une alerte — le foyer reste
   la référence, et cette ligne dit seulement en quoi elle s'en écarte. */
.mention-dates-propres {
  margin-left: var(--espace-1);
  font-size: var(--taille-micro);
  font-weight: 400;
  color: var(--gris-500);
  white-space: nowrap;
}

/* La pastille d'exemption : NEUTRE. Les deux grains portent le même mot et
   ne se distinguent que par leur icône (une silhouette, un groupe) — d'où
   la légende, obligatoire, aux deux largeurs. */
.marque-exemption {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  padding: 2px var(--espace-2);
  background: var(--gris-100);
  border: var(--bordure);
  border-radius: 999px;
  font-size: var(--taille-micro);
  font-weight: 600;
  color: var(--gris-700);
  text-transform: none;
  letter-spacing: 0;
  white-space: nowrap;
}

/* La coche « participe » et le tiret « ne s'applique pas » sont le cas de
   LOIN le plus fréquent : ils ne disent rien d'autre que « normal », et
   restent donc EN RETRAIT derrière les pastilles d'exemption. Mais en
   retrait n'est pas illisible : à la teinte de bordure (`--gris-300`) on ne
   les voyait qu'à peine. Ils prennent la teinte de TEXTE atténué —
   `--gris-500`, celle des mentions secondaires comme « enfant » — et un
   pixel de plus. La hiérarchie tient toujours : une pastille porte un fond,
   une bordure et un mot ; eux n'ont qu'un trait gris. Jamais de texte à
   côté d'eux — le seul mot d'une cellule doit être une exception. */
.icone-participe {
  width: 16px;
  height: 16px;
  color: var(--gris-500);
}

.cellule-sans-objet {
  color: var(--gris-500);
}

/* Visible pour un lecteur d'écran, invisible à l'œil. Une cellule qui ne
   contiendrait qu'une icône (`aria-hidden`) serait VIDE à l'oreille : sur
   cet écran, « participe » et « ne s'applique pas » ne se disent que par
   ce texte-là. `clip-path`, jamais `display: none` — ce dernier retire
   aussi de l'arbre d'accessibilité. */
.lecteur-ecran {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.legende-synthese {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-2) var(--espace-4);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

.legende-synthese li {
  display: flex;
  align-items: center;
  gap: var(--espace-1);
}

/* La coche et le tiret n'existent pas dans la liste : les décrire serait
   pire que pas de légende du tout. Sélecteur préfixé pour la même raison que
   la coche — `.legende-synthese li` (0,1,1) l'emporterait sinon. */
.legende-synthese .legende-large {
  display: none;
}

/* Point de rupture unique : même gabarit HTML, layout élargi en CSS. */
@media (min-width: 720px) {
  /* --- L'EN-TÊTE RESTE VISIBLE, SUR PC SEULEMENT ---------------------------

     `sticky`, JAMAIS `fixed`. Fixed sortirait l'en-tête du flux, et l'écran
     Participants s'appuie précisément sur le flux : `body:has(.table-synthese)`
     a une hauteur de 100dvh que le cadre défilant se partage avec l'en-tête
     (`flex: 1`). En le retirant du flux, le cadre gagnerait ses 69px et
     déborderait par le bas — le tableau perdrait ses dernières lignes sous
     l'écran. Sticky garde la place ET colle : aucun `padding-top` de
     compensation à tenir quelque part, et rien à recalculer si la hauteur
     change.

     PAS EN ÉTROIT, et c'est un arbitrage chiffré : l'en-tête y fait 130px,
     soit 19 % d'un iPhone SE (667px) contre 7 % d'un écran de 900px. La
     frise et la synthèse, les deux écrans les plus longs, sont justement
     ceux où chaque pixel vertical a été disputé. Et le geste coûte moins
     cher sur un téléphone : remonter en haut, c'est un coup de pouce, là où
     c'est un aller-retour à la souris. Rendre le bandeau collant en étroit
     tient en une ligne — il suffit de sortir ce bloc de la requête média. */
  .entete {
    position: sticky;
    top: 0;
    /* Au-dessus de tout le contenu, y compris des quatre niveaux collants du
       tableau de synthèse (z-index 1 à 4) et du bandeau de confirmation
       (10). Ils ne se recouvrent pas — le tableau colle DANS son propre
       cadre défilant, sous l'en-tête — mais l'ordre doit être dit une fois
       pour toutes plutôt que laissé au hasard de l'ordre du document. */
    z-index: 20;
  }

  :root {
    --hauteur-entete: 69px; /* large : tout tient sur une rangée */
    --hauteur-entete-parametres: 69px; /* large : tout rejoint la rangée du titre */
  }

  /* En large, la barre des paramètres REJOINT LA RANGÉE du titre, exactement
     comme la barre principale : la place existe, et une seconde rangée
     coûterait 57px de plus à un en-tête qui, lui, est COLLÉ sur PC. */
  .nav-parametres {
    order: 2;
    flex-basis: auto;
    margin: 0 auto 0 var(--espace-5);
    padding: 0;
    border-top: none;
  }

  .nav-retour {
    flex-basis: auto;
    padding-right: var(--espace-3);
    border-right: var(--bordure);
    border-bottom: none;
  }

  /* LES ANCRES. L'app renvoie vers `#ligne-N` (frise) et `#foyer-N`
     (paramètres) après chaque enregistrement. Sans ce décalage, le
     navigateur défile au bon endroit mais la cible se retrouve DERRIÈRE
     l'en-tête collé : on enregistre, et la ligne qu'on vient de modifier a
     disparu. C'est du CSS, pas du JS — `scroll-margin-top` est fait pour ça,
     et il vaut pour tous les défilements vers l'élément (clic sur un lien
     d'ancre, redirection, `scrollIntoView`), pas seulement pour le premier.

     La marge dépasse volontairement la hauteur de l'en-tête : la cible
     apparaît nettement en dessous plutôt qu'au ras du bord. */
  [id^="foyer-"],
  [id^="ligne-"] {
    scroll-margin-top: calc(var(--hauteur-entete) + var(--espace-4));
  }

  /* LE BANDEAU REPASSE SOUS L'EN-TÊTE, parce qu'ICI il y a un en-tête à
     passer dessous : collé, il occupe ces pixels en permanence. C'est le
     pendant exact du décalage d'ancre ci-dessus — les deux ne sont posés que
     dans la largeur où l'obstacle existe, et `--hauteur-entete` n'est
     consommé que là où il désigne quelque chose de réel. */
  .confirmation {
    top: calc(var(--hauteur-entete) + var(--espace-2));
    bottom: auto;
  }

  /* Troisième consommateur, même règle : les tuiles de poste collent SOUS
     l'en-tête là où il colle lui-même. En étroit elles collent à 0, où rien
     ne les précède. */
  .bandeau-comptes {
    top: var(--hauteur-entete);
  }

  /* La place existe ici : les quatre tiennent sur une rangée. Le 2×2 est la
     réponse de l'étroit, pas une préférence. */
  .bandeau-comptes:has(> :nth-child(4)) {
    grid-template-columns: repeat(4, 1fr);
  }

  /* La barre rejoint la rangée de l'en-tête : séjour, navigation, puis
     l'engrenage et le foyer poussés à droite par `margin-right: auto`.
     Mêmes entrées qu'en étroit — seul le pliage change. */
  .nav-principale {
    order: 2;
    flex-basis: auto;
    flex-wrap: wrap;
    gap: var(--espace-1) var(--espace-4);
    margin: 0 auto 0 var(--espace-5);
    padding: 0;
    border-top: none;
  }

  /* La place existe : on retrouve EXACTEMENT la barre d'avant — libellés
     longs, sur une rangée — AVEC leur icône.

     CECI ANNULE la décision de la Phase 2A, Partie 4 (« sur PC, la barre est
     exactement celle d'avant : des libellés, pas d'icônes ; à côté d'un mot
     entier l'icône doublerait un libellé déjà limpide »). La raison du
     changement n'est pas dans cet écran-ci, elle est ENTRE LES APPAREILS :
     on saisit une dépense sur le téléphone, on vérifie les soldes sur le
     PC, et le repère doit être la même forme des deux côtés. La barre était
     le seul endroit de l'app où le vocabulaire visuel dépendait de la
     largeur de la fenêtre. Le libellé COURT, lui, reste réservé à l'étroit :
     c'est une abréviation de place, pas un nom d'écran. */
  .nav-lien {
    display: inline-flex;
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    gap: var(--espace-2);
    padding: 0;
    font-size: var(--taille-corps);
  }

  /* Plus petite qu'en étroit (22px) : là-bas elle porte l'entrée à elle
     seule, ici elle accompagne un mot entier. */
  .nav-lien-icone {
    width: 18px;
    height: 18px;
  }

  .nav-lien-court {
    display: none;
  }

  .nav-lien-long {
    position: static;
    width: auto;
    height: auto;
    clip-path: none;
  }

  /* La place existe : l'engrenage reprend son libellé. */
  .entete-parametres-nom {
    position: static;
    width: auto;
    height: auto;
    clip-path: none;
  }

  /* Prénom, qualité et bouton d'ajout tiennent sur une rangée. */
  .form-ajout-participant .champ-parametre {
    flex: 1 1 200px;
  }

  /* La bascule des deux vues de la frise. Pas une frise rétrécie d'un côté
     et étirée de l'autre : deux écrans, dont un seul existe à la fois. */
  .frise-edition {
    display: block;
  }

  .frise-consultation {
    display: none;
  }
  .contenu {
    padding: var(--espace-6);
  }

  .grille-foyers {
    grid-template-columns: repeat(2, 1fr);
  }

  .colonne-compte {
    display: table-cell;
  }

  /* Le tableau des soldes a besoin de plus de largeur que les 640px du
     gabarit étroit (formulaires, listes) une fois les 3 colonnes de compte
     visibles — ciblé via :has() pour ne pas élargir les autres écrans. */
  .contenu:has(.table-soldes) {
    max-width: 960px;
  }

  /* La frise a besoin de plus que les 640px du gabarit étroit : vingt et un
     jours au repas près ne se lisent pas dans une colonne de formulaire. */
  .contenu:has(.frise) {
    max-width: 1100px;
  }

  /* La synthèse est LARGE par vocation : une colonne par compte, et elle se
     consulte surtout sur PC. Exception déclarée ici, avec les deux autres —
     jamais une largeur écrite dans un gabarit, sans quoi trois écrans
     décideraient chacun de leur mise en page. */
  .contenu:has(.table-synthese) {
    max-width: 1240px;
  }

  /* --- Matrice (large) ---------------------------------------------------
     La liste redevient un tableau, et le tableau retrouve son sticky en
     croix. Tout ce qui suit ne vaut QUE ≥720px : en dessous il n'y a plus
     de colonnes, donc rien à figer et rien à faire défiler. */

  /* Cet écran est un CADRE : la page ne défile pas, le tableau si. C'est la
     seule disposition qui rende le collage vrai — une hauteur bornée « à la
     louche » laissait le cadre déborder par le bas, et la page se remettait
     à défiler en emportant l'en-tête collé. */
  body:has(.table-synthese) {
    display: flex;
    flex-direction: column;
    /* Hauteur DÉFINIE, et c'est indispensable : sans elle il n'y a pas
       d'espace libre à répartir, `flex: 1` ne contraint plus le cadre, et
       c'est la PAGE qui se remet à défiler (mesuré : cadre de 917px). Pas
       d'`overflow: hidden` pour autant — sur un écran trop court, le cadre
       garde son plancher et la page reprend son défilement plutôt que de
       coincer le tableau dans une fente de 65px. */
    height: 100vh;
    height: 100dvh; /* iOS : `vh` fige la hauteur d'avant la rétraction de la barre d'adresse */
  }

  body:has(.table-synthese) .contenu {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0; /* sans quoi un enfant qui déborde pousse le cadre hors de l'écran */
    /* `width: 100%` n'est pas une redondance : `.contenu` porte `margin: 0
       auto` pour se centrer, et une marge automatique dans l'axe transverse
       ANNULE l'étirement d'un élément flex — il retombe alors sur sa largeur
       de contenu (mesuré : 628px dans un écran de 375). */
    width: 100%;
  }

  .table-defilante {
    /* `position: relative` n'est PAS décoratif : sans ancêtre positionné, le
       débordement du tableau collant remonte jusqu'au document — la page
       gagnait 468px de vide sous la légende, dans lesquels elle défilait
       pour de bon (mesuré en fenêtre réelle, pas seulement en émulation).
       Le cadre devient le bloc conteneur, et le débordement reste dedans. */
    position: relative;
    overflow: auto;
    flex: 1;
    min-height: 12rem;
    /* Le cadre unique revient : en large, les foyers sont des groupes DANS
       un même tableau, pas des cartes séparées — c'est la lecture en colonne
       qui fait l'écran, et cinq cartes la couperaient en cinq. */
    border: var(--bordure);
    border-radius: var(--rayon);
    background: var(--gris-000);
  }

  /* `border-collapse: collapse` FAIT DISPARAÎTRE les bordures des cellules
     collantes (les bords fusionnés appartiennent alors au tableau, qui
     défile, pas à la cellule qui reste). D'où `separate` + `border-spacing:
     0`, et des bordures portées par les cellules elles-mêmes. */
  .table-synthese {
    --h-entete: 34px; /* la hauteur de l'en-tête de colonnes, d'où part celui des foyers */
    display: table;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--taille-corps-sm);
    white-space: nowrap;
  }

  .table-synthese thead {
    display: table-header-group;
  }

  .table-synthese tbody {
    display: table-row-group;
  }

  /* MÊME SPÉCIFICITÉ (0,2,1) que la carte de l'étroit, la requête média
     tranchant par l'ordre : avec un sélecteur plus faible, la carte
     l'emporterait ici et le tbody cesserait d'être un `table-row-group`. */
  .table-synthese tbody.synthese-foyer {
    display: table-row-group;
    margin: 0;
    padding: 0;
    background: none;
    border-radius: 0;
  }

  .table-synthese tbody tr {
    display: table-row;
    padding: 0;
    border: none;
    gap: 0;
  }

  /* RÉGRESSION CORRIGÉE (introduite avec la liste mobile) : la règle
     `.table-synthese tbody tr.synthese-foyer-entete { display: block }`
     compte DEUX classes, celle juste au-dessus une seule — elle l'emportait
     donc jusqu'ici, et la ligne de foyer restait un bloc en large. Sa
     cellule à `colspan` se dimensionnait alors sur son contenu (354px au
     lieu de 1159) et élargissait la colonne des noms d'autant. Invisible
     tant que la bande était grise sur fond blanc ; flagrant dès qu'on la
     teinte. Troisième fois que ce piège de spécificité mord sur cet écran :
     ici on répond avec la MÊME spécificité, la requête média tranchant par
     l'ordre. */
  .table-synthese tbody tr.synthese-foyer-entete {
    display: table-row;
    padding: 0;
  }

  /* AUCUN trait entre les lignes de participants : c'est ce qui donnait
     l'aspect « grille administrative ». Les seules bordures horizontales
     restantes encadrent les bandes de foyer — elles séparent des GROUPES,
     ce qui est une information ; un trait sous chaque personne n'en était
     pas une. */
  .table-synthese th,
  .table-synthese td {
    display: table-cell;
    padding: var(--espace-2) var(--espace-3);
    text-align: left;
    border-bottom: none;
  }

  /* Le sticky est posé sur les CELLULES, jamais sur `thead`/`tr` : WebKit ne
     suit pas sur les conteneurs de lignes — la règle serait ignorée sur
     iPhone et sur personne d'autre, donc invisible en développement. */
  .table-synthese thead th {
    position: sticky;
    top: 0;
    z-index: 3;
    height: var(--h-entete);
    border-bottom: var(--bordure); /* la seule ligne que le retrait ci-dessus ne doit pas emporter */
    background: var(--gris-000); /* opaque, sinon le tableau défile en transparence dessous */
    font-size: var(--taille-petite);
    font-weight: 600;
    color: var(--gris-500);
    text-transform: uppercase;
    letter-spacing: 0.02em;
  }

  /* La colonne des noms : sans elle, défiler vers Alcool laissait des lignes
     d'« Exempté » sans savoir de qui il s'agit. */
  /* La bordure DROITE reste, seule bordure conservée hors des bandes :
     elle n'est pas décorative, c'est la couture de la colonne figée — sans
     elle, les noms se confondent avec les colonnes qui défilent dessous dès
     qu'on rétrécit la fenêtre. */
  .table-synthese th[scope="row"] {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--gris-000);
    border-right: var(--bordure);
  }

  /* Le coin : il croise les deux, il passe donc au-dessus des deux. */
  .table-synthese thead th:first-child {
    left: 0;
    z-index: 4;
    border-right: var(--bordure);
  }

  .table-synthese .col-coef,
  .table-synthese .col-compte {
    text-align: center;
  }

  .col-coef {
    font-size: inherit;
    color: inherit;
    white-space: normal;
  }

  /* Les étiquettes « repas »/« nuitée » appartiennent à la liste : ici c'est
     l'en-tête de colonne qui le dit. */
  .col-coef::before,
  .col-coef + .col-coef::before {
    content: none;
  }

  /* En matrice, toutes les cellules sont montrées — c'est la lecture en
     colonne qui fait l'écran. */
  .table-synthese .col-compte--participe {
    display: table-cell;
  }

  /* En matrice, l'exemption héritée du foyer REVIENT sur chaque ligne : une
     cellule vide dans la colonne « Alcool » se lirait « participe », et la
     lecture en colonne est justement tout l'intérêt de cette mise en page. */
  .table-synthese .col-compte--exempte_foyer {
    display: table-cell;
  }

  .col-compte--sans_objet .cellule-sans-objet {
    display: inline;
  }

  /* Le compte est déjà nommé par l'en-tête de colonne : la mention reste
     LUE (elle sert au lecteur d'écran) mais quitte l'affichage. */
  .mention-compte,
  .mention-sans-objet {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* La bande de foyer : accent DILUÉ et liseré à gauche, IDENTIQUES pour
     tous. C'est de la structure — « ici commence un autre groupe » — pas de
     l'identité : une couleur par foyer reviendrait sur la règle du
     monogramme (le budget couleur est réservé au sens). Le fond reste plus
     pâle que `--accent-surface` pour que la pastille du foyer COURANT, qui
     l'utilise, se détache encore posée dessus. */
  .synthese-foyer-entete th {
    position: sticky;
    top: var(--h-entete); /* juste sous l'en-tête de colonnes, jamais dessus */
    z-index: 2;
    /* Padding SYMÉTRIQUE. Il y avait `padding-top: var(--espace-4)` seul,
       hérité de l'époque où la bande n'était qu'un fond gris et devait
       s'écarter du groupe précédent : 16px en haut contre 8 en bas, le nom
       et sa pastille tombaient visiblement bas dans la bande (mesuré).
       Maintenant qu'elle porte un liseré et une teinte, elle se sépare
       toute seule — la respiration n'a plus à être payée par un décentrage. */
    padding-top: var(--espace-3);
    padding-bottom: var(--espace-3);
    /* Explicites : la règle de l'étroit met le retrait horizontal à zéro (la
       carte le porte), et un raccourci `padding` ne se restaure pas tout
       seul — la bande se collerait au bord gauche du cadre. */
    padding-left: var(--espace-4);
    padding-right: var(--espace-4);
    background: var(--accent-bande); /* opaque : c'est ce qui masque les lignes qui passent dessous */
    border-top: var(--bordure);
    border-bottom: var(--bordure);
    border-left: 3px solid var(--accent);
  }

  /* La première bande touche l'en-tête de colonnes, qui porte déjà sa
     ligne : deux traits collés se verraient. */
  .synthese-foyer:first-of-type .synthese-foyer-entete th {
    border-top: none;
  }

  .synthese-foyer-rangee {
    /* Collant à GAUCHE dans sa cellule : celle-ci fait toute la largeur du
       tableau (colspan), donc sans ça le nom du foyer partirait à droite dès
       qu'on défile vers les comptes — une bande grise anonyme entre deux
       groupes. La cellule colle en haut, son contenu colle à gauche : c'est
       la croix, sur une ligne qui n'a qu'une cellule. */
    position: sticky;
    left: 0;
    width: fit-content;
    max-width: 100%;
  }

  /* En large la rangée tient sur une ligne : les dates reprennent leur place
     à côté du nom plutôt que de forcer un retour. */
  .synthese-foyer-dates {
    flex-basis: auto;
  }

  .legende-synthese .legende-large {
    display: flex;
  }

  /* En matrice, TOUT est indiqué — coche comprise. La phrase de la liste y
     serait fausse, pas seulement inutile. */
  .aide-liste {
    display: none;
  }

  /* La liste garde la même structure en large : une seule mise en page à
     maintenir, elle tient déjà des deux côtés du point de rupture. */
}

/* --- Mise en route : la bannière et l'écran d'avancement ------------------
   NI ORANGE NI ROUGE, et ce n'est pas une préférence : une configuration
   incomplète est un état NORMAL au début du séjour, pas un défaut. Le
   bandeau d'anomalies (--texte-anomalie) dit « un calcul est faux » ; celle-
   ci dit « il reste quelque chose à faire ». Les confondre apprendrait à
   ignorer le premier.

   Deux poids délibérément différents : l'action porte l'accent dilué et un
   liseré, l'information reste sur la bordure neutre des cartes ordinaires. */
.banniere-config {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-3);
  margin-bottom: var(--espace-5);
  padding: var(--espace-3) var(--espace-4);
  border-radius: var(--rayon);
}

.banniere-config--action {
  background: var(--accent-surface);
  border: 1px solid var(--accent);
}

.banniere-config--info {
  background: var(--gris-000);
  border: var(--bordure);
}

/* Le texte pousse le bouton en bout de rangée, et passe seul à la ligne en
   étroit : `flex: 1 1 200px`, jamais une largeur fixe — le nom d'un foyer
   est libre et la phrase de conséquence est longue. */
.banniere-config-texte {
  flex: 1 1 200px;
}

/* Alignée sur le TITRE et non sur le bloc de texte : la sous-ligne passe à
   deux lignes en étroit, et une icône centrée verticalement se retrouve
   alors en face du vide entre les phrases. */
.banniere-config-icone {
  flex: none;
  align-self: flex-start;
  margin-top: 2px;
  color: var(--gris-700);
}

.banniere-config--action .banniere-config-icone {
  color: var(--accent);
}

.banniere-config-titre {
  margin: 0;
  font-size: var(--taille-corps-lg);
  font-weight: 600;
}

/* LA sous-ligne : elle énonce la conséquence. Sans elle, la bannière dit
   qu'il manque quelque chose sans dire ce que ça coûte — or ce que ça coûte
   est un zéro parfaitement crédible dans les comptes. */
.banniere-config-suite {
  margin: var(--espace-1) 0 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.banniere-config-bouton {
  flex: none;
}

.liste-avancement {
  list-style: none;
  margin: 0;
  padding: 0;
  border: var(--bordure);
  border-radius: var(--rayon);
  overflow: hidden;
}

.avancement {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-2) var(--espace-5);
  padding: var(--espace-3) var(--espace-4);
  border-bottom: var(--bordure);
}

.avancement:last-child {
  border-bottom: none;
}

/* ATTÉNUER CE QUI EST NORMAL, LAISSER RESSORTIR CE QUI DIFFÈRE — même
   principe que l'écran Participants. Un foyer complet est le cas attendu :
   son état part en gris clair. Le manque, lui, reste en pleine encre. Pas
   d'aplat teinté sur la rangée : le budget couleur est réservé au sens
   (sélection, créancier/débiteur), et « pas encore rempli » n'est pas un
   avertissement. */
.avancement--complet .avancement-etat {
  color: var(--gris-500);
}

.avancement--incomplet .avancement-etat {
  color: var(--gris-900);
  font-weight: 600;
}

/* Le nom prend la place restante ; l'état et l'action se rangent à droite et
   passent à la ligne ensemble en étroit. Une rangée flex sans `flex-wrap`
   qui mêle un élément souple et des éléments à largeur fixe écrase le
   souple au lieu de se replier (défaut vécu sur .carte-foyer-entete). */
.avancement-foyer {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  flex: 1 1 160px;
  font-weight: 600;
}

/* Même traitement que les noms de l'écran Participants : un nom de foyer
   reste du TEXTE, souligné au survol seulement. Le bleu natif d'un <a>
   introduirait une couleur que la charte n'a pas. */
.avancement-foyer a {
  color: inherit;
  text-decoration: none;
}

.avancement-foyer a:hover {
  text-decoration: underline;
}

.avancement-etat {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

/* En texte AUSSI, jamais en couleur seule : « Complet » se lit en noir et
   blanc, la coche ne fait que le confirmer. */
.avancement-coche {
  color: var(--texte-creancier);
}

.avancement-action {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  min-height: var(--cible-min);
}

.avancement-fini {
  margin-top: var(--espace-4);
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

/* En étroit, le nom prend TOUTE la rangée et l'état passe dessous avec son
   action : sinon la mise en page dépend de la longueur du libellé d'état
   (« Aucun membre » tient à côté du nom, « Aucune date de présence » non),
   et deux foyers voisins ne se lisent pas de la même façon. */
@media (max-width: 719px) {
  .avancement-foyer {
    flex: 1 1 100%;
  }

  .avancement-action {
    margin-left: auto;
  }
}

/* --- Le parcours organisateur et l'écran d'invitation --------------------- */

.banniere-config-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-3);
}

.etapes-parcours {
  list-style: none;
  margin: 0 0 var(--espace-6);
  padding: 0;
}

.etape {
  display: flex;
  align-items: flex-start;
  gap: var(--espace-3);
  padding: var(--espace-3) 0;
  border-bottom: var(--bordure);
}

.etape:last-child {
  border-bottom: none;
}

/* Le numéro porte l'ordre, donc le sens de lecture — pas une décoration.
   `flex: none` : sans lui, un cercle se déforme en ovale dès que le résumé
   pousse la rangée. */
.etape-numero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border: var(--bordure);
  border-radius: 50%;
  font-size: var(--taille-corps-sm);
  font-weight: 600;
  color: var(--gris-700);
}

.etape--faite .etape-numero {
  background: var(--accent-surface);
  border-color: var(--accent);
  color: var(--accent);
}

.etape-corps {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 160px;
}

.etape-titre {
  font-weight: 600;
  color: var(--gris-900);
  text-decoration: none;
}

.etape-titre:hover {
  text-decoration: underline;
}

.etape-resume {
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.etape-etat {
  display: inline-flex;
  align-items: center;
  gap: var(--espace-1);
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.etape-coche {
  color: var(--texte-creancier);
}

.titre-section {
  margin: 0 0 var(--espace-3);
  font-size: var(--taille-corps-lg);
}

/* Le fil du parcours, en bas d'écran : il conclut, il ne surplombe pas.
   Un bandeau en haut concurrencerait la bannière de mise en route et
   repousserait le contenu — or ce contenu est précisément ce qu'on est venu
   remplir. */
.fil-parcours {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-3);
  margin-top: var(--espace-6);
  padding-top: var(--espace-4);
  border-top: var(--bordure);
}

.fil-parcours-etape {
  flex: 1 1 160px;
  margin: 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.fil-parcours-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-3);
}

.bloc-lien {
  margin-bottom: var(--espace-5);
  padding: var(--espace-4);
  background: var(--gris-000);
  border: var(--bordure);
  border-radius: var(--rayon);
}

/* Monospace : ce qu'on vérifie ici n'est PAS le jeton — quarante-trois
   caractères aléatoires ne se relisent pas — c'est l'HÔTE, au début, qui dit
   si le lien marchera depuis un autre appareil (`192.168.1.56` et non
   `localhost`). Le jeton peut donc dépasser à droite sans que ça coûte
   quoi que ce soit : personne ne le copie à la main, c'est tout le propos
   des deux boutons. */
.champ-lien {
  width: 100%;
  margin: var(--espace-2) 0 var(--espace-3);
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-050);
  border: var(--bordure);
  border-radius: var(--rayon);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.actions-lien {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espace-3);
}

/* Le retour de copie : `hidden` tant que rien n'a été tenté, jamais une
   ligne vide qui réserverait sa place et ferait sauter le bloc. */
.retour-copie {
  margin: var(--espace-3) 0 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.avertissement-lien {
  display: flex;
  align-items: flex-start;
  gap: var(--espace-2);
  margin: 0 0 var(--espace-5);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.rappel-avancement {
  padding: var(--espace-3) var(--espace-4);
  background: var(--gris-050);
  border-radius: var(--rayon);
}

.rappel-avancement-titre {
  margin: 0;
}

.rappel-avancement-suite {
  margin: var(--espace-1) 0 var(--espace-2);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

/* Le RÉCAP DE FIN DE SÉJOUR — ce que chacun emporte
   ------------------------------------------------
   MISE EN PAGE : direction B de `recap-maquettes.html` (« la carte »). Les
   VALEURS — tailles, graisses, espacements, rayons — sont celles de la
   maquette et vivent donc ici en dur plutôt qu'en jetons : cet écran a une
   typographie de document, pas celle des écrans de saisie. Les COULEURS, à
   l'inverse, restent les jetons de l'app.

   Une page, deux sorties : l'écran et le papier partagent le même HTML,
   `@media print` retire ce qui n'a rien à faire sur une feuille. */

/* Une teinte plus sombre que l'accent, pour le dégradé du bandeau et pour le
   texte posé sur les fonds verts pâles. Dérivée, jamais un second hex : le
   garde-fou veut que `--accent` n'apparaisse qu'une fois. */
:root {
  --accent-encre: color-mix(in srgb, var(--accent) 74%, black);
}

.recap,
.recap-hors-impression {
  display: grid;
  gap: 14px;
}

.recap {
  margin-bottom: 14px;
}

/* LES TITRES SONT DES ÉTIQUETTES, pas du gras. C'est ce qui fait la
   différence entre « un mot en gras » et une hiérarchie : à 11px en
   capitales interlettrées, le titre cesse de concurrencer les montants. */
.recap-etiquette {
  margin: 0 0 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gris-500);
}

/* LE BANDEAU. Dégradé du plus sombre au plus clair : un aplat plat paraît
   imprimé, le dégradé donne la profondeur d'une carte. */
.recap-bandeau {
  padding: 26px 24px 22px;
  background: linear-gradient(180deg, var(--accent-encre), var(--accent));
  color: var(--accent-texte);
  border-radius: 14px;
}

/* LE SEUL ÉTAT QUI QUITTE LE VERT : plus rien n'attend personne, le document
   cesse d'appeler une action et devient une archive. Le vert est réservé à
   « il y a quelque chose à faire » — le garder ici ferait de lui une simple
   couleur de marque, et on n'y prêterait plus attention là où il compte.

   Un gris VERDI (`color-mix` avec l'accent), pas un gris neutre : le bandeau
   reste de la même famille, il change seulement de température. Dérivé, comme
   `--accent-encre`, pour que le hex d'accent continue de n'apparaître qu'une
   fois dans ce fichier. */
.recap-bandeau[data-etat="sejour-solde"] {
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--accent) 16%, var(--gris-900)),
    color-mix(in srgb, var(--accent) 16%, var(--gris-700))
  );
}

.recap-bandeau-sejour {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.recap-bandeau-contexte {
  margin: 3px 0 0;
  font-size: 12.5px;
  /* Sur un aplat, la hiérarchie secondaire se fait par l'OPACITÉ, jamais par
     un second gris : un gris de la palette poserait un texte terne sur fond
     coloré. */
  opacity: 0.72;
}

.recap-bandeau-verdict {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid rgb(255 255 255 / 0.22);
}

.recap-bandeau-qui {
  margin: 0;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  color: rgb(255 255 255 / 0.6);
}

/* LE MONTANT, ce que l'œil doit rencontrer en premier. */
.recap-verdict {
  margin: 7px 0 0;
  font-size: 34px;
  font-weight: 650;
  letter-spacing: -0.025em;
  line-height: 1.05;
}

/* UN MOT, PAS UN MONTANT — « Tu es à jour », « Séjour soldé ». Plus petit
   que le nombre qu'il remplace : à 34px une bonne nouvelle se met à crier,
   alors qu'un montant à payer, lui, doit s'imposer. La taille dit l'urgence,
   pas l'importance. */
.recap-verdict--mot {
  font-size: 26px;
}

.recap-verdict-a-qui {
  margin: 5px 0 0;
  font-size: 14px;
  opacity: 0.85;
}

/* LE RAPPEL DE CE QUI EST DÉJÀ RÉGLÉ. Une pastille posée sur l'aplat plutôt
   qu'une ligne de texte de plus : elle se distingue du verdict au premier
   coup d'œil, ce qui évite de lire deux montants comme un seul calcul. Elle
   prend la place de la décomposition, jamais en plus (voir recap.py). */
.recap-jalon {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 14px 0 0;
  padding: 5px 13px;
  background: rgb(255 255 255 / 0.14);
  border-radius: 999px;
  font-size: 12.5px;
}

.recap-jalon svg {
  width: 15px;
  height: 15px;
  flex: none;
}

/* LA DÉCOMPOSITION EN DERNIER, et c'est un choix : la vérification vient
   APRÈS la réponse. Deux blocs côte à côte plutôt qu'une phrase à puce —
   ce sont deux grandeurs, pas une énumération. */
.recap-bandeau-calcul {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin: 16px 0 0;
  font-size: 12.5px;
  opacity: 0.8;
}

/* LES CARTES N'ONT PAS DE BORDURE : le blanc sur le fond neutre suffit à les
   détacher, et une bordure grise alourdit un document qu'on veut calme. */
.recap-section {
  padding: 22px;
  background: var(--gris-000);
  border-radius: 14px;
}

.recap-table {
  width: 100%;
  border-collapse: collapse;
}

.recap-table td {
  padding: 9px 0;
  border-bottom: 1px solid var(--gris-300);
  font-size: 14px;
}

.recap-table tbody tr:last-child td {
  border-bottom: none;
}

.recap-table tfoot td {
  padding-top: 11px;
  border-top: 1.5px solid var(--gris-900);
  border-bottom: none;
  font-weight: 650;
}

.recap-montant {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.recap-etat {
  text-align: right;
  white-space: nowrap;
}

/* UNE PASTILLE PLEINE, pas un texte gris aligné : l'état est une donnée, pas
   une annotation. Le MOT la porte — elle reste donc lisible imprimée en
   gris. */
.etat-pastille {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--gris-100);
  color: var(--gris-700);
  font-size: 11px;
  white-space: nowrap;
}

.etat-pastille--fait {
  background: var(--accent-surface);
  color: var(--accent-encre);
}

/* MA LIGNE : c'est celle qu'on cherche dans le plan. */
.recap-ligne-mienne td {
  font-weight: 600;
  background: var(--accent-surface);
}

/* CE QUI EST FAIT NE DISPARAÎT PAS, IL S'ESTOMPE. Une ligne réglée n'appelle
   plus rien : elle passe au second plan pour que les virements qui restent
   ressortent. Elle reste parfaitement lisible — c'est elle qu'on vient
   rechercher dans un mois (« je t'ai payé, non ? »), et l'effacer serait
   perdre la seule preuve qui survivra à l'extinction du serveur.

   DÉCLARÉE APRÈS `.recap-ligne-mienne` et à SPÉCIFICITÉ SUPÉRIEURE (0,2,1
   contre 0,1,1) : ma ligne perd son surlignage dès qu'elle est faite, le
   surlignage servant à retrouver ce qu'il me RESTE à faire. Ce piège d'ordre
   a déjà mordu quatre fois dans ce fichier — ici la spécificité tranche, pas
   seulement la position. */
.recap-table .recap-ligne-faite td {
  background: none;
  font-weight: 500;
  opacity: 0.55;
}

.recap-avertissement {
  margin: 0 0 14px;
  font-size: 12px;
  line-height: 1.55;
  color: var(--gris-700);
}

/* TROIS COLONNES ÉGALES, séparées par un FILET obtenu au `gap` : le fond de
   la grille transparaît entre les cellules blanches. Pas des bordures — elles
   doubleraient aux jonctions et saliraient les coins arrondis. */
.recap-indicateurs {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2px;
  background: var(--gris-300);
  border-radius: 10px;
  overflow: hidden;
}

.recap-indicateurs > div {
  padding: 16px 14px;
  background: var(--gris-000);
}

/* AUTANT DE COLONNES QU'IL Y A D'INDICATEURS, dès que la place existe. Elles
   étaient TROIS en dur ; le bloc n'en compte plus que deux depuis que « par
   repas adulte » a disparu, et la troisième colonne laissait voir le fond de
   filet — une bande grise à la place d'un chiffre. `auto-fit` les compte, et
   ne se retrouvera pas fausse au prochain indicateur ajouté ou retiré.

   La requête média vit ICI, collée à
   la règle qu'elle surcharge, et non dans le bloc `@media` global plus haut
   dans le fichier.

   PIÈGE VÉCU, ET C'EST LA QUATRIÈME FOIS SUR CE PROJET : à spécificité égale,
   c'est l'ORDRE qui tranche. La règle à trois colonnes était déclarée au
   `@media` global (ligne ~3750), la base du composant six cents lignes plus
   bas — la base gagnait, et l'écran large restait empilé alors que le CSS
   « contenait » la bonne règle. Garder les deux côte à côte est le seul
   montage qui ne puisse pas se retourner quand le fichier grandit. */
@media (min-width: 720px) {
  .recap-indicateurs {
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
    grid-auto-flow: column;
  }
}

.recap-indicateur-valeur {
  margin: 0;
  font-size: 24px;
  font-weight: 650;
  letter-spacing: -0.025em;
  line-height: 1.1;
  color: var(--accent-encre);
}

.recap-indicateur-libelle {
  margin: 3px 0 0;
  font-size: 12.5px;
}

/* « dont X de courses » : une PART du montant au-dessus, pas un second
   indicateur. Plus petite que la valeur — sans quoi on la comparerait — mais
   pleine encre, à la différence de la note : c'est un chiffre, pas une
   glose. */
.recap-indicateur-detail {
  margin: 4px 0 0;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--gris-700);
}

.recap-indicateur-note {
  margin: 6px 0 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--gris-500);
}

/* LA PREUVE : la seule note rassurante du document, et la seule à porter une
   teinte verte. Ce n'est pas une carte blanche — c'est ce qui la distingue au
   premier regard. */
.recap-preuve {
  margin: 0;
  padding: 18px 20px;
  background: var(--accent-surface);
  border-radius: 14px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--accent-encre);
}

/* UN BLOC DE CITATION à fond NEUTRE : on ne modifie pas ce texte, on le
   PREND. Il reste un `<textarea>` — c'est lui que la sélection et
   `execCommand` copient, et un élément non rendu n'est pas sélectionnable —
   mais il en a l'allure. */
.recap-message {
  display: block;
  width: 100%;
  padding: 14px 16px;
  background: var(--surface-a-copier);
  border: none;
  border-radius: 10px;
  font: inherit;
  font-size: 13px;
  line-height: 1.7;
  color: var(--gris-700);
  resize: vertical;
}

.recap-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 14px;
}

.recap-retour-copie {
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--accent);
}

/* LE RETOUR VERS LE PLAN. Hors carte et discret : c'est une porte, pas une
   section du document. Sa classe est à elle plutôt que `recap-hors-impression`
   — cette dernière porte une grille, qui ferait d'un lien unique un élément
   étiré sur toute la largeur (jumeau du piège du `<p>` en flex). */
.recap-vers-plan {
  margin: 0 0 14px;
  padding: 0 4px;
}

/* Hors carte, comme la signature d'un document. */
.recap-edition {
  margin: 0;
  padding: 0 4px;
  font-size: 11px;
  color: var(--gris-500);
}

/* LA BANDE DE FIN DE SÉJOUR (écran d'accueil). Même vocabulaire que la
   bannière d'action de la mise en route — surface d'accent diluée et liseré
   — parce que c'est la même nature : quelque chose qui m'attend et que moi
   seul peux faire. Un second dialecte pour la même intention obligerait à
   deviner si la différence veut dire quelque chose.

   Ni orange ni rouge : un séjour qui se termine est l'issue NORMALE, pas un
   défaut. Le bandeau d'anomalies garde le rouge, et reste au-dessus. */
.bande-fin-sejour {
  display: flex;
  align-items: center;
  gap: var(--espace-3);
  margin-bottom: var(--espace-4);
  padding: var(--espace-3) var(--espace-4);
  background: var(--accent-surface);
  border-left: 3px solid var(--accent);
  border-radius: var(--rayon);
  color: inherit;
  text-decoration: none;
}

.bande-fin-sejour-texte {
  flex: 1;
  min-width: 0;
  font-size: var(--taille-corps);
  line-height: 1.5;
}

/* Le second membre passe à la ligne en étroit : « Le séjour est terminé. »
   doit rester lisible d'un bloc, jamais coupé au milieu. */
.bande-fin-sejour-texte span {
  display: block;
  color: var(--gris-700);
  font-size: var(--taille-corps-sm);
}

.bande-fin-sejour .icone-chevron {
  flex: none;
  color: var(--accent);
}

/* LA FEUILLE IMPRIMÉE. Peu de gens impriment, mais ceux qui le font gardent
   le seul exemplaire papier : elle doit rester correcte. */
@media print {
  @page {
    margin: 14mm;
  }

  .entete,
  .recap-hors-impression,
  .recap-vers-plan,
  .bande-fin-sejour,
  .banniere-configuration,
  .fil-parcours {
    display: none !important;
  }

  body {
    background: var(--gris-000);
    /* L'écran des présences borne le corps à 100dvh pour son cadre défilant ;
       sur papier, une hauteur d'écran tronquerait le document. */
    height: auto;
    font-size: 11pt;
  }

  .contenu {
    max-width: none;
    padding: 0;
  }

  .recap-section,
  .recap-bandeau,
  .recap-preuve,
  .recap-indicateurs > div {
    break-inside: avoid;
  }

  /* LE BANDEAU EN APLAT DEVIENDRAIT UN PAVÉ D'ENCRE. Beaucoup d'imprimantes
     ignorent les fonds, et celles qui les impriment noieraient le texte clair
     dans du vert foncé — le montant, justement, deviendrait le MOINS lisible
     du document. Sur papier il redevient du texte noir tenu par un cadre. */
  /* L'ÉTAT « soldé » DOIT ÊTRE NOMMÉ ICI AUSSI. `[data-etat]` vaut 0,2,0
     contre 0,1,0 pour `.recap-bandeau` : sans cette seconde moitié du
     sélecteur, le dégradé gris l'emporterait sur papier alors même que la
     règle « pas d'aplat à l'impression » est écrite juste au-dessus. Piège
     d'ordre et de spécificité, cinquième occurrence dans ce fichier. */
  .recap-bandeau,
  .recap-bandeau[data-etat] {
    background: none;
    color: var(--gris-900);
    border: 2px solid var(--gris-900);
    padding: 18px 20px;
  }

  .recap-bandeau-contexte,
  .recap-bandeau-qui,
  .recap-verdict-a-qui,
  .recap-bandeau-calcul {
    opacity: 1;
  }

  /* Le rappel est un aplat blanc à 14 % : invisible sur du papier blanc. Il
     redevient un contour, comme la pastille d'état des règlements. */
  .recap-jalon {
    background: none;
    border: 1px solid var(--gris-500);
  }

  /* SUR PAPIER, RIEN NE S'ESTOMPE. L'estompage sert à faire ressortir ce
     qu'il RESTE à faire — une affordance d'écran. Un document archivé, lui,
     n'appelle plus aucune action : toutes ses lignes ont la même valeur de
     preuve, et un gris pâle passé à l'imprimante ne se relit pas. La
     pastille « fait le 6 août » porte l'état, elle suffit. */
  .recap-table .recap-ligne-faite td {
    opacity: 1;
  }

  .recap-bandeau-qui {
    color: var(--gris-500);
  }

  .recap-bandeau-verdict {
    border-top-color: var(--gris-300);
  }

  /* Les cartes n'ont plus lieu d'être sur une feuille : un trait sépare aussi
     bien, et six rectangles imprimés font un formulaire administratif. */
  .recap-section {
    padding: 0 0 12px;
    background: none;
    border-radius: 0;
    border-bottom: 1px solid var(--gris-300);
  }

  .recap-indicateurs {
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
    grid-auto-flow: column;
    background: none;
    border: 1px solid var(--gris-300);
    border-radius: 0;
    gap: 0;
  }

  .recap-indicateurs > div {
    border-right: 1px solid var(--gris-300);
  }

  .recap-indicateurs > div:last-child {
    border-right: none;
  }

  .recap-indicateur-valeur {
    color: var(--gris-900);
  }

  /* Les aplats pâles ne survivent pas au gris : la teinte part, le texte
     reste. On les rend donc au trait. */
  .recap-preuve {
    background: none;
    color: var(--gris-900);
    padding: 12px 0 0;
    border-radius: 0;
  }

  .recap-ligne-mienne td {
    background: none;
  }

  .etat-pastille {
    background: none;
    border: 1px solid var(--gris-500);
  }

  a[href]::after {
    content: ""; /* pas d'URL entre parenthèses : ce n'est pas un article */
  }
}

/* LA CLÔTURE, au bas de l'écran Règlement. Encadrée parce qu'elle change de
   registre : au-dessus on règle au fil de l'eau, ici on ferme le séjour. */
.clore-sejour {
  margin-top: var(--espace-6);
  padding-top: var(--espace-5);
  border-top: var(--bordure);
}

.clore-sejour h2 {
  margin: 0 0 var(--espace-2);
}

.clore-sejour-phrase {
  margin: 0 0 var(--espace-3);
  color: var(--gris-700);
}

/* LE MASQUAGE : ce qui est scellé se voit, et se voit COMME scellé.
   ------------------------------------------------------------------
   On cache le contenu, jamais l'existence — donc rien n'est retiré de
   l'écran, tout est mis en retrait. Un tiret plutôt qu'un zéro : zéro serait
   un chiffre FAUX, le tiret est une absence assumée. */
.carte-compte-total--scelle,
.ligne-depense-montant--scelle {
  color: var(--gris-500);
  font-weight: 500;
}

/* Une carte scellée n'est pas une carte ordinaire : elle est en POINTILLÉS,
   sans fond. La forme dit « il y a quelque chose ici que tu ne vois pas »
   avant même qu'on lise la phrase. */
.ligne-depense--scellee {
  background: none;
  border: 1px dashed var(--gris-300);
}

.ligne-depense-scelle-phrase {
  grid-column: 1 / -1;
  margin: var(--espace-1) 0 0;
  font-size: var(--taille-petite);
  color: var(--gris-500);
}

/* ON NE RETIRE JAMAIS EN SILENCE. La phrase qui explique la disparition des
   colonnes est du même registre que la bande de mise en route : une
   information, pas un avertissement — d'où le gris et non l'orange. Une
   configuration transitoire n'est pas un défaut. */
.detail-masque {
  margin: var(--espace-3) 0 0;
  padding: var(--espace-2) var(--espace-3);
  background: var(--gris-050);
  border-radius: var(--rayon);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
  line-height: 1.5;
}

.recap-montant--scelle {
  color: var(--gris-500);
  font-weight: 400;
}

/* ══ LE BLOC CADEAUX ═══════════════════════════════════════════════════════
   IL N'A PAS DE CONTENEUR À LUI. Montant, Libellé, Date, Compte et Payé par
   sont à plat sur le fond de page ; la première version mettait « Pour qui »
   et « Qui offre » dans des cartes blanches, et deux grammaires de conteneur
   sur un même écran font lire la seconde comme rapportée. Ici : `fieldset`
   nu, `legend.etiquette`, `.rangee-pastilles` — le vocabulaire de « Payé
   par », dix centimètres plus haut. */

.champ-destinataires,
.champ-offrants {
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
  border: 0;
  padding: 0;
  margin: 0;
}

/* Un `legend` dans un `fieldset` en flex en DEVIENT un élément et disloque
   l'alignement — piège déjà payé sur la frise (`<legend>`) et sur un `<p>`
   en flex avant elle. Il reste flottant, comme sur les autres champs. */
.champ-destinataires > legend,
.champ-offrants > legend {
  float: left;
  width: 100%;
}

.aide-champ {
  margin: 0;
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

/* ── « Pour qui » : les pastilles de « Payé par », en interrupteurs ─────── */

/* L'ICÔNE NE PARAÎT QUE SUR UNE PASTILLE MARQUÉE. Réservée dans le flux, elle
   ferait cinq cadeaux gris alignés — exactement le papier peint qu'on retire. */
.pastille-destinataire .icone-cadeau {
  display: none;
  width: 16px;
  height: 16px;
  color: var(--accent);
}

.pastille-destinataire input:checked + .pastille-option-contenu .icone-cadeau {
  display: block;
}

.pastille-destinataire input:checked + .pastille-option-contenu {
  font-weight: 600;
}

/* LE SOUS-CAS RARE, révélé seulement quand son foyer est désigné. */
.precision-destinataire[hidden] {
  display: none;
}

.precision-destinataire-rangee {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espace-2);
  min-height: var(--cible-min);
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

/* UN `select` NATIF NU N'EXISTE NULLE PART AILLEURS dans l'app : il prend la
   bordure, le rayon et la hauteur de cible des autres champs. */
.champ-select {
  min-height: var(--cible-min);
  padding: 0 var(--espace-2);
  border: var(--bordure);
  border-radius: var(--rayon-sm);
  background: var(--gris-000);
  font: inherit;
  color: inherit;
}

.champ-select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── Les deux modes : le segmenté du compte, sans rien de neuf ──────────── */

.segmente-modes {
  margin: 0;
}

/* ── LE VERDICT ────────────────────────────────────────────────────────────
   C'est lui qui dit si l'enregistrement passera. Il était en petit, en
   gris-vert, en bas à gauche — la taille d'une note de bas de page pour le
   seul contrôle qui bloque. Il porte donc le poids d'un montant, et il DIT
   ce qui manque : l'absence d'un message positif ne se remarque pas. */
.verdict-cadeau {
  margin: var(--espace-2) 0 0;
}

.verdict-cadeau,
.part-egale {
  margin-bottom: 0;
  padding: var(--espace-2) var(--espace-3);
  border-radius: var(--rayon);
  font-size: var(--taille-corps);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.verdict-cadeau[hidden],
.part-egale[hidden] {
  display: none;
}

/* Écart : couleur d'anomalie. Le calcul n'est pas faux, mais l'enregistrement
   sera REFUSÉ — c'est un obstacle, pas un état normal en cours de saisie. */
.verdict-cadeau--ecart {
  background: var(--gris-050);
  color: var(--texte-anomalie);
}

.verdict-cadeau--juste {
  background: var(--accent-surface);
  color: var(--accent-encre);
}

/* La ligne de parts égales n'est pas un verdict : elle DÉCRIT ce qui va se
   passer. Surface neutre, encre ordinaire. */
.part-egale {
  background: var(--gris-050);
  color: var(--gris-700);
}

/* ── Le roster, replié en parts égales ─────────────────────────────────── */

.champ-offrants-liste {
  border: var(--bordure);
  border-radius: var(--rayon);
  background: var(--gris-000);
}

/* Un `summary` en flex cesse, dans WebKit, d'être la commande d'ouverture de
   son `details` : la mise en page vit dans un enfant. */
.offrants-resume {
  display: block;
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-3);
  cursor: pointer;
  list-style: none;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.offrants-resume::-webkit-details-marker {
  display: none;
}

.offrants-resume-rangee {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-3);
  min-height: var(--cible-min);
}

.champ-offrants-liste[open] .offrants-resume .concernes-chevron {
  transform: rotate(45deg);
}

.offrants-corps {
  border-top: var(--bordure);
  /* La gouttière des autres champs. Le bord du champ touchait presque le €,
     qui touchait presque le bord du cadre. */
  padding: 0 var(--espace-4);
}

.cadeau-offrant {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-3);
  min-height: var(--cible-min);
}

/* UNE SEULE STRUCTURE À LA FOIS. En parts égales les rangées n'ont aucun
   cadre : le filet les sépare. En montants libres chaque champ porte le sien,
   et le filet en plus faisait une grille de doubles traits — le langage de
   tableur que l'app a justement abandonné ailleurs. */
.cadeau-offrant ~ .cadeau-offrant {
  border-top: var(--bordure);
}

.champ-offrants:has([data-mode][value="libres"]:checked) .cadeau-offrant ~ .cadeau-offrant {
  border-top: 0;
}

.champ-offrants:has([data-mode][value="libres"]:checked) .offrants-corps {
  display: flex;
  flex-direction: column;
  gap: var(--espace-2);
  padding-top: var(--espace-3);
  padding-bottom: var(--espace-3);
}

/* DEUX TITRES POUR UN NIVEAU : « QUI OFFRE » puis « Qui offre combien » trois
   éléments plus bas. Le second part en montants libres — la liste y est le
   seul endroit où saisir, donc rien à replier, et le serveur rend `open`.
   En parts égales il reste et dit autre chose : « Qui participe ».

   La règle de base de `.offrants-resume` est déclarée PLUS HAUT, et doit le
   rester : `test_..._n_est_jamais_un_conteneur_flex` lit la PREMIÈRE
   occurrence du sélecteur dans le fichier. */
.champ-offrants:has([data-mode][value="libres"]:checked) .offrants-resume {
  display: none;
}

.champ-offrants:has([data-mode][value="libres"]:checked) .offrants-corps {
  border-top: 0;
}

.cadeau-offrant-foyer {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  min-width: 0;
}

.cadeau-offrant-nom {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* LE DESTINATAIRE NE CONTRIBUE PAS À SON PROPRE CADEAU. Atténué plutôt que
   retiré : le voir grisé DIT pourquoi il ne paie pas, là où son absence
   laisserait chercher. Le serveur le retire de toute façon — ceci n'est
   qu'un retour visuel, la justesse ne dépend jamais du CSS. */
.cadeau-offrant--exclu {
  opacity: 0.4;
}

.cadeau-offrant-part,
.cadeau-offrant-montant {
  display: flex;
  align-items: center;
  gap: var(--espace-2);
  flex: 0 0 auto;
  min-height: var(--cible-min);
  cursor: pointer;
  font-size: var(--taille-corps-sm);
  color: var(--gris-500);
}

.cadeau-offrant-part[hidden],
.cadeau-offrant-montant[hidden] {
  display: none;
}

/* IL REPREND `.montant-saisie` DANS LE GABARIT, et ne surcharge ici que ce
   qui le distingue du grand champ du haut : la taille. Le rayon, la bordure,
   le `:focus-within` accent et le € À L'INTÉRIEUR viennent de là — une seule
   façon de saisir un montant dans l'app. */
.cadeau-offrant-montant {
  flex: 0 0 auto;
  gap: var(--espace-1);
  padding: var(--espace-1) var(--espace-3);
}

.cadeau-offrant-montant input {
  width: 5.5rem;
  max-width: none;
  font-size: var(--taille-corps);
  font-weight: 600;
}

.cadeau-offrant-montant .montant-devise {
  font-size: var(--taille-corps);
}

/* ── Masquer aussi à… (replié : le cas rare) ────────────────────────────── */

.champ-masque-aussi {
  border: var(--bordure);
  border-radius: var(--rayon);
  background: var(--gris-000);
}

.cadeau-resume {
  display: block;
  min-height: var(--cible-min);
  padding: var(--espace-2) var(--espace-3);
  cursor: pointer;
  list-style: none;
  font-size: var(--taille-corps-sm);
  color: var(--gris-700);
}

.cadeau-resume::-webkit-details-marker {
  display: none;
}

.cadeau-resume-rangee {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espace-3);
  min-height: var(--cible-min);
}

.champ-masque-aussi[open] .cadeau-resume .concernes-chevron {
  transform: rotate(45deg);
}

.rangee-pastilles--repliee {
  padding: 0 var(--espace-3) var(--espace-3);
}

/* ── Le secret levé ─────────────────────────────────────────────────────── */

/* LA CASE DISPARAÎT DÈS QU'ELLE N'A PLUS DE SUJET, en direct : décocher le
   dernier « masqué aussi » ne laisse rien à lever. Le serveur ne la rend déjà
   qu'en présence d'un masque de précaution ; ceci couvre la seconde moitié,
   celle qui se produit pendant l'édition. Apparence seulement — le témoin
   `cadeau_offert_rendu` reste soumis, et la justesse reste serveur (même
   motif que l'enfant atténué sur un compte réservé aux adultes). */
.formulaire-depense:not(:has(input[name="masque_aussi"]:checked)) .cadeau-offert {
  display: none;
}

.cadeau-offert {
  display: flex;
  align-items: flex-start;
  gap: var(--espace-3);
  padding: var(--espace-3);
  background: var(--gris-050);
  border-radius: var(--rayon);
  cursor: pointer;
  font-size: var(--taille-corps-sm);
}

.cadeau-offert small {
  display: block;
  margin-top: 2px;
  color: var(--gris-500);
}
