@charset "UTF-8";

/* ==========================================================================
   MaFamille — feuille de style
   Charte « Verger » : fond blanc, une couleur de marque verte reprise du
   logo, cartes à grands arrondis et ombres douces, barre latérale à gauche.
   Structure calquée sur celle de linxia-connect (tableau de bord clair),
   avec le vert du logo à la place du violet.
   Polices système : aucune requête externe, conforme à la CSP stricte.
   ========================================================================== */

/*
 * L'attribut « hidden » doit l'emporter : plusieurs composants posent un
 * display (flex, grid) par leur classe, ce qui sinon les rend visibles alors
 * que le script les avait masqués.
 */
[hidden] { display: none !important; }

/* --------------------------------------------------------------- variables */

:root {
  /* Marque — vert du logo et ses déclinaisons */
  --vert:         #2e7d4f;
  --vert-fond:    #eaf5ee;
  --vert-fonce:   #23603c;
  --vert-clair:   #eaf4ed;
  --vert-bord:    #cfe6d7;

  /* Accents secondaires, pour coder les rubriques et les données */
  --abricot:      #d97b28;
  --abricot-clair:#fdf2e6;
  --bleu:         #b0561f;
  --bleu-clair:   #ecf4f8;
  --prune:        #8d5a8f;

  /* Surfaces */
  --fond:          #f7f9f7;
  --surface:       #ffffff;
  --surface-2:     #f2f6f3;
  --bordure:       #e4ebe6;
  --bordure-forte: #cbd8cf;

  /* Texte — contrastes vérifiés sur --surface */
  --texte:         #16261b;   /* 14.6:1 */
  --texte-doux:    #4d6154;   /* 7.4:1  */
  --texte-tenu:    #6b7d72;   /* 5.1:1  */

  /* Sémantique */
  --succes:        #2e7d4f;
  --succes-fond:   #eaf4ed;
  --erreur:        #a32b22;
  --erreur-fond:   #fbeae8;
  --info:          #b0561f;
  --info-fond:     #ecf4f8;

  /* Rythme typographique */
  --serif:  ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", Palatino, "Times New Roman", serif;
  --sans:   "Avenir Next", Avenir, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono:   ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* Espacement */
  --e1: 0.25rem;
  --e2: 0.5rem;
  --e3: 0.75rem;
  --e4: 1rem;
  --e5: 1.5rem;
  --e6: 2rem;
  --e7: 3rem;
  --e8: 4rem;

  /* Arrondis généreux, comme sur linxia-connect */
  --rayon:    9px;
  --rayon-lg: 16px;
  --rayon-xl: 24px;

  /* Ombres douces et basses, jamais dures */
  --ombre-1: 0 1px 4px rgba(133, 146, 173, .20);
  --ombre-2: 0 2px 10px rgba(133, 146, 173, .22);
  --ombre-3: 0 12px 32px rgba(133, 146, 173, .28);

  --largeur-max: 84rem;
  --largeur-texte: 68ch;
  --largeur-barre: 15rem;

  --transition: 180ms cubic-bezier(0.4, 0, 0.2, 1);

  --z-carte:   10;
  --z-barre:   25;
  --z-entete:  30;
  --z-menu:    40;
  --z-modale:  50;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --fond:          #121714;
    --surface:       #1a211d;
    --surface-2:     #222b26;
    --bordure:       #2e3a33;
    --bordure-forte: #3d4c43;

    --texte:         #e9f0eb;
    --texte-doux:    #b6c6bc;
    --texte-tenu:    #91a698;

    --vert:          #519f6f;
    --vert-fond:     #1b2e23;
    --vert-fonce:    #8ed0a7;
    --vert-clair:    #1f2f26;
    --vert-bord:     #2f4738;

    --abricot:       #e9a463;
    --abricot-clair: #2e2419;
    --bleu:          #c8783a;
    --bleu-clair:    #1b2831;

    --succes:        #519f6f;
    --succes-fond:   #1f2f26;
    --erreur:        #ef9d92;
    --erreur-fond:   #2f1c1a;
    --info:          #c8783a;
    --info-fond:     #1b2831;

    --ombre-1: 0 1px 4px rgba(0, 0, 0, .35);
    --ombre-2: 0 2px 10px rgba(0, 0, 0, .40);
    --ombre-3: 0 12px 32px rgba(0, 0, 0, .50);
  }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] {
    --fond:          #121714;
    --surface:       #1a211d;
    --surface-2:     #222b26;
    --bordure:       #2e3a33;
    --bordure-forte: #3d4c43;

    --texte:         #e9f0eb;
    --texte-doux:    #b6c6bc;
    --texte-tenu:    #91a698;

    --vert:          #519f6f;
    --vert-fond:     #1b2e23;
    --vert-fonce:    #8ed0a7;
    --vert-clair:    #1f2f26;
    --vert-bord:     #2f4738;

    --abricot:       #e9a463;
    --abricot-clair: #2e2419;
    --bleu:          #c8783a;
    --bleu-clair:    #1b2831;

    --succes:        #519f6f;
    --succes-fond:   #1f2f26;
    --erreur:        #ef9d92;
    --erreur-fond:   #2f1c1a;
    --info:          #c8783a;
    --info-fond:     #1b2831;

    --ombre-1: 0 1px 4px rgba(0, 0, 0, .35);
    --ombre-2: 0 2px 10px rgba(0, 0, 0, .40);
    --ombre-3: 0 12px 32px rgba(0, 0, 0, .50);
  }


/* ------------------------------------------------------------------ socle */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  /* 17px : au-dessus du minimum de 16px sur mobile */
  font: 400 1.0625rem/1.6 var(--sans);
  color: var(--texte);
  background: var(--fond);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 600;
  line-height: 1.25;
  color: var(--texte);
  margin: 0 0 var(--e3);
  text-wrap: balance;
}

h1 { font-size: clamp(1.75rem, 1.3rem + 2vw, 2.5rem); }
h2 { font-size: clamp(1.375rem, 1.15rem + 1vw, 1.75rem); }
h3 { font-size: 1.1875rem; }
h4 { font-size: 1.0625rem; }

p { margin: 0 0 var(--e4); max-width: var(--largeur-texte); }

a {
  color: var(--vert-fonce);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  transition: color var(--transition);
}

a:hover { color: var(--vert); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) a { color: var(--vert); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] a { color: var(--vert); }


/* Anneau de focus visible partout : jamais outline:none sans remplacement. */
:focus-visible {
  outline: 3px solid var(--vert);
  outline-offset: 2px;
  border-radius: 3px;
}

img { max-width: 100%; height: auto; display: block; }

hr {
  border: 0;
  border-top: 1px solid var(--bordure);
  margin: var(--e6) 0;
}

/* Lien d'évitement pour la navigation au clavier */
.saut-contenu {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--vert);
  color: #fff;
  padding: var(--e3) var(--e4);
  z-index: var(--z-modale);
  border-radius: 0 0 var(--rayon) 0;
}

.saut-contenu:focus {
  left: 0;
}

.visuellement-cache {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------- disposition */

.conteneur {
  width: 100%;
  max-width: var(--largeur-max);
  margin-inline: auto;
  padding-inline: var(--e4);
}

@media (min-width: 48rem) {
  .conteneur { padding-inline: var(--e5); }
}

.page { padding-block: var(--e5) var(--e8); }

.grille {
  display: grid;
  gap: var(--e5);
}

/* Grille éditoriale asymétrique : contenu principal + colonne latérale */
@media (min-width: 62rem) {
  .grille--editoriale {
    grid-template-columns: minmax(0, 2.2fr) minmax(16rem, 1fr);
    align-items: start;
  }
}

.grille--auto {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
}

.grille--trois,
.grille--deux {
  grid-template-columns: 1fr;
}

@media (min-width: 48rem) {
  .grille--deux  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grille--trois { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.pile > * + * { margin-top: var(--e4); }
.pile-serree > * + * { margin-top: var(--e2); }

.rangee {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
}

.rangee--espacee { justify-content: space-between; }
.rangee--enroulee { row-gap: var(--e2); }
.pousse-droite { margin-left: auto; }
.ml1 { margin-left: var(--e1); }
.ml2 { margin-left: var(--e3); }

/* ------------------------------------------------- en-tête et navigation */

.entete {
  position: sticky;
  top: 0;
  z-index: var(--z-entete);
  background: var(--surface);
  border-bottom: 1px solid var(--bordure);
  box-shadow: var(--ombre-1);
}

.entete__interieur {
  display: flex;
  align-items: center;
  gap: var(--e4);
  padding: var(--e3) var(--e4);
}

@media (min-width: 62rem) {
  .entete__interieur { padding-inline: var(--e5); }
}

/* Le logo et le nom du site */
.marque {
  display: inline-flex;
  align-items: center;
  gap: var(--e3);
  text-decoration: none;
  color: var(--texte);
  white-space: nowrap;
}

.marque__logo { width: 2.5rem; height: auto; flex: none; }

.marque__nom {
  font-family: var(--serif);
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.marque__ext { color: var(--vert); }

/* Sur très petit écran le nom reste affiché, simplement réduit. */
@media (max-width: 34rem) {
  .marque__logo { width: 2rem; }
  .marque__nom  { font-size: 1rem; }
  .entete__interieur { gap: var(--e2); padding-inline: var(--e3); }
}

@media (max-width: 26rem) {
  .marque__nom { font-size: 0.875rem; }
  .marque__ext { display: none; }
}

/* Champ de recherche de l'en-tête */
.recherche-entete {
  position: relative;
  flex: 1 1 12rem;
  max-width: 26rem;
}

.recherche-entete__loupe {
  position: absolute;
  left: var(--e3);
  top: 50%;
  transform: translateY(-50%);
  width: 1.05rem;
  height: 1.05rem;
  color: var(--texte-tenu);
  pointer-events: none;
}

.recherche-entete input[type="search"] {
  width: 100%;
  min-height: 2.5rem;
  padding: var(--e2) var(--e3) var(--e2) 2.4rem;
  border: 1px solid var(--bordure);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--texte);
  font: 400 0.9375rem/1.4 var(--sans);
}

.recherche-entete input[type="search"]:focus {
  outline: none;
  background: var(--surface);
  border-color: var(--vert);
  box-shadow: 0 0 0 3px var(--vert-clair);
}

/* Photo tirée au sort, avec la personne et son lien de parenté */
.moment {
  display: none;
  align-items: stretch;
  gap: var(--e3);
  padding: 0;
  overflow: hidden;
  border-radius: var(--rayon-lg);
  background: var(--surface);
  border: 1px solid var(--bordure);
  box-shadow: var(--ombre-1);
  text-decoration: none;
  color: inherit;
  max-width: 26rem;
  /*
   * Hauteur fixe : sans elle, une photo en portrait étirait la barre entière.
   * La marge basse négative laisse la vignette déborder sur les onglets sans
   * allonger l'en-tête ; le plan la fait passer au-dessus d'eux.
   */
  position: relative;
  z-index: 1;
  height: 6.25rem;
  align-self: center;
  margin-block: calc(var(--e2) * -1) calc(var(--e7) * -1);
  transition: box-shadow var(--transition), border-color var(--transition);
}

.moment:hover {
  box-shadow: var(--ombre-2);
  border-color: var(--vert);
  color: inherit;
}

@media (min-width: 68rem) { .moment { display: inline-flex; } }

/* Rectangulaire et large : la photo doit se voir, pas se deviner. */
.moment__image {
  width: 9rem;
  height: 100%;
  object-fit: cover;
  flex: none;
  background: var(--surface-2);
}

.moment__texte {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--e2) var(--e4) var(--e2) 0;
}

.moment__surtitre {
  display: block;
  margin-bottom: 0.15rem;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--vert);
}

.moment__legende {
  display: block;
  font-size: 0.6875rem;
  color: var(--texte-tenu);
  font-style: italic;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.moment__texte { min-width: 0; line-height: 1.25; }

.moment__nom {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.moment__lien {
  display: block;
  font-size: 0.75rem;
  color: var(--texte-tenu);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ----------------------------------------- messagerie dans la barre */

.messagerie { position: relative; }

.messagerie__declencheur {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  color: var(--texte-doux);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.messagerie__declencheur:hover { background: var(--surface-2); color: var(--vert-fonce); }
.messagerie__declencheur svg { width: 1.35rem; height: 1.35rem; }

/* La pastille chevauche l'icône, comme partout ailleurs. */
.messagerie__compteur {
  position: absolute;
  top: 0.15rem;
  right: 0.1rem;
  min-width: 1.05rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background: var(--erreur);
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.05rem;
  text-align: center;
}

.messagerie__calque {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + var(--e2));
  z-index: var(--z-menu);
  /* Élargi de 22 à 27 rem : les aperçus de conversation tenaient sur trois
     lignes chacun, ce qui rendait la liste illisible d'un coup d'œil. */
  width: 27rem;
  max-width: calc(100vw - 2rem);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-3);
  overflow: hidden;
}

.messagerie__bascule:checked ~ .messagerie__calque { display: block; }

.messagerie__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 0;
  padding: var(--e3) var(--e4);
  border-bottom: 1px solid var(--bordure);
  font-weight: 600;
}

.apercu {
  display: block;
  padding: var(--e3) var(--e4);
  border-bottom: 1px solid var(--bordure);
  color: inherit;
  text-decoration: none;
}

.apercu:hover { background: var(--surface-2); color: inherit; }

/* Un fil non lu se distingue par la couleur et le trait, pas seulement le gras. */
.apercu--nouveau {
  border-left: 3px solid var(--vert);
  background: var(--vert-clair);
}

.apercu__qui {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.apercu__texte {
  display: block;
  font-size: 0.8125rem;
  color: var(--texte-doux);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.apercu__date { display: block; margin-top: 2px; font-size: 0.6875rem; color: var(--texte-tenu); }

.messagerie__pied {
  display: block;
  padding: var(--e3);
  color: var(--vert-fonce);
  font-size: 0.875rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
}

.messagerie__pied:hover { background: var(--surface-2); }

/* ------------------------------------------------ menu de l'utilisateur */

.moi { position: relative; }

.moi__declencheur {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e1) var(--e2) var(--e1) var(--e1);
  border-radius: 999px;
  border: 1px solid var(--bordure);
  background: var(--surface);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.moi__declencheur:hover { background: var(--surface-2); border-color: var(--bordure-forte); }

.moi__portrait,
.moi__initiales {
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  flex: none;
  object-fit: cover;
}

.moi__initiales {
  display: grid;
  place-items: center;
  background: var(--vert-clair);
  color: var(--vert);
  font-size: 0.75rem;
  font-weight: 700;
}

.moi__prenom {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--texte);
  white-space: nowrap;
}

.moi__chevron {
  width: 1rem;
  height: 1rem;
  color: var(--texte-tenu);
  transition: transform var(--transition);
}

.moi__bascule:checked ~ .moi__declencheur .moi__chevron { transform: rotate(180deg); }

@media (max-width: 40rem) {
  .moi__prenom, .moi__chevron { display: none; }
  .moi__declencheur { padding: var(--e1); }
}

/* Le calque, ouvert par la case à cocher — aucun script nécessaire. */
.moi__calque {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + var(--e2));
  z-index: var(--z-menu);
  /* Deux centimètres de plus : « Revoir la visite guidée » et l'adresse
     électronique s'y repliaient sur deux lignes. */
  min-width: 17.5rem;
  padding: var(--e2);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-3);
}

.moi__bascule:checked ~ .moi__calque { display: block; }

.moi__entete {
  margin: 0;
  padding: var(--e3);
  border-bottom: 1px solid var(--bordure);
}

.moi__nom {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
}

.moi__mail {
  display: block;
  font-size: 0.8125rem;
  color: var(--texte-tenu);
  overflow: hidden;
  text-overflow: ellipsis;
}

.moi__lien {
  display: block;
  width: 100%;
  padding: var(--e3);
  margin-top: 2px;
  border: 0;
  border-radius: var(--rayon);
  background: none;
  color: var(--texte-doux);
  font: 400 0.9375rem/1.4 var(--sans);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.moi__lien:hover { background: var(--surface-2); color: var(--texte); }

.moi__lien--bouton {
  margin-top: var(--e2);
  border-top: 1px solid var(--bordure);
  border-radius: 0 0 var(--rayon) var(--rayon);
  color: var(--erreur);
}

/* Onglets de premier niveau */
.onglets {
  display: flex;
  gap: var(--e1);
  padding: 0 var(--e4);
  overflow-x: auto;
  scrollbar-width: none;
}

.onglets::-webkit-scrollbar { display: none; }

@media (min-width: 62rem) {
  .onglets { padding-inline: var(--e5); }
}

.onglets__lien {
  padding: var(--e3) var(--e4);
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--texte-doux);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 3px solid transparent;
  transition: color var(--transition), border-color var(--transition);
}

.onglets__lien:hover { color: var(--texte); }

.onglets__lien[aria-current="page"] {
  color: var(--vert);
  border-bottom-color: var(--vert);
}

/* Bouton hamburger : n'apparaît que quand la barre latérale est repliée */
.entete__hamburger {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  flex: none;
  border-radius: var(--rayon);
  border: 1px solid var(--bordure);
  color: var(--texte-doux);
  cursor: pointer;
}

.entete__hamburger svg { width: 1.25rem; height: 1.25rem; }
.entete__hamburger:hover { background: var(--surface-2); }

@media (min-width: 62rem) { .entete__hamburger { display: none; } }

/* ----------------------------------------------- cadre et barre latérale */

.cadre {
  display: grid;
  grid-template-columns: 1fr;
  max-width: var(--largeur-max);
  margin-inline: auto;
}

@media (min-width: 62rem) {
  .cadre { grid-template-columns: var(--largeur-barre) minmax(0, 1fr); }

  /* Sans barre latérale, le contenu prend toute la largeur. */
  .cadre--large { grid-template-columns: minmax(0, 1fr); }
}

.barre {
  display: none;
  padding: var(--e5) var(--e3);
  border-right: 1px solid var(--bordure);
  background: var(--surface);
}

/* Sur mobile, la barre se déplie sous l'en-tête via la case à cocher. */
.barre-bascule:checked ~ .cadre .barre { display: block; }

@media (min-width: 62rem) {
  .barre {
    display: block;
    position: sticky;
    top: 7.5rem;
    align-self: start;
    max-height: calc(100dvh - 7.5rem);
    overflow-y: auto;
  }
}

.barre__titre {
  margin: 0 0 var(--e3);
  padding-left: var(--e3);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--texte-tenu);
}

.barre__liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }

.barre__lien {
  display: block;
  padding: var(--e3);
  border-radius: var(--rayon);
  font-size: 0.9375rem;
  color: var(--texte-doux);
  text-decoration: none;
  transition: background var(--transition), color var(--transition);
}

.barre__lien:hover { background: var(--surface-2); color: var(--texte); }

.barre__lien[aria-current="page"] {
  background: var(--vert);
  color: #fff;
  font-weight: 600;
  box-shadow: var(--ombre-1);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .barre__lien[aria-current="page"] { color: #10221a; }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .barre__lien[aria-current="page"] { color: #10221a; }


.barre__pied {
  margin-top: var(--e5);
  padding: var(--e3);
  border-top: 1px solid var(--bordure);
}

.barre__moi {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--texte-tenu);
}

.contenu {
  min-width: 0;
  padding: var(--e5) var(--e4) var(--e8);
}

@media (min-width: 62rem) {
  .contenu { padding: var(--e6) var(--e6) var(--e8); }
}

/* ------------------------------------------------------------------ cartes */

.carte {
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-xl);
  padding: var(--e5);
  box-shadow: var(--ombre-1);
}

.carte__titre {
  font-size: 1.0625rem;
  font-family: var(--sans);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--texte-tenu);
  margin-bottom: var(--e4);
}

/* ------------------------------------------------- carte « personne » */

.personne {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding: var(--e3);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  /* La bordure change de couleur, pas la taille : aucun décalage au survol. */
  transition: border-color var(--transition), box-shadow var(--transition), background var(--transition);
}

.personne:hover {
  border-color: var(--vert);
  box-shadow: var(--ombre-2);
  background: var(--surface);
}

.personne__portrait {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
  flex-shrink: 0;
}

.personne__initiales {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--vert-clair);
  color: var(--vert-fonce);
  font-weight: 600;
  font-size: 0.9375rem;
  flex-shrink: 0;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .personne__initiales { background: var(--surface-2); color: var(--vert); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .personne__initiales { background: var(--surface-2); color: var(--vert); }


.personne__texte { min-width: 0; }

.personne__nom {
  font-weight: 600;
  font-size: 0.9375rem;
  line-height: 1.3;

  /* « break-word » et non « anywhere ». La différence ne se voit pas sur une
     boîte à largeur fixe, et elle est décisive partout ailleurs : « anywhere »
     autorise la coupure n'importe où ET ramène la largeur minimale du texte à
     UN CARACTÈRE. Dans une cellule de tableau, la colonne se laissait donc
     écraser jusqu'à rendre « Wilthien » sur deux lignes, coupé après le « e ».

     « break-word » coupe aussi les mots trop longs pour tenir, mais la colonne
     reste au moins aussi large que le plus long mot. Les cartes d'arbre gardent
     « anywhere », elles : leur largeur est fixée, rien ne peut s'y écraser. */
  overflow-wrap: break-word;
}

.personne__meta {
  font-size: 0.8125rem;
  color: var(--texte-tenu);
  line-height: 1.4;
}

.personne--compacte { padding: var(--e2); }
.personne--compacte .personne__portrait,
.personne--compacte .personne__initiales { width: 2.25rem; height: 2.25rem; font-size: 0.8125rem; }

/* Repère de sexe, doublé d'un libellé textuel : la couleur n'est jamais
   le seul porteur d'information. */
.personne--f { border-left: 3px solid var(--vert); }
.personne--m { border-left: 3px solid var(--bleu); }

/* --------------------------------------------------------------- arbre */

.arbre {
  overflow-x: auto;
  padding-bottom: var(--e4);
  -webkit-overflow-scrolling: touch;
}

.arbre__niveaux {
  display: flex;
  gap: var(--e5);
  align-items: center;
  min-width: min-content;
}

.arbre__colonne {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  gap: var(--e2);
  min-width: 12.5rem;
}

.arbre__couple {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  padding-left: var(--e4);
  position: relative;
}

/* Traits de filiation dessinés en CSS, sans images. */
.arbre__couple::before {
  content: "";
  position: absolute;
  left: 0;
  top: 25%;
  bottom: 25%;
  width: 2px;
  background: var(--bordure-forte);
}

.arbre__couple::after {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: var(--e3);
  height: 2px;
  background: var(--bordure-forte);
  transform: translateX(-100%);
}

.arbre__vide {
  display: grid;
  place-items: center;
  min-height: 3.25rem;
  padding: var(--e2);
  border: 1px dashed var(--bordure-forte);
  border-radius: var(--rayon);
  color: var(--texte-tenu);
  font-size: 0.8125rem;
  font-style: italic;
}

/* Arbre familial : parents au-dessus, fratrie et enfants en dessous */
.foyer {
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  padding: var(--e4);
  background: var(--surface-2);
}

.foyer__couple {
  display: grid;
  gap: var(--e3);
  margin-bottom: var(--e4);
}

/* LES DEUX CARTES CÔTE À CÔTE, L'UNION EN DESSOUS.

   La pastille « Mariage · date · lieu » occupait la colonne du milieu, en
   « auto » et sans césure : elle prenait donc tout ce que son texte demandait
   — 199 px sur 516, quarante pour cent du bloc — et les deux cartes se
   partageaient le reste. Sur « Aline Caumont La Force (née Donsimoni) », le
   nom tombait sur SEPT lignes, un mot par ligne.

   La placer en dessous rend aux cartes la moitié de la largeur chacune, et le
   même nom tient sur deux lignes. Le bloc grandit d'une cinquantaine de
   pixels ; c'est le prix, et il est bien inférieur aux cinq lignes gagnées.

   Elle ne perd rien à descendre : elle dit qui est marié à qui, et deux
   cartes voisines le disent déjà. Mesuré plutôt que choisi — trois autres
   dispositions ont été essayées, aucune ne descend en dessous de cinq lignes. */
@media (min-width: 40rem) {
  .foyer__couple {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }

  /* Placée explicitement, donc retirée du placement automatique : les deux
     cartes remplissent alors la première rangée d'elles-mêmes. */
  .foyer__couple > .foyer__lien {
    grid-column: 1 / -1;
    grid-row: 2;
    justify-self: center;
    margin-top: var(--e2);
  }
}

.foyer__lien {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: var(--e1) var(--e3);
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--bordure);
  font-size: 0.8125rem;
  color: var(--texte-doux);
  white-space: nowrap;
}

.foyer__enfants {
  padding-top: var(--e4);
  border-top: 1px dashed var(--bordure-forte);
}

/* Descendance : liste imbriquée avec traits de rattachement */
.descendance,
.descendance ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.descendance ul {
  margin-left: var(--e4);
  padding-left: var(--e5);
  border-left: 2px solid var(--bordure);
}

.descendance li { margin-block: var(--e2); position: relative; }

.descendance ul > li::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--e5));
  top: 1.5rem;
  width: var(--e4);
  height: 2px;
  background: var(--bordure);
}

/* -------------------------- arbre d'ascendance dans un viewport « télé »
 *
 * Le sujet est en bas, ses ancêtres remontent au-dessus. La structure DOM
 * est un arbre récursif : chaque .tv-branche empile un bloc d'ancêtres
 * (deux .tv-branche filles côte à côte) puis sa propre carte. Comme le
 * sujet est la racine, ses cartes filles se dessinent naturellement
 * au-dessus et sa carte à lui ferme l'arbre : il apparaît en bas.
 *
 * Les traits sont dessinés en CSS, jamais en image ni en style inline
 * (bloqués par la CSP). Deux pseudo-éléments par carte suffisent : une
 * barre horizontale entre les deux parents, une verticale qui descend
 * vers l'enfant.
 */

/* -------- curseur de générations (asc à gauche, sujet au centre, desc à droite) */

/* Curseur double « ascendance | sujet | descendance » : deux inputs range
   encadrent une pastille centrale. Chaque piste a une longueur proportionnelle
   à la profondeur réellement disponible (via data-tv-flex, appliqué en JS).
   Le remplissage visible utilise un dégradé linéaire piloté par --pct-asc /
   --pct-desc (mis à jour à chaque input), et l'ascendance coule de droite
   à gauche (RTL) pour que « plus loin du sujet = plus loin dans le temps ». */
.tv-curseur { padding: var(--e3) var(--e4); }

.tv-curseur__ligne {
  display: flex;
  align-items: center;
  gap: var(--e2);
}

.tv-curseur__piste {
  position: relative;
  min-width: 10rem;   /* garantit un label lisible même quand data-tv-flex
                         donne une part minuscule à cette moitié */
  --pct: 50;
}

.tv-curseur__label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--texte);
  margin-bottom: 0.35rem;
  white-space: nowrap;                    /* jamais de wrap sur plusieurs lignes */
  overflow: hidden;
  text-overflow: ellipsis;
}
.tv-curseur__label b {
  color: var(--vert-fonce);
  font-variant-numeric: tabular-nums;
}

.tv-curseur__piste--asc  .tv-curseur__label { text-align: right; padding-right: 0.25rem; }
.tv-curseur__piste--desc .tv-curseur__label { text-align: left;  padding-left:  0.25rem; }

/* Rails posés directement sur la piste : ::before = rail gris de fond,
   ::after = rail vert de progression, largeur pilotée par --pct (mis à
   jour par app.js à chaque input). L'ascendance remplit de droite à
   gauche pour se lire « plus loin du sujet = plus loin dans le temps ». */
.tv-curseur__piste {
  min-height: 2.5rem;
}
.tv-curseur__piste::before,
.tv-curseur__piste::after {
  content: "";
  position: absolute;
  left: 0.5rem;
  right: 0.5rem;
  bottom: 0.85rem;
  height: 4px;
  border-radius: 9999px;
  pointer-events: none;
}
.tv-curseur__piste::before {
  background: var(--bordure);
  box-shadow: inset 0 0 0 1px var(--bordure);
}
.tv-curseur__piste--desc::after {
  background: var(--texte);
  right: auto;
  width: calc((100% - 1rem) * var(--pct) / 100);
}
.tv-curseur__piste--asc::after {
  background: var(--texte);
  left: auto;
  width: calc((100% - 1rem) * var(--pct) / 100);
}
.tv-curseur__piste--vide::before,
.tv-curseur__piste--vide::after { display: none; }

/* Input range : on retire tout le rendu natif (piste + thumb WebKit +
   piste Firefox + accent-color) et on ne re-fait paraître QUE le thumb.
   background:none + accent-color:transparent tuent le fill bleu par défaut. */
.tv-curseur__input {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  position: relative;
  width: 100%;
  height: 1.75rem;
  margin: 0;
  padding: 0;
  cursor: grab;
  background: none;
  accent-color: transparent;
  z-index: 2;
}
.tv-curseur__input:active { cursor: grabbing; }
.tv-curseur__input:focus  { outline: none; }
.tv-curseur__input--asc   { direction: rtl; }

/* Thumb style Base UI : 16 px blanc, outline gris fin, outline bleu au
   focus. Recentrage vertical par margin-top négatif (rail 4 px). */
.tv-curseur__input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 9999px;
  background: #fff;
  outline: 1px solid var(--bordure-forte);
  border: 0;
  box-shadow: 0 1px 2px rgba(0,0,0,.08);
  cursor: grab;
  transition: outline-color 120ms ease;
  margin-top: -6px;
}
.tv-curseur__input::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 9999px;
  background: #fff;
  outline: 1px solid var(--bordure-forte);
  border: 0;
  box-shadow: 0 1px 2px rgba(0,0,0,.08);
  cursor: grab;
}
.tv-curseur__input:focus-visible::-webkit-slider-thumb { outline: 2px solid #1e40af; }
.tv-curseur__input:focus-visible::-moz-range-thumb     { outline: 2px solid #1e40af; }
/* Piste native masquée : Chromium/WebKit et Firefox. */
.tv-curseur__input::-webkit-slider-runnable-track { background: transparent; height: 1.75rem; }
.tv-curseur__input::-moz-range-track { background: transparent; height: 1.75rem; }

.tv-curseur__marques {
  display: flex;
  justify-content: space-between;
  padding: 2px 8px 0;
  pointer-events: none;
}
.tv-curseur__marques span {
  display: block;
  width: 2px;
  height: 4px;
  background: var(--bordure-forte);
  border-radius: 1px;
}

.tv-curseur__pivot {
  flex: 0 0 auto;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  border: 2px solid var(--vert);
  box-shadow: 0 0 0 3px var(--vert-clair);
  overflow: hidden;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  z-index: 1;
}
.tv-curseur__pivot img { width: 100%; height: 100%; object-fit: cover; }
.tv-curseur__pivot-initiales {
  font-weight: 600;
  color: var(--vert-fonce);
  font-size: 0.9375rem;
}

.tv-curseur__piste--vide {
  opacity: .35;
  height: 6px;
  background: repeating-linear-gradient(90deg, var(--bordure) 0 4px, transparent 4px 8px);
  border-radius: 999px;
  align-self: center;
  margin-top: 1.35rem;
}

/* Repli sans-JS : le bouton « Afficher » est masqué quand JavaScript est
   présent (une classe est posée sur <html> par app.js). Sinon il reste là
   pour que le formulaire soit soumissible sans script. */
.tv-curseur__actions {
  display: flex;
  gap: var(--e2);
  margin-top: var(--e2);
  justify-content: flex-end;
}
.tv-js .tv-curseur__actions .tv-curseur__submit { display: none; }

/* Ruban « revenir en plein écran » posé après une navigation qui a
   sorti l'utilisateur du mode plein écran. Un clic dedans déclenche
   la demande — le geste utilisateur est indispensable à la
   Fullscreen API. */
.tv-ruban-full {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  padding: var(--e2) var(--e3);
  border: 0;
  border-radius: var(--rayon);
  background: var(--vert);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--ombre-2);
  animation: tv-ruban-in 260ms ease-out;
}

@keyframes tv-ruban-in {
  from { opacity: 0; transform: translate(-50%, -8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tv-ruban-full { color: #10221a; }
}
:root[data-theme="dark"] .tv-ruban-full { color: #10221a; }

@media (max-width: 40rem) {
  .tv-curseur__pivot { width: 2.25rem; height: 2.25rem; }
  .tv-curseur__label { font-size: 0.6875rem; }
}

.tv-arbre {
  --tv-carte-l: 11rem;
  --tv-carte-h: 4.75rem;
  --tv-gap-h:   1.25rem;
  --tv-gap-v:   2rem;
  --tv-trait:   var(--bordure-forte);

  position: relative;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-lg);
  background: var(--surface-2);
  box-shadow: var(--ombre-1);
  overflow: hidden;
  isolation: isolate;
  contain: layout paint;
}

/* Le viewport, c'est ce qu'on voit — la « télé ». Sans JS il défile
   comme n'importe quel conteneur ; avec JS, la scène est déplacée par
   transform et le défilement natif est coupé (touch-action: none). */
.tv-arbre__viewport {
  height: min(72vh, 640px);
  overflow: auto;
  cursor: grab;
  background:
    radial-gradient(circle at 1px 1px, var(--bordure) 1px, transparent 1.5px);
  background-size: 24px 24px;
  background-position: center;
  -webkit-overflow-scrolling: touch;
}

.tv-arbre__viewport:focus-visible {
  outline: 2px solid var(--vert);
  outline-offset: -2px;
}

/* Piloté par JS quand le déplacement est en cours. */
.tv-arbre__viewport.tv-glisse         { cursor: grabbing; }
.tv-arbre__viewport.tv-pilote         { overflow: hidden; touch-action: none; }
.tv-arbre__viewport.tv-pilote .tv-arbre__scene { will-change: transform; }

/* La scène : on lui applique un transform translate + scale depuis JS.
   L'origine est fixée en haut-gauche pour rendre le calcul simple. */
.tv-arbre__scene {
  display: inline-block;
  padding: var(--e5);
  transform-origin: 0 0;
  user-select: none;
  -webkit-user-select: none;
}

/* -------- l'arbre récursif */

.tv-branche {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Espace horizontal entre deux branches sœurs : appliqué par la mère. */
}

.tv-branche__ancetres {
  display: flex;
  align-items: flex-end;
  gap: var(--tv-gap-h);
  position: relative;
}

/* Les deux sous-branches parentales prennent des parts égales, quelle que
   soit la profondeur qu'elles portent. C'est ce qui garantit que le père
   est à 25 % et la mère à 75 % de leur bloc d'ancêtres, et donc que le
   bracket horizontal tombe sur leurs centres. */
.tv-branche__ancetres > .tv-branche {
  flex: 1 1 0;
  min-width: 0;
}

.tv-branche__moi {
  position: relative;
}

/* Quand une branche a des ancêtres au-dessus, on réserve la place des
   traits en bas de __ancetres et on les dessine à cet endroit — pas sur
   __moi, qui est bien plus étroit et ferait un bracket ridicule.
   :has() : la géométrie découle de la structure, autant que le CSS la lise. */
.tv-branche:has(> .tv-branche__ancetres) > .tv-branche__ancetres {
  padding-bottom: var(--tv-gap-v);
}

/* Barre horizontale reliant les centres des deux parents. */
.tv-branche:has(> .tv-branche__ancetres) > .tv-branche__ancetres::before {
  content: "";
  position: absolute;
  bottom: var(--tv-gap-v);
  left: 25%;
  right: 25%;
  height: 2px;
  background: var(--tv-trait);
}

/* Descente verticale depuis le milieu du bracket vers la carte enfant. */
.tv-branche:has(> .tv-branche__ancetres) > .tv-branche__ancetres::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 2px;
  height: var(--tv-gap-v);
  background: var(--tv-trait);
  transform: translateX(-50%);
}

/* -------- descendance : structure top-down, branchement variable

   La descendance n'est pas binaire — quelqu'un peut avoir zéro, un ou
   sept enfants. La structure DOM suit : un .tv-desc-noeud contient sa
   carte, puis un flex-row .tv-desc-noeud__enfants pour ses enfants
   directs. Chaque enfant est à son tour un .tv-desc-noeud récursif.

   Le bracket qui joint les enfants à leur parent est dessiné en
   pseudo-éléments sur les cartes enfants : une descente verticale, et
   une barre horizontale qui court dans les gouttières entre voisins.
   Les enfants extrêmes tronquent la moitié externe pour finir sur leur
   propre centre — la connexion pointe alors bien vers le centre du
   groupe, quel que soit le nombre d'enfants. */

.tv-desc-noeud {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.tv-desc-noeud__moi {
  position: relative;
}

.tv-desc-noeud__enfants {
  display: flex;
  align-items: flex-start;
  gap: var(--tv-gap-h);
  padding-top: var(--tv-gap-v);
}

.tv-desc-noeud__enfants > .tv-desc-noeud {
  position: relative;
}

/* Barre horizontale entre les enfants, dans la gouttière au-dessus.
   Pour les enfants du milieu : elle traverse toute leur largeur plus
   la moitié des gouttières adjacentes. */
.tv-desc-noeud__enfants > .tv-desc-noeud::before {
  content: "";
  position: absolute;
  top: calc(-1 * var(--tv-gap-v));
  left: calc(-1 * var(--tv-gap-h) / 2);
  right: calc(-1 * var(--tv-gap-h) / 2);
  height: 2px;
  background: var(--tv-trait);
}

/* Le premier enfant démarre son bracket à son propre centre. */
.tv-desc-noeud__enfants > .tv-desc-noeud:first-child::before {
  left: 50%;
}

/* Le dernier enfant arrête son bracket à son propre centre. */
.tv-desc-noeud__enfants > .tv-desc-noeud:last-child::before {
  right: 50%;
}

/* Enfant unique : pas de bracket horizontal, juste la descente. */
.tv-desc-noeud__enfants > .tv-desc-noeud:only-child::before {
  display: none;
}

/* Descente verticale du bracket vers le haut de la carte enfant. */
.tv-desc-noeud__enfants > .tv-desc-noeud::after {
  content: "";
  position: absolute;
  top: calc(-1 * var(--tv-gap-v));
  left: 50%;
  width: 2px;
  height: var(--tv-gap-v);
  background: var(--tv-trait);
  transform: translateX(-50%);
}

/* -------- une carte

   La carte n'est plus un lien : c'est un conteneur avec deux liens à
   l'intérieur — un lien-zone en overlay qui couvre tout et mène à
   l'arbre de la personne, et un petit lien « Fiche » posé au-dessus qui
   mène à sa fiche. La CSS ci-dessous les positionne et prévient l'effet
   d'un lien-dans-un-lien (interdit en HTML) tout en gardant la carte
   entièrement cliquable. */

.tv-carte {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--e2);
  width: var(--tv-carte-l);
  min-height: var(--tv-carte-h);
  padding: var(--e2) var(--e3);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
  box-shadow: var(--ombre-1);
}

.tv-carte:hover,
.tv-carte:focus-within {
  border-color: var(--vert);
  box-shadow: var(--ombre-2);
  transform: translateY(-1px);
  color: var(--texte);
}

/* Le lien-zone : couvre toute la carte, mène à l'arbre de la personne.
   Aucun contenu visuel, aucun texte : c'est un rectangle cliquable. */
.tv-carte__zone {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  text-decoration: none;
}

.tv-carte__zone:focus-visible {
  outline: 2px solid var(--vert);
  outline-offset: -2px;
}

/* Le lien « Fiche » : petite pastille en bas à droite, remontée au-dessus
   du lien-zone pour capter son propre clic sans ambiguïté. */
.tv-carte__fiche {
  position: absolute;
  bottom: 3px;
  right: 4px;
  z-index: 2;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--texte-doux);
  text-decoration: none;
  padding: 3px 8px;
  border-radius: 4px;
  background: var(--surface-2);
  border: 1px solid var(--bordure);
  line-height: 1.1;
  opacity: .8;
  transition: opacity var(--transition), color var(--transition), background var(--transition), border-color var(--transition);
  -webkit-tap-highlight-color: transparent;
}

/* Cible tactile élargie : un carré invisible autour du bouton, qui capte
   les doigts imprécis sans agrandir le visuel. */
.tv-carte__fiche::before {
  content: "";
  position: absolute;
  inset: -6px;
}

.tv-carte:hover .tv-carte__fiche,
.tv-carte:focus-within .tv-carte__fiche {
  opacity: 1;
}

.tv-carte__fiche:hover,
.tv-carte__fiche:focus-visible {
  color: var(--vert-fonce);
  background: var(--vert-clair);
  border-color: var(--vert);
  outline: 0;
  opacity: 1;
}

/* Sur tactile, le lien est toujours au maximum de lisibilité. */
@media (hover: none) {
  .tv-carte__fiche { opacity: 1; }
}

.tv-carte--m { border-left: 3px solid var(--bleu); }
.tv-carte--f { border-left: 3px solid var(--abricot); }

.tv-carte--moi {
  border: 2px solid var(--vert);
  box-shadow: 0 0 0 3px var(--vert-clair);
}

/* Surbrillance temporaire : quand la recherche pointe une carte, un halo
   vert pulse trois secondes autour d'elle pour attirer l'œil. La CSS
   s'empile par-dessus le cadre existant du sujet — la pulsation reste
   visible même sur .tv-carte--moi. */
@keyframes tv-carte-surligne {
  0%   { box-shadow: 0 0 0 0   rgba(46, 125, 79, .55), 0 0 0 0 rgba(46, 125, 79, .35); }
  40%  { box-shadow: 0 0 0 6px rgba(46, 125, 79, .00), 0 0 0 3px rgba(46, 125, 79, .85); }
  100% { box-shadow: 0 0 0 6px rgba(46, 125, 79, .00), 0 0 0 3px rgba(46, 125, 79, .00); }
}

.tv-carte--surligne {
  animation: tv-carte-surligne 3s ease-out 1;
  animation-fill-mode: forwards;
  z-index: 4;                        /* passe devant le halo permanent du sujet */
  border-color: var(--vert) !important;
}

.tv-carte--vide {
  cursor: default;
  border-style: dashed;
  border-color: var(--bordure-forte);
  background: transparent;
  color: var(--texte-tenu);
  box-shadow: none;
}

.tv-carte--vide:hover,
.tv-carte--vide:focus-visible {
  border-color: var(--bordure-forte);
  box-shadow: none;
  transform: none;
}

.tv-carte__portrait {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
}

.tv-carte__portrait--initiales {
  display: grid;
  place-items: center;
  font-weight: 600;
  color: var(--vert-fonce);
  background: var(--vert-clair);
}

.tv-carte__portrait--anonyme {
  display: inline-block;
  color: var(--texte-tenu);
}

.tv-carte__portrait--anonyme svg {
  width: 100%;
  height: 100%;
  display: block;
}

.tv-anonyme__fond  { fill: var(--surface-2); }
.tv-anonyme__trait { fill: currentColor; opacity: .55; }

.tv-carte__texte {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.tv-carte__nom {
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--texte);
  /* Autorise le retour à la ligne pour ne rien tronquer : les noms longs
     s'étalent sur deux lignes plutôt que d'être coupés par une ellipse.
     La carte s'étire verticalement en conséquence. */
  overflow-wrap: anywhere;
  word-break: break-word;
  hyphens: auto;
  max-width: 8rem;
}

.tv-carte--vide .tv-carte__nom {
  font-style: italic;
  color: var(--texte-tenu);
}

.tv-carte__annees {
  font-size: 0.75rem;
  color: var(--texte-tenu);
}

/* -------- contrôles flottants */

.tv-arbre__ctrl {
  position: absolute;
  z-index: 2;
  display: none;              /* révélé par JS via classe sur .tv-arbre */
  gap: var(--e1);
  padding: var(--e1);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: 999px;
  box-shadow: var(--ombre-2);
}

.tv-arbre.tv-pret .tv-arbre__ctrl { display: inline-flex; }

.tv-arbre__ctrl--zoom { left: var(--e3);  bottom: var(--e3); }
.tv-arbre__ctrl--rech { right: var(--e3); top:    var(--e3); }

.tv-btn {
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--texte);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background var(--transition), color var(--transition);
}

.tv-btn:hover  { background: var(--surface-2); color: var(--vert-fonce); }
.tv-btn:active { background: var(--vert-clair); }

.tv-btn:focus-visible {
  outline: 2px solid var(--vert);
  outline-offset: 2px;
}

.tv-btn svg {
  width: 1.125rem;
  height: 1.125rem;
  display: block;
}

/* Le bouton plein écran porte deux icônes empilées ; on ne montre que
   celle qui décrit la CIBLE du clic (l'inverse de l'état courant).
   Les sélecteurs doublent la classe pour battre la spécificité du
   « .tv-btn svg { display: block } » défini plus haut ; sans ça les
   deux icônes s'affichaient en même temps. */
.tv-btn .tv-btn__icone--sortir { display: none; }
.tv-arbre.tv-plein-ecran .tv-btn .tv-btn__icone--entrer { display: none; }
.tv-arbre.tv-plein-ecran .tv-btn .tv-btn__icone--sortir { display: block; }

/* En plein écran, le .tv-arbre remplit tout : le viewport prend
   toute la hauteur disponible, les coins arrondis disparaissent. */
.tv-arbre:fullscreen,
.tv-arbre.tv-plein-ecran {
  width: 100vw;
  height: 100vh;
  border-radius: 0;
  border: 0;
}

.tv-arbre:fullscreen .tv-arbre__viewport,
.tv-arbre.tv-plein-ecran .tv-arbre__viewport {
  height: 100vh;
  border-radius: 0;
}

/* -------- dialogue de recherche */

.tv-arbre__dialog {
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-lg);
  padding: 0;
  background: var(--surface);
  color: var(--texte);
  box-shadow: var(--ombre-3);
  max-width: min(28rem, 92vw);
  width: 100%;
}

.tv-arbre__dialog::backdrop {
  background: rgba(0, 0, 0, .35);
  backdrop-filter: blur(2px);
}

.tv-arbre__dialog-form {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  padding: var(--e3);
  min-height: 8rem;
  max-height: min(70vh, 32rem);
}

.tv-arbre__dialog-tete {
  display: flex;
  align-items: center;
  gap: var(--e2);
}

.tv-arbre__dialog-tete input {
  flex: 1;
  min-width: 0;
  padding: var(--e2) var(--e3);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  background: var(--surface-2);
  color: var(--texte);
  font: inherit;
}

/* Filtres et tri, version « chips segmentés » — utilisés dans le dialogue
   de recherche ET dans la barre persistante au-dessus de l'arbre. Un seul
   composant .tv-chips pour les deux, ça garantit l'homogénéité. */
.tv-arbre__dialog-outils {
  display: grid;
  gap: var(--e2);
  padding: var(--e2) 0;
  border-bottom: 1px solid var(--bordure);
}
.tv-arbre__dialog-filtres {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
}

/* Un groupe .tv-chips doit rester sur UNE seule ligne (titre + pilule).
   Sans flex-shrink:0 le conteneur parent (.tv-filtres) le comprime et le
   titre passe à la ligne au-dessus des boutons — c'est ce que le premier
   jet faisait. On empêche aussi le retour à la ligne à l'intérieur. */
.tv-chips {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: 0 0 auto;
  white-space: nowrap;
}
.tv-chips__titre {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--texte-doux);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.tv-chips__groupe {
  display: inline-flex;
  padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--bordure);
  border-radius: 999px;
  flex-shrink: 0;
}
.tv-chips__bt {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--texte-doux);
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.tv-chips__bt:hover { color: var(--texte); }
.tv-chips__bt[aria-pressed="true"] {
  background: var(--vert);
  color: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.15);
}

/* Barre de filtres persistante posée au-dessus du viewport TV. */
.tv-filtres {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.5rem 0.75rem;
  margin-bottom: var(--e2);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
}

/* Mini-curseur flottant, injecté par app.js dans chaque .tv-arbre. Il
   n'apparaît qu'en plein écran (les grands sliders sortent alors du
   cadre) et suit le pointeur souris (positionné en absolu dans .tv-arbre
   par le JS). Contenu : deux petites plages type range asc/desc,
   étiquettes tabulaires. Cache par défaut, visible via .tv-mini-curseur--visible
   posé au mousemove et retiré après ~2 s d'inactivité. */
.tv-mini-curseur {
  position: absolute;
  z-index: 8;
  display: none;
  min-width: 200px;
  padding: 0.5rem 0.65rem;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-2);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  pointer-events: auto;
  font-size: 0.75rem;
  color: var(--texte);
  user-select: none;
  transition: opacity 120ms ease;
}
/* En plein écran le mini est TOUJOURS affiché — plus de suivi souris.
   Il s'ancre à côté de la barre de filtres (position posée par app.js).
   Hors plein écran, jamais visible. */
.tv-arbre.tv-plein-ecran > .tv-mini-curseur {
  display: block;
}
.tv-mini-curseur__ligne {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 2px 0;
}
.tv-mini-curseur__lib {
  min-width: 3.5rem;
  font-weight: 600;
  color: var(--texte-doux);
  font-variant-numeric: tabular-nums;
}
.tv-mini-curseur input[type="range"] {
  flex: 1 1 auto;
  height: 1rem;
  accent-color: var(--vert);
  cursor: grab;
}
.tv-mini-curseur input[type="range"]:active { cursor: grabbing; }

/* En plein écran, la barre est ré-ancrée dans .tv-arbre par app.js. Elle
   se pose alors en overlay flottant, semi-opaque, centrée en haut. */
.tv-arbre.tv-plein-ecran > .tv-filtres {
  position: absolute;
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: var(--ombre-2);
  margin-bottom: 0;
  max-width: calc(100% - 6rem);
}

@media (max-width: 40rem) {
  .tv-filtres { padding: 0.4rem 0.5rem; gap: 0.35rem 0.6rem; }
  .tv-chips__bt { padding: 0.2rem 0.55rem; font-size: 0.75rem; }
  .tv-chips__titre { font-size: 0.6875rem; }
}

/* Filtres visuels appliqués à l'arbre lui-même.

   Sexe et vivant : estompe douce (opacité + grayscale). Le sujet
   participe au filtre (si je filtre « en vie » et que je suis mort,
   ma carte s'estompe aussi) — c'est ce que le comportement précédent
   « on garde toujours le sujet visible » cassait, notamment sur vivant.

   Côté : pruning DUR (display:none) de la branche opposée. Le sujet
   n'a pas de data-tv-cote, on doit donc l'exempter explicitement,
   sinon on le fait disparaître aussi. La descendance et la carte moi
   restent visibles (une descendance n'a pas de côté, elle est de moi). */

.tv-filtre-sexe-h        .tv-carte:not([data-tv-sexe="h"]),
.tv-filtre-sexe-f        .tv-carte:not([data-tv-sexe="f"]),
.tv-filtre-vivant-oui    .tv-carte:not([data-tv-vivant="1"]),
.tv-filtre-vivant-non    .tv-carte:not([data-tv-vivant="0"]) {
  opacity: .18;
  filter: grayscale(0.8);
  transition: opacity var(--transition), filter var(--transition);
}

/* Pruning par profondeur : le slider ajoute cette classe sur les branches
   qui dépassent la génération demandée. display:none pour que le layout
   se réduise sans laisser de traits pendants. Idem pour la descendance. */
.tv-branche.tv-branche--masquee,
.tv-desc-noeud.tv-desc-noeud--masque {
  display: none !important;
}
/* Si une branche __ancetres n'a plus que des enfants masqués, on cache
   aussi son bracket horizontal/vertical (sinon on garde un trait qui
   pendouille au-dessus de la carte moi). */
.tv-branche__ancetres:has(> .tv-branche:not(.tv-branche--masquee))::before,
.tv-branche__ancetres:has(> .tv-branche:not(.tv-branche--masquee))::after {
  /* rien : au moins une branche visible → on garde les traits */
}
.tv-branche__ancetres:not(:has(> .tv-branche:not(.tv-branche--masquee)))::before,
.tv-branche__ancetres:not(:has(> .tv-branche:not(.tv-branche--masquee)))::after {
  display: none;
}

/* Pruning côté : on masque physiquement (display:none) toute .tv-branche
   dont l'attribut data-tv-cote-branche vaut le côté opposé au filtre.
   L'attribut est posé côté PHP directement sur la branche (les :has()
   imbriqués ne sont pas supportés en CSS — d'où cet aller-retour). */
.tv-filtre-cote-pat .tv-branche[data-tv-cote-branche="maternel"],
.tv-filtre-cote-mat .tv-branche[data-tv-cote-branche="paternel"] {
  display: none !important;
}

/* Trait horizontal reliant les deux parents : masqué dès qu'une branche
   enfant est du côté opposé (donc masquée). On garde le trait vertical
   (::after) qui relie le seul parent visible à la carte enfant. */
.tv-filtre-cote-pat .tv-branche__ancetres:has(> .tv-branche[data-tv-cote-branche="maternel"])::before,
.tv-filtre-cote-mat .tv-branche__ancetres:has(> .tv-branche[data-tv-cote-branche="paternel"])::before {
  display: none;
}

.tv-arbre__dialog-tete input:focus-visible {
  outline: 2px solid var(--vert);
  outline-offset: 1px;
  border-color: var(--vert);
}

.tv-arbre__dialog-liste {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  border-top: 1px solid var(--bordure);
  border-radius: var(--rayon);
}

.tv-arbre__dialog-liste li { margin: 0; }

.tv-arbre__dialog-liste button {
  width: 100%;
  padding: var(--e2) var(--e3);
  border: 0;
  background: transparent;
  color: var(--texte);
  text-align: left;
  font: inherit;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: var(--e2);
  border-bottom: 1px solid var(--bordure);
}

.tv-arbre__dialog-liste button:hover,
.tv-arbre__dialog-liste button:focus-visible {
  background: var(--vert-clair);
  color: var(--vert-fonce);
  outline: 0;
}

.tv-arbre__dialog-liste .tv-arbre__gen {
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--texte-tenu);
}

.tv-arbre__dialog-vide {
  margin: var(--e3) 0 0;
  color: var(--texte-tenu);
  text-align: center;
}

.tv-arbre__noscript {
  padding: var(--e2) var(--e3);
  background: var(--surface);
  border-top: 1px solid var(--bordure);
}

/* -------- bascules de vues (ascendance / descendance)

   Deux cases à cocher au-dessus des viewports, alignées horizontalement.
   Sans style particulier ce serait juste une paire de checkboxes ; on
   soigne l'espacement, on remonte le libellé de la case désactivée en
   discret, on aligne aux mêmes lignes que les autres rangees du site. */
.tv-bascule {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
  padding: var(--e2) var(--e3);
  margin: 0 0 var(--e3);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
}

.tv-bascule label {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  font-size: 0.9375rem;
  cursor: pointer;
}

.tv-bascule label:has(input[disabled]) {
  cursor: not-allowed;
  color: var(--texte-tenu);
}

.tv-bascule input {
  accent-color: var(--vert);
  width: 1.1em; height: 1.1em;
}

.tv-bascule small {
  font-size: 0.75rem;
  color: var(--texte-tenu);
}

/* Séparation entre les deux viewports quand ils s'affichent ensemble. */
.tv-vue + .tv-vue { margin-top: var(--e5); }

/* -------- vue combinée : asc + sujet + desc dans le même viewport

   La descendance rend sa carte de tête (le sujet) en « fantôme » : elle
   occupe la place mais ne se voit pas — un négatif de marge la fait
   remonter jusqu'à la superposition avec la vraie carte du sujet, tout
   en bas de l'ascendance. Résultat : une seule silhouette du sujet à
   l'écran, avec le bracket des enfants qui en descend et le bracket des
   parents qui y monte.

   Le calcul du décalage vertical dépend de la hauteur d'une carte
   (--tv-carte-h) et de la bordure d'une pixel autour. On l'exprime en
   variable pour ne pas se répéter. */

.tv-arbre__scene--complet {
  padding-top:    var(--e4);
  padding-bottom: var(--e4);
}

.tv-complet {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.tv-complet__asc,
.tv-complet__desc,
.tv-complet__seul-sujet {
  display: flex;
  justify-content: center;
}

/* Remonte la descendance pour que sa carte de tête (fantôme) recouvre
   pixel pour pixel la carte du sujet. La hauteur réelle de la carte peut
   dépasser --tv-carte-h quand un nom long passe sur deux lignes : le JS
   mesure et pose --tv-carte-h-sujet sur .tv-complet ; on retombe sur
   --tv-carte-h si JS est absent.

   La règle vaut dans les DEUX cas, que le sujet vienne de l'ascendance
   ou du .tv-complet__seul-sujet rendu quand on ne lui connaît aucun
   ancêtre. Une exception existait ici pour ce second cas ; elle laissait
   le fantôme se poser sous la vraie carte au lieu de disparaître dessous,
   et la même personne s'affichait deux fois. */
.tv-complet__desc {
  margin-top: calc(-1 * (var(--tv-carte-h-sujet, var(--tv-carte-h)) + 2px));
}

.tv-carte--fantome {
  visibility: hidden;
  pointer-events: none;
}

/* Le fantôme ET tout son parenté immédiate doivent être transparents
   aux clics : la carte fantôme est superposée à la vraie carte du sujet
   (celle en ascendance), et sans ça le badge ou la fiche du sujet sont
   captés par le .tv-desc-noeud__moi qui les recouvre, particulièrement
   sur mobile où certains navigateurs remontent l'événement plus haut
   dans la chaîne que la spec ne le voudrait. On coupe tout à la racine
   de la descendance quand la vraie carte est ailleurs (dans l'asc). */
.tv-complet__desc > .tv-desc-noeud > .tv-desc-noeud__moi {
  pointer-events: none;
}

/* Sur mobile, --tv-carte-h vaut moins ; le calc s'ajuste
   automatiquement grâce à la variable. Rien à faire ici. */

/* Quand deux vues autonomes (asc et desc séparées) sont affichées ensemble,
   chaque viewport se compresse — legacy pour le mode « vues empilées ». */
.tv-vue--compact .tv-arbre__viewport {
  height: min(44vh, 380px);
}

@media (max-width: 40rem) {
  .tv-vue--compact .tv-arbre__viewport {
    height: min(48vh, 340px);
  }
}

/* -------- bulle de survol : « qui c'est pour moi » */

.tv-bulle {
  position: absolute;
  z-index: 3;
  transform: translate(-50%, calc(-100% - 10px));
  padding: 0.25rem 0.55rem;
  background: var(--texte);
  color: var(--fond);
  border-radius: 0.375rem;
  font-size: 0.75rem;
  line-height: 1.3;
  font-weight: 500;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease-out;
  box-shadow: var(--ombre-2);
}

.tv-bulle::after {
  /* Petite pointe qui tombe vers la carte. */
  content: "";
  position: absolute;
  left: 50%;
  bottom: -4px;
  width: 8px; height: 8px;
  background: var(--texte);
  transform: translateX(-50%) rotate(45deg);
  border-bottom-right-radius: 2px;
}

.tv-bulle--visible { opacity: 1; }

/* -------- badge « + » sur une carte qui a des extras */

.tv-carte__extras {
  position: absolute;
  top: -10px;
  right: -10px;
  z-index: 3;                 /* au-dessus de la zone (1) et de la fiche (2) */
  width: 1.875rem;
  height: 1.875rem;
  padding: 0;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--vert);
  color: #fff;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: var(--ombre-1);
  opacity: .55;
  transition: opacity var(--transition), transform var(--transition);
  /* Étend la surface tactile bien au-delà du visuel : le doigt vise
     le rond, la marge invisible autour évite les ratés. */
  -webkit-tap-highlight-color: transparent;
}

.tv-carte__extras::before {
  content: "";
  position: absolute;
  inset: -8px;
  border-radius: 50%;
}

/* Sur appareils sans survol (téléphones, tablettes), la pastille reste
   toujours opaque — inutile d'attendre un hover qui n'existe pas. */
@media (hover: none) {
  .tv-carte__extras { opacity: 1; }
}

/* L'icône « deux personnes » : suit currentColor (blanc en clair,
   texte sombre en thème sombre — géré plus bas), taille agrandie pour
   qu'on repère la pastille du premier coup d'œil. */
.tv-carte__extras svg {
  width: 1.25rem;
  height: 1.25rem;
  display: block;
}

.tv-carte { position: relative; }

.tv-carte:hover .tv-carte__extras,
.tv-carte:focus-within .tv-carte__extras,
.tv-carte__extras:hover,
.tv-carte__extras:focus-visible {
  opacity: 1;
}

.tv-carte__extras:hover,
.tv-carte__extras:focus-visible {
  transform: scale(1.1);
  outline: 0;
}

/* En thème sombre, le vert clair ferait un halo trop crémeux : on garde
   le vert mais on assombrit le texte (comme les autres pastilles vertes). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tv-carte__extras { color: #10221a; }
}
:root[data-theme="dark"] .tv-carte__extras { color: #10221a; }

/* -------- popover ancré à une carte */

.tv-popover {
  position: absolute;
  z-index: 4;
  transform: translateX(-50%);
  min-width: 14rem;
  max-width: min(20rem, 90vw);
  background: var(--surface);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-3);
  color: var(--texte);
  animation: tv-popover-entre 140ms ease-out;
}

@keyframes tv-popover-entre {
  from { opacity: 0; transform: translate(-50%, -4px); }
  to   { opacity: 1; transform: translate(-50%, 0);    }
}

.tv-popover::before {
  /* Pointe vers la carte, en haut. */
  content: "";
  position: absolute;
  top: -6px;
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
  width: 12px; height: 12px;
  background: var(--surface);
  border-top: 1px solid var(--bordure-forte);
  border-left: 1px solid var(--bordure-forte);
}

.tv-popover__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2);
  padding: var(--e2) var(--e3);
  border-bottom: 1px solid var(--bordure);
  background: var(--surface-2);
  border-radius: var(--rayon) var(--rayon) 0 0;
}

.tv-popover__titre {
  font-size: 0.875rem;
  color: var(--texte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tv-popover__fermer {
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--texte-doux);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 4px;
}

.tv-popover__fermer:hover,
.tv-popover__fermer:focus-visible {
  background: var(--surface);
  color: var(--texte);
  outline: 0;
}

.tv-popover__corps {
  padding: var(--e2) var(--e3);
  max-height: 14rem;
  overflow-y: auto;
  /* Défilement inertiel natif sur iOS, et on isole le scroll pour qu'un
     coup de doigt à l'intérieur ne fasse pas défiler la page derrière. */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

.tv-popover__section + .tv-popover__section {
  margin-top: var(--e2);
  padding-top: var(--e2);
  border-top: 1px solid var(--bordure);
}

.tv-popover__section h4 {
  margin: 0 0 0.25rem;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--texte-tenu);
  font-weight: 700;
}

.tv-popover__section ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.tv-popover__section li {
  padding: 0.125rem 0;
  font-size: 0.875rem;
  display: flex;
  align-items: baseline;
  gap: var(--e2);
}

.tv-popover__nom {
  color: var(--texte);
  text-decoration: none;
  border-bottom: 1px dotted var(--bordure-forte);
  flex: 1 1 auto;
  min-width: 0;
}

.tv-popover__nom:hover,
.tv-popover__nom:focus-visible {
  color: var(--vert-fonce);
  border-bottom-color: var(--vert);
  outline: 0;
}

.tv-popover__fiche {
  font-size: 0.6875rem;
  color: var(--texte-tenu);
  text-decoration: none;
  padding: 1px 5px;
  border-radius: 3px;
  flex: 0 0 auto;
}

.tv-popover__fiche:hover,
.tv-popover__fiche:focus-visible {
  color: var(--vert-fonce);
  background: var(--vert-clair);
  outline: 0;
}

/* Compatibilité rétro : si un ancien rendu envoie encore <a> nu dans un
   li de section, on ne casse pas complètement le style. */
.tv-popover__section li > a:not(.tv-popover__nom):not(.tv-popover__fiche) {
  color: var(--texte);
  text-decoration: none;
  border-bottom: 1px dotted var(--bordure-forte);
}

.tv-popover__vide {
  margin: 0;
  padding: var(--e2) 0;
  color: var(--texte-tenu);
  font-size: 0.875rem;
  text-align: center;
}

/* Sur téléphone, le popover se transforme en feuille glissée en bas
   d'écran : plein largeur, ancré au sol, impossible à rater. Le JS
   n'a pas à changer — c'est la CSS mobile qui détache le popover de
   sa position d'ancrage. */
@media (max-width: 40rem) {
  .tv-popover {
    position: fixed;
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    bottom: 0;
    transform: none;
    min-width: 0;
    max-width: none;
    width: 100%;
    border-radius: var(--rayon-lg) var(--rayon-lg) 0 0;
    border-bottom: 0;
    box-shadow: 0 -12px 32px rgba(0, 0, 0, .4);
    animation: tv-popover-entre-mobile 200ms ease-out;
  }

  .tv-popover::before { display: none; }

  .tv-popover__corps {
    max-height: 55vh;
  }

  .tv-popover__section li { font-size: 1rem; padding: var(--e1) 0; }
}

@keyframes tv-popover-entre-mobile {
  from { transform: translateY(100%); }
  to   { transform: translateY(0);    }
}

/* Mode sombre : la trame de fond du viewport prend moins de contraste
   pour ne pas moucheter l'écran, et le vert du sujet suit son ton clair
   déjà défini plus haut. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tv-arbre__viewport {
    background:
      radial-gradient(circle at 1px 1px, var(--bordure) 1px, transparent 1.5px);
    background-size: 24px 24px;
    background-position: center;
  }
}
:root[data-theme="dark"] .tv-arbre__viewport {
  background:
    radial-gradient(circle at 1px 1px, var(--bordure) 1px, transparent 1.5px);
  background-size: 24px 24px;
  background-position: center;
}

/* Sur téléphone, la carte se resserre : la largeur descend, le nom rétrécit. */
@media (max-width: 40rem) {
  .tv-arbre {
    --tv-carte-l: 9.5rem;
    --tv-carte-h: 4.75rem;
    --tv-gap-h:   0.75rem;
    --tv-gap-v:   1.5rem;
  }

  .tv-carte__nom    { max-width: 6rem; font-size: 0.75rem; }
  .tv-carte__annees { font-size: 0.6875rem; padding-right: 2.5rem; }
  .tv-carte__portrait { width: 2rem; height: 2rem; }
  .tv-arbre__viewport { height: min(70vh, 520px); }

  /* La fiche prend moins de place visuelle mais garde une grande cible
     tactile grâce à son ::before qui déborde. Elle sort aussi un peu du
     bord droit pour ne pas empiéter sur les années. */
  .tv-carte__fiche {
    font-size: 0.625rem;
    padding: 2px 5px;
    bottom: 2px;
    right: 2px;
  }

  .tv-carte__fiche::before {
    inset: -10px;
  }
}

/* -------------------------------------------------------------- galerie */

.galerie {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr));
  gap: var(--e3);
}

.vignette {
  position: relative;
  display: block;
  border-radius: var(--rayon);
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--bordure);
  cursor: pointer;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.vignette:hover {
  border-color: var(--vert);
  box-shadow: var(--ombre-2);
}

.vignette__image {
  /* Le rapport est réservé avant chargement : pas de saut de mise en page. */
  aspect-ratio: 1;
  width: 100%;
  object-fit: cover;
}

.vignette__legende {
  padding: var(--e2) var(--e3);
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--texte-doux);
  background: var(--surface);
}

.photo-pleine {
  max-width: 100%;
  border-radius: var(--rayon-lg);
  border: 1px solid var(--bordure);
  background: var(--surface-2);
}


/* --------------------------------------- identification sur les photos */

/*
 * Les repères sont positionnés en pourcentage : ils suivent l'image quelle
 * que soit sa taille d'affichage. Les variables --x et --y sont posées par
 * le script (la CSP interdit l'attribut « style » dans le HTML).
 */
.marquage {
  position: relative;
  display: inline-block;
  max-width: 100%;
}

/*
 * L'image est un bloc, ce qui suffit à supprimer le blanc que laissait sous
 * elle la ligne de texte de son conteneur. Un « line-height: 0 » sur la zone
 * entière y pourvoyait aussi, mais le panneau de saisie vit maintenant à
 * l'intérieur : les noms proposés s'y empilaient à la même hauteur, chaque
 * suggestion écrite par-dessus la précédente.
 */
.marquage img.photo-pleine {
  display: block;
}

.marquage--actif img.photo-pleine {
  cursor: crosshair;
}

.marqueur {
  position: absolute;
  left: var(--x, 50%);
  top: var(--y, 50%);
  width: 0;
  height: 0;
  line-height: 1;
  text-decoration: none;
  color: inherit;
}

/* Le cadre est centré sur le point cliqué. */
.marqueur__cadre {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 4.5rem;
  height: 4.5rem;
  transform: translate(-50%, -50%);
  border: 2px solid rgb(255 255 255 / 0.9);
  /*
   * Un cercle, et non un carré. Un visage est rond, et le cercle épouse mieux
   * ce qu'il désigne : les angles d'un carré englobaient du décor — l'épaule
   * du voisin, un bout de mur — sur des photos de groupe où les visages se
   * touchent presque.
   */
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.35), inset 0 0 0 1px rgb(0 0 0 / 0.2);
  opacity: 0.55;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}

.marqueur__nom {
  position: absolute;
  left: 50%;
  top: calc(50% + 2.5rem);
  transform: translateX(-50%);
  padding: 0.15rem 0.5rem;
  max-width: 11rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.8125rem;
  color: #fff;
  background: rgb(0 0 0 / 0.72);
  border-radius: var(--rayon);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.marqueur:hover .marqueur__cadre,
.marqueur:focus-visible .marqueur__cadre,
.marqueur--vu .marqueur__cadre {
  opacity: 1;
  border-color: #fff;
}

.marqueur:hover .marqueur__nom,
.marqueur:focus-visible .marqueur__nom,
.marqueur--vu .marqueur__nom {
  opacity: 1;
}

.marqueur--provisoire .marqueur__cadre {
  opacity: 1;
  border-style: dashed;
  /*
   * Quand le repère naît d'un visage détecté, il en prend la taille : un cercle
   * fixe conviendrait à l'un et engloberait le voisin de l'autre. Le script
   * fournit des pixels — un pourcentage se résoudrait ici contre un repère de
   * largeur nulle, donc contre rien.
   */
  width: var(--taille, 4.5rem);
  height: var(--taille, 4.5rem);
}

.est-vise {
  border-radius: var(--rayon-lg);
  box-shadow: 0 0 0 2px var(--vert);
}

.marquage-barre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
  margin-top: var(--e3);
}

/*
 * Le champ de saisie flotte sur la photo, sous le carré qu'on vient de poser.
 *
 * Il vivait dans une carte au-dessous de l'image : on cliquait un visage en
 * haut, puis on cherchait des yeux où taper le nom, et le lien entre les deux
 * gestes se perdait. Sa position est calculée par le script, à la manière du
 * halo de la visite guidée.
 */
.marquage-choix {
  position: absolute;
  z-index: 5;
  width: min(20rem, calc(100% - 1rem));
  padding: var(--e3) var(--e4);
  box-shadow: var(--ombre-3);
}

/* La zone de marquage est déjà l'ancre des repères ; elle l'est aussi de ce
   panneau, et ne doit donc rien rogner. */
.marquage { overflow: visible; }

/* Bouton discret présenté comme un lien (retirer une identification). */
.lien-nu {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--texte-doux);
  text-decoration: underline;
  cursor: pointer;
}

.lien-nu:hover {
  color: var(--vert);
}


/* ------------------------------------------- comparaison de deux fiches */

table.comparaison th[scope="row"] {
  width: 12rem;
  text-align: left;
  font-weight: 600;
  color: var(--texte-doux);
  vertical-align: top;
}

table.comparaison td {
  vertical-align: top;
}

/* Les lignes qui diffèrent sont celles qu'il faut regarder. */
table.comparaison tr.comparaison__differe > td,
table.comparaison tr.comparaison__differe > th {
  background: var(--abricot-clair);
}

.choix-fiche {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  cursor: pointer;
}

.choix-fiche input[type="radio"] {
  margin-top: 0.2rem;
  flex: none;
}

.choix-fiche__texte {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  align-items: flex-start;
}

.choix-fiche__titre {
  font-weight: 600;
}


/* ------------------------------------------------------- calendrier */

/* Le groupe jour/mois/année sert d'ancrage au calque. */
.champ__inline[data-date] {
  position: relative;

  /*
   * UNE SEULE LIGNE, TOUJOURS. Jour, mois, année et le bouton du calendrier
   * forment une seule date ; sur deux lignes, ils se lisent comme deux champs
   * sans rapport.
   *
   * J'ai tourné deux fois autour du vrai problème. « nowrap » faisait déborder
   * le groupe hors de sa carte dans une colonne étroite ; j'ai alors autorisé
   * l'enroulement, ce qui a réglé le débordement et cassé la ligne unique.
   * Aucun des deux n'était la cause.
   *
   * La cause, ce sont les largeurs minimales : tant que chaque champ refuse de
   * se comprimer, la somme dépasse forcément le cadre et il faut bien que
   * quelque chose cède. En les laissant se réduire — le mois prenant ce qui
   * reste — les quatre tiennent sur une ligne dans 266 pixels comme dans 900.
   */
  flex-wrap: nowrap;
}

/*
 * Le jour et l'année sont bornés par leur contenu : deux et quatre chiffres,
 * qui ne s'abrègent pas. Le mois absorbe la place restante, et c'est lui qui
 * se rogne quand elle manque — « septembre » tronqué reste identifiable, un
 * jour tronqué ne l'est pas.
 */
/*
 * Les largeurs se mesurent en « ch », la largeur d'un chiffre dans la police du
 * champ — non en rem, qui ne dépend que de la racine.
 *
 * Un navigateur réglé sur une taille de texte minimale grossit les caractères
 * sans élargir les boîtes : « Année » se lisait « Anı » dans un champ de
 * 3,5 rem. En « ch », la boîte suit la police qu'on lui impose, quelle qu'en
 * soit l'origine — réglage d'accessibilité, préférence système, zoom du texte
 * seul. Le padding s'ajoute à part, la boîte devant contenir le texte ET son
 * espacement intérieur.
 */
.champ__inline[data-date] select[id$="_d"] {
  flex: 0 0 auto;
  width: calc(5ch + 2 * var(--e2) + 1.2em);
  min-width: 0;
  padding-inline: var(--e2);
}

.champ__inline[data-date] select[id$="_m"] {
  flex: 1 1 auto;
  /* Le mois est celui qui cède quand la place manque, mais pas au point de
     devenir illisible : « sep… » se comprend, une fenêtre de dix pixels non. */
  min-width: 6ch;
  /* Sans plafond, le mois avalait toute la largeur restante sur un
     formulaire large : « septembre » dans un champ de sept cents pixels. */
  max-width: calc(11ch + 2 * var(--e2) + 1.2em);
  padding-inline: var(--e2);
}

/*
 * La ligne ne s'étale pas non plus : une date reste un petit objet. Sa largeur
 * est celle de son contenu, bornée par la colonne — et non un nombre fixe, qui
 * étouffait le mois dès que la police grossissait : les quatre champs
 * s'élargissaient dans un cadre qui, lui, restait le même.
 */
.champ__inline[data-date] {
  width: fit-content;
  max-width: 100%;
}

.champ__inline[data-date] input[type="number"],
.champ__inline[data-date] input[id$="_y"] {
  flex: 0 0 auto;
  width: calc(6ch + 2 * var(--e2));
  min-width: 0;
  padding-inline: var(--e2);
}

.champ__inline[data-date] .date-ouvrir {
  flex: 0 0 2.25rem;
  width: 2.25rem;
  height: 2.25rem;
}

.champ__inline[data-date] select { min-width: 0; flex: 1 1 auto; }



.date-ouvrir {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  flex: none;
  padding: 0;
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte-doux);
  cursor: pointer;
  transition: border-color var(--transition), color var(--transition);
}

.date-ouvrir svg { width: 1.15rem; height: 1.15rem; }

.date-ouvrir:hover {
  border-color: var(--vert);
  color: var(--vert);
}

.calendrier-calque {
  position: absolute;
  top: calc(100% + var(--e2));
  left: 0;
  z-index: var(--z-menu);
  width: 19rem;
  max-width: calc(100vw - 2rem);
  padding: var(--e3);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  background: var(--surface);
  box-shadow: var(--ombre-3);
}

.calendrier-calque__entete {
  display: flex;
  align-items: center;
  gap: var(--e1);
  margin-bottom: var(--e2);
}

.calendrier-calque__fleche,
.calendrier-calque__titre,
.calendrier-calque__action,
.calendrier-calque__case {
  border: 0;
  background: none;
  font: inherit;
  color: var(--texte);
  cursor: pointer;
  border-radius: var(--rayon);
  transition: background var(--transition), color var(--transition);
}

.calendrier-calque__fleche {
  width: 2rem;
  height: 2rem;
  flex: none;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--texte-doux);
}

.calendrier-calque__titre {
  flex: 1;
  padding: var(--e1) var(--e2);
  font-weight: 600;
  text-align: center;
}

.calendrier-calque__fleche:hover,
.calendrier-calque__titre:hover,
.calendrier-calque__case:hover,
.calendrier-calque__action:hover {
  background: var(--vert-clair);
  color: var(--vert-fonce);
}

.calendrier-calque__grille {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.calendrier-calque__grille--large {
  grid-template-columns: repeat(4, 1fr);
  gap: var(--e1);
}

.calendrier-calque__jour-nom {
  padding: var(--e1) 0;
  font-size: 0.6875rem;
  font-weight: 700;
  text-align: center;
  color: var(--texte-tenu);
}

.calendrier-calque__case {
  padding: var(--e2) 0;
  font-size: 0.8125rem;
  text-align: center;
}

.calendrier-calque__case.est-choisi {
  background: var(--vert);
  color: #fff;
  font-weight: 600;
}

.calendrier-calque__vide { visibility: hidden; }

.calendrier-calque__pied {
  display: flex;
  gap: var(--e2);
  margin-top: var(--e2);
  padding-top: var(--e2);
  border-top: 1px solid var(--bordure);
}

.calendrier-calque__action {
  flex: 1;
  padding: var(--e2);
  font-size: 0.8125rem;
  color: var(--texte-doux);
}


/* --------------------------------------------- frise des photos par année */

.graphe--frise {
  margin: 0;
}

/* preserveAspectRatio="none" : la frise s'étire en largeur, pas en hauteur. */
.graphe--frise svg {
  display: block;
  width: 100%;
  height: 8rem;
}

.graphe--frise .grille { stroke: var(--bordure); }
.graphe--frise .axe-texte { fill: var(--texte-tenu); font-size: 11px; }

.frise__barre { fill: var(--vert); }

/* Hors de la période choisie : présent, mais en retrait. */
.frise__barre--hors { fill: var(--bordure-forte); }

/* Rectangle transparent plein-hauteur : la cible du clic. */
.frise__zone {
  fill: transparent;
}

.graphe--frise a:hover .frise__zone {
  fill: var(--vert-clair);
}

.graphe--frise a:hover .frise__barre {
  fill: var(--vert-fonce);
}

.graphe--frise a:focus-visible .frise__zone {
  fill: var(--vert-clair);
  stroke: var(--vert);
}


/* --------------------------------------------- menu « Ajouter » d'une fiche */

.ajout { position: relative; }

.ajout__declencheur {
  gap: var(--e2);
  cursor: pointer;
}

.ajout__declencheur svg { width: 1rem; height: 1rem; }
.ajout__chevron { transition: transform var(--transition); }
.ajout__bascule:checked ~ .ajout__declencheur .ajout__chevron { transform: rotate(180deg); }

.ajout__calque {
  display: none;
  position: absolute;
  right: 0;
  top: calc(100% + var(--e2));
  z-index: var(--z-menu);
  min-width: 12rem;
  padding: var(--e2);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-3);
}

.ajout__bascule:checked ~ .ajout__calque { display: block; }

.ajout__lien {
  display: block;
  padding: var(--e3);
  margin-top: 2px;
  border-radius: var(--rayon);
  color: var(--texte-doux);
  font-size: 0.9375rem;
  text-decoration: none;
}

.ajout__lien:hover { background: var(--surface-2); color: var(--texte); }

/* ------------------------------------------------------------ messagerie */

.fils { display: flex; flex-direction: column; gap: var(--e2); }

.fil {
  display: flex;
  align-items: center;
  gap: var(--e4);
  padding: var(--e4);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.fil:hover { border-color: var(--vert); box-shadow: var(--ombre-1); color: inherit; }

.fil--nouveau { border-left: 3px solid var(--vert); }

.fil__portraits { display: flex; flex: none; }
.fil__portraits > * + * { margin-left: -0.6rem; }

.fil__portrait {
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--surface);
  background: var(--surface-2);
}

.fil__portrait--initiales {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--texte-doux);
}

.fil__texte { min-width: 0; flex: 1; }

.fil__personnes { display: block; font-weight: 600; }
.fil--nouveau .fil__personnes { color: var(--vert-fonce); }

.fil__sujet { display: block; font-size: 0.875rem; color: var(--texte-doux); }

.fil__extrait {
  display: block;
  font-size: 0.875rem;
  color: var(--texte-tenu);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fil__date {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--e2);
  font-size: 0.75rem;
  color: var(--texte-tenu);
  white-space: nowrap;
}

.fil__pastille,
.moi__pastille {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--vert);
}

.moi__compteur {
  float: right;
  min-width: 1.25rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--vert);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  text-align: center;
}

/* Aperçu d'un échange, sur la fiche d'une personne. */
.echange {
  display: block;
  padding: var(--e3);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface-2);
  text-decoration: none;
  color: inherit;
}

.echange:hover { border-color: var(--vert); color: inherit; }

.echange__qui {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--vert-fonce);
}

.echange__texte { display: block; font-size: 0.875rem; }

.echange__date {
  display: block;
  margin-top: var(--e1);
  font-size: 0.6875rem;
  color: var(--texte-tenu);
}

/* ---------------------------------------------------------- discussion */

.discussion { display: flex; flex-direction: column; gap: var(--e4); }

.bulle { display: flex; gap: var(--e3); align-items: flex-end; }
.bulle--moi { flex-direction: row-reverse; }

.bulle__portrait {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
}

.bulle__portrait--initiales {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--texte-doux);
}

.bulle__corps {
  max-width: 34rem;
  padding: var(--e3) var(--e4);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  background: var(--surface);
}

/* Ses propres messages se distinguent par la couleur, pas seulement le côté. */
.bulle--moi .bulle__corps {
  background: var(--vert-clair);
  border-color: var(--vert-bord);
}

.bulle__nom {
  margin: 0 0 var(--e1);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--vert-fonce);
}

.bulle__texte { margin: 0; }

.bulle__date {
  margin: var(--e1) 0 0;
  font-size: 0.6875rem;
  color: var(--texte-tenu);
}

.bulle--moi .bulle__date { text-align: right; }


/* ------------------------------------------- carte semée de points (Agora) */

.carte-dessin--points .carte__fond {
  fill: var(--surface-2);
  stroke: var(--bordure);
  stroke-width: 0.6;
}

.carte-dessin--vise { cursor: crosshair; }

.carte__halo {
  fill: var(--vert);
  opacity: 0.18;
}

.carte__pastille {
  fill: var(--vert);
  stroke: var(--surface);
  stroke-width: 1.5;
}

.carte__point:hover .carte__halo { opacity: 0.35; }
.carte__point:hover .carte__pastille { fill: var(--vert-fonce); }

.carte__point:focus-visible .carte__pastille {
  stroke: var(--texte);
  stroke-width: 2.5;
}


/* --------------------------------------------- diagnostic des courriels */

.controle {
  display: flex;
  gap: var(--e3);
  align-items: flex-start;
  padding: var(--e3) 0;
  border-bottom: 1px solid var(--bordure);
}

.controle:last-child { border-bottom: 0; }

.controle__icone {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-weight: 700;
  font-size: 0.8125rem;
}

.controle--ok .controle__icone        { background: var(--succes-fond); color: var(--succes); }
.controle--attention .controle__icone { background: var(--abricot-clair); color: var(--abricot); }
.controle--erreur .controle__icone    { background: var(--erreur-fond); color: var(--erreur); }

.controle__detail {
  display: block;
  font-size: 0.875rem;
  color: var(--texte-doux);
  word-break: break-word;
}

/* Le dialogue avec le serveur : lisible tel quel, sans mise en forme. */
.dialogue {
  margin: 0;
  padding: var(--e4);
  border-radius: var(--rayon);
  background: var(--surface-2);
  border: 1px solid var(--bordure);
  font: 0.8125rem/1.6 var(--mono);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
}

.dialogue__envoi   { color: var(--texte-doux); }
.dialogue__reponse { color: var(--vert-fonce); }

/* ------------------------------------------------------- suivi des erreurs */

.erreur { border-left: 3px solid var(--bordure-forte); }
.erreur--active,
.erreur--revenue  { border-left-color: var(--erreur); }
.erreur--corrigee { border-left-color: var(--succes); opacity: 0.75; }

.erreur summary {
  cursor: pointer;
  color: var(--texte-doux);
}

/* ------------------------------------------------- en-tête d'une fiche */

.fiche-entete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  /* Serré : quand les actions passent sous le nom, cet écart est tout ce qui
     les en sépare, et il n'a pas à faire une respiration. */
  gap: var(--e3);
}

.fiche-entete__identite {
  display: flex;
  align-items: center;
  gap: var(--e4);
  min-width: 0;
}

.fiche-entete__portrait {
  width: 4.5rem;
  height: 4.5rem;
  flex: none;
  /* Arrondi franc plutôt que cercle : la charte du site est aux grands
     rayons, et un portrait carré arrondi recadre moins les visages. */
  border-radius: var(--rayon-lg);
  object-fit: cover;
  background: var(--surface-2);
  box-shadow: var(--ombre-1);
}

.fiche-entete__portrait--initiales {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--texte-doux);
}

.fiche-entete__texte { min-width: 0; }

.fiche-entete__parente {
  margin-top: var(--e1);
  font-size: 0.9375rem;
  color: var(--vert-fonce);
}

/*
 * Lien de parenté transformé en action inline : quand un lien avec le
 * lecteur est connu, le texte devient une pill discrète cliquable qui
 * ouvre la page /parente. Le survol laisse voir qu'il est cliquable
 * (bord vert, léger fond), la flèche indique la sortie vers une autre
 * vue. Design volontairement modeste pour ne pas voler la vedette au
 * nom au-dessus.
 */
.fiche-entete__parente-lien {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  color: var(--vert-fonce);
  text-decoration: none;
  border: 1px solid transparent;
  background: var(--vert-clair);
  transition: background var(--transition), border-color var(--transition), transform var(--transition);
}
.fiche-entete__parente-lien svg {
  opacity: 0.7;
  transition: transform var(--transition), opacity var(--transition);
}
.fiche-entete__parente-lien:hover,
.fiche-entete__parente-lien:focus-visible {
  background: var(--vert-clair);
  border-color: var(--vert-bord);
  text-decoration: none;
}
.fiche-entete__parente-lien:hover svg,
.fiche-entete__parente-lien:focus-visible svg {
  opacity: 1;
  transform: translate(2px, -2px);
}
.fiche-entete__parente-lien:focus-visible {
  outline: 2px solid var(--vert);
  outline-offset: 2px;
}

/*
 * LES ACTIONS SUR UNE SEULE LIGNE, « Modifier » compris.
 *
 * Elles s'empilaient : « Modifier » seul au-dessus, le reste en dessous. Sur un
 * écran où le nom et les boutons ne tiennent pas côte à côte — la plupart des
 * portables et beaucoup d'écrans de bureau — le bloc passe sous le nom, et
 * cette pile ajoutait une ligne entière entre le visage et les boutons :
 * soixante-quatre pixels de vide, pour un bouton seul aligné à droite.
 *
 * Une ligne unique qui s'enroule au besoin. « Modifier » y reste le premier,
 * détaché du reste par sa couleur, et l'on gagne la hauteur d'un bouton.
 */
.fiche-entete__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e2);
  /*
   * « align-items » ne suffisait pas. Sur un nom long, le bloc d'identité
   * occupe toute la largeur et les actions passent à la ligne suivante ; seules
   * sur cette ligne, « justify-content: space-between » les ramenait à gauche,
   * et le bouton Modifier flottait au milieu. La marge automatique les tient à
   * droite quel que soit le retour à la ligne.
   */
  margin-left: auto;
}

.fiche-entete__actions .rangee {
  flex-wrap: wrap;
  justify-content: flex-end;
}

.fiche-entete__actions .bouton svg { width: 1rem; height: 1rem; }

@media (max-width: 48rem) {
  /* Sur un téléphone, la colonne reprend ses droits : des boutons pleine
     largeur se visent au pouce, une ligne de vignettes non. */
  .fiche-entete__actions {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }
}

/* --------------------------------------------- titre d'une fiche */

.titre-personne {
  display: flex;
  align-items: center;
  gap: var(--e4);
  min-width: 0;
}

.titre-personne__portrait {
  width: 3.5rem;
  height: 3.5rem;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
  border: 2px solid var(--surface);
  box-shadow: var(--ombre-1);
}

.titre-personne__portrait--initiales {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--texte-doux);
}

/* ---------------------------------------------- onglets de filtrage */

.onglets-filtre {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
}

.onglets-filtre__lien {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  padding: var(--e2) var(--e4);
  border: 1px solid var(--bordure);
  border-radius: 999px;
  background: var(--surface);
  color: var(--texte-doux);
  font-size: 0.9375rem;
  text-decoration: none;
  transition: border-color var(--transition), color var(--transition);
}

.onglets-filtre__lien:hover { border-color: var(--vert); color: var(--vert-fonce); }

.onglets-filtre__lien.est-actif {
  background: var(--vert);
  border-color: var(--vert);
  color: #fff;
  font-weight: 600;
}

.onglets-filtre__nombre {
  min-width: 1.5rem;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--texte-doux);
  font-size: 0.8125rem;
  font-weight: 700;
  text-align: center;
}

.onglets-filtre__lien.est-actif .onglets-filtre__nombre {
  background: rgb(255 255 255 / 0.25);
  color: #fff;
}

/* En sombre, `a { color: var(--vert) }` l'emporte en spécificité sur la classe
   et repeignait le libellé en vert sur le vert du fond : l'onglet actif
   devenait illisible. Même traitement que .barre__lien[aria-current="page"],
   qui repose sur le même aplat. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .onglets-filtre__lien.est-actif { color: #10221a; }

  :root:not([data-theme="light"]) .onglets-filtre__lien.est-actif .onglets-filtre__nombre {
    background: rgb(255 255 255 / 0.32);
    color: #10221a;
  }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .onglets-filtre__lien.est-actif { color: #10221a; }

:root[data-theme="dark"] .onglets-filtre__lien.est-actif .onglets-filtre__nombre {
  background: rgb(255 255 255 / 0.32);
  color: #10221a;
}

/* --------------------------------------------------------- émoticônes */

.emoticones { position: relative; margin-top: var(--e2); }

.emoticones__declencheur {
  padding: var(--e1) var(--e3);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  background: var(--surface);
  font-size: 1.125rem;
  line-height: 1.2;
  cursor: pointer;
}

.emoticones__declencheur:hover { border-color: var(--vert); }

.emoticones__choix {
  position: absolute;
  bottom: calc(100% + var(--e2));
  left: 0;
  z-index: var(--z-menu);
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: 2px;
  width: max-content;
  max-width: min(24rem, 90vw);
  padding: var(--e2);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  background: var(--surface);
  box-shadow: var(--ombre-3);
}

.emoticones__item {
  padding: var(--e1);
  border: 0;
  border-radius: var(--rayon);
  background: none;
  font-size: 1.25rem;
  line-height: 1.2;
  cursor: pointer;
}

.emoticones__item:hover { background: var(--vert-clair); }

@media (max-width: 30rem) {
  .emoticones__choix { grid-template-columns: repeat(6, 1fr); }
}

/* ------------------------------------------------------ boîte à idées */

.idee--faite   { opacity: 0.7; }
.idee--ecartee { opacity: 0.55; }

.idee__corps { display: flex; gap: var(--e4); align-items: flex-start; }
.idee__texte { min-width: 0; flex: 1; }
.idee__soutien { flex: none; }

.idee__vote {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 3.25rem;
  padding: var(--e2);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte-doux);
  cursor: pointer;
  transition: border-color var(--transition), color var(--transition);
}

.idee__vote:hover { border-color: var(--vert); color: var(--vert-fonce); }

.idee__vote.est-soutenue {
  background: var(--vert-clair);
  border-color: var(--vert);
  color: var(--vert-fonce);
}

.idee__fleche { font-size: 0.75rem; line-height: 1; }
.idee__nombre { font-size: 1.0625rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* La réponse de l'administration se distingue de la proposition. */
.idee__reponse {
  padding: var(--e3);
  border-left: 3px solid var(--vert);
  background: var(--surface-2);
  border-radius: var(--rayon);
  font-size: 0.9375rem;
}

/* --------------------------------------------------- connexion Google */

/*
 * Un « ou » centré, sans filet d'aucune sorte. Il en portait trois : deux qui
 * l'encadraient, et un troisième par-dessous — celui-là venu de la classe
 * utilitaire « separateur », qui sert à séparer les lignes d'une liste et dont
 * ce bloc partageait le nom par hasard. Aucun des trois ne séparait quoi que ce
 * soit que le mot ne dise déjà, et ils prenaient de la hauteur sur l'écran où
 * elle est le plus comptée.
 */
.ou {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: var(--e4) 0;
  color: var(--texte-tenu);
  font-size: 0.875rem;
}

/* Le bouton reprend la charte de Google, qui l'impose pour son usage. */
.bouton-tiers,
.bouton-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--e3);
  width: 100%;
  min-height: 2.75rem;
  padding: var(--e2) var(--e5);
  border: 1px solid #dadce0;
  border-radius: var(--rayon);
  background: #ffffff;
  color: #3c4043;
  font: 600 0.9375rem/1.4 var(--sans);
  text-decoration: none;
  transition: box-shadow var(--transition), background var(--transition);
}

/*
 * L'écart entre deux boutons de connexion tiers. Il tenait auparavant à un
 * « margin-bottom » posé sur .bouton-tiers seul : le jour où Google est passé
 * devant, la marge s'est retrouvée sous le dernier bouton et les deux se sont
 * collés. La règle porte maintenant sur la succession, quel que soit l'ordre.
 */
.bouton-tiers + .bouton-tiers,
.bouton-tiers + .bouton-google,
.bouton-google + .bouton-tiers,
.bouton-google + .bouton-google {
  margin-top: var(--e3);
}

.bouton-tiers:hover,
.bouton-google:hover {
  background: #f8f9fa;
  box-shadow: var(--ombre-1);
  color: #3c4043;
}

/* -------------------------------------------------------------- boutons */

.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e2);
  min-height: 2.75rem;
  padding: var(--e2) var(--e5);
  border: 1px solid transparent;
  border-radius: var(--rayon);
  background: var(--vert);
  color: #fff;
  font: 600 0.9375rem/1.2 var(--sans);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.bouton:hover { background: var(--vert-fonce); color: #fff; }

.bouton:disabled,
.bouton[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

.bouton--secondaire {
  background: var(--surface);
  color: var(--texte);
  border-color: var(--bordure-forte);
}

.bouton--secondaire:hover {
  background: var(--surface-2);
  color: var(--texte);
  border-color: var(--vert);
}

.bouton--discret {
  background: transparent;
  color: var(--texte-doux);
  border-color: transparent;
  padding-inline: var(--e3);
}

.bouton--discret:hover { background: var(--surface-2); color: var(--texte); }

.bouton--danger { background: var(--erreur); }
.bouton--danger:hover { background: #7d241a; color: #fff; }

.bouton--petit { min-height: 2.25rem; padding: var(--e1) var(--e3); font-size: 0.8125rem; }

.bouton__icone { width: 1.125rem; height: 1.125rem; flex-shrink: 0; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bouton { color: #1a1510; }
  :root:not([data-theme="light"]) .bouton:hover { color: #1a1510; }
  :root:not([data-theme="light"]) .bouton--secondaire { color: var(--texte); }
  :root:not([data-theme="light"]) .bouton--secondaire:hover { color: var(--texte); }
  :root:not([data-theme="light"]) .bouton--danger { color: #1a1510; }
  :root:not([data-theme="light"]) .bouton--danger:hover { background: #d98577; color: #1a1510; }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .bouton { color: #1a1510; }
  :root[data-theme="dark"] .bouton:hover { color: #1a1510; }
  :root[data-theme="dark"] .bouton--secondaire { color: var(--texte); }
  :root[data-theme="dark"] .bouton--secondaire:hover { color: var(--texte); }
  :root[data-theme="dark"] .bouton--danger { color: #1a1510; }
  :root[data-theme="dark"] .bouton--danger:hover { background: #d98577; color: #1a1510; }


/* ------------------------------------------------------------ formulaires */

.champ { margin-bottom: var(--e4); }

.champ__label {
  display: block;
  margin-bottom: var(--e2);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--texte-doux);
}

.champ__aide {
  display: block;
  margin-top: var(--e1);
  font-size: 0.8125rem;
  color: var(--texte-tenu);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="url"],
input[type="tel"],
input[type="number"],
input[type="date"],
input[type="file"],
select,
textarea {
  width: 100%;
  min-height: 2.75rem;
  padding: var(--e2) var(--e3);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  /* 16px minimum : en dessous, iOS zoome au focus. */
  font: 400 1rem/1.5 var(--sans);
  transition: border-color var(--transition), box-shadow var(--transition);
}

textarea { min-height: 8rem; resize: vertical; }

input:hover,
select:hover,
textarea:hover { border-color: var(--vert); }

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--vert);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--vert) 25%, transparent);
}

input::placeholder,
textarea::placeholder { color: var(--texte-tenu); opacity: 1; }

select {
  appearance: none;
  padding-right: var(--e6);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236f6558'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--e3) center;
  background-size: 1rem;
}

.champ__inline {
  display: flex;
  gap: var(--e2);
  align-items: center;
  flex-wrap: wrap;
}

/* Jour / mois / année sur une seule ligne, chacun à sa largeur utile.
   Les sélecteurs sont qualifiés par leur type : sinon la règle générique
   « input[...] { width: 100% } », de spécificité supérieure à une simple
   classe, reprend le dessus et l'année occupe toute la largeur. */
.champ__inline select,
.champ__inline input[type="number"],
.champ__inline input[type="text"] {
  width: auto;
  flex: 0 0 auto;
}

.champ__inline select { min-width: 5.5rem; }

/* Le mois porte un libellé long (« septembre ») : il lui faut plus de place. */
.champ__inline select[id$="_m"] { min-width: 8rem; }

.champ__inline input[type="number"] { width: 6rem; }

/* Cases et boutons radio : la zone cliquable englobe le libellé. */
.case {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e2) 0;
  min-height: 2.75rem;
  cursor: pointer;
}

.case input[type="checkbox"],
.case input[type="radio"] {
  width: 1.25rem;
  height: 1.25rem;
  min-height: 0;
  margin-top: 0.1875rem;
  accent-color: var(--vert);
  flex-shrink: 0;
  cursor: pointer;
}

.case__texte { font-size: 0.9375rem; line-height: 1.5; }

fieldset {
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  padding: var(--e4);
  margin: 0 0 var(--e5);
}

legend {
  padding-inline: var(--e2);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--texte-doux);
}

.formulaire__actions {
  display: flex;
  gap: var(--e3);
  flex-wrap: wrap;
  padding-top: var(--e4);
  border-top: 1px solid var(--bordure);
}

/* --------------------------------------- champ mot de passe avec œil */

.champ-mdp { position: relative; }

/* De la place pour le bouton, sans que le texte passe dessous. */
.champ-mdp input { padding-right: 3rem; }

.champ-mdp__oeil {
  position: absolute;
  top: 0;
  right: 0;
  /* 44×44 : cible tactile réglementaire, alignée sur la hauteur du champ. */
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  color: var(--texte-tenu);
  cursor: pointer;
  border-radius: var(--rayon);
  transition: color var(--transition);
}

.champ-mdp__oeil:hover { color: var(--vert); }

.champ-mdp__oeil svg { width: 1.25rem; height: 1.25rem; }

/* ------------------------------------------- autocomplétion et jetons */

.suggestions-enveloppe { position: relative; }

.suggestions {
  position: absolute;
  z-index: var(--z-menu);
  top: calc(100% + 2px);
  left: 0;
  right: 0;
  margin: 0;
  padding: var(--e1);
  list-style: none;
  max-height: 17rem;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-3);
}

.suggestions li {
  padding: var(--e2) var(--e3);
  min-height: 2.75rem;
  display: flex;
  align-items: center;
  border-radius: var(--rayon);
  font-size: 0.9375rem;
  cursor: pointer;
}

.suggestions li:hover,
.suggestions li[aria-selected="true"] {
  background: var(--surface-2);
  color: var(--vert-fonce);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .suggestions li:hover, :root:not([data-theme="light"]) .suggestions li[aria-selected="true"] { color: var(--vert); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .suggestions li:hover, :root[data-theme="dark"] .suggestions li[aria-selected="true"] { color: var(--vert); }


.jetons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2);
  margin-top: var(--e3);
  min-height: 1.5rem;
}

.jeton__retirer {
  border: 0;
  background: none;
  color: inherit;
  font-size: 1.125rem;
  line-height: 1;
  padding: 0 0 0 var(--e1);
  cursor: pointer;
}

.jeton__retirer:hover { color: var(--erreur); }

/* ---------------------------------------------------------------- alertes */

.alerte {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e3) var(--e4);
  border-radius: var(--rayon);
  border: 1px solid;
  margin-bottom: var(--e4);
  font-size: 0.9375rem;
}

.alerte__icone { width: 1.25rem; height: 1.25rem; flex-shrink: 0; margin-top: 0.125rem; }

.alerte--succes { background: var(--succes-fond); border-color: var(--succes); color: var(--succes); }
.alerte--erreur { background: var(--erreur-fond); border-color: var(--erreur); color: var(--erreur); }
.alerte--info   { background: var(--info-fond);   border-color: var(--info);   color: var(--info); }

/* ---------------------------------------------------------------- tableaux */

.tableau-defilant {
  /* Les tableaux larges défilent dans leur cadre, jamais la page entière. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

caption {
  padding: var(--e3) var(--e4);
  text-align: left;
  font-size: 0.875rem;
  color: var(--texte-tenu);
}

th {
  text-align: left;
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--texte-tenu);
  background: var(--surface-2);
  padding: var(--e3) var(--e4);
  border-bottom: 1px solid var(--bordure);
  white-space: nowrap;
}

td {
  padding: var(--e3) var(--e4);
  border-bottom: 1px solid var(--bordure);
  vertical-align: middle;
}

tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-2); }

/* --------------------------------------------------------------- étiquettes */

.etiquette {
  display: inline-flex;
  align-items: center;
  gap: var(--e1);
  padding: 0.125rem var(--e2);
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: var(--surface-2);
  color: var(--texte-doux);
  border: 1px solid var(--bordure);
  white-space: nowrap;
}

.etiquette--terre { background: var(--vert-clair); color: var(--vert-fonce); border-color: var(--vert); }
.etiquette--olive { background: var(--succes-fond); color: var(--succes); border-color: var(--succes); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .etiquette--terre { background: var(--surface-2); color: var(--vert); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .etiquette--terre { background: var(--surface-2); color: var(--vert); }


/* -------------------------------------------------------------- compteurs */

/* Bandeau d'accueil : un aplat chaleureux qui donne le ton dès l'ouverture. */
.bandeau {
  display: grid;
  gap: var(--e5);
  padding: var(--e5);
  margin-bottom: var(--e6);
  border-radius: var(--rayon-lg);
  border: 1px solid var(--bordure);
  background:
    radial-gradient(circle at 12% 18%, color-mix(in srgb, var(--vert) 16%, transparent), transparent 55%),
    radial-gradient(circle at 88% 78%, color-mix(in srgb, var(--bleu) 18%, transparent), transparent 55%),
    var(--surface);
}

@media (min-width: 56rem) {
  .bandeau {
    /*
     * Trois colonnes : le titre prend ce qui reste, les compteurs et la photo
     * sont dimensionnés par leur contenu. La photo tombait sous le titre parce
     * que la grille n'en comptait que deux — elle passait donc à la ligne
     * suivante, en pleine largeur.
     */
    grid-template-columns: 1fr auto auto;
    align-items: center;
  }

  /* Dans le bandeau, les compteurs restent côte à côte : la colonne de
     droite est étroite et la règle auto-fit les empilerait. */
  .bandeau .compteurs {
    grid-template-columns: repeat(3, minmax(7rem, 1fr));
  }
}

.bandeau__titre { margin: 0 0 var(--e1); }

.bandeau__date {
  margin: 0;
  color: var(--texte-doux);
  font-size: 0.9375rem;
}

.compteurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr));
  gap: var(--e3);
}

.compteur {
  display: block;
  padding: var(--e4);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-left: 4px solid var(--bordure-forte);
  border-radius: var(--rayon-lg);
  text-align: center;
  text-decoration: none;
  color: inherit;
  transition: border-color var(--transition), box-shadow var(--transition);
}

a.compteur:hover {
  box-shadow: var(--ombre-2);
  color: inherit;
}

/* Chaque compteur porte sa propre couleur : c'est ce qui redonne de la vie
   à la page, et le libellé reste le porteur du sens. */
/*
 * Les compteurs puisent dans les mêmes variables que les graphiques : une
 * page où les tuiles de tête et les courbes ne parlent pas la même langue
 * paraît assemblée de bric et de broc. Les valeurs des deux modes suivent
 * automatiquement.
 */
.compteur--personnes { border-left-color: var(--gr-bleu); }
.compteur--photos    { border-left-color: var(--gr-orange); }
.compteur--unions    { border-left-color: var(--gr-violet); }
.compteur--noms      { border-left-color: var(--gr-emeraude); }
.compteur--prenoms   { border-left-color: var(--gr-rouge); }
.compteur--age       { border-left-color: var(--gr-bleu); }

.compteur--personnes .compteur__valeur { color: var(--gr-bleu); }
.compteur--photos    .compteur__valeur { color: var(--gr-orange); }
.compteur--unions    .compteur__valeur { color: var(--gr-violet); }
.compteur--noms      .compteur__valeur { color: var(--gr-emeraude); }
.compteur--prenoms   .compteur__valeur { color: var(--gr-rouge); }
.compteur--age       .compteur__valeur { color: var(--gr-bleu); }


/* Rubriques du bloc « Aujourd'hui » : un liseré coloré par nature
   d'événement, doublé du libellé — la couleur n'est jamais seule porteuse. */
.evt {
  font-size: 0.9375rem;
  margin: var(--e4) 0 var(--e2);
  padding-left: var(--e3);
  border-left: 3px solid var(--bordure-forte);
}

.evt--fete    { border-left-color: #2e7d4f; }
.evt--anniv   { border-left-color: #b0561f; }
.evt--mariage { border-left-color: #d97b28; }
.evt--deces   { border-left-color: var(--bordure-forte); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .evt--fete { border-left-color: #519f6f; }
  :root:not([data-theme="light"]) .evt--anniv { border-left-color: #c8783a; }
  :root:not([data-theme="light"]) .evt--mariage { border-left-color: #e9a463; }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .evt--fete { border-left-color: #519f6f; }
  :root[data-theme="dark"] .evt--anniv { border-left-color: #c8783a; }
  :root[data-theme="dark"] .evt--mariage { border-left-color: #e9a463; }


.carte--jour { border-left: 4px solid var(--vert); }

.compteur__valeur {
  display: block;
  font-family: var(--serif);
  font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.25rem);
  font-weight: 600;
  line-height: 1.1;
  /*
   * Le vert n'est qu'un défaut, pour un compteur sans teinte propre. Les
   * surcharges de mode qui figuraient ici l'imposaient à tous : plus
   * spécifiques et plus tardives, elles écrasaient la couleur de chaque
   * compteur, si bien que les bordures se coloraient et pas les nombres.
   * Les variables --gr-… savent déjà s'adapter au mode ; il n'y a plus rien
   * à corriger après coup.
   */
  color: var(--vert-fonce);
  font-variant-numeric: tabular-nums;
}


.compteur__libelle {
  font-size: 0.8125rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--texte-tenu);
}

/* ------------------------------------------------------------- calendrier */

.calendrier { width: 100%; border-collapse: collapse; }

.calendrier th {
  padding: var(--e2) 0;
  text-align: center;
  font-size: 0.75rem;
  background: transparent;
  border: 0;
}

.calendrier td {
  padding: 0;
  border: 1px solid var(--bordure);
  text-align: center;
}

.calendrier__jour {
  display: grid;
  place-items: center;
  /* Cible tactile suffisante même sur la grille dense du calendrier. */
  min-height: 2.75rem;
  width: 100%;
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
  color: var(--texte);
  text-decoration: none;
  cursor: pointer;
}

.calendrier__jour:hover { background: var(--surface-2); }
.calendrier__jour--hors-mois { color: var(--texte-tenu); opacity: 0.5; }

.calendrier__jour--aujourdhui {
  font-weight: 700;
  box-shadow: inset 0 0 0 2px var(--vert);
}

/* Un jour porteur d'événement est signalé par un point ET par son title. */
.calendrier__jour--evenement { background: var(--vert-clair); color: var(--vert-fonce); font-weight: 600; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .calendrier__jour--evenement { background: var(--surface-2); color: var(--vert); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .calendrier__jour--evenement { background: var(--surface-2); color: var(--vert); }


.calendrier__point {
  display: block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
  margin-top: 1px;
}

/* --------------------------------------------------------------- forum */

.message {
  padding: var(--e5);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
}

.message__entete {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding-bottom: var(--e3);
  margin-bottom: var(--e3);
  border-bottom: 1px solid var(--bordure);
  flex-wrap: wrap;
}

.message__sujet { font-family: var(--serif); font-size: 1.125rem; font-weight: 600; }
.message__corps { color: var(--texte); overflow-wrap: anywhere; }
.message__corps p:last-child { margin-bottom: 0; }

/* ------------------------------------------------------- barres de mesure */

.mesure { display: grid; gap: var(--e2); }

.mesure__ligne {
  display: grid;
  grid-template-columns: minmax(6rem, 10rem) 1fr auto;
  align-items: center;
  gap: var(--e3);
  font-size: 0.875rem;
}

/* La barre est un SVG : sa largeur est une donnée, et la politique de
   sécurité du contenu interdit un style calculé côté serveur. Les attributs
   géométriques SVG ne sont pas du CSS et passent donc sans exception. */
.mesure__piste {
  display: block;
  width: 100%;
  height: 0.625rem;
  overflow: visible;
}

.mesure__piste .fond  { fill: var(--surface-2); }
.mesure__piste .barre { fill: var(--vert); }

.mesure__valeur { font-variant-numeric: tabular-nums; color: var(--texte-tenu); }

.mesure__ligne--active span:first-child { font-weight: 600; }

/* --------------------------------------------------------- graphiques */

.figure {
  margin: 0;
}

.figure__titre {
  font-size: 0.9375rem;
  font-weight: 600;
  margin-bottom: var(--e1);
}

.figure__note {
  font-size: 0.8125rem;
  color: var(--texte-tenu);
  margin: var(--e2) 0 0;
}

.graphe {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  /*
   * Le SVG suit la largeur de son cadre : dans une carte pleine largeur, un
   * repère de 640 × 150 donnait un graphique de trois cents pixels de haut
   * pour douze colonnes. Le plafond le ramène à une proportion lisible ; les
   * graphiques des colonnes étroites, eux, n'atteignent jamais cette limite.
   */
  max-height: 17rem;
}

/*
 * LES TEINTES DES GRAPHIQUES.
 *
 * Cinq couleurs, choisies par scripts/validate_palette.js et non à l'œil.
 * Chacune passe la bande de clarté de son mode, le plancher de chroma et le
 * contraste sur la surface — en clair comme en sombre, où les valeurs sont
 * choisies séparément et non éclaircies mécaniquement.
 *
 * Une distinction qui décide de tout : ces teintes n'identifient pas des
 * séries qu'on compare, elles distinguent des SECTIONS. Deux d'entre elles ne
 * se rencontrent jamais dans un même repère — sauf la pyramide des âges, seul
 * graphique à deux séries. C'est donc cette paire-là, et elle seule, qui a été
 * validée sur tous les critères de séparation : bleu contre orange, ΔE 21 en
 * protanopie, 25 en vision normale, et une légende par-dessus, car l'identité
 * ne se confie jamais à la couleur seule.
 *
 * Le contrôle « toutes paires » échouerait sur bleu/violet — ΔE 2,5 en
 * protanopie. C'est sans conséquence ici : ils sont à trois écrans l'un de
 * l'autre. Le mentionner plutôt que de le taire évite qu'on les rapproche un
 * jour dans un même graphique sans y penser.
 */

:root {
  --gr-bleu:     #2f62a8;
  --gr-orange:   #c25a1e;
  --gr-violet:   #7d4ea3;
  --gr-rouge:    #a4342f;
  --gr-emeraude: #0e7f5c;

  /* La teinte en service, qu'une section peut remplacer. */
  --gr-trait:  var(--gr-bleu);
  --gr-trait-2: var(--gr-orange);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --gr-bleu:     #4d8cc9;
    --gr-orange:   #cd7433;
    --gr-violet:   #a077cc;
    --gr-rouge:    #cc5f5a;
    --gr-emeraude: #3d9e7c;
  }
}

:root[data-theme="dark"] {
  --gr-bleu:     #4d8cc9;
  --gr-orange:   #cd7433;
  --gr-violet:   #a077cc;
  --gr-rouge:    #cc5f5a;
  --gr-emeraude: #3d9e7c;
}

/* Une section choisit sa teinte ; tout ce qu'elle contient suit. */
[data-teinte="bleu"]     { --gr-trait: var(--gr-bleu); }
[data-teinte="orange"]   { --gr-trait: var(--gr-orange); }
[data-teinte="violet"]   { --gr-trait: var(--gr-violet); }
[data-teinte="rouge"]    { --gr-trait: var(--gr-rouge); }
[data-teinte="emeraude"] { --gr-trait: var(--gr-emeraude); }

.graphe .marque    { fill: var(--gr-trait); }
.graphe .marque--f { fill: var(--gr-trait); }
.graphe .marque--h { fill: var(--gr-trait-2); }

/* --------------------------------------------------------- courbe */

.graphe .courbe__aire  { fill: var(--gr-trait); opacity: 0.14; }
.graphe .courbe__trait { fill: none; stroke: var(--gr-trait); stroke-width: 2; stroke-linejoin: round; }

/* Anneau de la couleur du fond : le point reste net là où la courbe passe. */
.graphe .courbe__point { fill: var(--gr-trait); stroke: var(--surface); stroke-width: 2; }

/* ---------------------------------------------------------- jauge */

.jauges {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--e5);
}

.jauge__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e3);
  margin-bottom: var(--e2);
}

.jauge__titre  { font-size: 0.9375rem; font-weight: 600; }
.jauge__valeur { font-size: 1.125rem; font-weight: 700; color: var(--gr-trait); font-variant-numeric: tabular-nums; }

.jauge__piste { display: block; width: 100%; height: 0.5rem; }
.jauge__fond  { fill: var(--surface-2); }
.jauge__part  { fill: var(--gr-trait); }

.jauge__note { margin: var(--e2) 0 0; font-size: 0.8125rem; color: var(--texte-tenu); }

/*
 * Les barres horizontales des palmarès suivent la teinte de leur carte.
 *
 * La règle est restreinte aux cartes qui déclarent une teinte : sans cela
 * elle repeignait aussi la barre d'avancement du formulaire de fiche, qui
 * n'a rien d'un graphique et doit rester verte comme le reste du site.
 */
[data-teinte] .mesure__piste .barre { fill: var(--gr-trait); }

.graphe .grille      { stroke: var(--bordure); stroke-width: 1; }
.graphe .axe-texte   { fill: var(--texte-tenu); font-size: 10px; }
.graphe .val-texte   { fill: var(--texte-doux); font-size: 10px; font-weight: 600; }

.legende {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e4);
  margin-top: var(--e3);
  font-size: 0.8125rem;
  color: var(--texte-doux);
}

.legende__item { display: inline-flex; align-items: center; gap: var(--e2); }

.legende__puce {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 3px;
  flex-shrink: 0;
}

/*
 * Les puces de la légende reprennent les variables des marques : une légende
 * qui ne s'accorde pas au graphique qu'elle explique est pire qu'aucune.
 */
.legende__puce--f { background: var(--gr-trait); }
.legende__puce--h { background: var(--gr-trait-2); }


/* Vue tableau : toute donnée d'un graphique reste accessible en texte. */
.figure details summary {
  cursor: pointer;
  font-size: 0.8125rem;
  color: var(--texte-doux);
  margin-top: var(--e3);
}

.figure details[open] summary { margin-bottom: var(--e3); }

/* --------------------------------------------------------------- divers */

.fil-ariane {
  font-size: 0.875rem;
  color: var(--texte-tenu);
  margin-bottom: var(--e4);
}

.fil-ariane a { color: var(--texte-doux); }

.vide {
  padding: var(--e7) var(--e4);
  text-align: center;
  color: var(--texte-tenu);
  border: 1px dashed var(--bordure-forte);
  border-radius: var(--rayon-lg);
}

.vide__titre { font-family: var(--serif); font-size: 1.125rem; color: var(--texte-doux); margin-bottom: var(--e2); }

.pagination {
  display: flex;
  gap: var(--e2);
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  margin-top: var(--e6);
}

.pagination__lien {
  display: inline-grid;
  place-items: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding-inline: var(--e2);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  color: var(--texte-doux);
  text-decoration: none;
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
}

.pagination__lien:hover { background: var(--surface-2); border-color: var(--vert); }

.pagination__lien[aria-current="page"] {
  background: var(--vert);
  border-color: var(--vert);
  color: #fff;
  font-weight: 600;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .pagination__lien[aria-current="page"] { color: #1a1510; }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .pagination__lien[aria-current="page"] { color: #1a1510; }


.pied {
  padding-block: var(--e6);
  margin-top: var(--e8);
  border-top: 1px solid var(--bordure);
  font-size: 0.875rem;
  color: var(--texte-tenu);
}

/* Écrans de connexion : mise en page centrée sans navigation */
.ecran-simple {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--e5) var(--e4);
}

.ecran-simple__boite {
  width: 100%;
  max-width: 24rem;
}

.ecran-simple__marque {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e2);
  margin-bottom: var(--e5);
  text-align: center;
}

.ecran-simple__marque svg { width: 3rem; height: 3rem; color: var(--vert); }

/*
 * Le logo était servi à sa taille naturelle : deux cent vingt pixels, soit un
 * tiers de la hauteur utile d'un téléphone, qui repoussaient hors de l'écran les
 * façons de se connecter autres que le mot de passe. Il reste reconnaissable
 * bien plus petit — et il ne sert qu'à dire où l'on est.
 */
.ecran-simple__marque img {
  width: min(7.5rem, 34vw);
  height: auto;
}

/* Sur un écran court — un téléphone couché, une petite fenêtre — on rogne
   encore : c'est là que la place manque le plus. */
@media (max-height: 40rem) {
  .ecran-simple { padding-block: var(--e3); }
  .ecran-simple__marque { margin-bottom: var(--e3); }
  .ecran-simple__marque img { width: min(4.5rem, 22vw); }
}

/* ------------------------------------------------------------ utilitaires

   Petit jeu de classes destiné à remplacer les attributs style= dans les
   gabarits. La politique de sécurité du contenu (style-src 'self') interdit
   les styles en ligne : sans ces classes, chaque style= serait silencieusement
   ignoré par le navigateur.
   ------------------------------------------------------------------------ */

.m0        { margin: 0; }
.mt0       { margin-top: 0; }
.mt1       { margin-top: var(--e1); }
.mb1       { margin-bottom: var(--e1); }
.mt7       { margin-top: var(--e7); }
.casse-normale { text-transform: none; }
.mb0       { margin-bottom: 0; }
.mt2       { margin-top: var(--e2); }
.mt3       { margin-top: var(--e3); }
.mt4       { margin-top: var(--e4); }
.mt5       { margin-top: var(--e5); }
.mb2       { margin-bottom: var(--e2); }
.mb3       { margin-bottom: var(--e3); }
.mb4       { margin-bottom: var(--e4); }
.mb5       { margin-bottom: var(--e5); }
.mb6       { margin-bottom: var(--e6); }
.mb7       { margin-bottom: var(--e7); }

.tenu      { color: var(--texte-tenu); }
.doux      { color: var(--texte-doux); }
.terre     { color: var(--vert-fonce); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .terre { color: var(--vert); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .terre { color: var(--vert); }


.t-xxs     { font-size: 0.75rem; }
.t-xs      { font-size: 0.8125rem; }
.t-sm      { font-size: 0.875rem; }
.t-md      { font-size: 0.9375rem; }
.t-lg      { font-size: 1.0625rem; }
.t-xl      { font-size: 1.125rem; }

.serif     { font-family: var(--serif); }
.gras      { font-weight: 600; }
.normal    { font-weight: 400; }
.italique  { font-style: italic; }
.centre    { text-align: center; }
.droite    { text-align: right; }
.nowrap    { white-space: nowrap; }
.chiffres  { font-variant-numeric: tabular-nums; }
.mono      { font-family: var(--mono); }
.sans-deco { text-decoration: none; color: inherit; }

.pleine    { width: 100%; }
/*
 * Qualifiées par type : « input[type="search"] » pèse plus lourd qu'une
 * simple classe dans la cascade, et reprendrait la largeur pleine.
 */
.auto,
input.auto, select.auto, textarea.auto           { width: auto; }
.l-etroit,
input.l-etroit, select.l-etroit                  { width: 7rem; flex: none; }
.l-moyen,
input.l-moyen, select.l-moyen                    { min-width: 12rem; width: auto; flex: 1 1 12rem; }

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

/* Galerie resserrée des colonnes latérales */
.galerie--mini {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 5.5rem), 1fr));
}

/* Bloc de commande à copier dans un terminal */
.commande {
  display: block;
  overflow-x: auto;
  padding: var(--e3);
  margin: var(--e3) 0;
  background: var(--surface-2);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--texte);
  white-space: pre;
}

/* Bouton de formulaire présenté comme un lien de navigation */
.nav__bouton-lien {
  border: 0;
  background: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
  font: inherit;
}

.separateur      { border-bottom: 1px solid var(--bordure); }
.separateur-haut { border-top: 1px solid var(--bordure); }
.pad-v2          { padding-block: var(--e2); }
.pad-v3          { padding-block: var(--e3); }
.pad5            { padding: var(--e5); }

.encadre-fort {
  border-color: var(--vert);
  border-width: 2px;
}

.pleine-hauteur-carte { grid-column: 1 / -1; }

/* Listes de définitions des fiches */
.defs {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--e2) var(--e4);
  margin: 0;
  font-size: 0.9375rem;
}

.defs dt { color: var(--texte-tenu); }
.defs dd { margin: 0; }

/* ------------------------------------------------------ mouvement réduit */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------- impression */

@media print {
  .entete,
  .nav,
  .pied,
  .formulaire__actions,
  .bouton { display: none !important; }

  body { background: #fff; color: #000; font-size: 11pt; }
  .carte, .personne { border: 1px solid #999; box-shadow: none; }
  a { color: #000; text-decoration: underline; }
  .arbre { overflow: visible; }
}

/* --------------------------------------------- chemin par alliance ------ */

.chemin { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--e2); }

.chemin__etape { position: relative; }

/* Le libellé de la relation s'intercale entre deux personnes. */
.chemin__relation {
  display: inline-block;
  margin: var(--e2) 0 var(--e2) var(--e5);
  padding: var(--e1) var(--e3);
  border-radius: 999px;
  background: var(--vert-clair);
  border: 1px solid var(--vert-bord);
  color: var(--vert-fonce);
  font-size: 0.8125rem;
  font-weight: 600;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chemin__relation { color: var(--vert); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .chemin__relation { color: var(--vert); }


/* ------------------------------------------------------------- cartes --- */

.carte-figure { margin: 0; }

/*
 * Le SVG des cartes géographiques porte « carte-dessin » et non « carte » :
 * ce dernier nom désigne déjà le bloc encadré du site, et le SVG héritait
 * donc de son fond, de sa bordure et de son rembourrage.
 */
.carte-dessin {
  display: block;
  width: 100%;
  height: auto;
  /* Les tracés sont fins : on les laisse déborder légèrement du cadre. */
  overflow: visible;
}

/* Rampe séquentielle : une seule teinte, du clair au foncé. La magnitude se
   lit dans la clarté, jamais dans la teinte. */
.carte__zone           { fill: var(--surface-2); stroke: var(--surface); stroke-width: 0.7; }
.carte__zone--n0       { fill: #eaf4ed; }
.carte__zone--n1       { fill: #c7e3d1; }
.carte__zone--n2       { fill: #9bceae; }
.carte__zone--n3       { fill: #66b287; }
.carte__zone--n4       { fill: #3d9463; }
.carte__zone--n5       { fill: #22713f; }

/* Pour la carte des régions, les frontières départementales s'effacent. */
.carte__zone--fondue   { stroke-width: 0.35; stroke: rgba(255,255,255,.45); }

.carte__lien .carte__zone { cursor: pointer; transition: fill 120ms; }
.carte__lien:hover .carte__zone { fill: var(--vert); }
.carte__lien:focus-visible .carte__zone { stroke: var(--texte); stroke-width: 2; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .carte__zone { fill: var(--surface-2); stroke: var(--surface); }
  :root:not([data-theme="light"]) .carte__zone--n0 { fill: #1f3227; }
  :root:not([data-theme="light"]) .carte__zone--n1 { fill: #26492f; }
  :root:not([data-theme="light"]) .carte__zone--n2 { fill: #2f6b41; }
  :root:not([data-theme="light"]) .carte__zone--n3 { fill: #3d8b55; }
  :root:not([data-theme="light"]) .carte__zone--n4 { fill: #55a473; }
  :root:not([data-theme="light"]) .carte__zone--n5 { fill: #77c093; }
  :root:not([data-theme="light"]) .carte__zone--fondue { stroke: rgba(0,0,0,.35); }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .carte__zone { fill: var(--surface-2); stroke: var(--surface); }
  :root[data-theme="dark"] .carte__zone--n0 { fill: #1f3227; }
  :root[data-theme="dark"] .carte__zone--n1 { fill: #26492f; }
  :root[data-theme="dark"] .carte__zone--n2 { fill: #2f6b41; }
  :root[data-theme="dark"] .carte__zone--n3 { fill: #3d8b55; }
  :root[data-theme="dark"] .carte__zone--n4 { fill: #55a473; }
  :root[data-theme="dark"] .carte__zone--n5 { fill: #77c093; }
  :root[data-theme="dark"] .carte__zone--fondue { stroke: rgba(0,0,0,.35); }


.carte__echelle {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e3);
  margin-top: var(--e3);
  font-size: 0.75rem;
  color: var(--texte-tenu);
}

.carte__echelle-titre {
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.carte__palier { display: inline-flex; align-items: center; gap: var(--e1); }

.carte__puce {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 3px;
  border: 1px solid var(--bordure);
}

.carte__puce--n0 { background: #eaf4ed; }
.carte__puce--n1 { background: #c7e3d1; }
.carte__puce--n2 { background: #9bceae; }
.carte__puce--n3 { background: #66b287; }
.carte__puce--n4 { background: #3d9463; }
.carte__puce--n5 { background: #22713f; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .carte__puce--n0 { background: #1f3227; }
  :root:not([data-theme="light"]) .carte__puce--n1 { background: #26492f; }
  :root:not([data-theme="light"]) .carte__puce--n2 { background: #2f6b41; }
  :root:not([data-theme="light"]) .carte__puce--n3 { background: #3d8b55; }
  :root:not([data-theme="light"]) .carte__puce--n4 { background: #55a473; }
  :root:not([data-theme="light"]) .carte__puce--n5 { background: #77c093; }
}

/* Choix explicite du thème sombre par le visiteur */
:root[data-theme="dark"] .carte__puce--n0 { background: #1f3227; }
  :root[data-theme="dark"] .carte__puce--n1 { background: #26492f; }
  :root[data-theme="dark"] .carte__puce--n2 { background: #2f6b41; }
  :root[data-theme="dark"] .carte__puce--n3 { background: #3d8b55; }
  :root[data-theme="dark"] .carte__puce--n4 { background: #55a473; }
  :root[data-theme="dark"] .carte__puce--n5 { background: #77c093; }



/* ------------------------------------------------- sélecteur de thème --- */

.theme {
  margin-top: var(--e2);
  padding: var(--e3);
  border-top: 1px solid var(--bordure);
}

.theme__titre {
  display: block;
  margin-bottom: var(--e2);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--texte-tenu);
}

.theme__choix {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  padding: 2px;
  background: var(--surface-2);
  border-radius: var(--rayon);
}

.theme__bouton {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--e2) var(--e1);
  border: 0;
  border-radius: calc(var(--rayon) - 2px);
  background: none;
  color: var(--texte-tenu);
  font: 500 0.6875rem/1.2 var(--sans);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.theme__bouton svg { width: 1rem; height: 1rem; }

.theme__bouton:hover { color: var(--texte); }

.theme__bouton[aria-pressed="true"] {
  background: var(--surface);
  color: var(--vert);
  box-shadow: var(--ombre-1);
}

/* ---------------------------------------------------------------------------
 * Formulaire de fiche : sections dépliables.
 *
 * Le marqueur natif de <summary> est retiré au profit d'un chevron placé à
 * droite, qui pivote. Deux règles sont nécessaires et non une : Firefox et
 * Chrome utilisent « list-style », Safari le pseudo-élément WebKit.
 * ------------------------------------------------------------------------ */

.section {
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
  margin-bottom: var(--e3);
  /*
   * Pas de « overflow: hidden » ici, quoiqu'il arrondirait commodément le
   * fond de l'en-tête au survol : le calendrier des dates se déploie hors du
   * cadre de la section et s'en trouverait décapité. L'arrondi est donc porté
   * par l'en-tête lui-même, juste en dessous.
   */
}

.section[open] {
  border-color: var(--bordure-forte);
}

.section__tete {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding: var(--e3) var(--e4);
  cursor: pointer;
  list-style: none;
  user-select: none;
  border-radius: calc(var(--rayon) - 1px);
}

/* Repliée, la section n'est qu'un en-tête : il porte l'arrondi des quatre
   coins. Dépliée, ses angles du bas rejoignent le corps. */
.section[open] .section__tete {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.section__tete::-webkit-details-marker { display: none; }

.section__tete:hover { background: var(--surface-2); }

.section__tete:focus-visible {
  outline: 2px solid var(--vert);
  outline-offset: -2px;
}

.section__icone {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--vert);
}

.section__texte {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.section__titre {
  font-weight: 600;
  color: var(--texte);
}

.section__accroche {
  font-size: 0.8125rem;
  color: var(--texte-doux);
}

/*
 * Le compteur. Il reste discret tant que la section est incomplète : un
 * badge rouge sur « 0/3 » transformerait une fiche en devoir non rendu.
 */
.section__compte {
  flex: 0 0 auto;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--texte-doux);
  background: var(--fond);
  border: 1px solid var(--bordure);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
}

.section__compte--plein {
  color: var(--vert);
  border-color: var(--vert);
}

.section__chevron {
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  color: var(--texte-doux);
  transition: transform 0.15s ease;
}

.section[open] .section__chevron { transform: rotate(180deg); }

.section__corps {
  padding: 0 var(--e4) var(--e4);
  border-top: 1px solid var(--bordure);
  padding-top: var(--e4);
}

.section__corps .champ:last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  .section__chevron { transition: none; }
}

/* ---------------------------------------- barre d'avancement de la fiche */

.avancement {
  padding: var(--e4);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  background: var(--surface);
}

.avancement__ligne {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e3);
  margin-bottom: var(--e2);
}

.avancement__libelle {
  font-size: 0.875rem;
  color: var(--texte-doux);
}

.avancement__valeur {
  font-size: 1.25rem;
  color: var(--vert);
  font-variant-numeric: tabular-nums;
}

.avancement .mesure__piste { height: 0.5rem; }

.avancement__note { margin: var(--e2) 0 0; }

/* ---------------------------------------------------------------------------
 * Le CTA du bandeau : « Ajouter une personne ».
 *
 * Il vient d'un menu latéral où il ne servait qu'à ceux qui étaient déjà dans
 * « Ma famille ». Créer une fiche est une action, et une action se propose
 * partout.
 * ------------------------------------------------------------------------ */

.cta-entete {
  flex: 0 0 auto;
  white-space: nowrap;
}

/*
 * Le SVG n'a ni largeur ni hauteur propres : dans les boutons du corps de page
 * il hérite d'une taille, mais le bandeau n'a pas cette règle et le
 * pictogramme se réduisait à zéro — un pavé vert sans rien dedans.
 */
.cta-entete svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: 0 0 auto;
}

/*
 * Sous 68rem, le bandeau porte déjà la marque, la recherche, la photo du
 * hasard, la messagerie et le profil. Le libellé s'efface et le bouton se
 * réduit à sa croix — le geste reste, l'encombrement disparaît.
 */
@media (max-width: 68rem) {
  .cta-entete__texte { display: none; }
  .cta-entete { padding-inline: var(--e3); }
}

/* --------------------------------- le choix d'une recherche par lieu */

.choix-lieu {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
  padding: var(--e4);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.choix-lieu:hover,
.choix-lieu:focus-visible {
  border-color: var(--vert);
  transform: translateY(-2px);
}

/*
 * L'illustration est la vraie carte du site, dessinée à vide. Elle est donc
 * un SVG de plein format : la hauteur est bornée ici, et le débordement
 * masqué, sans quoi la carte du monde imposerait sa proportion à la carte.
 */
.choix-lieu__dessin {
  display: block;
  max-height: 9rem;
  overflow: hidden;
  border-radius: var(--rayon);
  background: var(--surface-2);
  padding: var(--e2);
}

.choix-lieu__dessin svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 8.5rem;
}


.choix-lieu__texte {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
}

.choix-lieu__titre {
  font-family: var(--serif);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--texte);
}

.choix-lieu__accroche {
  font-size: 0.9375rem;
  color: var(--texte-doux);
  line-height: 1.5;
}

.choix-lieu__compte {
  font-size: 0.8125rem;
  color: var(--vert);
}

@media (prefers-reduced-motion: reduce) {
  .choix-lieu { transition: none; }
  .choix-lieu:hover { transform: none; }
}

/* Ligne mise en avant dans un tableau (le pays choisi sur la carte du monde). */
.ligne--active { background: var(--vert-clair); }

/* ---------------------------------------------------------------------------
 * Les ancres et le bandeau collant.
 *
 * Le bandeau est en « position: sticky » et mesure environ 120 px. Un lien
 * vers « #photos » amenait donc le titre exactement dessous : on arrivait sur
 * une liste de photos décapitée, sans le nom du lieu — signalé sur
 * /agora?lieu=…#photos, mais le défaut valait pour toutes les ancres du site,
 * #resultats et #detail compris.
 *
 * « scroll-margin-top » ne coûte rien aux éléments qu'on n'atteint jamais par
 * une ancre : le navigateur ne s'en sert qu'au moment de faire défiler
 * jusqu'à eux. La règle peut donc rester large.
 * ------------------------------------------------------------------------ */

[id] {
  /*
   * Une seule valeur, sans exception pour les petits écrans : le bandeau y
   * reste collant et garde la même hauteur. J'avais d'abord réduit la marge
   * sous 48 rem en supposant le contraire — mesure faite, il fait 120 px des
   * deux côtés. Un peu de blanc au-dessus d'une ancre ne gêne personne ;
   * atterrir sous le bandeau, si.
   */
  scroll-margin-top: 8.5rem;
}

/* ---------------------------------------------------------------------------
 * Le bandeau sur un téléphone.
 *
 * À 375 px, ses sept éléments réclamaient 392 px : la page débordait de dix-sept
 * pixels vers la droite, l'avatar était rogné et tout le site défilait
 * latéralement. Mesuré, l'excédent tenait au nom du site — cent vingt pixels
 * à lui seul, à côté du logo qui dit déjà de quel site il s'agit.
 *
 * C'est donc lui qui cède, et rien d'autre : chacun des six éléments restants
 * est un geste (ouvrir le menu, chercher, ajouter, lire ses messages, son
 * profil), et un geste retiré est un geste perdu.
 * ------------------------------------------------------------------------ */

@media (max-width: 40rem) {
  .marque__nom { display: none; }

  /*
   * Les onglets débordent aussi, mais eux défilent — c'est voulu. Un dégradé
   * sur le bord droit le dit : sans lui, « Admin » semble ne pas exister.
   */
  .onglets {
    -webkit-mask-image: linear-gradient(to right, #000 85%, transparent);
    mask-image: linear-gradient(to right, #000 85%, transparent);
  }

  /* Le logo garde sa place de lien vers l'accueil, sans le texte à côté. */
  .marque { gap: 0; }

  .entete__interieur {
    gap: var(--e2);
    padding-inline: var(--e3);
  }
}

/*
 * Filet de sécurité. Le bandeau n'était pas seul en cause : n'importe quel
 * tableau ou titre trop large fait défiler la page entière sur le côté, et le
 * défaut ne se voit qu'au téléphone, là où personne ne teste. Les contenus
 * larges ont leur propre conteneur à défilement ; le corps de page, lui, ne
 * doit jamais bouger horizontalement.
 */
html, body { overflow-x: hidden; }

/* ------------------------------------------ choix du fournisseur de courriels */

.fournisseurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--e3);
}

.fournisseur {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  padding: var(--e3) var(--e4);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  cursor: pointer;
}

/*
 * LE BOUTON RADIO RESTE VISIBLE, et ce n'est pas un choix esthétique.
 *
 * Je l'avais masqué, en confiant l'indication du choix à
 * « .fournisseur:has(input:checked) ». Sur un navigateur sans « :has() », il
 * ne restait alors AUCUN signe de ce qui était coché : trois cartes
 * identiques, dont une déjà sélectionnée à l'insu du visiteur. En production,
 * un administrateur a cru choisir Brevo, Mailjet est resté coché, et sa clé
 * Brevo est partie dans MAILJET_API_KEY.
 *
 * Le rond natif est toujours juste, sur tous les navigateurs, sans qu'on ait
 * à le réinventer. La mise en valeur de la carte vient par-dessus, comme
 * agrément — plus jamais comme seule indication.
 */
.fournisseur {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--e3);
}

.fournisseur input {
  flex: 0 0 auto;
  margin-top: 0.15rem;
}

.fournisseur__texte {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  min-width: 0;
}

.fournisseur:hover { border-color: var(--bordure-forte); }

/*
 * L'agrément passe par le frère du bouton : « input:checked + … » fonctionne
 * partout, là où « :has() » manque encore à l'appel sur les navigateurs un
 * peu anciens.
 */
.fournisseur input:checked + .fournisseur__texte .fournisseur__nom {
  color: var(--vert);
}

.fournisseur:focus-within {
  outline: 2px solid var(--vert);
  outline-offset: 2px;
}

.fournisseur__nom { font-weight: 600; color: var(--texte); }

.fournisseur__voie {
  font-size: 0.75rem;
  color: var(--texte-tenu);
  font-family: var(--mono);
}

/* ---------------------------------------------------------------------------
 * La boîte à idées, dans le bandeau.
 *
 * Elle voisine la messagerie : ce sont les deux façons de dire quelque chose
 * au reste de la famille, l'une adressée, l'autre ouverte. Elle en reprend
 * donc exactement l'allure — deux icônes côte à côte qui ne se ressemblent pas
 * se liraient comme deux fonctions sans rapport.
 * ------------------------------------------------------------------------ */

/*
 * LA MARGE AUTOMATIQUE EST ICI, ET NULLE PART AILLEURS.
 *
 * Elle était portée par la messagerie ET par le menu du compte : chacun
 * réclamant l'espace libre, le navigateur le partageait entre les deux. Ampoule
 * collée au champ de recherche, enveloppe au milieu de nulle part, compte à
 * droite — trois éléments d'un même groupe éparpillés sur toute la largeur.
 *
 * Posée sur le premier des trois, elle pousse le groupe entier à droite et les
 * laisse côte à côte, ce qu'ils n'auraient jamais dû cesser d'être.
 */
.idee-entete {
  flex: 0 0 auto;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  color: var(--texte-doux);
  text-decoration: none;
  transition: background var(--transition), color var(--transition);
}

.idee-entete:hover { background: var(--surface-2); color: var(--vert-fonce); }
.idee-entete svg { width: 1.35rem; height: 1.35rem; }

/* ------------------------------------------- le menu « Ajouter » du bandeau */

.ajout-entete { position: relative; flex: 0 0 auto; }

.ajout-entete__calque {
  display: none;
  position: absolute;
  left: 0;
  top: calc(100% + var(--e2));
  z-index: var(--z-menu);
  min-width: 12rem;
  padding: var(--e2);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-3);
}

.ajout-entete__bascule:checked ~ .ajout-entete__calque { display: block; }

/*
 * Le focus clavier doit se voir sur l'étiquette, qui a pris le rôle du bouton.
 */
.ajout-entete__bascule:focus-visible + .cta-entete {
  outline: 2px solid var(--vert);
  outline-offset: 2px;
}

.ajout-entete__lien {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding: var(--e3);
  border-radius: var(--rayon);
  color: var(--texte);
  text-decoration: none;
  font-size: 0.9375rem;
}

.ajout-entete__lien:hover { background: var(--surface-2); color: var(--vert-fonce); }
.ajout-entete__lien svg { width: 1.1rem; height: 1.1rem; flex: 0 0 auto; color: var(--vert); }

/* Sur un téléphone, le calque s'ancre à droite pour ne pas sortir de l'écran. */
@media (max-width: 40rem) {
  .ajout-entete__calque { left: auto; right: 0; }
}

/* ---------------------------------------------------------------------------
 * La visite guidée du premier passage.
 *
 * Le voile assombrit la page, le halo découpe l'élément dont on parle, la
 * bulle porte le texte. Les trois sont positionnés en JavaScript : la
 * politique de sécurité interdit les styles écrits dans le HTML, mais pas les
 * affectations à element.style — vérifié dans le navigateur avant d'écrire
 * ceci plutôt que supposé.
 * ------------------------------------------------------------------------ */

.visite { position: fixed; inset: 0; z-index: var(--z-modale, 9000); }

/*
 * UN SEUL MÉCANISME D'ASSOMBRISSEMENT, et c'est le halo.
 *
 * Son ombre portée de 9999 px couvre tout l'écran sauf son propre rectangle :
 * elle assombrit et découpe d'un même geste. J'avais d'abord ajouté un voile
 * séparé par-dessus, qu'il fallait masquer quand le halo paraissait — au
 * moyen de « :has() », c'est-à-dire du piège déjà rencontré sur le choix du
 * fournisseur de courriels. Deux mécanismes valaient une couche de trop et
 * une règle qui ne s'applique pas partout ; il n'en reste qu'un.
 *
 * Le cadre entoure l'élément plutôt que de le détourer : connaître la forme
 * exacte de chaque cible serait fragile, un rectangle lumineux désigne aussi
 * bien et ne se trompe jamais de contour.
 */
.visite__halo {
  position: absolute;
  border-radius: var(--rayon-lg);
  border: 2px solid var(--vert);
  box-shadow: 0 0 0 9999px rgb(0 0 0 / 0.55), 0 0 0 4px rgb(46 125 79 / 0.35);
  pointer-events: none;
  transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease;
}

.visite__bulle {
  position: absolute;
  width: min(22rem, calc(100vw - 2rem));
  padding: var(--e5);
  background: var(--surface);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-3);
}

.visite__compteur {
  margin: 0 0 var(--e2);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--texte-tenu);
}

.visite__titre {
  margin: 0 0 var(--e3);
  font-size: 1.125rem;
}

.visite__texte {
  margin: 0 0 var(--e4);
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--texte-doux);
}

.visite__actions {
  display: flex;
  align-items: center;
  gap: var(--e2);
}

.visite__espace { flex: 1 1 auto; }

@media (prefers-reduced-motion: reduce) {
  .visite__halo { transition: none; }
}

/* Sur un téléphone, la bulle occupe le bas de l'écran plutôt que de courir
   après une cible qui tient déjà toute la largeur. */
@media (max-width: 30rem) {
  .visite__bulle { padding: var(--e4); }
}

/* Un bouton qui vit parmi des liens en prend l'apparence. */
.moi__lien--bouton {
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
}

/* ------------------------------------------ l'appel à identifier les photos */

.appel-qui {
  display: flex;
  align-items: center;
  gap: var(--e4);
  padding: var(--e4);
  margin-bottom: var(--e5);
  background: var(--vert-clair);
  border: 1px solid var(--vert);
  border-radius: var(--rayon-lg);
  color: inherit;
  text-decoration: none;
}

.appel-qui:hover { box-shadow: var(--ombre-2); }

.appel-qui__dessin { flex: 0 0 auto; width: 4rem; height: 3rem; color: var(--vert-fonce); }

.appel-qui__texte { flex: 1 1 auto; display: flex; flex-direction: column; gap: var(--e1); min-width: 0; }
.appel-qui__titre { font-weight: 700; color: var(--vert-fonce); }
.appel-qui__note  { font-size: 0.875rem; color: var(--texte-doux); line-height: 1.45; }

.appel-qui__action {
  flex: 0 0 auto;
  padding: var(--e2) var(--e4);
  border-radius: 999px;
  background: var(--vert);
  color: #fff;
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
}

@media (max-width: 40rem) {
  .appel-qui { flex-wrap: wrap; }
  .appel-qui__action { width: 100%; text-align: center; }
}

/* ------------------------------------------------------- « Perdu de vue » */

.perdus { border-color: var(--vert); }

.perdus__dessin {
  display: block;
  width: 6rem;
  height: 4.5rem;
  margin-bottom: var(--e3);
  color: var(--vert);
}

.perdus__titre { margin-bottom: var(--e2); }

.perdus__phrase {
  margin: 0 0 var(--e4);
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--texte-doux);
}

.perdus__reste { margin: var(--e3) 0 0; }

/* --------------------------------------------------------- les documents */

.doc {
  display: flex;
  align-items: center;
  gap: var(--e4);
  padding: var(--e3) var(--e4);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  text-decoration: none;
  color: inherit;
}

.doc:hover { border-color: var(--vert); }

/*
 * L'extension tient lieu de pictogramme. Un dessin par format serait plus
 * joli et moins juste : « PDF » et « TIF » ne se confondent pas, et le
 * visiteur sait déjà ce que chacun veut dire.
 */
.doc__icone {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--rayon);
  background: var(--surface-2);
  color: var(--texte-doux);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.03em;
}

.doc__texte { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.doc__titre { font-weight: 600; color: var(--texte); }
.doc__meta  { font-size: 0.8125rem; color: var(--texte-tenu); }

.doc--serre { padding: var(--e2) var(--e3); }
.doc--serre .doc__icone { width: 2.25rem; height: 2.25rem; font-size: 0.625rem; }

/* ------------------------------------ « ne s'est jamais connecté » */

.jamais__dessin {
  display: block;
  width: 4.5rem;
  height: 3rem;
  margin-bottom: var(--e3);
  color: var(--vert);
}

/* ------------------------------------ la photo au hasard, sur l'accueil */

.hasard {
  display: flex;
  align-items: stretch;
  gap: 0;
  flex: 0 1 22rem;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
}

.hasard:hover { border-color: var(--vert); }

.hasard__image {
  flex: 0 0 8rem;
  width: 8rem;
  height: 100%;
  object-fit: cover;
  background: var(--surface-2);
}

.hasard__texte {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  justify-content: center;
  padding: var(--e3) var(--e4);
  min-width: 0;
}

.hasard__surtitre {
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--vert);
  font-weight: 700;
}

.hasard__nom { font-weight: 600; color: var(--texte); }

.hasard__lien,
.hasard__legende {
  font-size: 0.8125rem;
  color: var(--texte-doux);
  /* Une légende peut être longue : deux lignes, puis on coupe. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.hasard__legende { color: var(--texte-tenu); font-style: italic; }

@media (max-width: 48rem) {
  .hasard { flex-basis: 100%; }
}

/* ------------------------------------- l'accueil de la visite guidée */

/*
 * LE VOILE DE L'ÉCRAN D'ACCUEIL, ET POURQUOI IL EST ICI ET NON SUR LA CARTE.
 *
 * Les étapes suivantes obtiennent leur assombrissement par l'ombre portée du
 * halo. L'accueil n'a pas de halo — il ne désigne rien — et se détachait donc
 * sur une page en pleine lumière, ce qui le faisait passer pour un encart
 * parmi d'autres plutôt que pour le début de quelque chose.
 *
 * Mon premier essai posait un pseudo-élément « position: fixed » sur la carte
 * elle-même. Il la recouvrait exactement au lieu de couvrir l'écran : la carte
 * porte un « transform » pour se centrer, et un transform fait qu'un
 * descendant « fixe » se cale sur LUI et non sur la fenêtre. En mode sombre le
 * défaut se remarquait à peine ; en clair, la carte devenait grise.
 *
 * Le voile est donc porté par la racine, qui ne subit aucune transformation.
 * La classe est retirée par le script au moment où la visite commence.
 */
.visite--accueil { background: rgb(0 0 0 / 0.55); }

.visite__accueil {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(30rem, calc(100vw - 2rem));
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  padding: var(--e6) var(--e5) var(--e5);
  text-align: center;
  background: var(--surface);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-3);
}

/*
 * L'arbre est dessiné, pas photographié : il suit le thème, reste net à toute
 * taille et ne coûte aucun chargement. Ses feuilles portent des visages —
 * c'est de famille qu'il s'agit, pas de botanique.
 */
.visite__arbre {
  display: block;
  width: min(14rem, 60%);
  height: auto;
  margin: 0 auto var(--e4);
  color: var(--vert-fonce);
}

.visite__feuilles circle { fill: var(--vert-clair); stroke: var(--vert); stroke-width: 2; }
.visite__silhouettes path { fill: var(--vert-fonce); }

.visite__surtitre {
  margin: 0 0 var(--e2);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--vert);
  font-weight: 700;
}

.visite__bienvenue {
  margin: 0 0 var(--e4);
  font-family: var(--serif);
  font-size: 1.75rem;
  line-height: 1.15;
}

.visite__intro {
  margin: 0 0 var(--e3);
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--texte-doux);
}

.visite__accueil .visite__actions { margin-top: var(--e5); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .visite__feuilles circle {
    fill: color-mix(in srgb, var(--vert) 25%, transparent);
  }
}

:root[data-theme="dark"] .visite__feuilles circle {
  fill: color-mix(in srgb, var(--vert) 25%, transparent);
}

/* Les deux actions du bas du calque, détachées des liens de navigation. */
.moi__actions {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  margin-top: var(--e3);
  padding-top: var(--e3);
  border-top: 1px solid var(--bordure);
}

/* ---------------------------------------------- la rangée d'outils */

.rang-outils {
  display: flex;
  align-items: center;
  gap: var(--e4);
  max-width: var(--largeur-max);
  margin-inline: auto;
  padding-inline: var(--e4);
}

/*
 * Les onglets prennent la place disponible et défilent seuls si besoin ; le
 * bouton reste collé au bord droit, sous le calque du profil.
 */
.rang-outils .onglets { flex: 1 1 auto; min-width: 0; padding-inline: 0; }
.rang-outils .ajout-entete { flex: 0 0 auto; margin-left: auto; }

/* Le calque de l'ajout s'ouvre vers la gauche : à droite, il sortirait. */
.rang-outils .ajout-entete__calque { left: auto; right: 0; }

@media (min-width: 62rem) {
  .rang-outils { padding-inline: var(--e5); }
}

/* ------------------------------------- aperçu d'un courriel, en admin */

.apercu-mail {
  padding: var(--e4);
  background: var(--surface-2);
  border-radius: var(--rayon);
  font-size: 0.875rem;
}

.apercu-mail__ligne { margin: 0 0 var(--e1); color: var(--texte-doux); }
.apercu-mail__filet { margin: var(--e3) 0; border: 0; border-top: 1px solid var(--bordure); }
.apercu-mail__salut { margin: 0 0 var(--e2); font-weight: 600; }

.apercu-mail__bouton {
  display: inline-block;
  margin: var(--e4) 0 0;
  padding: var(--e2) var(--e4);
  border-radius: var(--rayon);
  background: var(--vert);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 600;
}

/* ------------------------------------------- les visages suggérés */

/*
 * Ils sont repérés au dépôt, côté serveur, et proposés — jamais nommés. Le
 * trait est discontinu pour qu'on les distingue au premier coup d'œil des
 * repères confirmés, qui sont pleins : l'un est une hypothèse, l'autre un fait.
 */
.visage-suggere {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--taille);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  padding: 0;
  background: none;
  border: 2px dashed rgb(255 255 255 / 0.85);
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.35);
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}

.visage-suggere:hover,
.visage-suggere:focus-visible {
  opacity: 1;
  border-color: var(--vert);
  border-style: solid;
}

/* --------------------------------------------- fiches à compléter */

.a-completer {
  padding: var(--e2) 0;
  border-bottom: 1px solid var(--bordure);
}

.a-completer:last-child { border-bottom: 0; }

/*
 * Le nom et la jauge sur une ligne, la jauge poussée à droite. Elle dit d'un
 * coup d'œil ce qu'un pourcentage écrit ne dirait pas : que c'est presque fini.
 */
.a-completer__qui {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
}

/*
 * La jauge a un viewBox de mille unités et aucune largeur propre : laissée
 * libre dans une ligne flexible, elle réclamerait mille pixels. On la borne.
 */
.a-completer__qui svg {
  flex: 0 0 4.5rem;
  width: 4.5rem;
}

.a-completer__manque { margin-top: 0.15rem; }

/* ------------------------------------------ recettes et traditions */

/*
 * Une fiche de carnet plutôt qu'une ligne de liste. Une recette se choisit à
 * l'œil — « ah, la tarte » — et non en lisant une colonne de titres ; la carte
 * lui laisse la place de porter son nom en grand.
 */
.recette-carte {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  padding: var(--e4);
  text-decoration: none;
  color: inherit;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon-lg);
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.recette-carte:hover,
.recette-carte:focus-visible {
  border-color: var(--vert);
  transform: translateY(-2px);
}

.recette-carte__nature {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--vert);
}

.recette-carte__titre {
  font-family: var(--police-titre, inherit);
  font-size: 1.125rem;
  line-height: 1.25;
}

.recette-carte__meta { font-size: 0.8125rem; }

.recette-carte__origine {
  font-size: 0.875rem;
  font-style: italic;
  color: var(--texte-doux);
}

/* --- la recette elle-même */

/*
 * Les ingrédients à gauche, la marche à suivre à droite : on lit la liste une
 * fois, en faisant les courses, et les étapes vingt fois, les mains dans la
 * farine. Sur un téléphone posé sur le plan de travail, elles s'empilent.
 */
.recette-corps {
  display: grid;
  gap: var(--e5);
  grid-template-columns: 1fr;
}

@media (min-width: 52rem) {
  .recette-corps { grid-template-columns: minmax(0, 14rem) minmax(0, 1fr); }
}

.recette-liste {
  margin: 0;
  padding-left: 1.1rem;
}

.recette-liste li { margin-bottom: var(--e2); }

/* Les étapes sont numérotées et espacées : on y revient en cours de route et
   il faut retrouver où l'on en était. */
.recette-etapes { counter-reset: etape; list-style: none; margin: 0; padding: 0; }

.recette-etapes li {
  position: relative;
  padding-left: 2.25rem;
  margin-bottom: var(--e4);
}

.recette-etapes li::before {
  counter-increment: etape;
  content: counter(etape);
  position: absolute;
  left: 0;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  font-size: 0.8125rem;
  color: var(--vert);
  border: 1px solid var(--vert);
  border-radius: 50%;
}

/* ------------------------------------------ souvenirs enregistrés */

.enreg { display: flex; flex-direction: column; gap: var(--e3); }

.enreg__etat {
  display: flex;
  align-items: center;
  gap: var(--e3);
}

/* Un point rouge qui bat : le seul signe qui dise, sans le lire, que ça tourne. */
.enreg__pastille {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  background: var(--bordure-forte);
  flex: none;
}

.enreg__pastille--active {
  background: #d94141;
  animation: enreg-battement 1.4s ease-in-out infinite;
}

@keyframes enreg-battement {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* Le minuteur est le repère de celui qui parle : il doit se lire de loin. */
.enreg__minuteur {
  font-variant-numeric: tabular-nums;
  font-size: 1.5rem;
  line-height: 1;
}

.enreg__mode { font-size: 0.8125rem; }

.enreg__apercu,
.souvenir-lecteur {
  display: block;
  width: 100%;
  max-width: 100%;
  border-radius: var(--rayon);
}

.enreg__apercu { max-height: 22rem; background: #000; }

/* Le dépôt manuel s'efface d'un cran quand l'enregistrement est possible :
   il reste accessible, il n'est plus le chemin principal. */
.enreg__depot-replie { opacity: 0.72; }
.enreg__depot-replie:focus-within { opacity: 1; }

/* --- la liste de questions */

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

.questions__item {
  display: block;
  width: 100%;
  padding: var(--e2) var(--e3);
  margin-bottom: var(--e2);
  font-size: 0.9375rem;
  text-align: left;
  color: inherit;
  background: none;
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  cursor: pointer;
}

.questions__item:hover,
.questions__item:focus-visible { border-color: var(--vert); }

.questions__item--choisi {
  border-color: var(--vert);
  background: var(--surface-2);
}

/*
 * La question posée, citée. Des années après, elle sera la seule à dire à quoi
 * la réponse répond — d'où la place qu'on lui donne, avant l'enregistrement.
 */
.souvenir-question {
  margin: 0 0 var(--e5);
  padding: var(--e3) var(--e4);
  font-family: var(--police-titre, inherit);
  font-size: 1.25rem;
  line-height: 1.35;
  border-left: 3px solid var(--vert);
}

/* ------------------------- suggestions du bandeau, et symboles de sexe */

/*
 * Mars et Vénus. Un caractère au lieu de cinq lettres : sur une carte, la place
 * gagnée revient aux dates et au lieu, qui apprennent quelque chose. La couleur
 * suit celle déjà employée pour distinguer les fiches.
 */
.sexe {
  font-size: 0.95em;
  line-height: 1;
  /*
   * Le symbole a besoin d'air à sa gauche. Le nom et les renseignements se
   * suivent sur la même ligne quand la place le permet, et « Taddei♂ » se
   * lisait comme un seul mot — le signe paraissait accroché au nom plutôt que
   * de commencer la ligne suivante.
   */
  margin-inline-start: 0.4em;
}

/* Les mêmes deux couleurs que le liseré des cartes : le site n'en connaît
   pas d'autres pour cela, et en inventer deux troublerait la lecture. */
.sexe--m { color: var(--bleu); }
.sexe--f { color: var(--vert); }

/*
 * La liste du bandeau montre des cartes, pas des lignes : on cherche quelqu'un
 * qu'on connaît, et un visage se reconnaît plus vite qu'un nom ne se lit.
 */
.suggestions-riches {
  position: absolute;
  z-index: var(--z-menu);
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 26rem;
  overflow-y: auto;
  padding: var(--e2);
  background: var(--surface);
  border: 1px solid var(--bordure-forte);
  border-radius: var(--rayon-lg);
  box-shadow: var(--ombre-3);
}

.suggestions-riches__item {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding: var(--e2) var(--e3);
  border-radius: var(--rayon);
  text-decoration: none;
  color: inherit;
}

.suggestions-riches__item:hover,
.suggestions-riches__item--vise { background: var(--surface-2); }

.suggestions-riches__item .personne__portrait,
.suggestions-riches__item .personne__initiales {
  width: 2.5rem;
  height: 2.5rem;
  flex: none;
}

/* La dernière ligne, qui mène à la recherche complète : elle doit se lire
   comme une action, pas comme un huitième résultat. */
.suggestions-riches__tous {
  display: block;
  width: 100%;
  margin-top: var(--e2);
  padding: var(--e2) var(--e3);
  font-size: 0.8125rem;
  text-align: left;
  color: var(--texte-doux);
  background: none;
  border: 0;
  border-top: 1px solid var(--bordure);
  border-radius: 0;
  cursor: pointer;
}

.suggestions-riches__tous:hover { color: var(--vert); }

/* ------------------------------------------------- la case « décédée » */

/*
 * Séparé du reste par un filet et de l'air : ce n'est pas une section de plus,
 * c'est une question qu'on pose une fois, à la fin, et dont la réponse est non
 * pour la plupart des fiches.
 */
.deces {
  margin-top: var(--e5);
  padding-top: var(--e4);
  border-top: 1px solid var(--bordure);
}

.deces__bascule { margin-bottom: 0; }

.deces__corps { margin-top: var(--e4); }

/* ==================================================== l'arbre dessiné */

/*
 * Une généalogie se lit dans l'espace : les aïeux tout en haut, le conjoint à
 * côté, les enfants en dessous.
 *
 * TOUT L'ALIGNEMENT VIENT DE LA GRILLE, et c'est ce qui rend le dessin fiable.
 * Chaque génération divise la précédente par deux : huit grands-parents sur
 * huit colonnes, quatre parents sur deux colonnes chacun, deux conjoints sur
 * quatre. Le centre d'un couple tombe donc exactement sur le centre de son
 * enfant, sans qu'on ait rien à mesurer, et un nom long ne décale rien.
 *
 * Dix colonnes en tout : les huit de l'ascendance, plus une de chaque côté pour
 * les fratries.
 */
.arbre3-defilant {
  /* Sous sa largeur minimale, le tableau défile plutôt que de se comprimer
     jusqu'à l'illisible. */
  overflow-x: auto;
  padding-bottom: var(--e3);
}

.arbre3 {
  display: grid;
  min-width: 64rem;
  grid-template-columns: minmax(6.5rem, 0.9fr) repeat(8, minmax(6rem, 1fr)) minmax(6.5rem, 0.9fr);
  grid-template-rows: auto var(--e4) auto var(--e4) auto var(--e4) auto;
  gap: 0 var(--e2);
  align-items: center;
  justify-items: stretch;
}

/* Sans conjoint, la moitié droite n'a pas lieu d'être : six colonnes. */
.arbre3--seul {
  min-width: 40rem;
  grid-template-columns: minmax(6.5rem, 0.9fr) repeat(4, minmax(6rem, 1fr)) minmax(6.5rem, 0.9fr);
}

/* Sans aucun aïeul connu, les deux premières rangées disparaissent. */
.arbre3--sans-aieux { grid-template-rows: auto var(--e4) auto var(--e4) auto; }

.arbre3-cell { position: relative; min-width: 0; height: 100%; display: flex; align-items: center; }

/* --- rangée 1 : les huit grands-parents */

.arbre3-gp1 { grid-area: 1 / 2; }
.arbre3-gp2 { grid-area: 1 / 3; }
.arbre3-gp3 { grid-area: 1 / 4; }
.arbre3-gp4 { grid-area: 1 / 5; }
.arbre3-gp5 { grid-area: 1 / 6; }
.arbre3-gp6 { grid-area: 1 / 7; }
.arbre3-gp7 { grid-area: 1 / 8; }
.arbre3-gp8 { grid-area: 1 / 9; }

/* Les traits joignant chaque couple d'aïeuls, dans l'écart entre leurs cases. */
.arbre3-ua1 { grid-area: 1 / 2 / 2 / 4; }
.arbre3-ua2 { grid-area: 1 / 4 / 2 / 6; }
.arbre3-ua3 { grid-area: 1 / 6 / 2 / 8; }
.arbre3-ua4 { grid-area: 1 / 8 / 2 / 10; }

/* --- rangée 2 : les descentes des aïeuls vers les parents */

.arbre3-da1 { grid-area: 2 / 2 / 3 / 4; }
.arbre3-da2 { grid-area: 2 / 4 / 3 / 6; }
.arbre3-da3 { grid-area: 2 / 6 / 3 / 8; }
.arbre3-da4 { grid-area: 2 / 8 / 3 / 10; }

/* --- rangée 3 : les quatre parents, deux colonnes chacun */

.arbre3-p1 { grid-area: 3 / 2 / 4 / 4; }
.arbre3-p2 { grid-area: 3 / 4 / 4 / 6; }
.arbre3-p3 { grid-area: 3 / 6 / 4 / 8; }
.arbre3-p4 { grid-area: 3 / 8 / 4 / 10; }

.arbre3-up1 { grid-area: 3 / 2 / 4 / 6; }
.arbre3-up2 { grid-area: 3 / 6 / 4 / 10; }

/* --- rangée 4 : les descentes des parents vers le couple */

.arbre3-dp1 { grid-area: 4 / 2 / 5 / 6; }
.arbre3-dp2 { grid-area: 4 / 6 / 5 / 10; }

/* --- rangée 5 : les fratries, le couple */

.arbre3-fa       { grid-area: 5 / 1; }
.arbre3-moi      { grid-area: 5 / 2 / 6 / 6; }
.arbre3-conjoint { grid-area: 5 / 6 / 6 / 10; }
.arbre3-fb       { grid-area: 5 / 10; }
.arbre3-uc       { grid-area: 5 / 2 / 6 / 10; }

/* --- rangées 6 et 7 : la descente et les enfants */

.arbre3-de      { grid-area: 6 / 2 / 7 / 10; }
.arbre3-enfants { grid-area: 7 / 2 / 8 / 10; }

/* La variante sans conjoint : tout tient dans les colonnes 2 à 5. */
.arbre3--seul .arbre3-p1  { grid-area: 3 / 2 / 4 / 4; }
.arbre3--seul .arbre3-p2  { grid-area: 3 / 4 / 4 / 6; }
.arbre3--seul .arbre3-moi { grid-area: 5 / 2 / 6 / 6; }
.arbre3--seul .arbre3-fa  { grid-area: 5 / 1; }
.arbre3--seul .arbre3-fb  { grid-area: 5 / 6; }
.arbre3--seul .arbre3-de      { grid-area: 6 / 2 / 7 / 6; }
.arbre3--seul .arbre3-enfants { grid-area: 7 / 2 / 8 / 6; }

/* Sans aïeux, tout remonte de deux rangées. */
.arbre3--sans-aieux .arbre3-p1  { grid-row: 1; }
.arbre3--sans-aieux .arbre3-p2  { grid-row: 1; }
.arbre3--sans-aieux .arbre3-p3  { grid-row: 1; }
.arbre3--sans-aieux .arbre3-p4  { grid-row: 1; }
.arbre3--sans-aieux .arbre3-up1 { grid-row: 1; }
.arbre3--sans-aieux .arbre3-up2 { grid-row: 1; }
.arbre3--sans-aieux .arbre3-dp1 { grid-row: 2; }
.arbre3--sans-aieux .arbre3-dp2 { grid-row: 2; }
.arbre3--sans-aieux .arbre3-fa       { grid-row: 3; }
.arbre3--sans-aieux .arbre3-moi      { grid-row: 3; }
.arbre3--sans-aieux .arbre3-conjoint { grid-row: 3; }
.arbre3--sans-aieux .arbre3-fb       { grid-row: 3; }
.arbre3--sans-aieux .arbre3-uc       { grid-row: 3; }
.arbre3--sans-aieux .arbre3-de       { grid-row: 4; }
.arbre3--sans-aieux .arbre3-enfants  { grid-row: 5; }

/* --- les traits */

/*
 * Le trait d'union est centré sur la zone qu'il couvre et large de l'écart
 * entre les deux cases : il relie donc leurs bords sans les recouvrir.
 */
.arbre3-union {
  justify-content: center;
  align-items: center;
  pointer-events: none;
}

.arbre3-union::before {
  content: "";
  width: calc(var(--e2) + 2px);
  border-top: 2px solid var(--bordure-forte);
}

/*
 * La mention posée au-dessus du trait — « Mariage · 12/03/1998 ». Elle flotte
 * sans occuper de place : la rangée est déjà dimensionnée par les cases, et
 * pousser le trait vers le bas désalignerait tout ce qui en descend.
 */
.arbre3-union__mention {
  position: absolute;
  left: 50%;
  bottom: 50%;
  transform: translate(-50%, -0.35rem);
  padding: 0 var(--e2);
  font-size: 0.68rem;
  white-space: nowrap;
  color: var(--texte-tenu);
  background: var(--surface);
  border-radius: var(--rayon);
}

.arbre3-descente {
  justify-content: center;
  align-items: stretch;
}

.arbre3-trait {
  width: 0;
  border-left: 2px solid var(--bordure-forte);
}

/* --- une case */

.arbre3-case {
  position: relative;
  width: 100%;
  padding: var(--e2);
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-top: 3px solid var(--bordure-forte);
  border-radius: var(--rayon);
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.arbre3-case--m { border-top-color: var(--bleu); }
.arbre3-case--f { border-top-color: var(--vert); }

.arbre3-case:hover { transform: translateY(-2px); border-color: var(--vert); }

.arbre3-case__vers {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  text-decoration: none;
  color: inherit;
}

.arbre3-case__portrait {
  width: 3rem;
  height: 3rem;
  border-radius: var(--rayon);
  object-fit: cover;
  background: var(--surface-2);
}

.arbre3-case__portrait--initiales {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--texte-doux);
}

.arbre3-case__nom {
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.2;
  overflow-wrap: anywhere;
}

.arbre3-case__annees { font-size: 0.7rem; color: var(--texte-tenu); }

/*
 * Le lien vers la fiche ne se montre qu'au survol et au clavier : la case
 * entière mène déjà quelque part, et deux liens visibles côte à côte feraient
 * hésiter sur celui qu'il faut prendre.
 */
.arbre3-case__fiche {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.68rem;
  color: var(--texte-tenu);
  opacity: 0;
  transition: opacity 0.15s ease;
}

.arbre3-case:hover .arbre3-case__fiche,
.arbre3-case__fiche:focus-visible { opacity: 1; }

@media (hover: none) {
  /* Au doigt, il n'y a pas de survol : le lien reste visible. */
  .arbre3-case__fiche { opacity: 1; }
}

.arbre3-case--vide {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 3.5rem;
  border-style: dashed;
  border-top-style: dashed;
  border-top-color: var(--bordure);
  background: none;
  color: var(--texte-tenu);
  font-size: 0.85rem;
}

.arbre3-case--vide:hover { transform: none; border-color: var(--bordure); }

/* --- la pastille des frères et sœurs */

.arbre3-boite {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  width: 100%;
  padding: var(--e2) var(--e3);
  font-size: 0.78rem;
  background: var(--surface-2);
  border-radius: var(--rayon-lg);
}

.arbre3-boite__titre {
  font-size: 0.66rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--texte-tenu);
  margin-bottom: 0.15rem;
}

.arbre3-boite__nom a { text-decoration: none; }
.arbre3-boite__nom a:hover { text-decoration: underline; }

/* --- les enfants, en ligne sous le couple */

.arbre3-enfants { justify-content: center; }

.arbre3-enfants__ligne {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--e3);
  width: 100%;
}

.arbre3-enfant { width: 8rem; flex: none; height: auto; }

/* La zone de survol d'une colonne : invisible, mais elle capte le pointeur sur
   toute la hauteur — un jour à zéro n'a pas de barre à viser. */
.graphe .survol { fill: transparent; }

/* ------------------------------------------- le conjoint qui manque */

/*
 * « Conjoint inconnu » n'appelait aucun geste, alors que c'est justement la
 * case où il y en a un à faire : ces enfants ont eu deux parents, et celui-là
 * manque. Le pointillé dit le vide, le plus dit quoi en faire.
 */
.conjoint-manquant {
  display: flex;
  align-items: center;
  gap: var(--e3);
  padding: var(--e3);
  text-decoration: none;
  color: var(--texte-doux);
  border: 1px dashed var(--bordure-forte);
  border-radius: var(--rayon);
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.conjoint-manquant:hover,
.conjoint-manquant:focus-visible {
  color: var(--vert-fonce);
  border-color: var(--vert);
  border-style: solid;
  background: var(--surface-2);
}

.conjoint-manquant__plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  flex: none;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--vert);
  border: 2px solid currentColor;
  border-radius: 50%;
}

.conjoint-manquant__texte {
  display: flex;
  flex-direction: column;
  font-style: italic;
}

.conjoint-manquant__action {
  font-style: normal;
  font-size: 0.8125rem;
  color: var(--vert);
}

/* ------------------------------------- corriger ou retirer un message */

/*
 * Les deux actions vivent dans la ligne de date, en petit : elles ne servent
 * qu'à celui qui a écrit, et rarement. Les mettre en évidence donnerait à
 * chaque bulle deux boutons dont on n'a presque jamais besoin.
 */
.bulle__action {
  padding: 0;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  text-decoration: underline;
  cursor: pointer;
}

.bulle__action:hover { color: var(--vert); }

/* Un message retiré : la bulle demeure, le texte est en creux. */
.bulle__texte--retire {
  font-style: italic;
  opacity: 0.6;
}

.bulle__correction textarea {
  width: 100%;
  min-height: 4.5rem;
}

/* La galerie de l'accueil : un nombre de colonnes qui divise 15.

   Ailleurs, « auto-fill » est le bon choix — on remplit la largeur, tant pis
   pour la dernière ligne. Ici non : le bloc montre un nombre FIXE de photos,
   et douze vignettes réparties par cinq laissaient une dernière ligne de deux,
   qui se lit comme un manque plutôt que comme une fin.

   Trois colonnes puis cinq, parce que 15 se divise par les deux. Changer le
   nombre de photos servies sans changer ces valeurs — ou l'inverse — ramène la
   ligne bancale : les deux se tiennent, et HomeController le dit aussi. */
.galerie--accueil {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (min-width: 48rem) {
  .galerie--accueil {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

/* ------------------------------------------------- « Voir plus » en calque

   Une liste longue dans une colonne de côté repousse tout le reste hors de
   l'écran, pour une information qu'on ne lit pas ligne à ligne. On en montre
   quelques-unes, et le reste s'ouvre par-dessus.

   Sans JavaScript, comme le menu « Ajouter » du bandeau : une case à cocher
   masquée, un label pour l'actionner, et le calque révélé par :checked. Le
   geste marche même si un script échoue à se charger — et sur ce site la CSP
   interdit de toute façon le moindre gestionnaire en ligne. */

.plus__ouvrir {
  display: inline-block;
  margin-top: var(--e3);
  color: var(--vert);
  cursor: pointer;
  text-decoration: underline;
}

.plus__fond {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 60;
  align-items: center;
  justify-content: center;
  padding: var(--e4);
}

.plus__bascule:checked ~ .plus__fond {
  display: flex;
}

/* Le fond cliquable ferme le calque : il porte le même « for » que la croix.
   Il est SOUS le panneau dans l'ordre de pile, sans quoi il capterait les
   clics destinés au contenu. */
.plus__derriere {
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / 0.5);
  cursor: pointer;
}

.plus__calque {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(32rem, 100%);
  max-height: min(80vh, 40rem);
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-3);
}

.plus__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
  padding: var(--e4);
  border-bottom: 1px solid var(--bordure);
}

.plus__croix {
  cursor: pointer;
  padding: 0 var(--e2);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--texte-doux);
}

.plus__croix:hover {
  color: var(--texte);
}

/* Le corps défile, la tête reste : sur quarante lignes, retrouver la fermeture
   ne doit pas demander de remonter jusqu'en haut. */
.plus__corps {
  overflow-y: auto;
  padding: 0 var(--e4) var(--e4);
}

/* La colonne des noms ne se laisse pas comprimer par les chiffres.

   Un tableau répartit la largeur au prorata du contenu : trois colonnes de
   nombres courts et une de noms longs, et c'est la colonne des noms qui cède.
   On lui donne un plancher ; le tableau défile dans son cadre si l'ensemble
   ne tient pas, ce qu'il sait déjà faire. */
.col-membre {
  min-width: 14rem;
}

/* Le nom de la photo, au survol.

   Une bande en bas de la vignette, montée par-dessus l'image. Elle ne prend
   aucune place tant qu'elle est cachée : la grille garde exactement la même
   géométrie, survol ou non.

   Le dégradé plutôt qu'un fond plein : sous la première ligne de texte,
   l'image reste visible et l'on comprend qu'on lit une étiquette posée
   dessus, pas une case séparée. */
.vignette__survol {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  padding: var(--e4) var(--e2) var(--e2);
  font-size: 0.75rem;
  line-height: 1.25;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.6);
  background: linear-gradient(to top, rgb(0 0 0 / 0.78), rgb(0 0 0 / 0));
  opacity: 0;
  transition: opacity var(--transition);

  /* Deux lignes au plus, coupées proprement : une photo de groupe peut porter
     huit noms, et la bande ne doit pas recouvrir le visage qu'on regarde. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;

  /* Le survol vise l'image, pas l'étiquette : sans ça, la bande qui apparaît
     sous le curseur se met à clignoter au bord. */
  pointer-events: none;
}

.vignette:hover .vignette__survol,
.vignette:focus-visible .vignette__survol {
  opacity: 1;
}

/* SANS SURVOL, PAS DE SURVOL — et donc rien du tout. Sur un écran tactile le
   nom ne s'afficherait jamais : on le montre en permanence, ce qui est aussi
   ce qu'on veut là où l'on ne peut pas pointer pour interroger. */
@media (hover: none) {
  .vignette__survol {
    opacity: 1;
  }
}

/* Le mouvement en moins pour qui le refuse : l'étiquette apparaît, sans fondu. */
@media (prefers-reduced-motion: reduce) {
  .vignette__survol {
    transition: none;
  }
}

/* --------------------------------------------- pièces jointes d'un message

   L'image est montrée, pas rangée : c'est pour la montrer qu'on l'a jointe.
   Bornée en hauteur pour qu'un scan en portrait ne pousse pas la suite de la
   conversation hors de l'écran, et cliquable pour la voir en entier. */
.bulle__image {
  display: block;
  margin-top: var(--e2);
  border-radius: var(--rayon);
  overflow: hidden;
  border: 1px solid var(--bordure);
  background: var(--surface-2);
  max-width: 22rem;
}

.bulle__image img {
  display: block;
  width: 100%;
  max-height: 20rem;
  object-fit: cover;
}

/* Le reste s'annonce par son nom et son poids : un PDF ne s'affiche pas dans
   une bulle, et prétendre le contraire ferait un cadre vide. */
.bulle__piece {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  margin-top: var(--e2);
  padding: var(--e2) var(--e3);
  border-radius: var(--rayon);
  border: 1px solid var(--bordure);
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  font-size: 0.8125rem;
  max-width: 100%;
}

.bulle__piece:hover {
  border-color: var(--vert);
}

.bulle__piece svg {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
  color: var(--texte-tenu);
}

/* Le nom d'un fichier peut être long, et il vient de l'extérieur. */
.bulle__piece span {
  overflow-wrap: break-word;
  min-width: 0;
}

/* Le visage avant le nom, dans « à compléter ».

   On complète la fiche de quelqu'un qu'on connaît — c'est tout l'argument de
   cette carte. Un nom seul demande un effort de rappel ; un visage dit tout de
   suite si l'on est la bonne personne pour répondre.

   Deux rem : elle accompagne le nom, elle ne le concurrence pas. Le bloc reste
   une liste qu'on parcourt. */
.a-completer__nom {
  display: flex;
  align-items: center;
  gap: var(--e2);
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.a-completer__nom strong {
  overflow-wrap: break-word;
  min-width: 0;
}

.a-completer__nom:hover strong {
  text-decoration: underline;
}

.a-completer__portrait {
  flex: none;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
}

/* Sans photo, les initiales — comme partout ailleurs sur le site, plutôt
   qu'un trou ou une silhouette générique qui ne distingue personne. */
.a-completer__portrait--initiales {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--texte-doux);
  border: 1px solid var(--bordure);
}

/* --------------------------------------- le chemin de parenté, en arbre

   Un aïeul en haut, et deux descendances qui s'en éloignent. Deux colonnes
   parallèles répétaient l'ancêtre à leur sommet : la même personne deux fois,
   donnant à lire deux histoires séparées là où il n'y en a qu'une. */

.parente {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: var(--e5);
}

.parente__ancetre {
  max-width: 22rem;
  width: 100%;
  text-align: center;
}

.parente__titre {
  margin-bottom: var(--e2);
}

/* Le trait qui descend de l'ancêtre vers la fourche : il n'a pas besoin d'être
   dessiné en SVG, une bordure suffit et suit la couleur du thème. */
.parente__branches {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--e4);
  width: 100%;
  margin-top: var(--e4);
  padding-top: var(--e4);
  border-top: 1px dashed var(--bordure-forte);
}

@media (min-width: 40rem) {
  .parente__branches {
    grid-template-columns: 1fr 1fr;
    gap: var(--e6);
  }
}

.parente__branche {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  margin: 0;
  min-width: 0;
}

.parente__branche--seule {
  width: 100%;
  max-width: 22rem;
  margin-top: var(--e4);
  padding-top: var(--e4);
  border-top: 1px dashed var(--bordure-forte);
}

.parente__etape {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
  min-width: 0;
}

/* « son fils », « sa fille » : la marche entre deux étages. Sans elle, trois
   noms empilés ne disent pas comment ils tiennent ensemble. */
.parente__marche {
  font-size: 0.75rem;
  color: var(--texte-tenu);
  padding-left: var(--e2);
}

/* Où mène chaque branche. Côte à côte on le devine — la dernière carte est la
   personne ; empilées sur un téléphone, les deux descendances se lisent comme
   une seule, longue et fausse. */
.parente__branche-cadre {
  min-width: 0;
}

.parente__vers {
  margin-bottom: var(--e2);
  font-weight: 600;
}

/* Sur écran étroit, un trait sépare la seconde branche de la première : sans
   lui, la fourche disparaît et il ne reste qu'une file. */
@media (max-width: 40rem) {
  .parente__branche-cadre + .parente__branche-cadre {
    padding-top: var(--e4);
    border-top: 1px dashed var(--bordure-forte);
  }
}

/* Les cartes du chemin de parenté, dans l'arbre de l'ancêtre commun.

   Une mise en relief PERMANENTE, là où .tv-carte--surligne est un clignotement
   de trois secondes qui répond à une recherche. Ici il n'y a rien à retrouver :
   ces cartes SONT la réponse, et elles doivent le rester tant que la page est
   ouverte.

   Le reste de l'arbre s'efface un peu plutôt que de disparaître : les frères
   et sœurs écartés, les cousins du même aïeul font justement comprendre par
   quelle branche le lien passe. Les masquer reviendrait à redessiner les deux
   listes qu'on vient de quitter. */
/* La carte du chemin doit se lire d'un coup d'œil, y compris en thème
   clair où une bordure verte sur fond blanc reste discrète : on ajoute
   un fond teinté et un trait plus épais. */
.tv-carte--chemin {
  border-color: var(--vert);
  background: var(--vert-fond);
  box-shadow: 0 0 0 2px var(--vert), var(--ombre-2);
  z-index: 3;
}

/* L'ESTOMPAGE EST DÉCIDÉ AU RENDU, PLUS EN CSS. La règle d'avant se lisait
   « .tv-arbre__scene:has(.tv-carte--chemin) .tv-carte:not(.tv-carte--chemin) »
   et dépendait donc du moteur du visiteur : là où « :has() » manque ou se
   comporte autrement, plus rien ne distinguait le chemin du reste. La classe
   vient maintenant du serveur, et se relit dans le HTML. */
.tv-carte--hors-chemin {
  opacity: 0.45;
}

@media (prefers-reduced-motion: reduce) {
  .tv-carte--chemin { box-shadow: 0 0 0 2px var(--vert); }
}

/* -------- les traits du chemin

   Une carte verte reliée par un trait gris oblige l'œil à refaire le
   chemin lui-même. On allume donc aussi la descente verticale qui tombe
   sur chaque carte du chemin.

   La barre horizontale, elle, court dans les gouttières partagées avec
   les frères et sœurs : l'allumer pour un seul enfant déborderait sur
   ses voisins et désignerait le mauvais segment. On ne l'allume donc que
   lorsque TOUTE la fratrie affichée est sur le chemin — cas fréquent une
   fois l'arbre élagué — car il n'y a alors plus d'ambiguïté. Le compteur
   de fratrie repliée ne compte pas dans ce décompte : il n'est personne. */

.tv-desc-noeud__enfants > .tv-desc-noeud--chemin::after {
  background: var(--vert);
  width: 3px;
}

.tv-desc-noeud__enfants:not(:has(> .tv-desc-noeud:not(.tv-desc-noeud--chemin):not(.tv-desc-noeud--repli)))
  > .tv-desc-noeud--chemin::before {
  background: var(--vert);
  height: 3px;
}

/* -------- la fratrie repliée

   Quand l'arbre complet ne tient pas à l'écran, on ne garde que la
   lignée et l'on remplace la fratrie par un compteur. Il occupe une
   colonne comme une carte, pour que le bracket reste d'aplomb, mais
   reste discret : c'est un renvoi, pas une personne. */

.tv-desc-noeud--repli {
  align-self: flex-start;
  padding-top: 0.9rem;
}

.tv-repli {
  display: inline-block;
  padding: 0.3rem 0.7rem;
  border: 1px dashed var(--bordure-forte);
  border-radius: 999px;
  color: var(--texte-doux);
  font-size: 0.82rem;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
}

.tv-repli:hover,
.tv-repli:focus-visible {
  border-style: solid;
  border-color: var(--vert);
  color: var(--texte);
}

/* Le compteur n'est pas une carte : l'estompage ne le touche pas, et l'on
   lit donc toujours ce qu'on a replié. */
.tv-desc-noeud--repli { opacity: 1; }

/* Le dépliant des pièces jointes porte le même poids qu'un libellé de champ :
   c'est un intitulé, pas une action, et il ne doit pas concurrencer le bouton
   d'envoi placé juste dessous. */
/* La zone de saisie s'ouvre basse et grandit à la mise au point : en tête de
   fil, les huit rem du gabarit général repoussaient le dernier message hors
   de l'écran alors que la plupart des réponses tiennent en une ligne. */
.reponse__corps {
  min-height: 4.5rem;
  transition: min-height .15s ease;
}

.reponse__corps:focus {
  min-height: 8rem;
}

.reponse__pieces {
  cursor: pointer;
  color: var(--texte-doux);
  font-size: 0.9rem;
  font-weight: 600;
  list-style-position: inside;
}

.reponse__pieces:hover {
  color: var(--texte);
}

/* -------- les profils de réseaux sur une fiche

   Le logo EST le lien. Il porte la couleur de sa marque — c'est elle qui le
   rend reconnaissable avant même qu'on ait lu la forme — et ne suit donc pas
   le thème du site.

   LE SÉLECTEUR EST QUALIFIÉ PAR « a ». Le thème colore tous les liens en vert
   via « :root:not([data-theme="light"]) a » : une simple classe perdait
   contre lui, et les deux marques ressortaient vertes. En qualifiant par
   l'élément, on égale sa spécificité, et l'ordre du fichier tranche.

   Les teintes sombres ne sont pas les teintes officielles : sur fond noir,
   le bleu de LinkedIn (#0a66c2) tombe sous le contraste lisible. On les
   éclaircit du minimum qui le rétablit. */

.reseaux {
  display: flex;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

a.reseaux__lien {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  border: 1px solid var(--bordure);
  border-radius: 0.6rem;
  transition: border-color .12s ease, background-color .12s ease, transform .12s ease;
}

a.reseaux__lien--linkedin { color: #0a66c2; }
a.reseaux__lien--facebook { color: #1877f2; }

a.reseaux__lien:hover,
a.reseaux__lien:focus-visible {
  background: var(--surface-2);
  border-color: currentColor;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) a.reseaux__lien--linkedin { color: #4a9ae8; }
  :root:not([data-theme="light"]) a.reseaux__lien--facebook { color: #5b9cf8; }
}

:root[data-theme="dark"] a.reseaux__lien--linkedin { color: #4a9ae8; }
:root[data-theme="dark"] a.reseaux__lien--facebook { color: #5b9cf8; }

/* -------- une discussion du forum

   La réponse est décalée et allégée : c'est ce décalage, et non un trait ou
   un cadre, qui dit d'un coup d'œil ce qui est le sujet et ce qui lui répond.
   Sur un téléphone le retrait tombe à presque rien — il coûterait alors plus
   de largeur de texte qu'il ne rend de lisibilité. */

.message--reponse {
  margin-left: 2rem;
}

@media (max-width: 40rem) {
  .message--reponse { margin-left: 0.6rem; }
}

.message__pied {
  margin-top: 0.75rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--bordure);
}

/* Les pièces d'un message du forum s'alignent comme celles d'une bulle de
   messagerie, dont elles reprennent les classes — même objet, même rendu. */
.forum__pieces {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

/* -------- les réactions d'un message

   Des pastilles, pas des boutons : elles se posent SOUS le message, et ne
   doivent jamais peser autant que « Répondre ». D'où la taille réduite, le
   fond discret, et l'absence de couleur tant que la personne n'a pas réagi
   elle-même — auquel cas la sienne se distingue par un liseré vert, seul
   moyen de savoir d'un coup d'œil si l'on a déjà cliqué.

   Chaque pastille est un formulaire : « display: contents » les fait
   disparaître de la mise en page, sans quoi la rangée de pastilles serait
   une rangée de blocs de formulaire. */

.reactions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.75rem;
}

.reactions__forme {
  display: contents;
}

.reaction {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 1.9rem;
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--bordure);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--texte-doux);
  font-size: 0.95rem;
  line-height: 1.2;
  cursor: pointer;
  transition: border-color .12s ease, background-color .12s ease;
}

.reaction:hover,
.reaction:focus-visible {
  border-color: var(--vert);
}

.reaction--mienne {
  border-color: var(--vert);
  background: var(--vert-fond);
}

.reaction__n {
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

/* Le « + » n'est pas un bouton d'envoi mais le résumé d'un dépliant : on lui
   ôte le marqueur natif, qui ferait un triangle collé au signe. */
.reaction--ajout {
  list-style: none;
  padding: 0.15rem 0.6rem;
}

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

.reactions__ajout {
  position: relative;
}

/* La palette s'ouvre par-dessus le fil plutôt que de l'écarter : un message
   qui grandit en hauteur pousserait tout ce qui suit au moment du clic. */
.reactions__palette {
  position: absolute;
  z-index: 5;
  display: flex;
  gap: 0.3rem;
  margin-top: 0.3rem;
  padding: 0.4rem;
  border: 1px solid var(--bordure);
  border-radius: 0.7rem;
  background: var(--surface);
  box-shadow: var(--ombre-2);
}

@media (max-width: 30rem) {
  .reactions__palette { flex-wrap: wrap; max-width: 12rem; }
}

/* La pastille de commentaires d'une vignette : posée dans le coin, par-dessus
   l'image, pour ne pas voler de place à la légende. Fond sombre translucide
   plutôt qu'une couleur du thème — elle se pose sur des photos de toutes les
   teintes, et seul le contraste garanti la rend lisible sur les deux. */
.vignette__commentaires {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  background: rgba(12, 18, 15, .72);
  color: #fff;
  font-size: 0.75rem;
  line-height: 1.4;
}
