/* Cousinage — feuille de style de base.
 *
 * Direction artistique, plan A.6. Le registre est « calme et dense », jamais
 * « minimal et géant ».
 *
 * Trois interdits, appuyés sur un précédent récent : pas de verre dépoli, pas
 * de transparence, pas de flou. Apple a introduit Liquid Glass avec iOS 26 puis
 * a dû livrer, dès la bêta 4 d'iOS 26.1, un réglage « Tinted » pour l'atténuer
 * après des semaines de critiques d'accessibilité — texte sous 4,5:1 sur fonds
 * complexes, lisibilité dégradée en extérieur, fatigue en lecture prolongée.
 * Sur un public de 15 à 92 ans, c'est disqualifiant.
 *
 * La retenue ne se joue donc pas sur la taille du texte — le plancher de 18 px
 * est non négociable, 55 % des 60-74 ans étant en illectronisme ou en
 * compétences faibles — mais sur le chrome retiré : pas de carte autour de
 * chaque élément, pas d'ombre, des filets d'un pixel plutôt que des boîtes.
 */

:root {
  /* Le papier, pas l'écran : jamais de blanc pur. */
  --papier:      #FBF9F4;
  --surface:     #FFFFFF;
  --encre:       #1A1814;
  --encre-douce: #5C574E;
  --filet:       #E3DED2;
  --filet-clair: #F0ECE3;
  --lien:        #2C4A6E;
  /* ------------------------------------------------------------------
     UN GESTE N'EST PAS UN LIEN
     ------------------------------------------------------------------
     Le bouton principal prenait `--lien` comme fond. En thème sombre `--lien`
     doit être CLAIR pour se lire sur du presque noir — un bleu pâle — et le
     bouton devenait alors un pavé pastel avec du texte foncé dessus : l'objet
     le plus lumineux de l'écran, pour « Écrire ». Criard, et faux : la couleur
     d'un lien répond à « où cela mène », celle d'un geste à « que fait-on ».

     Deux jetons distincts, donc, et le geste garde le MÊME bleu profond dans
     les deux thèmes — seulement remonté d'un cran en sombre pour rester
     lisible. Un bouton doit avoir la même présence de jour comme de nuit. */
  --geste:       #2C4A6E;
  --geste-encre: #FBF9F4;
  --alerte:      #8E3129;
  --succes:      #2F5D45;

  /* ------------------------------------------------------------------
     CINQ CRANS, EN REM — ET LE RÉGLAGE DE CONFORT FAISAIT L'INVERSE
     ------------------------------------------------------------------
     Aucune typographie d'apparat : pas de titre à 34 px, pas de nombre géant,
     pas de bandeau pleine largeur.

     Ils étaient EN PIXELS, et c'est ce qui rendait le réglage « Confort de
     lecture » inopérant. `html[data-taille="grand"] { font-size: 117% }` agit
     sur la racine, donc sur les `rem` — donc sur `.page { max-width: 40rem }`
     et sur tous les rembourrages, mais PAS sur du texte codé en pixels. Le
     réglage élargissait les conteneurs sans grossir une seule lettre : la
     mesure passait de 72 à 84 signes, c'est-à-dire l'exact contraire de ce
     qu'il promet — sur le public que le projet vise nommément, dont 55 % des
     60-74 ans sont en illectronisme ou en compétences faibles.

     Les valeurs sont identiques à 16 px de racine : rien ne bouge au cran
     « normale », et le réglage prend enfin effet aux deux autres.

     `--t-sous-titre` remplace `--t-message`, qui était déclaré et n'avait
     AUCUN appelant — pendant que `font-size: 19px` était écrit sept fois en
     dur ailleurs dans cette feuille. */
  --t-titre: 1.5rem;        /* 24 px */
  --t-sous-titre: 1.1875rem; /* 19 px */
  --t-corps: 1.125rem;      /* 18 px */
  --t-meta: .9375rem;       /* 15 px */
  --t-onglet: .8125rem;     /* 13 px */
  /* Deux marches manquaient, et leur absence a produit les strays : quatorze
     déclarations à 12 px, onze à 13, quatre à 11, plus 10, 14, 16, 17, 20, 26
     et 28 px posés un par un. Une échelle incomplète ne se respecte pas — on
     écrit la valeur qu'il faut, et l'échelle devient décorative. */
  --t-micro: .75rem;        /* 12 px — étiquettes, méta de second rang */
  --t-grand: 1.75rem;       /* 28 px — le titre d'un écran d'accueil */

  /* Les cibles tactiles : 48 px de surface réelle est un plancher juridique,
     pas un objectif. Les actions principales montent à 56 px. */
  --cible: 48px;
  --cible-principale: 56px;

  /* La ligne qui fait rendre au navigateur SES propres objets dans le bon
     thème : le calendrier d'un champ date, les barres de défilement, les cases
     à cocher natives. Sans elle, le calendrier s'ouvre en blanc sur un écran
     noir, et l'on croit à un défaut du produit. */
  color-scheme: light dark;

  /* ------------------------------------------------------------------
     L'ÉCHELLE D'ESPACEMENT — NEUF CRANS, ET NON QUARANTE-HUIT
     ------------------------------------------------------------------
     La feuille employait quarante-huit valeurs distinctes en `rem`. Les
     grappes qui ne se distinguent pas à l'œil, mais qui font « respirer au
     hasard » : quatre valeurs entre 1,6 et 2,9 px, CINQ entre 11 et 14, quatre
     entre 17 et 22. Rien ne se voit ligne à ligne ; l'ensemble, si.

     Les valeurs négatives ne sont pas de l'espacement — ce sont des annulations
     du rembourrage de `.page`, calculées pour tomber juste — et les grandes
     (8, 15, 17, 46 rem) sont des largeurs de colonne. Ni les unes ni les autres
     n'entrent dans l'échelle.

     Elle est déclarée ici pour qu'on la lise avant d'écrire une règle de plus :
     une échelle qu'on reconstitue de mémoire se disperse à nouveau. */
  /* ------------------------------------------------------------------
     LA HAUTEUR RÉSERVÉE À LA BARRE DU BAS
     ------------------------------------------------------------------
     Un seul jeton, parce que DEUX endroits en dépendent : la barre elle-même,
     et le rembourrage que le document lui réserve. Ils étaient écrits deux
     fois, à l'identique, et deux calculs jumeaux finissent toujours par
     diverger d'un correctif à l'autre — l'un corrigé, l'autre oublié, et la
     barre recouvre le dernier paragraphe de chaque page.

     La zone sûre S'AJOUTE aux 64 px : sur un iPhone à encoche elle vaut 34 px,
     et les inclure dans la hauteur ne laisserait que 30 px aux six onglets. */
  --barre-basse: calc(64px + env(safe-area-inset-bottom, 0px));
  /* ------------------------------------------------------------------
     LA BANDE HAUTE A UNE HAUTEUR, ET PERSONNE NE LA CONNAISSAIT
     ------------------------------------------------------------------
     Elle est `sticky` en tête de chaque écran, 3 rem plus l'encoche. L'écran
     de la carte se calculait `100dvh - var(--barre-basse)` en l'ignorant : la
     page dépassait donc la fenêtre de la hauteur de cette bande, l'écran se
     mettait à défiler, le titre passait sous la barre et une bande vide
     s'ouvrait sous la feuille. JB, le 27 août : « même replié, pas bon ».

     Le jeton existait dans le dessin et pas dans la feuille — c'est le même
     défaut que `--barre-basse` inventé deux fois la semaine dernière. */
  --bande-haute: calc(3rem + env(safe-area-inset-top, 0px));

  /* ------------------------------------------------------------------
     L'ÉCHELLE DES PLANS, ÉCRITE UNE FOIS
     ------------------------------------------------------------------
     Leaflet a la sienne, et elle est haute : ses tuiles à 200, ses tracés à
     400, ses marqueurs à 600, ses infobulles à 650, ses bulles à 700, ses
     contrôles à 800 — et les CONTENEURS de contrôles, `.leaflet-top` et
     `.leaflet-bottom`, à 1000.

     Deux corrections successives ont échoué faute de connaître ce dernier
     chiffre : la feuille des lieux est passée de 401 à 800, et l'attribution
     continuait de se peindre dans son bandeau. On pose donc l'échelle du
     produit AU-DESSUS de toute celle de Leaflet, et on l'écrit ici plutôt que
     de la deviner à chaque correctif.

     La barre des onglets est le dernier plan : elle doit rester atteignable
     quoi qu'il arrive. La légende s'était déjà peinte par-dessus une fois, et
     l'on ne pouvait plus naviguer du tout depuis la carte. */
  --plan-feuille: 1100;
  --plan-barre: 1200;
  /* La hauteur du bandeau de la feuille : les contrôles de Leaflet s'en
     écartent, sinon l'attribution — que la licence des tuiles impose de
     montrer — disparaît sous lui. */
  --feuille-bandeau: 3.25rem;
  /* La hauteur de la feuille des lieux, et de son corps qui défile. Un seul
     endroit pour les deux : leur différence est le bandeau, et deux valeurs
     posées séparément finissent par ne plus se répondre. */
  --feuille-haute: min(38dvh, 26rem);

  --e0: .15rem; --e1: .25rem; --e2: .5rem;  --e3: .75rem;
  --e4: 1rem;   --e5: 1.25rem; --e6: 1.5rem; --e7: 2rem; --e8: 3rem;

  /* ==================================================================
     CE QUI SUIT EST LE SYSTÈME. RIEN NE S'ÉCRIT EN DEHORS.
     ==================================================================
     Le relevé qui a motivé ce bloc, fait sur cette feuille même :

       42 tailles de police en dur, sur 15 valeurs distinctes ;
       61 rayons d'angle en dur,    sur 16 valeurs distinctes ;
       139 rembourrages,            sur 65 valeurs distinctes ;
       193 marges,                  sur 54 valeurs distinctes.

     Seize rayons d'angle différents dans une même application, ce n'est pas un
     réglage fin : c'est l'absence de système. Chacun a été posé un jour pour
     une raison locale et juste, et leur somme donne une application qui a l'air
     bricolée sans qu'on sache dire par quoi — parce que le défaut n'est dans
     aucune règle, il est dans leur dispersion.

     RÈGLE : toute nouvelle déclaration de taille, de rayon, de graisse ou
     d'espacement emploie un jeton. Une valeur en dur doit se justifier par un
     commentaire, comme n'importe quelle autre exception de ce dépôt. */

  /* --- RAYONS : trois, et pas un de plus ------------------------------
     Le petit pour ce qui se pose sur le fond (étiquettes, champs), le moyen
     pour ce qui est un objet (cartouches, boutons, cartes), le rond pour ce
     qui porte un visage. Il n'y a pas de quatrième cas. */
  --r-petit: 4px;
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  --r-moyen: 10px;
  --r-rond: 999px;

  /* --- GRAISSES : trois -----------------------------------------------
     La courante, celle qui distingue, celle qui affirme. `550` et `700`
     n'étaient employés qu'une ou deux fois chacun — un écart qu'on ne voit pas
     mais qui empêche l'œil de tenir un rythme. */
  --g-normale: 400;
  --g-appuyee: 500;
  --g-forte: 650;

  /* --- ÉLÉVATION : deux ------------------------------------------------
     Une ombre pour ce qui flotte au-dessus de la page (médaillon, feuille de
     carte), une pour ce qui se soulève au survol. Le reste est plat : cette
     application est un registre, pas un tableau de bord. */
  --ombre-posee: 0 1px 3px rgb(0 0 0 / .10);
  --ombre-levee: 0 4px 14px rgb(0 0 0 / .12);

  /* --- DURÉES ----------------------------------------------------------
     Une seule, et courte. Au-delà de 160 ms on attend l'interface. */
  --duree: 140ms;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "EB Garamond", Georgia, serif;
  --sans: "Avenir Next", Avenir, "Segoe UI", system-ui, -apple-system, sans-serif;

  /* Les cinq teintes de la carte. Elles étaient écrites en dur au bas de la
     feuille, avec leurs valeurs CLAIRES — et `#2C4A6E` sur le fond sombre
     `#14130F` ne se voit pas : les points, c'est-à-dire l'objet même de
     l'écran, s'effaçaient. Un jeton par teinte, doublé plus bas. */
  --pastille-residence: #2C4A6E;
  --pastille-maison:    #2F5D45;
  --pastille-vendue:    #9A9384;
  --pastille-annonce:   #8E6B29;
  --pastille-nouvelle:  #8E3129;
}

/* Le chevron des listes déroulantes est une image encodée : sa couleur ne peut
   pas venir d'une variable, elle est donc redéclarée pour le thème sombre. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23A39C8E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) {
    /* ------------------------------------------------------------------
       TROIS JETONS QUI RÉPARENT ONZE ÉCRANS
       ------------------------------------------------------------------
       Relevé sur vingt-deux captures d'écran en thème sombre :

         · le texte secondaire (`--encre-douce`) tombait à moins de 4,5:1 sur
           le fond, et c'est lui qui porte l'âge, la filiation, l'aperçu d'un
           message, la date — c'est-à-dire tout ce qui SITUE. Illisible pour un
           lecteur de soixante-quinze ans, et le public visé en compte
           beaucoup ;

         · `--surface` était à huit points de `--papier` : le fond d'une carte
           ne se distinguait pas du fond de la page, et les cartes de l'arbre
           « disparaissaient en plein soleil » ;

         · `--filet` était trop sourd pour rattraper l'un ou l'autre.

       Le fond de page descend, la surface monte, l'encre douce remonte. La
       nuance chaude est conservée — c'est du papier, pas un terminal. */
    --papier:      #121110;
    --surface:     #221F1A;
    --encre:       #EDE8DD;
    --encre-douce: #B8B0A0;
    --filet:       #38342B;
    --filet-clair: #26231D;
    --lien:        #8FB4DA;
    /* Remonté d'un cran seulement : assez pour se détacher du fond, pas au
       point de devenir la chose la plus claire de l'écran. */
    --geste:       #3E6899;
    --geste-encre: #F2EFE6;
    --alerte:      #D4837A;
    --succes:      #7FB79A;

    /* Les mêmes teintes remontées : sur un fond presque noir, il faut monter
       la clarté, pas seulement la saturation. */
    --pastille-residence: #7FA5CE;
    --pastille-maison:    #74B295;
    --pastille-vendue:    #A39C8E;
    --pastille-annonce:   #D2A75C;
    --pastille-nouvelle:  #D4837A;
  }
}

/* Le réglage de confort, en trois crans, stocké côté serveur pour suivre
   l'utilisateur d'un appareil à l'autre. */
html[data-taille="grand"]      { font-size: 117%; }
html[data-taille="tres_grand"] { font-size: 133%; }

* { box-sizing: border-box; }

/* ==================================================================
   TROIS REGISTRES, ET ON DOIT LES DISTINGUER SANS LIRE
   ==================================================================
   Le produit mêle trois choses de nature différente sur les mêmes écrans, et
   rien ne les distinguait :

     CE QUI S'INFORME — le faire-part, le carnet, la fiche, l'arbre. On le LIT.
       Voix éditoriale : romaine, filets, aucun cadre. C'est le défaut, et il
       n'a besoin d'aucune classe.

     CE QUI S'ÉDITE — publier, déclarer, inscrire, déposer. On l'UTILISE.
       Voix utilitaire : linéale, champs, boutons. Replié tant qu'on ne s'en
       sert pas, parce qu'un formulaire ouvert d'office repousse ce qu'on vient
       lire. C'est `.deposer`, qui existait déjà.

     CE QUI S'ADMINISTRE — la régie : invitations, motifs, alliances,
       modération, mandats. Cela n'appartient pas au même monde : c'est le
       travail d'UNE personne pour toute la famille, et le montrer au même rang
       que le reste fait croire à chacun qu'il doit s'en occuper.

   La régie prend donc un registre à elle : un fond légèrement distinct, un
   filet complet, et une étiquette qui NOMME le rôle et sa durée quand elle est
   bornée. Pas de couleur d'alerte — administrer n'est pas dangereux — et pas
   d'ombre : la direction artistique l'interdit.

   ------------------------------------------------------------------
   LE MANDATAIRE EST EN LECTURE, SAUF DÉCLENCHEMENT
   ------------------------------------------------------------------
   `Delegation` porte déjà `portee` (lecture / lecture_ecriture), `expire_le`
   borné à trois mois, et la révocation. Ce qui manquait est de le DIRE à
   l'écran : un mandataire qui ne peut pas écrire doit voir pourquoi, et
   jusqu'à quand. D'où `.regie__mention`, qui porte le terme.
   ================================================================== */
.regie { margin: var(--e5) 0; padding: var(--e4) var(--e4);
  border: 1px solid var(--filet); border-radius: var(--r-petit);
  background: var(--filet-clair); }
.regie__intitule { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--e3); flex-wrap: wrap; margin: 0 0 var(--e2);
  font-family: var(--sans); font-size: var(--t-meta); font-weight: var(--g-appuyee);
  color: var(--encre-douce); }
/* Le terme d'un mandat se lit sur la ligne, jamais dans une note de bas de
   page : « jusqu'au 3 novembre » est ce qu'on vient vérifier. */
.regie__mention { font-size: var(--t-onglet); color: var(--encre-douce); }
.regie__gestes { display: flex; flex-wrap: wrap; gap: var(--e2); }
.regie__gestes form { margin: 0; }
/* En lecture seule, les gestes restent VISIBLES et inertes : les masquer
   ferait croire qu'ils n'existent pas, et l'on téléphonerait au régisseur. */
.regie--lecture .regie__gestes,
.regie--lecture .cousinage-carte__destinations { opacity: .55; pointer-events: none; }

/* ------------------------------------------------------------------
   LE GESTE DE RÉGIE SE DISTINGUE DU GESTE DE TOUS
   ------------------------------------------------------------------
   « Les motifs » et « Les familles alliées » portaient la forme d'un lien
   ordinaire, au milieu de gestes que tout le monde peut faire. Or ils engagent
   la famille ENTIÈRE — un motif paraît sur chaque faire-part — et ils ne sont
   ouverts qu'au régisseur.

   Ils prennent donc une forme à eux : le contour d'une action, mais en encre
   douce et sur le fond de la zone de régie, jamais l'aplomb d'un geste
   principal. On voit qu'on peut le faire ET qu'on est le seul à le pouvoir. */
.action--regie {
  display: inline-flex; align-items: center; gap: var(--e2);
  min-height: 38px; padding: 0 var(--e3);
  font-family: var(--sans); font-size: var(--t-meta); font-weight: var(--g-appuyee);
  color: var(--encre-douce); text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  transition: border-color var(--duree), color var(--duree);
}
.action--regie:hover { color: var(--encre); border-color: var(--encre-douce); }

/* ==================================================================
   L'ÉTIQUETTE — UN SEUL TRAITEMENT, ET SANS TRUQUAGE
   ==================================================================
   Quinze objets du produit portaient des petites capitales sur la LINÉALE :
   rubriques, rôles, genres, natures de groupe, heures d'un moment,
   intertitres. Aucune des trois linéales de la pile — Avenir Next, Segoe UI,
   system-ui — ne porte de table `smcp` : le navigateur les SYNTHÉTISE en
   écrasant des capitales à 70 %. Le résultat est comprimé, plus sombre que le
   texte voisin, et fautif sans qu'on sache dire pourquoi. C'est l'« écriture
   écrasée » qu'on voyait partout.

   Une étiquette n'a pas besoin de crier. Treize pixels, une graisse de 500, la
   couleur douce et la CASSE NORMALE suffisent à dire « ceci classe ce qui
   suit » — c'est ce que font les produits soignés de 2026, qui ont pour la
   plupart abandonné les bandeaux en capitales.

   La ROMAINE, elle, garde ses petites capitales : Iowan Old Style, Palatino et
   Georgia en portent de vraies, et la rubrique du carnet est le seul endroit
   du produit où cette voix a lieu d'être.

   ------------------------------------------------------------------
   ET LA GRAISSE : 600 ÉTAIT DEVENU LE DÉFAUT
   ------------------------------------------------------------------
   Trente-deux déclarations en 600 — boutons, étiquettes, noms, intitulés de
   liste, sous-titres. Quand tout est demi-gras, rien ne ressort, et la page
   paraît uniformément épaisse. La règle tient en trois lignes :

     400  le texte courant, et les étiquettes qui classent ;
     500  ce qui NOMME — un nom de personne, un titre en romaine ;
     600  ce qui S'ACTIONNE, et lui seul : un bouton, l'intitulé d'une ligne
          qu'on va toucher.
   ================================================================== */
.etiquette, .genre, .rubrique-linéale, .cousinage-carte__role,
.cousinage-carte__rubrique, .moment__heure, .intertitre-famille,
.etiquette--famille, .etiquette--mien, .etiquette--dun_cousin, .etiquette--duo {
  font-family: var(--sans); font-size: var(--t-onglet);
  font-weight: var(--g-appuyee); letter-spacing: 0; color: var(--encre-douce); }
/* --------------------------------------------------------------------------
   LA RÈGLE CI-DESSUS A DÉJÀ ÉTÉ DÉCAPITÉE UNE FOIS, ET VOICI CE QUE ÇA FAIT.
   Une passe de normalisation lui a retiré sa dernière ligne de sélecteurs ET
   son bloc de déclarations : il restait une liste finissant par une virgule.
   Or une liste de sélecteurs sans bloc N'EST PAS une règle morte — elle se
   SOUDE à la règle suivante, qui était `[hidden] { display: none !important }`.
   Toutes les étiquettes du produit — les noms des rôles des Conditions en
   premier — ont disparu de l'écran sans une erreur nulle part.
   -------------------------------------------------------------------------- */

/* ------------------------------------------------------------------
   `hidden` DOIT L'EMPORTER, ET IL NE L'EMPORTAIT PAS
   ------------------------------------------------------------------
   L'attribut `hidden` est posé par la feuille du NAVIGATEUR, à spécificité
   nulle : la moindre règle de classe qui déclare un `display` le bat. Le
   plateau d'aperçu du fil (`.plateau { display: flex }`) restait donc affiché
   en permanence, avec sa balise `<img>` sans source — que le navigateur rend
   en carré brisé, quatre traits qui ne se touchent pas. À l'écran, cela
   ressemblait à un dessin raté ; c'était une image absente.

   Le même piège attendait la grille d'aperçus des zones de dépôt. Une seule
   règle ferme les deux, et toutes celles à venir. */
[hidden] { display: none !important; }

body {
  margin: 0;
  /* La barre d'onglets fait 64 px PLUS la zone sûre. Le rembourrage comptait
     56 + 32 = 88 px et ignorait la zone sûre : sur un iPhone à encoche, les dix
     derniers pixels du contenu passaient sous la barre, sur tous les écrans. */
  /* La MÊME expression que la barre, par le jeton : deux calculs jumeaux
     finissent toujours par diverger d'un correctif à l'autre. */
  padding: 0 0 calc(var(--barre-basse) + 1rem);
  background: var(--papier);
  color: var(--encre);
  font-family: var(--sans);
  font-size: var(--t-corps);
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
  /* Rien du produit ne doit pouvoir faire défiler la page en LARGEUR. Quand
     cela arrivait — un nom insécable dans une rangée flexible sans
     `min-width: 0` — tous les autres écrans paraissaient décalés sans qu'on
     voie d'où cela venait. */
  overflow-x: hidden;
}

/* Aucun `<p>` du produit ne remettait ses marges à zéro : `.secondaire` ne pose
   que la couleur et le corps, si bien que tout sous-titre coûtait 30 px de
   marges de navigateur, invisibles dans la feuille. */
p { margin: 0 0 var(--e4); }

/* `min(40rem, 72ch)` : sans le second terme, le réglage de confort — qui agit
   sur la racine, donc sur les `rem` — élargirait la colonne EN MÊME TEMPS que
   le texte, et la mesure ne cesserait jamais de s'allonger. Le `ch` suit le
   corps réel : la ligne garde le même nombre de signes à tous les crans, ce
   qui est précisément l'objet du réglage. */
.page { max-width: min(40rem, 72ch); margin: 0 auto; padding: var(--e6) var(--e5); }

h1 { font-family: var(--serif); font-size: var(--t-titre); font-weight: var(--g-appuyee);
     line-height: 1.25; margin: 0 0 var(--e2); text-wrap: balance; }

.chapo { color: var(--encre-douce); margin: 0 0 var(--e6); }
.secondaire { color: var(--encre-douce); font-size: var(--t-meta); }

/* Les métadonnées ne descendent jamais sous 15 px et ne sont jamais en gris
   clair : horodatages et mentions légales compris. */

a { color: var(--lien); text-underline-offset: 2px; }

/* ------------------------------------------------------------------
   TOUT REPLI PORTE SON CHEVRON — COU-CRA-006
   ------------------------------------------------------------------
   Un `<summary>` ne dessine son triangle natif qu'en `display: list-item`.
   Douze replis du produit sont en `flex` ou en `grid` pour aligner leur
   contenu : tous avaient perdu leur marqueur, `list-style-type` déclaré ou non.

   Ils ne manquaient pas seulement de signe : sur « Inscrire le mien », le
   `summary` est en 18 px, graisse 500, couleur `--encre` — c'est-à-dire
   TYPOGRAPHIQUEMENT IDENTIQUE à un titre de section. Il n'était pas discret, il
   envoyait le mauvais signal, et seul le curseur au survol le démentait — donc
   rien du tout sur un téléphone. Sur un public dont une partie ouvre
   l'application trois fois par an, un contrôle qui ressemble à un titre n'est
   jamais actionné.

   On rend donc le signe à TOUS, une fois : un chevron qui pivote à l'ouverture,
   posé en `::after` — il survit à n'importe quelle disposition, là où le
   marqueur natif dépend d'elle. `margin-left: auto` le pousse au bout des
   replis en flex ; sur les autres il suit simplement le texte.

   `currentColor` par masque, et non un SVG en fond : le chevron doit suivre la
   couleur du libellé, qui change d'un repli à l'autre et d'un thème à l'autre.

   Ceux qui portent DÉJÀ leur propre signe — le trombone du composeur, la
   poignée de la feuille de carte, l'étiquette de motif — s'en dispensent
   nommément, plus bas. */
summary { list-style: none; }

/* Un `summary` peut loger un titre : la zone devient alors atteignable par la
   navigation de titre en titre, sans que le repli cesse d'être un bouton. Le
   titre n'apporte QUE sa sémantique — toute son allure vient du `summary`. */
summary > h2, summary > h3 { font: inherit; color: inherit; margin: 0;
  letter-spacing: inherit; }
summary::-webkit-details-marker { display: none; }

summary::after {
  content: ""; flex: 0 0 auto; width: 1em; height: 1em; margin-left: auto;
  background: currentColor;
  mask: var(--chevron) center / .75em .75em no-repeat;
  -webkit-mask: var(--chevron) center / .75em .75em no-repeat;
  transition: transform .15s ease;
}
details[open] > summary::after { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  summary::after { transition: none; }
}

/* Les trois qui ont leur propre signe. */
.composeur__joindre > summary::after,
.greffe--motif > summary::after,
.carte__feuille > summary::after { content: none; }


.carte { max-width: 26rem; margin: 3rem auto; }

.formulaire { display: flex; flex-direction: column; gap: var(--e3); }
label { font-weight: var(--g-appuyee); font-size: var(--t-meta); }

/* La mention « facultatif » DANS l'étiquette, et non sous le champ : posée en
   dessous, elle se lit après la saisie — c'est-à-dire trop tard. Elle reprend
   le poids et la couleur du texte secondaire pour ne pas concurrencer
   l'étiquette qu'elle qualifie. */
label .facultatif { font-weight: var(--g-normale); color: var(--encre-douce); }

/* ==================================================================
   TOUS LES CHAMPS, ET PAS SEULEMENT DEUX
   ==================================================================
   Seuls `email` et `text` étaient stylés. Le téléphone, le nombre, la date, la
   recherche, la liste déroulante et la zone de texte tombaient donc sur le
   rendu par défaut du navigateur — c'est-à-dire, en thème SOMBRE, une boîte
   BLANCHE au milieu d'un écran noir. Le champ téléphone y ajoutait l'icône de
   contacts de l'autoremplissage, sur fond blanc lui aussi.

   Une liste de sélecteurs, donc, et pas un type oublié. Le jour où l'on ajoute
   un `url_field`, il est déjà couvert. */
input[type="email"], input[type="text"], input[type="tel"], input[type="url"],
input[type="number"], input[type="password"], input[type="search"],
input[type="date"], input[type="datetime-local"], input[type="time"],
input[type="month"], input[type="week"],
textarea, select {
  font-family: var(--sans);
  font-size: var(--t-corps);
  min-height: var(--cible);
  padding: var(--e2) var(--e3);
  background: var(--surface);
  color: var(--encre);
  border: 1px solid var(--filet);
  border-radius: var(--r-petit);
  /* Sans cela, iOS arrondit et ombre les champs à sa façon, et Safari refuse
     la couleur de fond sur `select`. */
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  box-sizing: border-box;
}
textarea { min-height: 6rem; line-height: 1.5; resize: vertical; }

/* La flèche de la liste déroulante, dessinée par nous : `appearance: none` a
   retiré celle du système, et sans remplacement la liste ne se distingue plus
   d'un champ de saisie. Le chevron est encodé en `currentColor` via `mask`,
   pour qu'il suive le thème sans deuxième image. */
/* ------------------------------------------------------------------
   LE SÉLECTEUR FAISAIT CENT PIXELS DE HAUT
   ------------------------------------------------------------------
   `padding: 2rem` — trente-deux pixels sur les QUATRE côtés. L'intention
   était de dégager la place du chevron À DROITE ; écrite en raccourci, elle
   s'appliquait aussi en haut et en bas, et chaque liste déroulante devenait
   deux fois plus haute qu'un champ de saisie voisin. Sur l'écran « Déclarer
   une nouvelle », qui en aligne trois, cela donnait un formulaire deux fois
   trop long.

   Le sélecteur reprend donc EXACTEMENT le rembourrage d'un champ, et n'ajoute
   que ce qu'il lui faut à droite pour son chevron. */
select {
  padding: var(--e2) var(--e7) var(--e2) var(--e3);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235C574E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .75rem center;
  background-size: 18px 18px;
}

/* ------------------------------------------------------------------
   UN TEXTE D'AIDE N'EST PAS UNE VALEUR SAISIE
   ------------------------------------------------------------------
   JB, le 27 août 2026 : « je ne veux plus de placeholders pour les champs, car
   les gens croient que quelqu'un a déjà renseigné — ou alors mets un
   placeholder explicatif et non un exemple, en italique et grisé pour ne pas
   confondre ».

   Les soixante-huit textes d'EXEMPLE ont été retirés le 25 août, et une sonde
   qui rend les quarante-six écrans en compte zéro. Cette règle vaut donc pour
   le jour où l'on en reposerait un : l'italique dit, avant qu'on lise le mot,
   que ce n'est pas une réponse. La couleur seule ne suffisait pas — un champ
   pré-rempli est gris lui aussi sur la plupart des systèmes. */
::placeholder { color: var(--encre-douce); opacity: 1; font-style: italic; }

/* ------------------------------------------------------------------
   L'AUTOREMPLISSAGE IMPOSE SON PROPRE FOND, ET IL EST BLANC
   ------------------------------------------------------------------
   Chrome et Safari peignent un champ autorempli en blanc cassé ou en jaune
   pâle, et cette couleur n'est pas surchargeable directement. L'ombre intérieure
   de cent unités est le seul moyen de la recouvrir, et `-webkit-text-fill-color`
   le seul moyen de rendre le texte lisible par-dessus. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 100px var(--surface) inset;
  -webkit-text-fill-color: var(--encre);
  caret-color: var(--encre);
}

input:focus-visible, textarea:focus-visible, select:focus-visible,
button:focus-visible, a:focus-visible {
  outline: 2px solid var(--lien); outline-offset: 2px;
}

/* Le champ du code : six chiffres, larges, espacés, en chiffres tabulaires. */
.champ-code {
  font-size: var(--t-grand); letter-spacing: .4em; text-align: center;
  font-variant-numeric: tabular-nums; min-height: var(--cible-principale);
}

/* ==================================================================
   TOUT CE QUI SE CLIQUE, EN UN SEUL ENDROIT
   ==================================================================
   Il y avait NEUF familles de boutons pour trois rôles : `button` (56 px,
   plein), `input[type="submit"]` (copie littérale de la précédente, écrite
   soixante-dix lignes plus loin parce que le sélecteur ne l'atteignait pas),
   `.action` (48 px, plein), `.action--seconde`, `.bouton-discret` en DEUX
   règles — l'une pour le descendant, l'autre pour l'élément —,
   `.bouton-secondaire`, `.recherche button`, `.courrier__action`,
   `.consentement__refus`. Plus `.bouton-principal`, employé dans une vue et
   défini NULLE PART.

   Conséquence mesurable, et c'est exactement le grief : sur la page Famille,
   le bouton « Chercher » est un `<button>` nu de 56 px en graisse 600, à côté
   de deux `.action` de 48 px — même rang, trois hauteurs, deux graisses. Et
   sur « Moi », `submit_tag class: "action--seconde"` sortait NOIR PLEIN, parce
   que `input[type="submit"]` (spécificité 0-1-1) bat `.action--seconde`
   (0-1-0).

   Trois rôles, une hauteur, un rayon. Les anciens noms restent des ALIAS : les
   renommer dans quarante vues est exactement le genre de remplacement en masse
   qui a déjà cassé trois fois ce dépôt. On unifie l'apparence d'abord ; les
   noms suivront quand chaque vue sera relue une par une.

   `--cible` (48 px) partout, et non `--cible-principale` (56 px) : le plancher
   de 48 px est la règle, 56 px était l'exception devenue majoritaire. Ce qui
   doit vraiment dominer un écran le dit par `.action--large`, délibérément. */
button, input[type="submit"], .action, .bouton-principal,
.action--seconde, .bouton-secondaire button, button.bouton-secondaire,
.bouton-discret button, button.bouton-discret, .recherche button,
.courrier__action, .consentement__refus, .action--grave {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--e2);
  /* ------------------------------------------------------------------
     LA TYPOGRAPHIE D'UN BOUTON N'EST PAS CELLE D'UN PARAGRAPHE
     ------------------------------------------------------------------
     Elle était à `--t-corps` (18 px) en graisse 650 : la taille du texte
     courant, dans la graisse la plus lourde de la feuille, en blanc sur un
     aplat foncé. JB, le 26 août 2026 : « un poil grossier, les lettres ont
     trop de gras ou la police est trop grosse, il y a un truc ».

     Le truc est qu'un libellé de bouton est une ÉTIQUETTE, pas une phrase :
     on le reconnaît d'un coup d'œil, on ne le lit pas. 15 px en graisse 500,
     comme `.action--regie` — qui, elle, n'a jamais été trouvée grossière — et
     un demi-point d'interlettrage, que le blanc sur foncé réclame parce que le
     fond mange les contreformes.

     LA HAUTEUR NE BOUGE PAS : 48 px reste le plancher de la cible tactile. Ce
     qui change est la lettre, pas la surface qu'on vise avec le pouce. */
  font-family: var(--sans); font-size: var(--t-meta); font-weight: var(--g-appuyee);
  letter-spacing: .005em;
  min-height: var(--cible); padding: 0 var(--e5);
  /* ------------------------------------------------------------------
     LE BOUTON PRINCIPAL N'EST PLUS UN PAVÉ D'ENCRE
     ------------------------------------------------------------------
     Il prenait `--encre` : la couleur du TEXTE, c'est-à-dire le plus fort
     contraste de la page, pour le geste le plus banal. En thème sombre cela
     donnait un rectangle presque blanc — l'objet le plus lumineux de l'écran —
     et en thème clair un pavé noir. Criard dans les deux cas, et daté : le
     rectangle plein à angles vifs est un geste de 2015.

     Il prend `--lien`, la couleur de ce sur quoi on clique — celle que le
     lecteur a déjà apprise sur chaque nom de personne — et un rayon de 8 px.
     La direction artistique tient : pas d'ombre, pas de flou, pas de dégradé,
     un aplat et un filet. Ce qui change, c'est la VIOLENCE du contraste.

     Le rayon reste à 3 px sur les champs de saisie : un champ n'est pas un
     bouton, et leur donner la même forme est ce qui rend les formulaires
     illisibles. */
  border-radius: var(--r-moyen); cursor: pointer; text-decoration: none;
  background: var(--geste); color: var(--geste-encre);
  border: 1px solid var(--geste);
}
button:hover, input[type="submit"]:hover, .action:hover, .bouton-principal:hover {
  filter: brightness(1.08); }

/* Le second rang : bordé, jamais plein. */
.action--seconde, .bouton-secondaire button, button.bouton-secondaire,
.consentement__refus, .courrier__action {
  background: none; color: var(--encre); border-color: var(--filet); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) .action--seconde,
  :root:not([data-theme="clair"]) .bouton-secondaire button,
  :root:not([data-theme="clair"]) button.bouton-secondaire,
  :root:not([data-theme="clair"]) .consentement__refus,
  :root:not([data-theme="clair"]) .courrier__action,
  :root:not([data-theme="clair"]) .bouton-discret button,
  :root:not([data-theme="clair"]) button.bouton-discret,
  :root:not([data-theme="clair"]) .recherche button { color: var(--encre); }
}
.action--seconde:hover, .bouton-secondaire button:hover,
button.bouton-secondaire:hover, .consentement__refus:hover,
.courrier__action:hover { background: var(--filet-clair);
  border-color: var(--encre-douce); }

/* Le troisième rang : un filet, une encre douce, une graisse normale. C'est ce
   qu'on met à côté d'un champ, jamais ce qu'on met seul sur un écran. */
.bouton-discret button, button.bouton-discret, .recherche button {
  background: none; color: var(--encre-douce);
  border-color: var(--filet); font-weight: var(--g-normale); }
.bouton-discret button:hover, button.bouton-discret:hover,
.recherche button:hover { color: var(--encre); border-color: var(--encre-douce); }

button:focus-visible, input[type="submit"]:focus-visible,
.action:focus-visible, .action--seconde:focus-visible,
.bouton-principal:focus-visible {
  outline: 2px solid var(--lien); outline-offset: 2px; }

.action svg, .action--seconde svg, .bouton-principal svg {
  width: 20px; height: 20px; flex: 0 0 auto; }

/* Ce qui doit vraiment dominer un écran — le bouton de connexion, la saisie du
   code — le dit ici, et nulle part par accident. */
.action--large { min-height: var(--cible-principale); padding: 0 var(--e6); }

/* Un geste qui n'est pas un bouton : une action posée au fil du texte, en pied
   de page. Il ressemble à un lien parce que c'en est un, du point de vue de
   qui lit — mais il POSTE, d'où la balise. */
.bouton-lien, .bouton-lien:hover {
  display: inline-flex; align-items: center; min-height: 44px;
  padding: 0; margin: 0;
  background: none; border: 0; border-radius: 0;
  font-size: var(--t-onglet); font-weight: var(--g-normale); color: var(--encre-douce);
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.bouton-lien:hover { color: var(--alerte); }

/* Trente-cinq appels dans les vues, et aucune cible : un `inline-block` de
   15 px fait vingt-trois pixels de haut. C'est la forme la plus répandue du
   produit pour « Revenir » et « Annuler ». */
.lien-discret { display: inline-flex; align-items: center; min-height: var(--cible);
  margin: var(--e4); font-size: var(--t-meta); color: var(--encre-douce); }

/* ==================================================================
   LE PORTRAIT
   ==================================================================
   Une silhouette grise donnerait à trois cents personnes le même visage. Les
   initiales distinguent, et leur teinte se tire du nom : la même personne
   garde la même couleur d'un écran à l'autre, ce qui aide à la reconnaître
   dans une liste avant même d'avoir lu. */
/* ------------------------------------------------------------------
   L'INITIALE EST CENTRÉE, ET IL FAUT TROIS CHOSES POUR CELA
   ------------------------------------------------------------------
   `align-items: center` centre la BOÎTE de la ligne, pas les lettres qu'elle
   contient : une capitale n'occupe pas toute la hauteur de sa ligne, et son
   interligne — hérité du corps de page à 1,6 — la pousse vers le bas. Les
   monogrammes flottaient donc en bas à gauche de leur rond, et à 34 px la
   dérive se voit tout de suite.

   Il faut donc AUSSI `line-height: 1` pour que la ligne épouse la lettre, et
   `text-align: center` pour l'axe horizontal quand deux lettres n'ont pas la
   même chasse. Aucune des trois ne suffit seule. */
/* ------------------------------------------------------------------
   UN ROND EST CARRÉ — ET ON L'ÉCRIT, PLUTÔT QUE DE L'ESPÉRER
   ------------------------------------------------------------------
   Relevé par JB le 23 août 2026 : « le bug des médaillons qui deviennent
   pastille OVALE, rognant sur les côtés des initiales ». Ovale est une
   information géométrique — un disque ne s'aplatit que si sa largeur et sa
   hauteur cessent d'être égales.

   La largeur et la hauteur sont posées en ligne, en pixels, par
   `portrait_de`. Elles suffisent PARTOUT SAUF là où un conteneur les
   contraint : un `min-width` hérité, une grille étroite, un parent en flex qui
   comprime. Le cas ne s'est pas reproduit sous la main, et je n'ai pas voulu
   deviner lequel des trois.

   `aspect-ratio` et `flex-shrink: 0` ne diagnostiquent rien : ils rendent
   l'aplatissement IMPOSSIBLE, quelle que soit la cause. Un invariant écrit
   coûte moins qu'une enquête, et il tient les cas qu'on n'a pas imaginés. */
.portrait {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; flex-shrink: 0; aspect-ratio: 1;
  border-radius: var(--r-rond); object-fit: cover;
  background: var(--filet-clair);
  line-height: 1; text-align: center; overflow: hidden;
}
/* ------------------------------------------------------------------
   LES INITIALES SUIVENT LE THÈME, LA TEINTE NE FAIT QUE LES DISTINGUER
   ------------------------------------------------------------------
   La couleur de fond était écrite dans le gabarit — `hsl(angle 24% 82%)` — un
   pastel CLAIR dans les deux thèmes, avec une encre noire posée dessus. Sur
   fond sombre, quarante de ces ronds donnaient une planche de vignettes
   collées sur la page : ce qui devait dire « je n'ai pas encore de photo »
   criait plus fort que les vraies photographies.
   
   Le gabarit ne pose plus que l'ANGLE, qui identifie la personne. La clarté et
   la saturation appartiennent au thème, seul à savoir sur quoi le rond se
   pose. */
.portrait--initiales {
  font-family: var(--sans); font-weight: var(--g-forte); letter-spacing: .02em;
  background: hsl(var(--teinte, 0) 24% 84%);
  color: #1A1814;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) .portrait--initiales {
    /* Sourde et non pastel : le rond descend sous le niveau du texte au lieu
       de monter au-dessus. L'encre repasse à celle du papier. */
    background: hsl(var(--teinte, 0) 16% 26%);
    color: #E8E4DA;
  }
}
:root[data-theme="sombre"] .portrait--initiales {
  background: hsl(var(--teinte, 0) 16% 26%);
  color: #E8E4DA;
}

/* ==================================================================
   DÉPOSER UN FICHIER, ET QUE ÇA SE VOIE
   ==================================================================
   Le champ `file` natif est une petite boîte grise portant « Sélect. fichiers »
   — sur téléphone, à peine visible, et rien ne dit qu'on peut y déposer une
   photo. C'est la raison pour laquelle personne ne trouvait comment ajouter
   quoi que ce soit.

   Le champ est donc masqué et l'étiquette prend sa place : une zone franche,
   bordée en pointillé, de la hauteur d'une cible tactile. Le `label` porte le
   `for` — le clic passe au champ sans une ligne de code. */
.depot {
  display: flex; flex-direction: column; gap: var(--e1);
  padding: var(--e4) var(--e5); cursor: pointer;
  border: 1px dashed var(--filet); border-radius: var(--r-petit);
  background: var(--surface);
}
.depot:hover { border-color: var(--lien); background: var(--filet-clair); }
.depot:focus-within { outline: 2px solid var(--lien); outline-offset: 2px; }
/* Pendant qu'un fichier survole la zone. Le pointillé promettait ce geste sans
   que rien ne l'écoute : on glissait une photo dessus et le navigateur
   l'ouvrait en plein écran, en quittant la page. */
.depot--survole { border-color: var(--lien); border-style: solid;
  background: var(--filet-clair); }
.depot__intitule { font-family: var(--sans); font-weight: var(--g-appuyee); }
/* Le plafond et les formats se disent AVANT l'envoi. On ne les découvrait
   qu'après avoir attendu la montée d'un fichier trop lourd. */
.depot__aide { font-size: var(--t-meta); color: var(--encre-douce); }
.depot__geste { font-size: var(--t-meta); color: var(--lien);
  text-decoration: underline; text-underline-offset: 2px; }
.depot input[type="file"] {
  /* Masqué à l'œil, pas au clavier ni au lecteur d'écran. */
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
.depot__choisi { font-size: var(--t-meta); color: var(--encre);
  font-weight: var(--g-appuyee); overflow-wrap: anywhere; }

/* ------------------------------------------------------------------
   L'APERÇU EST RECTANGULAIRE, ET C'EST VOULU
   ------------------------------------------------------------------
   Un nom de fichier — `IMG_4472.HEIC` — n'apprend rien : sur trente photos
   d'un même après-midi, ils se ressemblent tous. La vignette dit en un coup
   d'œil si l'on a pris la bonne.

   Le RECTANGLE plutôt que le rond : on cadre ce qu'on voit. Une zone ronde à
   la saisie pour un rendu rond donnait un menton coupé qu'on ne découvrait
   qu'après ; un rectangle montre l'image entière, et le rond ne survient qu'à
   l'affichage, là où il appartient au vocabulaire du portrait. */
.depot__apercus { display: flex; flex-wrap: wrap; gap: var(--e2); margin: var(--e2); }
.depot__apercu { width: 72px; height: 72px; object-fit: cover;
  border-radius: var(--r-petit); border: 1px solid var(--filet); background: var(--filet-clair); }

/* ==================================================================
   UNE CARTE PAR COUSINAGE
   ==================================================================
   C'est le SEUL écran du produit qui gagne à être encadré, et pour une raison
   précise : il énumère des objets de même nature dont il faut voir la
   frontière. Partout ailleurs, le filet d'un pixel suffit et la carte serait
   du chrome — ici, sans elle, sept familles se lisent comme une seule liste de
   vingt liens gris.

   Le bord gauche prend la couleur de la famille : c'est le filet d'origine
   promu au rang de conteneur, et non un objet de plus. */
.cousinage-carte { margin: 0 0 var(--e4); border: 1px solid var(--filet);
  border-left: 3px solid var(--couleur-origine, var(--filet));
  border-radius: var(--r-petit); background: var(--surface); }
/* ------------------------------------------------------------------
   UN TITRE ET SES MENTIONS NE SE PARTAGENT PAS UNE LIGNE
   ------------------------------------------------------------------
   L'entête était une rangée `space-between` de trois éléments : le nom, le lien
   « Qui tient quoi » et le rôle. Le nom d'un cousinage est une PHRASE —
   « Cousinage de Philippe et Anne-Françoise de Cathelineau » — et il prenait
   deux ou trois lignes ; le lien se retrouvait dans une colonne de quarante
   pixels, coupé en trois mots empilés. JB, le 29 août 2026 : « c'est moche ».

   Le nom prend donc sa ligne, et les mentions la suivante, sur un seul rang
   séparé par des points médians — le rôle, les générations, le chemin vers les
   rôles. C'est la disposition de toutes les autres fiches du produit ; cette
   carte était la seule à ne pas la suivre. */
.cousinage-carte__entete { padding: var(--e4) var(--e4) var(--e1); }
.cousinage-carte__nom { display: block; font-family: var(--serif);
  font-size: var(--t-sous-titre); font-weight: var(--g-appuyee);
  min-width: 0; text-wrap: balance; }
.cousinage-carte__meta { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--e1) var(--e2); margin: 0; padding: 0 var(--e4) var(--e3);
  font-family: var(--sans); font-size: var(--t-meta); color: var(--encre-douce); }
/* Le séparateur disparaît en fin de ligne quand le rang passe à la ligne : un
   point médian orphelin en bout de rangée se lit comme une coquille. */
.cousinage-carte__separateur { color: var(--filet); }
.cousinage-carte__role { font-weight: var(--g-appuyee); color: var(--encre); }

/* ------------------------------------------------------------------
   UNE GRILLE, ET UN SEUL FILET ENTRE DEUX ZONES
   ------------------------------------------------------------------
   Chaque destination portait son propre `border-bottom` : sur une grille de
   trois colonnes et deux rangées, cela faisait six traits qui ne se rejoignent
   pas, plus celui de la régie, plus le bord de la carte. Une superposition de
   filets — l'exact contraire de ce que « des filets, pas des boîtes » veut
   dire : le filet vaut parce qu'il est RARE.

   Les liens se tiennent maintenant par la gouttière et la cible tactile. Il ne
   reste qu'un seul trait sur toute la carte, celui qui sépare ce qu'on LIT de
   ce qu'on ADMINISTRE — et il ne paraît qu'au régisseur. */
.cousinage-carte__destinations { display: grid; gap: var(--e2);
  grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  padding: var(--e2) var(--e4) var(--e4); }
/* Des CASES, comme partout où l'on choisit une destination — la rangée des
   registres d'« À la une », les dossiers de l'album. Les trois liens bleus
   nus, à côté des cases de la régie, se lisaient comme un oubli
   d'harmonisation : deux familles de gestes sur la même carte, sans raison
   lisible. La distinction qui compte — lire ou administrer — est portée par
   le filet et par la voix grise des cases de régie, pas par la forme. */
.cousinage-carte__destinations a { display: flex; align-items: center;
  justify-content: flex-start; min-height: 40px; padding: 0 var(--e3);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  font-size: var(--t-meta); color: var(--encre); text-decoration: none;
  background: var(--surface); }
.cousinage-carte__destinations a:hover { border-color: var(--lien);
  background: var(--filet-clair); }

/* Administrer sa famille n'est pas la même chose que la lire : UN filet le
   dit, et ce bloc ne paraît qu'au régisseur. */
/* La régie DANS une carte de cousinage ne pose ni cadre ni fond : la carte en
   a déjà un. Poser les deux donnait un rectangle dont le bord supérieur était
   d'une couleur et les trois autres d'une seconde, avec le texte collé au
   cadre — le rembourrage de `.regie` étant écrasé par cette règle-ci, déclarée
   plus bas. Un seul filet, et rien d'autre. */
/* Le rembourrage BAS manquait : les cases de régie touchaient le bord de la
   carte, et le seul filet de la carte se collait aux cases du dessus — on
   lisait « des séparateurs sous les liens » là où il n'y a qu'une frontière. */
.cousinage-carte .regie { margin: var(--e1) var(--e4) 0; padding: var(--e3) 0 var(--e4);
  border: 0; border-top: 1px solid var(--filet-clair);
  background: none; border-radius: 0; }
.cousinage-carte__regie .cousinage-carte__destinations { padding: 0; }
.cousinage-carte__rubrique { font-size: var(--t-micro); color: var(--encre-douce); }

/* ==================================================================
   LE MÉDAILLON
   ==================================================================
   Le rond appartient au portrait de famille depuis deux siècles : c'est la
   bonne forme, et elle reste. Ce qui manquait, c'est de décider CE QUI ENTRE
   DEDANS — le produit prenait le centre géométrique et l'on découvrait le
   menton coupé après avoir enregistré.

   `touch-action: none` est la déclaration sans laquelle rien ne marche sur un
   téléphone : sans elle, le navigateur interprète le glissement comme un
   défilement de page et les `pointermove` cessent d'arriver. */
.medaillon { display: flex; flex-wrap: wrap; gap: var(--e5); align-items: center;
  margin: 0 0 var(--e6); }
/* ------------------------------------------------------------------
   LE MÉDAILLON EST LE GESTE, ET IL LE DIT SANS UN MOT
   ------------------------------------------------------------------
   Il y avait à côté de lui un pavé de dépôt — intitulé, aide de deux lignes,
   bouton, champ — soit quatre objets pour dire ce que le rond dit déjà. On
   clique sur son visage pour le changer ; cela n'a pas besoin d'être expliqué,
   seulement signalé.

   Le curseur et l'anneau au survol sont ce signalement. Pas d'icône d'appareil
   photo posée dessus : elle masquerait justement le visage qu'on vient
   reconnaître. */
.medaillon__cercle { cursor: pointer;
  transition: box-shadow var(--duree); }
.medaillon__cercle:hover { box-shadow: 0 0 0 3px var(--filet); }
.medaillon__cercle:focus-within { outline: 2px solid var(--lien); outline-offset: 3px; }
.medaillon__legende { margin: var(--e2) 0 0; font-family: var(--sans);
  font-size: var(--t-meta); color: var(--encre-douce); }

.medaillon__cercle { position: relative; flex: 0 0 auto;
  width: 9rem; height: 9rem; border-radius: var(--r-rond); overflow: hidden;
  background: var(--filet-clair); border: 1px solid var(--filet);
  cursor: grab; touch-action: none; user-select: none;
  display: flex; align-items: center; justify-content: center; }
.medaillon__cercle:active { cursor: grabbing; }
.medaillon__actuel { width: 100%; height: 100%; object-fit: cover; }
/* Le grand rond du profil porte la MÊME teinte que les médaillons de
   l'annuaire, de l'arbre et du fil. Il était gris avec des initiales en encre
   douce : sur l'écran où l'on vient poser son visage, c'était un cercle éteint
   là où il fallait montrer ce que le produit fait déjà en attendant la photo.
   Le corps des lettres est posé par le gabarit, qui seul sait combien il y en
   a — le cercle coupe ce qui dépasse. */
.medaillon__cercle--initiales { background: hsl(var(--teinte, 0) 24% 84%); }
.medaillon__vide { font-family: var(--sans); font-weight: var(--g-forte);
  letter-spacing: .02em; line-height: 1; color: #1A1814; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) .medaillon__cercle--initiales {
    background: hsl(var(--teinte, 0) 16% 26%); }
  :root:not([data-theme="clair"]) .medaillon__vide { color: #E8E4DA; }
}
:root[data-theme="sombre"] .medaillon__cercle--initiales {
  background: hsl(var(--teinte, 0) 16% 26%); }
:root[data-theme="sombre"] .medaillon__vide { color: #E8E4DA; }
/* Positionnée au centre puis translatée : c'est le seul montage où le
   déplacement se dit en pixels sans dépendre de la taille de l'image. */
.medaillon__image { position: absolute; top: 50%; left: 50%;
  max-width: none; pointer-events: none; }
.medaillon__cote { flex: 1 1 14rem; min-width: 0;
  display: flex; flex-direction: column; gap: var(--e3); }
.depot--medaillon { padding: var(--e3) var(--e4); }
.medaillon__reglages { display: flex; flex-direction: column; gap: var(--e1); }
.medaillon__reglages input[type="range"] { width: 100%; accent-color: var(--lien); }
.medaillon__reglages p { margin: 0; }

/* ==================================================================
   LA MATRICE DE VISIBILITÉ
   ==================================================================
   Une ligne par rubrique, trois choix par ligne. Des boutons radio et non un
   menu déroulant : à trois valeurs, le déroulant cache l'information qu'il
   devait donner, et l'on ne voit plus d'un coup d'œil ce qu'on a réglé.

   Sur téléphone, les trois choix passent sous l'intitulé plutôt que de se
   serrer à côté : trois cibles de trois centimètres ne se touchent pas. */
/* L'intertitre de famille. Le nom du cousinage figurait sous CHAQUE ligne —
   quarante fois par écran, en gris, pour une information qui ne change qu'aux
   frontières de groupe. Dit une fois, il redevient un renseignement. */
/* ------------------------------------------------------------------
   LA CASE D'UNE FICHE CONFIDENTIELLE
   ------------------------------------------------------------------
   Elle reste sur l'arbre, à sa place, avec ses liens : c'est ce qui fait tenir
   la généalogie. Elle est grisée et ne porte plus rien. Le trait discontinu dit
   « il y a quelqu'un ici, et l'on n'en saura pas plus » — un rectangle plein
   vide se lirait comme un défaut d'affichage. */
.noeud--anonyme { border-style: dashed; color: var(--encre-douce);
  background: var(--filet-clair); }
.noeud--anonyme .noeud__nom { font-style: italic; font-size: var(--t-meta);
  font-family: var(--sans); }
.portrait--anonyme { background: var(--filet-clair);
  border: 1px dashed var(--filet); }

.intertitre-famille { display: flex; align-items: center; gap: var(--e2);
  margin: var(--e6) 0 var(--e1); font-family: var(--sans); font-size: var(--t-onglet);
  font-weight: var(--g-forte); color: var(--encre-douce); }
.intertitre-famille:first-of-type { margin: var(--e2); }
.intertitre-famille .origine { min-height: 1rem; height: 1rem; }

.matrice { list-style: none; margin: 0 0 var(--e5); padding: 0; }
/* AUCUN filet entre les rubriques : elles sont DIX-NEUF, et dix-neuf traits
   identiques empilés font un tableur. Le choix retenu se voit déjà — cadre,
   fond et graisse sur la pastille cochée — et la gouttière tient les lignes.
   « Le filet vaut parce qu'il est rare » : la règle avait été appliquée aux
   destinations d'un cousinage et s'était arrêtée une classe trop tôt. */
.matrice__ligne { display: flex; flex-wrap: wrap; gap: var(--e1) var(--e4);
  align-items: center; justify-content: space-between; padding: var(--e2) 0; }
.matrice__intitule { flex: 1 1 11rem; min-width: 0; font-weight: var(--g-appuyee);
  font-size: var(--t-meta); }
.matrice__choix { display: flex; flex-wrap: wrap; gap: var(--e0); }
/* 40 px passait WCAG (24) mais restait sous le jeton maison `--cible`. Cowork :
   « c'est un peu court pour un pouce âgé sur mobile — le dernier demi-centimètre
   qui manque à un écran désormais excellent ». Le jeton existait, il n'était pas
   appliqué ici : c'est exactement le défaut déjà réparé sur le médaillon. */
.matrice__niveau { display: inline-flex; align-items: center; gap: var(--e1);
  min-height: var(--cible); padding: 0 var(--e2); border-radius: var(--r-moyen);
  font-size: var(--t-onglet); font-weight: var(--g-normale); cursor: pointer;
  border: 1px solid transparent; }
.matrice__niveau:hover { background: var(--filet-clair); }
.matrice__niveau input { width: 18px; height: 18px; margin: 0; flex: 0 0 auto;
  accent-color: var(--lien); }
/* Le choix retenu se voit sans qu'on ait à lire le petit rond. */
.matrice__niveau:has(input:checked) { border-color: var(--lien);
  background: var(--filet-clair); font-weight: var(--g-forte); }

.ma-fiche { gap: var(--e4); margin: 0 0 var(--e7); }

.fiche-entete { display: flex; gap: var(--e4); align-items: center; margin: 0 0 var(--e4); }
.fiche-entete h1 { margin: 0; }
.fiche-entete__dessous { margin: var(--e0) 0 0; }

/* ------------------------------------------------------------------
   TROIS ÉTAPES DITES EN TÊTE, PLUTÔT QU'UNE EXPLICATION SOUS CHAQUE CHAMP
   ------------------------------------------------------------------
   Le formulaire d'invitation posait sept champs à la suite, chacun avec sa
   petite phrase. On ne savait ni ce qu'il fallait vraiment remplir, ni ce qui
   se passait ensuite. Les étapes se disent maintenant une fois, en tête. */
.marche { counter-reset: marche; list-style: none; margin: 0 0 var(--e6);
  padding: 0; }
.marche li { counter-increment: marche; position: relative;
  padding: 0 0 var(--e2) var(--e7); font-size: var(--t-meta); color: var(--encre-douce); }
.marche li::before {
  content: counter(marche); position: absolute; left: 0; top: 0;
  width: 1.5rem; height: 1.5rem; border-radius: var(--r-rond);
  display: flex; align-items: center; justify-content: center;
  background: var(--filet-clair); color: var(--encre);
  font-family: var(--sans); font-size: var(--t-onglet); font-weight: var(--g-forte);
}
.marche strong { color: var(--encre); }

/* Un formulaire long se lit par blocs. Le filet et le titre disent où l'on en
   est, et surtout qu'on peut s'arrêter de lire ce qui ne concerne pas le
   champ qu'on remplit. */
.bloc { border: 0; border-top: 1px solid var(--filet); margin: 0 0 var(--e6);
  padding: var(--e4) 0 0; display: flex; flex-direction: column; gap: var(--e2); }
.bloc legend { font-family: var(--sans); font-size: var(--t-onglet);
   color: var(--encre-douce);
  padding: 0; }

/* ==================================================================
   UNE ACTION EST UN BOUTON, PAS UNE PHRASE SOULIGNÉE
   ==================================================================
   Le produit posait ses gestes en liens de texte : « Mon courrier »,
   « Les invitations », « Ajouter quelqu'un autour de Foulques ». Un nom de
   personne en bleu se comprend — on clique sur les gens depuis trente ans.
   Une PHRASE en bleu, non : beaucoup de lecteurs ne l'essaient jamais.

   D'où trois formes, et pas une de plus :

     `.action`            le geste principal d'un écran. Plein, on ne le rate pas.
     `.action--seconde`   le même geste, en second rang : bordé, pas plein.
     un lien nu           pour NAVIGUER vers une page, et pour rien d'autre.

   La règle : si le clic FAIT quelque chose, c'est un bouton. S'il emmène
   quelque part, c'est un lien.

   Ces trois formes sont déclarées EN HAUT DE CETTE FEUILLE, en un seul bloc
   qui couvre aussi `button`, `input[type="submit"]` et les anciens noms restés
   dans les vues. Rien n'est redéclaré ici : c'était précisément la source de
   la divergence. */

/* ------------------------------------------------------------------
   UN GESTE QUI DÉFAIT NE TOUCHE PAS UN GESTE ORDINAIRE
   ------------------------------------------------------------------
   « Ajouter à la liste » et « Annuler l'événement » se suivaient, à la même
   largeur, sans rien entre eux. Deux boutons accolés se lisent comme une
   paire — or l'un s'emploie dix fois par soirée et l'autre défait tout.

   Le filet et la marge disent qu'on change de registre. La couleur d'alerte
   ne sert qu'ici : ailleurs dans le produit, elle ne dit que des erreurs. */
.geste-grave { margin: var(--e7) 0 0; padding: var(--e5) 0 0;
  border-top: 1px solid var(--filet); }
.geste-grave .secondaire { margin: var(--e2) 0 0; }
.action--grave { background: none; color: var(--alerte);
  border-color: var(--alerte); }
.action--grave:hover { background: var(--alerte); color: var(--papier);
  border-color: var(--alerte); }

/* ------------------------------------------------------------------
   PLUSIEURS GESTES DE SUITE
   ------------------------------------------------------------------
   Ils étaient empilés sous 26 rem — soit 416 px, un seuil qui COUPE LA GAMME
   DES TÉLÉPHONES EN DEUX : un iPhone 15 (393 px) empilait, un iPhone Plus
   (430 px) non. Un seuil de mise en page ne doit pas traverser une gamme
   d'appareils.

   `flex: 1 1 12rem` fait le travail à toute largeur, et sans requête de
   média : les boutons se mettent côte à côte quand ils tiennent, s'empilent
   sinon, et — c'est le point — ils ont alors LA MÊME LARGEUR. Sur la page
   Famille, « Ajouter un membre de la famille » et « Inviter quelqu'un »
   s'empilaient chacun à la largeur de son texte : deux rectangles inégaux,
   l'un plein, l'autre bordé, qui ne se lisaient pas comme une paire. */
.actions { display: flex; flex-wrap: wrap; gap: var(--e2); margin: var(--e4) 0 var(--e6); }
.actions > * { flex: 1 1 12rem; }

/* Des filets, pas des boîtes : c'est là que se gagne la densité, pas en
   rétrécissant le texte. */
.liste { list-style: none; margin: 0; padding: 0; }
.liste__ligne { display: flex; gap: var(--e3); align-items: flex-start;
  padding: var(--e4) 0; border-bottom: 1px solid var(--filet-clair); }

/* ------------------------------------------------------------------
   LE CORPS D'UNE LIGNE, ET LE `min-width: 0` QUI MANQUAIT PARTOUT
   ------------------------------------------------------------------
   Cinquante-trois vues portaient `style="flex:1"` en attribut, sans jamais le
   `min-width: 0` qui va avec. Un enfant de boîte flexible a `min-width: auto` :
   il REFUSE de descendre sous la taille de son contenu minimal. Un jeton
   insécable — une adresse électronique, une URL, « Marie-Alix de
   Kervenec-Trégarvan » — poussait donc la rangée au-delà de la page, et comme
   rien ne pose `overflow-x: hidden`, c'est le DOCUMENT ENTIER qui devenait
   défilable en largeur. Tous les autres écrans paraissaient alors décalés,
   sans qu'on voie d'où cela venait.

   Une classe plutôt qu'un attribut : c'est aussi la seule forme qui survivra
   à une politique de sécurité du contenu. */
/* ------------------------------------------------------------------
   UNE LIGNE DE LISTE PORTE DES GESTES, ET EN PORTRAIT ILS NE TIENNENT PAS
   ------------------------------------------------------------------
   La rangée était une boîte flexible qui ne se replie pas. Sur la liste des
   invitations — nom, adresse, état, puis « Relancer », « Retirer » et un repli
   — les trois gestes prenaient la largeur qu'ils voulaient et le texte se
   comprimait sur ce qui restait : deux caractères par ligne en portrait, une
   colonne de lettres. En paysage, la même page tenait très bien, ce qui est la
   signature exacte de ce défaut.

   `flex-wrap` avec une BASE de 14 rem, et non un seuil de média : le corps
   garde sa ligne tant qu'il a 14 rem, et passe seul au-dessus dès qu'il ne les
   a plus. Aucune largeur d'écran n'est nommée, donc rien ne coupe la gamme des
   téléphones en deux — c'est la leçon déjà payée deux fois dans cette feuille,
   sur les onglets et sur les actions.

   Les gestes se rangent alors à DROITE de leur nouvelle ligne : c'est là que
   l'œil les cherche après avoir lu, et c'est ce que font toutes les listes
   d'action depuis quinze ans. */
.liste__ligne { flex-wrap: wrap; }
.liste__corps { flex: 1 1 14rem; min-width: 0; overflow-wrap: anywhere; }
.liste__ligne > * { min-width: 0; }
.liste__ligne > form, .liste__ligne > .bouton-discret { flex: 0 0 auto; }
.liste__ligne > form:first-of-type { margin-left: auto; }

/* Un repli logé dans une ligne — « Elle est déjà dans l'arbre » sur une
   invitation — n'est pas un geste de plus à côté des autres : il OUVRE un
   formulaire, et un formulaire dans une colonne de six centimètres ne se
   remplit pas. Il prend donc sa propre ligne, sur toute la largeur. */
.liste__ligne > details { flex: 1 1 100%; margin-top: var(--e2); }

/* ------------------------------------------------------------------
   L'ÉTIQUETTE DE NATURE
   ------------------------------------------------------------------
   Elle DIT le droit plutôt que de le laisser deviner. Un groupe de famille
   existe sans nous et seul le régisseur le retire ; un groupe ouvert par un
   cousin, on l'a rejoint ; le sien, on en répond. Rien ne les distinguait. */
/* Un événement annulé : l'étiquette porte la seule couleur d'alerte de cet
   écran, parce que c'est la seule information qui change ce qu'on va faire. */
.etiquette--annule { display: inline-block; margin: 0 0 var(--e1);
  padding: var(--e0) var(--e2); border-radius: var(--r-petit); font-size: var(--t-micro);
  border: 1px solid var(--alerte); color: var(--alerte); }

/* ------------------------------------------------------------------
   LE FILET D'ORIGINE — UN FILET DE MARGE, PLUS UN POINT
   ------------------------------------------------------------------
   Il porte la seule couleur forte du produit, et une information que rien
   d'autre ne porte : de quelle famille vient cette ligne.

   Il était rond, de dix pixels, posé en tête de chaque ligne — donc lu comme
   une PUCE. Sur un écran qui empile des listes, cela donnait l'aspect d'une
   présentation à diapositives, ce que ce produit doit être en dernier.

   Un filet vertical dit la même chose et appartient au vocabulaire de
   l'imprimé : c'est le filet de marge d'un journal, cohérent avec « le papier,
   pas l'écran ». Il ne se confond avec aucune énumération. */
.origine { flex: 0 0 auto; width: 3px; border-radius: var(--r-petit); align-self: stretch;
  min-height: 1.6rem; }

.annonces { padding: var(--e3) var(--e5); }
.annonce { margin: 0 0 var(--e2); padding: var(--e3) var(--e4); font-size: var(--t-meta);
  border-left: 3px solid var(--lien); background: var(--filet-clair); }
.annonce--alert { border-left-color: var(--alerte); }
.annonce--notice { border-left-color: var(--succes); }

/* Barre d'onglets : 64 px, icône ET libellé texte permanent, jamais tronqué,
   jamais masqué au défilement. Cinq entrées, pas sept modules égaux. */
.onglets {
  position: fixed; bottom: 0; left: 0; right: 0;
  /* EXPLICITE, et au-dessus de tout : sans z-index elle dépendait de l'ordre du
     document, et la légende de la carte s'est déjà peinte par-dessus — on ne
     pouvait alors plus quitter l'écran de la carte. */
  z-index: var(--plan-barre);
  /* Six colonnes depuis que la carte est une destination. À 390 px cela donne
     65 px par onglet : le libellé tient encore, et la cible reste au-dessus du
     plancher tactile. Une septième ne passerait pas. */
  display: grid; grid-template-columns: repeat(6, 1fr);
  /* La hauteur INCLUT le rembourrage, `box-sizing: border-box` étant posé
     partout : `height: 64px` avec 34 px de zone sûre ne laissait que 30 px de
     boîte de contenu, et les six onglets de 48 px débordaient de dix-huit
     pixels dans la zone de l'indicateur d'accueil. La zone sûre s'AJOUTE. */
  /* ------------------------------------------------------------------
     UNE HAUTEUR FIXE COUPE LE TIERS INFÉRIEUR, ET C'EST UN CLASSIQUE
     ------------------------------------------------------------------
     C'était `height`. À six colonnes sur 390 px, chaque onglet dispose de
     65 px : « À la une » et « Messages » passent sur DEUX lignes, l'icône plus
     deux lignes dépassent les 64 px, et la boîte étant fixe, le débordement se
     fait couper net. On perd le bas des libellés — le tiers inférieur de la
     barre — et l'application a l'air cassée là où elle est le plus regardée.

     Le réglage « très grand » du confort de lecture le déclenche partout,
     puisqu'il agrandit précisément ces libellés. C'est-à-dire chez le public
     que ce produit vise nommément.

     `min-height` laisse la barre grandir de ce qu'il faut. La réserve du
     document suit la MÊME expression, portée par un jeton, pour que les deux
     ne puissent plus diverger. */
  min-height: var(--barre-basse);
  box-sizing: border-box;
  background: var(--surface);
  border-top: 1px solid var(--filet);
  /* EN BAS seulement : en raccourci, les 34 px d'un iPhone à encoche se
     posaient aussi à gauche, à droite et EN HAUT de la barre — six onglets
     comprimés dans ce qui restait. */
  padding-bottom: env(safe-area-inset-bottom);
}
.onglet {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--e0); min-height: var(--cible); text-decoration: none;
  color: var(--encre-douce); font-weight: var(--g-forte);
  /* `clamp` plutôt qu'un seuil : continu de 375 à 430 px, et la borne haute est
     un jeton, donc le libellé suit le réglage de confort de lecture. Un
     `font-size` en pixels sous une requête de média ne le suivait pas. */
  font-size: clamp(.6875rem, 2.6vw, var(--t-onglet));
  /* À six colonnes sur 390 px, « Messages » et « À la une » débordaient. */
  text-align: center; line-height: 1.1; padding: 0 var(--e0);
}
/* ------------------------------------------------------------------
   ONZE PIXELS EN DUR, ET UN SEUIL QUI COUPAIT LA GAMME EN DEUX
   ------------------------------------------------------------------
   `max-width: 26rem` vaut 416 px : le seuil passait ENTRE le Pixel (412) et
   l'iPhone Pro Max (430). Deux téléphones voisins, deux tailles de libellé.
   C'est mot pour mot le défaut déjà retiré des `.actions`.

   Pire, la valeur était en PIXELS : le réglage « très grand » n'agrandissait
   pas les libellés de navigation sur tout téléphone de moins de 416 px —
   c'est-à-dire sur celui de presque tout le monde, et pour le public de
   60-74 ans que le produit vise nommément. `clamp` ci-dessus le remplace, sans
   aucun seuil. La requête est neutralisée plutôt que supprimée : ce qu'elle
   contient encore reste à relire une par une. */
@media (max-width: 0px) { .onglet { font-size: var(--t-micro); } }
.onglet--actif { color: var(--encre); }

/* L'icône prend la couleur du texte de son onglet : c'est ce qui fait que
   l'onglet actif se lit d'un coup, sans seconde couleur à inventer. */
.onglet__icone { width: 22px; height: 22px; flex: 0 0 auto; }

/* La marque ne sert qu'à la colonne de l'écran large : sur un téléphone, la
   barre a cinq places et pas une de plus. */
.onglets__marque { display: none; }

/* Le pied de la colonne n'existe QUE sur écran large : la barre du téléphone a
   cinq places et pas une de plus. Sur téléphone, les deux gestes se trouvent
   en tête de l'annuaire et de l'arbre. */
.onglets__pied { display: none; }

/* ==================================================================
   L'ÉCRAN LARGE
   ==================================================================
   Tout ce qui précède est dessiné pour un téléphone, et c'est le bon ordre :
   c'est là que l'application se vit. Mais rendu tel quel sur un portable, le
   produit devient une colonne de six cent quarante pixels au milieu du vide,
   surmontée d'une barre d'onglets de deux mille pixels portant cinq libellés
   minuscules. C'est exactement ce qui fait dire « application de téléphone mal
   agrandie », et cela ne tenait qu'à l'absence de ces quelques règles.

   Le seuil est à 60 rem — 960 px au corps par défaut. En dessous, une colonne
   latérale mangerait la largeur de lecture ; au-dessus, elle la libère.

   TROIS CHOSES CHANGENT, ET RIEN DE PLUS :
     la barre du bas devient une COLONNE à gauche, avec la marque en tête ;
     la page se recentre dans ce qui reste, sans s'élargir beaucoup — une
       ligne de plus de soixante-dix signes cesse de se lire confortablement,
       et ce produit est fait pour être LU ;
     les marges s'ouvrent, parce qu'un écran large qui garde des marges de
       téléphone a l'air à l'étroit sans qu'on sache pourquoi.

   Aucun balisage ne change : c'est la même barre, retournée. */
/* ------------------------------------------------------------------
   LE PALIER DE TABLETTE — TRENTE-DEUX CENTIMÈTRES DE VIDE
   ------------------------------------------------------------------
   Entre 40 rem (la largeur maximale de la colonne de lecture) et 60 rem (le
   passage de la barre en colonne latérale), soit de 640 à 960 px — tablette en
   portrait, fenêtre de portable à moitié —, la page restait centrée à 640 px
   pendant que la barre d'onglets FIXE s'étirait sur toute la largeur : six
   libellés de 13 px perdus dans un mètre de vide, en bas d'un écran d'ordinateur.

   C'est mot pour mot le défaut que le commentaire de la barre dit vouloir
   corriger — « une barre de téléphone étirée sur un écran de portable » — et le
   seuil à 60 rem le laissait vivre sur trois cent vingt pixels de largeurs.

   La barre se recentre donc, à la largeur de la colonne qu'elle sert, et prend
   les coins arrondis d'un objet posé plutôt que d'un bord d'écran. */
/* 40 rem et non 44 : la colonne de lecture s'arrête à 40 rem, et entre 640 et
   704 px la barre s'étirait encore sur toute la largeur au-dessus d'une page
   centrée — exactement ce que cette règle existe pour empêcher. */
@media (min-width: 40rem) and (max-width: 59.99rem) {
  .onglets {
    left: 50%; right: auto; transform: translateX(-50%);
    width: min(100%, 40rem);
    border-left: 1px solid var(--filet);
    border-right: 1px solid var(--filet);
    border-radius: var(--r-moyen) var(--r-moyen) 0 0;
  }
}

@media (min-width: 60rem) {
  /* ------------------------------------------------------------------
     `padding` ET NON `padding-left` : DEUX CENT QUARANTE PIXELS DE VIDE
     ------------------------------------------------------------------
     La barre devenue colonne est en position fixe : il faut donc décaler le
     CORPS de sa largeur, sans quoi le texte passe dessous. C'est ce que fait
     `15rem`, qui est exactement la largeur de `.onglets`.

     Écrit en raccourci à une valeur, il posait AUSSI 240 px en haut, à droite
     et en bas. Horizontalement cela tombait juste par accident — la valeur
     égale la largeur du rail — et c'est ce qui a fait passer la faute :
     l'écran paraissait correct, et chaque page s'ouvrait pourtant sur une
     bande vide de deux cent quarante pixels avant son premier mot. */
  .avec-barre { padding: 0 0 0 15rem; }

  .onglets {
    top: 0; bottom: 0; right: auto; width: 15rem; height: auto;
    grid-template-columns: 1fr; grid-auto-rows: max-content;
    align-content: start; gap: var(--e0);
    /* .75rem sur les côtés : la colonne est étroite, et 1.5rem de chaque bord
       lui retiraient un quart de sa largeur utile — d'où « Ajouter un membre »
       cassé sur trois lignes. */
    /* Le bas réserve la ligne du médaillon, qui se pose par-dessus le rail :
       sans quoi il couvrirait le dernier onglet sur un écran court. C'est la
       même règle que la bande du haut sur téléphone — un élément en position
       fixe posé sur du contenu finit toujours par tomber sur quelque chose. */
    padding: var(--e6) var(--e3) 4.5rem;
    /* ------------------------------------------------------------------
       RIEN NE DÉBORDE DU FILET
       ------------------------------------------------------------------
       Les fonds des onglets — celui de l'onglet actif comme celui du survol —
       passaient PAR-DESSUS le trait qui sépare la colonne du contenu, et le
       bouton « Ajouter un membre » avec eux. Un filet qu'un fond traverse ne
       sépare plus rien : il devient un trait posé au milieu d'un bloc.

       `overflow: clip` plutôt que `hidden` : on ne veut pas créer de contexte
       de défilement dans une colonne qui ne défile pas. Et `min-width: 0` sur
       les enfants, sans quoi une grille refuse de les comprimer sous la
       largeur de leur contenu — c'est la cause réelle du débordement. */
    overflow: clip;
    border-top: none; border-right: 1px solid var(--filet);
    background: var(--papier);
  }

  .onglets__pied { display: block; }

  .onglets__marque {
    display: flex; align-items: center; gap: var(--e2);
    margin: 0 0 var(--e6); padding: 0 var(--e3);
    font-family: var(--serif); font-size: var(--t-sous-titre); color: var(--encre);
  }


  /* En ligne plutôt qu'empilé : à la verticale, une icône au-dessus de son
     libellé donne cinq blocs hauts qu'on parcourt mal du regard. */
  .onglet {
    flex-direction: row; justify-content: flex-start; gap: var(--e3);
    min-height: 42px; padding: 0 var(--e3); border-radius: var(--r-petit);
    font-size: var(--t-corps); font-weight: var(--g-appuyee);
  }
  /* ------------------------------------------------------------------
     LE SURVOL ET L'ONGLET ACTIF NE PEUVENT PAS SE RESSEMBLER
     ------------------------------------------------------------------
     Ils portaient le MÊME fond. Sur une capture d'écran du carnet, « Le
     carnet » et « Carte » paraissaient allumés tous les deux — la souris
     passait simplement sur le second — et l'on ne savait plus sur quel écran
     on se trouvait. C'est la question à laquelle une barre de navigation sert
     à répondre, et elle y répondait faux.

     L'actif porte donc un RAIL, que le survol n'a pas : une différence de
     forme se voit du coin de l'œil, une différence de fond de 4 % ne se voit
     pas du tout. */
  /* Le survol est BEAUCOUP plus faible que la sélection : ils portaient le même
     fond, et sur une capture d'écran du carnet deux entrées paraissaient
     allumées — la souris passait simplement sur la seconde. On ne savait plus
     sur quel écran on se trouvait, ce qui est la seule question à laquelle une
     barre de navigation sert à répondre. */
  .onglet:hover { background: color-mix(in srgb, var(--filet-clair) 45%, transparent);
    color: var(--encre); }
  .onglet--actif, .onglet--actif:hover {
    background: var(--filet-clair); font-weight: var(--g-forte);
    box-shadow: inset 3px 0 0 var(--encre);
  }

  /* La colonne de lecture se cale APRÈS la barre, elle ne se recentre pas
     dans ce qui reste : centrée, elle laissait un vide de deux cent cinquante
     pixels contre le filet et l'on avait l'impression d'un contenu poussé de
     côté. Une gouttière franche dit « voici la barre, voici le texte ». */
  .avec-barre .page { max-width: 46rem; margin: 0; padding: var(--e7) var(--e7) var(--e8) var(--e8); }
  .avec-barre .annonces { max-width: 46rem; margin: 0; padding: var(--e5) var(--e7) 0 var(--e8); }

  /* Les écrans SANS barre — connexion, inscription, mentions légales,
     récupération — gardent une colonne centrée : ce sont des pages qu'on lit
     seul, pas des écrans qu'on habite. */
  .page { max-width: 46rem; padding: var(--e8) var(--e7) var(--e8); }

  /* ------------------------------------------------------------------
     PAS DEUX FOIS LE MÊME GESTE SUR LE MÊME ÉCRAN
     ------------------------------------------------------------------
     Sur écran large, les deux gestes vivent au bas de la colonne — visibles
     de PARTOUT, ce qui est tout l'objet. Les répéter en tête de l'annuaire et
     de l'arbre ferait quatre boutons pour deux actions, et un écran qui
     propose deux fois la même chose fait douter qu'il s'agisse de la même. */
  .actions--dans-la-page { display: none; }

  /* Le pied de la colonne : les deux gestes qui font grandir la famille,
     séparés des destinations par un filet — ce ne sont pas des endroits où
     aller, ce sont des choses à faire. */
  /* `margin: 1.5rem` PLUS `padding: 1.25rem` retiraient 88 px des 240 du rail :
     il restait 152 px pour un libellé et son icône, et « Ajouter un membre »
     se cassait sur TROIS lignes. Le filet suffit à séparer ; il n'a pas besoin
     d'être encadré de blanc des deux côtés. */
  .onglets__pied {
    margin: var(--e6) var(--e3) 0; padding: var(--e5) 0 0;
    border-top: 1px solid var(--filet);
  }
  .onglets__pied .actions { flex-direction: column; align-items: stretch;
    gap: var(--e0); margin: 0; }
  /* `flex: 1 1 12rem` posait une base de 192 px que le rail ne peut pas tenir :
     l'élément rétrécissait et le texte se cassait. Dans la colonne, la base
     est la largeur disponible, et le libellé ne se coupe plus. */
  .onglets__pied .actions > * { flex: 0 0 auto; min-width: 0; max-width: 100%; }
  .onglets > * { min-width: 0; max-width: 100%; }

  /* ------------------------------------------------------------------
     DANS LA COLONNE, UN GESTE GARDE LE RYTHME DES DESTINATIONS
     ------------------------------------------------------------------
     Le bouton plein y devenait, en thème sombre, la chose la plus CLAIRE de
     l'écran : un pavé pâle qui écrasait les cinq onglets au-dessus de lui.
     Une action n'a pas à crier plus fort que la navigation qui l'entoure.

     Ils reprennent donc la ligne exacte d'un onglet — même hauteur, même
     graisse, même retrait — et ne s'en distinguent que par le filet qui les
     sépare, et par leur place : tout en bas. */
  .onglets__pied .action,
  .onglets__pied .action--seconde {
    width: 100%; justify-content: flex-start; gap: var(--e3);
    min-height: 42px; padding: 0 var(--e3); border-radius: var(--r-petit);
    font-family: var(--sans); font-size: var(--t-corps); font-weight: var(--g-appuyee);
    /* Le libellé s'ADAPTE : `white-space: nowrap` le forçait sur une ligne, et
       « Ajouter un membre » débordait alors du cadre du bouton — les lettres
       passaient par-dessus le filet, puis par-dessus le trait du rail. Un
       bouton dont le texte sort de sa boîte est le défaut le plus visible qui
       soit ; mieux vaut deux lignes qu'un débordement. */
    white-space: normal; text-align: left; line-height: 1.25;
    background: none; color: var(--encre-douce); border: 0;
  }
  .onglets__pied .action svg,
  .onglets__pied .action--seconde svg {
    width: 20px; height: 20px; flex: 0 0 20px; }

  /* ------------------------------------------------------------------
     UNE ACTION N'EST PAS UNE DESTINATION
     ------------------------------------------------------------------
     Les deux gestes reprenaient TRAIT POUR TRAIT la ligne d'un onglet : même
     hauteur, même graisse, même retrait, même absence de fond. Rien ne disait
     donc qu'« Ajouter un membre » FAIT quelque chose là où « Famille » MÈNE
     quelque part — et posés sous un filet, en gris, ils avaient l'air de deux
     onglets désactivés.

     Le premier prend donc un contour : c'est le seul indice dont on ait
     besoin, et il ne crie pas plus fort que la navigation au-dessus. Le
     second reste un lien, parce qu'inviter vient APRÈS avoir ajouté. */
  .onglets__pied .action {
    border: 1px solid var(--filet); color: var(--encre); font-weight: var(--g-appuyee);
  }
  .onglets__pied .action:hover {
    background: var(--filet-clair); border-color: var(--encre-douce); }
  .onglets__pied .action--seconde:hover {
    background: var(--filet-clair); color: var(--encre); border: 0; }

  /* Le fil de discussion occupe la hauteur : c'est le seul écran qu'on
     consulte en restant dessus, et une bulle de message qui s'arrête à
     mi-écran sur un portable a l'air cassée. */
  .message { max-width: 75%; }
}

/* Au-delà, on cesse d'élargir la colonne de lecture et l'on rend l'espace aux
   marges. Une largeur qui suit l'écran sans borne produit des lignes qu'on
   perd en revenant à la ligne — le défaut le plus banal du web, et le plus
   fatigant pour qui lit longtemps. */
@media (min-width: 90rem) {
  /* LE MÊME RACCOURCI, UNE SECONDE FOIS. `padding: 17rem` posait 272 px sur les
     QUATRE côtés du corps de page ; corriger le palier de 60 rem ne servait à
     rien puisque celui-ci le réécrasait dès 1440 px — c'est-à-dire sur la
     plupart des écrans de bureau. Le décalage compense la barre en position
     fixe : il est horizontal, il s'écrit horizontalement. */
  .avec-barre { padding: 0 0 0 17rem; }
  .onglets { width: 17rem; padding: var(--e6) var(--e3); }
}
/* `.onglet span` posait 18 px sur tout `<span>` d'un onglet. La règle ne
   matchait RIEN — aucun onglet n'en contenait — et elle était posée sans un mot
   entre deux requêtes de média. Depuis que les onglets portent une pastille de
   non-lus, elle aurait matché : le chiffre serait passé de 11 à 18 px, et la
   pastille aurait débordé de l'icône. Une règle orpheline n'est pas inerte,
   elle attend. */

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* Le mot d'accueil : le premier contenu que lit un nouvel arrivant, et le seul
   dont on soit certain qu'il existe le jour 1. Il mérite le registre du
   faire-part, pas celui de l'interface. */
.mot-daccueil { margin: 0 0 var(--e6); padding: var(--e4) 0 var(--e4) var(--e5);
  border-left: 2px solid var(--filet); font-family: var(--serif); }
.mot-daccueil p { margin: 0 0 var(--e2); font-size: var(--t-sous-titre); line-height: 1.45; }
.mot-daccueil cite { font-style: normal; font-size: var(--t-meta);
  color: var(--encre-douce); }

/* Une case à cocher n'est jamais un carré de 16 px perdu dans une ligne : la
   zone tactile couvre le libellé entier. */
.case { display: flex; gap: var(--e3); align-items: flex-start;
  min-height: var(--cible); padding: var(--e3) 0; font-weight: var(--g-normale);
  font-size: var(--t-corps); cursor: pointer; }
.case input { width: 24px; height: 24px; margin: var(--e0); flex: 0 0 auto; }
.case .secondaire { display: block; margin: var(--e1); }

/* L'identité affichée sur deux lignes. La seconde ne descend jamais sous
   15 px et n'est jamais en gris clair : c'est elle qui distingue les cinq
   « Marie de X » vivantes, elle doit donc rester lisible. */
.identite { display: block; }
.identite__nom { display: block; }
.identite__qualificatif { display: block; font-size: var(--t-meta);
  color: var(--encre-douce); margin: 0 0 var(--e5); }

/* La conversation. Des lignes, pas des bulles empilées dans des cartes : la
   densité se gagne en retirant le chrome, pas en rétrécissant le texte. */
/* ==================================================================
   LE FIL — À LA TAILLE ET AU RYTHME D'UNE MESSAGERIE
   ==================================================================
   Il ressemblait à un terminal : des paragraphes alignés à gauche, sans bulle,
   sans pièce jointe, avec l'heure sur sa propre ligne sous chaque message.
   Ce que tout le monde connaît depuis quinze ans tient en peu de choses, et
   les voici.

   LA TAILLE D'ABORD. Un message n'est pas un article : 15 px de corps, 1.35
   d'interligne, une bulle de 6 px de rayon. Le produit lit ailleurs en 18 px —
   ici on ÉCHANGE, et la densité fait qu'on voit la conversation plutôt que
   trois phrases.

   Le fil occupe la hauteur disponible et défile seul, le composeur restant
   collé en bas : sur un téléphone, un composeur qui remonte avec le contenu
   est le défaut le plus agaçant d'une messagerie. */
/* ------------------------------------------------------------------
   LE FIL EST UN PLEIN ÉCRAN, PAS UN ARTICLE
   ------------------------------------------------------------------
   Il était posé dans le flux du document, avec un `min-height` dont
   l'arithmétique laissait quarante pixels de défilement fantôme sur toute
   conversation, même vide — et le composeur tenait par un `sticky` acrobatique.

   Trois déclarations remettent les choses d'aplomb : une HAUTEUR (et non un
   minimum) qui annule le rembourrage de `.page`, un fil qui défile SEUL, et un
   composeur ancré par la boîte flexible plutôt que collé.

   `min-height: 0` sur `.fil` est le point critique : sans lui, un enfant de
   boîte flexible ne descend jamais sous la taille de son contenu — le
   conteneur déborde au lieu de défiler, et le composeur part hors de l'écran. */
/* La marge NÉGATIVE EN BAS n'est pas un ajustement à vue. `.page` ajoute
   1,5 rem de rembourrage bas et `body` en ajoute 1 de plus après la barre :
   le document mesurait donc 100dvh + 2,5 rem, la page défilait de deux
   centimètres et l'ENTÊTE DU FIL sortait par le haut — on voyait le titre
   coupé en deux. Les 64 px de barre et la zone sûre, eux, se compensent
   exactement entre la hauteur ci-dessous et le rembourrage du corps. */
/* `--clavier` est posée par l'îlot depuis `visualViewport`. Safari iOS
   n'applique pas `interactive-widget=resizes-content` : sans elle, le composeur
   passait DERRIÈRE le clavier et l'on tapait sans voir ce qu'on écrivait. Elle
   vaut zéro partout ailleurs, donc la règle est inerte sur ordinateur. */
.fil-page { display: flex; flex-direction: column;
  /* Moins la BANDE HAUTE (3 rem) : elle est entrée dans le flux au-dessus du
     contenu, et sans la retrancher le composeur descendait d'autant sous la
     ligne de flottaison — exactement le défaut que le clavier d'iOS produisait
     et que `--clavier` corrige déjà par l'autre bout. */
  /* 4 rem et non 3 : le rem manquant est celui que `body` garde APRÈS la
     barre. Sans lui, la dernière ligne du fil — « Un message vous gêne ? » —
     passait sous la barre d'onglets, coupée en deux. */
  height: calc(100dvh - 4rem - var(--barre-basse) - var(--clavier, 0px));
  margin: -1.5rem -1.25rem -2.5rem; padding: 0 var(--e5); overflow: hidden; }

/* ------------------------------------------------------------------
   L'ENTÊTE TIENT SUR UNE LIGNE
   ------------------------------------------------------------------
   Elle prenait 145 px : un titre en romaine de 24 px que `Conversation#libelle`
   remplit avec QUATRE noms complets — donc deux lignes pleines — plus une ligne
   de cousinage, plus les marges de paragraphe du navigateur que rien ne remet
   à zéro. Sur 640 px de fil utile, c'était un sixième de l'écran consommé en
   permanence pour redire ce qu'on vient de toucher du doigt.

   Un fil de discussion s'annonce comme une messagerie : le nom sur UNE ligne,
   en linéale, tronqué, avec le retour à gauche. Le cousinage passe en dessous
   en 13 px — l'information reste, elle cesse d'être un titre. */
.fil-entete { flex: 0 0 auto; display: flex; align-items: center; gap: var(--e2);
  height: 56px; margin: 0 -1.25rem; padding: 0 var(--e4);
  border-bottom: 1px solid var(--filet); background: var(--papier); }
.fil-entete__retour { display: inline-flex; align-items: center;
  justify-content: center; width: 40px; height: 40px; margin: -.5rem;
  flex: 0 0 auto; color: var(--encre-douce); border-radius: var(--r-rond); }
.fil-entete__retour:hover { background: var(--filet-clair); color: var(--encre); }
.fil-entete__retour svg { width: 24px; height: 24px; }
.fil-entete__identite { min-width: 0; display: flex; flex-direction: column;
  justify-content: center; }
.fil-entete__nom { font-family: var(--sans); font-size: var(--t-corps); font-weight: var(--g-appuyee);
  line-height: 1.2; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.fil-entete__origine { font-size: var(--t-onglet); color: var(--encre-douce);
  line-height: 1.2; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }

.fil { flex: 1 1 auto; min-height: 0; overflow-y: auto;
  overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  list-style: none; margin: 0; padding: var(--e2) 0 var(--e4);
  display: flex; flex-direction: column; gap: var(--e0); }

/* Le composeur devient une zone de dépôt quand on survole avec un fichier. */
.fil-page--survolee .composeur { border-color: var(--lien);
  background: var(--filet-clair); }

/* Le jour, une seule fois, en pastille centrée — la forme qu'ont prise toutes
   les messageries, et qu'on reconnaît sans la lire. */
.fil__jour { align-self: center; margin: var(--e4) 0 var(--e2); padding: var(--e1) var(--e3);
  background: var(--filet-clair); border-radius: var(--r-moyen);
  font-family: var(--sans); font-size: var(--t-micro); color: var(--encre-douce); }

/* ------------------------------------------------------------------
   LA BULLE
   ------------------------------------------------------------------
   Coin carré du côté d'où l'on parle, arrondi partout ailleurs : c'est la
   pointe, en deux déclarations et sans triangle dessiné. Elle ne paraît que
   sur la PREMIÈRE d'une suite du même auteur — répétée, elle hache le fil. */
.bulle {
  /* L'alignement est passé à `.fil__rang` : la bulle n'est plus l'enfant
     direct du fil, elle est dans une rangée qui porte aussi le médaillon. */
  max-width: 78%; min-width: 0;
  padding: var(--e2) var(--e2) var(--e1); border-radius: var(--r-moyen) var(--r-moyen) var(--r-moyen) var(--r-petit);
  background: var(--surface); border: 1px solid var(--filet);
  font-size: var(--t-meta); line-height: 1.35;
  display: flex; flex-direction: column; gap: var(--e0);
  /* Sur le CONTENEUR, et non sur le seul `__corps` : le nom de l'auteur, le
     nom de fichier d'une pièce jointe et l'état d'envoi n'en avaient pas, si
     bien qu'un `IMG_20260814_cousinade-tante-Agnès.jpg` sortait de l'écran —
     et, pour une bulle à soi collée au bord droit, sortait de la page.

     `break-word` et NON `anywhere` : les deux coupent un mot trop long, mais
     `anywhere` réduit aussi la largeur MINIMALE intrinsèque de la boîte à un
     signe. La bulle se serait alors dimensionnée bien plus étroite que son
     texte, qui aurait cassé au milieu de phrases ordinaires. */
  overflow-wrap: break-word;
}
.bulle--suite { border-radius: var(--r-moyen); }
.bulle--mienne { border-radius: var(--r-moyen) var(--r-moyen) var(--r-petit) var(--r-moyen);
  background: var(--filet-clair); border-color: var(--filet); }
.bulle--mienne.bulle--suite { border-radius: var(--r-moyen); }

.bulle__corps { margin: 0; overflow-wrap: anywhere; white-space: pre-wrap; }
.bulle__auteur { font-family: var(--sans); font-size: var(--t-onglet); font-weight: var(--g-appuyee);
  color: var(--lien); }

/* L'heure DANS la bulle, en bas à droite : sur sa propre ligne, elle doublait
   la hauteur du fil pour une information qu'on ne lit qu'en cas de doute. */
.bulle__pied { display: flex; align-items: center; justify-content: flex-end;
  gap: var(--e1); font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce); font-variant-numeric: tabular-nums; }
/* UNE coche, jamais deux : elle dit « parti », pas « lu ». Dire aux autres
   qu'on a lu est l'un des trois interdits de conception du produit. */
.bulle__pied svg { width: 14px; height: 14px; opacity: .8; }

/* La citation du message auquel on répond. Sans elle, une réponse à un message
   de la veille tombe dans le vide et l'on relit tout le fil. */
.bulle__citation { display: flex; flex-direction: column; gap: var(--e0);
  margin: 0 0 var(--e1); padding: var(--e1) var(--e2); text-decoration: none;
  border-left: 3px solid var(--lien); background: var(--filet-clair);
  border-radius: var(--r-petit); font-size: var(--t-onglet); color: var(--encre-douce); }
.bulle__citation-auteur { font-weight: var(--g-forte); color: var(--lien); }
.bulle__citation-corps { overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }

/* La photo occupe la bulle : une image dans un cadre avec des marges autour
   ressemble à une pièce jointe de courriel, pas à une photo qu'on montre. */
.bulle__photo { display: block; margin: -.4rem -.6rem var(--e1); }
.bulle__photo img { display: block; width: 100%; height: auto;
  border-radius: var(--r-moyen) var(--r-moyen) 0 0; }

.bulle__son { display: flex; align-items: center; gap: var(--e2); padding: var(--e0) 0; }
.bulle__son audio { height: 34px; max-width: 15rem; }
.bulle__duree { font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce); font-variant-numeric: tabular-nums; }

/* Un document se montre comme un document : l'icône, le nom, le poids. Une
   ligne de texte bleu soulignée ressemble à une note de bas de page. */
.bulle__fichier { display: flex; align-items: center; gap: var(--e2); min-width: 0;
  padding: var(--e2) var(--e1); text-decoration: none; color: var(--encre); }
.bulle__fichier svg { width: 28px; height: 28px; flex: 0 0 auto;
  color: var(--encre-douce); }
.bulle__fichier-texte { display: flex; flex-direction: column; min-width: 0; }
.bulle__fichier-nom { font-weight: var(--g-appuyee); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

.bulle--en-attente { opacity: .6; }
.bulle--refusee { border-color: var(--alerte); }
/* La raison du refus, sous la bulle qui a échoué. Elle n'était nulle part de
   visible : « non envoyé » accusait le réseau pendant que le serveur avait dit
   pourquoi. On ne demande pas à quelqu'un de deviner ce qu'il doit refaire. */
.bulle__motif { margin: var(--e1) 0 0; font-family: var(--sans);
  font-size: var(--t-micro); color: var(--alerte); }
.bulle--masquee { font-style: italic; color: var(--encre-douce); }
.bulle__etat { font-family: var(--sans); font-size: var(--t-micro); color: var(--encre-douce); }

/* ------------------------------------------------------------------
   LE COMPOSEUR
   ------------------------------------------------------------------
   Trois gestes qui encadrent le champ : joindre, écrire, parler. C'est là
   qu'on les cherche, et c'est la seule raison de les mettre là.

   Le champ grandit avec le texte jusqu'à cinq lignes, puis défile : une zone
   fixe d'une ligne cache ce qu'on vient d'écrire, une zone qui grandit sans
   borne mange l'écran. */
.composeur { flex: 0 0 auto; position: relative; z-index: 2;
  display: flex; align-items: flex-end; gap: var(--e1);
  margin: 0 -1.25rem; padding: .5rem 1rem calc(.5rem + env(safe-area-inset-bottom, 0px));
  background: var(--papier); border-top: 1px solid var(--filet); }

.composeur textarea {
  flex: 1; min-width: 0; min-height: 42px; max-height: 8.5rem;
  padding: var(--e2) var(--e4);
  /* 16 px, et jamais moins : sous ce seuil Safari iOS zoome à chaque prise de
     focus, et l'on doit repincer pour relire son fil. C'est le piège que le
     dépôt connaissait déjà pour les boutons, et où l'écran le plus utilisé du
     produit était retombé. */
  font-size: var(--t-corps); line-height: 1.35; border-radius: var(--r-rond); resize: none;
  /* Masqué par défaut, et rendu à `auto` par le contrôleur au seul moment où le
     texte dépasse le plafond — voir `conversation_controller.js`. En `auto`
     permanent, une barre surgissait à la cinquième ligne. Horizontalement,
     jamais : un message passe à la ligne, il ne défile pas. */
  overflow-y: hidden; overflow-x: hidden;
  scrollbar-width: thin;
}

/* ------------------------------------------------------------------
   L'OVALE BLANC GÉANT
   ------------------------------------------------------------------
   `.composeur__envoyer` posait `width: 42px; height: 42px; border-radius: 50%`
   — mais PAS `min-height`. La règle générique `button` impose
   `min-height: var(--cible-principale)`, soit 56 px, et `min-height` l'emporte
   sur `height` : le bouton faisait 42 × 56 avec un rayon de 50 %, c'est-à-dire
   une ellipse verticale. En thème sombre, `--encre` vaut #EAE5DA : une ellipse
   presque blanche, l'objet le plus clair de tout l'écran, pour le geste le
   plus banal du produit.

   Le micro subissait la même chose ; le trombone, qui est un `<label>` et
   échappe à la règle `button`, restait un cercle de 42. Les trois commandes
   d'une même barre n'avaient donc pas la même hauteur.

   `min-height: 0` répare les trois. Et l'envoi cesse d'être un aplat de la
   couleur du texte : il prend `--lien`, la couleur des choses sur lesquelles
   on clique. */
.composeur__bouton, .composeur__envoyer, .composeur__joindre > summary {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; min-height: 0; padding: 0; border-radius: var(--r-rond);
  cursor: pointer; background: none; border: 0; color: var(--encre-douce);
  list-style: none;
}
.composeur__joindre > summary::-webkit-details-marker { display: none; }
.composeur__bouton svg, .composeur__envoyer svg,
.composeur__joindre > summary svg { width: 22px; height: 22px; }
.composeur__bouton:hover, .composeur__joindre > summary:hover {
  background: var(--filet-clair); color: var(--encre); }
.composeur__envoyer { background: var(--lien); color: var(--papier);
  border: 1px solid var(--lien); }
.composeur__envoyer:hover { filter: brightness(1.12); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) .composeur__envoyer { color: #14130F; }
}

/* ------------------------------------------------------------------
   DEUX ENTRÉES DERRIÈRE LE TROMBONE
   ------------------------------------------------------------------
   Un `accept` mixte — `image/*,audio/*,.pdf` — ouvre l'explorateur de FICHIERS
   sur iOS, jamais la photothèque : on ne retrouvait donc pas ses photos. Une
   entrée `image/*` SEULE ouvre la pellicule. D'où deux choix, et non un. */
.composeur__joindre { position: relative; flex: 0 0 auto; }
.composeur__choix { position: absolute; bottom: 48px; left: 0; z-index: 5;
  display: flex; flex-direction: column; min-width: 12rem;
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-petit); padding: var(--e1); }
.composeur__choix label { display: flex; align-items: center; gap: var(--e3);
  min-height: var(--cible); padding: 0 var(--e3); font-weight: var(--g-appuyee);
  font-size: var(--t-meta); cursor: pointer; border-radius: var(--r-petit); }
.composeur__choix label:hover { background: var(--filet-clair); }
.composeur__choix svg { width: 20px; height: 20px; color: var(--encre-douce); }

/* Le plateau d'aperçu : on voit ce qu'on va envoyer, et l'on peut le retirer.
   La pièce partait auparavant à la seconde où on la choisissait. */
.plateau { flex: 0 0 auto; display: flex; align-items: center; gap: var(--e3);
  margin: 0 -1.25rem; padding: var(--e2) var(--e4);
  border-top: 1px solid var(--filet); background: var(--filet-clair); }
.plateau__vignette { width: 44px; height: 44px; flex: 0 0 auto;
  object-fit: cover; border-radius: var(--r-petit); background: var(--surface); }
.plateau__nom { flex: 1; min-width: 0; font-size: var(--t-meta);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plateau__retirer { flex: 0 0 auto; width: 40px; height: 40px; min-height: 0;
  padding: 0; display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: var(--r-rond); color: var(--encre-douce);
  cursor: pointer; }
.plateau__retirer:hover { background: var(--surface); color: var(--encre); }
.plateau__retirer svg { width: 20px; height: 20px; }

/* Le geste de réponse. Discret par nature : il s'efface sur un appareil à
   pointeur tant qu'on ne survole pas la bulle, et reste visible en pâle sur un
   écran tactile, où il n'y a pas de survol. Ce n'est pas « Signaler », qui
   accusait à chaque ligne — c'est le geste ordinaire d'une conversation. */
/* 26 px était la moitié du plancher tactile. La CIBLE monte à 40 px, le dessin
   reste à 15 : on agrandit ce qu'on touche, pas ce qu'on voit. */
/* « Répondre » et « Retirer » partagent la même règle : ce sont les deux gestes
   discrets du pied de bulle, ils doivent avoir exactement la même présence.
   Deux règles séparées finissent toujours par diverger d'un pixel. */
.bulle__repondre, .bulle__retirer {
  width: 40px; height: 40px; min-height: 0; margin: -.6rem -.4rem -.6rem 0;
  padding: 0; display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: var(--r-rond); cursor: pointer;
  color: var(--encre-douce); opacity: 0; transition: opacity .12s; }
.bulle__repondre svg, .bulle__retirer svg { width: 15px; height: 15px; }
.bulle:hover .bulle__repondre, .bulle__repondre:focus-visible,
.bulle:hover .bulle__retirer, .bulle__retirer:focus-visible { opacity: 1; }
.bulle__repondre:hover { background: var(--filet-clair); color: var(--encre); }
/* Le retrait est le seul geste irréversible du fil : au survol il le dit. */
.bulle__retirer:hover { background: var(--filet-clair); color: var(--alerte); }
@media (hover: none) { .bulle__repondre, .bulle__retirer { opacity: .5; } }
@media (prefers-reduced-motion: reduce) {
  .bulle__repondre, .bulle__retirer { transition: none; } }

/* ------------------------------------------------------------------
   LES CHAMPS SUIVENT LE SENS CHOISI, SANS UNE LIGNE DE JAVASCRIPT
   ------------------------------------------------------------------
   Déclarer un lien demande la nature de la FILIATION ou celle de l'UNION,
   jamais les deux. `:has()` lit la case cochée et masque le bloc qui ne sert
   pas.

   Écrit en positif — on masque ce qui ne correspond pas — pour que le
   formulaire reste entier là où `:has()` manque : les deux blocs paraissent, on
   répond à celui qui compte, et le serveur ignore l'autre. Un îlot Stimulus
   ferait la même chose en cessant de fonctionner sans lui. */
.formulaire--lien:has(#sens_conjoint:checked) .bloc--filiation,
.formulaire--lien:has(#sens_fratrie:checked)  .bloc--filiation,
.formulaire--lien:has(#sens_parent:checked)   .bloc--alliance,
.formulaire--lien:has(#sens_enfant:checked)   .bloc--alliance,
.formulaire--lien:has(#sens_fratrie:checked)  .bloc--alliance { display: none; }

/* La question de l'autre parent ne se pose que pour un ENFANT : demander qui
   est l'autre parent de son propre père n'a pas de sens, et un champ sans
   objet fait douter de tout le formulaire. Écrit en positif comme au-dessus —
   là où `:has()` manque, la question paraît et le serveur l'ignore. */
.formulaire--greffe:has(#lien_parent:checked)   .bloc--second-parent,
.formulaire--greffe:has(#lien_fratrie:checked)  .bloc--second-parent,
.formulaire--greffe:has(#lien_conjoint:checked) .bloc--second-parent { display: none; }

/* Les deux flèches de l'album. Pas de visionneuse en surimpression : cette
   page EST l'affichage en grand, et elle garde une adresse qu'on peut envoyer.
   La flèche « suivante » est le même dessin retourné — un seul chemin SVG dans
   le dépôt, et deux flèches qui ne peuvent pas diverger. */
.album-parcours { display: flex; justify-content: space-between; align-items: center;
  gap: var(--e3); margin: var(--e3) 0 var(--e5); }
.album-parcours__lien { display: inline-flex; align-items: center; gap: var(--e2);
  min-height: 44px; padding: 0 var(--e2); font-family: var(--sans);
  font-size: var(--t-onglet); color: var(--lien); text-decoration: none; }
.album-parcours__lien:hover { text-decoration: underline; }
.album-parcours__lien svg { width: 18px; height: 18px; }
.album-parcours__lien--suivant { margin-left: auto; }
.album-parcours__lien--suivant svg { transform: rotate(180deg); }

/* Un avis qui porte sur MOI et que je n'avais pas demandé — « votre conjoint
   vous indique comme vivant au… ». Il se distingue d'un bloc de formulaire
   ordinaire par son filet, sans crier : ce n'est pas une alerte, c'est un fait
   qu'on doit avoir lu avant de saisir la suite. */
/* ------------------------------------------------------------------
   UN AVIS EST UNE BOÎTE, PAS UN CHAMP DE FORMULAIRE
   ------------------------------------------------------------------
   `.bloc` est un `fieldset` : un filet en TÊTE, et zéro retrait latéral —
   c'est ce qu'il faut pour une pile de champs qui s'aligne sur la colonne de
   lecture. Posé sur un avis, cela collait le texte au filet de gauche, et le
   bouton débordait d'un bord à l'autre.

   L'avis, lui, est une boîte : du retrait sur les quatre côtés, un coin
   arrondi, et pas de filet en tête — celui de gauche dit déjà que c'est autre
   chose. */
.bloc--avis { border: 0; border-left: 3px solid var(--lien);
  border-radius: 0 var(--r-bloc) var(--r-bloc) 0;
  background: var(--filet-clair); padding: var(--e3) var(--e4); }
.bloc--avis > :first-child { margin-top: 0; }
.bloc--avis > :last-child { margin-bottom: 0; }
/* Le formulaire logé dedans ne reprend pas la marge basse d'un formulaire de
   plein écran : il est déjà dans une boîte qui en porte une. */
.bloc--avis .formulaire { margin: var(--e2) 0 0; gap: var(--e2); }

/* ==================================================================
   LES RANGS DE RÉGLAGE — UN ÉCRAN QUI SE BALAIE
   ==================================================================
   « Mon compte » alignait onze liens de texte de même graisse, sous des titres
   de même rang : « Être prévenu » y était indiscernable de « Se faire aider ».
   Le régisseur du produit lui-même a mis une minute à le trouver.

   Trois choses rendent un écran de réglages balayable, et il en faut les
   trois : un DESSIN à gauche que l'œil retrouve sans lire, une SURFACE
   CLIQUABLE entière plutôt que six caractères soulignés, et un CHEVRON qui dit
   que cela s'ouvre.

   La hauteur minimale de 3.5rem n'est pas décorative : c'est la cible de doigt
   que respectent iOS et Android, et le public de ce produit a 82 ans une fois
   sur cinq. */
.reglages { display: flex; flex-direction: column; margin: 0 0 var(--e4);
  border: 1px solid var(--filet); border-radius: var(--r-bloc);
  background: var(--papier); overflow: hidden; }

.reglage { display: flex; align-items: center; gap: var(--e3);
  min-height: 3.5rem; padding: var(--e3) var(--e3);
  color: var(--encre); text-decoration: none;
  border-top: 1px solid var(--filet); }
.reglage:first-child { border-top: 0; }
.reglage:hover { background: var(--filet-clair); }
.reglage:focus-visible { outline: 2px solid var(--lien); outline-offset: -2px; }

.reglage__icone { flex: 0 0 auto; width: 22px; height: 22px; color: var(--encre-douce); }
.reglage__corps { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column;
  gap: .15rem; }
.reglage__titre { font-family: var(--sans); font-weight: var(--g-forte);
  display: flex; align-items: baseline; gap: var(--e2); flex-wrap: wrap; }
.reglage__detail { font-size: var(--t-meta); color: var(--encre-douce);
  line-height: 1.35; }
.reglage__chevron { flex: 0 0 auto; width: 18px; height: 18px; color: var(--filet); }
.reglage:hover .reglage__chevron { color: var(--encre-douce); }

/* L'ÉTAT est ce qui fait la différence entre une liste d'options et un écran
   qui appelle. En attente, il prend la couleur d'alerte du produit — la seule
   de la page, sans quoi elle ne dirait plus rien. */
.reglage__etat { font-size: var(--t-meta); font-weight: var(--g-normale);
  color: var(--encre-douce); padding: .1rem .4rem; border-radius: var(--r-pastille, 4px);
  background: var(--filet-clair); }
.reglage__etat--attente { color: var(--alerte); background: transparent;
  border: 1px solid var(--alerte); }

/* La citation d'un message retiré depuis : même dessin, mais rien à ouvrir. */
.bulle__citation--sans-cible { cursor: default; opacity: .75; }

/* La citation de ce à quoi l'on répond, au-dessus du champ. */
.citation-en-cours { flex: 0 0 auto; display: flex; align-items: center; gap: var(--e2);
  margin: 0 -1.25rem; padding: var(--e2) var(--e4);
  border-top: 1px solid var(--filet); background: var(--filet-clair);
  border-left: 3px solid var(--lien); }
.citation-en-cours__texte { flex: 1; min-width: 0; display: flex;
  flex-direction: column; font-size: var(--t-onglet); color: var(--encre-douce); }

/* Le bouton de remontée du fil, et la ligne qui dit qu'il n'y a plus rien. */
/* Pleine largeur : la remontée contient désormais une LISTE de rangées, et
   `align-self: center` la rétrécissait à son bouton — les messages ramenés
   s'entassaient alors au milieu. */
.fil__remontee { align-self: stretch; width: 100%; align-items: center;
  display: flex; flex-direction: column;
  align-items: center; gap: var(--e1); width: 100%; margin: 0 0 var(--e2); }
/* Les deux règles qui alignaient les bulles remontées sont sans objet : elles
   passent par `.fil__rang` comme toutes les autres. */
.fil__remontee form { margin: var(--e1) 0; }

/* Le seul signalement de l'écran, en pied, discret. Il était sous chaque
   message — deux cents accusations par écran dans une famille. */
.signalement-du-pied { flex: 0 0 auto; margin: 0; padding: var(--e1) 0 var(--e0);
  text-align: center; }
/* La même CASE que partout — pied de page, dossiers, registres. Le lien nu
   flottait au centre sans appartenir à aucune famille de formes. */
.signalement-du-pied a { display: inline-flex; align-items: center; gap: var(--e2);
  min-height: 36px; padding: 0 var(--e3);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  background: var(--surface);
  font-size: var(--t-onglet); color: var(--encre-douce); text-decoration: none; }
.signalement-du-pied a:hover { color: var(--encre); border-color: var(--encre-douce);
  background: var(--filet-clair); }
.signalement-du-pied svg { width: 14px; height: 14px; }

/* Pendant l'enregistrement : le micro devient rouge et pulse. C'est le seul
   endroit du produit où une couleur d'alerte sert à autre chose qu'une erreur,
   et c'est justifié — on doit voir d'un coup d'œil qu'on est en train
   d'enregistrer. */
.composeur__bouton--enregistre { color: var(--alerte); animation: pouls 1.2s infinite; }
@keyframes pouls { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .composeur__bouton--enregistre { animation: none; }
}

.composeur__etat { margin: var(--e1) 0 0; font-family: var(--sans);
  font-size: var(--t-meta); color: var(--encre-douce); min-height: 1.2em; }

/* Sur écran large il n'y a plus de barre en bas : le composeur s'y colle. */
/* ------------------------------------------------------------------
   SUR ÉCRAN LARGE, LA BARRE N'EST PLUS EN BAS
   ------------------------------------------------------------------
   Le fil et la carte retranchaient 64 px pour une barre d'onglets qui, à cette
   largeur, est devenue une colonne à gauche : soixante-quatre pixels retirés
   pour rien, plus vingt-quatre de rembourrage — quatre-vingt-huit pixels de
   conversation perdus sur chaque écran de portable.

   La règle prévue pour le corriger, `min-height: calc(100dvh - 6rem)`, était
   INERTE : une hauteur minimale ne peut pas battre une `height` déjà posée.
   C'est le genre de correctif qui rassure sans rien faire. */
@media (min-width: 60rem) {
  .composeur { bottom: 0; }
  /* ------------------------------------------------------------------
     DEUX RÈGLES QUI SE CONTREDISAIENT, ET LE FIL PASSAIT SOUS LE RAIL
     ------------------------------------------------------------------
     `.fil-page` portait `margin: -2.5rem -2rem -4rem -3rem` pour annuler le
     rembourrage de `.page` et occuper toute la fenêtre. Mais la règle juste en
     dessous met déjà ce rembourrage à ZÉRO pour ces deux écrans : les marges
     négatives ne compensaient donc plus rien, elles TIRAIENT — de trois rem
     vers la gauche, c'est-à-dire sous la colonne de navigation. À l'écran, le
     fil de conversation disparaissait derrière le rail et « fermer ce groupe »
     se lisait « rmer ce groupe ».
     
     Chacune des deux était juste séparément ; leur somme ne l'était pas. On
     n'annule plus rien : le rembourrage est à zéro, il n'y a rien à reprendre. */
  .avec-barre .page:has(.carte-page), .avec-barre .page:has(.fil-page) {
    max-width: none; padding: 0;
  }
  .fil-page, .carte-page {
    /* La bande haute occupe 3 rem : sans les retrancher, le composeur descend
       d'autant sous la ligne de flottaison. */
    height: calc(100dvh - var(--bande-haute)); margin: 0; padding: 0 var(--e7);
  }


  /* `.message` déclarait `max-width: 75%` et n'avait AUCUN appelant : l'intention
     — élargir la bulle quand l'écran le permet — n'était jamais livrée. */
  .bulle { max-width: 68%; }
}

/* ------------------------------------------------------------------
   AU-DELÀ DE 1280 PX, LA COLONNE ÉTAIT COLLÉE AU RAIL
   ------------------------------------------------------------------
   `.avec-barre .page { margin: 0 }` avait été posé pour une gouttière de
   quarante-huit pixels sur un iPad — et n'a jamais été borné. À 1512 px cela
   laissait 536 px de vide à droite du texte ; à 1920, NEUF CENT DOUZE, soit
   près de la moitié de l'écran, d'un seul côté. C'est exactement ce que le
   commentaire de la barre redoute : un produit de téléphone mal agrandi.

   On ne centre pas tout à fait — la colonne reste plus proche de la barre que
   du bord, parce que l'œil revient vers la navigation — mais l'asymétrie cesse
   d'être une infirmité.

   `margin` ET NON `margin-inline` A COÛTÉ CENT VINGT-HUIT PIXELS DE VIDE EN
   HAUT DE CHAQUE ÉCRAN. Le raccourci à une valeur pose les QUATRE côtés : la
   gouttière voulue à gauche s'appliquait aussi au-dessus et au-dessous, et
   comme elle écrase le `margin: 0` posé plus haut, tout écran de 1440 px ou
   plus s'ouvrait sur une bande vide de la hauteur d'un titre — avant même le
   lien de retour. C'est un décalage horizontal, il s'écrit donc sur l'axe
   horizontal. */
@media (min-width: 90rem) {
  .avec-barre .page, .avec-barre .annonces {
    margin-inline: min(8rem, calc((100vw - 17rem - 46rem) / 2));
  }
}

.compteur { flex: 0 0 auto; min-width: 24px; height: 24px; padding: 0 var(--e2);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--encre); color: var(--papier); border-radius: var(--r-moyen);
  font-size: var(--t-meta); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------
   LA PASTILLE DE LA BARRE
   ------------------------------------------------------------------
   Elle se pose SUR l'icône, en haut à droite, comme partout depuis quinze ans.
   L'enveloppe n'existe que pour lui donner un conteneur positionné : sans
   elle, `position: absolute` la renverrait au coin de la page.

   Elle prend `--alerte` et non `--encre` : c'est le seul endroit du produit,
   avec le micro en enregistrement, où cette couleur ne dit pas une erreur — et
   c'est justifié, parce qu'une pastille qui se fond dans le texte ne remplit
   pas son office. Le filet de la couleur du fond la détache de l'icône. */
.onglet__porte-icone { position: relative; display: inline-flex; }
.onglet__pastille {
  position: absolute; top: -5px; left: 60%;
  min-width: 18px; height: 18px; padding: 0 var(--e1);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--alerte); color: var(--papier);
  border: 2px solid var(--surface); border-radius: var(--r-moyen);
  /* Le SEUL 700 du produit, et il est justifié : onze pixels sur un aplat
     coloré de dix-huit, aucune autre graisse ne se lit. */
  font-family: var(--sans); font-size: var(--t-micro); font-weight: var(--g-forte); line-height: 1;
  font-variant-numeric: tabular-nums; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) .onglet__pastille { color: #14130F; }
}
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* L'édition en cours. Un encadré sobre, jamais une bannière : l'échéance doit
   se voir sans crier. */
/* UN filet, en haut. Le second ouvrait quatre traits en deux cents pixels
   entre l'échéance et le composeur, qui porte déjà son propre cadre. */
.edition-en-cours { padding: var(--e4) 0 0; border-top: 1px solid var(--filet);
  margin: 0 0 var(--e6); }
.edition-en-cours__titre { margin: 0 0 var(--e1); font-weight: var(--g-appuyee);
  font-size: var(--t-meta); color: var(--encre-douce); }
.edition-en-cours__echeance { margin: 0 0 var(--e1); }
.edition-en-cours__places { margin: 0; font-size: var(--t-meta);
  color: var(--encre-douce); font-variant-numeric: tabular-nums; }

.composeur-publication { display: flex; flex-direction: column; gap: var(--e2);
  margin: 0 0 var(--e7); }
.composeur-publication textarea { font-family: var(--sans);
  font-size: var(--t-corps); padding: var(--e3) var(--e3); background: var(--surface);
  color: var(--encre); border: 1px solid var(--filet); border-radius: var(--r-petit);
  resize: vertical; }
.titre-optionnel summary { cursor: pointer; font-size: var(--t-meta);
  color: var(--encre-douce); min-height: var(--cible);
  display: flex; align-items: center; }

.section-edition { margin: 0 0 var(--e7); }
.section-edition__titre { display: flex; align-items: center; gap: var(--e2);
  font-family: var(--serif); font-size: var(--t-sous-titre); font-weight: var(--g-appuyee);
  padding: var(--e2); border-bottom: 1px solid var(--filet); }
.publication { padding: var(--e4) 0; border-bottom: 1px solid var(--filet-clair); }
.publication h3 { font-family: var(--serif); font-size: var(--t-sous-titre); font-weight: var(--g-appuyee);
  margin: 0 0 var(--e2); }
.publication__corps { margin: 0 0 var(--e2); overflow-wrap: anywhere; }

/* La ligne de rappel, en tête de la liste des conversations. Une LIGNE, pas une
   bannière ; non masquable jusqu'au bouclage, puis elle disparaît d'elle-même.
   Sans elle le rituel serait invisible, Messages étant l'écran qui s'ouvre. */
.rappel-edition { display: flex; gap: var(--e2); align-items: baseline;
  padding: var(--e3) 0; margin: 0 0 var(--e2); border-bottom: 1px solid var(--filet);
  font-size: var(--t-meta); }
.rappel-edition strong { font-variant-numeric: tabular-nums; }

/* L'en-tête d'écran : le titre, et le lien qui l'accompagne sur la MÊME ligne
   de base. Le seul cas du produit où deux éléments se partagent une ligne de
   titre, et il était traité par un `float:right` posé en attribut `style` — donc
   hors du flux, donc sans ligne de base commune. */
/* Un entête porte un titre et, souvent, UN geste. `align-items: center` et non
   `baseline` : une ligne de base commune entre une romaine de 24 px et un
   bouton de 40 aligne le texte du bouton sur celui du titre et pousse le
   bouton vers le bas — il paraît alors décroché. */
.entete { display: flex; align-items: center; justify-content: space-between;
  gap: var(--e4); flex-wrap: wrap; margin: 0 0 var(--e3); }

/* Le geste qui accompagne un titre : la même forme que partout, à l'échelle
   d'un entête. Sans elle, un lien nu de 15 px en bleu souligné se retrouvait à
   côté d'une romaine de 24 — deux registres qui ne se parlent pas. */
/* ------------------------------------------------------------------
   « COMPACTE » PORTE SUR LE DESSIN, PAS SUR LA ZONE TACTILE
   ------------------------------------------------------------------
   Elle valait 40 px de haut, c'est-à-dire moins que la cible du pouce, et le
   produit s'en sert pour des gestes ordinaires — « Compléter » sur une fiche
   de maison, « Ajouter » sur l'arbre. Un bouton compact doit être plus DISCRET,
   pas plus difficile à toucher : on garde le rembourrage réduit et la petite
   fonte, on rend la hauteur. */
.action--compacte { min-height: var(--cible); padding: 0 var(--e3);
  font-size: var(--t-meta); }
.entete h1 { margin: 0; }
.entete p { margin: 0; }

.recherche { display: flex; gap: var(--e2); margin: var(--e4) 0 var(--e6); align-items: stretch; }
.recherche input { flex: 1; font-family: var(--sans); font-size: var(--t-corps);
  min-height: var(--cible); padding: var(--e2) var(--e3); background: var(--surface);
  color: var(--encre); border: 1px solid var(--filet); border-radius: var(--r-petit); }

/* ------------------------------------------------------------------
   CHERCHER N'EST PAS UN ENGAGEMENT
   ------------------------------------------------------------------
   Ce bouton n'avait AUCUNE règle : il retombait sur le bouton d'action
   principale — fond `--encre`, 56 px, graisse 600. Le geste le plus anodin et le
   plus répété du produit portait donc le poids visuel le plus lourd, à côté d'un
   champ de 48 px qu'il dépassait de huit pixels. C'est cet écart de hauteur,
   autant que le contraste, qui datait l'ensemble.

   Il prend le traitement discret — filet et encre douce, comme
   `.bouton-discret` — et surtout la hauteur exacte du champ : `align-items:
   stretch` sur la rangée, et aucune hauteur minimale imposée ici. */
.recherche button { min-height: 0; background: var(--surface); }

/* Le lien d'invitation à transmettre soi-même. Il ne paraît qu'une fois : on
   l'encadre pour qu'on ne le manque pas, et on le compose en chasse fixe pour
   qu'il se recopie sans faute au téléphone. */
.lien-a-transmettre { margin: 0 0 var(--e6); padding: var(--e4) var(--e5);
  background: var(--surface); border: 1px solid var(--encre-douce);
  border-radius: var(--r-petit); }
.lien-a-transmettre__url { margin: var(--e1) 0 var(--e2); font-family: ui-monospace,
  "SF Mono", Menlo, monospace; font-size: var(--t-meta); word-break: break-all; }

/* Inviter depuis la fiche : encadré, parce que c'est le seul endroit de la
   fiche où l'on AGIT — tout le reste s'y lit. */
/* Les rôles : un nom, ce qu'il tient, et les gestes. Sur un téléphone les
   gestes passent sous le nom plutôt que de le comprimer. */
.role-ligne { flex-wrap: wrap; gap: var(--e2); }
.role-ligne__gestes { display: flex; flex-wrap: wrap; gap: var(--e2); }
.role-ligne__gestes form { display: flex; gap: var(--e2); margin: 0; }

/* COU-VIE-004 — ce qui est écrit et n'a pas encore paru. Ce n'est ni une alerte
   ni un article : c'est un accusé. Il a donc le poids d'un encadré discret, pas
   celui d'une rubrique — l'édition du dimanche reste l'événement. */
.a-paraitre { margin: var(--e4) 0; padding: var(--e3) var(--e4);
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-petit); }
.a-paraitre__compte { margin: 0; }
.a-paraitre__miennes { list-style: none; margin: var(--e3) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--e2); }
.a-paraitre__miennes li { display: flex; flex-direction: column; gap: var(--e0);
  padding-top: var(--e2); border-top: 1px solid var(--filet-clair); }
.a-paraitre__titre { font-family: var(--serif); }

/* Une VEILLE : elle ne réclame rien maintenant, elle constate un manque durable.
   Plus discrète qu'un acte — pas de bordure d'appel, pas de pastille — mais
   toujours présente. C'est la définition même de « discret et permanent ». */
.attente--veille { margin-top: var(--e4); }
.attente__ligne--veille { background: var(--surface);
  border: 1px dashed var(--filet); border-radius: var(--r-petit); }

/* L'identité que le parent VÉRIFIE avant de donner son accord. Ce n'est pas une
   décoration : c'est la pièce qu'on lit avant de cocher, et l'article 8 §2 du
   RGPD en fait la condition de validité du consentement. Elle prend donc le
   poids d'un encadré, pas d'une note de bas de page. */
.identite-verifiee { width: 100%; border-collapse: collapse; margin: var(--e4) 0;
  display: block; overflow-x: auto; }
.identite-verifiee caption { text-align: left; padding-bottom: var(--e2);
  font-family: var(--sans); font-size: var(--t-micro); color: var(--encre-douce); }
.identite-verifiee th { text-align: left; padding: var(--e2) var(--e3) var(--e2) 0;
  color: var(--encre-douce); font-weight: 600; white-space: nowrap; }
.identite-verifiee td { padding: var(--e2) 0; }
.identite-verifiee tr + tr th, .identite-verifiee tr + tr td {
  border-top: 1px solid var(--filet-clair); }

/* COU-FRI-010 — l'écran de fusion. Deux listes et un tableau : la table des
   déplacements est la seule du produit, et elle doit pouvoir défiler sur un
   téléphone sans emporter la page avec elle. */
.fusion-candidates { list-style: none; margin: var(--e3) 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--e2); }
.fusion-candidates a { display: flex; flex-direction: column; gap: var(--e1);
  padding: var(--e3); background: var(--surface);
  border: 1px solid var(--filet); border-radius: var(--r-petit);
  text-decoration: none; }
.fusion-refus { margin: var(--e4) 0; padding: var(--e4);
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-petit); }
.fusion-apercu { width: 100%; border-collapse: collapse; margin: var(--e3) 0;
  display: block; overflow-x: auto; }
.fusion-apercu caption { text-align: left; color: var(--encre-douce);
  font-size: var(--t-micro); padding-bottom: var(--e1); }
.fusion-apercu th, .fusion-apercu td { text-align: left; padding: var(--e2) var(--e3);
  border-bottom: 1px solid var(--filet-clair); }
.fusion-apercu td { text-align: right; font-variant-numeric: tabular-nums; }

/* COU-VIE-006 — la proposition de rattachement, à l'arrivée et sur son propre
   nœud d'arbre. Elle prend le poids d'une section et non celui d'une note :
   c'est ce qui donne une famille à quelqu'un qui vient d'entrer, et ce qui
   passe pour secondaire ne se lit pas. */
.rattachement-propose {
  margin: var(--e6) 0; padding: var(--e4);
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-petit);
}
.rattachement-propose h2 { margin-top: 0; }
.rattachement-propose__choix { list-style: none; margin: var(--e3) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--e2); }
/* Chaque choix occupe la ligne : ils se comparent, et l'on ne compare pas des
   libellés de longueurs différentes posés côte à côte. */
.rattachement-propose__choix form { margin: 0; }
.rattachement-propose__choix button { width: 100%; text-align: left; }

/* Deux encadrés de même nature sur la fiche d'annuaire : « voici ce que vous
   pouvez faire depuis ici ». Ils partagent leur forme parce qu'ils partagent
   leur rôle — les séparer ferait diverger deux blocs qu'on lit l'un sous
   l'autre. */
.invitation-depuis-fiche,
.correction-depuis-fiche {
  display: flex; flex-direction: column; gap: var(--e1);
  margin: var(--e6) 0; padding: var(--e3) var(--e4);
  background: var(--surface); border: 1px solid var(--filet); border-radius: var(--r-petit);
}

.etiquette { flex: 0 0 auto; font-size: var(--t-micro); color: var(--encre-douce);
  padding: var(--e1); min-width: 6.5rem; }

/* La parenté : une phrase en clair d'abord, le détail chiffré replié. C'est ce
   qui transforme une fonction technique en objet de conversation familiale. */
/* UN filet. Le second donnait quatre traits pour deux blocs de trois lignes,
   la parenté et l'invitation se suivant à cent pixels. Le bouton « Voir dans
   l'arbre » qui vient ensuite ferme le bloc de lui-même. */
.parente { padding: var(--e4) 0 0; margin: 0 0 var(--e6);
  border-top: 1px solid var(--filet); }
.parente__phrase { margin: 0 0 var(--e2); font-family: var(--serif); font-size: var(--t-sous-titre); }
.parente details summary { cursor: pointer; font-size: var(--t-meta);
  color: var(--encre-douce); min-height: var(--cible);
  display: flex; align-items: center; }

.segment { border: 0; border-top: 1px solid var(--filet); padding: var(--e4) 0 var(--e2);
  margin: 0; }
.segment legend { font-weight: var(--g-forte); font-size: var(--t-corps); padding: 0 var(--e2) 0 0; }
.effectifs { display: flex; gap: var(--e4); flex-wrap: wrap; }
.effectifs label { display: flex; flex-direction: column; gap: var(--e1);
  font-size: var(--t-meta); font-weight: var(--g-normale); }
.effectifs input { width: 5rem; min-height: var(--cible); padding: var(--e2);
  font-size: var(--t-corps); text-align: center;
  font-variant-numeric: tabular-nums; background: var(--surface);
  color: var(--encre); border: 1px solid var(--filet); border-radius: var(--r-petit); }

/* ==================================================================
   LES DEUX VOIX
   ==================================================================

   Décision du 15 août 2026. Le produit parle sur deux registres, et les
   confondre coûte des deux côtés.

   VOIX ÉDITORIALE — ce qui SE LIT : À la une, carnet mondain, faire-part,
   archives, arbre. Romaine à empattements, interlignage large, filets fins,
   rubriques en petites capitales. C'est le registre du papier de famille, et
   il porte la solennité que ces événements méritent : une naissance ne doit
   pas ressembler à une notification.

   VOIX UTILITAIRE — ce qui S'UTILISE : messages, annuaire, agenda, réglages.
   Linéale sobre et dense, déjà en place. Un fil de conversation en romaine se
   lit mal sur téléphone, et c'est l'écran le plus fréquenté du produit.

   La règle de départ tient toujours : petites écritures plutôt que grosses
   banderoles. La hiérarchie se fait par le blanc et le filet, jamais par la
   taille ni par la couleur — la seule couleur forte reste le filet
   d'origine. */

.editorial { font-family: var(--serif); }
.editorial p { line-height: 1.62; margin: 0 0 var(--e4); }

/* ------------------------------------------------------------------
   PETITES CAPITALES, JAMAIS DE CAPITALES FAUSSES
   ------------------------------------------------------------------
   La direction artistique l'écrit noir sur blanc, et c'est son cinquième
   point : « jamais de capitales fausses générées par text-transform. C'est le
   détail qui sépare un carnet d'un formulaire. »

   Il y en avait cinq. Elles produisaient les banderoles qu'on voyait sur chaque
   écran — MESSES ANNIVERSAIRES ET SERVICES RELIGIEUX, COUSINAGE D'ANSELME ET
   GUILLEMETTE DE KERVENEC — c'est-à-dire exactement la marque du formulaire que
   la DA voulait éviter.

   `` compose de vraies petites capitales — MAIS
   SEULEMENT SI LA FONTE EN PORTE UNE TABLE. La romaine en a une : Iowan Old
   Style, Palatino et Georgia composent de vrais petits caractères, et la
   rubrique du carnet garde donc ce traitement, qui est le sien depuis toujours.

   La LINÉALE n'en a pas. Ni Avenir Next, ni Segoe UI, ni system-ui : le
   navigateur les SYNTHÉTISE en écrasant des capitales à 70 %, ce qui donne une
   écriture comprimée, plus sombre que le texte voisin, et vaguement fautive
   sans qu'on sache dire pourquoi. Quinze étiquettes du produit étaient dans ce
   cas. Voir `.etiquette-libelle` : elles prennent une casse normale. */
/* ------------------------------------------------------------------
   LES PETITES CAPITALES VONT AUX MOTS COURTS, ET À EUX SEULS
   ------------------------------------------------------------------
   La romaine porte bien une vraie table de petites capitales — le raisonnement
   ci-dessus tient. Ce qui ne tenait pas, c'est de l'appliquer à une PHRASE :
   « MESSES ANNIVERSAIRES ET SERVICES RELIGIEUX » fait sept mots et quarante
   caractères. Le Figaro compose « NAISSANCES » en capitales, jamais un membre
   de phrase — au-delà de deux mots, l'interlettrage étale le texte, la casse
   uniforme retire les repères de forme, et l'on ne lit plus, on déchiffre.
   
   Casse normale, romaine, un demi-cran au-dessus du corps : c'est un titre de
   section, il se lit comme tel. */
.rubrique { display: block; font-family: var(--serif); font-size: var(--t-meta);
  font-weight: var(--g-appuyee); letter-spacing: 0;
  color: var(--encre-douce); margin: 0 0 var(--e1); }

/* Une entrée de carnet. Le filet la sépare, rien ne l'encadre. */
.entree { padding: var(--e4) 0; border-bottom: 1px solid var(--filet-clair); }
.entree:last-child { border-bottom: 0; }
.entree__titre { font-family: var(--serif); font-size: var(--t-sous-titre); font-weight: var(--g-appuyee);
  line-height: 1.3; margin: 0 0 var(--e1); }
.entree__detail { margin: 0; color: var(--encre-douce); font-size: var(--t-meta); }

/* Le filet de respiration entre deux ensembles éditoriaux. Deux traits fins
   plutôt qu'un gros : c'est ce qui distingue un faire-part d'un formulaire. */
/* UN trait, pas deux. `border-top` + `border-bottom` + `height: 3px` dessinait
   deux lignes à trois pixels l'une de l'autre — vingt-cinq fois, dans vingt
   vues. À l'œil, cela ne se lit pas comme un ornement mais comme un défaut
   d'affichage. */
.filet-orne { border: 0; border-top: 1px solid var(--filet);
  height: 0; margin: var(--e7) 0; }

/* Titre de section — utilisé partout, il n'existait dans aucune règle. */
/* ------------------------------------------------------------------
   UN TITRE SE SÉPARE PAR DU VIDE, PAS PAR UN TRAIT
   ------------------------------------------------------------------
   Il portait un filet en dessous, et quatre pixels plus bas venait un
   cartouche qui porte le sien : deux traits à deux millimètres l'un de
   l'autre, sans que rien ne les distingue. C'est le défaut le plus visible de
   toute la feuille, et il se répétait partout où un titre précède un objet
   encadré — c'est-à-dire presque partout.

   La règle qui remplace : L'ESPACE SÉPARE, le trait ne sert qu'à ce que
   l'espace ne peut pas faire — séparer deux choses qui se touchent
   nécessairement, comme deux lignes d'une même liste. Un titre a de l'air
   au-dessus et en dessous de lui ; il n'a pas besoin d'être souligné. */
.section-titre { font-family: var(--serif); font-size: var(--t-sous-titre);
  font-weight: var(--g-forte); letter-spacing: -.01em;
  margin: var(--e7) 0 var(--e3); padding: 0; }

/* Les groupes qu'on peut rejoindre : filet CREUX, en pointillé. On montre
   qu'ils existent sans les faire passer pour des fils déjà vivants. */
.liste--a-rejoindre .liste__ligne { align-items: stretch; }
.origine--creuse { width: 0; border-left: 3px dotted var(--filet); }

/* Un onglet dont l'écran n'existe pas encore. On l'affiche éteint plutôt que
   de le cacher : une absence annoncée se comprend, un lien qui ne répond pas
   au doigt ne se comprend pas. */
.onglet--a-venir { opacity: .38; cursor: default; }

/* Choisir les familles destinataires d'une publication. Une saisie, plusieurs
   instances concernées — jamais la même annonce retapée. */
.destinations { border: 0; padding: 0; margin: 0 0 var(--e1); }
.destinations legend { font-size: var(--t-meta); font-weight: var(--g-forte);
  color: var(--encre-douce); padding: 0; margin: 0 0 var(--e1); }
.destination { display: flex; gap: var(--e2); align-items: center;
  min-height: var(--cible); cursor: pointer; }
.destination input { width: 24px; height: 24px; flex: 0 0 auto; }

/* Trancher sur l'autorité parentale d'un mineur : deux boutons pleine largeur,
   nettement distincts, jamais deux liens côte à côte. */
.corroboration-choix { display: flex; flex-direction: column; gap: var(--e3);
  margin: var(--e6) 0; }
.corroboration-choix form { margin: 0; }
.corroboration-choix button { width: 100%; }
/* Couvert par le bloc unique du haut de feuille, au rang « seconde ». */

/* Le genre d'un élément du carnet ou du courrier. */
.genre { font-family: var(--sans); font-size: var(--t-micro); color: var(--encre-douce); }

/* L'état vide, jamais un écran blanc. On dit ce qui manque ET le geste qui le
   comble : arriver dans une pièce vide sans savoir quoi faire est la façon la
   plus sûre de ne pas revenir. */
/* PAS de filet : quand l'écran est vide, ce trait était le premier objet
   graphique après le titre — on annonçait une absence par une règle, le jour 1
   d'un cousinage, au moment exact où l'on décide de revenir ou non. */
/* ------------------------------------------------------------------
   UN ÉCRAN VIDE N'EST PAS UN ÉCRAN NOIR
   ------------------------------------------------------------------
   L'état vide existait sur dix-huit écrans, mais il était rendu comme du texte
   ordinaire : un titre, un paragraphe gris, puis mille trois cents pixels de
   néant en dessous. Relevé sur deux captures : « l'utilisateur âgé conclut que
   l'application est cassée ».

   Pour ce public, un écran muet SE LIT COMME UNE PANNE. Il faut donc que le
   vide soit visiblement une réponse, et non une absence de réponse : un cadre
   en pointillé — jamais un aplat, qui se lirait comme un chargement inachevé —,
   du contenu centré, et le geste qui comble le manque à portée de pouce. */
.vide { display: flex; flex-direction: column; align-items: center;
  justify-content: center; text-align: center;
  min-height: 14rem; padding: var(--e7) var(--e5); margin: var(--e5) 0;
  border: 1px dashed var(--filet); border-radius: var(--r-moyen); }
.vide__titre { font-family: var(--serif); font-size: var(--t-sous-titre);
  font-weight: var(--g-appuyee); margin: 0 0 var(--e2); }
.vide p { margin: 0 0 var(--e4); color: var(--encre-douce); max-width: 34ch; }
.vide > :last-child { margin-bottom: 0; }

/* ==================================================================
   L'ARBRE
   ==================================================================
   Un arbre se PARCOURT : rang par rang, du plus ancien au plus récent, et
   l'on se déplace en recentrant. Pas de tracé graphique, pas de lignes de
   liaison dessinées — sur un téléphone elles coûtent cher et n'apprennent
   rien de plus que l'ordre des rangs.

   Le filet vertical à gauche fait le travail d'une ligne de descendance, et
   il tient en une déclaration. */

.arbre__titre { margin: var(--e0); }

.arbre__rang { position: relative; padding: 0 0 var(--e2) var(--e4); margin: 0 0 var(--e6); }
.arbre__rang::before {
  content: ""; position: absolute; left: 0; top: .55rem; bottom: .9rem;
  width: 1px; background: var(--filet);
}

.arbre__compte {
  margin: var(--e2); font-variant-numeric: tabular-nums;
  letter-spacing: 0; text-transform: none;
}

/* Le nom d'une personne dans l'arbre étendu était un LIEN NU : bleu, souligné,
   onze fois par écran. C'est exactement ce que la règle du souligné écarte —
   « il ne se souligne pas quand il est le titre d'une ligne, la forme le dit
   déjà » — et la règle n'avait simplement jamais été appliquée ici, parce que
   la classe porte un autre nom. */
.arbre__nom { font-family: var(--serif); font-size: var(--t-sous-titre); display: block; }
a.arbre__nom { color: var(--encre); text-decoration: none;
  font-weight: var(--g-appuyee); }
a.arbre__nom:hover { text-decoration: underline; }
.arbre__dates { display: block; font-variant-numeric: tabular-nums; }
.arbre__fiche { flex: 0 0 auto; font-size: var(--t-meta); align-self: center; }

.arbre__profondeur { margin: var(--e5) 0 0; }

/* Greffer : replié par défaut. L'arbre se lit d'abord ; on ne met pas un
   formulaire de saisie devant les yeux de qui vient regarder sa famille. */
.greffe summary {
  cursor: pointer; min-height: var(--cible); display: flex; align-items: center;
  font-family: var(--sans); font-weight: var(--g-forte); font-size: var(--t-corps);
}
.greffe .formulaire { padding: var(--e2); }
/* La variante DOUCE, pour un geste répété sur chaque ligne d'une liste : la
   même case que partout, en voix grise, calée à droite. « Personnaliser ce
   motif » en gras, treize fois, se lisait comme une injonction. */
/* ------------------------------------------------------------------
   UN REPLI QUI A L'AIR D'UN GESTE, ET NON D'UN TITRE
   ------------------------------------------------------------------
   Le `summary` par défaut de ce dépôt est en corps du texte, graisse appuyée,
   encre pleine : typographiquement, un titre de section. Le chevron ajouté
   partout aide, mais ne suffit pas quand le repli contient la SEULE façon de
   faire quelque chose — « aucune chance de le voir sans aide », dixit JB devant
   celui de son profil.

   On lui donne alors un cadre et une hauteur de cible : il se lit comme un
   bouton, ce qu'il est. Réservé aux replis dont le contenu est un geste et non
   un complément — les autres restent discrets, et c'est bien. */
/* ------------------------------------------------------------------
   LE SIGNE SUR LE ROND, PLUTÔT QUE LA PHRASE À CÔTÉ
   ------------------------------------------------------------------
   « Cliquez sur le médaillon pour changer votre photo » était une phrase à lire
   pour un geste à faire. L'appareil photo posé sur le bord du rond se comprend
   sans lire, et c'est ce que font toutes les applications que la cible emploie
   déjà — la convention est acquise, on ne la réinvente pas.

   Il disparaît pendant le cadrage : à ce moment-là le geste n'est plus
   « choisir » mais « déplacer », et un appareil photo dirait le contraire. */
.medaillon__cercle { position: relative; }
/* DANS le disque, et non à cheval sur son bord. Le cercle porte
   `overflow: hidden` — il doit, pour découper la photo — si bien qu'un badge
   posé au coin n'en montrait qu'un ÉCLAT BLEU, sans forme et sans sens. « D'où
   vient le petit bout de bleu ? », a demandé JB, et c'est la bonne question :
   un signe qu'on ne reconnaît pas est pire que pas de signe du tout.

   À sept pour cent des bords, il tombe entièrement à l'intérieur du disque, sur
   la diagonale où l'œil le cherche. L'anneau sombre le détache de la photo,
   quelle qu'elle soit — on ne sait pas ce qu'il y a dessous. */
.medaillon__appareil {
  position: absolute; right: 7%; bottom: 7%; z-index: 2;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: var(--r-rond);
  background: var(--geste); color: var(--geste-encre);
  box-shadow: 0 0 0 2px rgba(18, 17, 16, .45); pointer-events: none; }
.medaillon__appareil svg { width: 17px; height: 17px; }
.medaillon__cercle:has(.medaillon__image:not([hidden])) .medaillon__appareil {
  display: none; }

/* ------------------------------------------------------------------
   DES PORTES, ET NON DES FILTRES
   ------------------------------------------------------------------
   `rangee-rubriques` fait défiler des chips courtes ; quatre destinations à
   libellés longs y débordaient, et la dernière — « Mon compte » — sortait de
   l'écran, coupée derrière une barre de défilement. On n'atteint pas une page
   dont on ne voit pas le nom.

   Empilées, chacune pleine largeur, avec la hauteur de cible : elles se lisent
   toutes, et le pouce ne vise plus. Au-delà de 40 rem elles se rangent en
   grille — la place existe alors, et quatre lignes pleine largeur y feraient
   vide. */
.rubriques-empilees { display: grid; gap: var(--e2); margin: var(--e6) 0 var(--e7); }
.rubriques-empilees > a {
  display: flex; align-items: center; gap: var(--e2);
  min-height: var(--cible); padding: 0 var(--e4);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  background: var(--surface); color: var(--encre);
  font-family: var(--sans); font-size: var(--t-corps);
  font-weight: var(--g-appuyee); text-decoration: none; }
.rubriques-empilees > a:hover { border-color: var(--encre-douce);
  background: var(--filet-clair); }
.rubriques-empilees .compteur { margin-left: auto; }

@media (min-width: 40rem) {
  .rubriques-empilees { grid-template-columns: repeat(2, 1fr); }
}

.greffe--geste > summary {
  min-height: var(--cible); padding: 0 var(--e4);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  background: var(--surface); }
.greffe--geste > summary:hover { border-color: var(--encre-douce);
  background: var(--filet-clair); }

.greffe--motif > summary { display: inline-flex; align-items: center;
  width: fit-content; margin-left: auto; min-height: 36px; padding: 0 var(--e3);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  background: var(--surface); color: var(--encre-douce);
  font-weight: var(--g-normale); font-size: var(--t-meta); list-style: none; }
.greffe--motif > summary::-webkit-details-marker { display: none; }
.greffe--motif > summary:hover { color: var(--encre);
  border-color: var(--encre-douce); background: var(--filet-clair); }

/* ==================================================================
   L'AGENDA
   ==================================================================
   Deux voix sur le même écran, et c'est délibéré. L'événement SE LIT : il
   prend la romaine du faire-part, comme le carnet. Ce qui l'entoure — le
   découpage en moments, les jauges, le carnet d'invités — S'UTILISE, et reste
   en linéale.

   La jauge est le seul objet neuf. Un filet plein sur un filet clair, deux
   pixels de haut : elle dit d'un coup d'œil s'il reste de la place, sans
   couleur d'alerte ni pourcentage géant. Le chiffre exact est écrit à côté —
   une barre seule ne se lit pas à 80 ans, et c'est le chiffre que
   l'organisateur vient chercher. */

.creer-evenement { margin: 0 0 var(--e6); }
.creer-evenement summary { cursor: pointer; min-height: var(--cible);
  display: flex; align-items: center; font-weight: var(--g-forte); font-size: var(--t-corps); }
.creer-evenement .formulaire { padding: var(--e2); }

/* Ces champs sont couverts par la règle générale des champs, en tête de cette
   feuille. Le bloc qui les redéclarait ici a été retiré : son raccourci
   `background:` écrasait `background-repeat`, et le chevron des listes
   déroulantes se répétait alors une douzaine de fois en travers du champ. */

.jauge { display: block; margin: var(--e1); }
/* SANS piste : elle traversait la colonne à douze pixels du filet de la ligne,
   si bien que trois moments au programme donnaient six traits. Le remplissage
   seul dit la même chose — un segment court se lit comme une proportion — et le
   chiffre à côté porte déjà le compte exact. */
.jauge__barre { background: none; }
.jauge__remplissage { display: block; height: 2px; background: var(--encre-douce); }
.jauge__chiffre { display: block; font-size: var(--t-meta);
  color: var(--encre-douce); font-variant-numeric: tabular-nums; }

/* ==================================================================
   L'ALBUM
   ==================================================================
   Voix ÉDITORIALE : un album de famille se regarde, il ne se gère pas. La
   grille elle-même reste muette — pas de cadre, pas d'ombre, pas de coin
   arrondi. Deux pixels de blanc séparent les photos, et c'est tout : le chrome
   autour d'une image ne dit rien de plus que l'image. */

/* ------------------------------------------------------------------
   `.album__titre` PORTAIT DEUX SENS, ET LE SECOND GAGNAIT
   ------------------------------------------------------------------
   Elle était déclarée ici pour le TITRE de l'écran — `<h1>Album</h1>` — et
   trois cents lignes plus bas pour la LÉGENDE sous une vignette : corps micro,
   encre douce, coupée à l'ellipse. Même spécificité, la seconde plus bas dans
   le fichier : c'est elle qui l'emportait, et le titre de l'écran s'affichait
   en taille de légende, en gris, sans sa marge.

   JB, le 30 août 2026 : « le Album du début mal aligné, police étrange, on n'y
   comprend pas grand-chose ». Rien dans le gabarit ne le disait — le `<h1>` est
   correctement écrit, et c'est la feuille qui le déguisait.

   Le titre reprend le style commun des `<h1>`, et la légende s'appelle
   désormais `.album__case-titre` : le nom dit à quoi il appartient, et personne
   ne peut le reprendre pour autre chose. Le filet des classes ambiguës tient
   la règle — voir `gabarits_et_filets_test.rb`. */

/* ------------------------------------------------------------------
   D'OÙ LA PHOTO PART — LISIBLE SUR LA VIGNETTE, DANS LA VUE MÊLÉE
   ------------------------------------------------------------------
   Posée SUR l'image, en bas, et non sous elle : sous la vignette, elle
   allongerait chaque case d'une ligne et défferait la grille. Le voile sombre
   la rend lisible sur une photo claire comme sur une photo sombre — c'est le
   seul endroit du produit où l'on écrit par-dessus une image, et c'est parce
   que la grille ne laisse aucune autre place. */
.album__origine {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  padding: var(--e1) var(--e2);
  font-family: var(--sans); font-size: var(--t-micro); line-height: 1.2;
  color: #fff; background: linear-gradient(transparent, rgba(0, 0, 0, .62));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  pointer-events: none;
}
/* Elle s'efface quand on coche : la case et l'origine se disputeraient la
   même vignette, et pendant qu'on range on regarde ce qu'on coche. */
.album-ranger[open] ~ .album .album__origine { opacity: .55; }

/* ------------------------------------------------------------------
   CHOISIR SA FAMILLE — UN SEUL MOTIF, `rangee-rubriques`
   ------------------------------------------------------------------
   `.album__famille` vivait ici et servait aux ARCHIVES, l'album étant passé
   aux pastilles le 25 août. Deux styles pour un seul geste : celui resté en
   arrière ne disait plus qu'il se cliquait, et son interligne de paragraphe
   écartait les deux noms. Retiré, faute de quoi le prochain écran le reprend.
   Voir `.rangee-rubriques`, plus haut. */

/* Même chose ici : le second bloc redéclarait `.formulaire select` et écrasait
   à son tour le chevron. Un champ de fichier hors zone de dépôt garde son
   plancher de cible tactile — 48 px, sans quoi il est inatteignable pour une
   main de 80 ans. */
input[type="file"] {
  font-family: var(--sans); font-size: var(--t-corps);
  min-height: var(--cible); max-width: 100%;
}

.album__compte { margin: var(--e2); font-variant-numeric: tabular-nums;
  letter-spacing: 0; text-transform: none; }

/* `auto-fill` plutôt qu'un nombre de colonnes fixe : trois sur un téléphone,
   cinq sur une tablette, sans point de rupture à maintenir. */
.album { display: grid; gap: var(--e0); margin: var(--e3) 0 var(--e4);
  grid-template-columns: repeat(auto-fill, minmax(min(6.5rem, 100%), 1fr)); }

/* ------------------------------------------------------------------
   LE MODE « RANGER » SE PREND SANS UNE LIGNE DE JAVASCRIPT
   ------------------------------------------------------------------
   La règle de mise en page du module est « lire, puis ajouter, puis ranger » :
   on arrive pour REGARDER. Une grille couverte de cases à cocher est une grille
   qu'on ne regarde plus, et l'on range rarement — jamais en premier.

   Le repli ouvert révèle les cases ET la barre du bas, par un sélecteur de
   frère suivant. Pas de `:has()`, pas de script, pas d'aller-retour : le
   `<details>` est posé AVANT la grille précisément pour que `~` suffise.

   Les cases restent dans le document quand le mode est fermé : `display: none`
   les retire de l'ordre de tabulation comme de la restitution vocale, ce que
   `visibility` ou une opacité nulle ne feraient pas. */
/* ------------------------------------------------------------------
   CE QU'ON REGARDE, ET SON RÉGLAGE, SUR UNE SEULE LIGNE
   ------------------------------------------------------------------
   Le titre de la grille, le compte et l'ordre de lecture se suivaient sur
   trois lignes, dont l'une au-dessus des deux autres. Ils répondent à la même
   question — « qu'est-ce que je regarde en ce moment ? » — et se lisent donc
   ensemble. Sur téléphone, l'ordre passe à la ligne plutôt que de serrer le
   titre. */
.album-etat {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--e2) var(--e4);
  margin: var(--e5) 0 var(--e3);
}
.album-etat .rubrique { margin: 0; }
/* ------------------------------------------------------------------
   L'ORDRE DE LECTURE EST UN LIEN, DONC UNE CIBLE DE 48 PX
   ------------------------------------------------------------------
   Il faisait 23 px de haut, et la sonde de tenue sur téléphone l'a refusé sous
   Linux : le carré de 44 px qu'elle interroge tombait sur le titre au-dessus et
   sur la barre d'onglets en dessous. Un lien de réglage se vise avec le pouce
   comme les autres — et `align-items: center` sur la ligne, sans quoi une boîte
   de 48 px alignée sur la ligne de base décalerait le titre. */
.album-etat__ordre { display: inline-flex; align-items: center;
  min-height: var(--cible);
  font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); }
.album-etat__ordre:hover { color: var(--lien); }

.album__case { position: relative; }
.album__coche { display: none; }
.album-ranger[open] ~ .album .album__coche {
  display: flex; position: absolute; top: var(--e1); left: var(--e1); z-index: 2;
  align-items: center; justify-content: center;
  /* 44 px : la cible tactile minimale du référentiel. Une case de 24 px posée
     sur une vignette de 6,5 rem se rate une fois sur trois au pouce. */
  min-width: 44px; min-height: 44px;
  background: var(--surface); border-radius: var(--r-petit);
  box-shadow: 0 1px 3px rgb(0 0 0 / 25%);
}
.album__coche input { width: 24px; height: 24px; margin: 0; }
/* Cochée, la vignette se retire un peu : on voit CE QU'ON A PRIS sans avoir à
   relire les cases une à une. */
.album__case:has(input:checked) .album__photo { outline: 3px solid var(--lien); outline-offset: -3px; }
.album__case:has(input:checked) img { opacity: .82; }

.album__case-titre { display: block; padding: var(--e1) var(--e1) 0;
  font-size: var(--t-micro); color: var(--encre-douce);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Une demande de retrait est un DOSSIER qu'on arbitre, pas une ligne de liste :
   elle porte un motif, une phrase écrite par quelqu'un, et des boutons dont
   chacun a une conséquence différente. Elle prend donc le poids d'une section. */
.retrait { margin: var(--e4) 0; padding: var(--e4);
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-petit); }
.retrait .actions { flex-wrap: wrap; gap: var(--e2); }

.album-vers-prive { display: flex; flex-direction: column; gap: var(--e0);
  margin: var(--e3) 0; }
/* La CIBLE, et non le texte : ce lien faisait 29 px de haut, et la sonde des
   cibles tactiles l'a attrapé le jour même où je l'ai posé. `--cible` vaut
   48 px — mieux que les 24 de WCAG 2.5.8 —, et un lien de navigation qui n'y
   satisfait pas se rate au pouce une fois sur trois. */
.album-vers-prive a { display: inline-flex; align-items: center;
  min-height: var(--cible); }

.album-ranger { margin: var(--e3) 0; }
/* ------------------------------------------------------------------
   UNE COMMANDE A LA LARGEUR DE SON TEXTE
   ------------------------------------------------------------------
   Ce `summary` prenait toute la largeur de la colonne, chevron poussé à l'autre
   bout : sur un écran large, quatre cent cinquante pixels séparaient « Ranger
   plusieurs photos » du signe qui dit qu'il se déplie. À cette distance le
   chevron ne se rattache plus à rien, et la ligne se lit comme un TROISIÈME
   titre de section — juste au-dessus de la grille, c'est-à-dire à l'endroit où
   l'on croit lire le nom de ce qu'on regarde.

   C'est le défaut que JB a nommé sur « Créer un dossier » le 30 août 2026, et
   il vivait ici aussi : on corrige une branche et l'on oublie sa jumelle. La
   commande prend donc la largeur de son texte — c'est le motif `.greffe--motif`
   déjà tranché pour les gestes répétés d'une liste. */
.album-ranger > summary, .commande-pliee > summary {
  min-height: var(--cible); width: fit-content;
  display: inline-flex; align-items: center; gap: var(--e2);
  padding: 0 var(--e3); border: 1px solid var(--filet);
  border-radius: var(--r-moyen); background: var(--surface);
  color: var(--encre-douce); font-size: var(--t-meta); }
.album-ranger > summary::after, .commande-pliee > summary::after { margin-left: 0; }
.album-ranger > summary:hover, .commande-pliee > summary:hover { color: var(--encre);
  border-color: var(--encre-douce); background: var(--filet-clair); }
/* LES GESTES D'UN DOSSIER SUR UNE SEULE RANGÉE. `.actions` étirait son unique
   enfant sur toute la largeur — `flex: 1 1 12rem` — et « Emporter ce dossier »
   devenait un bouton de mille pixels pour un geste secondaire. Ici chacun prend
   la largeur de son texte ; seul le repli ouvert réclame sa ligne entière, ce
   qui est juste puisqu'il porte un formulaire. */
.dossier-gestes { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--e2); margin: var(--e3) 0 var(--e5); }
.dossier-gestes > * { flex: 0 0 auto; }
.dossier-gestes .commande-pliee[open] { flex: 1 1 100%; }

.commande-pliee { margin: var(--e3) 0; }
.commande-pliee__corps { padding: var(--e3) 0 0; max-width: 28rem; }

.album-lot { display: none; }
.album-ranger[open] ~ .album-lot {
  display: block; margin: var(--e4) 0; padding: var(--e4);
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-petit);
}
.album-lot label { display: block; margin-top: var(--e3); }
.album-lot input[type=text], .album-lot select { width: 100%; }
.album-lot input[type=submit] { margin-top: var(--e3); }

/* ------------------------------------------------------------------
   TROIS BLOCS, ET AUCUN BOUTON PLEIN
   ------------------------------------------------------------------
   Le panneau alignait cinq boutons pleins en colonne, chacun suivi de son
   explication, le tout décalé de dix-huit pixels par le retrait qu'un
   navigateur donne d'office à un `<fieldset>`. JB, le 30 août 2026 : « on n'a
   jamais vu une interface aussi moche ». Deux causes distinctes.

   UN BOUTON PLEIN dit « c'est L'action de cet écran ». Il y en avait cinq :
   donc aucune. Ils passent au second rang, parce qu'on en CHOISIT un.

   ET DIX-HUIT PIXELS DE RETRAIT ne se lisent pas comme un groupe : ils se
   lisent comme un défaut d'alignement. Ce qui groupe, c'est un FILET et un
   titre — pas un décalage qu'on remarque sans savoir pourquoi.

   Les boutons d'une même paire vivent sur une RANGÉE : « garder » et
   « rendre » sont l'aller et le retour d'un même mouvement, et empilés avec
   une explication entre eux ils se retrouvaient à trois cents pixels l'un de
   l'autre. */
.lot-bloc { margin: var(--e5) 0 0; padding: var(--e4) 0 0;
  border-top: 1px solid var(--filet-clair); }
.lot-bloc:first-of-type { margin-top: var(--e4); }
.lot-bloc > h3 { margin: 0 0 var(--e1); }
.lot-bloc > .secondaire { margin: 0 0 var(--e2); }
.lot-bloc__suite { margin-top: var(--e4) !important; }
/* `flex-wrap` : deux libellés longs — « Les rendre à la famille », « Ne plus
   les lui montrer » — ne tiennent pas côte à côte sur un téléphone, et un
   bouton coupé en deux est pire que deux boutons empilés. */
.lot-bloc__gestes { display: flex; flex-wrap: wrap; gap: var(--e2);
  margin-top: var(--e3); }

/* Sur une fiche, la grille est un ÉCHANTILLON et non un album : douze vignettes
   plus petites, qui disent « il y a des photos de lui » sans prendre la moitié
   de l'écran à quelqu'un venu chercher une adresse. */
.album--fiche { grid-template-columns: repeat(auto-fill, minmax(min(5rem, 100%), 1fr)); }

/* La ligne qui rappelle de qui il s'agit quand le dépôt d'un acte part d'une
   fiche : on ne redemande pas ce qu'on sait déjà, on le CONFIRME. */
.rappel-concerne { margin: 0; padding: var(--e2) var(--e3);
  border-radius: var(--r-petit); background: var(--filet-clair);
  font-size: var(--t-meta); }

/* Ce qu'un demandeur a écrit, rendu mot pour mot : c'est la seule chose sur
   quoi le régisseur d'en face puisse décider, et elle doit se lire comme une
   citation et non comme un champ de formulaire. */
.alliance__explication { margin: 0 0 var(--e2); padding: var(--e2) var(--e3);
  border-left: 2px solid var(--filet); background: var(--filet-clair);
  border-radius: 0 var(--r-petit) var(--r-petit) 0; font-size: var(--t-meta); }

/* Une liste qu'on LIT, et non un inventaire à cocher : les puces disent qu'il y
   a plusieurs choses et que la liste est close, ce qu'un paragraphe en prose
   laisse toujours douteux. Sert là où l'on énumère ce qu'un geste ouvre et ce
   qu'il laisse fermé — c'est-à-dire là où l'on ne veut aucune ambiguïté. */
.liste-simple { margin: 0 0 var(--e3); padding-left: var(--e4); }
.liste-simple li { margin: 0 0 var(--e1); }

/* Une invitation que l'arrivée a devancée. Le ton reste celui d'une remarque
   et non d'une alarme : ce n'est pas une erreur, c'est un doublon probable, et
   c'est l'inviteur qui tranche. Même dessin que `.rappel-concerne`, dont c'est
   exactement le rôle — rappeler un fait connu à l'endroit où il sert. */
.mention-caduque { flex-basis: 100%; margin: var(--e1) 0 0;
  padding: var(--e2) var(--e3); border-radius: var(--r-petit);
  background: var(--filet-clair); font-size: var(--t-meta);
  color: var(--encre-douce); }

/* Le fond porte l'empreinte visuelle stockée en base. Sans elle, cent vingt
   vignettes arrivent dans le désordre sur la 4G de campagne et la page saute à
   chaque image reçue — le doigt appuie alors sur la photo qui vient de se
   déplacer. */
.album__photo { display: block; aspect-ratio: 1; overflow: hidden;
  background-color: var(--filet-clair);
  background-size: cover; background-position: center; }
.album__photo img { display: block; width: 100%; height: 100%; object-fit: cover; }

.album__reste { margin: 0 0 var(--e6); }

/* La photo seule. Elle occupe toute la largeur utile et rien ne l'encadre :
   c'est la pièce, pas son support. */
.photo { margin: 0 0 var(--e6); }
.photo__image { display: block; width: 100%; height: auto;
  background-color: var(--filet-clair);
  background-size: cover; background-position: center; }
.photo__legende { margin: var(--e3) 0 var(--e2); font-size: var(--t-meta);
  color: var(--encre-douce); }

/* Le retrait. Discret et complet à la fois : il ne se met pas en avant, mais
   il ne se cherche pas non plus — un droit qu'on ne trouve pas ne s'exerce
   jamais. */
.retrait { margin: var(--e6) 0 var(--e7); }

/* ==================================================================
   LE CARNET MONDAIN, LE FAIRE-PART ET LE CONSENTEMENT
   ==================================================================
   Trois registres sur trois écrans, et les confondre coûte des trois côtés.

   LE CARNET SE LIT — romaine, filets, rubriques en petites capitales. Il
   réutilise `.editorial`, `.rubrique` et `.entree`, déjà en place.

   LE FAIRE-PART SE CONTEMPLE — fer à centre, interlignage large, une seule
   famille typographique, hiérarchie par le corps et par l'espace, jamais par le
   gras. C'est le seul endroit du produit où un texte est centré : partout
   ailleurs le fer à gauche reste la règle.

   LE CONSENTEMENT S'UTILISE — linéale, un récapitulatif encadré de deux filets,
   et deux issues de MÊME SURFACE. Un consentement dont le refus coûte plus cher
   que l'accord n'est pas libre au sens de l'article 4.11. */

.carnet__rubrique { margin: 0 0 var(--e6); }
.carnet__mois { display: flex; align-items: center; gap: var(--e2);
  margin: var(--e5) 0 0; }

/* Les formules d'usage et la composition : repliées. On ne met pas un traité
   de typographie devant quelqu'un qui vient annoncer une naissance — mais on
   ne le lui cache pas non plus. */
.formules summary, .composition summary {
  cursor: pointer; min-height: var(--cible); display: flex; align-items: center;
  font-size: var(--t-meta); color: var(--encre-douce); }
.formules p { margin: 0 0 var(--e3); }

.sacrement-champs legend { font-size: var(--t-meta); font-weight: var(--g-forte);
  color: var(--encre-douce); }

.faire-part { font-family: var(--serif); text-align: center;
  margin: var(--e6) 0 var(--e7); padding: var(--e7) var(--e5); line-height: 1.75; }
.faire-part p { margin: 0 0 var(--e4); }

/* Le sujet est le point focal : le seul endroit du produit qui monte au-dessus
   du titre, et il n'y monte que de deux crans. Aucune banderole. */
.faire-part__sujet { font-size: var(--t-titre); line-height: 1.25; }
.faire-part__annonceurs { font-variant: small-caps; }
.faire-part__verbe { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); letter-spacing: .02em; }
.faire-part__motif { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); margin: var(--e6); }

/* ------------------------------------------------------------------
   LA VUE DE MÉNAGE — TROIS RANGS DE BOÎTES, ET LA DIRECTION SE LIT
   ------------------------------------------------------------------
   Les parents au-dessus, le couple au centre, les enfants en dessous.

   CHAQUE PERSONNE EST UNE BOÎTE, ET LA BOÎTE EST LE BOUTON. La première
   version alignait des noms soulignés dans des paragraphes : on les lisait
   sans voir qu'ils menaient quelque part, et sur un téléphone il fallait viser
   une ligne de texte de dix-neuf pixels. La boîte règle les deux d'un coup —
   elle se voit comme un chemin, et elle offre une cible franche.

   La grille se remplit d'elle-même : deux boîtes de front sur un téléphone,
   quatre sur un écran large, sans point de rupture à tenir à jour. */
.menage-vue { margin: var(--e6) 0; }

/* ------------------------------------------------------------------
   COU-ACC-005 — `min(…, 100%)` OU LA GRILLE DÉBORDE AU GROS TEXTE
   ------------------------------------------------------------------
   `minmax(8.5rem, 1fr)` promet des colonnes d'au moins 8,5 rem. Or `rem` suit
   le réglage de confort : à `tres_grand`, la racine est à 133 %, et 8,5 rem
   valent 133 % de plus en pixels. Passé le point où le conteneur est plus
   étroit que ce minimum, la piste ne se réduit PAS — elle déborde, et les
   enfants d'une fratrie sortent par la droite. Mesuré par l'audit : à 200 % de
   zoom et texte très grand, on perdait des enfants entiers.

   `min(8.5rem, 100%)` laisse la piste descendre sous le minimum quand il n'y a
   pas la place : la fratrie passe alors en UNE CARTE PAR LIGNE, ce que l'audit
   demandait, plutôt qu'en défilement horizontal invisible.

   NEUF GRILLES PORTAIENT LE MÊME MOTIF — l'arbre, l'album, les maisons, les
   destinations. Ce n'est pas neuf défauts, c'est une propriété de `minmax` avec
   une unité relative, et elle se traite d'un coup. `tenue_sur_telephone` mesure
   désormais le débordement pour qu'on ne la repose pas en écrivant la dixième. */
.noeuds { display: grid; gap: var(--e2); margin: var(--e1) 0 var(--e6);
  grid-template-columns: repeat(auto-fit, minmax(min(8.5rem, 100%), 1fr)); }

.noeud {
  display: flex; flex-direction: column; justify-content: center; gap: var(--e0);
  min-height: var(--cible); padding: var(--e2) var(--e3);
  background: var(--surface); border: 1px solid var(--filet); border-radius: var(--r-petit);
  color: var(--encre); text-decoration: none;
}
a.noeud { transition: border-color .12s, background-color .12s; }
a.noeud:hover { border-color: var(--lien); background: var(--filet-clair); }
a.noeud:focus-visible { outline: 2px solid var(--lien); outline-offset: 2px; }
a.noeud:active { background: var(--filet); }
@media (prefers-reduced-motion: reduce) { a.noeud { transition: none; } }

/* Le centre est rempli et n'est PAS un lien : on y est déjà. C'est aussi la
   seule boîte qui puisse en contenir un — d'où la fiche, qui n'a pas d'autre
   place possible sans imbriquer un lien dans un lien. */
.noeud--centre { background: var(--filet-clair); border-color: var(--encre-douce); }
.noeud__bas { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--e3); }
/* ------------------------------------------------------------------
   « SA FICHE » — 60 × 23 PIXELS, ET LE PLANCHER EST À 48
   ------------------------------------------------------------------
   Relevé par la sonde des cibles tactiles le 23 août 2026, sur `/famille`. Ce
   lien vivait déjà dans l'arbre, mais l'onglet « Arbre » menait alors à
   l'annuaire : la sonde ne le voyait pas. La bascule l'a mis sur le chemin le
   plus fréquenté du produit, et le défaut est apparu du même coup.

   C'est la ZONE qui s'étend, pas le dessin : le texte garde sa taille de note,
   et l'on ne fait pas grossir un lien secondaire au milieu d'une case d'arbre.
   Même remède que le médaillon de profil, pour la même raison. */
.noeud__fiche { font-family: var(--sans); font-size: var(--t-meta);
  min-height: var(--cible); display: inline-flex; align-items: center;
  padding: 0 var(--e2); }

/* De VRAIES petites capitales, comme les huit autres emplois du produit. Deux
   règles employaient encore `text-transform: uppercase` — « PÈRE », « MÈRE »
   sur l'écran le plus regardé de l'arbre, et la légende de tout formulaire
   long. La feuille interdit pourtant les fausses capitales en toutes lettres :
   « c'est le détail qui sépare un carnet d'un formulaire ». */
.noeud__role { font-family: var(--sans); font-size: var(--t-onglet);
   color: var(--encre-douce); }
.noeud__nom { font-family: var(--serif); font-size: var(--t-sous-titre); line-height: 1.25; }
.noeud__nee { font-family: var(--serif); font-size: var(--t-meta);
  font-style: italic; color: var(--encre-douce); }
.noeud__dates { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); font-variant-numeric: tabular-nums; }

/* Le foyer : le couple tient sur une ligne dès qu'il y a la place, et se
   superpose sur un téléphone. Les deux boîtes se lisent alors comme un seul
   objet, ce qu'un ménage est. */
.foyer { display: grid; gap: var(--e2); margin: var(--e1) 0 var(--e6); }
@media (min-width: 30rem) { .foyer { grid-template-columns: 1fr 1fr; } }

.menage-vue__mode { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); margin: -.6rem 0 var(--e5); }


/* ==================================================================
   LES TRAITS DE FILIATION
   ==================================================================
   L'arbre ne dessinait aucun arbre. Trois rangées de boîtes empilées, et pas
   un trait : ce que la case du haut est à celle du milieu, on le lisait dans
   un titre — « Ses parents » — au lieu de le voir. C'est l'écran qui vend le
   produit, et c'était le seul du lot qui ne montrait pas ce qu'il affirme.

   ------------------------------------------------------------------
   POURQUOI DU CSS SUR LES BOÎTES, ET NON UN SVG AUX COORDONNÉES CALCULÉES
   ------------------------------------------------------------------
   Un tracé SVG suppose de connaître la position de chaque case AVANT le rendu.
   Or la rangée se replie : sept enfants tiennent sur une ligne d'ordinateur et
   sur trois lignes de téléphone, et le nombre de colonnes est décidé par
   `auto-fit` au moment de la mise en page. Un SVG calculé à l'avance serait
   juste sur un écran et faux sur tous les autres — et faux d'une façon qui ne
   se voit pas en développement, où l'on regarde toujours le même.

   CHAQUE CASE PORTE DONC SON PROPRE TRAIT : une descente verticale, et sa
   moitié de rail horizontal. Le rail se reconstitue de proche en proche, la
   première case ne dessine que sa moitié droite, la dernière que sa gauche.
   Quand la rangée se replie, chaque ligne obtient naturellement son rail — ce
   qui est exactement la bonne lecture, et ce qu'aucun tracé absolu n'aurait
   donné.

   Le rail déborde de la moitié de la gouttière de part et d'autre
   (`calc(var(--e2) / -2)`) : sans ce débord, il s'interromprait dans chaque
   espace entre deux cases, et l'on obtiendrait des tirets au lieu d'un trait.
   ------------------------------------------------------------------ */
/* ------------------------------------------------------------------
   LA GOUTTIÈRE EST TENUE PAR LE `gap`, ET PAR RIEN D'AUTRE
   ------------------------------------------------------------------
   Les traits doivent ABOUTER. Tant que chaque bloc portait sa propre marge —
   `margin: var(--e1) 0 var(--e6)` sur les rangées, une autre sur le titre —
   la distance entre deux rangées était la somme de trois valeurs dont deux
   fusionnaient, et aucun trait ne pouvait la traverser exactement : il
   s'arrêtait deux millimètres trop tôt, ou dépassait dans le vide.

   Une colonne flex avec UN `gap` connu, et toutes les marges des enfants
   remises à zéro : la géométrie devient calculable, et `var(--saut)` est la
   seule mesure à laquelle les traits se réfèrent. */
.arbre-dessine { --saut: 1.6rem; --trait: 1px;
  display: flex; flex-direction: column; gap: var(--saut); }
.arbre-dessine > * { margin: 0; }

/* Le trait est plus soutenu que le filet des bordures : il porte du SENS —
   qui descend de qui — là où une bordure ne fait que délimiter. Au filet
   clair, il disparaissait à un mètre de l'écran. */
.arbre-dessine { --encre-filiation: var(--encre-douce); }

.noeuds--ascendants,
.noeuds--descendants { position: relative; }

/* Les cases se relient : il leur faut un repère pour leurs pseudo-éléments. */
.noeuds--ascendants > *,
.noeuds--descendants > * { position: relative; }

/* ---------- LE TITRE DE RANG : LE TRONC LE TRAVERSE ----------------------
   « Ses enfants » se pose entre le foyer et la rangée : le tronc devait donc
   soit le contourner, soit s'interrompre. On le fait PASSER — le titre est
   court et aligné à gauche, le tronc tombe dans la moitié droite restée
   vide. Sa hauteur est celle du titre plus la demi-gouttière qui le sépare du
   rail : deux mesures connues, donc un trait qui aboute exactement. */
.rubrique--filiation { position: relative; }
.rubrique--descend::before {
  content: ""; position: absolute; left: 50%; top: 0;
  width: var(--trait); height: calc(100% + var(--saut) / 2);
  background: var(--encre-filiation); }

/* ------------------------------------------------------------------
   UNE SEULE LIGNE, QUI DÉFILE — ET NON UNE GRILLE QUI SE REPLIE
   ------------------------------------------------------------------
   La grille `auto-fit` renvoyait le septième enfant à la ligne, et le rail du
   sixième partait alors vers le bord droit sans rien y rejoindre : un trait
   qui pointe vers personne, c'est-à-dire exactement ce qu'un arbre ne doit
   jamais dessiner. Les familles visées ont couramment sept enfants ; ce n'est
   pas le cas rare.

   La fratrie tient donc sur UNE ligne, qui défile latéralement quand elle
   déborde — comme le bandeau des nouvelles et la rangée des registres, avec
   le même estompage. Le rail reste alors juste par construction.

   Le `padding` remplace la gouttière (annulée par la marge négative) : les
   traits sont ainsi dessinés DANS la boîte de remplissage du conteneur, que
   le défilement ne rogne pas. Posés dans la gouttière, ils auraient été
   coupés net par `overflow`. */
.noeuds--ascendants,
.noeuds--descendants {
  position: relative; display: flex; gap: var(--e2);
  justify-content: safe center; overflow-x: auto;
  scrollbar-width: none; }
.noeuds--ascendants::-webkit-scrollbar,
.noeuds--descendants::-webkit-scrollbar { display: none; }

.noeuds--ascendants > *,
.noeuds--descendants > * { position: relative;
  flex: 0 0 clamp(8rem, 42vw, 10.5rem); }

/* ---------- LES ENFANTS : le rail au-dessus, la descente vers la case ---- */
.noeuds--descendants { padding-top: var(--saut);
  margin-top: calc(var(--saut) * -1) !important; }

.noeuds--descendants > *::before {
  content: ""; position: absolute; left: 50%; top: calc(var(--saut) / -2);
  width: var(--trait); height: calc(var(--saut) / 2);
  background: var(--encre-filiation); }

.noeuds--descendants > *::after {
  content: ""; position: absolute; top: calc(var(--saut) / -2);
  left: calc(var(--e2) / -2); right: calc(var(--e2) / -2);
  height: var(--trait); background: var(--encre-filiation); }

/* Le premier ne dessine que sa moitié droite, le dernier que sa gauche : sans
   quoi le rail dépasserait des deux côtés, et pointerait vers personne. */
.noeuds--descendants > *:first-child::after { left: 50%; }
.noeuds--descendants > *:last-child::after { right: 50%; }
/* Un enfant unique n'a pas de rail : une barre horizontale de zéro largeur
   reste un point, et un point posé au-dessus d'une case se lit comme un
   défaut d'affichage. */
.noeuds--descendants > *:only-child::after { content: none; }

/* ---------- LES PARENTS : la descente sous la case, puis le rail ---------- */
.noeuds--ascendants { padding-bottom: var(--saut);
  margin-bottom: calc(var(--saut) * -1) !important; }

.noeuds--ascendants > *::before {
  content: ""; position: absolute; left: 50%; top: 100%;
  width: var(--trait); height: calc(var(--saut) / 2);
  background: var(--encre-filiation); }

.noeuds--ascendants > *::after {
  content: ""; position: absolute; top: calc(100% + var(--saut) / 2);
  left: calc(var(--e2) / -2); right: calc(var(--e2) / -2);
  height: var(--trait); background: var(--encre-filiation); }

.noeuds--ascendants > *:first-child::after { left: 50%; }
.noeuds--ascendants > *:last-child::after { right: 50%; }
.noeuds--ascendants > *:only-child::after { content: none; }

/* ---------- LE FOYER : le tronc qui monte, celui qui descend -------------
   Le tronc part du MILIEU du couple, qui est le milieu du conteneur : c'est
   la barre de l'alliance des chartes de généalogie, d'où part la descendance.
   Seul quand il n'y a pas de conjoint, le foyer est donc CENTRÉ — sans quoi
   la case occuperait la moitié gauche et le tronc tomberait à côté d'elle.

   IL PREND LA MÊME LARGEUR DE CASE QUE LES AUTRES RANGS, un cran plus large.
   En grille `1fr 1fr`, les deux cases du couple s'étiraient sur toute la
   colonne pendant que parents et enfants tenaient en dix rem : le foyer
   devenait une dalle, et l'arbre n'avait plus de forme d'arbre. Un cran plus
   large tout de même — c'est le ménage qu'on regarde en arrivant. */
.arbre-dessine > .foyer { display: flex; flex-wrap: wrap; gap: var(--e2);
  justify-content: center; }
/* `flex: 1 1` avec un plafond, et non une largeur fixe : à 320 px les deux
   cases ne tiennent pas côte à côte, et une largeur fixe les aurait fait
   déborder de la page — le défilement horizontal du corps est la seule chose
   que cette feuille s'interdit sans condition. Elles se replient d'elles-mêmes,
   sans point de rupture à tenir à jour. */
.arbre-dessine > .foyer > * { flex: 1 1 9rem; max-width: 13rem; min-width: 0; }
.foyer { position: relative; }

/* Le dernier segment sous le rail des parents. Il est porté par le FOYER et
   conditionné au voisinage immédiat d'une rangée d'ascendants : posé sur la
   rangée elle-même, il serait tombé hors de sa boîte et le défilement l'aurait
   rogné ; posé sans condition, il aurait paru sur une fiche sans parents, où
   il serait sorti du néant. */
.noeuds--ascendants + .foyer::before {
  content: ""; position: absolute; left: 50%; bottom: 100%;
  width: var(--trait); height: calc(var(--saut) / 2);
  background: var(--encre-filiation); }

/* Le tronc vers le bas n'existe QUE s'il y a une descendance : un trait qui
   sort du foyer et ne rejoint rien annonce des enfants qu'on n'a pas. */
.foyer--descend::after {
  content: ""; position: absolute; left: 50%; top: 100%;
  width: var(--trait); height: var(--saut);
  background: var(--encre-filiation); }

/* ---------- LA BARRE DE L'ALLIANCE ---------------------------------------
   Entre les deux cases du couple, à hauteur de leur milieu : c'est le signe
   « = » des chartes, et c'est lui qui dit que la descendance vient des DEUX.
   Sur un téléphone les cases s'empilent, et la barre devient VERTICALE — la
   même information, dans le sens où la mise en page les a posées. */
.arbre-dessine > .foyer > * { position: relative; }
.arbre-dessine > .foyer > :first-child:not(:only-child)::after {
  content: ""; position: absolute; left: 100%; top: 50%;
  width: var(--e2); height: var(--trait);
  background: var(--encre-filiation); }

/* Sous 22 rem, le couple ne tient plus côte à côte et se replie : la barre de
   l'alliance bascule alors à la verticale, sans quoi elle pointerait vers la
   droite, où il n'y a plus personne. La valeur suit la base de 9 rem des deux
   cases (9 + 9 + la gouttière), elle n'est pas choisie au jugé. */
@media (max-width: 22rem) {
  .arbre-dessine > .foyer > :first-child:not(:only-child)::after {
    top: 100%; left: 50%; height: var(--e2); width: var(--trait); }
}

/* L'estompage à droite dit « il y en a d'autres » — même geste que le bandeau
   des nouvelles. Une fratrie de sept ne tient pas sur un écran, et un rail
   tranché net au bord se lit comme un défaut d'affichage. */
.noeuds--ascendants, .noeuds--descendants {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 2rem), transparent); }
.noeuds--ascendants:not(:has(> :nth-child(4))),
.noeuds--descendants:not(:has(> :nth-child(4))) {
  -webkit-mask-image: none; mask-image: none; }

@media (pointer: fine) {
  .noeuds--descendants { scrollbar-width: thin;
    scrollbar-color: var(--filet) transparent; }
  .noeuds--descendants::-webkit-scrollbar { display: block; height: 8px; }
  .noeuds--descendants::-webkit-scrollbar-thumb {
    background: var(--filet); border-radius: var(--r-rond); }
}

/* La fratrie se replie : elle compte souvent huit personnes, et elle n'est pas
   ce qu'on vient chercher — on la déplie quand on cherche un frère précis. */
/* Le repli venait buter sur « Ses enfants » : deux lignes de même taille et de
   même couleur collées l'une à l'autre, qu'on lisait comme un seul bloc. */
.menage-vue__fratrie { margin: -.8rem 0 var(--e6); }
.menage-vue__fratrie summary { cursor: pointer; font-family: var(--sans);
  font-size: var(--t-meta); color: var(--encre-douce); padding: var(--e2) 0; }

/* Le parcours par générations : replié, il sert au généalogiste, pas à la
   cousine qui cherche qui est qui. */
.arbre-etendu > summary { cursor: pointer; font-family: var(--sans);
  font-size: var(--t-meta); color: var(--encre-douce);
  padding: var(--e3) 0; border-top: 1px solid var(--filet); }

/* ------------------------------------------------------------------
   LE MÉNAGE : LE COUPLE, PUIS SES ENFANTS
   ------------------------------------------------------------------
   La maille de tous les livrets de famille. Le couple se lit d'un trait, les
   enfants s'indentent sous lui — c'est la seule indentation du produit, et elle
   dit une filiation. */
/* Le trait sous CHAQUE ménage faisait, dans « Voir plusieurs générations d'un
   coup », un filet horizontal par personne — cinq petits-enfants, cinq traits
   pleine largeur. Un rang de généalogie n'est pas un tableau : ce qui sépare
   deux ménages, c'est le blanc, et le rang porte déjà son propre filet
   vertical à gauche. */
.menage { padding: var(--e2) 0; }
.menage__parents { margin: 0; }
.menage__et { color: var(--encre-douce); margin: 0 var(--e0); }

.menage__enfants { list-style: none; margin: var(--e2) 0 0; padding: 0 0 0 var(--e4);
  border-left: 1px solid var(--filet); }
.menage__enfants li { padding: var(--e0) 0; }

/* ------------------------------------------------------------------
   LE MOTIF AU TRAIT
   ------------------------------------------------------------------
   Il prend la couleur du texte — troisième point de la direction artistique —
   ce qui le fait basculer seul en thème sombre et le grise pour le deuil sans
   qu'aucun fichier soit dupliqué. */
.motif { margin: 0; display: flex; flex-direction: column; align-items: center;
  gap: var(--e1); color: var(--encre-douce); }
.motif svg { display: block; }

/* Sur la rubrique du carnet : discret, il annonce sans concurrencer le titre. */
/* Le motif COIFFE son libellé, il ne flotte pas au-dessus. Empilés, le dessin
   et les petites capitales formaient deux blocs sans rapport visible — une
   icône oubliée là. Sur la même ligne, l'un devient la coiffe de l'autre. */
.rubrique-tete { display: flex; align-items: center; gap: var(--e2);
  margin: var(--e6) 0 var(--e3); }
.rubrique-tete .rubrique { margin: 0; }
.rubrique-tete .motif--rubrique { margin: 0; flex: 0 0 auto; }
.motif--rubrique svg { width: 24px; height: 24px; display: block; }

/* Sur le faire-part : c'est l'ornement de tête, il a droit à sa place. */
.motif--faire-part svg { width: 52px; height: 52px; }
.motif--faire-part { margin: 0 0 var(--e6); color: var(--encre); }

/* L'écran des motifs de la famille : le dessin à gauche, le réglage à droite.
   Le dessin doit être VU pendant qu'on décide de le remplacer. */
.motif-reglage { display: flex; gap: var(--e5); align-items: flex-start;
  padding: var(--e5) 0; border-bottom: 1px solid var(--filet-clair); }
.motif-reglage__apercu { flex: 0 0 auto; }
.motif-reglage__corps { flex: 1; min-width: 0; }
.motif--reglage svg { width: 44px; height: 44px; }
.motif--reglage { color: var(--encre); }

/* La signature du dessinateur — « Colombe, 2026 ». Sans elle, une illustration
   n'est qu'un décor ; avec elle, c'est un héritage. */
.motif__signature { font-family: var(--sans); font-size: var(--t-micro);
   color: var(--encre-douce); }

/* ------------------------------------------------------------------
   LE DEUIL EST UN THÈME, PAS UNE COULEUR
   ------------------------------------------------------------------
   Septième point de la direction artistique : « une entrée de décès change de
   gabarit : filet d'encadrement, épigraphe possible, illustration en gris,
   aucune réaction rapide, aucun compteur. Le respect se code. »

   La forme moderne de l'avis de décès s'est fixée sous Louis-Philippe, les
   ornements disparaissant au profit du seul bord encadrant. On ne change donc
   NI la couleur NI le corps : on encadre, on éloigne, on grise le trait. */
.faire-part--deuil { border: 1px solid var(--encre-douce); }

.entree--deuil { border: 1px solid var(--filet); padding: var(--e4) var(--e5);
  margin: var(--e5) 0; }
/* Le motif du deuil se retire d'un ton : `currentColor` fait le reste, et
   aucun fichier n'est dupliqué. C'est le troisième point de la DA qui rend le
   septième possible. */
.entree--deuil .motif { color: var(--encre-douce); }

/* Le repli au-delà de douze lignes. La contrainte fabrique le ton du carnet
   sans jamais l'enseigner — c'est ce qui l'empêche de devenir un fil de
   discussion. */
.entree__repli summary { cursor: pointer; }
.entree__repli[open] summary { display: none; }

.faire-part__mentions { margin: var(--e5) 0 0; font-size: var(--t-meta);
  color: var(--encre-douce); }
.faire-part__mentions dt { display: inline; font-family: var(--serif);
  font-size: var(--t-meta); }
.faire-part__mentions dt::after { content: " "; }
.faire-part__mentions dd { display: inline; margin: 0; }
.faire-part__mentions dd::after { content: "\A"; white-space: pre; }

.consentement__recapitulatif { margin: 0 0 var(--e6); padding: var(--e4) 0;
  border-top: 1px solid var(--filet); border-bottom: 1px solid var(--filet); }
.consentement__recapitulatif dt { font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce); }
.consentement__recapitulatif dd { margin: 0 0 var(--e3); }
.consentement__recapitulatif dd:last-child { margin: 0; }

.consentement__choix { margin: var(--e5) 0 0; }
.consentement__choix input[type="submit"] { width: 100%; }
.consentement__refus { width: 100%; }

/* ------------------------------------------------------------------
   UNE CORRECTION QUI NE CONCERNE PAS QUE LE CARNET
   ------------------------------------------------------------------
   `f.submit` et `submit_tag` rendent `<input type="submit">`, que la règle
   `button` ne touche pas : le bouton principal d'« À la une », de « Moi », de
   la fondation et de tous les formulaires du produit tombait au style natif du
   navigateur — donc sous le plancher de 18 px, donc en zoom automatique à
   chaque prise de focus sur iOS. Trouvé en construisant le carnet.

   `input[type="submit"]` figure DÉSORMAIS dans le bloc unique du haut de
   feuille, avec `button` et `.action`. La règle qui se trouvait ici en était
   la copie littérale — dix déclarations identiques, écrites deux fois : toute
   évolution du bouton devait être faite aux deux endroits, ou elle divergeait
   en silence. C'est ce qui faisait sortir en NOIR PLEIN un
   `submit_tag class: "action--seconde"`, la spécificité de
   `input[type="submit"]` (0-1-1) battant celle de `.action--seconde` (0-1-0). */

/* ==================================================================
   LES MAISONS DE FAMILLE, LE COURRIER ET LES ANNONCES
   ==================================================================
   Trois écrans, deux voix. La fiche d'une maison SE LIT : elle réutilise
   `.editorial`, `.rubrique`, `.entree` et `.filet-orne`, et n'ajoute que le
   bloc de position. Le Courrier et les annonces S'UTILISENT — `.liste`,
   `.liste__ligne`, `.formulaire`, `.vide` — sans un objet de plus.

   Rien ici n'introduit de carte, de vignette de produit ni de prix en gros
   caractères : ce sont les trois signes d'une place de marché, et le registre
   de ces annonces est celui du service rendu entre cousins. */

.deposer { margin: 0 0 var(--e6); }
.deposer summary { cursor: pointer; min-height: var(--cible);
  display: flex; align-items: center; font-family: var(--sans);
  font-weight: var(--g-appuyee); font-size: var(--t-corps); }
.deposer .formulaire { padding: var(--e2); }

.position-saisie { border: 0; padding: 0; margin: var(--e2) 0 0; }
.position-saisie legend { font-size: var(--t-meta); font-weight: var(--g-forte);
  color: var(--encre-douce); padding: 0; }

/* La position. Deux filets, AUCUNE CARTE : demander une tuile de fond à un
   serveur étranger lui transmettrait exactement les coordonnées qu'on protège,
   avec l'adresse IP du lecteur. La mention qui suit n'est pas une note de bas
   de page — c'est elle qui dit pourquoi le chiffre est arrondi, et sans elle un
   membre croit l'adresse fausse et la « corrige ». */
.position { padding: var(--e4) 0; margin: var(--e6) 0;
  border-top: 1px solid var(--filet); border-bottom: 1px solid var(--filet); }
.position__coordonnees { margin: 0 0 var(--e1); font-family: var(--sans);
  font-variant-numeric: tabular-nums; }
.position__mention { margin: 0; font-size: var(--t-meta); color: var(--encre-douce); }

/* Le Courrier. La ligne porte tout : le genre, l'objet, et les boutons. */
.courrier__corps { flex: 1; display: block; }
.courrier__titre { display: block; }
.courrier--non-lu .courrier__titre { font-weight: var(--g-appuyee); }
.courrier__actions { display: flex; flex-wrap: wrap; gap: var(--e2);
  align-items: center; margin: var(--e2); }
/* La forme vient du bloc unique du haut de feuille, au rang « seconde ». Il ne
   reste ici que ce qui lui est propre : un corps de 15 px, parce que ces
   boutons se répètent sur chaque ligne d'une liste. */
.courrier__action { padding: 0 var(--e4); font-size: var(--t-meta); }
.courrier__classer { margin: 0; }
.courrier__classer button { min-height: var(--cible); font-size: var(--t-meta); }

/* Les catégories d'annonces : des liens en filet, jamais un menu déroulant. À
   quatre entrées, le déroulant cache l'information qu'il devait donner. */
.categories { display: flex; flex-wrap: wrap; gap: var(--e4); margin: 0 0 var(--e5);
  padding: var(--e2); border-bottom: 1px solid var(--filet); }
/* La hauteur y était, la largeur non : « Don » mesurait 32 px de large. Une
   cible se juge dans SES DEUX dimensions — WCAG 2.5.8 parle d'un carré. */
.categorie { font-size: var(--t-meta); text-decoration: none;
  color: var(--encre-douce); min-height: var(--cible); min-width: var(--cible);
  padding: 0 var(--e2); justify-content: center;
  display: inline-flex; align-items: center; }
.categorie--actif { color: var(--encre); font-weight: var(--g-forte); }

/* `.annonce` était déjà pris par le bandeau de message du gabarit — deux objets
   sans rapport sous le même nom. */
/* ------------------------------------------------------------------
   `flex: 1` NE PASSE JAMAIS À LA LIGNE — IL SE COMPRIME
   ------------------------------------------------------------------
   La ligne d'une annonce porte le nom de la famille à gauche et l'annonce à
   droite. Le nom est en `white-space: nowrap` — il prend donc sa largeur
   naturelle, deux cent soixante pixels pour « Cousinage de G. et S. de Fromont
   de Bouaille » — et l'annonce avait `flex: 1`, c'est-à-dire une base de ZÉRO :
   elle ne réclame aucune place, elle prend ce qui reste.

   Sur un téléphone, ce qui reste est la moitié de l'écran. Le titre se coupait
   en deux, le corps se brisait tous les trois mots, et « Retirer cette annonce »
   passait sur deux lignes dans un bouton de la largeur d'un pouce. JB, le
   29 août 2026 : « autre souci d'UX pour l'annonce, regarde en mode mobile ».

   `flex-wrap: wrap` était bien posé sur la ligne, et ne servait à rien : une
   base de zéro ne déclenche jamais le passage à la ligne, elle se comprime
   jusqu'à un pixel. La base de 14 rem — celle que `.liste__corps` porte déjà
   pour cette raison exacte — fait passer l'annonce SOUS le nom dès que la place
   manque. C'est le même défaut que les cinquante-trois `style="flex:1"` sans
   `min-width: 0` relevés plus haut dans cette feuille : une valeur qui a l'air
   de dire « prends la place » et qui dit « rends-la toute ». */
.petite-annonce { flex: 1 1 14rem; min-width: 0; display: block;
  overflow-wrap: anywhere; }
.petite-annonce__titre { display: block; }
.petite-annonce__prix { display: block; font-variant-numeric: tabular-nums; }
.petite-annonce__corps { display: block; color: var(--encre-douce); }
/* Le site d'une entreprise de la famille : sur sa ligne, et coupable n'importe
   où — un nom de domaine est un mot insécable qui déborderait la colonne. */
.petite-annonce__site { display: block; font-family: var(--sans);
  font-size: var(--t-meta); overflow-wrap: anywhere; }
.petite-annonce__reponse summary { cursor: pointer; min-height: var(--cible);
  display: flex; align-items: center; font-size: var(--t-meta);
  color: var(--encre-douce); }

/* Les mentions de seconde ligne passent à la ligne dans ces deux listes : en
   ligne, elles se collent au titre et la ligne devient illisible dès le premier
   nom un peu long. */
.courrier__corps .secondaire, .petite-annonce .secondaire { display: block; }

/* L'éditeur. Il ne paraît que sur les emplacements autorisés, et il ne
   ressemble à rien d'autre du produit : un filet à gauche, du gris, aucune
   couleur. Ce qui perd un produit familial, ce n'est pas d'avoir de la
   publicité — c'est de l'insérer dans le flux familial lui-même. */
.editeur { margin: var(--e7) 0; padding: var(--e4) 0 var(--e4) var(--e4);
  border-left: 2px solid var(--filet); }
.editeur__accroche { margin: var(--e1) 0; }

/* `button_to` pose la classe sur le BOUTON, jamais sur un conteneur : la règle
   `.bouton-discret button` ne l'atteint donc pas, et le bouton retombait au
   noir plein des actions principales. Le défaut est antérieur à ce chantier —
   « Rejoindre », dans la liste des conversations, l'a depuis l'origine. */
/* Couvert par le bloc unique du haut de feuille : `button.bouton-discret` y
   figure nommément, parce que `button_to` pose la classe sur le BOUTON et non
   sur un conteneur. */

/* ==================================================================
   LES ARCHIVES
   ==================================================================
   Deux voix sur le même parcours. L'ACTE SE LIT : romaine, filets, rubrique en
   petites capitales — c'est la pièce la plus ancienne que la famille possède,
   et l'écran ne la traite pas comme une ligne de gestionnaire de fichiers. LE
   CAVIARDAGE S'UTILISE : linéale, un verdict entre deux filets, des champs,
   une décision.

   Le seul objet neuf est le rectangle de caviardage. Un aplat d'encre pleine,
   sans transparence — la feuille l'interdit partout, et c'est ici que
   l'interdit compte le plus : un masque à travers lequel on devine l'écriture
   ne masque rien. */

.archives__titre { margin: var(--e3); }

/* Le champ de date manquait à la règle des formulaires : sans lui il tombe au
   style natif du navigateur, donc sous le plancher de 18 px, donc en zoom
   automatique à chaque prise de focus sur iOS. L'écran de dépôt en porte trois. */
.formulaire input[type="date"] {
  font-family: var(--sans); font-size: var(--t-corps);
  min-height: var(--cible); padding: var(--e2) var(--e3);
  background: var(--surface); color: var(--encre);
  border: 1px solid var(--filet); border-radius: var(--r-petit); max-width: 100%;
}

/* Le verdict. Deux filets, comme le récapitulatif du consentement : c'est une
   décision qu'on lit avant d'agir, pas une bannière. AUCUNE couleur d'alerte —
   un refus n'est pas une faute, c'est la loi, et le rouge le ferait passer
   pour une erreur de saisie. */
.verdict { margin: 0 0 var(--e6); padding: var(--e4) 0 var(--e4) var(--e4);
  border-top: 1px solid var(--filet); border-bottom: 1px solid var(--filet); }
.verdict--ferme  { border-left: 3px solid var(--encre-douce); }
.verdict--ouvert { border-left: 3px solid var(--succes); }
.verdict__intitule { margin: 0 0 var(--e1); font-family: var(--serif); font-size: var(--t-sous-titre); }
.verdict__motif { margin: 0 0 var(--e2); }

/* La planche et ses masques. Le rectangle est positionné en POURCENTAGE de
   l'image : juste à toutes les largeurs d'écran, sans une seule mesure en
   pixels et sans point de rupture à maintenir. */
.planche { position: relative; margin: 0 0 var(--e5); line-height: 0; }
.planche__image { display: block; width: 100%; height: auto;
  background-color: var(--filet-clair); }
.planche__zone { position: absolute; background: var(--encre);
  outline: 1px solid var(--papier); }
.planche__zone[hidden] { display: none; }

/* Une zone à masquer : quatre champs serrés, qui tiennent sur la largeur d'un
   téléphone. */
.zone { border: 0; border-top: 1px solid var(--filet); padding: var(--e4) 0 var(--e2); margin: 0; }
.zone legend { font-weight: var(--g-forte); font-size: var(--t-corps); padding: 0 var(--e2) 0 0; }
.zone__champs { display: flex; gap: var(--e3); flex-wrap: wrap; }
.zone__champs label { display: flex; flex-direction: column; gap: var(--e1);
  font-size: var(--t-meta); font-weight: var(--g-normale); }
.zone__champs input { width: 5.5rem; min-height: var(--cible); padding: var(--e2);
  font-family: var(--sans); font-size: var(--t-corps); text-align: center;
  font-variant-numeric: tabular-nums; background: var(--surface);
  color: var(--encre); border: 1px solid var(--filet); border-radius: var(--r-petit); }

/* L'acte publié. Toute la largeur utile, rien ne l'encadre : c'est la pièce,
   pas son support. */
.acte__planche { display: block; width: 100%; height: auto; margin: var(--e2) 0 var(--e4);
  background-color: var(--filet-clair); }

/* La transcription se lit comme l'acte — même romaine, même interlignage : c'est
   le même texte, écrit deux fois à trois siècles d'écart. */
.transcription__texte { margin: 0 0 var(--e2); }
.transcription__texte p { margin: 0 0 var(--e2); }

/* ==================================================================
   MODÉRATION, RÉCUPÉRATION, MANDATS
   ==================================================================
   Voix utilitaire : ces écrans s'utilisent, ils ne se lisent pas. Aucune forme
   nouvelle, sauf là où il en fallait une — la grille de visages. */

/* Le contenu signalé, rappelé tel quel : au signalant avant son geste, au
   modérateur avant sa décision. Un filet à gauche, pas un encadré — c'est une
   citation, pas une pièce à conviction. */
.cite-signalee { margin: 0 0 var(--e5); padding: var(--e3) 0 var(--e3) var(--e4);
  border-left: 3px solid var(--filet); }
.cite-signalee p { margin: 0 0 var(--e1); overflow-wrap: anywhere; }
.cite-signalee cite { font-style: normal; font-size: var(--t-meta);
  color: var(--encre-douce); }

/* Une entrée de la file du modérateur. Un filet la sépare, rien ne l'encadre :
   un modérateur de cousinage est un cousin qui y passe quelques minutes par
   mois, et tout ce qui ressemble à un outil d'administration le fera renoncer. */
.signalement { padding: var(--e5) 0; border-bottom: 1px solid var(--filet); }
.signalement:last-of-type { border-bottom: 0; }

/* La grille de visages de la récupération d'accès.
   `auto-fit` avec un plancher à 8 rem : deux colonnes sur un téléphone, trois
   sur une tablette. Chaque case fait 72 px, bien au-dessus du plancher de
   48 px — on ne se trompe pas de visage parce qu'on a mal visé, et c'est le
   seul écran du produit où l'erreur coûte un essai sur trois. */
.visages { display: grid; gap: var(--e2); margin: 0 0 var(--e6);
  grid-template-columns: repeat(auto-fit, minmax(min(8rem, 100%), 1fr)); }
.visage { min-height: 72px; padding: var(--e3) var(--e2);
  background: var(--surface); color: var(--encre);
  border: 1px solid var(--filet); border-radius: var(--r-petit);
  font-family: var(--serif); font-size: var(--t-corps); font-weight: var(--g-normale);
  line-height: 1.3; text-wrap: balance; }

/* Une échéance de mandat ou d'invitation. Chiffres tabulaires : une colonne de
   dates qui danse se relit mal, et ces dates-là sont ce qu'on vient vérifier. */
.echeance { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ==================================================================
   LES ÉPHÉMÉRIDES
   ==================================================================
   Voix ÉDITORIALE, sans discussion : « il y a cinq ans, le décès de » se lit,
   il ne se clique pas. En linéale à côté d'un bouton, la même phrase
   deviendrait une notification — c'est-à-dire exactement ce qu'on ne veut pas
   recevoir un 3 novembre.

   Le bloc ouvre « À la une » parce qu'un cousinage tout neuf n'a ni photo ni
   publication, mais a toujours des dates. Il tient l'écran, il ne le prend
   pas : plus serré que l'entrée de carnet, dont l'espacement le ferait paraître
   plus solennel qu'il n'est. */

.ephemerides { margin: 0 0 var(--e6); }
.ephemerides .entree { padding: var(--e3) 0; }
.ephemeride__texte { font-family: var(--serif); font-size: var(--t-sous-titre);
  line-height: 1.45; margin: 0; }

/* ==================================================================
   COMPOSER LES FOYERS DEPUIS L'ARBRE
   ==================================================================
   Ce qui S'UTILISE. On coche trois cents lignes d'affilée : c'est le seul écran
   du produit où la densité prime, et où la zone tactile doit couvrir toute la
   ligne — `.case` s'en charge déjà, il ne lui manque que de l'occuper. */

.foyers-composes .case { flex: 1; }
/* Le nom du foyer porte la ligne ; les personnes qui le composent viennent
   dessous. C'est l'ordre de lecture : on reconnaît « Anselme et Guillemette »,
   puis on vérifie qui est compté avec eux. */
.foyer-propose__nom { display: block; font-weight: var(--g-appuyee); }
.foyer-propose__membres { display: block; font-size: var(--t-meta);
  color: var(--encre-douce); }
/* L'adresse est facultative et n'a pas d'étiquette : trois cents « Son adresse
   électronique » noieraient les noms. Le gabarit du champ dit ce qu'on attend. */
.foyer-propose__adresse { display: block; width: 100%; margin: var(--e2); }

/* ==================================================================
   ÊTRE PRÉVENU
   ==================================================================
   Un seul motif nouveau, et il n'est pas décoratif : sur iPhone, l'API Push
   n'existe que pour une application posée sur l'écran d'accueil. Il faut donc
   un bloc qui DIT ce qui manque, avant le bouton, sans ressembler à une alerte
   — c'est une consigne, pas une erreur.

   Un filet à gauche, comme `.cite-signalee` et `.mot-daccueil` : aucune boîte,
   aucune ombre, rien qui ressemble à un panneau d'administration. */
.avis-push { margin: 0 0 var(--e5); padding: var(--e3) 0 var(--e3) var(--e4);
  border-left: 3px solid var(--filet); }
.avis-push p { margin: 0 0 var(--e2); }
.avis-push p:last-child { margin: 0; }

/* L'état réel des notifications sur CET appareil, écrit par le navigateur. Il
   se distingue de la consigne : celle-ci est permanente, celui-là répond. */
.avis-push--diagnostic { border-left-color: var(--encre-douce); }

/* ==================================================================
   LA CARTE DE LA FAMILLE
   ==================================================================
   Le fond vient de l'IGN ; nos points sont posés par-dessus, dans le
   navigateur. Ils sont DESSINÉS EN CSS plutôt qu'en image : ils suivent le
   thème, et rien n'est téléchargé.

   La hauteur est en `dvh` : sur un téléphone, `vh` compte la barre d'adresse
   qui disparaît au défilement, et la carte déborde alors de l'écran. */
/* `.carte-famille` et non `.carte` : ce dernier désigne depuis l'origine la
   carte au sens de FICHE CARTONNÉE — l'encadré de 26 rem des écrans de
   connexion et d'inscription. Le conteneur en héritait la largeur, Leaflet
   mesurait 414 px au lieu de 736, et ne demandait donc que les tuiles d'une
   grille trop petite. Le reste de la carte restait vide, sans que rien ne le
   rattrape : les tuiles manquantes ne sont jamais redemandées. */
/* ------------------------------------------------------------------
   LA CARTE EST L'ÉCRAN
   ------------------------------------------------------------------
   Elle occupait un peu plus de la moitié de la hauteur, sous un titre en
   romaine et un sous-titre, au-dessus d'une légende sur deux lignes, d'un repli
   et de deux boutons tombés sous le pli : cinquante-huit pour cent de l'écran
   servaient à encadrer ce qu'on venait regarder.

   Une grille de deux rangées — l'entête d'une ligne, la carte pour tout le
   reste. Le `1fr` avec `min-height: 0` est le point critique : sans lui, la
   rangée refuse de descendre sous la taille de son contenu et la grille
   déborde au lieu de tenir. */
/* ------------------------------------------------------------------
   TROIS MEUBLES À DÉFALQUER, ET ON N'EN COMPTAIT QUE DEUX
   ------------------------------------------------------------------
   La hauteur se calculait `100dvh - barre-basse`, en ignorant la BANDE HAUTE —
   qui est `sticky` en tête de page, donc dans le flux, donc dans la hauteur du
   document. La page dépassait d'exactement 48 px : mesuré, pas supposé.

   Ce qu'on en voyait : l'écran défilait, le titre « La carte » glissait sous la
   bande, et la feuille des lieux — ancrée au bas d'une page trop grande —
   passait sous la ligne de flottaison. Après un filtre elle disparaissait
   franchement. JB, le 29 août : « le bandeau qui remonte est encore pas bon,
   il y a plein de problèmes ».

   ET LES MARGES NÉGATIVES DISPARAISSENT AVEC. Elles annulaient à la main le
   rembourrage de `.page` — `-24px` contre `24px` en haut, `-40px` contre
   `24px` en bas, soit seize pixels de trop. On met le rembourrage à zéro à la
   source : deux valeurs qui doivent s'annuler finissent toujours par diverger. */
/* ------------------------------------------------------------------
   LES SEIZE PIXELS, ET ILS ÉTAIENT LE `+ 1rem` DE LA RESPIRATION
   ------------------------------------------------------------------
   `body` réserve `--barre-basse + 1rem` en bas : la hauteur de la barre
   d'onglets, plus un souffle pour que la dernière ligne ne colle pas dessous.
   C'est juste sur une page qui défile — et faux sur celle-ci, qui fait
   EXACTEMENT la hauteur de la fenêtre : le souffle s'ajoute alors à un tout
   déjà plein, et le document dépasse de seize pixels.

   Je les avais laissés le 29 août en écrivant dans l'essai que je n'en trouvais
   pas la source, et en posant le seuil à 24 pour les tolérer. C'était deux
   valeurs qui doivent s'accorder — 80 d'un côté, 64 de l'autre — et c'est
   exactement le défaut que le commentaire de `body` annonce trois lignes plus
   haut : « deux calculs jumeaux finissent toujours par diverger ».

   La page gère sa propre hauteur : elle n'a pas besoin qu'on lui en réserve. */
body:has(.carte-page), body:has(.fil-page) { padding-bottom: 0; }
.page:has(.carte-page) { max-width: none; padding: 0; }
.carte-page { position: relative; display: grid; grid-template-rows: auto 1fr;
  height: calc(100dvh - var(--bande-haute) - var(--barre-basse));
  /* `overflow: hidden` FERME la question plutôt que de la reposer à chaque
     ajout : Leaflet pose un mandataire d'animation à cinq mille pixels du haut,
     et la feuille ouverte porte une liste plus longue qu'elle. Aucun des deux
     ne doit faire défiler la PAGE — ils défilent chez eux. */
  overflow: hidden; margin: 0; }
/* `position: relative` N'EST PAS UN DÉTAIL : sans lui, la feuille des lieux —
   `position: absolute; bottom: 0` — se cale sur le bloc conteneur INITIAL,
   c'est-à-dire sur le bas de la fenêtre et non sur le bas de la carte. Avec son
   `z-index`, elle se peignait PAR-DESSUS LES SIX ONGLETS : sur l'écran de la
   carte, on ne pouvait plus naviguer du tout. */
/* ------------------------------------------------------------------
   LA CARTE N'EST PAS UN ÉCRAN À PART
   ------------------------------------------------------------------
   Son titre était rapetissé à 19 px — le corps d'un sous-titre — quand tous
   les autres écrans en portent 24, et il flottait dans un en-tête de deux
   crans de haut. C'était la seule page du produit à déroger, et cela se voit
   avant tout le reste : le titre ne pesait plus rien face à la carte, et
   l'écran n'avait plus l'air d'appartenir au même site.
   
   L'écran est plein cadre, ce qui justifiait de comprimer sa tête ; mais on
   comprime l'ESPACE, pas la typographie. */
.carte-page__entete { margin: 0; padding: var(--e3) var(--e5) var(--e2); }

.carte-famille { position: relative; min-height: 0; margin: 0;
  border-top: 1px solid var(--filet); overflow: hidden; }
.carte-famille__toile { width: 100%; height: 100%; background: var(--filet-clair); }

/* Leaflet pose ses propres couleurs sur la bulle et les commandes. On les
   ramène dans la palette du produit, sans quoi la carte a l'air d'un morceau
   d'une autre application collé au milieu de la page. */
/* ------------------------------------------------------------------
   LEAFLET IMPOSE SA FONTE, ET IL FAUT LA LUI REPRENDRE PARTOUT
   ------------------------------------------------------------------
   La bibliothèque pose sa propre pile — Helvetica, Arial — sur tout ce qui
   vit dans la carte : les cartouches, les commandes, l'attribution. Deux
   polices se côtoyaient donc sur le même écran sans qu'on sache pourquoi, et
   c'est le genre d'écart qu'on sent avant de savoir le nommer.
   
   La linéale du produit pour l'interface, la romaine pour ce qui SE LIT — le
   nom d'une maison, celui d'une personne. La même règle que partout. */
.leaflet-container, .leaflet-container * { font-family: var(--sans); }
.leaflet-container { font-size: var(--t-meta); background: var(--filet-clair); }
.leaflet-container .cartouche__nom { font-family: var(--serif); }
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
  background: var(--surface); color: var(--encre);
  border: 1px solid var(--filet); box-shadow: none; border-radius: var(--r-petit); }
.leaflet-bar a { background: var(--surface); color: var(--encre);
  border-bottom-color: var(--filet); }
.leaflet-bar a:hover { background: var(--filet-clair); }
.leaflet-control-attribution { background: var(--surface); color: var(--encre-douce); }
.leaflet-control-attribution a { color: var(--lien); }

.pastille { display: inline-block; width: 12px; height: 12px; flex: 0 0 auto;
  border-radius: var(--r-rond); border: 2px solid var(--surface); }
/* Un filet de 3 px du fond détache le point des tuiles. C'était une OMBRE —
   la seule du produit, alors que la direction artistique les interdit en
   toutes lettres. Le filet suit le thème, l'ombre non. */
.pastille--carte { width: 16px; height: 16px; border-width: 3px; }

/* Cinq genres, cinq teintes — et ce sont les seules couleurs franches du
   produit. Une carte sans code de couleur ne dit rien de plus qu'une liste.
   Elles passent par des jetons : les valeurs en dur étaient les teintes
   CLAIRES, et `#2C4A6E` sur un fond `#14130F` ne se voit pas — les points de
   la carte, c'est-à-dire l'objet même de l'écran, s'effaçaient en thème
   sombre. */
.pastille--residence    { background: var(--pastille-residence); }
.pastille--maison       { background: var(--pastille-maison); }
.pastille--maison-vendue{ background: var(--pastille-vendue); }
.pastille--annonce      { background: var(--pastille-annonce); }
.pastille--nouvelle     { background: var(--pastille-nouvelle); }

/* ==================================================================
   LE CARTOUCHE — CE QU'ON LIT QUAND ON TOUCHE UN POINT
   ==================================================================
   C'était une bulle Leaflet nue : un lien bleu souligné et une ligne grise, en
   13 px, dans un rectangle blanc à coins de douze pixels avec une ombre —
   c'est-à-dire trois choses que le reste du produit s'interdit, et rien de ce
   qu'il fait. Étranger de bout en bout.

   Le cartouche reprend le vocabulaire de la maison : la PHOTO en bandeau à
   fleur du bord, comme la photo d'une bulle du fil ; la rubrique en petites
   capitales ; le nom en romaine ; le lieu en linéale douce. Le bloc ENTIER est
   le lien — une phrase soulignée ne se clique pas, un objet oui.

   ------------------------------------------------------------------
   DEUX RÈGLES, PARCE QU'IL Y A DEUX BALISES
   ------------------------------------------------------------------
   Le cartouche est un `<a>` quand il mène quelque part, et un `<div>` quand il
   ne mène nulle part — une résidence sans maison de famille n'est pas une
   destination. La règle entière portait `a.cartouche`, et les résidences ont
   donc perdu d'un coup leur mise en page : le texte se collait en un seul bloc,
   sans rubrique, sans nom, sans lieu.

   Relevé par JB sur la carte, et le contraste disait la cause : Planay, une
   maison de famille, était juste ; toutes les résidences étaient cassées.

   LA MISE EN PAGE APPARTIENT À LA CLASSE — elle vaut pour les deux balises. Ne
   reste sur `a.cartouche` que ce qui doit battre `.leaflet-container a`, qui
   pèse (0,1,1) et gagnerait sur une classe seule : la couleur et le
   soulignement, c'est-à-dire ce que Leaflet impose aux liens. */
.cartouche {
  display: flex; flex-direction: column; min-width: 14rem; max-width: 16rem; }
a.cartouche, a.cartouche:hover {
  color: var(--encre); text-decoration: none; }
/* 4:3 et non 3:2 : `Derivee` rend un CARRÉ, et tout cadre plus large le
   recoupe une seconde fois — sur une façade, c'est le toit et le perron qu'on
   perd. Le 4:3 est le compromis qui garde la maison entière sans donner un
   cartouche carré, qui aurait l'air d'un avatar. */
.cartouche__image { display: block; width: calc(100% + 2px);
  margin: -1px -1px var(--e2); aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--r-petit) var(--r-petit) 0 0; background: var(--filet-clair); }
/* Sans photographie, PAS de rectangle gris : un aplat vide se lit comme une
   image qui n'a pas chargé. Le motif au trait de la rubrique dit la même chose
   et appartient au produit. */
.cartouche__motif { display: flex; align-items: center; justify-content: center;
  width: calc(100% + 2px); margin: -1px -1px var(--e2); aspect-ratio: 3 / 1;
  background: var(--filet-clair); border-radius: var(--r-petit) var(--r-petit) 0 0;
  color: var(--encre-douce); }
.cartouche__motif svg { width: 28px; height: 28px; }
.cartouche__rubrique { font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce); }
.cartouche__nom { font-family: var(--serif); font-size: var(--t-sous-titre); font-weight: var(--g-appuyee);
  line-height: 1.25; }
.cartouche__lieu { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); }

/* La fermeture faisait 24 px de cible, en Tahoma gris — la moitié du plancher
   tactile que le produit qualifie de « plancher juridique, pas un objectif »,
   sur un public de 15 à 92 ans. */
.leaflet-popup-close-button {
  width: 44px !important; height: 44px !important;
  font-family: var(--sans) !important; font-size: 20px !important;
  line-height: 44px !important; color: var(--encre-douce) !important;
  padding: 0 !important; }
.leaflet-popup-content { margin: .75rem 1rem !important; font-size: var(--t-meta); }
/* Le lien de Leaflet pèse (0,1,1) : la couleur du produit ne l'atteint que
   nommément. */
.leaflet-container a.leaflet-popup-close-button:hover { color: var(--encre); }

/* Le pointage à la main, replié. Il n'est le recours que d'un lieu sur dix :
   déplié d'office, il ferait croire que la saisie d'une adresse ne suffit pas. */
/* ------------------------------------------------------------------
   LES RUBRIQUES DE LA FAMILLE, EN RANGÉE DÉFILANTE
   ------------------------------------------------------------------
   Elles étaient cinq liens gris de 15 px en PIED de page, après tout le fil de
   l'édition — c'est-à-dire à quinze cents pixels de défilement. Et sous la
   forme (`.lien-discret`) qui sert partout ailleurs à « Annuler » et
   « Revenir » : le lecteur avait appris que cette forme veut dire retour
   arrière, et six modules du produit la portaient.

   La rangée défile latéralement plutôt que de passer à la ligne : deux lignes
   de puces reprennent la place qu'on vient de gagner. */
/* Le composeur de publication, replié. Il coûtait cinq cents pixels entre le
   haut de l'écran et le premier article ; il en coûte quarante-huit. Le
   `summary` a l'allure d'un champ, parce que c'est ce qu'on vient chercher —
   mais il ne prétend pas en être un : le curseur reste celui d'un bouton. */
.composeur-replie { margin: 0 0 var(--e6); }
.composeur-replie > summary { display: flex; align-items: center;
  min-height: var(--cible); padding: 0 var(--e4); cursor: pointer;
  border: 1px solid var(--filet); border-radius: var(--r-petit);
  background: var(--surface); color: var(--encre-douce);
  font-size: var(--t-corps); }
.composeur-replie > summary:hover { border-color: var(--lien); color: var(--encre); }
.composeur-replie[open] > summary { margin: var(--e3); }

/* ------------------------------------------------------------------
   LE LIEN DE REMONTÉE
   ------------------------------------------------------------------
   Onze écrans n'avaient aucun lien sortant vers leur parent : on en sortait par
   le bouton du navigateur, geste que le produit n'avait pas prévu et qui
   n'existe pas dans une application posée sur un écran d'accueil.

   EN TÊTE, jamais en pied : un retour qu'il faut chercher au bas de
   quatre-vingts lignes n'est pas un retour. Et il porte le nom de sa
   destination — une flèche seule ne dit pas où elle mène. */
/* L'annuaire : la LIGNE ENTIÈRE est le lien, avec le visage. C'était deux cents
   noms en bleu souligné les uns sous les autres — le seul écran du produit qui
   rendait des personnes sans montrer leur visage, alors que le portrait existe
   et sert déjà partout ailleurs. */

/* Un rang de titre en dessous : l'écran empilait onze titres de MÊME rang,
   chacun ouvrant sur deux rem de marge, sans qu'on sache lequel relève de soi
   et lequel de ses familles. */
/* Un rang de titre EN DESSOUS ne porte pas le trait de son parent : il
   héritait le `border-bottom` de `.section-titre`, si bien que la hiérarchie
   qu'il pose était niée par le filet qui l'accompagne. Sur « Moi », cela
   faisait sept traits de titre sur un seul écran. */
.section-titre--mineur { font-size: var(--t-corps); font-family: var(--sans);
  font-weight: var(--g-appuyee); margin: var(--e6) 0 var(--e1);
  border-bottom: 0; padding: 0; }

.annuaire__ligne { display: flex; align-items: center; gap: var(--e3);
  flex: 1; min-width: 0; padding: var(--e0) 0; text-decoration: none;
  color: var(--encre); }
.annuaire__ligne:hover .annuaire__nom { text-decoration: underline;
  text-underline-offset: 2px; }
.annuaire__nom { display: block; font-weight: var(--g-appuyee); }
/* Deux cents cousins, deux cents filets : l'écran redevenait la table des
   matières qu'on venait justement de quitter. Le portrait rond et le nom en
   graisse 500 découpent les lignes bien mieux qu'un trait. */
.annuaire .liste__ligne { padding: var(--e2) 0; border-bottom: 0; }
.annuaire .liste__corps { display: flex; flex-direction: column; gap: var(--e0); }

.remonter { margin: 0 0 var(--e2); }
.remonter__lien { display: inline-flex; align-items: center; gap: var(--e1);
  min-height: 44px; margin: -.35rem; padding: 0 var(--e1);
  font-size: var(--t-meta); color: var(--encre-douce); text-decoration: none;
  border-radius: var(--r-petit); }
.remonter__lien:hover { color: var(--encre); background: var(--filet-clair); }
.remonter__lien svg { width: 17px; height: 17px; }

/* `.destinations` DÉSIGNAIT DÉJÀ AUTRE CHOSE : le groupe de cases « Dans
   quelles familles ? » des composeurs. Déclarée ici plus bas et à spécificité
   égale, la rangée défilante l'écrasait : le `fieldset` héritait `display: flex`
   et `overflow-x: auto`, ses cases s'alignaient sur une seule ligne écrasée,
   et le débordement partait sans barre de défilement visible. Quatre
   formulaires touchés — publier, inscrire un événement, déclarer au carnet,
   renseigner un sacrement.

   C'est la récidive exacte de la collision `.carte` / `.carte-famille` déjà
   payée une fois. D'où un nom qui ne peut appartenir qu'à cet objet. */
.rangee-rubriques { display: flex; gap: var(--e2); margin: 0 0 var(--e5);
  overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.rangee-rubriques::-webkit-scrollbar { display: none; }
/* 40 px de haut : sous la cible du pouce, et ces six-là sont le portail du
   produit — c'est par eux qu'on entre dans le carnet, l'agenda, les annonces. */
.destination-rubrique { flex: 0 0 auto; display: inline-flex; align-items: center;
  min-height: var(--cible); padding: 0 var(--e3); border: 1px solid var(--filet);
  border-radius: var(--r-moyen); font-size: var(--t-meta); text-decoration: none;
  color: var(--encre); white-space: nowrap; background: var(--surface); }
.destination-rubrique:hover { border-color: var(--lien); background: var(--filet-clair); }
/* Le dossier ouvert se distingue par le FOND, pas par une bordure de plus :
   c'est la même convention que l'onglet actif de la barre. */
.destination-rubrique--active { background: var(--filet-clair);
  border-color: var(--encre-douce); font-weight: var(--g-appuyee); }

/* « Ce qu'on fête aujourd'hui » est un TITRE, pas un ornement : c'est la seule
   chose de cet écran qu'on vient chercher tous les jours. */
.ephemerides__titre { font-family: var(--serif); font-size: var(--t-sous-titre);
  font-weight: var(--g-appuyee); margin: 0 0 var(--e2); }

.projet__nom { font-weight: var(--g-appuyee); }

/* L'heure d'un moment. Elle passe AVANT le libellé et en petites capitales :
   sur un week-end de deux jours, c'est elle qu'on cherche des yeux, pas le nom
   du moment. */
.moment__heure { display: block; font-size: var(--t-micro); color: var(--encre-douce);
  font-variant-numeric: tabular-nums; }

.pointage { margin: var(--e2) 0 0; }
.pointage summary { cursor: pointer; min-height: var(--cible);
  display: flex; align-items: center; font-size: var(--t-meta);
  color: var(--lien); }
.pointage__toile { height: 16rem; margin: var(--e2) 0; border-radius: var(--r-petit);
  border: 1px solid var(--filet); background: var(--filet-clair); }

/* ------------------------------------------------------------------
   LA LÉGENDE ET LA LISTE PASSENT PAR-DESSUS
   ------------------------------------------------------------------
   Un APLAT OPAQUE avec un filet d'un pixel : pas de verre dépoli, pas de
   transparence, pas de flou — les trois interdits de la direction artistique
   tiennent. Le `z-index` de 400 passe au-dessus des tuiles de Leaflet et sous
   ses commandes, qui sont à 800.

   Repliée, la légende ne montre que ses cinq pastilles : le code de couleur se
   lit sans qu'on ait à ouvrir, et il n'occupe plus deux lignes de libellés. */
/* La légende se pose SUR les tuiles : il lui faut un fond franc et une ombre,
   sans quoi elle se confond avec le relief de la carte. C'est le seul endroit
   du produit où une ombre se justifie — l'objet flotte réellement au-dessus
   d'autre chose. */
/* ------------------------------------------------------------------
   ELLE REMONTE, ELLE AUSSI, AU-DESSUS DU BANDEAU
   ------------------------------------------------------------------
   Posée à `bottom: var(--e3)`, elle occupait le bas-gauche — c'est-à-dire
   l'emplacement exact du bandeau de la feuille des lieux, qui passe désormais
   par-dessus. Elle disparaissait donc dessous, et l'écran perdait sa légende
   sans que rien ne le dise.

   Et 400 la plaçait SOUS les marqueurs de Leaflet (600) : les épingles se
   peignaient au travers, comme elles le faisaient dans la liste. Elle passe
   sous la feuille et au-dessus de tout Leaflet, ce qui est sa place — on la
   consulte pour lire la carte, jamais par-dessus la liste. */
/* ------------------------------------------------------------------
   LA LÉGENDE N'EXISTE PLUS : ELLE EST DEVENUE LE FILTRE
   ------------------------------------------------------------------
   Elle vivait dans un coin de la carte et ne servait qu'à traduire des
   couleurs — elle posait une question — « qu'est-ce que je regarde ? » — sans
   pouvoir y répondre. Les mêmes catégories sont désormais des pastilles qui
   FILTRENT, en tête de la feuille du bas, et la pastille de couleur entre dans
   la puce : la légende est là où elle sert.

   Le style est retiré plutôt que laissé « au cas où » : une classe morte dans
   la feuille est reprise par le prochain écran qui cherche un motif. */

/* La liste des lieux : une feuille ancrée au bas de la carte, fermée par
   défaut. C'est la seule version lisible au clavier et au lecteur d'écran — et
   le chemin le plus rapide pour retrouver quelqu'un dont on connaît le nom. */
/* ------------------------------------------------------------------
   AU-DESSUS DE TOUS LES PLANS DE LEAFLET, ET NON DE DEUX D'ENTRE EUX
   ------------------------------------------------------------------
   La feuille était à 401 : juste au-dessus des tuiles (200) et du plan des
   tracés (400). Mais Leaflet empile ses MARQUEURS à 600, ses infobulles à 650
   et ses bulles à 700 — tous au-dessus. Les points de la carte traversaient
   donc la liste des maisons et se peignaient par-dessus les noms : on lisait
   « La Minoterie » avec une épingle en travers.

   800 passe au-dessus du dernier plan de Leaflet et reste sous la barre des
   onglets, qui doit rester atteignable quoi qu'il arrive — c'est la leçon déjà
   payée par la légende, qui recouvrait les six onglets. */
/* ------------------------------------------------------------------
   ELLE SE POSE SUR LA BARRE, ELLE N'EN EST PAS UNE SECONDE RANGÉE
   ------------------------------------------------------------------
   Le fond et le filet étaient ceux de `.onglets`, au pixel près. Collées l'une
   à l'autre, les deux ne faisaient qu'un seul bloc — sur fond sombre, une
   masse noire de trois rangées dont on ne comprenait pas où l'une finissait.
   Le bandeau avait alors l'air d'un étage de la navigation, et non de la
   poignée d'une feuille posée par-dessus la carte.

   Ce qui les sépare n'est pas une couleur — la barre est partout, on ne la
   retouche pas depuis ici — c'est la FORME. Deux coins arrondis en haut, et
   l'œil lit une feuille qui glisse sur la barre. C'est le dessin qu'ont toutes
   les feuilles du bas depuis dix ans, et il n'a besoin d'aucun mot. */
.carte__feuille { position: absolute; left: 0; right: 0; bottom: 0;
  z-index: var(--plan-feuille);
  background: var(--surface);
  border-top: 1px solid var(--filet);
  border-top-left-radius: var(--r-moyen); border-top-right-radius: var(--r-moyen);
  /* Une feuille OPAQUE : sans ombre, l'œil ne comprend pas qu'elle flotte, et
     la carte qu'on aperçoit au ras du filet ressemble à un défaut de rendu.
     L'ombre est portée plus loin qu'avant, parce qu'elle a maintenant deux
     choses à décoller : la carte au-dessus, la barre en dessous. */
  box-shadow: 0 -10px 28px rgb(0 0 0 / 18%);
  max-height: 65%; display: flex; flex-direction: column;
  /* Les coins ne servent à rien si la liste qui défile les recouvre. */
  overflow: hidden; }
/* Le bandeau : une POIGNÉE, un compte, un chevron. Il ne disait que « les
   lieux, et mes adresses postales » — deux destinations, aucune indication
   qu'il y eût une liste dessous ni combien. On ne l'ouvrait donc pas.

   La poignée est le seul ornement, et elle porte un sens : c'est le dessin
   qu'on saisit sur toute feuille du bas depuis dix ans, et il dit « ceci se
   tire » sans un mot. */
/* ------------------------------------------------------------------
   LE TITRE PARLE LA LANGUE DE L'APPLICATION, ET NON LA SIENNE
   ------------------------------------------------------------------
   Il était en `--sans`, appuyé à 650, à la taille des onglets : c'est-à-dire
   dans une combinaison qu'AUCUN autre titre du produit n'emploie. Posé au
   milieu d'un écran par ailleurs entièrement composé, il ne ressemblait à rien
   de ce qui l'entourait — l'étrangeté qu'on remarque sans pouvoir la nommer.

   Il reprend donc l'idiome que le produit s'est déjà donné pour nommer une
   liste, celui de `.rubrique` : le serif de lecture, la taille des mentions,
   l'appui à 500. Ce bandeau NOMME ce qu'il y a dessous, il ne commande rien —
   le sans-serif est réservé à ce qui est de la mécanique, les onglets et les
   réglages. */
/* ------------------------------------------------------------------
   LE BANDEAU EST LE TITRE DE CE QU'IL OUVRE, PAS UNE MENTION EN PIED
   ------------------------------------------------------------------
   « 14 lieux sur la carte » se lisait à la taille des mentions — 15 px, en
   encre douce — collé au bas d'une barre haute de 48. JB, le 29 août 2026 :
   « c'est affiché en petit et en bas ; je comprends que le bandeau soit large
   pour faciliter le clic, mais on a une impression de mal fini, non ? ».

   Il a raison, et le défaut n'est pas la hauteur : c'est que le texte ne
   REMPLISSAIT pas la hauteur qu'on lui donnait. Une poignée centrée, un titre
   au corps du texte courant, et le tout centré verticalement — la barre cesse
   d'être une marge autour d'une note et devient ce qu'elle est, l'en-tête de
   la liste. C'est le dessin de Plans et de Maps, à la lettre près. */
.carte__feuille > summary { flex: 0 0 auto; min-height: var(--cible);
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  gap: var(--e3); padding: var(--e2) var(--e5) var(--e3);
  cursor: pointer; font-family: var(--serif); font-size: var(--t-corps);
  font-weight: var(--g-appuyee); color: var(--encre);
  /* `list-style: none` seul ne suffit pas sur WebKit : le triangle de rendu
     natif se pose par `::-webkit-details-marker` et vient s'aligner AVANT la
     poignée, ce qui donne un second chevron sans rapport avec le nôtre. */
  list-style: none; }
.carte__feuille > summary::-webkit-details-marker { display: none; }

/* La poignée est le seul ornement, et elle porte un sens : c'est le dessin
   qu'on saisit sur toute feuille du bas depuis dix ans, et il dit « ceci se
   tire » sans un mot. Elle s'éclaircit — à pleine valeur de filet elle se
   lisait comme un trait égaré au-dessus du texte. */
.carte__feuille-poignee { grid-column: 1 / -1; justify-self: center;
  width: 2.5rem; height: 4px; border-radius: var(--r-rond);
  background: var(--filet); opacity: .9; margin-bottom: var(--e2); }

.carte__feuille-titre { min-width: 0; overflow-wrap: anywhere; }
/* Le NOMBRE est ce qu'on vient chercher : c'est lui qui dit s'il vaut la peine
   d'ouvrir. Il passe à l'encre pleine pendant que le reste de la phrase reste
   en encre douce, et prend les chiffres à chasse fixe — sans quoi « 11 » paraît
   deux fois plus étroit que « 5 » d'un écran à l'autre. */
.carte__feuille-compte { color: var(--encre); font-weight: var(--g-forte);
  font-variant-numeric: tabular-nums; }
/* Le reste de la phrase reste une mention : c'est le NOMBRE qu'on vient lire. */
.carte__feuille-titre { color: var(--encre-douce); }
.carte__feuille-titre .carte__feuille-compte { color: var(--encre); }
/* Le chevron pointe vers le HAUT quand la feuille est fermée — c'est le sens
   dans lequel elle va s'ouvrir — et se retourne une fois ouverte. */
.carte__feuille > summary .reglage__chevron { transform: rotate(-90deg);
  color: var(--encre-douce); }
.carte__feuille[open] > summary .reglage__chevron { transform: rotate(90deg); }
/* Un filet SOUS le bandeau quand la feuille est ouverte, et lui seul : fermée,
   la feuille n'a rien à séparer d'elle-même. Sans lui, le titre et la première
   ligne de la liste se touchaient et l'on ne savait plus lequel des deux était
   l'en-tête. */
.carte__feuille[open] > summary { border-bottom: 1px solid var(--filet); }
/* ------------------------------------------------------------------
   LA DERNIÈRE LIGNE DOIT PASSER SOUS LA BARRE, PAS DERRIÈRE
   ------------------------------------------------------------------
   La liste défile jusqu'au bout et la barre du bas est FIXE : la dernière ligne
   s'arrêtait donc dessous, à moitié cachée. On voyait une pastille bleue coupée
   en deux dans le coin, sans comprendre à quoi elle appartenait — JB, le
   26 août : « on a un point bleu demi-coupé en bas à gauche, c'est bizarre ».

   Ce n'était pas un défaut de dessin : c'était du CONTENU derrière un meuble.
   Le rembourrage de fin réserve la hauteur de la barre. `--barre-basse` porte
   DÉJÀ `env(safe-area-inset-bottom)` — le rajouter le compterait deux fois, et
   la liste s'arrêterait à quatre centimètres du bas. Le jeton existait ; je
   venais d'en inventer un second, et le filet posé dix lignes plus tôt l'a
   attrapé dans la seconde. */
.carte__feuille-corps { flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: var(--e3) var(--e5) calc(var(--e5) + var(--barre-basse)); }

/* ------------------------------------------------------------------
   LA FEUILLE OUVERTE LAISSE VOIR LA CARTE
   ------------------------------------------------------------------
   Elle montait à 65 % : ouverte, il restait un tiers d'écran de carte, sur
   lequel on ne pouvait plus rien viser. JB, le 27 août : « c'est encore moche
   quand il est monté ».

   Deux tiers de carte, un tiers de feuille — c'est le premier cran de Plans et
   de Maps, et la raison en est simple : on ouvre la liste POUR retrouver un
   point sur la carte, pas pour cesser de la voir. Sur un écran haut, la feuille
   peut prendre plus sans rien coûter ; c'est ce que dit `min()`. */
/* ------------------------------------------------------------------
   `display: flex` SUR UN `<details>` NE CONTRAINT PAS SON CONTENU
   ------------------------------------------------------------------
   La feuille était haute de 224 px et son corps de 469 : le contenu était
   COUPÉ, et non défilable. JB, le 29 août 2026 : « pas de défilement dans le
   bandeau, on ne voit que les premiers ».

   La cause est une particularité de `<details>` : le navigateur glisse tout ce
   qui n'est pas le `<summary>` dans un emplacement interne, et cet emplacement
   ne reçoit pas la contrainte de hauteur d'un contexte flex comme le ferait un
   enfant ordinaire. `flex: 1 1 auto; min-height: 0` sur le corps ne suffit
   donc pas — il calculait sa hauteur naturelle, et `overflow: hidden` sur la
   feuille rognait le reste. Un défilement qui n'existe pas ressemble trait
   pour trait à une liste qui s'arrête.

   On BORNE donc le corps explicitement, sur la même valeur que la feuille
   moins son bandeau. C'est moins élégant qu'une chaîne flex, et c'est vrai
   dans tous les navigateurs. */
.carte__feuille { max-height: var(--feuille-haute); }
.carte__feuille-corps { max-height: calc(var(--feuille-haute) - var(--feuille-bandeau)); }
@media (max-height: 40rem) {
  /* Sur un petit téléphone, 38 % ne tiennent pas trois lignes : on rend la
     moitié, et la carte reste la moitié. */
  .carte__feuille { max-height: 50dvh; }
  .carte__feuille-corps { max-height: calc(50dvh - var(--feuille-bandeau)); }
}

/* LE BANDEAU ENTIER EST LA CIBLE, la poignée n'est que le signe. On visait un
   trait de 2,25 rem sur 4 px ; c'est la même faute que les cibles de 23 px
   qu'on a passé une semaine à relever. */
.carte__feuille > summary { touch-action: manipulation; }

/* Le compte d'une catégorie, dans sa pastille de filtre : à chasse fixe, pour
   que « 1 » et « 14 » n'écartent pas le libellé d'une largeur différente. */
.carte__compte-genre { color: var(--encre-douce); font-variant-numeric: tabular-nums;
  margin-left: var(--e1); }
.destination-rubrique--active .carte__compte-genre { color: var(--encre); }
/* La pastille de couleur entre DANS la puce : c'est elle qui fait désormais la
   légende, et il n'y a plus de légende ailleurs. */
.destination-rubrique .pastille { margin-right: var(--e2); }

/* Les lignes se resserrent : quatorze lieux tenaient sur deux écrans et demi
   parce que chaque ligne portait l'interligne d'un paragraphe. Le nom et sa
   mention sont deux lignes d'une même entrée, pas deux paragraphes. */
.carte__lieux .liste__corps { display: flex; flex-direction: column; gap: 2px; }
.carte__lieux .liste__ligne { padding-block: var(--e2); align-items: flex-start; }

/* Les sorties d'écran, en pied de feuille et en liens : ce sont des
   destinations, pas des actions, et elles ne se mettent pas devant ce qu'on est
   venu voir. */
.carte__sorties { display: flex; flex-wrap: wrap; gap: var(--e2) var(--e5);
  margin-top: var(--e4); padding-top: var(--e3);
  border-top: 1px solid var(--filet-clair);
  font-family: var(--sans); font-size: var(--t-meta); }

/* ------------------------------------------------------------------
   LES CONTRÔLES DE LEAFLET S'ÉCARTENT DU BANDEAU
   ------------------------------------------------------------------
   L'attribution des tuiles vit en bas à droite, exactement où se pose le
   bandeau de la feuille. Passer la feuille au-dessus la ferait DISPARAÎTRE —
   et la licence des tuiles de l'IGN impose de la montrer. Passer la feuille
   au-dessous laissait les contrôles se peindre dans le bandeau, ce qui est le
   défaut qu'on répare.

   La seule sortie est de leur laisser la place : ils remontent de la hauteur
   du bandeau. Le zoom en bas à gauche remonte avec eux, et c'est un gain — il
   tombait sous le pouce qui ouvre la feuille. */
.carte-page .leaflet-bottom { bottom: var(--feuille-bandeau); }

/* ------------------------------------------------------------------
   LES CIBLES DE LEAFLET, QUI NE CONNAÎT PAS NOS JETONS
   ------------------------------------------------------------------
   Les marqueurs mesuraient 16 x 16 px et les boutons de zoom 30 x 30 —
   c'est-à-dire moins que le minimum de WCAG 2.5.8 (24 px), et très loin des
   48 px que le produit s'est donnés. Sur une carte de famille consultée par des
   gens de soixante-dix ans, un point de seize pixels ne se touche pas.

   Le POINT reste petit à l'œil : c'est sa zone qui grandit, par un
   pseudo-élément centré. Grossir le dessin aurait couvert le village voisin. */
/* ------------------------------------------------------------------
   OÙ CE QU'ON ÉCRIT SERA LU
   ------------------------------------------------------------------
   Une seule famille : la destination reste dans un champ caché pour le
   serveur, et se LIT pour la personne. Le ton est celui d'une mention, pas
   d'une alerte — rien ne va mal, on rappelle un fait. */
.portee { display: flex; align-items: center; gap: var(--e2);
  margin: 0 0 var(--e3); font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); }

/* ------------------------------------------------------------------
   NE JAMAIS REPOSER `position` SUR UN MARQUEUR DE LEAFLET
   ------------------------------------------------------------------
   Cette règle portait `position: relative`, pour donner au pseudo-élément un
   bloc conteneur. Elle écrasait les « required styles » de Leaflet — même
   spécificité, et notre feuille est servie APRÈS la sienne, délibérément.

   Leaflet ne déplace un marqueur que par `transform`, en comptant sur
   `position: absolute; left: 0; top: 0` pour partir de l'origine du volet.
   En `relative`, `left/top` ne font plus rien : chaque marqueur reprend sa
   place dans le FLUX, seize pixels sous le précédent, et la translation
   s'ajoute à ce décalage. Le premier point tombe juste, le dixième est cent
   quarante pixels trop bas — à l'échelle de la France, cent quarante
   kilomètres. Marseille avait les pieds dans l'eau.

   Deux raisons pour lesquelles personne ne l'a vu : le décalage CROÎT avec le
   rang, donc les premiers points semblent justes et l'on croit à un mauvais
   géocodage ; et avec UN SEUL point il n'existe pas — l'essai de tenue n'en
   posait qu'un. Il en faut deux pour que le défaut existe.

   `position: absolute` est déjà un bloc conteneur : le pseudo-élément n'a
   jamais eu besoin de `relative`. */
.leaflet-marker-icon::after { content: ""; position: absolute;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: var(--cible); height: var(--cible); border-radius: var(--r-rond); }

.carte-page .leaflet-control-zoom a { width: var(--cible); height: var(--cible);
  line-height: var(--cible); font-size: 1.25rem; }

/* Le bandeau de la feuille : `min-height` ne s'applique pas à un `summary` en
   `display: grid` sans hauteur de ligne suffisante — on la pose donc aussi sur
   le contenu. */
.carte__feuille > summary { min-height: var(--cible); }


/* Les photos d'une maison : deux de front, pleine largeur sur téléphone. Une
   grille serrée ferait une planche-contact ; on en montre peu et grand, parce
   qu'on les regarde une par une. */
.maison-photos { display: grid; gap: var(--e2); margin: var(--e5) 0;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); }
.maison-photos img { display: block; width: 100%; height: auto;
  border-radius: var(--r-petit); }

/* Ce qui manque encore à sa fiche : un encadré doux, jamais une alerte. Rien
   n'y est obligatoire, et le bloc disparaît dès que tout est complet. */
.a-completer { margin: 0 0 var(--e7); padding: var(--e4) var(--e4);
  background: var(--filet-clair); border-radius: var(--r-petit); }
.a-completer .rubrique { display: block; margin: var(--e2); }
/* Un bloc qui a un FOND n'a pas besoin de filets internes : c'est le premier
   de « Moi », et il ouvrait l'écran sur un aplat rempli de traits. */
.a-completer .liste__ligne { border-bottom: 0; }
.a-completer .liste__ligne:last-child { border-bottom: 0; padding: 0; }

/* ------------------------------------------------------------------
   UNE DESCRIPTION SOUS SON LIEN, JAMAIS À SA SUITE
   ------------------------------------------------------------------
   `.secondaire` est un `span`, donc en ligne : dans une rangée de liste, la
   description coulait juste après le lien, sur la même ligne. On lisait « Qui
   peut m'aider à rentrer Les personnes qui pourront confirmer que c'est bien
   vous » d'un seul tenant, et le lien se perdait dans la phrase.

   Une seule règle, et toutes les listes du produit y gagnent. */
.liste__ligne .secondaire { display: block; margin: var(--e0); }


/* ==================================================================
   LES FAMILLES ALLIÉES
   ==================================================================
   Cet écran n'avait AUCUN style : `.alliance`, `.alliance-portee`,
   `.alliance__nom` et `.alliance__gestes` étaient employées dans la vue et
   déclarées nulle part. Il tombait donc au rendu par défaut du navigateur —
   du Times Roman de 16 px sur une page dont toute la typographie est réglée
   au demi-point.

   Une alliance est un ACTE de régie : deux familles décident de se voir. Le
   registre est celui des cartes de cousinage de « Moi », dont il reprend le
   filet et l'entête. */
.alliance { margin: 0 0 var(--e4); padding: var(--e4) var(--e4); border: 1px solid var(--filet);
  border-radius: var(--r-petit); background: var(--surface); }
.alliance__nom { display: block; font-family: var(--serif);
  font-size: var(--t-sous-titre); font-weight: var(--g-appuyee); margin: 0 0 var(--e1); }
.alliance-portee { display: block; font-size: var(--t-meta);
  color: var(--encre-douce); margin: 0 0 var(--e2); }
.alliance__gestes { display: flex; flex-wrap: wrap; gap: var(--e2);
  padding: var(--e2); border-top: 1px solid var(--filet-clair); }
.alliance__gestes form { margin: 0; }

/* Le cercle familial, sur l'écran de consentement d'un sacrement. Même défaut :
   une classe employée, aucune règle. */
.cercle-familial { margin: var(--e3) 0; padding: var(--e3) 0 var(--e3) var(--e4);
  border-left: 3px solid var(--filet); }
.cercle-familial p { margin: 0 0 var(--e1); }
.cercle-familial p:last-child { margin: 0; }


/* ==================================================================
   LES PETITES HAUTEURS — LE PAYSAGE, ET LE CLAVIER OUVERT
   ==================================================================
   La feuille ne comptait AUCUNE requête de hauteur. Un iPhone couché fait
   393 px de haut, dont il reste environ 345 après la barre du navigateur : le
   fil de discussion y montrait SOIXANTE-SEIZE PIXELS de conversation — deux
   bulles — sous une entête de 56, un composeur de 80, une ligne d'état et un
   pied de signalement. Et l'on est couché précisément quand on écrit à deux
   mains.

   Rien n'est retiré qui porte une information : l'entête se resserre, la ligne
   d'état vide cesse de réserver sa place, et le drapeau — qui sert une fois par
   an — laisse la sienne à ce qu'on est venu lire. */
@media (max-height: 30rem) {
  .fil-entete { height: 44px; }
  .fil-entete__origine { display: none; }
  .signalement-du-pied { display: none; }
  .carte-page__entete { padding: var(--e0) var(--e5); }
  .medaillon__cercle { width: 6rem; height: 6rem; }
}

/* Vingt et un pixels réservés en permanence pour une ligne vide
   quatre-vingt-dix-neuf pour cent du temps. Elle ne prend sa place que
   lorsqu'elle a quelque chose à dire. */
.composeur__etat:empty { min-height: 0; margin: 0; }

/* Aucune règle globale ne bornait la largeur des images : toute image posée
   sans classe débordait. Celle-ci ne coûte rien et ferme la question. */
img { max-width: 100%; }

/* ------------------------------------------------------------------
   LA PHOTO D'UNE BULLE RÉSERVE SA PLACE — SANS ÊTRE COUPÉE
   ------------------------------------------------------------------
   `width: 100%; height: auto` sans rapport d'aspect : la hauteur n'est connue
   qu'une fois l'image chargée. Dans un fil ancré en bas, chaque photo qui
   arrive fait donc SAUTER le fil sous le doigt — au moment précis où l'on
   lisait autre chose.

   La parade était `aspect-ratio: 4 / 3` avec `object-fit: cover`, posé sur
   TOUTES les photos : le fil ne sautait plus, et l'on amputait chaque photo de
   ce qui dépassait du 4/3 — brutalement pour une photo tenue à la verticale.
   Deux découpes s'ajoutaient même, celle-ci et le carré que produisait le
   serveur.

   La vraie parade est de DONNER les dimensions à l'image : `width` et `height`
   posés sur la balise font réserver au navigateur la boîte exacte, avant le
   premier octet. Le repli 4/3 ne vaut donc plus que pour une image dont on
   ignore la taille — analyse pas encore passée, fichier exotique — et
   `contain` y remplace `cover` : mieux vaut une bande vide qu'une photo
   coupée. */
.bulle__photo img:not([width]) { aspect-ratio: 4 / 3; object-fit: contain; }


/* ==================================================================
   LE BANDEAU — « À LA UNE » EST UNE VUE, PAS UN TYPE DE CONTENU
   ==================================================================
   Il était une table, un écran et un rituel hebdomadaire à la fois : d'où
   l'impossibilité de dire ce qu'on y met. Ce n'est qu'une présentation — les
   cinq choses les plus récentes de la famille, qu'elles viennent du carnet (le
   permanent, pour la postérité) ou des annonces (l'éphémère, qui passe).

   Il DÉFILE latéralement : cinq lignes empilées en tête repousseraient ce qu'on
   vient lire. Et chaque carte porte sa provenance par un filet de couleur —
   c'est le seul indice dont on ait besoin pour savoir si l'on regarde un
   faire-part ou un canapé à donner. */
/* L'ÉCU D'UNE FAMILLE
   Le grisé passe par `filter`, et non par des couleurs de rechange : la
   désaturation s'applique au tranché comme au plein, sans qu'aucune règle ait à
   connaître les teintes. Le contour PONCTUÉ est le second indice qu'exige la
   WCAG 1.4.1 — en vision monochrome, c'est lui qui distingue la fiche qui
   attend son membre de celle qui l'a. */
.ecu { width: 1.15em; height: 1.38em; flex: 0 0 auto; vertical-align: -0.25em; }

/* DEUX ÉCUS ACCOLÉS — le bas qui s'approche de l'autre, comme sur une
   chevalière. Dix degrés : assez pour qu'on voie que les deux se répondent,
   assez peu pour que chaque écu reste lisible. Ils rétrécissent d'un quart,
   sans quoi la paire déborderait la place d'un écu seul. */
.ecus-accoles { display: inline-flex; align-items: flex-start; gap: 0; }
.ecus-accoles .ecu { width: .86em; height: 1.04em; }
.ecus-accoles .ecu:first-child { transform: rotate(10deg); transform-origin: top right; }
.ecus-accoles .ecu:last-child  { transform: rotate(-10deg); transform-origin: top left; }
@media (prefers-reduced-motion: reduce) { .ecus-accoles .ecu { transform: none; } }
.ecu__contour { stroke: var(--filet); stroke-width: 1.5; }

/* LA PRÉSENCE SE DIT SUR LE VISAGE, PAS SUR L'ÉCU
   L'écu grisé portait deux informations à la fois : la lignée et la présence.
   Un fondateur qui n'avait pas répondu à son invitation perdait sa couleur, et
   la diagonale de toute sa descendance devenait illisible.

   Le cerclé PONCTUÉ est le second indice qu'exige la WCAG 1.4.1 : en vision
   monochrome, c'est lui — et non le gris — qui distingue la fiche qui attend
   son membre de celle qui l'a. */
/* LE ROND ET SES LETTRES RESTENT, SEULE LA COULEUR S'EN VA.
   Une version d'un jour retirait les initiales des fiches sans compte : le
   médaillon DISPARAISSAIT tant que la feuille de style n'était pas là — un
   `<span>` vide n'occupe rien — et les cases de l'arbre changeaient de hauteur
   selon qu'il était rendu ou non. Deux défauts pour un.

   Le gris porte l'attente à lui seul, et il la porte bien : on lit les lettres,
   on voit qu'elles n'ont pas leur couleur. */
.portrait--en-attente { filter: grayscale(1) opacity(0.72); }
/* Le coin haut-gauche d'une fiche : les écus s'y empilent horizontalement, un
   par famille PARTAGÉE avec le lecteur — jamais plus, la politique de
   `person_cousinage_scopes` s'en chargeant. */
/* Dans une vignette du trombinoscope, l'écu suit le texte au lieu de se poser
   dans un coin : la carte n'a pas de repère de position, et la grille l'y
   collerait de travers. */
.ecus--vignette { position: static; justify-content: center; margin-top: 2px; }
.ecus { position: absolute; top: var(--e2); left: var(--e2);
        display: flex; gap: 2px; pointer-events: none; }
/* La boîte devient le repère de position de ses écus. Sans cela, ils se
   placeraient sur le premier ancêtre positionné — c'est-à-dire n'importe où. */
.noeud { position: relative; }

/* EMPÊCHER LE DOUBLON PLUTÔT QUE LE RÉPARER
   Un rapprochement n'est PAS une erreur, et il ne doit pas en porter la
   couleur : `--alerte` ferait croire à une faute de saisie devant trois Marie
   de Kergorlay, qui sont trois personnes bien réelles. C'est une question, et
   elle se pose du même ton que la proposition croisée. */
.rapprochement { margin: var(--e4) 0; padding: var(--e4);
                 border: 1px solid var(--filet); border-left: 3px solid var(--geste);
                 border-radius: 4px; background: var(--filet-clair); }
.rapprochement > p { margin: 0 0 var(--e3); }
.rapprochement .liste { margin: 0 0 var(--e3); }

/* COU-VIE-005 — LA MARCHE SUIVANTE, PROPOSÉE
   Elle se distingue d'un message de succès : celui-ci dit ce qui est fait, la
   proposition dit ce qui RESTE à faire. Les confondre ferait lire l'une pour
   l'autre, et l'on cliquerait sans savoir. D'où le filet à gauche et le fond
   tenu, qui la rattachent visuellement au carnet plutôt qu'aux alertes. */
.proposition { margin: 0 0 var(--e5); padding: var(--e4);
               border: 1px solid var(--filet); border-left: 3px solid var(--geste);
               border-radius: 4px; background: var(--filet-clair); }
.proposition p { margin: 0 0 var(--e3); font-family: var(--serif);
                 font-size: var(--t-corps); line-height: 1.5; }
.proposition .bouton { display: inline-block; }

/* Le défilement se voit, sinon il n'existe pas. Sans l'estompage, la dernière
   carte est COUPÉE NET en plein mot au bord de la colonne, et rien ne dit qu'il
   y en a d'autres : cela se lit comme un défaut d'affichage, pas comme une
   invitation à faire glisser. */
.bandeau { display: flex; gap: var(--e2); margin: 0 0 var(--e5);
  overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
  padding-right: 2.5rem; }
.bandeau::-webkit-scrollbar { display: none; }
.bandeau__nouvelle { flex: 0 0 auto; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: var(--e0);
  width: min(22rem, 78vw); min-height: 44px; padding: var(--e3);
  border: 1px solid var(--filet); border-left: 3px solid var(--filet);
  border-radius: var(--r-petit); background: var(--surface);
  text-decoration: none; color: var(--encre); }
.bandeau__nouvelle:hover { border-color: var(--lien); }
/* Deux provenances, deux couleurs — et les PETITES ANNONCES n'en sont pas :
   elles se consultent dans leur onglet. Un canapé à donner posé à côté d'un
   faire-part de décès abîme les deux, et c'est la première chose que voit un
   membre en se connectant. */
/* Trois provenances, trois couleurs. C'est ce qui remplace l'exclusion : une
   annonce et un faire-part peuvent voisiner tant qu'on voit du premier coup
   d'œil lequel est lequel. */
.bandeau__nouvelle--carnet     { border-left-color: var(--pastille-nouvelle); }
.bandeau__nouvelle--ephemeride { border-left-color: var(--pastille-maison); }
.bandeau__nouvelle--annonce    { border-left-color: var(--pastille-annonce); }
/* L'INTITULÉ DE LA BANDE — il n'existait qu'en `aria-label`, donc pour les
   seuls lecteurs d'écran. Discret : il nomme, il ne pèse pas. */
.bandeau__intitule { margin: 0 0 var(--e2); font-family: var(--sans);
  font-size: var(--t-micro); font-weight: var(--g-forte);
  letter-spacing: .06em; text-transform: uppercase; color: var(--encre-douce); }

.bandeau__rubrique { font-family: var(--sans); font-size: var(--t-micro); font-weight: var(--g-appuyee);
  color: var(--encre-douce); }
.bandeau__titre { font-family: var(--serif); font-size: var(--t-corps);
  line-height: 1.3; overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
/* `flex-wrap` : le nom de famille et la date se partagent le pied de la carte.
   Sans lui, « Cousinage de Philippe et Anne-Françoise de Cathelineau » se coupe
   à « Cousinage de P. et A.-F. de … » — deux lignes courtes valent mieux qu'une
   ligne tronquée, surtout quand la ligne tronquée est celle qui dit CHEZ QUI. */
.bandeau__quand { display: flex; align-items: center; gap: var(--e1);
  flex-wrap: wrap;
  margin-top: auto; padding-top: var(--e1);
  font-family: var(--sans); font-size: var(--t-micro); color: var(--encre-douce); }

/* ------------------------------------------------------------------
   LA CARTE NE GÈLE PLUS À DIX-SEPT REM
   ------------------------------------------------------------------
   `min(17rem, 78vw)` avait l'air adaptatif et ne l'était que sur les
   téléphones ÉTROITS : au-delà de 349 px de large, c'est toujours le 17 rem qui
   l'emporte, et la carte reste à 272 px quelle que soit la place. Une seule
   nouvelle dans la bande de 1088 px du portail donnait une note collante égarée
   au milieu du papier, avec un titre replié sur deux lignes qui tenait sur une.

   Relevé par JB le 30 août 2026 — et il l'avait déjà dit le 29 : « on a une
   carte Cousinage qui déconne ». J'avais corrigé le NOM affiché dessus et pas
   sa TAILLE, qui était la question. Le 22 rem laisse le `78vw` gouverner sur
   tout téléphone au-delà de l'iPhone SE : le bord de la carte suivante reste
   visible, donc on sait qu'il faut faire glisser.

   ------------------------------------------------------------------
   ET AU-DELÀ DE DEUX CARTES DE LARGE, LE CARROUSEL N'A PLUS DE RAISON
   ------------------------------------------------------------------
   Il défile latéralement parce que cinq nouvelles empilées repousseraient d'un
   cran ce qu'on vient lire : c'est vrai sur un téléphone, où la HAUTEUR est la
   ressource rare. Sur un écran large, la ressource rare est la largeur laissée
   vide — et faire glisser pour atteindre ce qui aurait pu être visible est un
   geste qu'on ne fait pas. Les cartes partagent donc la bande, avec un plancher
   pour rester lisibles et un plafond pour qu'une nouvelle seule ne s'étire pas
   sur toute la page.

   L'estompage et la réserve de droite partent avec le défilement : ils
   annonçaient « il y en a d'autres » alors qu'il n'y a plus rien à atteindre. */
@media (min-width: 48rem) {
  .bandeau { flex-wrap: wrap; overflow-x: visible; scroll-snap-type: none;
    -webkit-mask-image: none; mask-image: none; padding-right: 0; }
  .bandeau__nouvelle { flex: 1 1 17rem; width: auto; max-width: 26rem; }
}

/* Le geste qui suit la matrice de visibilité : il la complète, il ne la
   concurrence pas — d'où la même colonne, un cran plus bas, et son explication
   sous lui plutôt qu'à côté. */
.apres-matrice { display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--e2); margin: var(--e5) 0 0; }


/* ==================================================================
   LA BANDE HAUTE — UNE PLACE RÉSERVÉE, PAS UN OBJET QUI FLOTTE
   ==================================================================
   Le profil a quitté la barre du bas : elle tient six places et il en fallait
   une pour l'album, une pour les archives. Il est devenu un médaillon en haut
   à droite — le visage vaut mieux qu'une silhouette générique, et c'est la
   convention de tout ce que la cible emploie déjà.

   Mais il FLOTTAIT. Relevé sur dix captures d'écran : il recouvrait une carte
   de nouvelle, le bouton « Déclarer une nouvelle », une liste déroulante de
   réglage, un bouton radio — et il était lui-même tranché par le bord haut de
   l'écran. Un élément en position fixe posé sur du contenu qui défile finira
   toujours par tomber sur quelque chose ; il faut lui RÉSERVER sa place.

   La bande est collante et opaque : le contenu passe dessous en défilant,
   jamais dessus. Quarante-huit pixels, et toute cette famille de défauts
   devient impossible. */
.bande-haute { position: sticky; top: 0; z-index: 40;
  display: flex; justify-content: flex-end; align-items: center;
  height: 3rem; padding: 0 var(--e3);
  padding-top: env(safe-area-inset-top, 0px);
  background: var(--papier); }

/* ------------------------------------------------------------------
   LE LIEN D'ÉVITEMENT
   ------------------------------------------------------------------
   Hors écran tant qu'il n'a pas le focus, et posé DEVANT tout dès qu'il l'a.
   `clip-path` plutôt que `display: none` : ce dernier le retirerait de l'ordre
   de tabulation, c'est-à-dire le supprimerait.

   Il ne se voit jamais à la souris, et c'est voulu : il ne coûte rien à qui ne
   s'en sert pas. */
.evitement { position: absolute; left: var(--e3); top: var(--e3);
  z-index: calc(var(--plan-barre) + 1);
  padding: var(--e2) var(--e4); min-height: var(--cible);
  display: inline-flex; align-items: center;
  background: var(--surface); color: var(--encre);
  border: 1px solid var(--encre); border-radius: var(--r-petit);
  font-family: var(--sans); font-size: var(--t-meta); text-decoration: none;
  transform: translateY(-200%); }
.evitement:focus { transform: none; }
/* Le `main` prend le focus par le lien d'évitement : il ne doit pas en porter
   le liseré, qui n'apprendrait rien — la page entière a bougé, cela se voit. */
.page:focus { outline: none; }

/* ------------------------------------------------------------------
   LA CIBLE DU POUCE — 48 px, ET LE JETON EXISTAIT DÉJÀ
   ------------------------------------------------------------------
   L'accès au profil mesurait 39 x 18 px, dans le coin supérieur droit, qui est
   la pire zone pour un pouce quand on tient le téléphone d'une main. C'était le
   SEUL point d'entrée vers le profil et les réglages.

   `--cible` vaut 48 depuis l'origine et n'était pas appliqué ici. On l'applique
   sans grossir le dessin : c'est la ZONE qui s'étend, pas le portrait. */
.medaillon-profil { position: relative; display: inline-flex;
  align-items: center; gap: var(--e2);
  min-width: var(--cible); min-height: var(--cible);
  padding: 0 var(--e2);
  border-radius: var(--r-rond); }
/* `display: block` sur la seule IMAGE — il retire l'espace de ligne de base
   sous un <img>. Posé aussi sur `.portrait--initiales`, il défaisait le
   centrage en flex des initiales : « JE » sortait du rond par le haut, et le
   coin de l'écran montrait deux lettres orphelines au-dessus d'un disque vide. */
.medaillon-profil img { border-radius: var(--r-rond); display: block; }
.medaillon-profil .portrait { border-radius: var(--r-rond); }
.medaillon-profil:hover { box-shadow: 0 0 0 3px var(--filet-clair); }

/* La pastille du Courrier suit le médaillon : c'est par lui que passent les
   mandats, les corroborations et les mises en relation. */
.medaillon-profil__pastille { position: absolute; top: -3px; right: -3px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--pastille-nouvelle); color: #fff;
  border: 2px solid var(--papier); border-radius: var(--r-rond);
  font-family: var(--sans); font-size: 10px; font-weight: var(--g-forte);
  font-variant-numeric: tabular-nums; }

/* Sur écran large, la barre est devenue une colonne : la bande n'a plus lieu
   d'être en haut du contenu, et le médaillon rejoint le pied du rail. */
/* ------------------------------------------------------------------
   AU PIED DU RAIL, C'EST UNE LIGNE, PAS UN ROND ORPHELIN
   ------------------------------------------------------------------
   Un disque de trente-quatre pixels posé seul dans un coin ne dit rien : on
   ne sait ni à qui il appartient, ni ce qu'il ouvre, et il ressemblait à un
   objet oublié là. Sur téléphone c'est la convention et elle se comprend —
   la barre du haut n'a que lui. Dans une colonne de navigation où chaque
   entrée porte une icône ET un libellé, il détonnait.
   
   Il prend donc la forme des autres entrées : le visage à la place de
   l'icône, le prénom à côté, la même hauteur, le même retrait. C'est ce que
   font les applications de bureau, et pour la même raison. */
@media (min-width: 60rem) {
  /* ------------------------------------------------------------------
     LE RAIL LE RECOUVRAIT ENTIÈREMENT
     ------------------------------------------------------------------
     La colonne de navigation porte `z-index: var(--plan-barre)`, c'est-à-dire
     1200, un fond OPAQUE, et occupe exactement `left: 0; width: 15rem` du haut
     au bas de l'écran — la place même où le médaillon vient se poser. À 41, il
     était peint dessous : invisible.

     Sur ordinateur, c'était le SEUL chemin vers le profil, les réglages, le
     courrier et la déconnexion. Il ne restait qu'à rétrécir la fenêtre pour
     faire croire à un téléphone.

     Aucun essai ne pouvait le voir : toute la tenue est éprouvée à 390 px de
     large, et rien ne regardait jamais l'écran large. C'est réparé par
     `tenue_sur_ordinateur_test.rb`. */
  .bande-haute { position: fixed; top: auto; bottom: var(--e5);
    left: 0; right: auto; width: 15rem; height: auto;
    padding: 0 var(--e3); background: none; justify-content: stretch;
    z-index: calc(var(--plan-barre) + 1); }
  .medaillon-profil { display: flex; align-items: center; gap: var(--e3);
    width: 100%; min-height: 42px; padding: 0 var(--e3);
    border-radius: var(--r-moyen); color: var(--encre-douce);
    font-family: var(--sans); font-size: var(--t-corps);
    font-weight: var(--g-appuyee); text-decoration: none; }
  .medaillon-profil:hover { background: var(--filet-clair); color: var(--encre);
    box-shadow: none; }
  .medaillon-profil__nom { min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  /* La pastille revient contre le visage, et non au coin d'une ligne large. */
  .medaillon-profil__pastille { left: 27px; right: auto; }
}
@media (max-width: 59.99rem) { .medaillon-profil__nom { display: none; } }
@media (min-width: 90rem) { .bande-haute { width: 17rem; } }

/* ------------------------------------------------------------------
   POURQUOI IL N'Y A PAS DE RÈGLE `.onglets` ICI
   ------------------------------------------------------------------
   Il y en a eu une — `grid-template-columns: repeat(6, 1fr)` — posée en fin de
   feuille pour dire « six onglets et non sept ». Même spécificité que celle du
   bloc écran large, mais PLUS BAS : elle gagnait donc partout, y compris
   au-delà de 60 rem où la barre devient une colonne. Résultat sur un portable :
   six onglets étalés en travers du haut de l'écran, par-dessus le contenu.

   Le nombre de colonnes se déclare à UN endroit, là où la barre est dessinée
   pour le téléphone. Une règle de rattrapage en fin de feuille est toujours le
   début d'une collision de cascade. */


/* ------------------------------------------------------------------
   LA CROIX DE SORTIE, POSÉE SUR L'IMAGE
   ------------------------------------------------------------------
   Elle est le geste universel pour quitter une photo, et elle doit être là où
   le pouce la cherche : en haut à droite, SUR l'image, sans avoir à faire
   défiler quoi que ce soit.

   `position: sticky` et non `absolute` : sur une photo en portrait qui dépasse
   l'écran, une croix absolue part hors de vue dès qu'on défile un peu — c'est
   le défaut qu'on répare, reproduit autrement.

   Fond sombre et translucide, encre claire : une croix doit rester lisible
   au-dessus d'un ciel comme d'une soutane, et l'on ne sait pas ce qu'il y a
   sous elle. */
.photo { position: relative; }
.photo__fermer {
  position: sticky; top: var(--e3); float: right; z-index: 3;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--cible); height: var(--cible); margin: var(--e3) var(--e3) calc(-1 * var(--cible)) 0;
  border-radius: var(--r-rond);
  background: rgba(18, 17, 16, .55); color: #FFFDF8;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.photo__fermer:hover { background: rgba(18, 17, 16, .78); }
.photo__fermer:focus-visible { outline: 3px solid #FFFDF8; outline-offset: 2px; }
.photo__fermer svg { width: 22px; height: 22px; }

/* La phrase de la famille passe AVANT la date et l'appareil, et elle se lit :
   romaine, corps du texte courant, pas la grise des métadonnées. */
.photo__mot { font-family: var(--serif); font-size: var(--t-corps);
  line-height: 1.5; color: var(--encre); margin: var(--e4) 0 var(--e2);
  text-wrap: pretty; }

/* ==================================================================
   LA FRISE DES MOIS DU CARNET
   ==================================================================
   Elle remplace deux flèches en pied de page. Sur un mois vide — le cas le
   plus fréquent — l'écran ne donnait rien à faire et rien à voir, et il
   fallait douze clics pour atteindre le mois d'une naissance. Elle défile
   latéralement comme le bandeau : la même forme pour le même geste. */
/* UNE ligne qui défile, jamais un pavé qui se replie : même geste que le
   bandeau des nouvelles, même estompage au bord. Les flèches du pas à pas
   tiennent les deux bouts — elles seules savent atteindre un mois vide. */
.carnet__frise { display: flex; flex-wrap: nowrap; gap: var(--e1);
  list-style: none; margin: 0; padding: 0; min-width: 0; flex: 1 1 auto;
  overflow-x: auto; scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 1.5rem), transparent); }
.carnet__frise::-webkit-scrollbar { display: none; }
.carnet__frise li { flex: 0 0 auto; }
.carnet__frise--seule { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); align-self: center; }

/* 40 x 40 : sous la cible du pouce, pour les flèches qui font défiler les mois
   du carnet — un geste qu'on répète, sur un écran que consultent des gens de
   soixante-dix ans. */
.carnet__pas { flex: 0 0 auto; display: inline-flex; align-items: center;
  justify-content: center; width: var(--cible); min-height: var(--cible);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  background: var(--surface); color: var(--encre-douce);
  font-size: 1.25rem; line-height: 1; text-decoration: none; }
a.carnet__pas:hover { color: var(--encre); border-color: var(--encre-douce);
  background: var(--filet-clair); }
.carnet__pas--eteint { opacity: .35; }
.carnet__mois-lien { display: inline-flex; align-items: center;
  min-height: 40px; padding: 0 var(--e3); white-space: nowrap;
  font-family: var(--sans); font-size: var(--t-meta); font-weight: var(--g-appuyee);
  color: var(--encre-douce); text-decoration: none;
  border: 1px solid transparent; border-radius: var(--r-rond);
  transition: color var(--duree), border-color var(--duree); }
.carnet__mois-lien:hover { color: var(--encre); border-color: var(--filet); }
/* Le mois qu'on lit se DÉSIGNE, il ne se colore pas : dans un registre, la
   couleur est réservée à l'origine d'un cousinage. */
.carnet__mois-lien--courant { color: var(--encre); background: var(--filet-clair);
  border-color: var(--filet); }



/* ==================================================================
   TROIS ZONES, ET ON DOIT LES DISTINGUER SANS LES LIRE
   ==================================================================
   Le grief, mot pour mot : « on a un PB de clarté entre zone d'information,
   zone d'édition, et zone de gestion par le régisseur ». Elles portaient toutes
   la même surface blanche, le même filet, le même rayon — seul le texte les
   distinguait, c'est-à-dire qu'il fallait les lire pour savoir ce qu'on
   regardait.

   Trois traitements, et un seul indice chacun. On ne cumule pas : une zone qui
   porte une couleur ET un fond ET un liséré crie plus fort que le contenu.

     INFORMATION — le fond de la page, aucun cadre. C'est l'état par défaut :
       ce qu'on vient lire n'a pas à se signaler.
     ÉDITION — un fond de surface et un filet. C'est un objet posé SUR la
       page, dans lequel on écrit.
     RÉGIE — le fond gris du papier et un liséré à gauche. Le gris dit
       « atelier », pas « contenu ».

   Le pied de page, lui, n'est aucune des trois : il est en retrait, sans
   cadre, en encre douce — et il ne porte JAMAIS un geste d'écriture. Un lien
   d'exploitation posé au milieu de liens de lecture est exactement ce qui fait
   cliquer par erreur. */
.zone-edition {
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-moyen); padding: var(--e5);
  margin: 0 0 var(--e5);
}
.zone-edition > :first-child { margin-top: 0; }
.zone-edition > :last-child { margin-bottom: 0; }

/* La régie reprend le liséré à gauche, qui est déjà la marque d'une zone
   réservée sur `.cousinage-carte`. Une seule grammaire pour une seule idée. */
.regie { border-left: 3px solid var(--encre-douce); }

/* ------------------------------------------------------------------
   LE PIED DE PAGE N'EST PAS UNE ZONE DE GESTE
   ------------------------------------------------------------------
   Il porte les mentions, les chemins de retour, les liens d'éditeur. Rien qui
   écrive, rien qui supprime. En retrait, sans cadre : on doit voir d'un coup
   d'œil que la page est finie. */
.pied-de-page {
  margin: var(--e8) 0 0; padding: var(--e5) 0 0;
  border-top: 1px solid var(--filet-clair);
  display: flex; flex-wrap: wrap; gap: var(--e2) var(--e4);
  font-family: var(--sans); font-size: var(--t-meta); color: var(--encre-douce);
}
/* Des CASES, comme toute destination du produit. La forme précédente — un
   filet décollé SOUS le lien, plus bas qu'un soulignement — se voulait
   discrète et se lisait comme un style oublié : ni lien souligné, ni bouton.
   La case dit « on peut appuyer ici », garde la voix grise du pied de page,
   et offre la cible tactile que le trait n'a jamais eue. */
/* `.pied-de-page a` bat `.lien-discret` par la spécificité (0-1-1 contre 0-1-0),
   et posait 40 px : les liens de pied — « Mon courrier », « Les maisons de
   famille » — restaient sous la cible du pouce alors que leur classe la portait.
   C'est le genre d'écrasement qu'aucune lecture du CSS ne révèle, et qu'un
   navigateur qui MESURE trouve en une seconde. */
.pied-de-page a { display: inline-flex; align-items: center;
  min-height: var(--cible); padding: 0 var(--e3);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  color: var(--encre-douce); text-decoration: none; background: var(--surface); }
.pied-de-page a:hover { color: var(--encre); border-color: var(--encre-douce);
  background: var(--filet-clair); }
/* La case rend la marge du lien discret superflue dans un pied : elle
   doublerait la gouttière que la rangée pose déjà. */
.pied-de-page .lien-discret { margin: 0; }

/* ------------------------------------------------------------------
   LA PROGRESSIVITÉ DE LA TYPOGRAPHIE
   ------------------------------------------------------------------
   Une hiérarchie ne tient pas par la taille seule : à deux crans d'écart, deux
   titres de tailles voisines se confondent si tout le reste est identique.
   Chaque niveau change DEUX choses — la taille et une autre — pour que l'œil
   distingue sans mesurer.

     h1  romaine, 24 px, graisse normale, interlettrage resserré
     h2  romaine, 19 px, appuyée
     h3  linéale, 15 px, forte           ← changement de FONTE, pas de taille
     étiquette  linéale, 12 px, forte, capitales espacées

   Le soulignement est réservé aux LIENS DANS LE TEXTE. Un titre souligné, un
   bouton souligné, une étiquette soulignée : trois façons de faire croire
   qu'on peut cliquer. */
h1 { letter-spacing: -.015em; text-wrap: balance; }
h2 { letter-spacing: -.01em; text-wrap: balance; }
h3 { font-family: var(--sans); font-size: var(--t-meta);
     font-weight: var(--g-forte); letter-spacing: 0; margin: var(--e5) 0 var(--e2); }


/* ==================================================================
   LES MAISONS DE FAMILLE — LA PHOTO D'ABORD
   ==================================================================
   Une maison de famille se cherche des yeux avant de se lire. La fiche
   ouvrait sur une rubrique, un titre, un chapô, et la photo venait en vignette
   de quinze rem au milieu du texte — c'est-à-dire à la place d'une illustration
   d'article, alors qu'elle EST le sujet.

   UN AVERTISSEMENT qui commande toutes les valeurs ci-dessous : `Derivee` rend
   toujours un CARRÉ (`thumbnail_image(taille, height: taille, crop: :centre)`).
   Un cadre plus large que 1:1 recoupe donc l'image une seconde fois, et l'on
   perd le haut et le bas d'une façade. Les proportions restent proches du
   carré pour cette raison, et non par goût. */
.maison-heros { display: block; margin: 0 0 var(--e5);
  border-radius: var(--r-moyen); overflow: hidden;
  text-decoration: none; color: inherit; }
.maison-heros__image { display: block; width: 100%;
  aspect-ratio: 4 / 3; object-fit: cover; background: var(--filet-clair); }
@media (min-width: 40rem) { .maison-heros__image { aspect-ratio: 3 / 2; } }

/* Une maison sans photo : un filet EN POINTILLÉ et aucun aplat. Un rectangle
   gris plein se lit comme une image qui n'a pas fini de charger — on attend, on
   recharge, et l'on conclut que le produit est cassé. Le pointillé dit « il n'y
   a rien ici », ce qui est vrai et se répare. */
.maison-heros--manquante { display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--e2);
  aspect-ratio: 4 / 3; padding: var(--e5); text-align: center;
  color: var(--encre-douce);
  border: 1px dashed var(--filet); border-radius: var(--r-moyen);
  transition: border-color var(--duree), color var(--duree); }
@media (min-width: 40rem) { .maison-heros--manquante { aspect-ratio: 3 / 2; } }
a.maison-heros--manquante:hover { border-color: var(--encre-douce); color: var(--encre); }
.maison-heros__manque { display: block; font-family: var(--serif);
  font-size: var(--t-sous-titre); }
.maison-heros__geste { display: block; font-family: var(--sans);
  font-size: var(--t-meta); font-weight: var(--g-appuyee); color: var(--encre); }

/* Le registre : des cartes, pas une liste de texte. */
.maisons-cartes { display: grid; gap: var(--e4);
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  margin: 0 0 var(--e6); }

/* La carte ENTIÈRE est le lien : une zone cliquable de la taille d'un doigt
   vaut mieux qu'un titre de dix-huit pixels au milieu d'une carte inerte. */
.maison-carte { display: flex; flex-direction: column;
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  overflow: hidden; background: var(--surface);
  text-decoration: none; color: var(--encre);
  transition: border-color var(--duree); }
/* Au survol, le FILET seul se renforce. Ni ombre portée, ni translation : une
   carte qui se soulève au passage de la souris est le vocabulaire d'un tableau
   de bord, pas d'un registre de famille. */
.maison-carte:hover { border-color: var(--encre-douce); }
.maison-carte__image { display: block; width: 100%;
  aspect-ratio: 4 / 3; object-fit: cover; background: var(--filet-clair); }
.maison-carte__sans-photo { display: flex; align-items: center;
  justify-content: center; aspect-ratio: 4 / 3; padding: var(--e3);
  text-align: center; font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce);
  border-bottom: 1px dashed var(--filet); }
.maison-carte__corps { display: flex; flex-direction: column; gap: var(--e1);
  padding: var(--e3) var(--e4) var(--e4); }
.maison-carte__nom { display: block; font-family: var(--serif);
  font-size: var(--t-sous-titre); font-weight: var(--g-appuyee); line-height: 1.25; }
.maison-carte__detail { display: block; font-family: var(--sans);
  font-size: var(--t-meta); color: var(--encre-douce);
  overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical; }

/* Vendue : le souvenir reste, l'appartenance non. La désaturation dit cela
   sans un mot, et sans retirer la maison du registre — les retirer effacerait
   la moitié des souvenirs qu'on vient y chercher. */
.maison-carte--vendue .maison-carte__image { filter: grayscale(.65); }
.maison-carte--vendue { border-style: dashed; }

/* Sur l'écran de modification : ce qu'on a DÉJÀ, à un pas plus court, pour ne
   pas prendre le pas sur le formulaire. */
.maison-photos--vignettes { grid-template-columns: repeat(auto-fill, minmax(min(7rem, 100%), 1fr)); }


/* ==================================================================
   LA LISTE DES FILS — DEUX LIGNES, UN VISAGE, RIEN D'AUTRE
   ==================================================================
   Chaque ligne portait TROIS signaux pour dire une seule chose : un rail de
   couleur, un titre souligné en bleu, et une puce grise « Groupe de cousins »
   posée dessous. Trois lignes de hauteur pour un nom et un compteur — et le
   souligné bleu, qui est la marque d'un lien DANS un texte, appliqué au titre
   d'une ligne dont la totalité est déjà cliquable.

   Ce qui remplace : le visage. Une famille se reconnaît aux visages, et c'est
   la première chose qu'on vient voir en se connectant. */
.fils { list-style: none; margin: 0; padding: 0; }
.fils__ligne { border-bottom: 1px solid var(--filet-clair); }
.fils__ligne:last-child { border-bottom: 0; }

/* LA LIGNE ENTIÈRE est le lien : une cible de la taille d'un doigt vaut mieux
   qu'un titre de dix-huit pixels. Jamais de soulignement, `:hover` compris —
   c'est un fond qui répond, pas un trait. */
.fils__lien { display: flex; align-items: center; gap: var(--e3);
  padding: var(--e3) var(--e2); margin: 0 calc(-1 * var(--e2));
  color: var(--encre); text-decoration: none; border-radius: var(--r-moyen); }
.fils__lien:hover { background: var(--filet-clair); }

/* Les visages se chevauchent, comme partout où l'on montre un groupe : le
   liséré de la couleur du papier les redécoupe, sinon ils forment une tache. */
.fils__visages { flex: 0 0 auto; display: flex; }
.fils__visages > * + * { margin-left: -18px; box-shadow: 0 0 0 2px var(--papier); }
/* La NATURE du fil se dit par l'anneau du médaillon, et non par une puce grise
   sous le titre : tiret pour le groupe posé par le produit, encre du lien pour
   celui qu'on a ouvert soi-même. Reprise exacte du vocabulaire des étiquettes
   qu'elle remplace. */
.fils__visages--famille > :first-child { box-shadow: 0 0 0 2px var(--papier),
  0 0 0 3px var(--filet-fort, var(--filet)); }
.fils__visages--mien > :first-child { box-shadow: 0 0 0 2px var(--papier),
  0 0 0 3px var(--lien); }

.fils__texte { flex: 1; min-width: 0; display: flex; flex-direction: column;
  gap: var(--e0); }
.fils__tete { display: flex; align-items: baseline; gap: var(--e2); }
.fils__nom { flex: 1; min-width: 0; font-family: var(--sans);
  font-size: var(--t-corps); font-weight: var(--g-appuyee); color: var(--encre);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fils__heure { flex: 0 0 auto; font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce); font-variant-numeric: tabular-nums; }
.fils__bas { display: flex; align-items: center; gap: var(--e2); }
.fils__apercu { flex: 1; min-width: 0; font-family: var(--sans);
  font-size: var(--t-meta); color: var(--encre-douce);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* La pastille de non-lus prend la teinte des nouvelles, non l'encre : un
   compteur noir plein est l'objet le plus sombre de l'écran, pour le fait le
   plus ordinaire qui soit — quelqu'un a écrit. */
.compteur--non-lus { background: var(--pastille-nouvelle); color: #fff;
  border: 0; }

/* ==================================================================
   LE FIL — UN VISAGE PAR PRISE DE PAROLE
   ==================================================================
   La bulle était le `<li>` lui-même, et s'alignait par `align-self`. Elle est
   maintenant dans une RANGÉE, qui porte le côté, l'ancre et le rang — c'est
   elle qui fait la place du médaillon.

   Un seul médaillon par suite de messages du même auteur : répété sous six
   bulles d'affilée, il cesse d'être un visage pour devenir une texture. Mais
   la PLACE reste réservée sur les suivantes, sinon les bulles d'une même suite
   ne s'alignent plus sur la première. */
.fil__rang { display: flex; align-items: flex-end; gap: var(--e2); width: 100%; }
.fil__rang--mien { justify-content: flex-end; }
.fil__rang--suite { margin-top: calc(-1 * var(--e1)); }
.fil__visage { flex: 0 0 30px; width: 30px; align-self: flex-end; }
.fil__remontee-liste { list-style: none; margin: 0; padding: 0; width: 100%;
  display: flex; flex-direction: column; gap: var(--e0); }
.fil-entete__visages { flex: 0 0 auto; display: flex; }
.fil-entete__visages > * + * { margin-left: -14px;
  box-shadow: 0 0 0 2px var(--surface); }


/* ==================================================================
   L'ANNUAIRE — UNE GRILLE DE VISAGES, PAS UNE LISTE DE NOMS
   ==================================================================
   « Les gens aimeront voir leurs proches en se connectant. » Un annuaire de
   famille qui rend deux cents lignes de texte demande de LIRE pour reconnaître,
   là où un visage se reconnaît sans effort. C'est aussi ce qui distingue un
   carnet de famille d'un fichier.

   `auto-fit` et non `auto-fill` : à trois personnes, `auto-fill` laisserait
   cinq colonnes vides à droite — une famille qui débute aurait l'air d'une
   famille amputée. */
/* Deux colonnes sur téléphone, et non trois. À trois, les noms se cassent en
   deux lignes, les cartes prennent des hauteurs inégales, et le sous-titre —
   « fille de Grégoire et Marie-Alix » — est tronqué juste sur le SECOND parent,
   c'est-à-dire précisément sur ce qui désambiguïse deux homonymes. */
.annuaire__grille { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
  gap: var(--e3); }
/* Hauteur commune : sans elle, une vignette à sous-titre long décale toute sa
   rangée et la grille perd son rythme. */
.annuaire__visage { justify-content: flex-start; min-height: 11.5rem; }
.annuaire__visage { display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: var(--e0); min-height: var(--cible);
  padding: var(--e2); border-radius: var(--r-moyen);
  color: var(--encre); text-decoration: none; }
.annuaire__visage:hover { background: var(--filet-clair); }
.annuaire__visage:hover .annuaire__nom { text-decoration: underline; }
/* Une fiche confidentielle garde sa VIGNETTE — donc sa place dans la famille —
   et ne dit rien d'autre. La retirer laisserait un trou que chacun chercherait
   à combler en demandant autour de lui, ce qui est le contraire du but. */
.annuaire__visage--anonyme { color: var(--encre-douce); }
.annuaire__visage--anonyme .annuaire__nom { font-family: var(--sans);
  font-size: var(--t-meta); font-style: italic; font-weight: var(--g-normale); }
.annuaire__nee { display: block; font-family: var(--serif); font-style: italic;
  font-size: var(--t-meta); color: var(--encre-douce); }
/* Bornée à deux lignes : « fille d'Anselme et Guillemette de Kervenec » en fait
   quatre dans une vignette étroite, et la grille perd son rythme. */
.annuaire__dessous { display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
  font-family: var(--sans); font-size: var(--t-meta); color: var(--encre-douce); }

/* Un filet fin autour du médaillon : sans lui, une photo pâle — un bébé sur un
   drap blanc, une robe de mariée — se fond dans le papier et la vignette a
   l'air vide. */
.portrait--vignette, .portrait--dominant, .portrait--noeud {
  box-shadow: 0 0 0 1px var(--filet); }
.portrait--noeud { margin-bottom: var(--e0); }

/* La fiche : le portrait domine l'en-tête. Sur téléphone il passe AU-DESSUS du
   nom plutôt qu'à côté — à 128 px, un nom long à côté de lui se coupe en trois
   lignes de deux mots. */
.fiche-entete--dominante { display: flex; align-items: center; gap: var(--e4); }
.fiche-entete--dominante > * { min-width: 0; }
.fiche-entete--dominante .portrait--dominant { flex: 0 0 auto; }
@media (max-width: 30rem) {
  .fiche-entete--dominante { flex-direction: column; text-align: center; }
}

/* ==================================================================
   UNE PERSONNE SUR UNE LIGNE — ENTOURAGE, UNION, ARBRE ÉTENDU
   ==================================================================
   La même forme partout où l'on nomme quelqu'un au fil du texte. Écrite une
   fois : c'est ce qui manquait, et chaque écran avait le sien. */
.personne-en-ligne { display: inline-flex; align-items: center; gap: var(--e1);
  vertical-align: middle; color: var(--encre); text-decoration: none; }
a.personne-en-ligne:hover .personne-en-ligne__nom { text-decoration: underline; }
.personne-en-ligne__corps { display: inline-flex; flex-wrap: wrap;
  align-items: baseline; gap: 0 var(--e1); min-width: 0; }
.personne-en-ligne__nom { font-weight: var(--g-appuyee); }
.personne-en-ligne--anonyme { color: var(--encre-douce); }
.personne-en-ligne--anonyme .personne-en-ligne__nom {
  font-family: var(--sans); font-size: var(--t-meta);
  font-style: italic; font-weight: var(--g-normale); }

/* La case de l'arbre porte son visage en tête. `width: 100%` sur le bas, sans
   quoi la rangée dates/fiche perd son `space-between` dès que la case devient
   une colonne centrée. */
.noeud--visage { align-items: center; text-align: center; }
.noeud--visage .noeud__bas { width: 100%; }

/* Les résultats de recherche gardent la liste : on y cherche un NOM, et une
   grille de visages répondrait à côté de la question. Le sélecteur portait sur
   `.annuaire`, qui n'existe plus depuis que la liste est devenue une grille. */
.annuaire__ligne .liste__corps { display: flex; flex-direction: column;
  gap: var(--e0); }


/* ==================================================================
   LE SOULIGNÉ EST RENDU AUX LIENS DANS LE TEXTE
   ==================================================================
   Sur trois écrans de suite — archives, événements, courrier — chaque ligne de
   liste portait son titre en BLEU SOULIGNÉ. Or ces lignes sont déjà cliquables
   sur toute leur surface : le souligné n'ajoutait rien qu'on ne sût, et il
   transformait une liste de faire-part en une page de résultats de moteur de
   recherche. Sur fond sombre, le bleu pâle par-dessus le gris devient même la
   chose la plus criarde de l'écran.

   La règle : un lien SE SOULIGNE quand il est noyé dans une phrase, parce que
   rien d'autre ne le signale. Il ne se souligne PAS quand il est le titre
   d'une ligne, d'une carte ou d'un cartouche — la forme le dit déjà. */
.liste__corps > a:first-child,
.liste__ligne > a:first-child,
.entree__titre a,
.archives__acte a,
.evenement__titre a {
  color: var(--encre); text-decoration: none; font-weight: var(--g-appuyee);
}
.liste__corps > a:first-child:hover,
.liste__ligne > a:first-child:hover,
.entree__titre a:hover,
.archives__acte a:hover,
.evenement__titre a:hover { text-decoration: underline; }

/* ------------------------------------------------------------------
   LE MÉDAILLON NE SE POSE PAS SUR LE GESTE
   ------------------------------------------------------------------
   Il recouvrait « Mon arbre » et « Déposer un acte » : deux objets flottants
   au même coin, dont l'un est fixe. L'en-tête garde donc sa place libre. */
.entete { padding-right: 3rem; }
@media (min-width: 60rem) { .entete { padding-right: 0; } }


/* Une référence d'archive, repliée : présente pour le généalogiste qui la
   cherche, invisible pour la famille qui lit. */
.reference summary { font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce); cursor: pointer; }
.reference p { margin: var(--e1) 0 0; font-family: var(--mono, var(--sans));
  font-size: var(--t-micro); }


/* ==================================================================
   CE QUI DÉFILE LATÉRALEMENT LE DIT
   ==================================================================
   Le bandeau des nouvelles et la rangée des registres se coupaient net au bord
   de la colonne — une carte tranchée en plein mot, une pastille à moitié
   visible — sans flèche, sans point de position, sans estompage. Sur un
   ordinateur, où l'on ne fait pas glisser du doigt par réflexe, cela se lit
   comme un défaut d'affichage et non comme une invitation.

   L'estompage à droite dit « il y en a d'autres » sans rien ajouter à l'écran,
   et il disparaît de lui-même quand on arrive au bout. */
.rangee-rubriques {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2rem), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 2rem), transparent);
  padding-right: 2rem; }

/* Sur un pointeur fin — donc un ordinateur — la barre de défilement se MONTRE.
   La masquer est juste sur un écran tactile, où le doigt suffit ; sur un
   ordinateur, elle est le seul indice qu'il y a quelque chose à faire. */
@media (pointer: fine) {
  .bandeau, .rangee-rubriques { scrollbar-width: thin;
    scrollbar-color: var(--filet) transparent; }
  .bandeau::-webkit-scrollbar, .rangee-rubriques::-webkit-scrollbar {
    display: block; height: 8px; }
  .bandeau::-webkit-scrollbar-thumb, .rangee-rubriques::-webkit-scrollbar-thumb {
    background: var(--filet); border-radius: var(--r-rond); }
  .bandeau::-webkit-scrollbar-track, .rangee-rubriques::-webkit-scrollbar-track {
    background: transparent; }
}


/* ==================================================================
   UN GESTE NE SE COLLE PAS À CE QU'IL VALIDE
   ==================================================================
   Le grief : « les boutons sont collés » sous les conteneurs. La cause est une
   gouttière uniforme — `.formulaire { gap: .75rem }` — appliquée aussi bien
   entre deux champs, où douze pixels vont très bien, qu'entre le dernier champ
   et le bouton qui CONCLUT le formulaire.

   Or ce ne sont pas deux choses du même rang. Le bouton ferme la saisie : il
   lui faut le blanc qui dit « on a fini de remplir, voici ce qu'on en fait ».
   Sans lui, on l'active en croyant remplir un champ de plus — et c'est
   exactement l'erreur qu'un formulaire ne doit pas rendre possible.

   `:last-child` plutôt qu'une classe : le geste de conclusion est toujours le
   dernier, et aucune vue n'a besoin de s'en souvenir. */
.formulaire > input[type="submit"]:last-child,
.formulaire > button[type="submit"]:last-child,
.formulaire > .actions:last-child,
.zone-edition > input[type="submit"]:last-child,
.deposer > input[type="submit"]:last-child {
  margin-top: var(--e4);
}

/* Et le conteneur lui-même respire sous ce qui le précède : un `fieldset` posé
   contre le champ d'au-dessus fait croire qu'il en fait partie. */
.bloc + .bloc, .bloc + label, fieldset + label { margin-top: var(--e3); }

/* Le formulaire ne touche pas le titre qui l'annonce. */
.section-titre + .formulaire, h2 + .formulaire { margin-top: var(--e4); }


/* Le compte du filtre : muet tant qu'on n'a rien tapé, puis « 3 personnes sur
   96 ». Il répond à la seule question qu'on se pose en filtrant — ai-je trop
   restreint ? — et `aria-live` la fait dire au lecteur d'écran. */
.recherche__compte { margin: calc(-1 * var(--e2)) 0 var(--e4); min-height: 1.2em; }


/* ==================================================================
   CE QUI VOUS ATTEND — DES ACTES, PAS DU COURRIER
   ==================================================================
   L'écran portait trois boutons de poids égal par élément, dont aucun n'était
   principal et dont le troisième retombait seul sur une deuxième ligne. Et le
   contenu humain — « Elle m'intéresse. », la seule phrase écrite par quelqu'un
   — était en gris, plus petit que le titre système qui l'entourait.

   Une ligne, un acte, un chemin. Le liséré de gauche dit de quelle nature il
   est, et c'est le seul ornement : ces lignes sont peu nombreuses par
   construction, elles n'ont pas besoin de se distinguer entre elles à dix
   mètres. */
.attente { list-style: none; margin: var(--e5) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--e2); }
.attente__ligne { border: 1px solid var(--filet);
  border-left: 3px solid var(--encre-douce);
  border-radius: var(--r-moyen); background: var(--surface); }

/* La nature par la couleur du liséré. L'autorité parentale porte la teinte des
   nouvelles : c'est l'acte le plus grave de la liste — il engage la
   responsabilité de qui le pose. */
.attente__ligne--corroboration { border-left-color: var(--pastille-nouvelle); }
.attente__ligne--mandat        { border-left-color: var(--pastille-residence); }
.attente__ligne--moderation    { border-left-color: var(--pastille-annonce); }
.attente__ligne--mise_en_relation { border-left-color: var(--pastille-maison); }
/* Une alliance engage DEUX familles : elle prend le vert des maisons, le seul
   ton de la palette qui ne soit ni l'alerte ni la nouveauté — c'est une
   décision qu'on prend posément, pas une chose qui presse. */
.attente__ligne--alliance      { border-left-color: var(--pastille-maison); }

/* TOUTE la ligne est le chemin — et SANS SOULIGNEMENT. Un lien se souligne
   quand il est noyé dans une phrase, parce que rien d'autre ne le signale ;
   ici la forme le dit déjà, et le trait ne faisait qu'alourdir. */
.attente__lien { display: flex; align-items: flex-start; gap: var(--e3);
  padding: var(--e4); color: var(--encre); text-decoration: none;
  border-radius: var(--r-moyen); }
.attente__lien:hover { background: var(--filet-clair); }
.attente__corps { flex: 1; min-width: 0; display: flex;
  flex-direction: column; gap: var(--e0); }
.attente__genre { font-family: var(--sans); font-size: var(--t-micro);
  font-weight: var(--g-forte); color: var(--encre-douce);
  text-transform: uppercase; letter-spacing: .06em; }
.attente__titre { font-family: var(--serif); font-size: var(--t-sous-titre);
  line-height: 1.3; }
/* L'encre NORMALE : c'est la phrase qu'une personne a écrite, et elle était
   plus pâle que l'étiquette système au-dessus d'elle. */
.attente__detail { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre); }
.attente__quand { flex: 0 0 auto; font-family: var(--sans);
  font-size: var(--t-micro); color: var(--encre-douce); }

.attente__ligne--classee { border-left-color: var(--filet);
  background: none; opacity: .7; }
.attente__ligne--classee .attente__corps { padding: var(--e3) var(--e4); }
.attente__classees { margin: var(--e7) 0 0; }
.attente__classees summary { font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); cursor: pointer; }


/* ==================================================================
   « À LA UNE » EST UN PORTAIL, PAS UNE LECTURE
   ==================================================================
   La colonne du produit est bornée à 72 caractères — la mesure d'une ligne
   qu'on relit sans se perdre — et cette borne vaut pour tout ce qui se LIT.
   Elle ne vaut pas pour un écran qui rassemble : sur 1600 px, « À la une »
   tenait dans 640 px avec mille pixels de blanc à droite, et l'on croyait
   regarder une application de téléphone égarée sur un ordinateur.

   Le fil garde sa mesure ; les registres occupent le blanc. En dessous de
   75 rem, la grille redevient une colonne et les registres reprennent leur
   place en tête — c'est l'ordre du document, on n'a rien à défaire.

   `:has(> .portail)` élargit la page POUR CET ÉCRAN SEULEMENT : élargir
   `.page` en général emporterait le carnet, les articles et les fiches, qui
   ont besoin de leur mesure. */
@media (min-width: 75rem) {
  .page:has(> .portail) { max-width: 68rem; }

  .portail { display: grid; gap: 0 var(--e7); align-items: start;
    grid-template-columns: minmax(0, 40rem) minmax(0, 18rem); }
  /* L'ordre du DOM met les registres en premier — c'est ce qu'on veut sur un
     téléphone. `order` les renvoie à droite ici, sans dupliquer le balisage. */
  .portail__fil  { order: 1; min-width: 0; }
  .portail__cote { order: 2; min-width: 0;
    position: sticky; top: var(--e5); }

  /* Dans la colonne de côté, les registres s'empilent : ils y ont la hauteur
     et non la largeur, et une rangée qui défile latéralement dans 18 rem
     n'aurait montré que deux entrées sur six. */
  .rangee-rubriques--portail { display: flex; flex-direction: column;
    gap: var(--e1); overflow: visible;
    -webkit-mask-image: none; mask-image: none; padding-right: 0; }
  .rangee-rubriques--portail .destination-rubrique { width: 100%; }
}


/* ==================================================================
   LES TROUVAILLES DU BALAYAGE DES 83 GABARITS
   ==================================================================
   Trois classes étaient posées dans les vues sans qu'aucune règle ne les
   habille — le HTML disait une intention que la feuille n'honorait pas. */

/* Les gestes d'un acte en attente (Courrier) : le lien et le bouton « c'est
   fait » se serraient sans gouttière. */
.attente__gestes { display: flex; align-items: center; gap: var(--e3);
  flex-wrap: wrap; margin-top: var(--e1); }

/* Les circonstances d'un faire-part : le texte libre sous l'annonce, en
   romaine douce — c'est un récit, pas une méta-donnée. */
.faire-part__circonstances { font-family: var(--serif); color: var(--encre-douce);
  max-width: 34rem; margin: var(--e3) auto 0; line-height: 1.6; }

/* Les annonces en fin de section d'édition : séparées du fil par un filet
   clair, jamais par un trait appuyé — c'est la page des annonces d'un journal,
   pas une rupture. */
.section-edition__annonces { margin-top: var(--e5); padding-top: var(--e4);
  border-top: 1px solid var(--filet-clair); }

/* Le petit pied de liens (mentions, conditions, fermeture) : la marge vivait
   en `style=` dans deux gabarits, c'est-à-dire nulle part. */
.pied-de-liens { margin-top: var(--e6); }


/* ------------------------------------------------------------------
   L'ORIGINAL DU FAIRE-PART
   ------------------------------------------------------------------
   Le scan du carton se montre comme une pièce posée sur la table : pleine
   largeur, une ombre posée, pas de cadre — le carton A SON bord. Le clic
   ouvre la fiche de la photo, où vivent le retrait et l'identification. */
.faire-part-original { display: block; margin: 0 0 var(--e4); }
.faire-part-original img { display: block; width: 100%; height: auto;
  border-radius: var(--r-petit); box-shadow: var(--ombre-posee); }

/* L'avis du médaillon : le HEIC d'iPhone ne s'affiche pas dans le navigateur,
   et le rond restait vide sans un mot. Ton d'information, pas d'alerte — rien
   n'a échoué, on explique seulement ce qu'on ne peut pas montrer. */
.medaillon__avis { margin: 0 0 var(--e2); padding: var(--e2) var(--e3);
  border-left: 3px solid var(--geste); border-radius: var(--r-petit);
  background: var(--filet-clair); font-size: var(--t-meta);
  color: var(--encre); }

/* Le motif d'un refus, CONTRE le champ qu'il concerne. Le résumé en tête de
   page ne suffit pas : sur un formulaire long, le bouton est en bas et le
   motif hors du champ de vision — on croit alors que le bouton ne fait rien. */
.erreur-de-champ { margin: var(--e1) 0 0; padding: var(--e1) var(--e2);
  border-left: 3px solid var(--alerte); border-radius: var(--r-petit);
  background: var(--filet-clair); font-size: var(--t-meta); color: var(--encre); }

/* ==================================================================
   LE RÉGIME D'UN FIL — COU-VIE-008
   ==================================================================
   Replié par défaut : c'est un réglage qu'on ouvre le jour où le fil vous a
   réveillé une fois de trop, pas une chose à lire en arrivant. Déplié, il tient
   la largeur du fil sans se distinguer du reste du pied. */
.reglage-du-fil { margin: var(--e3) 0 0; }
.reglage-du-fil > summary {
  cursor: pointer; font-family: var(--sans); font-size: 14px;
  color: var(--encre-douce); padding: var(--e2) 0;
}
.reglage-du-fil .formulaire { margin-top: var(--e2); }
/* Deux lignes par choix : le libellé dit ce qui arrive, la note dit ce que ça
   coûte. Sans la note, « une seule alerte par fil » ne se distingue pas de
   « chaque message » pour qui ne connaît pas déjà la réponse. */
.case--regime { align-items: flex-start; }
.case--regime > span { display: flex; flex-direction: column; gap: 2px; }

/* ==================================================================
   L'ARBRE PREND LA LARGEUR — MAIS PAS LES TEXTES QUI L'ENTOURENT
   ==================================================================
   Sur un écran de 2400 px, l'arbre tenait dans les 46 rem de la colonne de
   lecture pendant que 1 300 px restaient vides à droite. Quatre enfants
   débordaient déjà et se lisaient en faisant défiler à l'horizontale — sur un
   ORDINATEUR, c'est-à-dire précisément là où la place ne manque pas.

   Un arbre n'est pas un texte. Sa contrainte est la LARGEUR d'une fratrie, pas
   la mesure de 72 signes qui gouverne tout le reste du produit : les deux ne se
   règlent pas par le même nombre. On libère donc le dessin, et l'on garde la
   mesure sur ce qui se lit — le chapô, les formulaires de greffe, la correction
   d'un lien. Sans cette seconde moitié, « Ajouter un proche » s'étalerait sur
   deux mille pixels, et l'on aurait remplacé un défaut par un pire.

   `:has(.arbre__titre)` plutôt qu'une classe posée sur `<main>` : le gabarit
   n'a pas de mécanisme pour qu'une vue le fasse, et en inventer un pour un
   écran serait payer cher une règle qui tient en deux sélecteurs. */
@media (min-width: 60rem) {
  .avec-barre .page:has(.arbre__titre) { max-width: none; padding-right: var(--e7); }
  .avec-barre .page:has(.arbre__titre) > :not(.menage-vue):not(.arbre-etendu):not(hr) {
    max-width: 46rem;
  }
  /* Les cases peuvent respirer : à 13 rem, « Jean-Baptiste de Fromont de
     Bouaille » se coupait en quatre lignes. Le plafond ne vaut que là où il y a
     la place — sur téléphone, il reste ce qu'il était. */
  .arbre-dessine > .foyer > * { max-width: 15rem; }
  /* ON NE FAIT PAS PASSER LE RANG À LA LIGNE, et c'est délibéré. `flex-wrap`
     semble la réponse évidente — il l'était dans la première version de cette
     règle — mais les traits de filiation sont des pseudo-éléments qui joignent
     un enfant au suivant SUR SA LIGNE : repliés, ils dessineraient un second
     rail sous une fratrie qui n'a qu'un tronc, et l'arbre mentirait sur la
     parenté au lieu de simplement défiler. Le rang garde donc son défilement,
     et c'est la LARGEUR gagnée qui fait qu'on n'a plus à s'en servir. */
}

/* Au-delà de 1440 px, la colonne de l'arbre était RECENTRÉE par la règle
   générale des écrans larges — qui pose une gouttière calculée sur 46 rem de
   contenu. L'arbre n'en fait plus 46 : la gouttière le repoussait donc vers la
   droite et lui reprenait ce qu'on venait de lui donner. */
@media (min-width: 90rem) {
  .avec-barre .page:has(.arbre__titre) { margin-inline: 0; }
}

/* ==================================================================
   L'ENVOI D'UN FICHIER LOURD — COU-VIE-010
   ==================================================================
   Replié : c'est un chemin qui sert une fois par été, et le mettre devant les
   yeux de tous ferait hésiter sur le geste ordinaire — déposer un lot de
   photos, qui est ce qu'on fait quatre-vingt-dix-neuf fois sur cent. */
.depot-lourd { margin: var(--e4) 0; }
.depot-lourd > summary {
  /* 39 px de haut, sous les 44 exigés : un repli qu'on ouvre au doigt, sur
     l'écran où l'on dépose une vidéo depuis son téléphone. La sonde des cibles
     tactiles ne le voyait pas — il vivait sous la ligne de flottaison, et les
     deux paragraphes qui expliquent enfin l'album l'ont ramené dans la fenêtre.
     C'est le premier vrai défaut qu'elle attrape depuis qu'elle sait mesurer. */
  cursor: pointer; font-family: var(--sans); font-size: 15px;
  color: var(--encre-douce); padding: var(--e2) 0;
  display: flex; align-items: center; min-height: var(--cible);
}
.depot-lourd > div { margin-top: var(--e3); display: grid; gap: var(--e3); }
/* `progress` natif, à la largeur de la zone : il porte son propre rôle ARIA et
   son propre libellé vocalisé, ce qu'une barre dessinée à la main n'a jamais.
   Masqué tant qu'il vaut zéro — une barre vide avant tout envoi annonce une
   attente qui n'a pas commencé. */
.depot-lourd progress { width: 100%; height: 8px; }
.depot-lourd progress[value="0"] { visibility: hidden; }

/* ==================================================================
   LA MARQUE D'UNE VIDÉO DANS LA GRILLE — COU-VIE-010
   ==================================================================
   L'affiche d'une vidéo est une image fixe : rien ne la distingue d'une
   photographie, et l'on clique pour découvrir que c'est autre chose. Le repère
   porte le triangle ET la durée — c'est la durée qu'on regarde avant de décider
   d'ouvrir, plus encore que la nature du fichier.

   En bas à gauche : le haut de la vignette porte déjà la case de sélection en
   mode lot, et deux objets au même coin se recouvrent. */
.album__video {
  position: absolute; left: var(--e2); bottom: var(--e2);
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px var(--e2); border-radius: var(--r-moyen);
  /* Un fond propre, et non une transparence : l'affiche peut être claire ou
     sombre, et un repère qui disparaît sur les vidéos de plein jour ne sert
     qu'aux autres. */
  background: rgba(20, 18, 15, .78); color: #fff;
  font-family: var(--sans); font-size: 12px; font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.album__video svg { width: 12px; height: 12px; }

/* Le lecteur prend la place de l'image, aux mêmes règles — sans quoi une vidéo
   verticale d'iPhone déborderait de la fiche. */
.photo__image[controls] { max-width: 100%; height: auto; background: #000; }
.photo__preparation { margin-top: var(--e3); }

/* ==================================================================
   DÉSIGNER LA PHOTO DE TÊTE D'UNE MAISON
   ==================================================================
   Le geste vit SOUS la vue qu'il concerne : c'est en regardant les photos qu'on
   se dit « c'est celle-là qu'il faut montrer ». Discret — on ne le pose qu'une
   fois par maison, et il ne doit pas disputer l'attention à la photo. */
.maison-photos__vue { margin: 0; display: grid; gap: var(--e1); }
.maison-photos__geste { margin: 0; justify-self: start; }

/* ==================================================================
   COU-ACC-002 — LA MATRICE SE GROUPE, ET LES SACREMENTS SE REPLIENT
   ==================================================================
   Quinze lignes de trois choix faisaient quarante-cinq décisions sur l'écran le
   plus important du produit en matière de confiance. Le risque n'est pas
   l'erreur, c'est l'ABANDON — on referme sans rien régler, et l'on garde les
   valeurs par défaut, ce qui ramène au défaut que cet écran répare.

   `fieldset` et `legend` NATIFS, et non deux `div` : ils portent le groupement
   dans l'arbre d'accessibilité, où un lecteur d'écran annonce « Mes sacrements,
   groupe » avant chaque option. Un titre dessiné à la main ne dit rien à qui
   n'y voit pas — et c'est précisément le public que ce correctif vise. */
.matrice-groupe { border: 0; padding: 0; margin: 0 0 var(--e6); }
.matrice-groupe > legend {
  font-family: var(--sans); font-size: var(--t-meta); font-weight: var(--g-forte);
  letter-spacing: .04em; text-transform: uppercase; color: var(--encre-douce);
  padding: 0; margin-bottom: var(--e2);
}
.matrice-groupe > .secondaire { margin: 0 0 var(--e3); }

/* Le détail s'ouvre sous le réglage commun, décalé : on voit d'un coup d'œil
   qu'il en dépend, et qu'on n'a pas à s'en occuper si le commun suffit. */
.matrice-detail { margin-top: var(--e3); }
.matrice-detail > summary {
  cursor: pointer; font-family: var(--sans); font-size: 15px;
  color: var(--encre-douce); min-height: var(--cible);
  display: flex; align-items: center;
}
.matrice-detail > .matrice { border-left: 2px solid var(--filet-clair);
  padding-left: var(--e4); }

/* ==================================================================
   COU-VIE-002 — LE NOM DE LA FAMILLE, ÉCRIT QUELQUE PART
   ==================================================================
   Le texte intégral de l'écran d'accueil ne le contenait NULLE PART, sur un
   produit dont c'est l'objet même. Il paraît maintenant dans la bande haute sur
   téléphone, et en tête du rail sur écran large — les deux seuls endroits
   permanents.

   En `--serif` : c'est une identité, pas une étiquette de navigation. C'est la
   même fonte que le nom d'une personne sur sa fiche, et pour la même raison.

   `justify-content` passe à `space-between` : la bande ne portait que le
   médaillon, poussé à droite. Le nom prend la gauche, et rien ne bouge quand il
   n'y en a pas — un membre sans famille voit la bande d'avant. */
.bande-haute { justify-content: space-between; gap: var(--e2); }
.bande-haute__famille {
  font-family: var(--serif); font-size: var(--t-corps); color: var(--encre);
  text-decoration: none; min-height: var(--cible);
  display: inline-flex; align-items: center; padding: 0 var(--e2);
  /* Un nom long ne pousse pas le médaillon hors de l'écran : il se coupe. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bande-haute__famille:hover { text-decoration: underline; }

/* ------------------------------------------------------------------
   UN COMMENTAIRE QUI AFFIRME UN FAIT SUR AILLEURS DEVIENT FAUX TOUT SEUL
   ------------------------------------------------------------------
   Cette règle était `display: none` au-delà de 60 rem, et sa raison était
   écrite : « le rail porte déjà le nom ». Elle est FAUSSE. Le rail ne pose sa
   marque `.onglets__marque` que `unless mes_familles.any?` — c'est-à-dire
   uniquement quand on n'appartient à AUCUN cousinage, où c'est un titre et non
   un choix. Le gabarit le dit d'ailleurs à l'endroit exact, et les deux
   commentaires se contredisaient depuis des semaines.

   Conséquence mesurée le 30 août 2026 : sur un ordinateur, un membre de
   plusieurs cousinages n'avait AUCUN moyen de voir lequel était retenu ni d'en
   changer — sauf sur les quatre écrans qui portent leur propre sélecteur, ou en
   passant par « Mes cousinages ». Sur les huit écrans qui n'en montrent qu'un
   — les rôles, les alliances, les invitations, la visibilité, les fusions… —
   on subissait le choix sans le voir.

   C'est la leçon déjà écrite pour la feuille de Leaflet, au mot près : « un
   commentaire qui affirme "ceci ne sert qu'ici" devient faux tout seul ; quand
   une règle porte sur un ensemble d'écrans, c'est un ESSAI qui doit la tenir,
   pas une phrase ». Il y en a un désormais.

   Sur écran large, la bande vit au pied du rail : le cousinage se pose donc
   AU-DESSUS du médaillon, en colonne, et non à côté de lui dans 272 pixels. */
@media (min-width: 60rem) {
  .bande-haute { flex-direction: column; align-items: stretch; gap: var(--e1); }
  .bande-haute__famille { justify-content: flex-start; padding: 0 var(--e3);
    font-size: var(--t-meta); color: var(--encre-douce); min-height: 40px; }
  .bande-haute__famille:hover { color: var(--encre); text-decoration: none;
    background: var(--filet-clair); border-radius: var(--r-moyen); }
}
/* La marque du rail est devenue un LIEN — elle en garde l'allure d'un titre. */
a.onglets__marque { text-decoration: none; }
a.onglets__marque:hover { text-decoration: underline; }

/* ==================================================================
   COU-CRA-005 — LE PICTOGRAMME D'UN GROUPE SANS VISAGE
   ==================================================================
   Un groupe automatique dont les membres ne sont pas matérialisés n'avait à
   montrer que soi-même : quatre fils portaient la même photo. Le pictogramme
   occupe exactement la place d'un portrait de 44 px — sinon la liste se
   décalerait d'une ligne à l'autre selon que le fil a des visages ou non. */
.fils__groupe {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: var(--r-rond);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--filet-clair); color: var(--encre-douce);
}
.fils__groupe svg { width: 22px; height: 22px; }

/* ==================================================================
   COU-CRA-008 — DE QUELLE FAMILLE VIENT CETTE PERSONNE
   ==================================================================
   L'annuaire mêlait quatre membres de la famille propre et treize d'une famille
   ALLIÉE, sans le moindre marqueur. Les alliés en sont sortis le 22 août 2026 —
   « ça fera trop », et c'est l'ARBRE qui relie les familles alliées, en cliquant
   la pièce rapportée.

   La mention reste, et dit autre chose : DE LAQUELLE DE MES FAMILLES vient cette
   fiche. Elle n'a de sens qu'à partir de deux, et elle en porte souvent deux à
   la fois — les cousinages s'emboîtent, celui des grands-parents étant contenu
   dans celui des arrière-grands-parents. */
.annuaire__familles {
  display: block; font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); font-style: italic;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ==================================================================
   COU-VIE-001 — CE QUE LA FAMILLE A FAIT, QUAND L'ÉDITION N'EST PAS PARUE
   ==================================================================
   L'écran ne portait qu'un constat gris, suivi de six cent vingt-six pixels de
   rien : les trois quarts de la hauteur d'un téléphone, sans un geste offert.
   On ouvre l'application, on ne voit rien, on referme.

   Chaque fait porte SON lien : le geste est à côté de ce qui le motive, et non
   relégué en bas de page où l'on ne descend pas. */
.vie-recente { list-style: none; margin: var(--e4) 0; padding: 0;
  display: grid; gap: var(--e2); }
.vie-recente__fait {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e3); flex-wrap: wrap;
  padding: var(--e3); border-radius: var(--r-moyen);
  background: var(--filet-clair);
}
.vie-recente__fait > span { font-family: var(--serif); }
/* Le geste principal respire : c'est lui qui fait exister l'édition suivante. */
.vide__geste { margin: var(--e6) 0 0; }

/* Le nom de la famille sur une ligne de fil — seulement quand on en a
   plusieurs, et seulement hors filtre : sinon il répète ce qu'on vient de
   choisir. Discret, sous le titre, à la place où l'œil cherche le contexte. */
.fils__famille {
  display: block; font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Le nom de la famille à côté de son filet. Discret : c'est un repère, pas un
   titre — on le lit quand on le cherche, et il ne dispute rien au contenu. */
.origine__nom {
  font-family: var(--sans); font-size: var(--t-meta); color: var(--encre-douce);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* IL NE PREND PAS PLUS DE LA MOITIÉ, et il cède le premier : c'est une
     étiquette, pas le contenu. Sans ce plafond, un nom de famille long
     repoussait l'annonce dans une colonne de la largeur d'un pouce. */
  flex: 0 1 auto; max-width: min(50%, 16rem);
}

/* ------------------------------------------------------------------
   LA FAÇADE, DANS SON PROPRE FORMULAIRE
   ------------------------------------------------------------------
   Elle paraît AU-DESSUS de la zone de dépôt, et c'est ce qui répond à la
   question qu'on se pose en ouvrant : « y a-t-il déjà une photo, et laquelle ».
   L'intitulé du dépôt le disait déjà en mots — « Changer la photo » — mais il
   se lisait sur `@residences.first`, donc sur l'adresse d'à côté : on pouvait
   lire « Changer la photo » au-dessus d'une maison qui n'en avait aucune.

   `object-fit: cover` sur une hauteur bornée : une façade est plus large que
   haute, et une photo prise en portrait ne doit pas pousser le bouton
   d'enregistrement hors de l'écran. */
.vignette-facade {
  display: block;
  width: 100%;
  max-height: 12rem;
  object-fit: cover;
  border-radius: var(--r-petit);
  margin-bottom: var(--e2);
}

/* ------------------------------------------------------------------
   L'AVEU — CE QUE LE PRODUIT A DEVINÉ
   ------------------------------------------------------------------
   Il doit se voir sans crier. Un encart d'alerte ferait croire à une panne :
   ce n'en est pas une, c'est une incertitude, et la plupart des points devinés
   sont justes. Le filet à gauche et le point d'interrogation suffisent à
   distinguer la mention d'un texte ordinaire.

   Et il ne vaut QUE s'il est rare — d'où `point_devine` à false par défaut :
   marquer tous les points anciens comme devinés ferait douter de ce qui est
   juste, et l'on cesserait de le lire. */
.aveu {
  display: flex; gap: var(--e2); align-items: flex-start;
  border-left: 3px solid var(--pastille-annonce);
  padding: var(--e2) var(--e3); margin: var(--e2) 0;
  font-size: .9375rem; line-height: 1.5;
}
.aveu__marque {
  flex: 0 0 auto; width: 1.25rem; height: 1.25rem; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--pastille-annonce); color: var(--pastille-annonce);
  font-size: .75rem; font-weight: 700; line-height: 1;
}


/* ------------------------------------------------------------------
   ASSEZ LARGE POUR QU'UN NOM TIENNE SUR UNE LIGNE
   ------------------------------------------------------------------
   `min-width: 15rem` — 240 px — et « Cous. de G. & S. de F. de B. » se coupait
   en deux, alors que c'est déjà la forme la plus abrégée que le produit sache
   écrire. Un nom qui passe à la ligne dans un basculeur, c'est une ligne de
   liste qui ressemble à deux entrées : on compte trois cousinages là où il y en
   a deux.

   JB, le 30 août 2026 : « élargie pour éviter les passages à la ligne même si
   le nom du cousinage est long ». Le panneau prend donc la largeur qu'il faut,
   jusqu'au bord de l'écran moins une marge, et un nom qui déborderait quand
   même se coupe à l'ellipse plutôt que de se replier — le nom entier reste dans
   l'infobulle et dans `aria-label`. */
}
/* CE QUE LE CHOIX COMMANDE — la seule ligne du dépliant qui réponde à une
   question qu'on se pose vraiment : « à quoi sert cette liste ? ». Plus douce
   que l'état, parce qu'elle explique au lieu de constater. */
}
}
/* La famille regardée porte une marque QUI N'EST PAS QUE LA COULEUR : un
   contraste de couleur seul n'est pas perçu par tout le monde, et c'est la
   seule information de cette liste. */
}

/* Les gestes qui SORTENT une photo de l'album de la famille sont séparés de
   ceux qui la rangent : nommer et déplacer ne changent rien à qui la voit,
   garder et montrer changent exactement cela. Les mettre côte à côte sans
   filet ferait cliquer « garder » en croyant ranger. */
.album-lot__prive {
  margin-top: var(--e4); padding-top: var(--e3);
  border: 0; border-top: 1px solid var(--filet);
}
.album-lot__prive legend {
  font-size: .8125rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--encre-douce); padding: 0 var(--e2) 0 0;
}
.album-lot__prive .secondaire { margin: var(--e1) 0 var(--e3); }

/* La qualité d'un lieu — « Maison de famille · La Cabane des 4 ». Elle se lit
   d'un trait : le libellé dit CE QUE C'EST, le nom seul est cliquable parce que
   lui seul mène quelque part. Souligner les deux ferait croire à deux
   destinations. */
.qualite-lieu {
  display: inline-flex; align-items: baseline; gap: .35em;
  font-size: .9375rem; color: var(--encre-douce);
}
.qualite-lieu a { color: inherit; text-underline-offset: .18em; }
.qualite-lieu a:hover, .qualite-lieu a:focus-visible { color: var(--lien); }

/* ------------------------------------------------------------------
   UNE CARTE PAR ADRESSE
   ------------------------------------------------------------------
   JB, le 26 août : « pas super clair à cause des boutons Corriger et Qui vit
   qui ne ressortent pas comme tels, on a tout en gras, on a tout aligné à
   gauche, la vue est confuse ».

   Trois défauts qui se tenaient : rien ne DÉLIMITAIT une adresse de la suivante
   — un filet sépare, il ne rassemble pas, et les deux dépliants flottaient sans
   dire à quelle adresse ils appartenaient ; tout portait la MÊME GRAISSE, donc
   plus rien n'était mis en avant ; et tout était sur UNE SEULE COLONNE, si bien
   qu'une commande et un renseignement avaient exactement la même forme. */
.residences { list-style: none; margin: var(--e4) 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--e3); }
.residence {
  border: 1px solid var(--filet); border-radius: 6px;
  background: var(--surface); overflow: hidden;
}
.residence__entete {
  display: flex; gap: var(--e3); align-items: flex-start;
  padding: var(--e3); }
.residence__corps { flex: 1; min-width: 0; }

/* LA VIGNETTE — la maison d'un coup d'œil, à gauche, à taille fixe pour que
   deux adresses s'alignent quelles que soient leurs photos. Elle NE PROMET
   RIEN QU'ELLE N'OFFRE : le cartouche de la carte montrait jadis un grand
   emplacement vide et non cliquable à la place de la photo — il promettait
   sans offrir, et JB a cherché deux jours. Vide, celle-ci le DIT. */
.residence__vignette {
  flex: 0 0 auto; width: 92px; height: 92px; border-radius: 4px;
  overflow: hidden; background: var(--filet-clair);
}
.residence__vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* SANS PHOTO : un aplat, pas un cadre en pointillé. Le pointillé se lit comme
   une zone de dépôt — donc comme une promesse de cliquer — et cette vignette
   n'est pas cliquable. Le produit a déjà payé cette promesse-là sur le
   cartouche de la carte, qui montrait un grand emplacement vide et muet. Le
   geste est juste dessous, nommé : « Corriger cette adresse ». */
.residence__vignette--vide {
  display: flex; align-items: center; justify-content: center; text-align: center;
  background: var(--filet-clair);
  color: var(--encre-douce); font-size: .6875rem; padding: var(--e2);
}
@media (max-width: 30rem) { .residence__vignette { width: 68px; height: 68px; } }

/* LE GENRE porte la graisse, et lui seul dans la carte : c'est ce qu'on
   cherche en parcourant la liste. L'adresse se lit dessous, en corps normal. */
.residence__genre {
  margin: 0; font-weight: var(--g-forte); font-size: var(--t-corps);
}
.residence__adresse { margin: 2px 0 0; line-height: 1.4; }
.residence__corps .qualite-lieu { margin-top: var(--e1); }
.residence__retirer { flex: 0 0 auto; }

/* ------------------------------------------------------------------
   UN GESTE REPLIÉ RESSEMBLE À UNE COMMANDE, PAS À UN TITRE
   ------------------------------------------------------------------
   `.greffe summary` porte la graisse forte, ce qui convient à un titre de
   section — pas à deux commandes posées côte à côte au pied d'une carte. Ici le
   nom du geste est en graisse MOYENNE, ce qu'il ouvre est dit dessous en petit,
   et le chevron dit que ça se déplie. */
.residence__gestes { border-top: 1px solid var(--filet-clair); }
.geste-plie { border-bottom: 1px solid var(--filet-clair); }
.geste-plie:last-child { border-bottom: 0; }
/* DEUX LIGNES, DEUX COLONNES, ET LE COMPTE SUR LA PREMIÈRE. Sans
   `grid-column` explicite, la pastille de compte tombait dans la cellule
   suivante — donc sous le nom du geste, seule sur sa ligne. Le nom et le compte
   se lisent ensemble : « Qui vit ici 6 » est une phrase, « Qui vit ici » puis
   « 6 » n'en est pas une. */
.geste-plie > summary {
  cursor: pointer; list-style: none; min-height: var(--cible);
  display: grid; grid-template-columns: 1fr auto;
  grid-template-areas: "nom chevron" "quoi chevron";
  align-items: center; gap: 0 var(--e2); padding: var(--e2) var(--e3);
}
.geste-plie__ligne { grid-area: nom; display: flex; align-items: baseline;
  gap: var(--e1); flex-wrap: wrap; }
.geste-plie > summary::-webkit-details-marker { display: none; }
.geste-plie > summary::after {
  content: "▾"; grid-area: chevron; color: var(--encre-douce); font-size: .8em;
}
.geste-plie[open] > summary::after { content: "▴"; }
.geste-plie > summary:hover { background: var(--filet-clair); }
.geste-plie__nom { font-weight: 600; }
.geste-plie__quoi { grid-area: quoi; font-size: .8125rem; color: var(--encre-douce); }
.geste-plie__compte {
  display: inline-block; margin-left: var(--e1); padding: 0 .45em;
  border-radius: 999px; background: var(--filet-clair);
  font-size: .75rem; font-variant-numeric: tabular-nums;
}
.geste-plie > *:not(summary) { padding: 0 var(--e3) var(--e3); }
.geste-plie .formulaire { padding: 0 var(--e3) var(--e3); }

/* ------------------------------------------------------------------
   LES DEUX GESTES DU RAIL — SANS CADRE, DONC AVEC LEUR VRAI NOM
   ------------------------------------------------------------------
   Le cadre de `.action` forçait une seule ligne dans une colonne de quinze
   rem : « Ajouter un membre de la famille » débordait, et l'intitulé avait été
   raccourci en « Ajouter » — un mot qui ne dit ni quoi, ni à quoi.

   Sans cadre, l'intitulé passe à la ligne et dit ce que le geste fait. C'est ce
   qui compte dans un rail : on n'y survole rien, et rien n'y explique.

   L'icône garde sa ligne de base sur la PREMIÈRE ligne du texte : centrée
   verticalement sur deux lignes, elle flotte au milieu et cesse de désigner le
   geste. */
.geste-rail {
  display: flex; align-items: flex-start; gap: var(--e2);
  padding: var(--e2) var(--e3); min-height: var(--cible);
  border-radius: 6px; text-decoration: none; color: inherit;
  font-size: .9375rem; line-height: 1.35; text-wrap: pretty;
}
/* ------------------------------------------------------------------
   UN `<svg>` SANS TAILLE N'EST PAS UNE PETITE ICÔNE
   ------------------------------------------------------------------
   `svg_lucide` n'émet qu'un `viewBox` : aucun `width`, aucun `height`. Vingt
   sites d'icône de cette feuille en posent un — `.onglet__icone`, `.motif`,
   `.bulle__pied svg`, tous les autres — et celui-ci l'avait oublié. Un élément
   remplacé sans dimensions prend alors la taille par défaut du navigateur, et
   les deux gestes du rail s'affichaient en DESSINS DE DEUX CENTS PIXELS qui
   chassaient leur propre libellé hors de la colonne.

   Trouvé le 30 août 2026 en capturant l'écran de l'album pour tout autre chose.
   Il ne se voit pas de la même façon partout — la résolution d'un `<svg>` sans
   dimensions dans un conteneur flexible diffère d'un moteur à l'autre —, ce qui
   explique qu'il ait survécu : selon le navigateur, on voit un dessin énorme ou
   pas de dessin du tout, et « pas de dessin » ne ressemble pas à un défaut.
   C'est la famille des vingt sites dont dix-neuf sont justes. */
.geste-rail svg { width: 22px; height: 22px; flex: 0 0 auto; margin-top: .15em; }
.geste-rail:hover, .geste-rail:focus-visible { background: var(--filet-clair); }

/* Le titre d'une rangée de pastilles. Une rangée de deux noms de cousinage sans
   intitulé ne dit pas ce qu'elle règle — on la prend pour un fil d'Ariane. */
.rangee-titre {
  margin: var(--e3) 0 var(--e1); font-size: .8125rem; letter-spacing: .04em;
  color: var(--encre-douce); font-weight: 600;
}

/* ------------------------------------------------------------------
   CE QU'IL FAUT SAVOIR DU LIEU, AU BORD DE LA CARTE
   ------------------------------------------------------------------
   « Maison de famille · La Cabane des 4 » vivait dans la colonne de droite,
   décalé sous l'adresse et aligné sur elle. Cette ligne ne qualifie pas
   l'adresse : elle qualifie LE LIEU tout entier, vignette comprise. Elle passe
   donc sous les deux, au bord de la carte — et les avertissements avec elle.

   Un filet clair les sépare de l'entête sans les couper des gestes : ce sont
   trois étages d'une même carte, pas trois blocs voisins. */
.residence__notes {
  padding: 0 var(--e3) var(--e3);
  display: flex; flex-direction: column; gap: var(--e1);
}
.residence__notes > :last-child { margin-bottom: 0; }
.residence__notes .qualite-lieu,
.residence__notes .secondaire { margin: 0; }
/* L'entête perd son rembourrage bas quand les notes le reprennent : sans cela,
   deux rembourrages se suivent et la ligne semble décrochée de l'adresse. */
.residence__entete:has(+ .residence__notes) { padding-bottom: var(--e2); }

/* ------------------------------------------------------------------
   LA FICHE D'UNE MAISON — TROIS RETOUCHES D'ESPACE
   ------------------------------------------------------------------ */

/* Le RÉSUMÉ suit une entête qui porte déjà le titre et le bouton : le chapô du
   produit, lui, prend une marge haute pensée pour suivre un titre SEUL. La
   description décrochait donc de la maison qu'elle décrit. */
.maison__resume {
  margin: var(--e2) 0 var(--e4);
  font-family: var(--serif, inherit); font-size: 1.0625rem;
  color: var(--encre-douce);
}

/* LES PROPRIÉTAIRES, un par ligne. Ils paraissaient deux fois — une phrase
   « Propriétaires déclarés : X, Y », puis la même liste dessous : la première
   venait d'une époque où la seconde n'existait pas. */
.proprietaires { list-style: none; margin: var(--e1) 0 var(--e3); padding: 0;
  display: flex; flex-direction: column; gap: var(--e2); }
.proprietaires .secondaire { display: block; }

/* LA VISITE EN LIGNE tombait sous « Je n'en suis plus propriétaire » sans rien
   pour l'en séparer : on lisait les deux comme un même bloc, alors qu'ils n'ont
   aucun rapport. */
.visite-en-ligne { margin-top: var(--e5); padding-top: var(--e3);
  border-top: 1px solid var(--filet-clair); }

/* ------------------------------------------------------------------
   PLUSIEURS LIEUX SUR UN MÊME POINT
   ------------------------------------------------------------------
   À l'échelle de la France, huit kilomètres font deux à cinq pixels et un
   marqueur en fait seize : deux maisons voisines se recouvrent entièrement, et
   celle dessinée en dernier attrape tous les clics. Le cartouche les porte donc
   toutes, et DIT combien — sans quoi on croit encore n'en voir qu'une. */
.cartouche-groupe { display: flex; flex-direction: column; gap: var(--e2);
  max-height: 60vh; overflow-y: auto; }
.cartouche-groupe__compte {
  margin: 0 0 var(--e1); font-size: .75rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--encre-douce);
}

/* ==================================================================
   LA CARTE SUR GRAND ÉCRAN — LA LISTE EST UNE COLONNE, PAS UNE FEUILLE
   ==================================================================
   ELLE EST ÉCRITE ICI, EN FIN DE FEUILLE, ET C'EST LE POINT QUI M'A COÛTÉ
   UNE PASSE. Posée dans le bloc `min-width: 60rem` du début du fichier, elle
   perdait contre `.carte__feuille { position: absolute }` — même spécificité,
   1 500 lignes plus bas, donc l'ordre tranche. Le média ne pèse rien dans le
   calcul : une règle sous condition ne bat pas une règle sans condition.

   La sonde l'a dit tout de suite — `feuille.pos=absolute` sur un écran de
   1440 px —, et sans elle j'aurais annoncé une refonte qui n'était pas servie.

   ------------------------------------------------------------------
   POURQUOI UNE COLONNE
   ------------------------------------------------------------------
   La feuille du bas est le bon objet sur un téléphone : la carte y est tout
   l'écran, et l'on tire la liste par-dessus. Sur un écran de bureau elle n'a
   aucune raison d'être — il y a la place à côté, et la faire monter revient à
   cacher la carte qu'on regarde pour lire la liste qui en parle. JB, le
   27 août 2026 : « c'est encore moche quand il est monté », puis « même
   replié, pas bon ».

   C'est ce que font Plans, Maps, Airbnb et Komoot : liste à droite, carte à
   gauche, les deux à l'écran en même temps. On ne choisit plus entre voir et
   lire.

   LE MÊME DOCUMENT SERT LES DEUX : on ne duplique pas la liste. Le `details`
   cesse d'être posé par-dessus, prend la seconde colonne, et `deplie` l'ouvre.
   Une seconde liste pour le bureau serait deux endroits à corriger le jour où
   l'on ajoute une colonne. */
@media (min-width: 60rem) {
  .carte-page { grid-template-columns: 1fr minmax(18rem, 24rem); }
  .carte-page__entete { grid-column: 1 / -1; }
  .carte-famille { grid-column: 1; grid-row: 2; }
  .carte__feuille {
    position: static; grid-column: 2; grid-row: 2;
    max-height: none; min-height: 0;
    border-top: 0; border-left: 1px solid var(--filet);
    border-radius: 0; box-shadow: none;
  }
  /* ------------------------------------------------------------------
     UNE COLONNE NE SE REPLIE PAS — ELLE LAISSERAIT UN VIDE
     ------------------------------------------------------------------
     Fermée, la colonne de droite était un rectangle NOIR de vingt-quatre rem
     sur toute la hauteur : la place était réservée et rien ne s'y peignait.
     C'est le premier des « plein de problèmes » de JB, et le plus visible.

     Le bandeau reste — il porte le compte, qui est le titre de la colonne —
     mais il cesse d'être un bouton : il n'y a rien à replier quand la place
     est là. Le chevron s'en va avec, sans quoi il promettrait un geste sans
     effet. */
  .carte__feuille > summary { pointer-events: none; cursor: default; }
  .carte__feuille > summary .reglage__chevron { display: none; }
  /* La poignée dit « ceci se tire » : une colonne ne se tire pas. */
  .carte__feuille-poignee { display: none; }
  /* Dans une colonne étroite, les pastilles du filtre PASSENT À LA LIGNE au
     lieu de défiler : un rang qui défile horizontalement dans un panneau large
     de vingt-quatre rem cache la moitié de ses options — « Maisons de f… ». */
  .carte__feuille .rangee-rubriques { flex-wrap: wrap; overflow-x: visible;
    -webkit-mask-image: none; mask-image: none; padding-right: 0; }
  /* Le rembourrage de fin réservait la hauteur de la barre du bas, qui
     n'existe pas ici — la liste s'arrêtait à quatre centimètres du vide. Et la
     borne de hauteur devient celle de la COLONNE : toute la fenêtre moins la
     bande haute et le bandeau de la feuille. */
  .carte__feuille-corps { padding-bottom: var(--e5);
    max-height: calc(100dvh - var(--bande-haute) - var(--feuille-bandeau)); }
  /* L'attribution des tuiles reprend sa place : plus de bandeau sous elle. */
  .carte-page .leaflet-bottom { bottom: 0; }
}

/* ==================================================================
   UNE DATE EN TROIS CASES — LE MOTIF DE GOV.UK
   ==================================================================
   Le champ de date natif est découpé en trois segments et le curseur se pose
   sur CELUI QU'ON CLIQUE : qui vise l'année et tape « 1968 » l'obtient, qui
   clique un peu à gauche alimente le jour avec « 19 » et le mois avec « 68 ».
   D'où l'intermittence que JB décrit — elle dépend du pixel touché.

   Trois cases, donc, dans l'ordre où on dit une date. LA CASE DE L'ANNÉE EST
   PLUS LARGE : sa largeur dit ce qu'on attend avant qu'on ait lu l'étiquette,
   et c'est le seul indice qui n'a pas besoin d'être lu. */
.date-trois { border: 0; padding: 0; margin: 0 0 var(--e3); min-width: 0; }
.date-trois > legend { padding: 0; margin: 0 0 var(--e2);
  font-family: var(--sans); font-size: var(--t-meta);
  font-weight: var(--g-appuyee); color: var(--encre); }
.date-trois__cases { display: flex; gap: var(--e3); align-items: flex-end; }
.date-trois__case { display: flex; flex-direction: column; gap: var(--e1);
  min-width: 0; }
.date-trois__case label { font-size: var(--t-micro); font-weight: var(--g-normale);
  color: var(--encre-douce); }
.date-trois__case input { width: 4.5rem; }
.date-trois__case--annee input { width: 6.5rem; }
/* On retire les flèches de l'incrémenteur : personne n'atteint 1890 en
   cliquant, et elles rognent la place des chiffres. */
.date-trois input::-webkit-outer-spin-button,
.date-trois input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.date-trois input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* ==================================================================
   AFFINER — UNE LIGNE QUI PORTE SON ÉTAT, AU LIEU DE DEUX RANGÉES
   ==================================================================
   Le trombinoscope empilait TROIS rangées de pastilles pleine largeur avant le
   premier visage : la famille, l'ordre d'affichage, qui est entré. Plus de deux
   cents pixels de commandes, de même poids, sans hiérarchie entre elles — et la
   recherche arrivait après, alors qu'on ouvre cet écran pour chercher quelqu'un
   neuf fois sur dix. JB, le 29 août 2026 : « moins bloc, plus propre ».

   Ce qui faisait le bloc n'était pas les pastilles : c'est qu'il y en avait
   trois rangées équivalentes. La famille reste visible — c'est une PORTÉE, du
   même rang que sur l'album — et les deux réglages descendent ici.

   LE RÉSUMÉ PORTE L'ÉTAT : « Affiner — par nom · tout le monde ». On lit ce qui
   est appliqué sans ouvrir, et l'on n'ouvre que pour changer. C'est ce qu'un
   menu déroulant ne sait pas faire, et c'est la raison pour laquelle ce dépôt
   les refuse sur les portées depuis le 26 août. */
.affiner { margin: 0 0 var(--e4); }
.affiner > summary {
  cursor: pointer; min-height: var(--cible); display: flex; align-items: center;
  gap: var(--e2); font-family: var(--sans); font-size: var(--t-meta);
  font-weight: var(--g-appuyee); color: var(--encre);
}
.affiner > summary::-webkit-details-marker { display: none; }
/* Le chevron plutôt que le triangle du navigateur : c'est le signe que le
   produit emploie partout ailleurs pour « ceci se déplie ». */
.affiner > summary::after {
  content: ""; width: .5rem; height: .5rem; margin-left: auto;
  border-right: 2px solid var(--encre-douce);
  border-bottom: 2px solid var(--encre-douce);
  transform: rotate(45deg); transition: transform var(--duree);
}
.affiner[open] > summary::after { transform: rotate(-135deg); }
.affiner__corps { padding: var(--e2) 0 0; }

/* ==================================================================
   CHOISIR SA FAMILLE — UN MENU, ET LES DEUX FORMES DU NOM
   ==================================================================
   La rangée de pastilles défilait horizontalement dès trois noms longs, et
   personne n'a l'idée de la tirer : elle cachait des familles SANS LE DIRE, ce
   qui est pire qu'un menu — le menu, lui, annonce qu'il y a une liste. */
.choix-famille { display: flex; flex-wrap: wrap; align-items: flex-end;
  gap: var(--e2) var(--e3); margin: 0 0 var(--e4); }
.choix-famille label { flex: 1 0 100%; }
.choix-famille select { flex: 1 1 16rem; min-width: 0; }
.choix-famille__ailleurs { font-family: var(--sans); font-size: var(--t-meta);
  align-self: center; }

.album-geste > summary:hover { border-color: var(--encre-douce);
  background: var(--filet-clair); }
.album-geste > summary > h2 { grid-area: titre; }
.album-geste__quoi { grid-area: quoi; font-family: var(--sans);
  font-size: var(--t-meta); font-weight: var(--g-normale); letter-spacing: 0;
  color: var(--encre-douce); }
.album-geste > summary::-webkit-details-marker { display: none; }
/* Le chevron est calé DANS le cadre, contre le bord droit — et non poussé à
   l'autre bout d'une page de mille pixels, où il ne se rattache plus à rien.
   C'est ce qui faisait lire le repli comme un titre sur écran large. */
.album-geste > summary::after {
  content: "▾"; grid-area: chevron; color: var(--encre-douce);
  font-size: .75em; font-family: var(--sans); width: auto; height: auto;
  margin: 0; border: 0; transform: none;
}
.album-geste[open] > summary::after { content: "▴"; }
.album-geste[open] > summary { margin: 0 0 var(--e4); }
.album-geste .formulaire { padding: 0 var(--e2) var(--e2); }
.album-geste > .secondaire { display: block; padding: 0 var(--e2) var(--e3); }

/* LA VUE, et son titre a la même allure que les deux gestes : c'est ce qui
   fait qu'on les lit comme trois choix de même rang. Le filet au-dessus dit
   qu'on change de sujet — les réglages qui suivent appartiennent à la vue, et
   non aux gestes. */
.album-vue { margin: var(--e6) 0 0; padding: var(--e5) 0 0;
  border-top: 1px solid var(--filet-clair); }
.album-vue > h2 { margin: 0 0 var(--e3); font-family: var(--serif);
  font-size: var(--t-sous-titre); font-weight: var(--g-forte); }

/* « Votre album » n'est pas une famille : c'est l'ABSENCE de choix de famille,
   et c'est un autre écran. Il restait un lien nu posé à côté d'un menu — « ça
   semble sans style », JB le 30 août. Il prend l'allure des autres commandes de
   choix du produit, celle d'une pastille de rangée : on voit qu'il se clique,
   et on ne le confond pas avec le menu qui, lui, reste dans la famille. */
.choix-famille__ailleurs { flex: 0 0 auto; display: inline-flex;
  align-items: center; min-height: var(--cible); padding: 0 var(--e3);
  border: 1px solid var(--filet); border-radius: var(--r-moyen);
  background: var(--surface); color: var(--encre); text-decoration: none; }
.choix-famille__ailleurs:hover { border-color: var(--lien); color: var(--lien); }


/* ==================================================================
   LA VISIONNEUSE EN SURIMPRESSION
   ==================================================================
   Le motif, et pourquoi il renverse la décision écrite sur la page d'une photo,
   sont dans `album_visionneuse_controller.js`. Ici, trois choses seulement.

   UN FOND SOMBRE, TOUJOURS, DANS LES DEUX THÈMES. Une photo se regarde sur du
   noir : c'est ce que font toutes les visionneuses depuis la diapositive, et
   pour une raison optique — un fond clair autour d'une image en réduit le
   contraste perçu. C'est le seul endroit du produit qui ne suit pas le thème,
   et c'est délibéré.

   LA CROIX ET LES FLÈCHES NE DÉFILENT PAS. Elles sont posées sur la scène, pas
   dans le flux : sur un téléphone tenu d'une main, la sortie doit être au même
   endroit quelle que soit la photo. JB l'a demandé ainsi — « croix pour fermer
   en mobile et fixe ».

   LES CIBLES FONT `--cible`. La sonde des cibles tactiles balaie cet écran. */
.visionneuse {
  width: 100%; max-width: 100%; height: 100dvh; max-height: 100dvh;
  margin: 0; padding: 0; border: 0; background: #12100D; color: #F2EDE3;
}
/* ------------------------------------------------------------------
   `display` NE SE POSE QUE SUR `[open]`, ET C'EST LA RÈGLE ENTIÈRE
   ------------------------------------------------------------------
   La feuille du navigateur ferme un `<dialog>` par `dialog:not([open]) {
   display: none }`. Poser `display: grid` sur la classe l'écrase — même
   spécificité, notre feuille servie après la sienne — et la boîte FERMÉE se
   rend alors dans le flux : un grand rectangle noir posé sous la grille, en bas
   de page, et le clic sur une vignette n'ouvre plus rien puisque tout est déjà
   là. JB, le 30 août 2026 : « la visionneuse noire s'affiche en bas sous les
   miniatures ».

   La règle `[open]` qui suivait ne servait à rien : elle répétait ce que la
   première avait déjà imposé aux DEUX états.

   ET LA SONDE NE L'A PAS VU. Elle interrogeait `dialog.open`, qui valait bien
   `false` : elle mesurait l'ÉTAT, pas ce qui est à l'écran. C'est le repère du
   dépôt — « visible » ne suffit pas, on demande ce qu'on voit — appliqué à
   l'envers : ici il fallait demander que ce soit INVISIBLE. */
.visionneuse[open] { display: grid; grid-template-rows: auto 1fr auto; }
.visionneuse::backdrop { background: #12100D; }
/* LA BARRE DE DÉFILEMENT DE LA PAGE PERCE LE FOND. Le `::backdrop` couvre la
   zone de rendu, pas la gouttière que le navigateur peint à côté : sur un
   ordinateur, il restait une bande blanche haute comme l'écran, sur la droite
   d'une visionneuse noire. Le document ne défile plus tant qu'elle est ouverte
   — ce qui est de toute façon ce qu'on veut : la page dessous n'a rien à faire
   pendant qu'on regarde une photo. */
html:has(.visionneuse[open]) { overflow: hidden; }

.visionneuse__barre { display: flex; align-items: center; gap: var(--e3);
  padding: calc(var(--e2) + env(safe-area-inset-top, 0px)) var(--e3) var(--e2); }
.visionneuse__compte { margin: 0; margin-right: auto; font-family: var(--sans);
  font-size: var(--t-meta); color: #B8B0A0; font-variant-numeric: tabular-nums; }

.visionneuse__fermer, .visionneuse__fleche {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: var(--cible); height: var(--cible); padding: 0;
  border: 0; border-radius: 50%; background: rgba(255, 255, 255, .08);
  color: #F2EDE3; cursor: pointer;
}
.visionneuse__fermer svg  { width: 22px; height: 22px; }
.visionneuse__fleche svg  { width: 26px; height: 26px; }
.visionneuse__fermer:hover, .visionneuse__fleche:hover:not(:disabled) {
  background: rgba(255, 255, 255, .18); }
/* Une flèche qui ne fait rien ET ne le dit pas apprend à ne plus s'en servir.
   Elle garde sa place — la scène ne doit pas se décaler d'un bout à l'autre. */
.visionneuse__fleche:disabled { opacity: .25; cursor: default; }

/* LA SCÈNE : les flèches EN SURIMPRESSION de l'image sur téléphone, où chaque
   pixel de largeur compte, et de part et d'autre dès qu'il y a la place. */
/* ------------------------------------------------------------------
   L'IMAGE NE DÉBORDE PAS SUR LA CROIX
   ------------------------------------------------------------------
   `place-items: center` dimensionne le cadre sur son CONTENU : une photo de
   1200 pixels de haut donnait donc un cadre de 1200 pixels, qui débordait par
   le haut et recouvrait le bouton de fermeture. On ne pouvait plus sortir de la
   photo autrement que par `Échap` ou le geste de retour.
   
   `minmax(0, 1fr)` borne la rangée : sans le `0`, une piste `1fr` prend pour
   minimum la taille de son contenu, ce qui est exactement le piège. Le
   `max-height: 100%` du cadre et de l'image achèvent la chaîne.

   TROUVÉ LE 31 AOÛT 2026, à la première fois qu'un essai a attaché de VRAIES
   images. Toutes mes captures précédentes montraient le texte de remplacement :
   on ne peut pas voir qu'une photo déborde quand il n'y a pas de photo. */
.visionneuse__scene { position: relative; display: grid; min-height: 0;
  grid-template-rows: minmax(0, 1fr); align-items: center; justify-items: center;
  padding: 0 var(--e2);
  /* LE FOND DE VIGNETTE EST POSÉ ICI, et non sur l'image : la scène est une
     rangée de la grille du `<dialog>`, donc une boîte DÉFINIE, alors qu'un
     `<img>` non décodé n'en a aucune. C'est la condition que le motif
     `fond_empreinte` de la grille remplit et que j'avais oubliée. */
  background-size: contain; background-position: center; background-repeat: no-repeat; }
.visionneuse__fleche { position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 1; }
.visionneuse__fleche--avant { left: var(--e2); }
.visionneuse__fleche--apres { right: var(--e2); }

/* `stretch` ET NON `center` SUR TOUTE LA CHAÎNE. Centré, chaque niveau se
   dimensionne sur son CONTENU : le cadre prenait la hauteur de l'image, et
   l'image un pourcentage d'une hauteur qui dépendait d'elle — donc zéro tant
   qu'elle n'était pas décodée. Le fond de vignette n'avait alors aucune surface,
   et la sonde a mesuré vingt-huit pixels là où il en fallait plus de cent.
   Le centrage de la photo est rendu par `object-fit: contain`, qui le fait
   mieux : il letterboxe au lieu de recadrer. */
.visionneuse__cadre { display: grid; grid-template-rows: minmax(0, 1fr) auto;
  gap: var(--e2); margin: 0; min-height: 0; max-height: 100%; max-width: 100%; }
/* `min-height: 0` sur toute la chaîne : sans lui, une grille refuse de
   descendre sous la hauteur naturelle de son contenu, et une photo en portrait
   pousse la barre du bas hors de l'écran. */
/* ------------------------------------------------------------------
   L'IMAGE PREND SA CELLULE, SANS QUOI SON FOND NE PEINT RIEN
   ------------------------------------------------------------------
   Un `<img>` dont l'image n'est pas encore décodée mesure ZÉRO par zéro tant
   qu'aucune dimension ne lui est donnée : `max-width` et `max-height` bornent
   sans dimensionner. Le `background-image` de la vignette n'avait donc aucune
   surface où se peindre, et le correctif de la latence perçue était INERTE —
   relevé par la relecture de code du 31 août 2026.

   Le motif `fond_empreinte` de la grille marche, lui, parce qu'il est posé sur
   des éléments DIMENSIONNÉS : `.album__photo` enveloppe un `<img>` de 400 × 400.
   J'avais recopié le motif sans sa condition.

   `object-fit: contain` letterboxe la photo dans la cellule, et
   `background-size: contain` cadre la vignette exactement pareil : les deux se
   superposent au pixel près. */
/* ------------------------------------------------------------------
   L'IMAGE REVIENT À `max-*`, ET C'EST DÉLIBÉRÉ
   ------------------------------------------------------------------
   `width/height: 100%` lui donnait bien une boîte, mais au prix d'une hauteur
   en POURCENTAGE d'une piste de grille — que Chrome sans tête résout et qu'un
   autre moteur peut rendre à zéro. La photo ne s'affichait alors nulle part,
   alors que les octets étaient arrivés : cinq aperçus demandés, cinq servis en
   trente millisecondes, et un écran noir.
   
   Bornée par `max-*`, une image prend sa taille INTRINSÈQUE — elle s'affiche
   donc toujours — et `minmax(0, 1fr)` sur la rangée l'empêche de déborder sur
   la croix. Le fond de vignette, lui, a déménagé sur la scène, qui a une boîte
   définie. Chaque chose là où elle peut tenir. */
.visionneuse__image { min-height: 0; max-width: 100%; max-height: 100%;
  object-fit: contain; }
.visionneuse__legende { font-family: var(--sans); font-size: var(--t-meta);
  color: #B8B0A0; text-align: center; padding: 0 var(--e3); }

.visionneuse__pied { margin: 0;
  padding: var(--e3) var(--e3) calc(var(--e3) + env(safe-area-inset-bottom, 0px));
  text-align: center; }
.visionneuse__fiche { display: inline-flex; align-items: center;
  min-height: var(--cible); padding: 0 var(--e4);
  border: 1px solid rgba(255, 255, 255, .22); border-radius: var(--r-moyen);
  color: #F2EDE3; text-decoration: none; font-family: var(--sans);
  font-size: var(--t-meta); }
.visionneuse__fiche:hover { border-color: #F2EDE3; }

/* Sur un écran large, la scène respire et les flèches sortent de l'image :
   posées dessus, elles couvriraient un visage alors qu'il y a mille pixels de
   noir de chaque côté. */
@media (min-width: 48rem) {
  .visionneuse__scene { padding: 0 calc(var(--cible) + var(--e5)); }
  .visionneuse__fleche--avant { left: var(--e5); }
  .visionneuse__fleche--apres { right: var(--e5); }
}

/* Une image qui apparaît d'un coup au milieu du noir fatigue ; une qui glisse
   ment sur ce qui s'est passé. Un fondu court, et rien pour qui l'a refusé. */
@media (prefers-reduced-motion: no-preference) {
  .visionneuse__image { animation: visionneuse-parait var(--duree) ease-out; }
  @keyframes visionneuse-parait { from { opacity: 0; } to { opacity: 1; } }
}

/* ------------------------------------------------------------------
   LE DÉPLIANT S'OUVRE VERS LE HAUT AU PIED DU RAIL
   ------------------------------------------------------------------
   Sur écran large, la bande est collée au BAS de la fenêtre : une liste qui
   descend en sort, et l'on voit deux lignes d'un panneau qui en compte six.

   ET CETTE RÈGLE EST ICI, EN FIN DE FEUILLE, PARCE QUE L'ORDRE DÉCIDE. Posée
   avec le reste du bloc « écran large » — trois cents lignes plus haut que la
   déclaration de `@media (min-width: 60rem) {
  }

/* La trace d'un renommage, sous les gestes du dossier : discrète, mais lisible
   sans ouvrir quoi que ce soit. C'est là qu'on se demande « ce n'est pas le nom
   que je cherchais », donc c'est là que la réponse doit être. */
.dossier-trace { margin: calc(-1 * var(--e3)) 0 var(--e5); }

/* ------------------------------------------------------------------
   LA VIGNETTE EN FOND DU `<img>`, ET RIEN À ORCHESTRER
   ------------------------------------------------------------------
   Le navigateur peint le fond d'une image AVANT de l'avoir chargée, et la
   vignette est déjà dans son cache — la grille l'y a mise. On voit donc la
   photo tout de suite, en trente-cinq kilooctets, et l'aperçu la recouvre
   exactement quand il arrive : `contain` sur les deux, même cadrage.

   C'est le motif que la grille emploie déjà avec `fond_empreinte`. Il n'a
   besoin d'aucun JavaScript, ce qui est précisément sa qualité : ma première
   version échangeait le `src` sur un `onload`, et tout ce qui empêche cet
   `onload` laisse l'écran vide. */

