/* 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. */
  --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;
  --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. */
  padding: 0 0 calc(64px + env(safe-area-inset-bottom, 0px) + 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; }

.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;
}

::placeholder { color: var(--encre-douce); opacity: 1; }

/* ------------------------------------------------------------------
   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);
  font-family: var(--sans); font-size: var(--t-corps); font-weight: var(--g-forte);
  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: 44px;
  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. */
.portrait {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; 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); }
.cousinage-carte__entete { display: flex; align-items: baseline;
  justify-content: space-between; gap: var(--e3); padding: var(--e4) var(--e4) var(--e0); }
.cousinage-carte__nom { font-family: var(--serif); font-size: var(--t-sous-titre);
  font-weight: var(--g-appuyee); min-width: 0; }
.cousinage-carte__role { flex: 0 0 auto; font-size: var(--t-micro); color: var(--encre-douce); }
.cousinage-carte__generations { margin: 0; padding: 0 var(--e4) var(--e2);
  font-size: var(--t-meta); color: var(--encre-douce); }

/* ------------------------------------------------------------------
   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(9rem, 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); }
.matrice__niveau { display: inline-flex; align-items: center; gap: var(--e1);
  min-height: 40px; 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. */
.liste__corps { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.liste__ligne > * { min-width: 0; }

/* ------------------------------------------------------------------
   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;
  /* 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. */
  height: calc(64px + env(safe-area-inset-bottom, 0px));
  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. */
    padding: var(--e6) var(--e3);
    /* ------------------------------------------------------------------
       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 - 64px - env(safe-area-inset-bottom, 0px) - 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); }
.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;
  overflow-y: auto;
}

/* ------------------------------------------------------------------
   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; } }

/* 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 - 3rem); 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. */
.action--compacte { min-height: 40px; 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. */
.invitation-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. */
.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 { margin: var(--e3); }

/* Choisir sa famille : des liens en filet, jamais un menu déroulant. À deux ou
   trois cousinages, le déroulant cache l'information qu'il devait donner. */
.album__familles { display: flex; flex-wrap: wrap; gap: var(--e4); margin: 0 0 var(--e5);
  padding: var(--e2); border-bottom: 1px solid var(--filet); }
.album__famille { font-size: var(--t-meta); text-decoration: none;
  color: var(--encre-douce); min-height: var(--cible);
  display: inline-flex; align-items: center; }
.album__famille--actif { color: var(--encre); font-weight: var(--g-forte); }

/* 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(6.5rem, 1fr)); }

/* 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(5rem, 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); }

/* 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; }

.noeuds { display: grid; gap: var(--e2); margin: var(--e1) 0 var(--e6);
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 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); }
.noeud__fiche { font-family: var(--sans); font-size: var(--t-meta); }

/* 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 summary::marker { color: var(--encre-douce); }
.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); }
.categorie { font-size: var(--t-meta); text-decoration: none;
  color: var(--encre-douce); min-height: var(--cible);
  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. */
.petite-annonce { flex: 1; display: block; }
.petite-annonce__titre { display: block; }
.petite-annonce__prix { display: block; font-variant-numeric: tabular-nums; }
.petite-annonce__corps { display: block; color: var(--encre-douce); }
.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(8rem, 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. */
.carte-page { position: relative; display: grid; grid-template-rows: auto 1fr;
  height: calc(100dvh - 64px - env(safe-area-inset-bottom, 0px));
  margin: -1.5rem -1.25rem -2.5rem; }
/* `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.

   Le sélecteur porte `a.cartouche` : `.leaflet-container a` pèse (0,1,1) et
   gagnerait sur une classe seule, quel que soit l'ordre des feuilles. */
a.cartouche, a.cartouche:hover {
  display: flex; flex-direction: column; min-width: 14rem; max-width: 16rem;
  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. */
/* Le Courrier ouvre « Moi » avec son compte. C'était une ligne de texte au
   milieu de la page, alors que c'est par lui que passent les mandats, les
   corroborations et les mises en relation. */
.appel-courrier { display: flex; align-items: center; justify-content: space-between;
  gap: var(--e3); margin: 0 0 var(--e5); padding: var(--e3) var(--e4);
  border: 1px solid var(--filet); border-radius: var(--r-petit);
  background: var(--surface); text-decoration: none; color: var(--encre); }
.appel-courrier:hover { border-color: var(--lien); }
.appel-courrier__intitule { font-weight: var(--g-appuyee); }

/* 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; }
.destination-rubrique { flex: 0 0 auto; display: inline-flex; align-items: center;
  min-height: 40px; 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. */
.carte__legende { position: absolute; bottom: var(--e3); left: var(--e3); z-index: 400;
  max-width: calc(100% - 1.5rem);
  background: var(--surface); border: 1px solid var(--filet);
  border-radius: var(--r-moyen); box-shadow: var(--ombre-posee);
  font-family: var(--sans); font-size: var(--t-meta); }
.carte__legende > summary { display: flex; align-items: center; gap: var(--e1);
  min-height: 36px; padding: 0 var(--e2); cursor: pointer;
  color: var(--encre-douce); }
.carte__legende ul { list-style: none; margin: 0; padding: var(--e0) var(--e2) var(--e2); }
/* L'encre NORMALE, et non l'encre douce : une légende qu'on ouvre est une
   légende qu'on vient lire, et elle était plus pâle que le texte qu'elle
   explique. */
.carte__legende li { display: flex; align-items: center; gap: var(--e2);
  min-height: 26px; color: var(--encre); }

/* 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. */
.carte__feuille { position: absolute; left: 0; right: 0; bottom: 0; z-index: 401;
  background: var(--surface); border-top: 1px solid var(--filet);
  max-height: 65%; display: flex; flex-direction: column; }
.carte__feuille > summary { flex: 0 0 auto; min-height: var(--cible);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; font-family: var(--sans); font-size: var(--t-meta);
  color: var(--encre-douce); }
.carte__feuille-corps { flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 0 var(--e5) var(--e5); }


/* 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(15rem, 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
   ------------------------------------------------------------------
   `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. L'album le faisait déjà bien, avec `aspect-ratio` ; le
   fil ne l'avait pas eu. */
.bulle__photo img { aspect-ratio: 4 / 3; object-fit: cover; }


/* ==================================================================
   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. */
/* 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(17rem, 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); }
.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; }
.bandeau__quand { font-family: var(--sans); font-size: var(--t-micro);
  color: var(--encre-douce); }

/* 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); }

.medaillon-profil { position: relative; display: inline-flex;
  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) {
  .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: 41; }
  .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 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; }

.carnet__pas { flex: 0 0 auto; display: inline-flex; align-items: center;
  justify-content: center; width: 40px; min-height: 40px;
  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 { display: inline-flex; align-items: center;
  min-height: 40px; 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(15rem, 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(7rem, 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(12rem, 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); }

/* 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); }
