/* Habillage commun. Toutes les couleurs viennent du thème de l'événement :
   ce fichier ne contient aucune valeur de couleur en dur. */

/* Transitions entre pages, SANS application monopage.
 *
 * Le reproche est juste : chaque clic provoquait un éclair blanc, et on
 * perdait le fil. Mais réécrire le site en JavaScript coûterait ce qui
 * fait sa solidité — la fenêtre légale, le sélecteur du pied, le mur
 * lui-même fonctionnent sans JavaScript, et continueront.
 *
 * Le navigateur sait faire les deux choses qui manquaient :
 *   — fondre l'ancienne page dans la nouvelle (ci-dessous) ;
 *   — précharger la page survolée (voir barre.php).
 * Le résultat perçu est celui d'une application monopage. Là où ce n'est
 * pas géré, la navigation reste celle d'aujourd'hui : rien ne casse. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: pm-sortie .16s ease both; }
::view-transition-new(root) { animation: pm-entree .22s ease both; }
@keyframes pm-sortie { to { opacity: 0; } }
@keyframes pm-entree { from { opacity: 0; } }
/* La barre et le pied ne clignotent pas : ils sont les mêmes des deux
   côtés, on les fait glisser d'un seul tenant. */
.barre-haut { view-transition-name: pm-barre; }
footer { view-transition-name: pm-pied; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

* { box-sizing: border-box; }

/* ⚠ LE DÉFILEMENT VERS UNE ANCRE EST DOUX — demandé le 22/08. Un saut instantané
   sur une page de vingt-quatre écrans ne dit pas OÙ l'on a atterri : on se retrouve
   ailleurs sans avoir vu le chemin, et il faut relire le titre pour se situer. Le
   glissement montre la direction, ce qui est la moitié de l'information.
   ⚠ Et il s'éteint pour qui a demandé moins d'animations : un défilement animé est
   exactement ce que ce réglage système existe pour supprimer — pour certaines
   personnes, c'est une source de nausée, pas un agrément. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
/* L'attribut « hidden » ne vaut que « display:none » dans la feuille du
   navigateur : n'importe quelle règle du site qui pose un display le
   contredit. « L'icône en couleur » restait ainsi affichée alors qu'elle
   portait « hidden », parce que .champ-choix est en flex. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }

/* Le pied de page reste EN BAS DE L'ÉCRAN même quand la page est
   courte — sur « Connexion », il flottait au milieu du vide. La page est
   une colonne : le contenu prend la place restante, le pied suit. */
body {
  margin: 0;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  overflow-x: hidden;
}

/* --- Décors ---------------------------------------------------------- */
/* Le cadre a été retiré : il traversait le contenu, et une fois passé à
   l'arrière-plan il ne servait plus à rien qu'à border la fenêtre. Le
   décor tient tout seul. La règle reste, neutralisée, pour que le
   balisage existant n'ait rien à changer. */
.d-cadre { display: none; }

.d-svg, .d-lights, .d-plein, .d-bas, .d-haut { display: block; pointer-events: none; }
/* Les décors restent en place au défilement : ce sont les bords du
   « cadre », pas du contenu. Un feuillage qui remonte avec la page casse
   l'illusion de l'affiche. */
.d-coin { position: fixed; width: clamp(120px, 20vw, 240px); aspect-ratio: 200/120;
          color: var(--sage-pale); opacity: .9; pointer-events: none; z-index: 1; }
/* Les branches du haut partent du bas de la barre, jamais derrière : le
   décor commence là où finit l'interface. */
.d-coin.tl { top: var(--barre-h, 54px); left: 14px; }
.d-coin.tr { top: var(--barre-h, 54px); right: 14px; transform: scaleX(-1); }
.d-coin.bl { bottom: 14px; left: 14px; transform: scaleY(-1); }
.d-coin.br { bottom: 14px; right: 14px; transform: scale(-1, -1); }
.d-coin .d-svg { width: 100%; height: 100%; }
@media (max-width: 720px) { .d-coin.bl, .d-coin.br { display: none; } .d-coin { opacity: .5; } }

.d-lights { position: fixed; top: 0; left: 0; width: 100%; height: 74px;
            color: var(--ink); z-index: 1; }
.d-bulb { animation: d-glow 3.8s ease-in-out infinite; }
@keyframes d-glow { 0%, 100% { opacity: .72; } 50% { opacity: 1; } }

.d-plein { position: fixed; inset: 0; width: 100%; height: 100%;
           color: var(--sage-pale); z-index: 0; }
.d-bas { position: fixed; left: 0; bottom: 0; width: 100%; height: 120px;
         color: var(--sage-pale); z-index: 0; }
.d-haut { position: fixed; left: 0; top: 0; width: 100%; height: 200px;
          fill: var(--sage-pale); z-index: 0; }
@media (prefers-reduced-motion: reduce) { .d-bulb { animation: none; } }

/* --- La barre du haut -------------------------------------------------- */
/* Fixe, discrète, translucide : elle doit être atteignable sans faire
   défiler 40 000 octets de réglages, sans pour autant se disputer
   l'attention avec le grand titre. */
.barre-haut {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-barre);
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  /* ⚠ « clamp(16px, 3vw, 30px) » alignait la barre sur la FENÊTRE, pas sur la
     page : à 1440 px le logo partait à 30 px quand le titre commençait à 190.
     Voir « --page-bord ». */
  padding: 9px var(--page-bord);
  background: color-mix(in srgb, var(--cream) 62%, transparent);
  backdrop-filter: blur(14px) saturate(1.15);
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
  border-bottom: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
}
/* ⚠ LE NOM DU SITE NE SUIT PAS LE THÈME DE LA PAGE. « var(--serif) » vient du
   thème, et chaque thème choisit sa famille : les pages publiques sont en
   « jardin-creme » (Iowan Old Style), l'espace de l'organisateur en « encre-bleue »
   (Didot). « Les petits mots » changeait donc de police entre l'accueil et
   Administration — relevé, pas supposé : Iowan Old Style 16,5 px d'un côté, Didot
   16,5 px de l'autre, même graisse. Un mot-symbole est une identité, pas une
   préférence d'affichage : il garde la police du site, quel que soit le thème que
   l'organisateur a choisi pour SA fête. Le repli sur « --serif » sert aux vignettes
   de thème, qui n'émettent pas le jeton. */
.barre-haut .marque {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--serif-marque, var(--serif)); font-size: 16.5px;
  color: var(--ink-marque, var(--ink));
  text-decoration: none; white-space: nowrap;
}
/* ⚠ UNE MOSAÏQUE DE QUATRE NOTES, PLUS UNE RANGÉE DE TROIS — 20/08.
   Trois rectangles alignés se lisaient, à seize pixels, comme un pictogramme de
   MENU. Et une rangée ne dit pas ce que fait le produit : l'objet du site est un
   MUR de mots, pas une liste. Quatre notes en carré donnent une silhouette
   reconnaissable jusqu'en favicon.
   ⚠ « --pas » est la seule mesure à changer : les trois endroits où la marque
   apparaît — barre, projection, pieds imprimés — n'ont plus qu'à la redéfinir,
   et les proportions suivent. Une marque dont chaque copie a ses propres
   chiffres finit par avoir trois formes.
   Les inclinaisons ne sont pas symétriques exprès : quatre notes posées bien
   droites font une grille de tableur, quatre notes de guingois font un mur. */
.pastilles { --pas: 7px; display: grid; grid-template-columns: repeat(2, var(--pas));
             gap: calc(var(--pas) * .2); }
.pastilles i { width: var(--pas); height: calc(var(--pas) * 1.14); border-radius: 1.5px;
               background: var(--sage); display: block; }
/* ⚠ LE LOGO DE LA BARRE NE SUIT PAS LE THÈME, comme le nom à côté de lui — et
   c'est SUR MOBILE que cela compte, puisque le nom y est masqué et que les tuiles
   restent seules. Scopé à « .barre-haut .marque » : les mêmes pastilles servent
   ailleurs (la vitrine, la projection), où suivre le thème est juste. */
.barre-haut .marque .pastilles i { background: var(--sage-marque, var(--sage)); }
.pastilles i:nth-child(1) { transform: rotate(-9deg); opacity: .92; }
.pastilles i:nth-child(2) { transform: rotate(5deg);  opacity: .62; }
.pastilles i:nth-child(3) { transform: rotate(3deg);  opacity: .74; }
.pastilles i:nth-child(4) { transform: rotate(-5deg); opacity: .52; }
.barre-haut .marque:hover { color: var(--sage-marque, var(--sage)); }

.barre-haut .liens { display: flex; align-items: center; gap: clamp(6px, 1.6vw, 18px); }
.barre-haut .liens a {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 14px; color: var(--ink-soft); text-decoration: none;
  padding: 8px 4px; white-space: nowrap;
}
.barre-haut .liens a:hover { color: var(--sage); }
.barre-haut .liens a[aria-current] { color: var(--sage); }
.barre-haut .liens a.espace {
  border: 1px solid var(--line); border-radius: 999px; padding: 8px 15px;
  color: var(--sage);
}
.barre-haut .liens a.espace:hover { background: var(--sage); border-color: var(--sage);
                                    color: var(--paper); }
/* L'avertissement des tâches planifiées — visible du seul administrateur, et
   seulement quand une tâche est en défaut. ⚠ « --rouge » et « --sur-rouge » et non
   un rouge écrit en dur : ce sont des jetons calculés contre le fond du moment, et
   « color:#fff » sur fond rouge donnait 1,9:1 en mode sombre. */
.barre-haut .liens a.espace.pm-alerte { color: var(--rouge); border-color: var(--rouge-bord); }
.barre-haut .liens a.espace.pm-alerte:hover { background: var(--rouge);
                                              border-color: var(--rouge);
                                              color: var(--sur-rouge); }

/* --- Les deux réglages d'affichage, au bout de la barre ------------------
 *
 * Ils sont RONDS et SANS LIBELLÉ, comme les pastilles flottantes : ce ne sont pas
 * des destinations mais des réglages, et la barre ne doit pas laisser croire
 * qu'elle mène quelque part de plus.
 * ⚠ Le trait est « --line », celui des pastilles, et non « --line-champ » : la
 * leçon du menu « Aller à… », deux jours plus tôt.
 */
.barre-affichage { display: inline-flex; align-items: center; gap: 6px;
                   /* Un peu d'air avec « Mon espace » : ils changent de nature. */
                   margin-inline-start: clamp(2px, .8vw, 8px); }
.barre-nuit, .barre-affichage .pop-menu > button {
  width: 34px; height: 34px; padding: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--paper); color: var(--sage);
  cursor: pointer; font: inherit; flex: 0 0 auto;
  transition: transform .15s ease, border-color .15s ease, color .15s ease;
}
.barre-nuit:hover, .barre-affichage .pop-menu > button:hover {
  border-color: var(--sage); transform: scale(1.07);
}
.barre-nuit:focus-visible, .barre-affichage .pop-menu > button:focus-visible {
  outline: 2px solid var(--sage); outline-offset: 2px;
}
/* ⚠ Le chevron de « .pop-menu > button » n'a pas sa place ici : il n'y a pas de
   libellé à côté duquel s'aligner, et un drapeau plus un chevron dans 34 px ne se
   lisent plus ni l'un ni l'autre. */
.barre-affichage .pop-menu > button svg { display: none; }

/* Le drapeau : un disque, dessiné par des dégradés (voir lib/langue.php).
   ⚠ Un filet intérieur plutôt qu'une bordure : les drapeaux qui portent du blanc
   (France, Italie, Royaume-Uni) se fondraient dans le papier sans lui, et une
   bordure extérieure aurait mangé sur le rond du bouton. */
.drapeau {
  display: inline-block; width: 18px; height: 18px; border-radius: 50%;
  flex: 0 0 auto; background-size: cover; background-position: center;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 22%, transparent);
}

/* Les deux pictogrammes du contraste : un seul est montré, selon le mode.
   ⚠ EN CSS ET NON EN JAVASCRIPT : les deux tracés sont dans la page, et la bascule
   reste juste même si le script tombe — un bouton qui montre le mauvais symbole
   dit le contraire de ce qu'il fait. */
/* ⚠ L'ORDRE COMPTE, ET J'AVAIS ÉCRIT CINQ RÈGLES DONT DEUX SE CONTREDISAIENT : la
   dernière rallumait la lune en mode clair. Trois règles suffisent, dans cet
   ordre — le cas général, puis ce qu'on cache, puis l'exception du mode sombre. */
.barre-nuit .ic-soleil, .barre-nuit .ic-lune { display: inline-flex; }
.barre-nuit .ic-lune { display: none; }
:root[data-nuit] .barre-nuit .ic-soleil { display: none; }
:root[data-nuit] .barre-nuit .ic-lune { display: inline-flex; }

/* Le menu des langues : des BOUTONS, pas des liens (voir assets/affichage.js).
   « .pop-liste a » ne les habille donc pas — on reprend la même facture. */
.pm-langue-choix {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border: 0; border-radius: 6px; background: none;
  font: inherit; font-size: 14px; color: var(--ink-soft);
  text-align: start; cursor: pointer;
}
.pm-langue-choix:hover { background: var(--cream); color: var(--sage); }
.pm-langue-choix:focus-visible { outline: 2px solid var(--sage); outline-offset: -2px; }
.pm-langue-choix.on { color: var(--sage); font-weight: 600; }
#pm-langues { min-width: 186px; }

@media (max-width: 620px) {
  /* Sur un téléphone la barre est déjà serrée : les deux réglages passent au même
     pas que les pastilles de cette largeur. */
  .barre-nuit, .barre-affichage .pop-menu > button { width: 30px; height: 30px; }
  .drapeau { width: 16px; height: 16px; }
  .barre-affichage { gap: 4px; }
}
/* ⚠ « .nu » : le même bouton, sans libellé. Ce n'est pas une nouvelle forme —
   c'est l'état que la barre prend déjà d'elle-même sous 620 px, où le mot
   s'efface et l'icône suffit (voir la media query juste dessous). On en fait un
   état PERMANENT pour le mur d'un invité, où la barre n'a le droit qu'à
   l'essentiel : une icône passe, un menu de service non.
   Le libellé part dans l'infobulle — le mur charge site.css ET infobulle.js,
   donc c'est la vraie bulle du site et non celle du système. */
.barre-haut .liens a.espace.nu { padding: 8px 10px; }
.barre-haut .liens a.espace.nu span { display: none; }
/* Sur un téléphone, quatre entrées plus la marque ne tiennent pas dans
   375 px : mesuré à 550 px de large, soit 175 px hors de l'écran.
   Trois réductions successives, dans cet ordre :
     — le libellé de « Mon espace » s'efface, l'icône suffit ;
     — le nom du site s'efface, les trois pastilles le remplacent ;
     — et en dernier recours la barre de liens défile au doigt. */
@media (max-width: 620px) {
  .barre-haut { padding: 7px 12px; gap: 8px; }
  .barre-haut .marque { font-size: 15px; }
  .barre-haut .liens { gap: 4px; min-width: 0; overflow-x: auto;
                       scrollbar-width: none; }
  .barre-haut .liens::-webkit-scrollbar { display: none; }
  .barre-haut .liens > * { flex: 0 0 auto; }
  .barre-haut .liens a { font-size: 13.5px; padding: 8px 6px; }
  .barre-haut .liens a.espace { padding: 8px 10px; }
  .barre-haut .liens a.espace span { display: none; }
}
@media (max-width: 430px) {
  .barre-haut .marque .nom { display: none; }
}
@media print { .barre-haut { display: none; } }

/* --- Structure -------------------------------------------------------- */
/* « width: 100% » n'est pas décoratif ici.
   Le corps de page est une colonne flex ; un « margin: 0 auto » sur un
   élément flex ANNULE l'étirement, et l'élément se dimensionne alors sur
   son contenu, plafonné par max-width. Tant que le contenu tenait, cela
   ne se voyait pas — le tableau d'administration à treize colonnes a
   révélé le défaut : la page entière défilait de côté. */
/* ⚠ UNE SEULE LARGEUR POUR TOUT LE SITE — 20/08. Il y en avait deux : 940 px
   pour les pages de texte et la création, 1100 pour la galerie, les tableaux et
   les murs. On passait donc d'une page à l'autre en voyant le contenu glisser
   de quatre-vingts pixels, ce que François a relevé en comparant l'accueil à la
   galerie.
   ⚠ Ce qui protège la lisibilité malgré l'élargissement : les blocs de PHRASES
   restent bornés par « --sect » (640 px). Une page plus large n'allonge donc
   pas les lignes de texte — elle donne seulement la même marge à tout le
   monde. « .wrap.large » subsiste comme alias, pour ne pas avoir à toucher les
   sept gabarits qui l'écrivent. */
.wrap { position: relative; z-index: var(--z-contenu); width: 100%; max-width: 1100px;
        margin: 0 auto; padding: 0 20px 60px; }
.wrap.large { max-width: 1100px; }
/* Tout ce qui est une PHRASE est aligné à gauche — décidé le 17/08, pour tout
   le site. Restent centrés les ÉTIQUETTES et les CONTRÔLES : la légende de
   douze pixels sous une vignette de 46 px, une lettre de jour dans un
   calendrier, un badge, la pastille flottante d'un message. Une légende de deux
   mots dans une tuile est de la typographie, pas un paragraphe. */
.hero { text-align: start; padding: 88px 0 6px; }
/* Les deux pages sans barre — le mur d'un invité et l'écran de
   projection — n'ont rien à dégager en haut : le titre remonte, et le
   mur arrive plus vite sous les yeux. Si le navigateur ignore « :has »,
   la marge d'origine reste : rien ne casse. */
body:not(:has(.barre-haut)) .hero { padding-top: 34px; }
@media (orientation: landscape) and (max-height: 500px) {
  body:not(:has(.barre-haut)) .hero { padding-top: 18px; }
}

h1 { font-family: var(--serif); font-weight: 400; line-height: 1.12; margin: 0 0 6px;
     font-size: clamp(28px, 6.4vw, 50px); }
.script { display: block; font-family: var(--serif); font-style: italic;
          font-size: clamp(38px, 10vw, 74px); line-height: 1.05; }
/* L'interligne serré du grand titre en italique (1.05) fait plonger les
   jambages — le « g » et le « j » de « grands jours » arrivaient sur la
   ligne de sous-titre. La marge la descend franchement. */
/* L'ornement s'aligne aussi, et perd son filet de GAUCHE : deux filets
   encadraient un texte centré ; à gauche, le premier pendait dans le vide avant
   le premier mot. */
/* ⚠ PLUS DE TRAIT, ET TOUTE LA LARGEUR. Le petit filet vert à droite du
   sous-titre était un ornement qui coûtait cher : il prenait jusqu'à 64 px et
   14 px de gouttière, donc sur téléphone il poussait le texte à la ligne — un
   sous-titre de trois mots sur deux lignes avec un trait pendant au bout de la
   première. Et sur grand écran il ne décorait rien, puisque le texte
   commençait à gauche. Retiré partout, sur toutes les pages.
   L'interlettrage descend de .28em à .16em : c'est lui, plus que la taille,
   qui faisait déborder « UN MUR, UN QR CODE, AUCUN COMPTE » d'une ligne. */
.fleur { display: block; color: var(--sage); margin: 26px 0 16px; }
.fleur::before, .fleur::after { display: none; }
.fleur span { display: block; font-size: 13px; letter-spacing: .16em;
              text-transform: uppercase; line-height: 1.5; }
/* ⚠ PLUS DE BORNE DE 34 EM. Elle protégeait la lecture — une ligne de cent
   cinquante caractères se perd — mais elle coupait tous les paragraphes du site
   aux deux tiers de leur colonne, et François l'a signalé trois fois. Le garde-
   fou existe ailleurs : « .wrap » borne la page à 940 px et « .wrap.large » à
   1 100, soit au plus cent quinze caractères par ligne. La borne de 34 em
   protégeait donc d'un danger que le conteneur écartait déjà.
   « margin: 0 » et non « 0 auto » : le bloc part de la gauche. */
.lede { max-width: none; margin: 0; color: var(--ink-soft);
        font-size: clamp(15px, 2.2vw, 17px); }

/* Le sous-titre de la carte « Le mur » — même habit que ceux des autres
   activités, qui sont écrits en ligne dans leur JavaScript (14,5 px, encre douce,
   dix-huit pixels d'air en dessous). Ici il est en CSS parce que le mur est rendu
   par PHP : c'est la même valeur, pas une seconde décision. */
.sous-mur { color: var(--ink-soft); font-size: 14.5px; margin: 0 0 18px; }

/* Tous les titres de section : même taille, alignés à gauche.
   « text-align: left » explicitement, car plusieurs cartes centrent leur
   contenu par un style en ligne — le texte de la carte peut être centré, son
   titre non. Sans cette ligne, quatre pages affichaient des titres centrés
   par héritage, et la page se lisait comme une affiche. */
/* ⚠ UN SEUL ÉCART AVANT UN TITRE DE SECTION — 24 px, le même qu'entre deux
   encadrés (20/08). Sur une page de texte — la galerie, l'accueil, « confiance »
   — la frontière entre deux sections n'est pas une bordure, c'est un « h2 » : son
   écart du haut EST l'espacement entre sections. Il valait 26, 34, 38, 40, 44 et
   46 px selon le gabarit, posé à la main dans neuf endroits. Tous retirés.
   ⚠ « margin-top » sur le h2 et non « margin-bottom » sur ce qui précède : c'est
   le titre qui sait qu'il ouvre une section, pas le paragraphe qui la ferme. Et
   un h2 qui ouvre une carte n'en a pas besoin — le rembourrage de la carte fait
   déjà l'espace, d'où la neutralisation qui suit. */
h2 { font-family: var(--serif); font-weight: 400; font-size: 24px;
     margin: 24px 0 8px; text-align: start; }
.carte > h2:first-child, .feuille > h2:first-child, form > h2:first-child,
section > h2:first-child, .hero h2, dialog h2 { margin-top: 0; }
/* Les titres de fenêtre suivent la règle : à gauche. Ils étaient centrés au
   motif qu'une modale n'est pas une section de page — mais son contenu est un
   formulaire aligné à gauche, et l'en-tête centré s'en détachait. */
.feuille h2, #pm-legal h2, #pm-a11y-panneau h2 { text-align: start; }

/* Téléphone tenu à l'horizontale : il ne reste que 375 px de hauteur.
   Le grand titre mangeait tout l'écran et le mur commençait sous la
   ligne de flottaison — un invité qui scanne le QR en tenant son
   téléphone en travers ne voyait aucun Post-it. */
@media (orientation: landscape) and (max-height: 500px) {
  /* 58 px et non 24 : la barre du haut est fixe et haute d'une
     cinquantaine de pixels — en dessous, le titre passait dessous. */
  .hero { padding: 58px 0 2px; }
  h1 { font-size: clamp(24px, 4.6vw, 34px); }
  .script { font-size: clamp(27px, 6vw, 40px); }
  .fleur { margin: 10px 0 8px; }
  .lede { font-size: 14.5px; }
  .wrap { padding-bottom: 30px; }
}

/* --- Boutons et champs ------------------------------------------------ */
/* L'ÉCHELLE DES BOUTONS — quatre pas, nommés, et rien entre eux.
 *
 * Relevé le 17/08 sur la seule page d'un événement : SEPT hauteurs différentes
 * pour des éléments qui se présentent tous comme des boutons — 52, 51, 49, 44,
 * 40, 39 et 38 px. Neuf dans la feuille de style, plus sept tailles posées en
 * style en ligne. Personne ne l'avait décidé : chaque section avait ajusté la
 * sienne, et l'incohérence s'était accumulée section par section.
 *
 *   grand  54  l'appel à l'action d'une page d'accueil, une fois par page
 *   normal 46  l'action ordinaire — c'est le défaut, on n'écrit rien
 *   petit  38  une action secondaire dans une rangée dense
 *   mini   30  une action en fin de ligne dans une liste
 *
 * ⚠ Aucune taille de bouton ne s'écrit plus en style en ligne ni dans une règle
 * de section. Un bouton qui a besoin d'une autre taille a besoin d'un des quatre
 * pas ; s'il n'y rentre pas, c'est le pas qui manque, et il se nomme.
 *
 * Le mini reste au-dessus des 24 px du critère WCAG 2.5.8 ; les trois autres
 * tiennent la cible de 44 px recommandée au doigt.
 */
:root {
  --bt-grand: 54px;  --bt-grand-t: 17px;
  --bt: 46px;        --bt-t: 15.5px;
  --bt-petit: 38px;  --bt-petit-t: 14px;
  --bt-mini: 30px;   --bt-mini-t: 13px;
}
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  min-height: var(--bt); padding: 11px 22px;
  border: 1px solid var(--sage); border-radius: 999px;
  background: var(--paper); color: var(--sage);
  font: inherit; font-size: var(--bt-t); text-decoration: none; cursor: pointer;
  /* « line-height » posé : sans lui le bouton hérite du 1,55 du corps de page,
     son contenu fait 24 px, et la hauteur dépasse le pas de deux pixels — 48 au
     lieu de 46. Deux pixels ne se voient pas seuls ; ils se voient à côté d'un
     champ de 46. */
  line-height: 1.2;
  transition: background .18s ease, color .18s ease, transform .12s ease;
}
.btn.grand { min-height: var(--bt-grand); padding: 14px 30px; font-size: var(--bt-grand-t); }
.btn.petit { min-height: var(--bt-petit); padding: 8px 16px; font-size: var(--bt-petit-t); gap: 7px; }
.btn.mini  { min-height: var(--bt-mini);  padding: 5px 12px; font-size: var(--bt-mini-t); gap: 6px; }
.btn:hover { background: var(--sage); color: var(--paper); }
.btn:active { transform: translateY(1px); }
.btn.plein { background: var(--sage); color: var(--paper); }
.btn.plein:hover { filter: brightness(1.1); }
.btn.discret { border-color: var(--line); color: var(--ink-soft); }
/* Le survol doit repasser en couleur claire : sans cette règle, la couleur
   de .discret l'emportait sur celle de :hover (même spécificité, déclarée
   plus bas), donnant du texte sombre sur fond vert. */
.btn.discret:hover { background: var(--sage); color: var(--paper); border-color: var(--sage); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

label { display: block; font-size: 14px; margin: 16px 0 6px; color: var(--ink-soft); }
/* Un libellé en TÊTE de sa colonne n'a pas de marge haute.
   Sans cette règle, il fallait un « style="margin-top:0" » sur chaque
   premier libellé d'une grille — et il suffisait d'en oublier un pour que
   deux champs côte à côte soient décalés de la hauteur d'une marge. C'est
   exactement ce qui décalait « Ville » sous « Code postal ». Une règle vaut
   mieux que dix rappels.

   ⚠ Ne viser QUE les cellules de grille. Une première version incluait
   « .atelier label:first-child », qui attrapait presque tous les libellés de
   la page et supprimait le rythme vertical entre les champs d'une même
   colonne. */
.deux > label:first-child, .deux > * > label:first-child,
.ch > label:first-child, .carte > label:first-child { margin-top: 0; }
/* ⚠ UN « input[type=hidden] » COMPTE COMME PREMIER ENFANT, et défait la règle
   ci-dessus. Il n'affiche rien, mais « label:first-child » ne s'applique plus :
   le premier libellé retrouve ses 16 px de marge haute, et la colonne descend
   d'autant. C'est ce qui décalait le texte d'explication de « Créer une
   organisation » — sa colonne de droite commençait 16 px plus haut que « Nom »,
   parce que le formulaire de gauche s'ouvre sur « <input type="hidden"
   name="action"> ».
   Sept formulaires du site ouvrent ainsi (un en administration, six en
   réglages) : la règle vaut donc mieux qu'un « style="margin-top:0" » ajouté à
   chacun — c'est déjà le raisonnement du commentaire précédent, poussé d'un
   cran. Deux niveaux d'input caché suffisent : aucun formulaire n'en aligne
   trois avant son premier libellé. */
.deux > * > input[type=hidden]:first-child + label,
.deux > * > input[type=hidden]:first-child + input[type=hidden] + label,
.carte > input[type=hidden]:first-child + label,
.ch > input[type=hidden]:first-child + label { margin-top: 0; }
/* LES CHAMPS ONT LA HAUTEUR DES BOUTONS, et ce n'est pas une coquetterie.
   Ils faisaient 51 px quand un bouton normal en fait 46. Or dans une rangée
   souple — « écris ton mot » + « Ajouter », « colle le lien » + « Enregistrer » —
   les enfants d'un flex s'étirent par défaut sur la hauteur de la ligne : le
   bouton prenait donc 51 px à côté du champ, et 46 ailleurs. Le même bouton
   changeait de taille selon son voisinage, ce qui explique une bonne part de
   l'incohérence constatée.
   « line-height » posé explicitement : sans lui, la hauteur dépend de la
   police du navigateur, et « min-height » ne suffit pas à la fixer. */
input[type=text], input[type=number], input[type=date], input[type=email], textarea, select {
  width: 100%; padding: 11px 13px; font: inherit; font-size: 16px; line-height: 1.35;
  min-height: var(--bt);
  border: 1px solid var(--line-champ, var(--line)); border-radius: 4px;
  background: var(--paper); color: var(--ink);
}
input:focus, textarea:focus, select:focus {
  outline: none; border-color: var(--sage);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 20%, transparent);
}
textarea { min-height: 120px; resize: vertical; font-family: var(--serif); font-style: italic; }
.compte { text-align: end; font-size: 12px; color: var(--ink-soft); margin-top: 5px; }
/* « safe flex-end » et non « flex-end ».
 *
 * Une rangée alignée à droite (ou centrée) qui DÉBORDE pousse son début
 * hors de la boîte, du côté opposé — et ce début devient inatteignable :
 * on ne peut pas défiler vers une zone négative. C'est exactement ce
 * qu'on voyait, le premier bouton coupé à gauche.
 *
 * Le mot-clé « safe » dit au navigateur de retomber sur le début dès que
 * le contenu déborde. L'alignement voulu s'applique quand tout tient,
 * et il s'efface quand il ferait perdre du contenu. */
.ligne { display: flex; gap: 10px; justify-content: safe flex-end; margin-top: 20px;
         flex-wrap: wrap; }
.ligne .btn { flex: 1 1 auto; }
/* ⚠ UNE NOTE DANS UNE LIGNE DE BOUTONS PREND SA PROPRE LIGNE, ET LA DERNIÈRE.
   Écrite entre deux boutons avec « width:100% », elle renvoyait le dernier —
   « Enregistrer », l'action principale de la page — sur une TROISIÈME rangée :
   quatre boutons et une phrase occupaient trois lignes. Le « order » le garantit
   même si le gabarit la place au milieu, ce qui est le cas naturel quand la note
   commente le bouton d'avant.
   ⚠ « text-align: start » et non « left » : la ligne est en « flex-end », donc
   sans cela la phrase se collait à droite ; et « left » serait faux en arabe. */
.ligne-note { flex: 1 0 100%; order: 9; margin: 10px 0 0;
              font-size: 12.5px; color: var(--ink-soft); text-align: start; }
/* « align-items: start » sur toutes les grilles de mise en page.
   Par défaut une grille étire ses éléments (« stretch ») et les aligne donc
   par le haut ET par le bas — ce qui va tant qu'ils se ressemblent. Dès que
   l'un est plus haut, tout ce qui est à l'intérieur des autres descend pour
   remplir, et la seconde colonne paraît décalée. Vu sur les réglages, où
   « Qui l'on fête » commençait plus bas que la carte d'à côté.
   La règle vaut PARTOUT : c'est un défaut d'alignement, pas un choix. */
.deux, .modeles, .docs, .manques, .moderation {
  align-items: start;
}
/* ⚠ « .grille-act » EST SORTIE DE CETTE LISTE — 22/08, demandé : « les cartes
   doivent avoir la même hauteur ». C'est une exception assumée à la règle du
   dessus, et elle tient à ce que ces cartes SONT : non pas des sections de mise en
   page, mais un CHOIX — six tuiles qu'on compare du regard avant de cocher. Une
   tuile plus haute que sa voisine se lit comme « celle-ci compte plus », alors que
   la seule différence est la longueur d'une phrase d'explication.
   ⚠ IL FAUT LES DEUX DÉCLARATIONS. « align-items: stretch » seul étire une carte
   à la hauteur de SA rangée ; « grid-auto-rows: 1fr » donne la même hauteur à
   TOUTES les rangées. Sans la seconde, les trois cartes du haut restaient plus
   grandes que les trois du bas — c'est exactement ce que montrait la capture. */
.grille-act { align-items: stretch; grid-auto-rows: 1fr; }
/* ⚠ PAS « .choix-grille » : ses pastilles doivent s'étirer pour être toutes de
   la même hauteur. « Enterrement de vie de jeune fille ou garçon » passe sur
   deux lignes et rendait sa rangée plus haute que les autres. */
.choix-grille { grid-auto-rows: 1fr; }
.deux { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* Une aide qui parle des DEUX colonnes, ou d'un champ trop étroit pour la porter :
   elle prend la rangée entière. ⚠ « 1 / -1 » et non « span 2 » : si la grille passe
   un jour à une seule colonne sur téléphone, « span 2 » créerait une deuxième
   colonne fantôme — le dépôt a déjà payé pour ça. */
.deux > .aide-large { grid-column: 1 / -1; margin: 2px 0 0;
                      font-size: 12.5px; color: var(--ink-soft); }

/* L'adresse retenue sur l'appareil, et le moyen de l'oublier. */
.email-memoire { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-soft); }
.email-oublier { border: 0; background: none; padding: 0; cursor: pointer;
                 font: inherit; font-size: 12.5px; color: var(--sage);
                 text-decoration: underline; text-underline-offset: 2px; }
.email-oublier:hover { text-decoration-thickness: 2px; }
.email-oublier:focus-visible { outline: 2px solid var(--sage); outline-offset: 3px; }
/* ⚠ LA MARGE HAUTE QU'UN LIBELLÉ AURAIT EUE. La règle d'au-dessus retire les
   16 px du premier libellé de chaque colonne — nécessaire pour que les deux
   colonnes s'alignent entre elles — mais elle retire du même coup l'espace
   au-dessus du BLOC. Mesuré sur « Ajouter un lieu » :

       « Rue et numéro » (libellé simple)     16 px au-dessus
       « Code postal / Ville » (bloc .deux)    0 px au-dessus

   Un champ sur deux se collait donc au précédent, et le formulaire avait deux
   rythmes. Les 16 px rendus ici valent aussi après un titre : h3 + libellé
   simple donnait 4 + 16 ; h3 + .deux donne maintenant la même chose. */
.deux { margin-top: 16px; }
/* Premier élément de son conteneur : rien au-dessus de quoi s'écarter. */
.carte > .deux:first-child, .ch > .deux:first-child,
.atelier > .deux:first-child, form > .deux:first-child { margin-top: 0; }
@media (max-width: 560px) { .deux { grid-template-columns: 1fr; } }
/* Un élément de grille vaut « min-width: auto » : il refuse de descendre
   sous la largeur minimale de son contenu, et pousse alors toute la page.
   C'est le défaut le plus fréquent de cette feuille — il s'est déjà
   manifesté sur « .wrap » puis sur « .atelier ». On le désamorce ici pour
   toutes les grilles de mise en page du site. */
.deux > *, .choix-grille > *, .grille-act > *, .modeles > * { min-width: 0; }

/* ⚠ UN SEUL ÉCART ENTRE DEUX SECTIONS, PARTOUT — 24 px, demandé le 20/08 avec
   sa référence : l'espace entre « Pour qui » et « Documents à partager » sur la
   page Réglages. Cet écart-là venait du « row-gap » de la grille « .atelier » ;
   ailleurs, « .carte » n'avait AUCUNE marge et chaque gabarit posait la sienne à
   la main — relevé avant correction : 14, 18, 20, 22, 26 et 44 px selon la page,
   plus deux « margin-block » composites. Dix-sept déclarations en ligne, toutes
   retirées.
   ⚠ La marge est en HAUT et non en bas : une marge basse s'ajoute à celle du
   pied de page et fait un blanc double en fin de document. En haut, la première
   section n'en a pas besoin — d'où « :first-child ». */
.carte { background: var(--paper); border: 1px solid var(--line); border-radius: 5px;
         padding: 24px; margin-block: 24px 0;
         box-shadow: 0 2px 10px color-mix(in srgb, var(--ink) 12%, transparent); }
.carte:first-child { margin-top: 0; }
/* ⚠ « CE QUE TU APPORTES » COLLAIT À LA SECTION DU DESSUS — 21/08. Le bloc est
   une « .carte » montée par assets/apports.js DANS « #mes-engagements », donc
   elle en est le PREMIER ENFANT : la règle ci-dessus lui mettait « margin-top:0 »,
   et la carte de la cagnotte la touchait, sans un pixel entre elles. La règle
   n'est pas fautive — un premier enfant ne doit pas pousser son conteneur —, il
   manquait l'air AU CONTENEUR.
   ⚠ Posé sur le conteneur et non sur la carte : caché, « #mes-engagements » est en
   « display:none » et ne génère aucune boîte, donc aucune marge parasite quand
   l'invité n'a rien promis. Une marge sur la carte, elle, aurait dû être annulée
   à la main dans ce cas. */
#mes-engagements { margin-top: 24px; }
/* ⚠ ET LES SECTIONS QUI NE SONT PAS DES CARTES. Sur le mur, « qui apporte quoi »,
   la ligne de vie, « en un mot » et la musique sont des <section class="sect">
   remplies par le navigateur : elles n'ont pas de bordure, mais ce sont bien des
   sections, et elles portaient leurs propres marges (34 px). Même écart pour
   toutes, sinon « le même espacement partout » ne vaut que pour les encadrés. */
main section.sect, main > section { margin-block: 24px 0; }
main section.sect:first-child, main > section:first-child { margin-top: 0; }
/* Une section vide n'occupe pas d'espace : celles du mur naissent vides et se
   remplissent quand l'activité répond. Sans cela, quatre marges de 24 px
   s'empilent en haut du mur avant que rien ne soit affiché. */
main section.sect:empty { margin-block: 0; }
/* ⚠ DANS « .atelier », C'EST LA GRILLE QUI ESPACE. Les cartes y sont des
   éléments de grille : leur marge s'AJOUTERAIT au « row-gap » et donnerait 48 px
   là où l'on en veut 24 — et c'est justement cette page qui sert de référence.
   Les cartes INTERNES d'un formulaire, elles, sont en flux normal et gardent
   leur marge : c'est elle qui les sépare. */
.atelier > .carte { margin-block: 0; }

/* ═══ LA GRAMMAIRE DES ENCARTS INFORMATIFS — une seule, 22/08 ═══
 *
 * ⚠ TROIS HABITS DIFFÉRENTS DISAIENT LA MÊME CHOSE. « .avis » portait un filet
 * vert épais sur fond de papier, « .apports-mien » un filet vert sur fond crème,
 * et « .attente-jour » un vrai cadre sur fond crème. François a tranché : tous
 * comme le dernier — « pas de trait gras vertical à gauche, couleur de fond
 * identique, bordure identique ».
 *
 * ⚠ ET LES TROIS SÉLECTEURS PARTAGENT MAINTENANT LA MÊME DÉCLARATION, au lieu de
 * la recopier. C'est la règle du dépôt : trois littéraux identiques finissent
 * toujours par diverger, et c'est exactement ce qui s'était passé ici.
 *
 * ⚠ LE FOND DOIT ÊTRE « --cream » ET NON « --paper ». Un encart vit le plus
 * souvent DANS une carte, et une carte est en papier : sur fond de papier, sans
 * son filet, l'encart disparaîtrait purement et simplement. C'est le filet qui le
 * faisait exister, donc le retirer OBLIGE à changer le fond — les deux vont
 * ensemble, ce n'est pas un choix de goût.
 *
 * ⚠ CE QUI RESTE EN DEHORS, ET POURQUOI :
 *   — « .avis.avis-alcool » : mention légale, rouge clair cerné de rouge sombre,
 *     demandée telle quelle (deux classes, donc elle gagne) ;
 *   — « .manques li » (administration) et « .cuis-l.alerte » : des ALERTES, pas des
 *     informations, et leur filet rouge est ce qui les distingue dans une liste ;
 *   — « .q-phrases li », « .q-mien » : des CONTRIBUTIONS d'invités, pas des encarts ;
 *   — « .ex-note », « .dlg-exemple-avis » : des légendes en retrait, sans cadre ni
 *     fond. Les transformer en encarts ajouterait des boîtes là où il n'y en a pas.
 * ═══════════════════════════════════════════════════════════════════ */
.avis, .attente-jour, .apports-mien {
  padding: 11px 14px; border-radius: 6px;
  background: var(--cream); border: 1px solid var(--line);
  line-height: 1.5;
}
.avis { margin: 18px 0; font-size: 14.5px; color: var(--ink-soft); }

/* Le cadre du mur, DANS la section « Le mur ».
   Il porte la classe de collection et le rognage : la carte, elle, doit garder
   son « overflow » libre, sinon elle couperait le titre au premier arrondi.
   C'est aussi lui que « *:has(> .pm-mur) » désigne — le conteneur de requête
   des écarts en « cqi », et le défilement horizontal sous 700 px. */
.mur-cadre { border-radius: 5px; overflow: hidden; }

/* La ligne d'état sous la mosaïque, dans la même section : « Le mur est encore
   vierge » appartient au mur, pas à la page. Elle flottait sous la carte, à la
   place d'une légende d'image. */
.compteur { text-align: start; margin: 14px 0 0; color: var(--ink-soft); font-size: 14px; }
.compteur b { font-family: var(--serif); color: var(--ink); }

/* --- Choix visuels (thème, collection, type) -------------------------- */
.choix-grille { display: grid; gap: 10px; margin-top: 8px;
                grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
/* Trois colonnes fixes : neuf cartes forment un carré, qui se lit d'un
   coup d'œil. Deux colonnes sous 700 px, une seule sous 460. */
.choix-grille.trois { grid-template-columns: repeat(3, 1fr); gap: 12px; }
@media (max-width: 700px) { .choix-grille.trois { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 460px) { .choix-grille.trois { grid-template-columns: 1fr; } }

.lien-galerie { font: inherit; font-size: 13px; color: var(--sage); cursor: pointer;
                background: transparent; border: 0; padding: 2px 0;
                display: inline-flex; align-items: center; gap: 6px;
                text-decoration: underline; text-underline-offset: 3px; }
.lien-galerie:hover { color: var(--ink); }
.choix-grille label { margin: 0; cursor: pointer; }
.choix-grille input { position: absolute; opacity: 0; width: 0; height: 0; }
.pastille { display: block; padding: 12px; border: 1.5px solid var(--line); border-radius: 5px;
            background: var(--paper); text-align: center; font-size: 13.5px; color: var(--ink);
            transition: border-color .15s ease, transform .12s ease; }
.choix-grille input:checked + .pastille { border-color: var(--sage); border-width: 2px;
                                          transform: translateY(-2px); }
.choix-grille input:focus-visible + .pastille { outline: 2px solid var(--sage); outline-offset: 2px; }
.pastille .bande { display: flex; gap: 3px; margin-bottom: 8px; }
.pastille .bande i { flex: 1; height: 16px; border-radius: 2px; }
.pastille .gros { font-size: 21px; display: block; margin-bottom: 3px; }
/* ⚠ SORTIS DU STYLE EN LIGNE. Le titre et le sous-titre d'une pastille étaient
   écrits dans l'attribut « style » de chaque occasion : aucune règle, aucune
   requête média ne pouvait plus les toucher — c'est ce qui les laissait à
   12,5 px sur téléphone alors qu'une règle les relevait, sans effet. */
.pastille strong { display: block; font-family: var(--serif); font-weight: 400;
                   font-size: 15.5px; }
.pastille .sous, .pastille > span:last-child { color: var(--ink-soft); font-size: 12.5px; }

/* --- Fenêtres --------------------------------------------------------- */
/* La largeur des sections d'une page d'événement, en un seul endroit.
   Le mur se calculait sur le nombre de colonnes — « min(860, 90 × grille) »,
   soit 450 px pour une mosaïque de cinq — pendant que les autres sections
   étaient figées à 640 : sur la même page, cinq largeurs différentes.
   Une variable, et tout s'aligne. */
/* ⚠ L'ÉCHELLE DES CALQUES — nommée, parce qu'elle ne l'était pas et que cela
   se voyait. Relevé le 20/08 : vingt-six valeurs de « z-index » dans le dépôt,
   de 0 à 1000, posées au fil des besoins. Conséquences visibles sur les
   captures de François :
     · le menu « Colonnes » d'un tableau était à 8, les pastilles flottantes à
       35 : le bouton de réglages se dessinait PAR-DESSUS le menu ouvert ;
     · le voile du panneau d'accessibilité était à 34 et son panneau à 36, avec
       les pastilles à 35 — donc COINCÉES ENTRE LES DEUX, visibles au-dessus du
       voile qui était censé tout éteindre.
   Cinq noms suffisent. Toute nouvelle couche doit en choisir un, pas inventer
   un nombre.
     contenu  →  la page
     barre    →  la barre du haut, collée
     flottant →  les pastilles de coin : au-dessus du contenu, SOUS tout menu
     menu     →  listes déroulantes, calendriers, menus de tableau
     toast    →  le retour d'information éphémère : au-dessus de tout ce qui
                 précède, car il annonce le résultat de ce qu'on vient de
                 faire — un message caché par le bouton qui l'a déclenché ne
                 sert à rien. ⚠ Il ne peut PAS passer devant un « dialog »
                 modal, qui vit dans le calque supérieur du navigateur ; les
                 fenêtres portent leur propre retour d'information.
     voile    →  l'écran sombre d'une fenêtre
     modale   →  la fenêtre elle-même
   ⚠ Un « popover » natif et un « dialog » modal vivent dans le CALQUE SUPÉRIEUR
   du navigateur, au-dessus de tout ceci quel que soit leur z-index. C'est une
   raison de plus de les préférer. */
:root {
  --z-contenu: 2;
  --z-barre: 30;
  --z-flottant: 31;
  --z-menu: 45;
  --z-toast: 50;
  --z-voile: 60;
  --z-modale: 61;
}

:root { --sect: min(100%, 640px); }

/* ⚠ LE BORD DE LA PAGE, pour tout ce qui est FIXE. « .wrap » borne le contenu à
   1 100 px avec 20 px de rembourrage : ses bords tombent donc à
   max(20px, (100vw - 1100px) / 2 + 20px) de la fenêtre. La barre du haut et les
   pastilles flottantes, elles, se posaient sur la fenêtre — 30 px pour la barre,
   16 px pour les pastilles — si bien que le logo, les liens et les pastilles
   flottaient à côté de la colonne de texte au lieu de s'aligner dessus. Mesuré
   en 1440 px : contenu de 190 à 1250, logo à 30, liens jusqu'à 1410, pastilles à
   16 et 1424.
   ⚠ « 50% » ET NON « 50vw » : pour un élément fixe, le pourcentage se résout sur
   le bloc conteneur initial, qui EXCLUT la barre de défilement classique —
   « vw » l'inclut, et tout aurait glissé d'une quinzaine de pixels sur Windows.
   Et la formule est celle de « .wrap », pas une approximation :
   (100vw - 1100px) / 2 + 20px = 100vw / 2 - 530px, à toute largeur. */
:root { --page-bord: max(20px, calc(50% - 530px)); }

/* L'AXE des pastilles flottantes, depuis le bas de la fenêtre : 16 px de marge
   plus la moitié des 44 px de la pastille. Sur téléphone, 12 + 20.
   ⚠ CE N'EST PLUS UNE BANDE À ÉVITER, C'EST UNE LIGNE À REJOINDRE — revirement
   du 21/08. La veille, le pied recevait un rembourrage assez grand pour passer
   AU-DESSUS des pastilles, qui se posaient sur « Tous droits réservés · Hébergé
   en France » et le rendaient illisible. Corrigé — mais le pied se retrouvait
   alors 61 px plus haut qu'elles (mesuré), et les trois éléments du bas de page
   ne formaient plus une ligne.
   ⚠ Ce qui rend l'alignement possible sans revenir au recouvrement : les glyphes
   de cette ligne ne vont que de 399 à 1041 sur un écran de 1440, alors que les
   pastilles sont à 190–234 et 1206–1250 — 165 px d'air de chaque côté, mesurés.
   Le texte est centré et court ; il ne les atteint pas.
   ⚠ Les deux déclarations sont VOISINES : la media query avait été séparée de sa
   variable par un bloc inséré entre les deux, et ma première substitution a
   échoué là-dessus. Une valeur et sa variante se tiennent. */
:root { --flottant-axe: 38px; --flottant-taille: 44px; }
@media (max-width: 560px) { :root { --flottant-axe: 32px; --flottant-taille: 40px; } }

/* Un bouton qui porte un pictogramme en tête : l'icône et le mot se tiennent,
   et le trait ne s'étire pas si le libellé passe sur deux lignes. */
.avec-ic { display: inline-flex; align-items: center; gap: 5px; }
.avec-ic svg { flex: 0 0 auto; }
/* ⚠ Toute règle qui pose ensuite « margin: … » en forme courte sur une de ces
   sections REMET ces deux marges à zéro et casse le centrage, sans toucher à la
   largeur : la section reste bonne et glisse à gauche. Utiliser « margin-block ».
   C'est le défaut qu'on a corrigé le 17/08 sur six sections de e.php. */
.sect { max-width: var(--sect); margin-inline: auto; }

/* La fenêtre d'écriture, plus large : « Personnaliser ma note » y déplie
   les formes, les couleurs, les écritures et les icônes, et 470 px les
   serraient au point qu'on ne voyait pas qu'il y avait quelque chose à
   ouvrir. */
#dlg { width: min(94vw, 640px); }
dialog.large { width: min(94vw, 620px); }
/* ⚠ « color: var(--ink) » N'EST PAS REDONDANT, C'EST LE CORRECTIF. La feuille du
   navigateur pose « dialog { background-color: Canvas; color: CanvasText } » :
   nous ne remplacions que le FOND. « CanvasText » vaut NOIR tant que la page ne
   déclare pas « color-scheme: dark » — donc en mode sombre, chaque fenêtre du
   site écrivait en noir pur sur un papier à #2a2928. Mesuré, pas supposé :
   getComputedStyle(dialog).color rendait rgb(0,0,0) alors que --ink valait bien
   #e7e2d9. Personne ne l'avait vu parce qu'en mode clair le noir passe pour de
   l'encre. */
dialog { border: 0; padding: 0; width: min(94vw, 470px); border-radius: 6px;
         background: transparent; color: var(--ink);
         box-shadow: 0 24px 70px color-mix(in srgb, var(--ink) 45%, transparent); }
dialog::backdrop { background: color-mix(in srgb, var(--ink) 55%, transparent); }
/* ⚠ LE REMBOURRAGE DE LA FEUILLE EN UN SEUL ENDROIT — 26/08. La barre d'action collée
   au bas doit déborder jusqu'aux bords du papier : elle a besoin des mêmes valeurs, et
   deux nombres recopiés auraient divergé au premier réglage. */
:root { --dlg-marge-x: 24px; --dlg-marge-bas: 22px; }
.feuille { position: relative; background: var(--paper);
           padding: 26px var(--dlg-marge-x) var(--dlg-marge-bas);
           border: 1px solid var(--line); border-radius: 6px; }

/* ⚠ LA BARRE QUI REFERME UNE FENÊTRE RESTE AU BAS DE LA FENÊTRE — 26/08, signalé sur la
   note du mur : « s'il y a déjà une image il y a confusion entre les boutons de la photo
   et les boutons annuler et valider ».
   Mesuré avant d'y toucher, sur une note portant une voix ET une photo : « Valider » était
   HORS CHAMP à l'ouverture, sur téléphone comme sur un écran de 1 280 × 900, et les boutons
   de la photo se trouvaient 79 px plus haut. On rencontrait donc une paire de boutons avant
   celle qui referme.
   ⚠ C'EST LE <dialog> QUI DÉFILE, PAS LA FEUILLE — relevé, pas supposé : le dialogue porte
   « overflow-y: auto » et « max-height: calc(100% - 38px) », la feuille n'a ni l'un ni
   l'autre.
   ⚠ ET LE « sticky » VA SUR LE CADRE, PAS SUR LA RANGÉE — trouvé en mesurant, après un
   premier jet qui ne collait à rien. « assets/rangs.js » ENVELOPPE toute « .ligne » dans un
   « .defile-cadre » (pour la faire défiler horizontalement si les boutons débordent) : ce
   cadre devenait le bloc conteneur du sticky, haut de 82 px, c'est-à-dire exactement la
   taille de la rangée — aucune course, donc aucun collage. Le DOM servi ne le montre pas :
   le cadre est créé par un script, et il fallait demander son parent à la page vivante.
   ⚠ ON NE RETIRE PAS LA RANGÉE DE « rangs.js » pour autant : en allemand ou en arabe, deux
   boutons et une phrase d'état peuvent déborder d'un écran de 320 px, et le défilement
   horizontal est alors le seul recours. Épinglé ET défilant : les deux servent.
   ⚠ « :has() » CIBLE LE SEUL CADRE QUI NOUS INTÉRESSE. « #dlg .defile-cadre » aurait aussi
   pris ceux des cartes de voix et de photo, qui sont des « .rang > .piste » — donc des
   cibles de rangs.js elles aussi.
   ⚠ Sans le script, pas de cadre et pas de barre collée : le repli est l'état d'avant, pas
   un état neuf.
   ⚠ DEUX AUTRES RÈGLES PORTENT LA MÊME VALEUR HORIZONTALE EN DUR : « .destructif::before »
   et « dialog.destructif .feuille::before » écrivent « -24px ». Elles ne sont PAS passées
   au jeton parce que leurs valeurs VERTICALES visent un autre rembourrage, et que je n'ai
   pas mesuré ces fenêtres-là. Si « --dlg-marge-x » change, les relire. */
#dlg .defile-cadre:has(> .ligne-collante) {
  position: sticky; bottom: 0; z-index: 3;
  margin: 18px calc(-1 * var(--dlg-marge-x)) calc(-1 * var(--dlg-marge-bas));
  padding: 13px var(--dlg-marge-x) var(--dlg-marge-bas);
  background: var(--paper);
  border-top: 1px solid var(--line);
}
/* La rangée elle-même ne porte plus que la disposition de son contenu : sa marge du haut
   est passée au cadre, sinon les deux s'ajouteraient. */
#dlg .ligne-collante { margin-top: 0; align-items: center; }
/* L'état (« Modifications non enregistrées ») prend la place restante, à gauche des deux
   boutons — et passe à la ligne avant eux si la largeur ne suffit pas. */
#dlg .ligne-collante .dlg-etat {
  flex: 1 1 auto; min-width: 0; order: -1;
  font-size: 13px; color: var(--ink-soft); line-height: 1.3;
}
#dlg .ligne-collante .dlg-etat:empty { display: none; }
#dlg .ligne-collante .btn { flex: 0 1 auto; min-width: 116px; }
.feuille h2 { text-align: start; margin-bottom: 2px; }
.feuille .sous { text-align: start; color: var(--ink-soft); font-size: 14px; margin: 0 0 14px; }

/* --- Choix d'un nombre (invités) --------------------------------------- */
/* Une grille, pas une rangée défilante : ces onze valeurs doivent être
   toutes visibles d'un coup. En rangée, la moitié était hors champ, et
   sans « min-width:0 » la colonne du formulaire débordait de sa carte. */
.grille-nb {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; min-width: 0;
}
.pastille-nb {
  font: inherit; font-size: 15px; min-width: 52px; padding: 10px 15px;
  border: 1.5px solid var(--line-champ, var(--line)); border-radius: 999px;
  background: var(--paper); color: var(--ink-soft); cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.pastille-nb:hover { border-color: var(--sage); color: var(--sage); }
.pastille-nb.on { background: var(--sage); border-color: var(--sage); color: var(--paper); }
.pastille-nb:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }

/* --- Choix de l'icône du QR -------------------------------------------- */
.orn-choix {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  margin: 0; padding: 9px 8px 7px; min-width: 62px; cursor: pointer;
  border: 1.5px solid var(--line); border-radius: 7px;
  background: var(--paper); color: var(--ink-soft);
  transition: border-color .15s ease, color .15s ease, transform .12s ease;
}
.orn-choix input { position: absolute; opacity: 0; width: 0; height: 0; }
.orn-choix .vu { font-size: 10.5px; line-height: 1.1; text-align: center; }
.orn-choix:hover { border-color: var(--sage-pale); color: var(--sage); }
.orn-choix.on {
  border-color: var(--sage); border-width: 2px; color: var(--sage);
  transform: translateY(-2px);
}
.orn-choix:focus-within { outline: 2px solid var(--sage); outline-offset: 2px; }

/* --- Champs : les exemples se distinguent de ce qu'on a saisi ---------- */
/* Un exemple affiché de la même encre que le texte réel se lit comme du
   texte réel : on croit le champ rempli et on passe au suivant. */
::placeholder { color: var(--ink-soft); opacity: .68; font-style: italic; }
input:focus::placeholder, textarea:focus::placeholder { opacity: .45; }

/* --- Pied de page : une seule ligne ------------------------------------ */
/* Une seule ligne, vraiment : elle défile au doigt plutôt que de se
   replier, sinon « une ligne » n'en est une que sur grand écran. */
.pied-ligne {
  /* « safe center » : le pied défile aussi, et centré-qui-déborde rend
     son premier lien inatteignable. */
  display: flex; flex-wrap: nowrap; align-items: center; justify-content: safe center;
  gap: 10px; margin: 0; font-size: 13px; color: var(--ink-soft);
  overflow-x: auto; scrollbar-width: none; padding-bottom: 2px;
  scroll-snap-type: x proximity;
}
.pied-ligne > * { scroll-snap-align: start; }
.pied-ligne::-webkit-scrollbar { display: none; }
.pied-ligne > * { flex: 0 0 auto; white-space: nowrap; }
.pied-ligne a { color: var(--sage); text-decoration: underline; text-underline-offset: 3px; }
.pied-ligne a:hover { color: var(--ink); }
/* ⚠ SUR TÉLÉPHONE, LE PIED S'EMPILE — 20/08. « Une seule ligne qui défile »
   marche pour une rangée de boutons qu'on longe ; pour quatre mentions
   légales, cela oblige à faire défiler un ruban de treize pixels pour savoir
   ce qu'il contient, entre deux pastilles flottantes qui l'encadrent. On
   empile, et les points de séparation disparaissent — ils ne séparent plus
   rien. */
/* Les groupes restent des morceaux de ligne sur grand écran : rien ne change. */
.pied-groupe { display: inline-flex; align-items: center; gap: 10px; }
@media (max-width: 700px) {
  .pied-ligne {
    flex-direction: column; flex-wrap: nowrap; align-items: center;
    gap: 8px; overflow: visible; scroll-snap-type: none;
  }
  /* ⚠ LES POINTS DE TÊTE DISPARAISSENT, PAS CEUX DE L'INTÉRIEUR. Un point en
     début de ligne ne sépare rien ; celui qui reste, entre « Tous droits
     réservés » et « Hébergé en France », sépare vraiment deux mentions sur la
     même ligne. D'où le ciblage du PREMIER enfant de chaque groupe, et non de
     tous les points. */
  .pied-groupe > [aria-hidden="true"]:first-child,
  .pied-ligne > .pied-sep { display: none; }
  .pied-ligne > * { white-space: normal; text-align: center; }
  .pied-groupe { flex-wrap: wrap; justify-content: center; }
}
.pied-menu { position: relative; display: inline-flex; }
.pied-menu button {
  display: inline-flex; align-items: center; gap: 5px;
  font: inherit; font-size: 13px; color: var(--sage); cursor: pointer;
  background: transparent; border: 0; padding: 2px 0;
  text-decoration: underline; text-underline-offset: 3px;
}
.pied-menu button:hover { color: var(--ink); }
.pied-menu button svg { transition: transform .18s ease; }
.pied-menu button[aria-expanded="true"] .pm-chevron { transform: rotate(180deg); }
/* La liste s'ouvre VERS LE HAUT : en bas de page, elle sortirait sinon
   de la fenêtre et il faudrait défiler pour la lire. */
/* Le popover natif a son propre calque : on ne fixe que sa forme, pas
   sa pile. « margin:0 » annule le centrage par défaut du navigateur. */
.pied-liste, .pied-liste:popover-open {
  position: fixed; margin: 0; inset: auto; z-index: 32; border: 1px solid var(--line);
  max-height: 70vh; overflow-y: auto; display: flex; flex-direction: column; min-width: 232px;
  padding: 7px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 -8px 30px color-mix(in srgb, var(--ink) 22%, transparent);
}
.pied-liste:not(:popover-open):not([hidden]) { display: none; }
.pied-liste::backdrop { background: transparent; }
.pied-liste a {
  padding: 9px 13px; border-radius: 5px; font-size: 13.5px;
  color: var(--ink-soft); text-decoration: none; text-align: start; white-space: nowrap;
}
.pied-liste a:hover { background: var(--cream); color: var(--sage); }
@media (max-width: 420px) { .pied-ligne { gap: 7px; font-size: 12px; } }

/* --- Fenêtre des informations légales ---------------------------------- */
/* Plus large que les autres fenêtres : ce sont des pages de texte. */
#pm-legal { width: min(94vw, 760px); }
#pm-legal .feuille { padding: 22px 24px 20px; max-height: 88vh;
                     display: flex; flex-direction: column; }
/* ⚠ LA MARGE ÉTAIT SYMÉTRIQUE, ET C'EST CE QUI DÉSALIGNAIT LE TITRE — 22/08.
   « margin: 0 30px » dégageait la croix de fermeture… des DEUX côtés. Le titre
   commençait donc 30 px plus à droite que les onglets et que le texte, ce qui se
   lit comme « le titre n'est pas aligné à gauche » — alors que son « text-align »
   était bien « left ». La croix est en haut à DROITE : seule la marge droite avait
   une raison d'être. */
#pm-legal h2 { margin: 0 44px 14px 0; font-size: 22px; flex: 0 0 auto; }

/* Les onglets tiennent sur UNE ligne, toujours. Trop étroit pour les
   cinq ? Ils défilent horizontalement — passer à la ligne ferait sauter
   la hauteur de la fenêtre à chaque changement d'onglet. */
.pm-legal-onglets {
  display: flex; flex-wrap: nowrap; gap: 7px; overflow-x: auto; overflow-y: hidden;
  padding-bottom: 9px; margin-bottom: 4px; border-bottom: 1px solid var(--line);
  /* ⚠ PLUS DE « scrollbar-width: thin » ICI NON PLUS — 31/08 : « la barre de
     défilement ne doit jamais être visible, dans aucune boîte ». C'était la seule
     exception qui restait dans la feuille, et une règle générale qui garde une
     exception oubliée finit par ne plus être une règle. Les cinq onglets tiennent
     dans la fenêtre en français ; s'ils débordent dans une autre langue, le geste
     de défilement fonctionne toujours — c'est l'indice qui manque, pas le
     mouvement, et l'indice sera un mot le jour où quelqu'un le relèvera. */
  flex: 0 0 auto;
}
.pm-legal-onglets a {
  flex: 0 0 auto; white-space: nowrap;
  font-size: 13px; padding: 7px 14px; border-radius: 999px; text-decoration: none;
  border: 1px solid var(--line); color: var(--ink-soft); background: var(--paper);
}
.pm-legal-onglets a:hover { border-color: var(--sage); color: var(--sage); }
.pm-legal-onglets a.on,
.pm-legal-onglets a.on:hover { background: var(--sage); border-color: var(--sage);
                               color: var(--paper); }

/* Hauteur stable : sans elle, la fenêtre s'ouvrait petite puis grandissait
   d'un coup une fois le texte arrivé. Le contenu est désormais préchargé,
   mais la réserve reste — un réseau lent existe toujours. */
/* Hauteur de RÉFÉRENCE : celle de la page « Cookies », la plus courte
   des cinq. Les autres défilent à l'intérieur. La fenêtre garde ainsi
   exactement la même taille d'un onglet à l'autre — c'est le
   redimensionnement qui donnait l'impression d'un chargement. */
#pm-legal-corps { overflow-y: auto; padding: 14px 2px 4px; line-height: 1.62;
                  flex: 0 0 auto; height: min(56vh, 430px); }
#pm-legal-corps h3 { font-family: var(--serif); font-weight: 400; font-size: 18px;
                     margin: 22px 0 6px; color: var(--ink); }
#pm-legal-corps h2 { font-family: var(--serif); font-weight: 400; font-size: 20px;
                     margin: 4px 0 8px; text-align: start; }
#pm-legal-corps p, #pm-legal-corps li { color: var(--ink-soft); margin: 0 0 10px; }
#pm-legal-corps a { color: var(--sage); }
#pm-legal-corps table { width: 100%; border-collapse: collapse; font-size: 14px; margin: 10px 0; }
#pm-legal-corps th, #pm-legal-corps td { text-align: start; padding: 8px 10px;
                     border-bottom: 1px solid var(--line); vertical-align: top;
                     color: var(--ink-soft); }
#pm-legal-corps th { font-family: var(--serif); font-weight: 400; color: var(--ink); }
#pm-legal-corps code { background: var(--cream); padding: 1px 5px; border-radius: 3px;
                       font-size: 13px; }
#pm-legal-corps .carte { background: transparent; border: 0; padding: 0; box-shadow: none; }
/* La croix de fermeture : même place, même taille, dans toutes les
   fenêtres du site. 38 px, donc atteignable au pouce. */
.pm-fermer { position: absolute; inset-inline-end: 12px; top: 12px; z-index: 2;
  width: 38px; height: 38px; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-champ, var(--line));
  background: var(--paper); color: var(--ink-soft); }
.pm-fermer:hover { color: var(--ink); border-color: var(--sage); }
.pm-fermer:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
/* Le titre d'une fenêtre ne passe jamais sous la croix. */
.feuille > h2:first-of-type { padding-inline-end: 44px; }
#pm-legal .ligne { flex: 0 0 auto; }
/* Rien de ce qui vient d'une autre page ne doit pouvoir écarter la
   fenêtre : ni une image sans dimension, ni un tableau trop large. */
#pm-legal-corps img, #pm-legal-corps svg { max-width: 100%; height: auto; }
#pm-legal-corps .tableau, #pm-legal-corps table { display: block; overflow-x: auto; }

/* --- Contenu de la page « Vos données restent en France » --------------
   Décrit un contenu et non une page : la fenêtre légale reprend le même
   balisage, elle doit donc hériter des mêmes règles. Laissées dans le
   <style> de confiance.php, le bouclier s'affichait à pleine largeur
   dans la fenêtre, faute de dimension. */
/* ⚠ UNE SEULE COLONNE DEPUIS LE 22/08. Ces quatre blocs portaient un pictogramme
   de 44 px à gauche du titre — et c'était la SEULE page du site où un titre en
   portait un. La grille à deux colonnes n'avait plus rien à placer dans la
   première : laissée telle quelle, elle aurait creusé un retrait de 60 px que
   personne n'expliquait. Retirer une décoration, c'est aussi retirer la place
   qu'elle occupait. */
.fait { padding: 18px 0; border-bottom: 1px solid var(--line); }
.fait:last-child { border-bottom: 0; }
.fait h3 { font-family: var(--serif); font-weight: 400; font-size: 19px; margin: 0 0 4px; }
.fait p { margin: 0; color: var(--ink-soft); font-size: 15px; line-height: 1.55; }
.tableau { width: 100%; border-collapse: collapse; font-size: 14.5px; margin-top: 10px; }
.tableau th, .tableau td { text-align: start; padding: 9px 10px;
                           border-bottom: 1px solid var(--line); vertical-align: top; }
.tableau th { font-family: var(--serif); font-weight: 400; color: var(--ink); }
.tableau td { color: var(--ink-soft); }

/* --- Message éphémère ------------------------------------------------- */
/* ⚠ « width: max-content » N'EST PAS UNE COQUETTERIE. Avec « left: 50% » et
   sans « right », la largeur disponible pour un élément fixe est ce qui reste
   À DROITE du milieu — la MOITIÉ de l'écran. La boîte se rétrécissait donc à
   187 px sur un téléphone de 375, le « max-width: 420 » n'étant jamais
   atteignable, et « translate(-50%) » la décalait alors vers la droite : le
   message partait hors de l'écran à droite. Défaut d'origine, hérité de la
   première version en ligne, révélé par un message long.
   « max-content » remplace ce calcul par la largeur du texte, que
   « max-width » plafonne ensuite pour de bon. */
/* ⚠ LE TOAST ÉTAIT UNE PASTILLE BRUN FONCÉ — refait le 22/08 : « les toasts marron
   foncés sont laids, il faut trouver une autre façon, en lien avec le thème ».
   Il avait raison sur le fond : c'était le SEUL élément sombre du site. Papier crème,
   filets fins, accent sauge — et au milieu, une pastille d'encre pleine. Elle ne
   ressemblait à rien d'autre, donc elle ressemblait à une erreur.

   ⚠ CE N'EST PAS UNE QUESTION DE GOÛT MAIS DE GRAMMAIRE : le site a déjà une façon
   de dire « ceci flotte au-dessus de la page » — les fenêtres. Papier, filet d'un
   pixel, ombre portée. Le toast la reprend, au lieu d'inventer un troisième habit.
   C'est le même raisonnement qui avait fait remplacer le bandeau de modération par
   ce toast : trois façons de dire quelque chose, c'est trois choses à accorder.

   ⚠ ET L'OMBRE FAIT LE TRAVAIL DU CONTRASTE. Un toast papier sur une carte papier
   ne se distinguerait pas par sa couleur : c'est l'ombre qui le décolle, comme pour
   les fenêtres. Sans elle, le message se fondrait dans la page — pire que trop
   sombre.
   ⓘ Contraste du texte : encre sur papier, le rapport le plus élevé du site. */
.flash { position: fixed; left: 50%; bottom: 26px; transform: translate(-50%, 130%);
         opacity: 0; pointer-events: none; z-index: 40;
         width: max-content; max-width: min(92vw, 420px); padding: 13px 20px;
         background: var(--paper); color: var(--ink);
         border: 1px solid var(--line); border-radius: 999px;
         box-shadow: 0 14px 40px color-mix(in srgb, var(--ink) 24%, transparent);
         font-size: 14.5px; text-align: center;
         transition: transform .3s cubic-bezier(.2,.8,.3,1), opacity .25s ease; }
.flash.montre { transform: translate(-50%, 0); opacity: 1; }
/* ⚠ « z-index: 40 » ci-dessus était un nombre nu, hors de l'échelle nommée :
   il tombait entre « flottant » (31) et « menu » (45), donc un calendrier ou
   une liste déroulante ouverte passait DEVANT le message. Et le remplacer par
   « --z-flottant » aurait été pire — 31 est plus bas que 40, le toast serait
   passé sous les pastilles de coin. Un barreau propre, au-dessus des menus. */
.flash { z-index: var(--z-toast); }
.flash-mot { display: block; }

/* Avec un bouton « Annuler », la pastille devient une boîte : un bouton dans
   une forme entièrement arrondie touche le bord. Et elle redevient cliquable
   — « pointer-events: none » plus haut protège les toasts sans action, qui ne
   doivent pas intercepter un clic destiné à la page dessous. */
.flash.avec-bouton { border-radius: 16px; padding: 14px 18px; pointer-events: auto;
                     display: flex; align-items: center; gap: 14px; text-align: start; }
.flash.avec-bouton .flash-mot { flex: 1 1 auto; min-width: 0; }
.flash.avec-bouton form { margin: 0; flex: 0 0 auto; }
/* ⚠ LES BLANCS EN OPACITÉ ONT DISPARU AVEC LE FOND SOMBRE : « rgba(255,255,255,.45) »
   sur du papier crème donnait une bordure invisible. Un bouton dessiné pour un fond
   qui n'existe plus est un bouton qu'on ne voit plus — et c'est celui qui ANNULE. */
.flash-annuler { background: var(--cream); border: 1px solid var(--line);
                 color: var(--ink); border-radius: 999px;
                 padding: 6px 14px; font-size: 13.5px; font-family: inherit;
                 cursor: pointer; white-space: nowrap; }
.flash-annuler:hover { background: var(--paper); border-color: var(--sage); }
.flash-annuler:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }

/* Sur un téléphone, le toast remonte au-dessus de la barre d'onglets du
   navigateur, et garde seize pixels de marge de chaque côté. */
@media (max-width: 560px) {
  .flash { bottom: calc(18px + env(safe-area-inset-bottom, 0px));
           max-width: calc(100vw - 32px);
           /* ⚠ UNE BORNE DE HAUTEUR, ET UN TEXTE PLUS SERRÉ — 22/08, signalé :
              « le toast a des textes trop longs, il devient omniprésent,
              particulièrement sur mobile ». Les messages ont été raccourcis (le plus
              long passe de 157 à 93 caractères, mesuré), mais une borne reste
              nécessaire : le jour où l'on écrit une phrase de trop, le toast ne doit
              pas pouvoir manger la moitié de l'écran.
              ⚠ ON NE COUPE PAS LE TEXTE. Un « line-clamp » cacherait la fin d'un
              message qui dit quoi faire — « Réessaie », « Rouvre ta note » — et un
              conseil tronqué est pire qu'un conseil long. On resserre l'interligne
              et le rembourrage, et l'on plafonne à un tiers de la hauteur avec un
              défilement propre si jamais on dépasse. */
           max-height: 33vh; overflow-y: auto; overscroll-behavior: contain;
           padding: 11px 16px; font-size: 14px; line-height: 1.4;
           /* Un rayon plein cercle sur trois lignes de texte donne des coins qui
              rognent le mot ; au-delà d'une ligne, c'est un rectangle arrondi. */
           border-radius: 16px; }
}

/* ⚠ « flex: 1 0 auto » interdisait à main de RÉTRÉCIR (facteur 0) : sur
   un téléphone, la page restait à la largeur de son contenu — 1 171 px
   mesurés sur les réglages — et tout débordait. C'est ce qui rendait
   les pages de gestion impraticables sur mobile.
   « min-width: 0 » est indispensable en plus : un élément flex refuse
   par défaut de passer sous la largeur minimale de son contenu. */
main { flex: 1 1 auto; min-width: 0; }
/* Le pied reste CENTRÉ, et c'est un choix, pas un oubli. Son contenu n'est pas
   de la prose : quatre mentions courtes et un menu, séparés par des points
   médians, sur une seule ligne qui défile au doigt. Ce sont des étiquettes, donc
   la même famille que les légendes de vignette.
   Et surtout : le pied vit HORS de « main », sans largeur de colonne. Aligné à
   gauche il partirait du bord de la page — à 20 px — quand tout le reste d'une
   page d'événement commence à 121 px. C'est exactement le défaut de deux
   alignements sur une même page qu'on vient de corriger sur le titre. */
/* ⚠ LE PIED GARDE LA BANDE DES PASTILLES LIBRE — mesuré le 21/08 : arrivé en
   bas de la galerie, le pied occupait 830→900 et les deux pastilles 840→884.
   Elles se posaient donc SUR « · Tous droits réservés · Hébergé en France ».
   Rien n'était bloqué — « Mentions légales » est au milieu, à 920→1041, hors
   de leur portée — mais le texte devenait illisible, ce qui est déjà trop pour
   la seule mention légale visible de la page.
   ⚠ DU REMBOURRAGE, PAS UN OBSERVATEUR. On pouvait guetter l'entrée du pied
   dans la fenêtre et faire monter les pastilles ; c'est du JavaScript pour un
   problème de place. Un rembourrage bas suffit : arrivé au bout du document,
   c'est lui qui occupe la bande des pastilles, et le texte reste au-dessus.
   La valeur suit la géométrie des pastilles : 16 px du bord + 44 px de haut,
   plus l'air. Elles montent déjà d'un cran quand une barre flottante est là
   (« html.avec-barre » plus bas) — même idée, autre déclencheur. */
footer { flex: none; position: relative; z-index: 2; text-align: center;
         /* ⚠ « - 13px » est la DEMI-HAUTEUR de la dernière ligne, mesurée à
            26 px pour un corps de 13,5. C'est ce qui met son MILIEU sur l'axe des
            pastilles plutôt que son bas — sans quoi on alignerait deux choses par
            des bords qui ne se correspondent pas. La valeur suit « font-size »
            juste dessous : si le corps du pied change, celle-ci change avec lui. */
         padding: 18px 20px calc(var(--flottant-axe, 38px) - 13px);
         color: var(--ink-soft); font-size: 13.5px; }
/* ⚠ SUR TÉLÉPHONE, ON NE S'ALIGNE PAS — ON S'ÉCARTE, et c'est mesuré et non
   supposé. En 390 px, le pied tient sur TROIS lignes dans un seul bloc : aligner
   le milieu du bloc met sa dernière ligne 20 px sous l'axe, et les deux pastilles
   ne laissent que 262 px entre elles — il restait 8 px d'air de chaque côté,
   contre 165 sur un écran de 1440. Huit pixels, ce n'est pas un alignement, c'est
   un frôlement.
   Le pied repasse donc AU-DESSUS d'elles : 12 px de marge + 40 px de pastille +
   10 px d'air. ⚠ Et l'on ne peut pas viser la dernière ligne en CSS — il n'existe
   pas de sélecteur de ligne — d'où le choix d'écarter plutôt que de restructurer
   le balisage du pied pour un alignement qui serait de toute façon à l'étroit. */
@media (max-width: 560px) { footer { padding-bottom: 62px; } }
.retour { display: inline-flex; align-items: center; gap: 9px; min-height: var(--bt);
          padding: 11px 24px; border: 1px solid var(--sage); border-radius: 999px;
          background: var(--paper); color: var(--sage); font-size: 15.5px; text-decoration: none; }
.retour:hover { background: var(--sage); color: var(--paper); }

/* Le retour sous le titre. Volontairement plus discret que « .retour » du
   bas de page : il précède le contenu et ne doit pas se lire comme
   l'action principale. Pas de fond, un cadre fin, et la flèche seulement
   sur le premier lien — celle du second dirait « en arrière » pour un
   déplacement qui n'en est pas un. */
.rang-retour { margin: -6px 0 18px; }
.rang-retour > .piste, .rang-retour > .defile-cadre > .piste { padding: 2px 0 6px; gap: 8px; }
.lien-retour { display: inline-flex; align-items: center; gap: 7px;
               /* 44 px : la cible tactile minimale recommandée, et le
                  critère 2.5.8 de WCAG en exige 24 au strict minimum. */
               min-height: var(--bt-petit); padding: 8px 16px;
               /* « --line-champ » et non « --line » : ce lien est un
                  contrôle, et son contour doit tenir le 3:1 du critère
                  WCAG 1.4.11. « --line » est décoratif et n'est pas
                  vérifié par tools/contrastes.php — c'est la règle que
                  ce dépôt s'est donnée. */
               border: 1px solid var(--line-champ, var(--line)); border-radius: 999px;
               background: transparent; color: var(--ink-soft);
               font-size: 14px; text-decoration: none; white-space: nowrap; }
.lien-retour.premier { color: var(--sage); border-color: var(--sage); }
.lien-retour:hover { background: var(--sage); border-color: var(--sage); color: var(--paper); }
.lien-retour svg { flex: 0 0 auto; }

/* Les documents partagés par l'organisateur. */
.docs { display: grid; gap: 10px; }
.doc { display: flex; align-items: center; gap: 13px; text-decoration: none;
       border: 1px solid var(--line-champ, var(--line)); border-radius: 6px;
       padding: 12px 14px; background: var(--paper); color: var(--ink); }
/* Plus de survol sur la ligne entière : elle n'est plus cliquable. Ce sont
   « Voir » et « Télécharger » qui agissent, et une ligne qui s'allume sans rien
   faire promet un clic qui n'existe pas. */
.doc .ic { display: flex; align-items: center; justify-content: center;
           width: 36px; height: 36px; flex: 0 0 auto; border-radius: 50%;
           background: var(--cream); color: var(--sage); }
/* « min-width: 0 » et « flex: 1 » : sans les deux, un nom de document long
   refuse de se couper et pousse les deux boutons hors de la carte. */
.doc .quoi { min-width: 0; flex: 1 1 150px; }
.doc .quoi strong { display: block; font-family: var(--serif); font-weight: 400;
                    font-size: 16px; overflow-wrap: anywhere; }
.doc .quoi span { font-size: 12.5px; color: var(--ink-soft); }
.doc { flex-wrap: wrap; }
.doc-gestes { display: flex; gap: 8px; flex-wrap: wrap; flex: 0 0 auto; }
/* Plus de taille ici : ces boutons portent « petit » dans le balisage. */

/* Les deux fenêtres partagées : la carte d'un lieu, l'aperçu d'un document. */
.dlg-carte-note { margin: 10px 0 0; font-size: 12px; color: var(--ink-soft);
                  line-height: 1.5; }
/* Ce qui s'affiche à la place de la carte quand l'adresse n'a pas été
   reconnue. Une carte vide ne dit rien ; cette phrase dit quoi faire. */
.dlg-carte-vide { margin: 0; padding: 26px 20px; font-size: 14px; line-height: 1.6;
                  color: var(--ink-soft); background: var(--cream);
                  border-radius: 6px; }
/* Le cadre s'ajuste à l'image, et non l'inverse.
   Avec « width: 100% » plus un « max-height », une page A4 en portrait était
   ramenée à 520 px de haut ET laissée à 568 px de large : deux bandes crème de
   cent pixels de chaque côté. « width: auto » avec les deux bornes fait que le
   cadre épouse la page. */
/* « fit-content » : le cadre prend la largeur de l'image, pas celle de la
   fenêtre. Centrer l'image dans un cadre pleine largeur laissait cent pixels de
   crème de chaque côté d'une page A4 — un passe-partout que personne n'a
   demandé. Le cadre est donc centré, lui, dans la fenêtre. */
/* Le cadre s'ajuste à l'image, et non l'inverse : « width: 100% » plus un
   « max-height » laissait deux bandes crème de cent pixels autour d'une page A4.
   PLUSIEURS pages s'empilent et défilent dans le cadre — c'est ce qui remplace
   « ouvrir dans un onglet ». Une seule page ne défile pas, la hauteur suivant
   son contenu. */
.dlg-doc-vue { display: flex; flex-direction: column; align-items: center; gap: 10px;
               width: fit-content; max-width: 100%; margin-inline: auto;
               max-height: min(62vh, 520px); overflow-y: auto;
               border-radius: 6px; background: var(--cream);
               border: 1px solid var(--line); }
/* La MÊME borne pour toutes les pages, seule ou non : chacune tient dans la
   hauteur du cadre, et l'on passe à la suivante en faisant défiler. Sans cela
   une page unique s'affichait à 367 px de large (bornée par la hauteur) et trois
   pages à 570 (bornées par la largeur) — deux échelles pour le même document. */
.dlg-doc-vue img { display: block; width: auto; height: auto;
                   max-width: 100%; max-height: min(62vh, 520px); }
/* Les deux gestes sur UNE rangée, et de même hauteur.
   Ils étaient dans deux paragraphes distincts — l'un dans le bloc masquable du
   PDF, l'autre en dehors — donc sur deux lignes, et de deux hauteurs
   différentes puisque rien ne les accordait. Une rangée souple, une hauteur
   imposée aux deux : ils se lisent comme un choix, pas comme deux idées.
   « flex-wrap » quand même : à 320 px, « Ouvrir dans un onglet » et
   « Télécharger » côte à côte ne tiennent pas, et mieux vaut qu'ils passent
   dessous que de déborder. */
.dlg-doc-bas { display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
               margin: 14px 0 0; }
/* Taille normale, donc rien à écrire. */
/* La phrase qui dit POURQUOI l'onglet : un bouton « Ouvrir dans un onglet »
   sans explication passerait pour un renoncement. */
.dlg-doc-mot { margin: 12px 0 0; font-size: 14px; line-height: 1.55;
               color: var(--ink-soft); }

/* « Y aller » : les trois applications de navigation, sous l'adresse.
   ⚠ ELLE NE PASSE PLUS À LA LIGNE — 20/08. Trois boutons de cent dix pixels ne
   tiennent pas sur 375 px : ils s'empilaient, et c'est exactement la rangée que
   François a photographiée. Elle défile maintenant du doigt, comme toutes les
   autres rangées du site, avec son estompe aux deux bouts. « flex-wrap » est
   retiré ici, mais c'est « .defilable » (posé par rangs.js, plus loin dans cette
   feuille donc gagnant à spécificité égale) qui impose « nowrap ». */
/* ⚠ CETTE RANGÉE NE SERT PLUS QU'À LA FENÊTRE DE CARTE (lib/fenetre-carte.php)
   depuis le 22/08 : le programme, lui, emploie un menu (« .y-aller-m »). Vérifié
   avant de ne PAS supprimer ces règles — elles auraient emporté les boutons de la
   fenêtre avec elles. */
.y-aller { display: flex; align-items: center; gap: 7px; margin: 9px 0 0; }
.y-aller-l { font-size: 13px; color: var(--ink-soft); margin-inline-end: 2px; }
/* ⚠ LE LIBELLÉ REVIENT SUR TÉLÉPHONE. Il était caché en dessous de 560 px parce
   qu'il prenait à lui seul la première ligne et repoussait les trois boutons sur
   trois lignes — 128 px de haut POUR CHAQUE lieu. La rangée ne passant plus à la
   ligne, ce coût a disparu : le libellé occupe le début de la piste, se lit au
   repos, et s'en va quand on fait défiler. Sans lui, trois marques alignées sous
   une adresse ne disent pas ce qu'elles font. */
.y-aller .btn { flex: 0 0 auto; }
/* ⚠ « :not(.ic-marque) » — la teinte sauge était pensée pour NOTRE flèche de
   navigation, seule icône de ces boutons à l'époque. Depuis que les marques y
   sont, cette règle (deux classes et un type) battait celle qui donne au noir
   d'Apple sa teinte de repos : la pomme sortait verte. Les marques portent leurs
   propres couleurs — elles sont donc exclues, plutôt que de surenchérir en
   spécificité de part et d'autre. */
.y-aller .btn svg:not(.ic-marque) { color: var(--sage); }
/* ⚠ AU SURVOL, LA MARQUE À UNE COULEUR SUIT LE TEXTE. Le bouton devient vert et
   son texte blanc ; la pomme, elle, restait noire sur vert — presque invisible.
   Son tracé porte « fill:currentColor », donc il suffit de changer « color ».
   ⚠ La teinte de repos arrive dans « --ic » et non dans un « color » en ligne :
   un attribut « style » bat toute règle de feuille, et il aurait fallu un
   « !important » pour le survol. Ici les deux déclarations vivent dans cette
   feuille et la plus spécifique gagne — sans forcer.
   Waze et Google Maps ne sont pas concernées : plusieurs couleurs chacune, et
   « currentColor » les écraserait toutes en une. */
/* ⚠ UN LISERÉ AUTOUR DES MARQUES MULTICOLORES — demandé le 20/08.
   L'épingle de Google Maps porte du bleu et du vert ; sur un bouton survolé qui
   devient vert sauge, ses bords se confondent avec le fond et elle se lit comme
   une tache. La pomme, elle, a été réglée autrement (elle suit la couleur du
   texte) parce qu'elle n'a qu'une teinte.
   ⚠ « drop-shadow » ET NON « stroke ». Un contour posé sur les tracés dessinerait
   aussi les coutures INTERNES de l'épingle — elle est faite de cinq quartiers qui
   se chevauchent, et l'on verrait cinq lignes en travers. « drop-shadow » suit le
   canal alpha : il entoure la SILHOUETTE, et aussi le trou du cercle intérieur,
   qui est précisément ce que François demande. Deux passes sans décalage font un
   liseré continu là où une seule laisse des angles clairs.
   La couleur est celle du FOND DE LA SECTION — « --paper » — donc invisible au
   repos, où le bouton est déjà de cette couleur, et visible dès que le bouton
   s'assombrit. Un liseré qui ne se voit que quand il sert. */
.ic-marque:not(.ic-marque-mono) {
  filter: drop-shadow(0 0 1.4px var(--paper)) drop-shadow(0 0 1.4px var(--paper))
          drop-shadow(0 0 1.4px var(--paper));
}
.ic-marque-mono { color: var(--ic, currentColor); }
.btn:hover .ic-marque-mono { color: var(--paper); }

/* La carte des lieux.
   L'encart de consentement occupe la même place que la carte, à peu près :
   la page ne saute donc pas trop quand on l'affiche. */
.carte-zone { margin: 16px 0 0; }
/* ⚠ « --line » ET NON « --line-champ » — 22/08. Cet encart avait déjà le bon fond
   et le bon rayon, mais un trait plus sombre : « --line-champ » est la couleur des
   BORDS DE CHAMPS, plus marquée exprès pour qu'on voie où l'on tape. Ici il n'y a
   rien à taper, et l'écart se voyait — relevé au balayage des encarts de la page,
   qui a montré une bordure rgb(143,137,127) là où les autres ont rgb(216,207,193).
   Un écart d'un seul jeton suffit à faire deux familles d'encarts. */
.carte-avis { border: 1px solid var(--line); border-radius: 6px;
              background: var(--cream); padding: 11px 14px; text-align: start; }
.carte-avis p { margin: 0 0 12px; font-size: 13.5px; color: var(--ink-soft);
                line-height: 1.5; }
.carte-toile { height: 320px; border-radius: 6px; border: 1px solid var(--line);
               /* Leaflet pose les tuiles en absolu : sans « isolation », le
                  décor du thème (z-index 1) traversait la carte. */
               isolation: isolate; z-index: 0; }
.carte-toile .leaflet-container { font: inherit; background: var(--cream); }
.carte-toile .leaflet-popup-content { font-size: 13.5px; line-height: 1.5; }
.carte-toile .leaflet-popup-content strong { font-family: var(--serif); font-weight: 400;
                                             font-size: 15.5px; }
.carte-toile .leaflet-popup-content .ad { color: var(--ink-soft); font-size: 12.5px; }
.carte-toile .leaflet-control-attribution { font-size: 10.5px; }
/* Dans une fenêtre, la carte prend la hauteur disponible plutôt que 320 px
   fixes : on ouvre justement une fenêtre pour voir plus grand. */
.dlg-carte .carte-toile { height: min(62vh, 520px); }

/* Le repère : un cercle et un chiffre, dessinés — aucun PNG à héberger. */
.carte-pin { display: flex; align-items: center; justify-content: center; }

/* Les outils de la carte : + , − et « ? ».
   Dessinés, parce que le « zoomControl » de Leaflet porte sa propre allure et
   que rien de non dessiné n'a le droit d'apparaître. Posés SUR la carte, à
   droite, là où le pouce d'une main droite les trouve.
   « z-index: 500 » : au-dessus des tuiles et des repères de Leaflet, qui vont
   jusqu'à 400 dans sa propre échelle. */
.carte-outils { position: absolute; top: 10px; inset-inline-end: 10px; z-index: 500;
                display: flex; flex-direction: column; gap: 6px; }
.carte-outil { width: 38px; height: 38px; font: inherit; font-size: 19px;
               line-height: 1; cursor: pointer; border-radius: 8px;
               display: flex; align-items: center; justify-content: center;
               background: var(--paper); color: var(--ink);
               border: 1px solid var(--line-champ, var(--line));
               box-shadow: 0 2px 8px var(--ombre); }
.carte-outil:hover { border-color: var(--sage); color: var(--sage); }
.carte-outil.on { background: var(--sage); border-color: var(--sage); color: var(--paper); }
.carte-outil:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }

/* La liste des lieux, en encart flottant sur la carte.
   En BAS À GAUCHE : en haut à droite elle passerait sous les outils, et à
   droite elle masquerait l'attribution d'OpenStreetMap, qui est une obligation
   de licence. Sa hauteur est bornée et elle défile — un parcours de huit
   étapes recouvrirait toute la carte. */
.carte-liste { position: absolute; inset-inline-start: 10px; bottom: 24px; z-index: 500;
               width: min(258px, calc(100% - 66px)); max-height: 62%; overflow-y: auto;
               padding: 8px; border-radius: 8px;
               background: var(--paper); border: 1px solid var(--line-champ, var(--line));
               box-shadow: 0 6px 22px var(--ombre); }
.carte-liste .cl-t { margin: 2px 4px 6px; font-size: 11.5px; letter-spacing: .06em;
                     text-transform: uppercase; color: var(--sage); }
.carte-liste button { display: block; width: 100%; text-align: start; font: inherit;
                      padding: 7px 8px; border: 0; border-radius: 6px; cursor: pointer;
                      background: transparent; color: var(--ink); }
.carte-liste button:hover { background: var(--cream); }
.carte-liste button:focus-visible { outline: 2px solid var(--sage); outline-offset: -2px; }
.carte-liste button strong { display: block; font-family: var(--serif); font-weight: 400;
                             font-size: 14.5px; }
.carte-liste button span { display: block; font-size: 12px; color: var(--ink-soft); }
@media (max-width: 560px) {
  /* Sur un téléphone, l'encart devient un bandeau qui prend toute la largeur.
     Mesuré à 375 px : en carte flottante de 229 px il recouvrait 48 % d'une
     carte de 297 × 320, et chaque adresse passait à la ligne. Pleine largeur et
     moins haut, il en recouvre moins ET se lit mieux — la place gagnée vient
     des lignes qu'on ne replie plus. */
  .carte-liste { left: 10px; right: 10px; width: auto; max-height: 42%; }
}
/* Le champ saisissable à liste — ni « select » ni « datalist ». Il
   reprend exactement l'allure des champs de texte du site : quelqu'un ne
   doit pas avoir à deviner qu'il peut aussi écrire dedans. */
.combo { position: relative; }
.combo > input { padding-inline-end: 42px; }        /* la place de la flèche */
.combo-fleche { position: absolute; inset-inline-end: 4px; top: 50%; transform: translateY(-50%);
                display: flex; align-items: center; justify-content: center;
                width: 34px; height: 34px; border: 0; border-radius: 6px;
                background: transparent; color: var(--ink-soft); cursor: pointer; }
.combo-fleche:hover { color: var(--sage); }
.combo.sans-liste .combo-fleche { display: none; }
.combo.sans-liste > input { padding-inline-end: 14px; }
.combo-liste { position: absolute; z-index: var(--z-menu); left: 0; right: 0; top: calc(100% + 4px);
               /* La hauteur est posée par le script d'après la place réelle :
                  celle-ci n'est qu'un repli si le JavaScript n'a pas encore
                  mesuré. */
               max-height: 260px; overflow-y: auto; overscroll-behavior: contain;
               margin: 0; padding: 5px; list-style: none;
               background: var(--paper); border: 1px solid var(--line-champ, var(--line));
               border-radius: 7px; box-shadow: 0 8px 26px var(--ombre); }
/* Une question de questionnaire, côté invité. Les styles étaient posés en
   JavaScript, ligne par ligne, dans des « style.cssText » de cent
   caractères : illisibles, et hors de portée d'un thème. */
.q-vote { margin: 0 0 20px; }
.q-vote:last-child { margin-bottom: 0; }
.q-vote-t { font-family: var(--serif); font-size: 17px; margin: 0 0 4px; }
.q-vote-a { font-size: 13px; color: var(--ink-soft); margin: 0 0 8px; }
/* La réponse d'un sondage est une large barre cliquable : elle prend la hauteur
   du pas normal, comme tout ce qui se clique. */
.q-rep { display: block; width: 100%; text-align: start; font: inherit; font-size: 15px;
         min-height: var(--bt); line-height: 1.2;
         margin-bottom: 8px; padding: 12px 14px; border-radius: 5px;
         position: relative; overflow: hidden; cursor: pointer;
         border: 1px solid var(--line-champ, var(--line));
         background: var(--paper); color: var(--ink); }
/* Coché : un anneau INTÉRIEUR, pas une bordure plus épaisse. Une bordure de
   2 px au lieu de 1 fait grandir la barre de deux pixels, et la réponse choisie
   devenait la seule de la liste à ne pas tenir le pas de l'échelle. */
.q-rep.on { border-color: var(--sage); box-shadow: inset 0 0 0 1px var(--sage); }
.q-rep[disabled] { cursor: default; }
.q-rep .jauge { position: absolute; inset: 0 auto 0 0; opacity: .38;
                transition: width .3s ease; }
.q-rep .dessus { position: relative; display: flex; justify-content: space-between;
                 gap: 12px; }
.q-rep .n { color: var(--ink-soft); font-size: 13.5px; white-space: nowrap; }

/* Une note de 1 à 5 : cinq crans côte à côte. */
.q-echelle { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 7px; }
/* Le même carré que les crans de la playlist : c'est le même geste, noter. Il
   était à 52 px ici et 40 là. */
.q-echelle .cran { font: inherit; font-family: var(--serif); font-size: 17px;
                   min-height: 40px; border-radius: 6px; cursor: pointer;
                   border: 1px solid var(--line-champ, var(--line));
                   background: var(--paper); color: var(--ink); }
.q-echelle .cran:hover { border-color: var(--sage); color: var(--sage); }
.q-echelle .cran.on { background: var(--sage); border-color: var(--sage); color: var(--paper); }
.q-echelle .cran[disabled] { cursor: default; opacity: .6; }

/* Une réponse écrite, et celles des autres. */
.q-libre { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; }
.q-libre input { flex: 1 1 180px; min-width: 0; margin: 0; }
.q-libre .btn { flex: 0 0 auto; }
.q-phrases { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 7px; }
.q-phrases li { font-size: 14.5px; color: var(--ink); padding: 9px 12px;
                border-radius: 5px; background: var(--cream);
                border-inline-start: 3px solid var(--sage); }

/* SA réponse à une question ouverte, à part et reconnaissable.
   Le liseré plein reprend celui des phrases des autres — c'est la même nature
   de contenu — mais le fond de papier et le libellé disent que celle-ci est la
   sienne, et qu'elle se change. */
.q-mien { padding: 11px 13px; border-radius: 6px; background: var(--paper);
          border: 1px solid var(--line-champ, var(--line));
          border-inline-start: 3px solid var(--sage); }
.q-mien-l { margin: 0; font-size: 12px; letter-spacing: .06em; text-transform: uppercase;
            color: var(--sage); }
.q-mien-t { margin: 4px 0 0; font-family: var(--serif); font-size: 16px;
            color: var(--ink); overflow-wrap: anywhere; }
.q-mien-g { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 11px 0 0; }
/* 24 px de haut au minimum, atteints par le remplissage : un lien-bouton de
   douze pixels de texte est une cible qu'on rate (WCAG 2.5.8). */
.q-mien-x { font: inherit; font-size: var(--bt-mini-t); min-height: var(--bt-mini);
            padding: 5px 10px;
            background: none; border: 0; border-radius: 5px; cursor: pointer;
            color: var(--ink-soft); text-decoration: underline; }
.q-mien-x:hover { color: var(--ink); background: var(--cream); }
.q-mien-x:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }

/* --- La musique : les goûts, puis la playlist --------------------------- */
/* Les styles : des pastilles à bascule, pas une liste à cocher.
   Vingt-deux entrées en cases à cocher font une colonne de vingt-deux lignes
   qu'on ne lit pas ; en pastilles elles tiennent en cinq rangs et se
   parcourent d'un regard. « flex » et non « grid » : les libellés vont de
   « Pop » à « Chanson française », et des colonnes égales gaspilleraient la
   largeur de la plus longue sur toutes les autres. */
.gouts { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 0; }
.gout { display: inline-flex; align-items: center; gap: 7px; font: inherit;
        font-size: var(--bt-petit-t);
        min-height: var(--bt-petit); padding: 8px 16px; border-radius: 999px; cursor: pointer;
        background: var(--paper); color: var(--ink);
        border: 1px solid var(--line-champ, var(--line)); }
/* Le survol ne s'allume que sur un vrai bouton. Côté organisateur, les mêmes
   pastilles ne font que RAPPORTER ce que les invités ont coché : réagir au
   survol les faisait passer pour cliquables. */
button.gout:hover { border-color: var(--sage); color: var(--sage); }
.gout.on { background: var(--sage); border-color: var(--sage); color: var(--paper); }
.gout[disabled] { opacity: .6; cursor: default; }
/* Les pastilles de contenu d'un plat : dix-sept par ligne de menu, donc plus
   petites que partout ailleurs. Sans cela, une carte de dix plats faisait six
   écrans, et l'organisateur perdait de vue le plat qu'il étiquetait. */
.gouts-mini { gap: 5px; }
.gouts-mini .gout { font-size: 12.5px; min-height: 30px; padding: 4px 10px; gap: 5px; }
.gouts-mini .gout svg { flex: 0 0 auto; }
/* La ligne d'un plat cesse d'être centrée : son intitulé est au-dessus de ses
   pastilles, et « Retirer » doit rester en haut, à hauteur du nom du plat. */
.plst-l.plst-plat { align-items: flex-start; }
.plst-l.plst-plat .plst-q { gap: 6px; }
/* Côté invité, les mêmes pastilles se glissent SOUS l'intitulé du plat, sans
   couper la puce de la liste : « inline-flex » les garderait sur la ligne du
   texte et pousserait les plats longs sur trois lignes. */
.menu-cont { display: flex; margin-top: 4px; }
.menu-cont .gout { cursor: default; background: var(--cream); }
.gout:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
.gout .g-n { font-size: 12.5px; padding: 1px 7px; border-radius: 999px;
             background: var(--cream); color: var(--ink-soft); }
/* Sur une pastille COCHÉE, le badge n'a PAS de fond à lui — et ce n'est pas un
   choix d'allure. J'y avais mis « rgba(255,255,255,.28) », donc le sage
   éclairci de 28 % de blanc, avec le texte en papier : mesuré à 2,86 au pire
   sur les seize thèmes, 14 en échec. Le texte posé directement sur le sage de
   la pastille hérite du contraste que le thème garantit déjà — 4,60 au pire,
   zéro échec. Le cerne blanc redonne la forme de pastille : c'est une bordure,
   donc 3:1 suffit, et il est à 4,6. */
.gout.on .g-n { background: none; color: var(--paper);
                border: 1px solid rgba(255, 255, 255, .5); padding: 0 6px; }

/* La playlist. Une ligne par morceau : ce qu'on écoute à gauche, la note à
   droite. Sur un téléphone les crans passent sous le titre — cinq boutons de
   44 px et un titre de chanson ne tiennent pas sur 320 px de large. */
.plst { display: grid; gap: 8px; margin: 4px 0 0; }
.plst-l { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: center;
          justify-content: space-between;
          padding: 10px 12px; border-radius: 6px; background: var(--cream); }
/* « min-width: 0 » : sans lui, un titre long refuse de se couper et pousse les
   crans hors de la ligne. Le défaut le plus tenace de cette feuille. */
.plst-q { flex: 1 1 190px; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.plst-q strong { font-family: var(--serif); font-weight: 400; font-size: 16px;
                 color: var(--ink); overflow-wrap: anywhere; }
.plst-q span { font-size: 13px; color: var(--ink-soft); }
.plst-c { flex: 0 0 auto; display: grid; grid-template-columns: repeat(5, 40px); gap: 5px; }
.plst-c .cran { font: inherit; font-family: var(--serif); font-size: 16px;
                min-height: 40px; border-radius: 6px; cursor: pointer;
                background: var(--paper); color: var(--ink-soft);
                border: 1px solid var(--line-champ, var(--line)); }
.plst-c .cran:hover { border-color: var(--sage); color: var(--sage); }
/* Les crans jusqu'à la note choisie sont pleins : on lit « quatre sur cinq »
   d'un coup d'œil, là où un seul cran allumé demande de compter. */
.plst-c .cran.on { background: var(--sage); border-color: var(--sage); color: var(--paper); }
.plst-c .cran:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
@media (max-width: 430px) {
  .plst-c { grid-template-columns: repeat(5, minmax(0, 1fr)); width: 100%; }
}

/* Le menu d'un format de QR : télécharger, ou copier. Même allure que la
   liste d'un combobox — le site n'a qu'une seule façon de dérouler. */
.qr-fmt { position: relative; }
.qr-fmt-menu { position: absolute; z-index: var(--z-menu); left: 50%; transform: translateX(-50%);
               top: calc(100% + 5px); min-width: 150px; padding: 5px;
               display: flex; flex-direction: column; gap: 2px;
               background: var(--paper); border: 1px solid var(--line-champ, var(--line));
               border-radius: 7px; box-shadow: 0 8px 26px var(--ombre); }
.qr-fmt-menu button { font: inherit; font-size: 14px; text-align: start;
                      padding: 9px 12px; border: 0; border-radius: 5px;
                      background: transparent; color: var(--ink); cursor: pointer; }
.qr-fmt-menu button:hover { background: var(--cream); color: var(--sage); }

/* Le séparateur entre les pays de tête et la liste alphabétique. Un trait,
   pas un intitulé : « Autour de vous » puis « Tous les pays » ajouterait
   deux lignes de texte dans une liste qui n'en montre que cinq.

   Écrit « .combo-liste li.combo-sep » et non « .combo-sep » : la règle
   « .combo-liste li » compte une classe ET un type, donc l'emporte, et le
   séparateur héritait de son padding de 9 px et de ses coins arrondis —
   d'où le gros pavé beige arrondi au lieu d'un trait.

   Un « border-top » plutôt qu'une hauteur : une bordure fait exactement un
   pixel quel que soit le zoom, là où un bloc de 1 px de haut avec un fond
   se voit arrondir et grossir. Retrait de douze pixels de chaque côté, pour
   que le trait ne touche pas les bords — c'est ce qui le rend discret. */
.combo-liste li.combo-sep {
  height: 0; padding: 0; margin: 6px 12px; background: none;
  border: 0; border-top: 1px solid var(--line); border-radius: 0;
  pointer-events: none;
}
.combo-liste li.combo-sep:hover { background: none; }

/* Dépliée vers le haut quand il y a plus de place au-dessus. */
.combo-liste.vers-haut { top: auto; bottom: calc(100% + 4px);
                         box-shadow: 0 -8px 26px var(--ombre); }
.combo-liste li { padding: 9px 12px; border-radius: 5px; font-size: 15px;
                  color: var(--ink); cursor: pointer; }
.combo-liste li:hover, .combo-liste li.sur { background: var(--cream); color: var(--sage); }
.combo-liste li.combo-rien { color: var(--ink-soft); font-size: 13.5px; cursor: default;
                             font-style: italic; }
.combo-liste li.combo-rien:hover { background: transparent; color: var(--ink-soft); }

/* « Utiliser ma position ». Volontairement modeste : c'est une commodité,
   pas l'action attendue — le pays se choisit très bien à la main. */
/* ⚠ 16 px AU-DESSUS, PAS 7 — 22/08, demandé : « l'espacement sous "Utiliser ma
   position" devrait être le même au-dessus ». Mesuré avant de choisir : 7 px
   dessus, 16 px dessous — le 16 vient du « margin-top » du label qui suit, qui
   est l'écart standard entre deux champs de ce formulaire. C'est donc lui qui
   fait loi, et le bouton cesse de paraître collé au bloc du dessus. */
.pays-ici-ligne { display: flex; flex-wrap: wrap; align-items: center;
                  gap: 8px 10px; margin: 16px 0 0; }
.pays-ici { display: inline-flex; align-items: center; gap: 6px;
            font: inherit; font-size: var(--bt-petit-t); min-height: var(--bt-petit);
            padding: 8px 16px; border-radius: 999px; cursor: pointer;
            border: 1px solid var(--line-champ, var(--line));
            background: transparent; color: var(--ink-soft); }
.pays-ici:hover { border-color: var(--sage); color: var(--sage); }
.pays-ici[disabled] { opacity: .55; cursor: default; }
.pays-ici-mot { font-size: 12.5px; color: var(--ink-soft); }

/* Le mot d'explication sous le champ « ville » : code postal inconnu, ou
   pays que l'API officielle ne couvre pas. Les propositions, elles, sont
   dans la liste du champ — une rangée de boutons en plus ne faisait que
   répéter. */
.communes-mot { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-soft); }

/* Ce que devient le service, montré après le mot d'un invité. Sobre :
   c'est un remerciement qui se termine par une porte, pas une réclame. */
.apres-mot { text-align: start; }
.apres-mot p { margin: 0 0 15px; font-size: 14.5px; color: var(--ink-soft);
               line-height: 1.6; }

.carte-pin span { display: flex; align-items: center; justify-content: center;
                  width: 26px; height: 26px; border-radius: 50%;
                  background: var(--sage); color: var(--paper);
                  border: 2px solid var(--paper); box-shadow: 0 1px 4px var(--ombre-forte);
                  font-size: 13px; font-weight: 600; }

@media (max-width: 560px) {
  .hero { padding-top: 70px; }
  .wrap { padding: 0 14px 44px; }
}

/* ==================================================================
   Composants de formulaire
   Les contrôles natifs des navigateurs ignorent totalement le thème :
   une case à cocher reste bleu système sur un thème terracotta. On les
   redessine — en gardant le contrôle natif dessous, pour ne rien perdre
   du clavier ni des lecteurs d'écran.
   ================================================================== */

/* --- Les cartes de chiffres ------------------------------------------
 * Une rangée qui défile, des cartes de taille fixe. Employée par
 * l'administration (les totaux de la plateforme) et par les réglages (les
 * chiffres d'UN événement).
 * ⚠ ÉCRITE ICI ET NON DANS LES DEUX PAGES. Elle vivait dans le <style> en ligne
 * de administration.php ; la recopier dans les réglages aurait fait deux
 * déclarations à tenir d'accord, et l'une des deux aurait fini par mentir.
 * ⚠ La rangée est horizontale dès le premier peint grâce à « .rang > .piste »
 * plus haut dans cette feuille — c'est ce qui a corrigé le saut vertical des
 * onze cartes au chargement.
 */
.chiffres { gap: 12px; margin-bottom: 6px; }
.chiffre { flex: 0 0 auto; width: 132px; height: 132px;
           display: flex; flex-direction: column;
           align-items: center; justify-content: center; gap: 4px;
           background: var(--paper); border: 1px solid var(--line); border-radius: 6px;
           padding: 12px; text-align: center; }
.chiffre b { display: block; font-family: var(--serif); font-size: 27px; font-weight: 400; }
.chiffre span { font-size: 12.5px; color: var(--ink-soft); }

/* --- Une liste longue : cinq items visibles, le reste défile -------------
 *
 * ⚠ DEMANDÉ LE 31/08 : « les longues listes d'items doivent être contenues dans des
 * conteneurs scrollables avec 5 visibles pour ne pas alourdir la longueur de la
 * page ». Les réglages faisaient 7 400 px ; une liste de vingt plats en ajoutait
 * mille à elle seule, et l'onglet suivant partait hors de portée.
 *
 * ⚠ UNE LISTE COURTE NE DÉFILE PAS : c'est un « max-height », donc quatre items
 * s'affichent entiers et rien ne bouge. La boîte ne coûte que là où il y a trop.
 *
 * ⚠ AUCUNE BARRE DE DÉFILEMENT, ICI NON PLUS — tranché le 31/08 : « la barre de
 * défilement ne doit jamais être visible, dans aucune boîte ». J'en avais rallumé
 * une (« scrollbar-width: thin ») en raisonnant qu'il fallait bien dire que la
 * liste continue, et que la règle du 20/08 ne visait que les rangées de boutons.
 * La règle est générale ; c'est à moi de trouver l'indice autrement.
 * ⚠ ET L'INDICE EST UN MOT, PAS UNE OMBRE. Une estompe en masque « gommait à
 * moitié le bouton du bord » — c'est écrit dans cette feuille — et un « ::after »
 * dans un conteneur qui défile s'en va avec le contenu. assets/pile5.js écrit donc
 * une ligne sous la boîte : « 5 sur 41 — la liste défile ». Elle dit ce que la
 * barre disait, plus le nombre total, et elle se lit au lieu de se deviner.
 *
 * ⚠ LA HAUTEUR VIENT D'UN JETON, pas d'un nombre écrit ici : les items ne font pas
 * la même hauteur d'une liste à l'autre — une ligne de plat porte des pastilles
 * d'allergènes, une ligne de lieu une icône et deux lignes de texte. Chaque liste
 * peut donc régler « --pile-item » sans toucher à cette règle.
 */
.pile5 {
  /* ⚠ CE PLAFOND N'EST QU'UN REPLI : la hauteur exacte de cinq items est POSÉE PAR
     MESURE (assets/pile5.js), parce qu'un item ne fait pas la même hauteur d'une
     liste à l'autre. Mesuré le 31/08 dans les réglages : 46 px pour une personne
     fêtée, 66 pour un lieu, 86 pour une photo avec sa vignette. Un nombre écrit ici
     donnait donc 3,4 photos là où l'on en demandait 5.
     ⚠ ET LE REPLI EST GÉNÉREUX, PAS SERRÉ (60 % de l'écran) : sans script, une
     liste un peu trop haute vaut mieux qu'une liste coupée à trois items. Au
     chargement, le script la RESSERRE — un mouvement vers le bas se lit comme un
     ajustement, un mouvement vers le haut comme un défaut. */
  max-height: 60vh;
  overflow-y: auto; overscroll-behavior: contain;
  /* De l'air à droite pour que la barre ne touche pas le texte. */
  padding-inline-end: 6px;
}
/* La ligne qui dit que la liste continue — posée par assets/pile5.js, retirée par
   lui dès que la liste tient. Même facture que les autres précisions du site :
   petite, en encre douce, sans encadré. */
.pile5-note { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-soft); }

/* --- Cases à cocher et boutons radio --------------------------------- */
.champ-choix {
  display: flex; align-items: flex-start; gap: 11px;
  cursor: pointer; margin: 12px 0; font-size: 15.5px; color: var(--ink);
  line-height: 1.45;
}
.champ-choix input[type=checkbox],
.champ-choix input[type=radio] {
  position: absolute; opacity: 0; width: 0; height: 0;
}
.champ-choix .marque {
  flex: 0 0 auto; width: 22px; height: 22px; margin-top: 1px;
  border: 1.5px solid var(--line-champ, var(--line)); background: var(--paper);
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color .15s ease, background .15s ease, transform .1s ease;
}
.champ-choix input[type=checkbox] + .marque { border-radius: 5px; }
.champ-choix input[type=radio] + .marque    { border-radius: 50%; }

.champ-choix .marque::after {
  content: ""; opacity: 0; transform: scale(.4);
  transition: opacity .14s ease, transform .14s cubic-bezier(.2,.9,.3,1.3);
}
.champ-choix input[type=checkbox] + .marque::after {
  width: 11px; height: 6px; margin-top: -3px;
  border-inline-start: 2.5px solid var(--paper); border-bottom: 2.5px solid var(--paper);
  transform: rotate(-45deg) scale(.4);
}
.champ-choix input[type=radio] + .marque::after {
  width: 9px; height: 9px; border-radius: 50%; background: var(--paper);
}
.champ-choix input:checked + .marque { background: var(--sage); border-color: var(--sage); }
.champ-choix input:checked + .marque::after { opacity: 1; }
.champ-choix input[type=checkbox]:checked + .marque::after { transform: rotate(-45deg) scale(1); }
.champ-choix input[type=radio]:checked + .marque::after { transform: scale(1); }
.champ-choix input:focus-visible + .marque {
  outline: 2px solid var(--sage); outline-offset: 3px;
}
.champ-choix:hover .marque { border-color: var(--sage); }
.champ-choix input:disabled + .marque { opacity: .45; }

/* --- Listes déroulantes ---------------------------------------------- */
select {
  appearance: none; -webkit-appearance: none;
  padding-inline-end: 42px; cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--sage) 50%),
    linear-gradient(135deg, var(--sage) 50%, transparent 50%);
  background-position: calc(100% - 21px) 55%, calc(100% - 15px) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
select:hover { border-color: var(--sage); }

/* --- Interrupteurs ---------------------------------------------------- */
.bascule { display: flex; align-items: center; gap: 12px; cursor: pointer; margin: 12px 0; }
.bascule input { position: absolute; opacity: 0; width: 0; height: 0; }
.bascule .piste {
  flex: 0 0 auto; width: 46px; height: 26px; border-radius: 999px;
  background: var(--line); position: relative; transition: background .2s ease;
}
.bascule .piste::after {
  content: ""; position: absolute; top: 3px; inset-inline-start: 3px;
  width: 20px; height: 20px; border-radius: 50%; background: var(--paper);
  box-shadow: 0 1px 3px var(--ombre-forte); transition: transform .2s cubic-bezier(.3,.8,.3,1);
}
.bascule input:checked + .piste { background: var(--sage); }
.bascule input:checked + .piste::after { transform: translateX(20px); }
.bascule input:focus-visible + .piste { outline: 2px solid var(--sage); outline-offset: 3px; }

/* --- Calendrier ------------------------------------------------------- */
/* Certaines collections dépassent volontairement de leur case (le ruban
   adhésif, la punaise). Dans l'aperçu, elles sortaient du cadre : on
   réserve la marge nécessaire et on coupe ce qui dépasse encore. */
/* L'aperçu collant se cale sous la barre fixe : à 22 px du haut de la
   fenêtre, il passait dessous et se faisait couper. */
.apercu { top: calc(var(--barre-h, 54px) + 16px) !important; }
.apercu .cadre { overflow: hidden; }
/* La marge de l'aperçu est la MÊME pour les huit styles.
   Elle était réservée style par style — plus haute pour le ruban et la
   punaise, plus basse pour la bulle : changer de style faisait alors
   sauter tout le bloc vers le bas. La marge la plus généreuse est
   appliquée à tous : plus rien ne bouge d'un choix à l'autre. */
.apercu .pm-mur { padding: 11% 7% 9%; gap: 6%; }
.apercu .pm-note { font-size: 90%; }

.cal-champ { position: relative; }
.cal-champ .cal-ouvrir {
  position: absolute; inset-inline-end: 6px; top: 50%; transform: translateY(-50%);
  /* 40 px : c'était la seule cible du site sous le seuil confortable au
     doigt. Le champ lui-même reste cliquable, mais viser une icône de
     34 px à côté d'un champ, sur un téléphone, se rate. */
  width: 40px; height: 40px; border: 0; background: transparent; cursor: pointer;
  color: var(--sage); display: flex; align-items: center; justify-content: center;
  border-radius: 6px;
}
.cal-champ .cal-ouvrir:hover { background: color-mix(in srgb, var(--sage) 12%, transparent); }

.cal {
  position: absolute; z-index: var(--z-menu); top: calc(100% + 6px); inset-inline-start: 0;
  width: min(320px, 92vw); padding: 14px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 14px 40px color-mix(in srgb, var(--ink) 22%, transparent);
  display: none;
}
.cal.ouvert { display: block; }
.cal-tete { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.cal-tete button {
  width: 32px; height: 32px; border: 1px solid var(--line); border-radius: 8px;
  background: transparent; color: var(--sage); cursor: pointer; font: inherit; font-size: 15px;
}
.cal-tete button:hover { background: var(--sage); color: var(--paper); border-color: var(--sage); }
.cal-mois { font-family: var(--serif); font-size: 16.5px; color: var(--ink); }
.cal-grille { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.cal-grille .jour-nom {
  text-align: center; font-size: 11.5px; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .06em; padding-bottom: 5px;
}
.cal-grille button {
  aspect-ratio: 1; border: 0; border-radius: 7px; background: transparent;
  color: var(--ink); font: inherit; font-size: 14px; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.cal-grille button:hover:not(:disabled) { background: color-mix(in srgb, var(--sage) 16%, transparent); }
.cal-grille button.hors { color: var(--ink-soft); opacity: .38; }
.cal-grille button.aujourdhui { box-shadow: inset 0 0 0 1.5px var(--sage-pale); }
.cal-grille button.choisi { background: var(--sage); color: var(--paper); }
.cal-grille button:focus-visible { outline: 2px solid var(--sage); outline-offset: 1px; }
.cal-pied { display: flex; justify-content: space-between; margin-top: 10px; gap: 8px; }
.cal-pied button {
  flex: 1; border: 1px solid var(--line); border-radius: 999px; background: transparent;
  color: var(--ink-soft); font: inherit; font-size: 13px; padding: 7px; cursor: pointer;
}
.cal-pied button:hover { border-color: var(--sage); color: var(--sage); }

/* ==================================================================
   ÉCRITURE DE DROITE À GAUCHE — ce qui a été converti, et ce qui ne l'a pas été
   ==================================================================
 * L'arabe fait partie des huit langues servies, et « dir="rtl" » retourne la mise
 * en page entière. Relevé le 22/08 : 215 déclarations de ce fichier nommaient un
 * côté PHYSIQUE.
 *
 * ⚠ CONVERTIES (92) : tout ce qui doit suivre le sens de LECTURE — les alignements
 * de texte, les marges et rembourrages latéraux, les filets latéraux, les croix de
 * fermeture, les chevrons de champ, les ancrages de menu, les pastilles flottantes,
 * les puces de liste. Elles portent maintenant des propriétés logiques
 * (« inset-inline-start », « margin-inline-end », « text-align: start »…).
 *
 * ⚠ NON CONVERTIES, ET C'EST UN CHOIX MESURÉ :
 *   — « left: 0; right: 0 » : pleine largeur, aucun sens de lecture en jeu ;
 *   — « left: 50% » avec « translateX(-50%) » : un centrage n'a pas de côté ;
 *   — les ornements de coin (« .d-coin »), qui portent déjà leurs propres
 *     « transform: scaleX(-1) » : les retourner demanderait de retourner aussi les
 *     transformations, et un feuillage décoratif est symétrique par nature ;
 *   — les 84 « border-radius » à quatre valeurs : un arrondi non miroir est une
 *     subtilité, pas un défaut, et réécrire quatre-vingt-quatre déclarations à
 *     l'aveugle ferait plus de mal que le défaut qu'on corrige.
 *
 * ⚠ CE QUI RESTE À FAIRE EST NOMMÉ, PAS ENJAMBÉ : les arrondis non miroir, et les
 * quelques ornements de coin. Aucun des deux ne rend un texte illisible ni ne
 * casse une mise en page — vérifié en mesurant les débordements page par page.
 */

/* ==================================================================
   Accessibilité
   ================================================================== */
/* ⚠ CACHÉ PAR DÉCOUPE, PLUS PAR « left: -9999px » — 22/08. Mesuré en arabe : la
   page annonçait 9 999 px de défilement horizontal. En écriture de gauche à
   droite, un élément posé à gauche de l'origine ne compte pas dans la zone
   défilante ; en écriture de droite à gauche, l'axe s'inverse et le même élément
   se retrouve DANS cette zone. Le lien d'évitement — le tout premier de la page,
   celui qui existe pour les lecteurs d'écran — rendait donc chaque page de tout
   le site défilable sur dix mille pixels dès qu'on lisait en arabe.
   ⚠ La découpe (« clip-path: inset(50%) ») n'a pas ce défaut : l'élément reste à
   sa place, de taille nulle, et aucun axe ne s'en trouve étendu. C'est le motif
   recommandé pour ce qui doit rester lisible par un lecteur d'écran sans occuper
   d'espace — et il ne dépend d'aucune direction. */
.pm-evitement {
  position: absolute; inset-inline-start: 0; top: 0; z-index: 100;
  width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
  background: var(--sage); color: var(--paper); padding: 12px 20px;
  border-start-start-radius: 0; border-start-end-radius: 0;
  border-end-end-radius: 8px; border-end-start-radius: 0;
  text-decoration: none; font-size: 15px;
}
.pm-evitement:focus {
  width: auto; height: auto; overflow: visible; clip-path: none;
}

/* ⚠ CES DEUX PASTILLES NE SE POSITIONNENT PLUS ELLES-MÊMES — 20/08.
   Elles vivent dans « #pm-reglages », un seul bouton flottant qui les
   déplie vers le haut (voir assets/accessibilite.js et le bloc
   « #pm-reglages » plus bas). On garde ici leur APPARENCE — le rond, le
   trait, l'ombre — et l'on retire la POSITION, qui appartient désormais au
   groupe. Une pastille qui se place elle-même DANS un groupe qui se place
   aussi, c'est deux vérités pour une même chose. */
#pm-a11y-bouton, #pm-cookies {
  width: var(--flottant-taille); height: var(--flottant-taille);
  border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--paper); color: var(--sage);
  box-shadow: 0 2px 10px var(--ombre); transition: transform .15s ease;
}
#pm-a11y-bouton:hover, #pm-cookies:hover { transform: scale(1.08); }

/* Les trois pastilles flottantes forment une colonne à gauche, et le
   retour en haut leur fait face à droite.
   Une tentative de les mettre côte à côte sur petit écran a produit
   l'inverse : la feuille de pied-cookies.php, insérée APRÈS celle-ci,
   redonnait à #pm-cookies sa position d'origine et les deux se
   superposaient. Elles restent donc empilées partout, un cran plus
   petites sur téléphone. */
/* ⚠ TOUT LE BLOC « @media (max-width: 560px) » QUI SUIVAIT ÉTAIT MORT — mesuré le
   22/08 : il posait 40 px de côté et « bottom: 12px » aux pastilles, mais
   « #pm-haut » et « #pm-reglages » sont redéclarés PLUS BAS dans ce fichier avec
   44 px et 16 px. À spécificité égale, la dernière déclaration gagne : sur un
   téléphone, les pastilles faisaient donc 44 px à 16 px du bas, c'est-à-dire
   exactement comme sur un écran d'ordinateur, et personne ne l'avait vu.
   ⚠ Corrigé PAR DES JETONS et non par un « !important » : la taille et l'axe sont
   déclarés une fois sur « :root », donc la media query n'a plus rien à écraser —
   elle change une valeur, pas une règle. C'est la seule façon de ne pas
   redécouvrir le même duel de spécificité dans six mois.
   ⚠ Et le « bottom » se DÉDUIT de l'axe : « axe − taille/2 » place le centre de la
   pastille sur l'axe, quelle que soit sa taille. Un « bottom » écrit à la main
   était juste dans un format sur deux.
   ⚠ LES JETONS SONT ÉCRITS DANS LES DÉCLARATIONS INDIVIDUELLES, pas dans un bloc
   groupé posé ici. J'avais commencé par le bloc groupé — et je suis retombé dans
   le défaut que je corrigeais : il précède « #pm-haut { width: 44px } » de vingt
   lignes, donc il perdait. Un correctif de spécificité placé au mauvais endroit est
   un correctif qui ne s'applique pas. */
@media (max-width: 560px) {
  /* Les « !important » qui étaient ici visaient la feuille de
     pied-cookies.php, insérée APRÈS celle-ci et qui redonnait à #pm-cookies
     sa position d'origine. Le duel n'a plus lieu : plus personne ne
     positionne cette pastille, ni ici ni là-bas. */
  /* ⚠ Seule la position VERTICALE change ici. Les 12 px horizontaux qui y
     étaient recréaient le désalignement sur téléphone : le bord de la page vaut
     20 px à cette largeur, et les pastilles doivent tomber dessus comme
     ailleurs. */
  /* « .sommaire » n'est pas une pastille ronde : son bouton a la hauteur du pas
     « petit ». Il s'aligne donc sur l'axe avec SA propre demi-hauteur, plus bas. */
  #pm-a11y-panneau { bottom: 108px; }
}

/* --- « Début de page », dans le sommaire ------------------------------- */
/* ⚠ LA PASTILLE DE RETOUR EN HAUT A ÉTÉ RETIRÉE — 31/08, demandé. Toute sa
   déclaration vivait ici (« #pm-haut »), avec son apparition après un écran et
   demi de défilement. Deux pastilles au même coin pour deux gestes voisins, dont
   l'une ne savait faire qu'une chose : le geste vit maintenant en tête du menu
   « Aller à… », qui sait aussi aller ailleurs.
   ⚠ ET LE DÉCALAGE DE 54 px DU SOMMAIRE PART AVEC ELLE (voir plus bas) : il
   n'existait que pour ne pas la recouvrir. */
/* ⚠ LA DÉCLARATION DE « .som-haut » A ÉTÉ FONDUE DANS CELLE DES LIENS DU MENU,
   plus bas dans cette feuille. Elle vivait ici, jumelle, avec « font: inherit » —
   et « inherit » sur un bouton hérite du CONTENEUR, pas des voisins qu'on voulait
   imiter : 16 px contre 14, noir pur contre « --ink-soft ». Un bouton au milieu
   d'ancres doit être dans LEUR déclaration, sinon il leur ressemble à peu près. */

/* Les actions d'une rangée de retour, poussées à droite (voir lib/retour.php).
   ⚠ « margin-left: auto » sur CET élément, pas de « space-between » sur la piste :
   la piste défile, et répartir l'espace dans un conteneur qui déborde rend son
   début inatteignable — trois lots payés pour cette leçon. */
.retour-actions { display: inline-flex; align-items: center; gap: 9px;
                  margin-inline-start: auto; flex: 0 0 auto; }
.retour-actions .btn { min-height: var(--bt-petit); padding: 8px 16px;
                       font-size: var(--bt-petit-t); }

/* Les onglets, quand ils vivent SUR la rangée de tête (les réglages).
   ⚠ DEUX CLASSES, ET CE N'EST PAS DE LA COQUETTERIE. « .onglets » est déclaré
   PLUS BAS dans ce fichier (ligne ~1860) avec « margin-bottom: 24px » : à
   spécificité égale, la dernière déclaration gagne, et une règle écrite ici
   n'aurait rien fait — 24 px de vide sous la rangée de tête, sans explication.
   C'est la même raison qui a fait écrire « .avis.avis-alcool ». */
.onglets.tete-onglets { margin: 0; flex: 0 0 auto; align-items: center; }
.rang-retour > .piste, .rang-retour > .defile-cadre > .piste { align-items: center; }
/* La rangée porte maintenant des pastilles de 40 px à côté d'un lien discret :
   sans un peu d'air, le lien touche le premier onglet. */
.rang-retour.avec-actions .lien-retour.premier { margin-inline-end: 6px; }

@media (max-width: 720px) {
  /* ⚠ SOUS 720 px, LA RANGÉE DE TÊTE PASSE À LA LIGNE AU LIEU DE DÉFILER — et
     c'est une adaptation propre au téléphone, demandée. Mesuré à 375 px : les trois
     groupes font 784 px dans une piste de 347. La rangée défile bien (rangs.js
     l'équipe), donc rien n'est PERDU — mais « Ouvrir le mur » et « Écran de
     projection » se trouvaient à plus de deux largeurs d'écran vers la droite,
     c'est-à-dire invisibles pour qui ne devine pas qu'il faut tirer la rangée.
     Un bouton qu'on ne voit pas est un bouton qui n'existe pas.
     ⚠ On coupe donc le défilement ET on autorise le retour à la ligne : les deux
     ensemble, sinon un conteneur qui défile ignore « flex-wrap ».
     ⚠ « margin-left: auto » est annulé : dans une rangée qui passe à la ligne, il
     pousserait les actions seules sur leur ligne, alignées à droite, loin du
     libellé qu'elles complètent. */
  .rang-retour > .piste,
  .rang-retour > .defile-cadre > .piste {
    flex-wrap: wrap; overflow: visible; row-gap: 8px;
  }
  .retour-actions { margin-inline-start: 0; }
  /* Les onglets prennent leur propre ligne : ce sont eux qu'on vient chercher, et
     les mêler aux deux sorties ferait une bouillie de six boutons. */
  .onglets.tete-onglets { flex: 1 0 100%; }
}
@media (max-width: 520px) {
  /* Plus bas encore, on gagne de la place sur les libellés plutôt que sur le
     nombre de boutons : un bouton retiré est un bouton qu'on ne trouve plus. */
  .retour-actions .btn { padding: 7px 12px; }
  /* Idem : « .onglets .ong » est déclaré plus bas, et une media query n'ajoute
     aucune spécificité. */
  .onglets.tete-onglets .ong { padding: 7px 12px; }
}

/* --- Rangées défilantes ------------------------------------------------ */
/* --- Une rangée de boutons qui défile ---------------------------------
 *
 * Sur un téléphone, un pavé de six boutons empilés repousse le contenu
 * hors de l'écran. Ils tiennent donc sur une ligne qui se pousse au
 * doigt.
 *
 * Le vrai risque d'une ligne qui défile est qu'on ne devine pas qu'elle
 * défile : une barre coupée net a l'air complète. On s'appuie sur le
 * bouton suivant qu'on aperçoit derrière le bord — et l'accrochage
 * garantit qu'à l'arrêt ce bord tombe toujours entre deux boutons,
 * jamais au milieu de l'un d'eux. */
.defilable {
  display: flex; flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  /* Un bouton n'est JAMAIS coupé au repos.
   *
   * L'accrochage fait que la rangée s'arrête toujours sur le bord d'un
   * bouton : quand on lâche, celui du début est entier, et en bout de
   * course celui de la fin l'est aussi. C'est la garantie demandée, et
   * elle vaut à toutes les largeurs d'écran sans rien calculer.
   *
   * « mandatory » et non « proximity » : avec « proximity » le
   * navigateur décide s'il accroche ou non, et il ne le fait pas
   * toujours — la garantie n'en serait plus une. Les boutons font tous
   * moins d'un écran de large, donc rien ne peut devenir inatteignable. */
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 0;
}
.defilable::-webkit-scrollbar { display: none; }
.defilable > * { flex: 0 0 auto; scroll-snap-align: start; }
/* Le dernier élément accroche par sa fin, sinon la rangée ne peut pas
   aller jusqu'au bout quand il reste moins d'une largeur de bouton. */
.defilable > *:last-child { scroll-snap-align: end; }

/* AUCUNE estompe.
 *
 * Elle était posée en « mask-image » sur la rangée : le bouton du bord
 * était gommé à moitié, et l'on ne savait plus si c'était un bouton, un
 * défaut d'affichage ou une commande désactivée. Une deuxième version la
 * peignait par-dessus, sans toucher au contenu — mais un voile sur un
 * bouton entièrement visible reste un bouton qu'on voit mal.
 *
 * Elle est donc retirée. Ce qui dit qu'une rangée continue, c'est le
 * bouton suivant qu'on aperçoit derrière le bord — et l'accrochage
 * garantit qu'on ne voit jamais un demi-bouton à l'arrêt. */
.rang { position: relative; }

/* Les boutons cessent de s'étirer : une rangée qui défile n'a pas à
   remplir la largeur. */
.ligne.defilable .btn { flex: 0 0 auto; }
/* ⚠ DEUX CHEMINS, et pas un sélecteur de descendance : rangs.js insère un
   « .defile-cadre » entre la rangée et sa piste pour porter l'estompe. Écrire
   « .rang .piste » attraperait aussi le .piste d'un interrupteur (.bascule),
   qui n'a rien à voir malgré le même nom. */
.rang > .piste, .rang > .defile-cadre > .piste { padding: 2px 0 8px; gap: 8px; }
/* ⚠ LA RANGÉE EST HORIZONTALE DÈS LE PREMIER RENDU, sans attendre rangs.js.
   « display:flex » ne venait que de « .defilable », classe posée par le script :
   entre le premier affichage et l'exécution du script, les cartes de
   l'administration s'empilaient VERTICALEMENT, puis sautaient à l'horizontale.
   Onze cartes de 132 px, c'est un saut de mille cinq cents pixels sous les yeux
   — relevé par François le 31/08, et c'est le genre de secousse qui fait
   paraître un site cassé même quand tout finit bien.
   ⚠ L'ÉTAT FINAL NE CHANGE PAS : « .defilable » repose les mêmes propriétés,
   plus le défilement et l'accrochage. On ne fait qu'avancer la mise en page au
   premier peint — ce que la CSS sait faire seule, elle doit le faire seule.
   ⚠ Les deux chemins sont écrits, comme au-dessus : rangs.js insère un
   « .defile-cadre » entre la rangée et sa piste. */
.rang > .piste, .rang > .defile-cadre > .piste { display: flex; flex-wrap: nowrap;
                                                  overflow-x: auto; }

/* ⚠ « min-height » BATTAIT « height » SUR LE BOUTON DES LANGUES. La règle
   générale « .pop-menu > button » pose « min-height: var(--bt-petit) », soit
   38 px ; la règle du bandeau posait « height: 34px » (30 sous 560 px) sans
   toucher au plancher. Mesuré au navigateur : bouton de langue 30 × 38, bouton
   clair/sombre 30 × 30 — une ellipse à côté d'un rond, et le « border-radius:50% »
   ne faisait qu'en souligner l'ovale. Demandé le 31/08 : mêmes dimensions,
   même diamètre.
   ⚠ Trouvé en MESURANT (getBoundingClientRect), pas en regardant : à l'œil les
   deux boutons se ressemblent, et huit pixels de haut ne se voient qu'une fois
   qu'on les cherche. */
.barre-affichage .pop-menu > button { min-height: 0; min-width: 0; }
/* ⚠ LA RANGÉE D'ONGLETS AJOUTAIT SA PROPRE MARGE. Mesuré sur les réglages :
   48 px entre les onglets et « L'essentiel », contre 24 partout ailleurs — la
   grille donnait ses 24 px et la rangée en remettait 24. Une rangée est une
   commande, pas une section : c'est la grille qui espace. */
.atelier > .rang { margin-block: 0; }
.atelier > .rang > .piste { padding-bottom: 0; }


/* Voile derrière le panneau : les autres fenêtres du site en ont un
   (elles utilisent <dialog>), celle-ci n'en avait pas — deux
   comportements différents pour deux fenêtres, c'est déroutant. */
#pm-a11y-voile {
  position: fixed; inset: 0; z-index: var(--z-voile);
  background: color-mix(in srgb, var(--ink) 45%, transparent);
}
#pm-a11y-panneau {
  /* ⚠ Le panneau s'ouvre AU-DESSUS de la pastille de réglages : il doit
     partager son bord, sinon il se décale d'elle dès que la fenêtre s'élargit.
     Il restait à 16 px alors que la pastille suit maintenant le bord de la
     page. */
  position: fixed; inset-inline-start: var(--page-bord); bottom: 124px; z-index: var(--z-modale);
  width: min(340px, calc(100vw - 2 * var(--page-bord))); padding: 20px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 16px 44px color-mix(in srgb, var(--ink) 26%, transparent);
}
#pm-a11y-panneau h2 { font-family: var(--serif); font-weight: 400; font-size: 19px; margin: 0 0 14px; }
#pm-a11y-panneau .bascule { margin: 14px 0; align-items: flex-start; font-size: 14.5px; }
#pm-a11y-panneau .bascule .d { color: var(--ink-soft); font-size: 12.5px; }
#pm-a11y-panneau .pm-a11y-raz {
  width: 100%; margin-top: 8px; padding: 9px; border-radius: 999px;
  border: 1px solid var(--line); background: transparent; color: var(--ink-soft);
  font: inherit; font-size: 13.5px; cursor: pointer;
}
#pm-a11y-panneau .pm-a11y-fermer {
  position: absolute; top: 10px; inset-inline-end: 12px; border: 0; background: none;
  color: var(--ink-soft); font-size: 17px; cursor: pointer; line-height: 1;
}
@media print { #pm-a11y-bouton, #pm-a11y-panneau, .pm-evitement { display: none !important; } }

/* --- les réglages eux-mêmes --- */
/* On agrandit le CORPS et non la racine : body fixe 16px en absolu, ce qui
   annulait tout agrandissement posé sur html. */
html.a11y-texte body { font-size: 19.2px; }
html.a11y-texte .pm-note .pm-qui { font-size: clamp(8px, 1.5vw, 15px); }
html.a11y-texte .pm-note .pm-txt { font-size: clamp(7px, 1.3vw, 13px); }

/* Le thème pose --ink-soft sur :root dans une balise <style> insérée APRÈS
   cette feuille. À spécificité égale, c'est lui qui gagnait : il faut donc
   une spécificité supérieure (html.classe) pour reprendre la main. */
html.a11y-contraste { --ink-soft: var(--ink); --sage-pale: var(--sage); }
html.a11y-contraste .lede,
html.a11y-contraste .compteur,
html.a11y-contraste .pm-note .pm-txt { opacity: 1; }
html.a11y-contraste .d-coin, html.a11y-contraste .d-plein,
html.a11y-contraste .d-bas, html.a11y-contraste .d-lights { opacity: .25; }
html.a11y-lisible, html.a11y-lisible .pm-note,
html.a11y-lisible .pm-qui, html.a11y-lisible .pm-txt, html.a11y-lisible body {
  font-family: Verdana, "DejaVu Sans", Tahoma, sans-serif !important;
  font-style: normal !important;
}
html.a11y-lisible h1, html.a11y-lisible h2, html.a11y-lisible h3,
html.a11y-lisible .script, html.a11y-lisible .cal-mois {
  font-family: Verdana, "DejaVu Sans", Tahoma, sans-serif !important; font-style: normal !important;
}
html.a11y-calme *, html.a11y-calme *::before, html.a11y-calme *::after {
  animation: none !important; transition: none !important;
}
html.a11y-calme .pm-note { transform: none !important; }
html.a11y-liens a, html.a11y-liens .btn { text-decoration: underline !important;
                                          text-underline-offset: 3px; }
html.a11y-espace body { line-height: 1.9 !important; letter-spacing: .02em;
                        word-spacing: .08em; }
html.a11y-espace p, html.a11y-espace li { margin-bottom: 1em !important; }

/* Un point de scène sans contenu : présent, mais visiblement inerte. */
.points button.vide { opacity: .18; cursor: default; }
.points button.vide:hover { opacity: .18; }

/* --- Onglets d'une page légale en plein écran -------------------------- */
.onglets { display: flex; gap: 8px; margin-bottom: 24px; justify-content: flex-start; }
.onglets a { flex: 0 0 auto; font-size: 14px; padding: 8px 16px; border-radius: 999px;
             text-decoration: none; white-space: nowrap;
             border: 1px solid var(--line); color: var(--ink-soft); background: var(--paper); }
.onglets a:hover { border-color: var(--sage); color: var(--sage); }
.onglets a.on, .onglets a.on:hover { background: var(--sage); border-color: var(--sage);
                                     color: var(--paper); }

/* --- La galerie en fenêtre, depuis la création ------------------------- */
#dlg-galerie { width: min(96vw, 1000px); }
#dlg-galerie .feuille { max-height: 90vh; display: flex; flex-direction: column;
                        padding: 22px 22px 18px; }
#dlg-galerie .corps-galerie { flex: 1 1 auto; overflow-y: auto; padding-inline-end: 4px; }
#dlg-galerie .corps-galerie h2 { margin-top: 22px; }
#dlg-galerie .ligne { flex: none; }

/* --- Actions destructrices --------------------------------------------
   Rouge, mais discret tant qu'on ne s'en approche pas : un bouton rouge
   plein au milieu d'une liste attire le clic qu'il devrait décourager. */
/* ⚠ LE TEXTE PREND « --rouge-fort », LA BORDURE GARDE « --rouge-bord ». Deux
   seuils, deux jetons : une bordure se contente de 3:1, un texte exige 4,5:1, et
   « --rouge-bord » descend à 4,29:1 sur les seize thèmes. Le mélanger aux deux
   usages, c'est faire hériter le texte du seuil de la bordure. */
.ev .actes button.danger, .btn.danger { border-color: var(--rouge-bord); color: var(--rouge-fort); }
.ev .actes button.danger:hover, .btn.danger:hover { background: var(--rouge-bord); color: var(--sur-rouge); }
.btn.danger-plein { background: var(--rouge-bord); border-color: var(--rouge-bord); color: var(--sur-rouge); }
.btn.danger-plein:hover { filter: brightness(1.08); }
.btn.danger-plein[disabled] { opacity: .45; }

/* --- Erreur de saisie : sous le champ, jamais en haut de page ---------
   Un bandeau en tête de page oblige à remonter, lire, redescendre et
   retrouver le champ fautif. Le message se met là où est l'erreur. */
.erreur-champ {
  display: flex; align-items: flex-start; gap: 7px;
  margin: 7px 0 0; font-size: 13px; color: var(--rouge-fort);
}
.erreur-champ::before { content: "!"; flex: none;
  width: 16px; height: 16px; border-radius: 50%; background: var(--rouge-bord); color: var(--sur-rouge);
  font-size: 11px; font-weight: 700; line-height: 16px; text-align: center; }
input[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--rouge-bord); box-shadow: 0 0 0 3px var(--rouge-voile);
}

/* --- Jauge de remplissage ---------------------------------------------- */
.jauge { height: 9px; border-radius: 999px; background: var(--line); overflow: hidden; }
.jauge i { display: block; height: 100%; border-radius: 999px; background: var(--sage);
           transition: width .5s cubic-bezier(.2,.9,.3,1); }
#dlg-relance { width: min(94vw, 560px); }

/* --- Personnaliser sa note --------------------------------------------
 *
 * Replié par défaut. Écrire un mot ne doit pas commencer par un
 * questionnaire : celui qui veut juste écrire ne voit qu'une ligne,
 * celui qui veut choisir déplie.
 *
 * La couleur se prend dans la palette du thème, jamais dans une roue
 * chromatique. Soixante-cinq invités libres de toute couleur donnent un
 * mur qui n'en est plus un ; six teintes qui vont ensemble tiennent
 * quoi qu'il arrive. */
.perso {
  margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--line);
}
.perso-titre {
  display: flex; align-items: center; gap: 8px; width: 100%;
  cursor: pointer; user-select: none; text-align: start;
  padding: 4px 0; min-height: var(--bt-petit);
  border: 0; background: transparent; font: inherit;
  font-size: var(--bt-petit-t); color: var(--ink-soft);
}
.perso-titre::after {
  content: ""; flex: none; margin-inline-start: auto;
  width: 7px; height: 7px; border: 1.5px solid currentColor;
  border-width: 0 1.5px 1.5px 0; transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .18s ease;
}
.perso.ouvert > .perso-titre { color: var(--ink); }
.perso.ouvert > .perso-titre::after { transform: rotate(-135deg) translate(-2px, -2px); }
.perso-titre:focus-visible { outline: 2px solid var(--sage); outline-offset: 3px; }
.perso label { margin-bottom: 2px; }

.vign-forme {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  width: 78px; padding: 7px 6px 6px;
  background: none; border: 1.5px solid transparent; border-radius: 8px;
  font: inherit; cursor: pointer;
}
.vign-forme:hover { border-color: var(--line); }
.vign-forme.on { border-color: var(--sage); background: var(--sage-voile, var(--sage-pale)); }
.vign-forme:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
.vign-forme .lib {
  font-size: 11.5px; line-height: 1.15; color: var(--ink-soft); text-align: center;
}
.vign-forme.on .lib { color: var(--ink); }
/* La vignette laisse dépasser ce qui dépasse : ruban, punaise et coin
   corné vivent hors du carré, les rogner les rendrait méconnaissables. */
.apercu-forme {
  display: grid; place-items: center;
  width: 48px; height: 48px; overflow: visible;
}
.apercu-forme .pm-note {
  width: 36px; padding: 0; cursor: pointer;
  box-shadow: 0 1px 1px var(--ombre), 0 5px 9px -5px var(--ombre-forte);
}
.apercu-forme .pm-note::before,
.apercu-forme .pm-note::after { pointer-events: none; }

.choix-teinte { align-items: center; }
.teinte {
  display: flex; align-items: center; gap: 7px;
  min-height: 40px; padding: 4px 6px;
  background: none; border: 0; border-radius: 999px; font: inherit; cursor: pointer;
}
.teinte .rond {
  display: block; width: 26px; height: 26px; border-radius: 50%;
  border: 1.5px solid var(--line-champ, var(--line));
  transition: box-shadow .15s ease;
}
/* « Au hasard » : les teintes du thème en camaïeu, pour dire que c'est
   le mur qui choisira — pas une absence de couleur. */
.teinte .rond.auto {
  background: conic-gradient(from 210deg, var(--sage), var(--gold),
              var(--sage-pale), var(--sage));
}
.teinte .lib { font-size: 12.5px; color: var(--ink-soft); }
.teinte:hover .rond { box-shadow: 0 0 0 2px var(--paper), 0 0 0 3.5px var(--line); }
.teinte.on .rond { box-shadow: 0 0 0 2px var(--paper), 0 0 0 3.5px var(--sage); }
.teinte.on .lib { color: var(--ink); }
.teinte:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }

/* --- Choisir ses activités --------------------------------------------
 *
 * Une carte par activité, avec un aperçu de ce que ça donne. « La ligne
 * de vie » ne veut rien dire ; un trait avec trois points datés, si.
 *
 * L'état coché est lu par « :has() » sur la case native, jamais par une
 * classe posée à la main : une classe se désynchronise au premier clic,
 * la case, non. */
.grille-act {
  display: grid; gap: 10px; margin-top: 4px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.carte-act {
  position: relative; display: flex; flex-direction: column; gap: 7px;
  padding: 14px 15px 13px; cursor: pointer;
  background: var(--paper); border: 1.5px solid var(--line); border-radius: 8px;
  transition: border-color .15s ease, background .15s ease;
}
.carte-act input { position: absolute; opacity: 0; width: 0; height: 0; }
.carte-act:hover { border-color: var(--ink-soft); }
/* ⚠ « .on » EN PLUS de « :has() », et non à la place.
   « :has() » est absent de Firefox avant 121 et des vieux navigateurs embarqués
   d'Android ; la case, elle, est invisible (opacity:0 juste au-dessus). Sur ces
   navigateurs, les six activités s'affichaient donc toutes pareilles, cochées ou
   non. La classe est posée par PHP au chargement et suivie par un script en bas
   de creer.php ; « :has() » reste le filet quand il n'y a pas de script. */
.carte-act:has(input:checked),
.carte-act.on {
  border-color: var(--sage); background: var(--sage-voile, var(--sage-pale));
  box-shadow: inset 0 0 0 1px var(--sage);
}
.carte-act:has(input:focus-visible) { outline: 2px solid var(--sage); outline-offset: 2px; }
/* La coche, en haut à droite : on doit pouvoir compter ce qu'on a pris
   d'un coup d'œil, sans relire les titres. */
.carte-act::after {
  content: ""; position: absolute; top: 11px; inset-inline-end: 12px;
  width: 17px; height: 17px; border-radius: 50%;
  border: 1.5px solid var(--line-champ, var(--line)); background: var(--paper);
  transition: background .15s ease, border-color .15s ease;
}
.carte-act:has(input:checked)::after,
.carte-act.on::after {
  background: var(--sage) no-repeat center/10px 8px;
  border-color: var(--sage);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
viewBox='0 0 10 8'%3E%3Cpath d='M1 4.2 3.6 6.8 9 1.2' fill='none' stroke='%23fff' \
stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* La couleur est ÉCRITE : une carte d'activité est un <label>, et
   « label { color: var(--ink-soft) } » lui donnait un titre gris pâle —
   c'est de là que venait le titre illisible. */
.carte-act .nom  { font-family: var(--serif); font-size: 16px; padding-inline-end: 22px;
                   color: var(--ink); }
.carte-act .quoi { font-size: 12.5px; color: var(--ink-soft); line-height: 1.4; }
.carte-act .vue  { display: flex; align-items: center; height: 34px; overflow: hidden; }

/* Les étiquettes de scènes de l'aperçu de projection (page Réglages).
 *
 * ⚠ DES NOTES INCLINÉES, ET C'EST LA GRAMMAIRE DU PRODUIT — 22/08, demandé :
 * « des notes légèrement inclinées comme dans le mur en projection ». Le pied de
 * la projection a des points ronds, qui suffisent dans une salle ; ici on vient
 * VÉRIFIER activité par activité, donc il faut lire le nom de chacune.
 *
 * ⚠ POURQUOI ICI ET NON DANS LE <style> DE reglages.php, à côté de ses voisines
 * « .apercu » : parce que cette page est derrière une session, et qu'une règle
 * qu'on ne peut voir que connecté ne peut pas être MESURÉE. Posée ici, elle se
 * vérifie sur n'importe quelle page — et c'est ainsi qu'on a constaté que la
 * rotation s'appliquait bien. Le composant est piloté par un script d'assets,
 * pas par le gabarit : sa place est du côté du composant.
 *
 * ⚠ L'ANGLE EST DANS UNE VARIABLE, ET L'ÉTAT COMPOSE. « transform » ne
 * s'additionne pas : une règle d'état qui reposerait « transform: scale(…) »
 * effacerait la rotation sans rien dire — leçon déjà payée sur les scènes de
 * projection. */
.proj-notes { display: flex; flex-wrap: wrap; gap: 7px 6px;
              justify-content: center; margin: 12px 0 2px; }
.proj-note {
  --rot: 0deg;
  font: inherit; font-family: var(--serif); font-size: 12.5px; line-height: 1.2;
  padding: 6px 9px 7px; cursor: pointer;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line); border-radius: 2px;
  box-shadow: 0 1px 3px var(--ombre);
  transform: rotate(var(--rot));
  transition: background .15s ease, border-color .15s ease, transform .15s ease;
}
.proj-note:hover { border-color: var(--sage);
                   transform: rotate(var(--rot)) translateY(-1px); }
.proj-note.on { background: var(--sage-voile, var(--cream)); border-color: var(--sage);
                box-shadow: inset 0 0 0 1px var(--sage), 0 1px 3px var(--ombre);
                transform: rotate(var(--rot)) scale(1.04); }
.proj-note:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .proj-note { transition: none; } }

/* Les aperçus. Dessinés en CSS plutôt qu'en images : ils se teignent avec
   le thème choisi juste au-dessus, et ne coûtent aucune requête. */
.v-onde { display: flex; align-items: center; gap: 3px; height: 24px; }
.v-onde i {
  width: 3px; border-radius: 2px; background: var(--sage);
  height: 30%; opacity: .85;
}
.v-onde i:nth-child(2)  { height: 62%; }
.v-onde i:nth-child(3)  { height: 96%; }
.v-onde i:nth-child(4)  { height: 48%; }
.v-onde i:nth-child(5)  { height: 78%; }
.v-onde i:nth-child(6)  { height: 34%; }
.v-onde i:nth-child(7)  { height: 88%; }
.v-onde i:nth-child(8)  { height: 56%; }
.v-onde i:nth-child(9)  { height: 70%; }
.v-onde i:nth-child(10) { height: 40%; }
.v-onde i:nth-child(11) { height: 26%; }

.v-nuage { font-family: var(--serif); line-height: 1; white-space: nowrap; color: var(--sage); }
.v-nuage b  { font-weight: 400; font-size: 17px; }
.v-nuage i  { font-style: normal; font-size: 12px; opacity: .7; margin-inline-start: 6px; }
.v-nuage em { font-style: normal; font-size: 14px; opacity: .85; margin-inline-start: 6px; }

.v-frise { position: relative; display: block; width: 84px; height: 24px; }
.v-frise::before {
  content: ""; position: absolute; left: 0; right: 0; top: 11px;
  height: 1.5px; background: var(--line);
}
.v-frise i {
  position: absolute; top: 7px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--sage); border: 1.5px solid var(--paper);
}
.v-frise i:nth-child(1) { left: 0; }
.v-frise i:nth-child(2) { left: 36px; }
.v-frise i:nth-child(3) { left: 74px; }

/* Trois lignes cochables. Chaque ligne est une rangée flex — case puis
   barre — plutôt qu'une barre avec une case en position absolue : les
   cases finissaient par se chevaucher et déborder de l'aperçu. */
.v-liste { display: flex; flex-direction: column; gap: 3px; }
.v-liste i { display: flex; align-items: center; gap: 6px; }
.v-liste i::before {
  content: ""; flex: none;
  width: 7px; height: 7px; border-radius: 2px; border: 1.2px solid var(--sage);
}
.v-liste i::after {
  content: ""; height: 4px; border-radius: 2px; background: var(--line);
}
.v-liste i:nth-child(1)::after { width: 58px; }
.v-liste i:nth-child(2)::after { width: 42px; }
.v-liste i:nth-child(3)::after { width: 50px; }
.v-liste i:nth-child(2)::before { background: var(--sage); }

/* La musique : les cinq crans d'une note, quatre remplis. C'est le geste même
   que l'invité fera sur chaque morceau. */
.v-notes { display: flex; gap: 4px; align-items: center; }
.v-notes i { width: 11px; height: 14px; border-radius: 2px;
             border: 1.2px solid var(--sage); }
.v-notes i:nth-child(-n+4) { background: var(--sage); }

/* --- Combien d'invités : trois tailles de fête -------------------------
 *
 * Onze nombres alignés d'affilée, personne ne les lit : on cherche le
 * sien dans une file. Nommer les familles fait qu'on se reconnaît avant
 * de compter — « belle tablée », c'est déjà une réponse. */
.famille-nb { display: flex; align-items: center; gap: 12px; margin-top: 9px; }
.famille-nb .lib {
  flex: 0 0 118px; font-size: 13px; color: var(--ink-soft); line-height: 1.2;
}
.famille-nb .grille-nb { margin-top: 0; }
@media (max-width: 520px) {
  .famille-nb { display: block; }
  .famille-nb .lib { display: block; margin-bottom: 5px; }
}

/* --- Le sommaire d'une page d'événement --------------------------------
 *
 * Tout est sur une page : le mur, puis les activités. Ces ancres évitent
 * de remonter à l'aveugle quand la page fait quatre écrans. Elles ne
 * remplacent pas une navigation — il n'y a nulle part où aller. */
/* ⚠ LE SOMMAIRE FLOTTE — 22/08, en réponse à « est-il bien placé ? ».
 *
 * Il ne l'était pas, et la mesure le disait : avec la ligne de vie garnie, la page
 * du mur fait 24 écrans (43 au plafond de souvenirs). Un menu de navigation posé
 * DANS l'en-tête n'existe donc que sur le premier écran — pour s'en servir à
 * l'écran 20, il faut remonter à l'aveugle les vingt écrans qu'il servait
 * justement à éviter. C'est la seule commande de la page qui devient inutile à
 * mesure qu'on en a besoin.
 *
 * ⚠ ET C'EST UNE GRAMMAIRE QUI EXISTE DÉJÀ, pas une invention : le site a deux
 * pastilles flottantes (le retour en haut, les réglages). Celle-ci se range avec
 * elles, à gauche du retour en haut — qui garde sa place, celle où on le cherche.
 * ⚠ PAS « REPOSITIONNABLE ». Un menu qu'on déplace demande un état à garder, se
 * retrouve là où on l'a oublié, et se dispute la place avec le doigt qui défile.
 * Le coin bas-droit est là où la main tient déjà le téléphone.
 * ⚠ Il reste une PASTILLE ALLONGÉE et non un rond : « Aller à… » se lit, une icône
 * de liste se devine. Les deux autres pastilles sont des réglages qu'on cherche
 * une fois ; celle-ci sert à chaque section.
 */
/* ⚠ IL SE DÉCALE SEULEMENT QUAND LE RETOUR EN HAUT EST LÀ — 22/08. Cette pastille
   n'apparaît qu'après un écran et demi de défilement : réserver sa place en
   permanence laissait un trou de 54 px au bord droit sur le premier écran, et un
   élément flottant qui ne s'aligne sur rien se lit comme mal posé. « :has » lit
   l'état réel du document — pas une classe qu'un script aurait à tenir à jour. */
.sommaire {
  position: fixed; inset-inline-end: var(--page-bord);
  /* Le même axe que les deux pastilles et la barre du bas : son bouton a une
     hauteur fixe égale à « --flottant-taille » (voir plus bas), donc la même
     formule les met tous les quatre sur une seule ligne. */
  bottom: calc(var(--flottant-axe) - var(--flottant-taille) / 2);
  z-index: var(--z-flottant); margin: 0; max-width: none; width: auto;
}
/* ⚠ LE DÉCALAGE DE 54 px A DISPARU AVEC LA PASTILLE DE RETOUR EN HAUT — 31/08.
   Il existait pour ne pas la recouvrir : « body:has(#pm-haut.visible) .sommaire »
   poussait le sommaire d'une largeur de pastille dès qu'elle apparaissait, et la
   transition « right » servait à ce que le glissement ne se lise pas comme un
   défaut d'affichage. Plus de voisine, plus de décalage, plus de glissement — le
   sommaire tombe simplement sur « --page-bord », comme tout ce qui flotte. */

/* --- Une étape du programme : icône, texte, et « Y aller » -------------- */
/* ⚠ TROIS COLONNES, ET « align-items: start ». Le menu s'aligne sur la PREMIÈRE
   ligne du texte, pas sur son milieu : avec une précision d'accès en troisième
   ligne, un menu centré tomberait à hauteur de l'adresse et paraîtrait rattaché à
   elle. */
.lieu-l { display: grid; grid-template-columns: auto 1fr auto; gap: 14px;
          align-items: start; padding: 12px 0; }
.lieu-l[data-trait] { border-bottom: 1px solid var(--line); }
.lieu-adr { margin: 3px 0 0; font-size: 14px; color: var(--ink-soft); }
.lieu-note { margin: 3px 0 0; font-size: 13px; color: var(--ink-soft); }
/* Le menu reste petit : c'est une commodité à côté d'une adresse, pas l'action
   principale de la carte. */
.y-aller-m > button { padding: 6px 12px; min-height: 34px; font-size: 13px; }
/* Sous 560 px, les trois colonnes ne tiennent pas : le menu passe sous le texte,
   aligné à gauche avec lui plutôt que sous l'icône. */
@media (max-width: 560px) {
  .lieu-l { grid-template-columns: auto 1fr; }
  .lieu-l .y-aller-m { grid-column: 2; margin-top: 8px; }
}
/* ⚠ LE MÊME BORD QUE LES PASTILLES RONDES — 22/08, demandé. « .pop-menu > button »
   emprunte « --line-champ », le trait des CHAMPS de formulaire, plus appuyé que
   celui des pastilles ; côte à côte avec le retour en haut et les réglages, la
   différence se voyait. Ici « --line », comme #pm-haut et #pm-reglages-bouton.
   ⚠ Seulement dans « .sommaire » : ailleurs (le menu d'agendas, « Y aller ») le
   bouton est DANS un formulaire, entouré de champs — c'est là que le trait de
   champ est le bon. */
.sommaire .pop-menu > button { box-shadow: 0 2px 10px var(--ombre);
                               border-color: var(--line);
                               /* Une hauteur FIXE, égale à celle des pastilles :
                                  c'est ce qui permet de l'aligner sur le même axe
                                  sans mesurer son texte. */
                               height: var(--flottant-taille); }
.sommaire .pop-menu > button:hover { border-color: var(--sage); }
@media print { .sommaire { display: none; } }
/* Les ancres partent de la GAUCHE, comme le reste. « flex-start » et non
   « safe center » : le mot-clé « safe » servait à garder le premier bouton
   atteignable quand la rangée débordait en étant centrée — un départ à gauche
   n'a pas ce problème du tout. */
/* --- Le sommaire de la page ---------------------------------------------
 *
 * ⚠ CE N'EST PLUS UNE RANGÉE DE BOUTONS, et tout ce qui suivait ici a été
 * retiré le 20/08 : une soixantaine de lignes qui réglaient le repli à la
 * ligne en dessous de quatre ancres, la pastille de chaque ancre, et son
 * survol. Le raisonnement d'alors était juste — mesuré sur 375 px, trois
 * ancres demandaient 374 px pour 347 disponibles, donc la troisième était
 * coupée de 13 px, ce qui ne dit rien sinon que l'affichage est raté.
 *
 * Mais il répondait à la mauvaise question. François a tranché : se
 * déplacer dans une page est un CHOIX dans une liste, pas une série
 * d'actions. Un menu tient sur une ligne avec trois parties comme avec
 * huit, et la question du repli ne se pose plus jamais. Voir « .pop-menu »
 * en bas de ce fichier, et le commentaire de e.php.
 *
 * ⚠ Le popover reste un DESCENDANT de « .sommaire » dans le document, même
 * s'il est peint dans le calque supérieur : une règle « .sommaire .anc »
 * l'atteindrait donc encore, et sa pastille battrait « .pop-liste a » à la
 * spécificité. C'est pourquoi il n'en reste aucune. */
.sommaire { display: block; }
/* La barre du haut est fixe : sans cette marge, une ancre dépose la
   section juste dessous, à moitié cachée. */
:target, #mur, #apports, #frise, #anim, #circuler { scroll-margin-top: calc(var(--barre-h, 54px) + 16px); }

/* --- Choisir son écriture et son icône ---------------------------------
 *
 * L'aperçu montre le MOT « Merci » dans la fonte, pas son nom : « Gochi
 * Hand » ne dit rien à personne, un mot écrit dedans, tout. */
.ecrit {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  min-width: 76px; padding: 8px 12px 7px;
  background: none; border: 1.5px solid transparent; border-radius: 8px;
  font: inherit; cursor: pointer;
}
.ecrit:hover { border-color: var(--line-champ, var(--line)); }
.ecrit.on { border-color: var(--sage); background: var(--sage-voile, var(--paper)); }
.ecrit:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
.ecrit .ex {
  font-size: 19px; line-height: 1.15; color: var(--ink); white-space: nowrap;
  font-synthesis-weight: none;
}
.ecrit .lib { font-size: 11px; color: var(--ink-soft); white-space: nowrap; }
.ecrit.on .lib { color: var(--ink); }

.vign-ic {
  display: flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; flex: 0 0 auto;
  background: var(--paper); border: 1.5px solid var(--line-champ, var(--line));
  border-radius: 8px; color: var(--ink-soft); cursor: pointer;
}
.vign-ic:hover { color: var(--ink); border-color: var(--sage); }
.vign-ic.on { border-color: var(--sage); background: var(--sage-voile, var(--paper));
              color: var(--sage); border-width: 2px; }
.vign-ic:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
/* « Aucune » : une case barrée, plutôt qu'un vide qu'on prendrait pour
   un bogue de chargement. */
.vign-ic .rien {
  display: block; width: 18px; height: 18px; border-radius: 50%;
  border: 1.5px solid currentColor; position: relative;
}
.vign-ic .rien::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 1.5px; height: 22px; background: currentColor;
  transform: rotate(45deg); transform-origin: center;
  top: -3px; left: 0; right: 0;
}

/* --- Tableaux d'administration : chercher, trier, choisir -------------- */
.tab-outils {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 4px 0 10px;
}
.tab-cherche {
  display: flex; align-items: center; gap: 8px; flex: 1 1 200px; max-width: 320px;
  padding: 0 12px; border: 1px solid var(--line-champ, var(--line)); border-radius: 999px;
  background: var(--paper); color: var(--ink-soft);
}
.tab-cherche input {
  flex: 1 1 auto; width: 100%; border: 0; background: none; outline: none;
  padding: 9px 0; font: inherit; font-size: 14.5px; color: var(--ink);
}
.tab-cherche:focus-within { border-color: var(--sage);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 20%, transparent); }
.tab-compte { font-size: 13px; color: var(--ink-soft); }
.tab-colonnes { margin-inline-start: auto; position: relative; }
.tab-colonnes-titre {
  /* « font: inherit » : un bouton sans cela prend la police du système,
     ce qui est exactement le défaut qu'on chasse. */
  font: inherit; cursor: pointer; user-select: none;
  padding: 8px 16px; min-height: var(--bt-petit); display: inline-flex; align-items: center;
  border: 1px solid var(--line-champ, var(--line)); border-radius: 999px;
  background: var(--paper); font-size: var(--bt-petit-t); color: var(--ink-soft);
}
.tab-colonnes.ouvert > .tab-colonnes-titre { border-color: var(--sage); color: var(--ink); }
.tab-colonnes .choix {
  position: absolute; inset-inline-end: 0; top: calc(100% + 6px); z-index: var(--z-menu);
  min-width: 190px; max-height: 60vh; overflow-y: auto;
  padding: 12px 14px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 10px 30px color-mix(in srgb, var(--ink) 18%, transparent);
}
.tab-colonnes .champ-choix { margin: 0 0 8px; font-size: 14px; }
.tab-colonnes .champ-choix:last-child { margin-bottom: 0; }
.tab-rien { padding: 16px; margin: 0; font-size: 14px; color: var(--ink-soft); }
/* Sur téléphone la barre se replie : « Colonnes » repoussé à droite se
   retrouvait seul sur sa ligne, contre le bord. Il revient à gauche. */
@media (max-width: 540px) {
  .tab-colonnes { margin-inline-start: 0; }
  .tab-cherche { max-width: none; }
}

/* L'en-tête cliquable annonce ce qu'il fait : la flèche n'apparaît qu'une
   fois le tri posé, sinon douze flèches grises brouillent la lecture. */
th.triable { cursor: pointer; user-select: none; white-space: nowrap; }
th.triable:hover { color: var(--sage); }
th.triable:focus-visible { outline: 2px solid var(--sage); outline-offset: -2px; }
th.triable::after {
  content: ""; display: inline-block; width: 0; height: 0; margin-inline-start: 6px;
  border: 4px solid transparent; opacity: 0;
}
th[aria-sort="ascending"]::after  { border-bottom-color: currentColor; margin-bottom: 3px; opacity: .8; }
th[aria-sort="descending"]::after { border-top-color: currentColor; margin-top: 3px; opacity: .8; }

/* Une ligne archivée se lit encore : on la marque, on ne l'efface pas.
   L'opacité seule était invisible au tri comme à la recherche — d'où la
   colonne « État », qui, elle, se trie. */
table.liste tr.archivee td { opacity: .62; font-style: italic; }
table.liste td.pictos { white-space: nowrap; color: var(--sage); }
table.liste td.pictos span { display: inline-block; margin-inline-end: 3px; vertical-align: -2px; }

/* --- Un événement sans responsable, en administration ------------------ */
.orphelin {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  border-top: 1px solid var(--line); padding-top: 12px; margin-top: 12px;
}
.orphelin .quoi { min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.orphelin strong { font-family: var(--serif); font-weight: 400; }
/* Un slug n'a pas d'espace où se couper : on l'autorise à casser
   n'importe où plutôt qu'à pousser la page. */
.orphelin a { font-size: 13.5px; overflow-wrap: anywhere; }
.orphelin .quand { font-size: 13.5px; color: var(--ink-soft); }

/* --- L'abonnement d'une organisation, côté organisateur ---------------- */
.abo { margin-bottom: 20px; }
.abo h2 { display: flex; align-items: center; gap: 9px; margin-bottom: 8px; }
.abo .l { margin: 0 0 6px; font-size: 14.5px; color: var(--ink-soft); }
.abo .l strong { color: var(--ink); }
.abo .rang { margin-top: 14px; }
/* Une échéance proche se signale, une échéance passée s'annonce. Ni
   l'une ni l'autre ne crie : les événements et les messages ne sont
   jamais en jeu, seules les options le sont. */
.abo.proche { border-color: var(--gold); }
.abo.fini   { border-color: var(--rouge-bord); }

/* --- Liste déroulante ---------------------------------------------------
 *
 * Le menu natif est dessiné par le système, jamais par la page. Celui-ci
 * suit le thème. Le <select> d'origine reste dessous : il porte la valeur,
 * le formulaire l'envoie, et sans JavaScript il redevient visible. */
.sel { position: relative; display: block; }
.sel-natif {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
.sel-bouton {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; min-height: var(--bt); padding: 11px 13px; line-height: 1.35;
  font: inherit; font-size: 15.5px; text-align: start; cursor: pointer;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--line-champ, var(--line)); border-radius: 4px;
}
.sel-bouton .lib { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sel-bouton svg { flex: 0 0 auto; color: var(--ink-soft); transition: transform .16s ease; }
.sel-bouton:hover { border-color: var(--sage); }
.sel-bouton:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
.sel.ouvert .sel-bouton { border-color: var(--sage);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sage) 20%, transparent); }
.sel.ouvert .sel-bouton .pm-chevron { transform: rotate(180deg); }

/* ⚠ LA LISTE VIT DANS LE CALQUE SUPÉRIEUR DU NAVIGATEUR (« popover »), et non
   dans la boîte du champ. Raison : un conteneur de défilement DÉCOUPE ses
   descendants absolus, quel que soit leur z-index — et le tableau des
   organisations en a un (« .enveloppe »). Voir le commentaire de choix.js.
   « position:fixed » et « inset:auto » : les coordonnées sont posées par
   choix.js, qui choisit aussi le sens d'ouverture. « margin:0 » annule le
   centrage que le navigateur applique par défaut à un popover. */
.sel-liste {
  position: fixed; inset: auto; margin: 0; z-index: var(--z-menu);
  max-height: 46vh; overflow-y: auto; padding: 5px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 6px;
  box-shadow: 0 14px 34px color-mix(in srgb, var(--ink) 22%, transparent);
}
/* Repli pour un navigateur sans « popover » : la liste reste dans la boîte du
   champ, comme avant. Elle peut alors être découpée dans un tableau — c'est le
   comportement d'hier, pas une régression. */
.sel-liste:not([popover]) {
  display: none; position: absolute; left: 0; right: 0; top: calc(100% + 5px);
}
.sel.ouvert .sel-liste:not([popover]) { display: block; }
.sel-liste::backdrop { background: transparent; }
.sel-opt {
  padding: 9px 11px; border-radius: 4px; cursor: pointer;
  font-size: 15px; color: var(--ink); display: flex; align-items: center; gap: 8px;
}
.sel-opt:hover, .sel-opt.vise { background: var(--sage-voile, var(--cream)); }
.sel-opt.choisi { color: var(--sage); }
/* La coche, plutôt qu'un simple fond : la couleur seule ne suffit pas à
   dire « choisi » — c'est le critère 1.4.1 (l'information ne passe pas
   uniquement par la couleur). */
.sel-opt.choisi::after {
  content: ""; margin-inline-start: auto; width: 13px; height: 8px;
  border: 2px solid currentColor; border-width: 0 0 2px 2px;
  transform: rotate(-45deg) translateY(-2px);
}
.sel-opt[aria-disabled="true"] { opacity: .45; cursor: default; }
/* Compacte : dans un tableau, la même liste tient sur une ligne de cellule. */
/* Le mois d'un souvenir, à côté de l'année : largeur fixe, comme l'année. La
   boîte « .sel » remplace le <select> dans la rangée en display:flex — c'est
   donc ELLE qui doit porter la largeur. */
.sel.frise-mois { flex: 0 0 150px; }
.sel.compacte .sel-bouton { min-height: 34px; padding: 5px 9px; font-size: 13px; }
.sel.compacte .sel-liste { inset-inline-start: auto; inset-inline-end: 0; min-width: 160px; }

/* La ligne « Tu publies sous le nom X · Changer », dans les activités du mur.
   Discrète : c'est un rappel, pas une consigne — et elle n'apparaît que si un
   prénom est déjà en mémoire, sinon il n'y a rien à corriger. */
/* ⚠ CETTE LIGNE N'ÉTAIT PAS ASSEZ VISIBLE — 22/08. Treize pixels d'encre douce,
   posés nus entre le sommaire et la première carte : elle disparaissait entre deux
   blocs encadrés. Or c'est la SEULE porte pour corriger un prénom, et l'invité qui
   a tapé « Azfz » un soir d'essai n'a que celle-là.
   Elle prend donc l'habit des encarts informatifs — même fond, même filet, même
   rayon (voir la grammaire commune plus haut). Rien de nouveau n'est inventé : ce
   qui manquait, c'était de la traiter comme ce qu'elle est, une information. */
.pm-prenom-ligne {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px;
  margin: 0; padding: 11px 14px; border-radius: 6px;
  background: var(--cream); border: 1px solid var(--line);
  font-size: 14px; color: var(--ink); line-height: 1.5;
}
.pm-prenom-ligne strong { color: var(--ink); font-weight: 600; }
#pm-prenom { margin: 0 0 18px; }
.pm-prenom-changer {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 14px; color: var(--sage);
  text-decoration: underline; text-underline-offset: 2px;
}
.pm-prenom-changer:hover { text-decoration-thickness: 2px; }
.pm-prenom-changer:focus-visible { outline: 2px solid var(--sage); outline-offset: 3px; }

/* --- La modération en mosaïque (reglages.php, « Comme le mur ») ----------
 *
 * ⚠ CE N'EST PAS LE MUR, c'est sa géométrie : le même nombre de colonnes, donc
 * la même place pour chaque case. On modère avec les yeux, pas avec la mémoire
 * du texte. Les cases libres sont dessinées en creux — sans elles, huit mots sur
 * quatre-vingt-une cases donneraient une grille de huit, qui ne ressemble à rien
 * de ce qu'on a sous les yeux.
 */
.mod-mur {
  display: grid; grid-template-columns: repeat(var(--cols, 9), minmax(0, 1fr));
  gap: 6px; margin: 0 0 4px;
}
.mod-case {
  position: relative; min-width: 0; aspect-ratio: 1;
  display: flex; flex-direction: column; gap: 2px; overflow: hidden;
  padding: 6px 6px 4px; border-radius: 3px; border: 1px solid var(--line);
  background: var(--pm-c, var(--paper));
  font-size: 10.5px; line-height: 1.25; text-align: start;
}
.mod-case.libre { background: transparent; border-style: dashed; opacity: .5;
                  align-items: center; justify-content: center; }
.mod-case.libre span { font-size: 10px; color: var(--ink-soft); }
.mod-case .no  { font-size: 9.5px; color: color-mix(in srgb, var(--ink) 55%, transparent); }
.mod-case .qui { font-weight: 700; white-space: nowrap; overflow: hidden;
                 text-overflow: ellipsis; }
.mod-case .txt { flex: 1 1 auto; overflow: hidden; color: var(--note-encre, var(--ink)); }
/* La commande de retrait : au survol, comme sur le mur — et toujours visible au
   doigt, où le survol n'existe pas. */
.mod-case .oter {
  position: absolute; top: 3px; inset-inline-end: 3px; width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--paper); color: var(--rouge-bord);
  opacity: 0; transition: opacity .15s ease;
}
.mod-case:hover .oter, .mod-case:focus-within .oter { opacity: 1; }
@media (hover: none) { .mod-case .oter { opacity: .9; } }
/* ⚠ Sous 560 px, une mosaïque de neuf colonnes donne des cases de vingt pixels :
   illisibles, et intouchables au doigt. On tombe à quatre colonnes — la place
   relative se perd un peu, mais une case qu'on peut viser vaut mieux qu'une
   géométrie exacte qu'on ne peut pas toucher. */
@media (max-width: 560px) {
  .mod-mur { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* --- La modération sur place (assets/moderation.js) ---------------------
 *
 * ⚠ DISCRET, PARCE QUE C'EST LA PAGE DES INVITÉS. Une croix rouge sur chaque
 * mot ferait du mur une planche de contrôle : la commande n'apparaît qu'au
 * survol et au focus, et reste toujours atteignable au doigt (44 px de cible
 * réelle malgré ses 22 px visibles, par le rembourrage).
 */
/* ⚠ PLUS AUCUNE PASTILLE RONDE À CROIX ROUGE — retirée le 21/08 sur demande.
   Elle flottait au coin de l'élément, donc au-dessus du texte de quelqu'un :
   elle ressemblait autant à une erreur d'affichage qu'à une commande, et elle
   recouvrait ce qu'elle concernait. Deux habits la remplacent, et un seul
   fichier les dessine : un LIEN nommé là où il y a la place d'un mot, une croix
   DANS l'étiquette là où il n'y en a pas.
   ⚠ Ne pas la réintroduire « juste pour ce cas-là » : c'est ainsi qu'une
   interface se remplit de trois façons de faire la même chose. */

/* La dernière ligne d'un élément modérable, quand aucun hôte n'est prévu :
   après le contenu, alignée à droite. */
.mod-pied { margin: 8px 0 0; text-align: end; }

/* L'étiquette : la croix est DEDANS, à côté du texte. */
.mod-badge { display: inline-flex; align-items: center; gap: 5px; }
.mod-badge-x {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  background: color-mix(in srgb, var(--rouge-bord) 12%, transparent); color: var(--rouge-bord);
}
.mod-badge-x:hover { background: color-mix(in srgb, var(--rouge-bord) 22%, transparent); }
.mod-badge-x:focus-visible { outline: 2px solid var(--rouge-bord); outline-offset: 2px; }

/* ⚠ LE NUAGE DEVIENT DES ÉTIQUETTES, LE TEMPS DE MODÉRER. Un nuage est une
   figure — des tailles, de l'air, rien à cliquer ; poser des croix dedans le
   rendrait illisible. La classe n'est ajoutée que par assets/anim.js quand le
   mode modération est allumé : les invités continuent de voir un nuage.
   ⚠ « font-size » n'est PAS touché : le poids du mot reste lisible, c'est
   l'information principale de cette figure. */
/* Ce qu'on apporte, dans la carte « Qui apporte quoi ».
   ⚠ Le liseré vert a disparu : cet encart suit maintenant la grammaire commune,
   déclarée plus haut avec « .avis » et « .attente-jour ». */
.apports-mien { margin: 14px 0 18px; }
/* ⚠ MÊME HABIT QUE LE NOM D'UN LIEU (« Manoir de Procé ») — demandé le 22/08 :
   sérif, 17 px, et en gras. La petite capitale espacée en faisait une étiquette de
   formulaire, alors que c'est un TITRE : ce qu'on apporte est de la même nature que
   ce qui est annoncé ailleurs sur la page. Une même nature d'information mérite le
   même habit — sinon la page paraît écrite par deux personnes. */
.apports-mien-t { margin: 0 0 8px; font-family: var(--serif); font-size: 17px;
                  font-weight: 700; color: var(--ink); }
.apports-mien ul { margin: 0 0 12px; padding-inline-start: 20px; }
.apports-mien li { font-family: var(--serif); font-size: 16px; margin-bottom: 4px; }
.apports-mien-n { margin: 9px 0 0; font-size: 13px; color: var(--ink-soft); }

/* La mention sanitaire, à la fin de « Qui apporte quoi ».
   ⚠ Un filet en haut plutôt qu'un encart complet : elle est DANS la carte
   maintenant, et un cadre à l'intérieur d'un cadre fait deux boîtes là où il n'y a
   qu'une idée. Le rouge sourd du filet suffit à dire « ceci est une obligation »
   sans crier. */
.apports-alcool { margin: 18px 0 0; }

/* La mention sanitaire — un vrai encart, rouge clair cerné de rouge sombre.
 *
 * ⚠ DEUX CLASSES (« .avis.avis-alcool ») POUR BATTRE LA GRAMMAIRE COMMUNE : à
 * spécificité égale, la dernière déclaration gagnerait, et ce fichier est long.
 * Deux classes rendent l'intention explicite au lieu de dépendre de l'ordre des
 * lignes. ⓘ « .avis » n'a plus de filet épais à gauche depuis le 22/08 — il n'y a
 * donc plus rien à annuler, seulement une couleur à imposer.
 *
 * ⚠ LE CONTRASTE EST CALCULÉ, PAS ESTIMÉ. Le texte prend « var(--ink) » et non
 * « var(--ink-soft) » : sur ce fond, l'encre douce du thème de Mélanie (#7b7266)
 * donne 4,23:1 — SOUS le seuil AA de 4,5 pour du texte courant. L'encre pleine
 * (#403b35) donne 9,9:1. Une mention légale est précisément ce qu'il ne faut pas
 * rendre pénible à lire. La bordure (#9c4230) est à 5,8:1 du fond, bien au-delà
 * des 3:1 attendus d'un élément d'interface.
 * ⚠ Les jetons de couleur varient selon le THÈME (lib/themes.php) : tous ont une
 * encre sombre, donc ce fond clair tient partout. C'est « --ink-soft » qui bougeait
 * assez pour passer sous le seuil — raison de plus de ne pas s'en servir ici.
 */
.avis.avis-alcool {
  margin: 0;
  background: var(--alcool-fond);
  border: 1px solid var(--alcool-bord);
  border-radius: 5px;
  color: var(--ink);
  padding: 14px 17px;
}
.avis.avis-alcool strong { color: var(--ink); }
.avis.avis-alcool p { color: var(--ink); }

/* Les rangées de pastilles trop longues — voir assets/badges.js.
   ⚠ « + 96 autres » est un BOUTON, donc il porte une icône ? Non : c'est l'ouverture
   d'une fenêtre, pas une action sur les données, et la règle du dépôt réserve
   l'icône aux ACTIONS. Il reste nu, comme les tailles de page 10/25/50/100. */
.badges-plus {
  font: inherit; font-size: 13px; padding: 4px 12px; border-radius: 999px;
  border: 1px dashed var(--line); background: transparent; color: var(--ink-soft);
  cursor: pointer; white-space: nowrap;
}
.badges-plus:hover { border-style: solid; color: var(--ink); }

/* La fenêtre : plus large que les autres et bornée en hauteur, parce qu'elle peut
   contenir cent vingt mots. C'est la LISTE qui défile, jamais la fenêtre entière —
   sinon la croix de fermeture partirait vers le haut hors de vue. */
/* ⚠ LE FOND EST PORTÉ PAR LES BLOCS INTÉRIEURS, PAS PAR LE « dialog ». La règle
   de base du site pose « background: transparent » sur tout dialogue — chaque
   fenêtre habille ses propres blocs (voir « .dlg-qr-tete »). Je l'avais oublié :
   la première version montrait la liste de mots par-dessus le mur, illisible.
   Une convention qu'on ne connaît pas ressemble à un bug d'opacité. */
dialog.dlg-badges { width: min(96vw, 760px); background: transparent; }
.dlg-badges-corps {
  padding: 16px 18px 20px;
  background: var(--paper); border: 1px solid var(--line); border-top: 0;
  border-radius: 0 0 10px 10px;
  /* Une hauteur PLANCHER autant qu'un plafond : « plein écran » a été demandé,
     et une fenêtre de 196 px pour vingt mots ne se lit pas comme une fenêtre
     mais comme une infobulle. C'est la LISTE qui défile, jamais la fenêtre —
     sinon la croix de fermeture partirait hors de vue. */
  /* ⚠ PLANCHER MODESTE SUR GRAND ÉCRAN. À 400 px, vingt mots laissaient les deux
     tiers de la fenêtre vides : une fenêtre trop grande pour son contenu se lit
     comme un défaut d'affichage, pas comme du calme. Elle grandit avec la liste
     jusqu'au plafond, et c'est sur téléphone que « plein écran » est littéral. */
  min-height: min(26vh, 200px); max-height: min(72vh, 620px); overflow-y: auto;
  display: flex; flex-wrap: wrap; gap: 7px; align-items: flex-start;
  align-content: flex-start;
}
/* Sur téléphone, « plein écran » veut dire plein écran. */
@media (max-width: 560px) {
  dialog.dlg-badges { width: 100vw; max-width: 100vw; margin: 0; height: 100vh; height: 100dvh; }
  .dlg-badges .dlg-qr-tete { border-radius: 0; border-inline-start: 0; border-inline-end: 0; }
  .dlg-badges-corps {
    border-radius: 0; border-inline-start: 0; border-inline-end: 0;
    min-height: calc(100vh - 52px); max-height: calc(100vh - 52px);
    min-height: calc(100dvh - 52px); max-height: calc(100dvh - 52px);
  }
}
/* En nuage : ce n'est pas une liste mais une figure, donc on laisse le texte
   couler au lieu de l'aligner en grille. */
/* Les mots de « En un mot », dans la page et dans la fenêtre plein écran.
   ⚠ UNE SEULE DÉCLARATION POUR LES DEUX : le nuage de la fenêtre doit ressembler
   au nuage de la page, et deux règles voisines auraient divergé au premier
   changement — c'est déjà la raison pour laquelle assets/anim.js n'a qu'UNE
   fabrique de mot, appelée deux fois.
   ⚠ « align-items: baseline » : les mots ne font pas la même taille (14 à 32 px
   selon leur fréquence), et alignés par le haut ils sembleraient flotter. */
.nuage-mots, .dlg-badges-corps.en-nuage {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: flex-start; gap: 10px 12px; margin: 16px 0 0;
  text-align: start;
}

/* Enregistrer une voix, choisir une photo — assets/enregistreur.js. */
/* --- Le formulaire de la ligne de vie -----------------------------------
 *
 * ⚠ TROIS CHAMPS, TROIS HAUTEURS — mesuré le 22/08 : l'année 48 px, le mois 48, le
 * souvenir 46. Deux pixels ne se voient pas seuls ; côte à côte, ils font un bord
 * qui n'est pas droit, et c'est ce que François a relevé. La hauteur est donc posée
 * UNE fois, sur les trois.
 * ⚠ ET LA VALEUR EST « --bt », LE JETON QUI EXISTE DÉJÀ (46 px) : c'est la hauteur
 * de tous les champs du site (« input[type=text] { min-height: var(--bt) } »).
 * Inventer un « --champ-h » à côté aurait fait deux jetons pour une seule idée —
 * et un repli « var(--champ-h, 48px) » aurait surtout caché le fait que personne ne
 * sait plus quelle est la vraie hauteur.
 * ⓘ La cause du 48 était le « align-items: stretch » par défaut de la rangée : le
 * champ « Année » s'étirait à la hauteur du mois, plus haut de deux pixels.
 */
/* ⚠ « align-items: center » ET UNE HAUTEUR IMPOSÉE, pas « stretch » : c'est
   « stretch » qui créait le défaut. Le champ « Année » s'étirait à la hauteur de la
   liste du mois — deux pixels de plus — au lieu de garder la sienne. Et
   « min-height » ne suffit pas : la liste habillée porte un filet de 1,5 px, donc
   elle dépassait le minimum. On POSE la hauteur, une fois, avec le jeton des
   champs. */
.frise-date { display: flex; gap: 9px; flex-wrap: wrap; margin-bottom: 9px;
              align-items: center; }
.frise-annee { flex: 0 0 92px; }
.frise-date .frise-annee,
.frise-date .sel,
.frise-date .sel-bouton,
.frise-texte { height: var(--bt); min-height: var(--bt); }
.frise-date .sel { flex: 0 0 150px; display: flex; }
.frise-date .sel-bouton { width: 100%; }
.frise-texte { width: 100%; }

/* Les trois commandes sur une seule rangée, alignées sur leur milieu.
 * ⚠ « align-items: center » et non « stretch » : le bouton d'envoi est plus haut
 * que les deux autres (il porte l'action principale), et les étirer tous à la même
 * hauteur ferait trois boutons pleins de la même importance.
 * ⚠ Et une hauteur COMMUNE leur est donnée quand même : « parfaitement alignés »
 * veut dire un bord haut et un bord bas communs, pas seulement un milieu commun. */
.frise-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 9px;
                 margin-top: 12px; }
.frise-actions .enr-bloc { margin: 0; flex-direction: row; flex-wrap: wrap;
                           gap: 9px; align-items: center; }
.frise-actions .enr-ligne { flex: 0 0 auto; }
/* ⚠ Le mot d'explication ne s'étire PLUS dans cette rangée : son
   « flex: 1 1 160px » poussait le bouton suivant à la ligne, ce qui était la cause
   des trois lignes. Il reste à sa largeur de texte, et passe à la ligne seul quand
   il n'y a plus de place. */
.frise-actions .enr-mot { flex: 0 1 auto; }
/* ⚠ « height » ET NON « min-height » : le bouton d'envoi porte son propre
   rembourrage et dépassait de cinq pixels — mesuré 38/38/43. « Parfaitement
   alignés » veut dire un bord haut ET un bord bas communs ; un minimum ne garantit
   ni l'un ni l'autre. La hiérarchie reste dite par la COULEUR (le bouton plein),
   pas par la taille. */
.frise-actions .btn,
.frise-actions .btn.plein { height: var(--bt-petit); min-height: var(--bt-petit); }

.enr-bloc { margin: 12px 0 0; display: flex; flex-direction: column; gap: 8px; }
.enr-ligne { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.enr-mot { font-size: 12.5px; color: var(--ink-soft); flex: 1 1 160px; min-width: 0; }
.enr-duree { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--ink-soft); }
.enr-effacer { border: 0; background: none; padding: 0; cursor: pointer; font: inherit;
               font-size: 12.5px; color: var(--sage); text-decoration: underline;
               text-underline-offset: 2px; }
.enr-ecoute { width: 100%; max-width: 320px; }
.enr-apercu { max-width: 180px; height: auto; border-radius: 5px;
              border: 1px solid var(--line); display: block; }
/* ⚠ Le sélecteur de fichiers reste dans le DOM, INVISIBLE : c'est le motif employé
   partout pour un composant natif porteur de valeur. Le supprimer ou le masquer par
   « display:none » l'empêcherait de s'ouvrir au clic programmé. */
.enr-fichier { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }

/* La voix et la photo D'UN SOUVENIR, dans sa carte sur la frise. */
.sv-photo { display: block; margin: 8px 0 0; max-width: 160px; height: auto;
            border-radius: 5px; border: 1px solid var(--line); cursor: zoom-in; }
.sv-photo-grande { max-width: 100%; cursor: zoom-out; }
.sv-voix { display: block; margin: 8px 0 0; width: 100%; max-width: 320px; height: 34px; }
.sv-voix-d { margin: 3px 0 0; font-size: 12px; color: var(--ink-soft); }

/* L'attente avant le jour de la fête — la ligne de vie et le nuage.
   ⚠ Une carte vide se lit comme une panne : ce paragraphe est ce qui transforme
   « il n'y a rien » en « ça vient ». Discret mais pas effacé — c'est une
   information, pas une note de bas de page. */
.attente-jour { margin: 0 0 14px; font-size: 13.5px; color: var(--ink-soft); }
.attente-jour strong { color: var(--ink); font-weight: 600; }

/* ⚠ LES ÉTIQUETTES DU NUAGE NE SERVENT QU'À MODÉRER (voir assets/anim.js). Elles
   étaient posées pour tous les invités, et une rangée de pastilles cadrées ne se
   replie pas comme un nuage de mots : elle débordait de la carte sur téléphone.
   Le repli est réaffirmé ici, pour que même en modération avec cent mots rien ne
   sorte du cadre. */
/* LE BADGE D'UNE CONTRIBUTION — une seule grammaire, deux activités.
 *
 * ⚠ UNE SEULE DÉCLARATION POUR « QUI APPORTE QUOI » ET « EN UN MOT » — 22/08,
 * demandé : « les badges de "En un mot" doivent être comme les badges de "Qui
 * apporte quoi" sans l'avatar au début ». Les deux étaient dessinés séparément —
 * l'un par un « style » en ligne dans assets/apports.js, l'autre par une taille de
 * police variable dans assets/anim.js — et ils ne se ressemblaient plus du tout.
 * Deux dessins pour une même idée divergent au premier changement, et c'est déjà
 * la raison pour laquelle anim.js n'a qu'UNE fabrique de mot appelée deux fois.
 *
 * ⚠ « inline-flex » et non « inline » : un élément en ligne avec du rembourrage se
 * coupe en deux au passage à la ligne — moitié du fond d'un côté, moitié de
 * l'autre. Une étiquette coupée en deux ressemble à un défaut d'affichage.
 */
.badge-c {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; line-height: 1.5;
  /* ⚠ UNE HAUTEUR COMMUNE, avec ou sans avatar : « comme les badges de Qui apporte
     quoi » veut dire de la même taille, sinon deux activités voisines ont deux
     tailles de pastille. Mesuré : 34 px avec l'avatar de 26, 28 sans. */
  min-height: 34px;
  padding: 3px 12px; border-radius: 999px;
  background: var(--cream); border: 1px solid var(--line);
  max-width: 100%;
}
/* Avec un avatar en tête, le rembourrage de ce côté se réduit : la pastille le
   porte déjà. Sans avatar, les deux côtés sont égaux. */
.badge-c.avec-avatar { padding-inline-start: 3px; }

/* ⚠ « .nuage-etiquette » A DISPARU — 22/08. Elle ne servait plus qu'à annuler
   l'opacité que assets/anim.js posait mot par mot pour dire la fréquence ; cette
   opacité a disparu avec le passage aux badges (la fréquence est écrite « ×3 »).
   La règle ne faisait donc plus rien, et une règle qui ne fait rien est pire qu'une
   règle absente : elle fait croire à un réglage. La classe est retirée des deux
   côtés — feuille et script — le même jour. */
/* Le pied d'une note lue : la commande de retrait à gauche, la signature à
   droite. ⚠ La cellule de gauche existe même vide — sinon la signature cesserait
   d'être poussée à droite dès que le mode modération est éteint, c'est-à-dire
   pour tous les invités. */
.lire-pied { display: flex; align-items: baseline; justify-content: space-between;
             gap: 14px; margin: 0; }
.lire-pied > p { min-width: 0; }
/* ⚠ UN LIEN, PAS UN BOUTON À L'ŒIL : ce n'est pas l'action principale de cette
   fenêtre — on vient lire un mot, pas le retirer. Souligné pour qu'on sache que
   c'est cliquable, en rouge sourd pour qu'on sache que c'est un retrait. */
/* ⚠ « --rouge-fort » ET NON « --rouge-bord » — mesuré le 22/08 dans le DOM rendu,
   en mode CLAIR : 4,41:1 pour un texte de 13,5 px, sous les 4,5:1 exigés. Le
   balayage précédent n'avait couvert que le mode sombre, et le contrôle des jetons
   (tools/contrastes.php) ne pouvait rien dire : il juge des paires, pas le rendu.
   « --rouge-bord » est fait pour une BORDURE, où 3:1 suffit ; son pire cas sur les
   seize thèmes est 4,29:1. « --rouge-fort » descend à 4,79:1 au pire. Un nom de
   jeton dit à quoi il sert : l'employer ailleurs, c'est hériter de son seuil. */
.mod-lien {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 13.5px; color: var(--rouge-fort);
  text-decoration: underline; text-underline-offset: 2px;
}
.mod-lien:hover { text-decoration-thickness: 2px; }
.mod-lien:focus-visible { outline: 2px solid var(--rouge-bord); outline-offset: 3px; }

/* ⚠ « .mod-bandeau » A ÉTÉ RETIRÉ — 21/08. C'était un panneau sombre fixé en bas
   de l'écran, qui recouvrait le contenu et inventait un troisième habit de message
   à côté des toasts et du bandeau de suppression. Le mode modération passe
   maintenant par le toast du site : un seul habit, dans le thème, et qui s'en va.
   ⚠ Ne pas le réintroduire : trois façons de dire quelque chose, c'est trois
   choses à accorder au thème, et la troisième est toujours celle qu'on oublie. */

/* --- Lecteur audio ------------------------------------------------------
 *
 * L'élément <audio> décode et joue ; ces commandes-ci sont les nôtres.
 * Le lecteur natif est un rectangle d'usine, différent sur chaque
 * navigateur. */
.lect {
  display: flex; align-items: center; gap: 11px;
  padding: 8px 12px; border-radius: 999px;
  background: var(--paper); border: 1px solid var(--line-champ, var(--line));
  /* ⚠ DE L'AIR AU-DESSUS — 21/08. Mesuré dans la fenêtre de lecture du mur :
     l'écart entre le bas de la carte du mot et le haut de la pastille valait
     ZÉRO, les deux bords se touchaient. Ce qui ressemblait à un peu d'espace
     était le rembourrage INTÉRIEUR des deux boîtes, pas leur séparation.
     14 px, et non une valeur au jugé : c'est le rythme que la fenêtre s'est
     déjà donné entre son sous-titre et son contenu (« .feuille .sous »).
     Posé sur « .lect » et non sur la fenêtre : le lecteur paraît à trois
     endroits (la lecture d'un mot, la réécoute avant envoi, la voix déjà
     déposée), et les trois collent à ce qui les précède.
     ⚠ Cela ne double NULLE PART : les marges de deux frères en flux se
     fondent, donc le « margin:0 0 8px » du paragraphe « Tu as déjà laissé ta
     voix ici » ne s'ajoute pas — il en reste 14, pas 22. */
  margin-block-start: 14px;
}
.lect audio { display: none; }
.lect-jouer {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: var(--sage); color: var(--paper);
}
.lect-jouer:hover { filter: brightness(1.08); }
.lect-jouer:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
.lect-piste {
  flex: 1 1 auto; height: 8px; min-width: 60px; border-radius: 999px; cursor: pointer;
  background: var(--line); position: relative; touch-action: none;
}
.lect-piste i {
  display: block; height: 100%; width: 0; border-radius: 999px; background: var(--sage);
}
/* La tête de lecture : sans elle, on ne devine pas que la barre se tire. */
.lect-piste::after {
  content: ""; position: absolute; top: 50%; left: 0; width: 0; height: 0;
}
.lect.lit .lect-piste i::after {
  content: ""; position: absolute; inset-inline-end: -5px; top: -3px;
  width: 14px; height: 14px; border-radius: 50%; background: var(--sage);
  border: 2px solid var(--paper);
}
.lect-piste i { position: relative; }
.lect-piste:focus-visible { outline: 2px solid var(--sage); outline-offset: 3px; }
.lect-temps {
  flex: 0 0 auto; font-family: var(--serif); font-size: 14px; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* Le champ de recherche : la croix et la loupe que WebKit ajoute lui-même
   sont grises et hors thème. La nôtre est déjà dessinée. */
input[type=search] { -webkit-appearance: none; appearance: none; }
input[type=search]::-webkit-search-decoration,
input[type=search]::-webkit-search-cancel-button,
input[type=search]::-webkit-search-results-button { -webkit-appearance: none; display: none; }

/* --- L'identité d'une structure ---------------------------------------- */
.logo-vu {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px; margin-bottom: 16px;
  background: var(--cream); border: 1px solid var(--line); border-radius: 6px;
}
.logo-vu img { max-height: 64px; max-width: 220px; display: block; }

/* Le champ de fichier natif est un bouton gris posé par le système. Il
   reste dans la page — c'est lui qui ouvre la fenêtre de l'appareil —
   mais on ne le voit pas. */
.fich-natif {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: 0;
  overflow: hidden; clip-path: inset(50%); border: 0;
}
.fich { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.fich-nom { font-size: 13.5px; color: var(--ink-soft); }

/* Le champ hexadécimal, avec sa pastille de rendu. Pas de « type=color » :
   c'est la roue du système, grise et hors thème. */
.hexa { display: flex; align-items: center; gap: 12px; }
.hexa input { flex: 1 1 auto; max-width: 190px; font-family: var(--serif); }
.hexa-vu {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 6px;
  border: 1px solid var(--line-champ, var(--line));
}
/* Aucune couleur choisie : un damier, plutôt qu'un carré blanc qu'on
   prendrait pour du blanc choisi. */
.hexa-vu.vide {
  background:
    linear-gradient(45deg, var(--line) 25%, transparent 25% 75%, var(--line) 75%) 0 0/12px 12px,
    linear-gradient(45deg, var(--line) 25%, transparent 25% 75%, var(--line) 75%) 6px 6px/12px 12px,
    var(--paper);
}
/* La marque d'une structure remplace la nôtre : c'est ce qui est vendu
   avec la formule professionnelle. Elle ne renvoie nulle part — un logo
   de client qui ramène à notre accueil serait un contresens. */
.barre-haut .marque-org { cursor: default; }
.barre-haut .marque-org img { display: block; height: 26px; width: auto; }

/* --- L'action principale, quand elle est sortie de l'écran -------------
 *
 * Une seule barre, un seul bouton : celui que la page attend vraiment.
 * Elle laisse LIBRES les deux coins du bas — c'est là que vivent les
 * pastilles cookies, accessibilité et retour en haut — et les pousse
 * quand même d'un cran tant qu'elle est là : sur un téléphone de 375 px,
 * une barre pleine largeur passe exactement par-dessus. */
/* ⚠ UNE BANDE, PLUS UNE PASTILLE FLOTTANTE — 22/08, demandé : « tous les boutons
   du bas doivent être alignés sur la même ligne (paramètres, retour, enregistrer,
   scroll-to-top) et sont situés sur la ligne des boutons retour et enregistrer ».

   La barre était posée à « bottom: 14px » et les pastilles étaient REMONTÉES
   au-dessus d'elle (voir « html.avec-barre » plus bas) : quatre commandes, trois
   hauteurs. Mesuré : centres à 37, 98 et 104 px du bas.

   ⚠ ON NE CALCULE PLUS UN « bottom », ON CENTRE SUR L'AXE. La bande fait deux
   fois « --flottant-axe » de haut et centre son contenu : le milieu des boutons
   tombe donc EXACTEMENT sur l'axe, quelle que soit leur hauteur — et elle change,
   les boutons passant au pas « petit » sous 560 px. Un « bottom » écrit en dur
   aurait été juste sur un seul des deux formats. C'est le même axe que le pied de
   page et les deux pastilles : une seule valeur, quatre usages.
   ⚠ « pointer-events » : la bande est large et invisible ; sans cela elle
   couvrirait toute la largeur de l'écran et rendrait incliquable ce qui est
   dessous. Les boutons se réarment un par un. */
.barre-flottante {
  position: fixed; z-index: var(--z-flottant);
  left: 0; right: 0; bottom: env(safe-area-inset-bottom, 0px);
  height: calc(2 * var(--flottant-axe)); pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  /* De quoi ne JAMAIS atteindre les pastilles, quelle que soit la largeur :
     le bord de page, plus la pastille, plus un peu d'air.
     ⚠ DEUX VALEURS, PAS UNE — et la seconde est MESURÉE. À gauche vit une pastille
     ronde de 44 px ; à droite « Aller à… », qui en fait 129 et dont le libellé se
     traduit, donc la largeur change avec la langue. Un seul nombre calibré sur le
     rond laissait le bouton « Enregistrer » chevaucher le sommaire de trente
     pixels sur un écran de 375 — le sommaire n'existait pas sur cette page avant
     le 31/08. assets/sommaire.js publie « --sommaire-l » ; le repli de 54 px vaut
     pour les pages qui n'en ont pas. */
  padding-inline: calc(var(--page-bord) + 54px)
                  calc(var(--page-bord) + var(--sommaire-l, 54px) + 10px);
  max-width: 100%;
  /* NI cadre NI fond depuis qu'il n'y a plus qu'un bouton : la pastille
     blanche autour dessinait un contour qui ne délimitait rien, et le flou
     d'arrière-plan coûtait une couche de composition pour rien. C'est le
     bouton lui-même qui porte son ombre — voir « .barre-flottante .btn ». */
  background: transparent; border: 0; gap: 9px;
  /* ⚠ PLUS D'ANIMATION D'ENTRÉE, ET C'EST UNE SUPPRESSION MESURÉE — 23/08.
     Elle ne s'est JAMAIS jouée. La barre est créée avec « hidden », donc
     « display: none », et l'animation restait éternellement à son premier
     instant : « getAnimations() » la donnait « running » avec
     « currentTime: 0 », indéfiniment. Son seul effet observable était donc de
     figer la barre sur son image de départ.
     Ce qui a permis de le voir : la comparaison avec #pm-reglages, qui n'a
     aucune animation et tombe pile sur l'axe (centre à 38 px du bas, comme
     --flottant-axe le promet), alors que le bouton de la barre était à 24.
     Quatorze pixels sous l'axe, sur le seul recours pour enregistrer une page
     de sept mille pixels.
     ⚠ Et la même image de départ portait un « translateX(-50%) » qui poussait
     la barre d'une DEMI-FENÊTRE vers la gauche — 579 px — donc hors de l'écran.
     Une animation qui ne se joue pas n'est pas un ornement inerte : c'est un
     style permanent qu'on croit temporaire. */
}
.barre-flottante > * { pointer-events: auto; }
/* Pas d'« opacity » dans cette animation.
   La barre est créée avec « hidden », donc « display: none » : l'animation
   ne peut pas démarrer, et si elle se retrouve figée sur son image de
   départ, une opacité de 0 rend le bouton INVISIBLE alors qu'il est censé
   être le seul recours pour enregistrer. Constaté en atelier — opacité 0
   bien après la fin des 220 ms.
   ⚠ ET LE « -50% » ÉTAIT EXACTEMENT LE MÊME DÉFAUT, EN PIRE — trouvé le 23/08 en
   mesurant. Cette barre est « left: 0; right: 0 » : elle fait toute la largeur.
   Un « translateX(-50%) » la déplace donc de LA MOITIÉ DE LA FENÊTRE vers la
   gauche — 579 px sur un écran de 1 158 — et non de quelques pixels. Relevé sur la
   page réelle, animation éteinte pour comparer : « transform: none » donne
   left = 0, l'animation figée donne left = −579. La barre était INVISIBLE, alors
   qu'elle est le seul recours pour enregistrer sur une page de sept mille pixels.
   Le « -50% » venait d'une version où la barre était « left: 50% » et devait se
   recentrer ; elle ne l'est plus depuis qu'elle est une BANDE.
   ⚠ Le commentaire du dessus l'affirmait pourtant : « une translation figée décale
   de 14 px, ce qui ne se remarque pas ». C'était vrai de l'axe Y, mesuré ; l'axe X
   ne l'avait jamais été. « Mesurer la chose visée, pas une approchante » — et une
   phrase rassurante écrite à côté du défaut l'a protégé plus sûrement qu'un
   silence. */
/* ⚠ « barre-monte » A ÉTÉ RETIRÉE — voir la note dans « .barre-flottante ». La
   règle « prefers-reduced-motion » qui l'éteignait part avec elle : elle donnait,
   sans qu'on le sache, le SEUL rendu correct de cette barre. */
/* L'ombre est portée par le BOUTON, discrètement : juste assez pour le
   détacher du contenu qui défile derrière, sans le cadre blanc qui
   l'entourait avant. */
.barre-flottante .btn {
  flex: 0 0 auto;
  box-shadow: 0 4px 16px color-mix(in srgb, var(--ink) 22%, transparent);
}
.barre-flottante .btn:hover {
  box-shadow: 0 6px 20px color-mix(in srgb, var(--ink) 28%, transparent);
}
/* Le « Retour » a besoin d'un fond opaque : la barre n'en a plus, et un
   bouton discret laissé translucide se lirait par-dessus le contenu qui
   défile derrière. Un cran plus petit que l'action principale — deux
   boutons de même poids se disputeraient le regard. */
.barre-flottante .btn.discret {
  background: var(--paper);
}

/* Le dernier champ ne se glisse pas sous la barre. */
html.avec-barre body { padding-bottom: 78px; }
/* ⚠ LES TROIS LIGNES QUI REMONTAIENT LES PASTILLES ONT DISPARU — 22/08. Elles
   posaient « bottom: 76px » et « 82px » sur #pm-reglages, #pm-haut et .sommaire
   pour les écarter de la barre. Or la barre est maintenant une bande centrée sur
   le MÊME axe : les écarter, c'est précisément casser l'alignement demandé. La
   bande réserve horizontalement la place des pastilles, ce qui rend la protection
   verticale inutile.
   ⚠ Et deux lignes « html.avec-barre #pm-cookies { bottom: …px !important } » sont
   parties avec : mesuré dans le navigateur, cette pastille est « position: static »
   depuis qu'elle vit DANS #pm-reglages — un « bottom » n'y faisait rien, pas même
   avec un !important. Une règle qui ne fait rien est pire qu'une règle absente :
   elle fait croire à une protection. */
/* ⚠ ET LE SOMMAIRE REMONTE, LUI — 31/08, et c'est le rétablissement PARTIEL de ce
   que le 22/08 avait supprimé au-dessus. Le raisonnement d'alors était juste tant
   que la pastille de droite était un rond de 40 px et que la bande ne portait
   qu'un bouton : réserver la place horizontalement suffisait.
   Mesuré le 31/08 sur un écran de 375 px, Réglages, bande visible : « Annuler » et
   « Enregistrer » font 198 px à eux deux, il ne reste que 148 px entre les deux
   réserves, et « justify-content: center » les CENTRE — un rembourrage ne rogne
   pas, il ne fait que pousser. « Enregistrer » finissait à 241 px et le sommaire
   commençait à 226 : quinze pixels de chevauchement sur le seul recours pour
   enregistrer une page de sept mille pixels.
   ⚠ La conclusion est géométrique, pas esthétique : sur 375 px, une bande de deux
   boutons et une pastille de 129 ne tiennent pas sur une ligne. On empile — la
   bande sur l'axe, le sommaire juste au-dessus — et seulement là où c'est étroit.
   Au-delà de 560 px il reste 334 px de libre, mesuré : rien à écarter.
   ⚠ « 2 × axe » est la HAUTEUR de la bande, pas un nombre choisi : le sommaire se
   pose donc exactement dessus, quelle que soit la taille des boutons. */
@media (max-width: 560px) {
  html.avec-barre .sommaire {
    bottom: calc(2 * var(--flottant-axe) + 10px);
  }
}

/* Les pastilles montent d'un cran tant que la barre est là. */
/* ⚠ La même correction que pour les deux autres pastilles : le bandeau d'effacement
   différé occupe le bas de l'écran, et une liste qui n'en cite que deux sur trois est
   presque toujours un oubli. */
html.avec-barre #pm-a11y-panneau { bottom: 190px; }

@media (max-width: 560px) {
  /* Sur téléphone, la barre s'appuie entre les deux pastilles plutôt que
     de courir d'un bord à l'autre : pleine largeur, elle les recouvrait. */
  .barre-flottante {
    /* ⚠ PLUS DE « bottom » NI DE « left: 50% » ICI : la bande occupe toute la
       largeur et se centre sur « --flottant-axe », qui vaut déjà 32 px à cette
       taille. Le seul réglage propre au téléphone est l'ÉCART RÉSERVÉ aux
       pastilles, qui y sont de 40 px et non 44 — à gauche seulement : à droite,
       c'est la largeur mesurée du sommaire qui commande. */
    padding-inline: calc(var(--page-bord) + 48px)
                    calc(var(--page-bord) + var(--sommaire-l, 48px) + 10px);
    gap: 10px;
  }
  .barre-flottante .quoi { display: none; }
  /* Sur un téléphone, la barre passe au pas « petit » : deux boutons de 46 px
     plus le clavier ne laissent plus voir le champ qu'on remplit. */
  .barre-flottante .btn { min-height: var(--bt-petit); padding: 8px 16px;
                          font-size: var(--bt-petit-t); }
  /* Mesuré à 371 px : la barre occupe 10..70 depuis le bas. À 70, les
     pastilles collaient exactement son bord supérieur — pas de
     chevauchement, mais l'air de s'être posées dessus par accident.
     Douze pixels d'écart suffisent à ce que ce soit lu comme voulu. */
  html.avec-barre #pm-a11y-panneau { bottom: 180px; }
}

/* --- Les modèles d'événement ------------------------------------------- */
.modeles { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
.modele { position: relative; }
.modele .prendre {
  display: block; padding: 14px 40px 13px 15px; text-decoration: none;
  background: var(--paper); border: 1.5px solid var(--line-champ, var(--line));
  border-radius: 8px; color: var(--ink);
  transition: border-color .15s ease, background .15s ease;
}
.modele .prendre:hover { border-color: var(--sage); background: var(--sage-voile, var(--paper)); }
.modele .prendre:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
.modele .bande { display: flex; gap: 3px; margin-bottom: 9px; }
.modele .bande i { flex: 1; height: 13px; border-radius: 2px; }
.modele strong { display: block; font-family: var(--serif); font-weight: 400; font-size: 16px; }
.modele .d { font-size: 12.5px; color: var(--ink-soft); }
/* Le retrait est discret : on vient là pour prendre un modèle, pas pour
   en supprimer un. */
.modele .oter {
  position: absolute; top: 10px; inset-inline-end: 10px;
  width: 26px; height: 26px; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: none; color: var(--ink-soft);
}
.modele .oter:hover { border-color: var(--rouge-bord); color: var(--rouge-bord); }
.modele .oter:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }

/* --- Modération des messages ------------------------------------------- */
.moderation { display: grid; gap: 10px; }
.mot {
  border: 1px solid var(--line); border-radius: 6px; padding: 12px 14px;
  background: var(--paper);
}
.mot .t { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.mot .t strong { font-family: var(--serif); font-weight: 400; font-size: 15.5px; }
.mot .q { font-size: 12.5px; color: var(--ink-soft); }
.mot .q svg { vertical-align: -2px; }
.mot .m {
  margin: 6px 0 10px; font-size: 14.5px; line-height: 1.45;
  font-family: var(--serif); font-style: italic; white-space: pre-wrap;
}
.mot form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.mot .motif { flex: 1 1 180px; min-width: 0; font-size: 13.5px; padding: 8px 11px; }
.mot .oter { flex: 0 0 auto; min-height: var(--bt-petit); padding: 8px 16px;
             font-size: var(--bt-petit-t); }
.mot .oter:hover { border-color: var(--rouge-bord); color: var(--rouge-bord); background: transparent; }

.retraits { list-style: none; padding: 0; margin: 0; font-size: 13.5px; color: var(--ink-soft); }
.retraits li { padding: 7px 0; border-top: 1px solid var(--line); }
.retraits strong { color: var(--ink); font-weight: 400; font-family: var(--serif); }
.retraits .motif-vu { font-style: italic; }
/* La date et l'heure d'un événement, sous le mot d'accueil. */
.hero .quand {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 4px 0 0; font-size: 14.5px; color: var(--ink-soft);
}
.hero .quand svg { color: var(--sage); flex: 0 0 auto; }
/* ⚠ LE RAPPEL D'AGENDA, SOUS LA DATE — 21/08. Il ne prend pas « .quand » telle
   quelle : cette règle est un « display:flex » qui aligne une icône et une
   ligne de texte, et un bouton dedans s'étirerait sur toute la largeur. Une
   ligne à lui, avec l'air qu'il faut au-dessus, et rien de plus. */
/* ⚠ AUTANT D'AIR EN DESSOUS QU'AU-DESSUS — 21/08. La règle posait « 10px 0 0 » :
   l'écart mesuré au-dessus du bouton valait 14 px (les 10 de marge plus la
   descente de la ligne de date, qui est un « inline-flex »), et l'écart en
   dessous ZÉRO — le mot d'accueil touchait le bouton. Un bouton collé au texte
   qui le suit se lit comme son étiquette, ce qu'il n'est pas.
   ⚠ 14 px et non 10 : c'est l'écart RÉELLEMENT mesuré au-dessus qu'il faut
   égaler, pas la valeur écrite au-dessus. Mesurer la chose visée, pas une
   approchante. */
/* ⚠ LA DATE ET LE BOUTON SUR UNE LIGNE — 22/08. « align-items: center » et non
   « baseline » : le bouton est une boîte, aligner sa ligne de base sur celle du
   texte le fait flotter d'un pixel ou deux vers le haut, ce qui se voit sur une
   rangée de deux éléments seulement.
   ⚠ « flex-wrap: wrap » : un créneau sur plusieurs jours plus un bouton ne tiennent
   pas sur 375 px. Il repasse dessous — l'ancien comportement, donc jamais pire. */
/* ⚠ LE TITRE ET « PROJETER » SUR UNE LIGNE — 28/08. Même raisonnement que la
   date et son bouton d'agenda dix lignes plus bas : une action qui prolonge une
   information se met à côté d'elle, pas en dessous.
   ⚠ La marge du bas passe du « h1 » à la RANGÉE, et le « h1 » est remis à zéro :
   dans un conteneur flex les marges ne se fondent pas, et laisser 6 px sous le
   titre l'aurait fait flotter de 3 px au-dessus du bouton avec
   « align-items: center ». L'écart sous le titre reste celui qui a été mesuré
   avant le changement — 6 px ici plus 6 px de « .quand-ou », soit 12 px.
   ⚠ « flex-wrap » : sur un téléphone, un titre de deux lignes et un bouton ne
   tiennent pas côte à côte. Le bouton repasse dessous. */
.hero .titre-ligne { display: flex; flex-wrap: wrap; align-items: center;
                     gap: 10px 16px; margin: 0 0 6px; }
.hero .titre-ligne h1 { margin: 0; }
/* La même taille réduite que le bouton d'agenda : à côté d'un titre de 50 px,
   c'est une commodité, pas l'action principale de la page — celle-là est d'écrire
   un mot, et elle est sur le mur. */
.hero .projeter-b { font-size: 13px; padding: 5px 13px; min-height: 34px; }
.hero .quand-ligne { display: flex; flex-wrap: wrap; align-items: center;
                     gap: 8px 14px; margin: 6px 0 14px; }
.hero .quand-ligne .quand { margin: 0; }
/* Moins haut : c'est une commodité à côté d'une date, pas une action principale.
   34 px de haut contre 40 avant — assez pour rester une cible confortable au doigt
   dans une rangée où rien d'autre n'est cliquable. */
/* ⚠ « min-height » AUTANT QUE LE REMBOURRAGE. Première version : seuls
   « font-size » et « padding » étaient réduits, et le bouton restait à 46 px —
   « .btn » pose « min-height: var(--bt-petit) », qui gagnait en silence. Mesuré
   dans le navigateur : hauteur 46 px pour un rembourrage de 5 px. Réduire un
   rembourrage sans toucher au plancher ne réduit rien. */
.hero .agenda-b { font-size: 13px; padding: 5px 13px; min-height: 34px; }
/* Le menu d'agendas garde la taille réduite du bouton qu'il remplace : c'est une
   commodité à côté d'une date, pas l'action principale de la page. */
.hero .agenda-m > button { font-size: 13px; padding: 5px 13px; min-height: 34px; }
/* Une note DANS un menu : elle explique le prix des deux derniers choix. Elle n'est
   pas cliquable, donc elle ne prend ni le survol ni le focus des liens. */
.pop-note { display: block; padding: 8px 13px 4px; font-size: 12.5px; line-height: 1.45;
            color: var(--ink-soft); border-top: 1px solid var(--line); margin-top: 4px; }
/* Le lieu porte le même habit que la date : c'est la même question. */
.hero .quand-ou { margin: 6px 0 0; }

/* --- « Comment ça marche ? » -------------------------------------------
 *
 * Le mode d'emploi d'une page se range derrière un bouton dès que
 * l'écran est petit : sur un téléphone, quatre paragraphes se lisent
 * avant la première image, et l'on venait voir des images.
 * Sur grand écran le texte reste en place — il ne gêne personne, et
 * le lire évite d'avoir à cliquer. */
.aide-quoi {
  display: none; align-items: center; gap: 8px;
  margin: 0 auto 18px; padding: 8px 16px; min-height: var(--bt-petit);
  border: 1px solid var(--line-champ, var(--line)); border-radius: 999px;
  background: var(--paper); color: var(--ink-soft);
  font: inherit; font-size: var(--bt-petit-t); cursor: pointer;
}
.aide-quoi svg { color: var(--sage); flex: 0 0 auto; }
.aide-quoi:hover { color: var(--ink); border-color: var(--sage); }
.aide-quoi:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
@media (max-width: 760px) {
  .sur-grand-ecran { display: none; }
  .aide-quoi { display: inline-flex; }
}
#dlg-aide-galerie .feuille p {
  font-size: 14.5px; line-height: 1.55; color: var(--ink-soft); margin: 0 0 14px;
}
#dlg-aide-galerie .feuille p:last-child { margin-bottom: 0; }
#dlg-aide-galerie .feuille strong { color: var(--ink); font-weight: 400; }

/* --- La zone sans retour ------------------------------------------------
 *
 * L'encadré de purge. Il ne ressemble à aucune autre carte du site, et c'est le
 * but : on doit voir qu'on change de registre avant de lire le titre. Bordure de
 * la teinte d'alerte, mesurée à 3,05 au pire contre les seize papiers — le seuil
 * d'un élément d'interface.
 */
.purge-zone {
  border: 1px solid var(--rouge);
  border-inline-start-width: 4px;
  margin-top: 34px;
}
/* ⚠ LE TITRE N'EST PAS COLORÉ, et c'est une mesure qui l'a décidé. Un rouge
   fixe ne peut pas satisfaire seize papiers : « #a1483a » tombe à 2,24 sur les
   thèmes sombres — deux en échec sur 4,5:1. Un rouge assez clair pour eux
   échouerait sur les clairs. Le registre est déjà porté par la bordure (3,05 au
   pire, seuil d'un élément d'interface), par le bouton et par les mots. */

/* Ce que la purge emporte, chiffré. Une liste sèche : ce n'est pas le moment
   d'être élégant, c'est le moment d'être exact. */
.purge-quoi { list-style: none; padding: 0; margin: 0 0 18px; }
.purge-quoi li {
  padding: 7px 0 7px 15px; position: relative;
  font-size: 14.5px; color: var(--ink); line-height: 1.4;
  border-bottom: 1px solid var(--line);
}
.purge-quoi li:last-child { border-bottom: 0; }
.purge-quoi li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 15px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--rouge);
}
.purge-quoi strong { font-family: var(--serif); font-size: 17px; }
.purge-quoi span { color: var(--ink-soft); font-size: 13px; }

/* --- Le menu et ce que chacun évite -------------------------------------
 *
 * Les rubriques du menu : un titre par service, une liste dessous. Rien
 * d'inventé — la même carte, les mêmes titres que partout ailleurs.
 */
.menu-serv { margin: 0 0 18px; }
.menu-serv:last-child { margin-bottom: 0; }
.menu-serv h3 {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--serif); font-size: 16px; font-weight: 400;
  color: var(--ink-soft); margin: 0 0 6px;
}
.menu-serv ul { list-style: none; padding: 0; margin: 0; }
.menu-serv li {
  font-family: var(--serif); font-size: 17px; color: var(--ink);
  padding: 3px 0 3px 19px; position: relative; line-height: 1.45;
}
/* Un tiret cadratin, pas une puce ronde : c'est ainsi qu'un menu s'écrit. */
.menu-serv li::before { content: "—"; position: absolute; inset-inline-start: 0; color: var(--ink-soft); }

/* Les marques d'allergie et de régime.
 *
 * ⚠ DEUX LIGNES PAR PASTILLE, et c'est nécessaire. Le nom seul ne suffit pas :
 * personne ne sait de mémoire que l'épeautre est du gluten ni que la noix de
 * cajou est un fruit à coque. Vingt-trois pastilles à deux lignes tiennent en
 * cinq colonnes sur un écran large et une sur un téléphone — c'est un
 * formulaire, pas une mosaïque : la clarté passe avant la densité.
 *
 * ⚠ L'icône décore, le mot informe. Jamais d'icône seule ici : une allergie
 * signalée par un dessin serait un pari sur l'interprétation de quelqu'un dont
 * la santé est en jeu.
 */
.marqs { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 0; }
.marq {
  display: flex; align-items: flex-start; gap: 9px; text-align: start;
  font: inherit; cursor: pointer;
  flex: 1 1 190px; max-width: 260px;
  padding: 9px 13px; border-radius: 10px;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--line-champ, var(--line));
  transition: border-color .14s ease, background .14s ease, color .14s ease;
}
.marq:hover { border-color: var(--sage); }
.marq:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
.marq[aria-pressed=true] { background: var(--sage); border-color: var(--sage); color: var(--paper); }
.marq svg { flex: 0 0 auto; margin-top: 1px; }
.marq b { display: block; font-weight: 700; font-size: 14.5px; line-height: 1.25; }
.marq i {
  display: block; font-style: normal; font-size: 12px; line-height: 1.3;
  color: var(--ink-soft); margin-top: 2px;
}
/* Sur une pastille cochée, l'aide n'a pas de couleur à elle : le gris sur le
   sage tombe sous le seuil, alors que le papier hérite du contraste que le
   thème garantit. Même leçon que le badge des goûts musicaux. */
.marq[aria-pressed=true] i { color: var(--paper); opacity: .85; }

/* Une pastille qui contient un bouton radio : elle s'allume sans JavaScript.
   « :has() » est déjà employé ailleurs dans ce fichier — même mécanique que
   « .pm-note:has(.pm-ic) ». Le radio reste dans la page, invisible, porteur de la
   valeur : le formulaire fonctionne sans script, ce qui est la règle du site. */
label.gout:has(input:checked) {
  background: var(--sage); border-color: var(--sage); color: var(--paper);
}
label.gout:has(input:focus-visible) { outline: 2px solid var(--sage); outline-offset: 2px; }
label.gout:hover { border-color: var(--sage); }

/* Ce que l'invité a déclaré, rappelé sous le menu. Discret : c'est un
   rappel, pas une annonce. */
.mien-regime {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: 14px 0 0; padding: 11px 13px;
  background: var(--cream); border: 1px solid var(--line); border-radius: 8px;
  font-size: 14px; color: var(--ink-soft);
}
.mien-regime strong { color: var(--ink); font-weight: 700; }

/* Le résumé pour la cuisine, côté organisateur. Une ligne par contrainte, le
   nombre en tête — c'est ce qu'un traiteur lit d'abord. */
.cuis { display: grid; gap: 7px; margin: 8px 0 0; }
.cuis-l {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--paper); font-size: 14.5px;
}
.cuis-n {
  flex: 0 0 auto; min-width: 26px; text-align: center;
  font-family: var(--serif); font-size: 17px; font-weight: 700; color: var(--ink);
}
.cuis-q { color: var(--ink-soft); font-size: 13px; }
/* Les allergies portent un liseré. Dans une liste où « végétarien » voisine avec
   « arachides », il aide l'œil à trouver ce qui peut envoyer quelqu'un à
   l'hôpital — mais il ne PORTE pas l'information : le mot « allergie » est écrit
   dans la ligne, faute de quoi la distinction disparaîtrait pour qui ne voit pas
   les couleurs.
   La teinte est celle que le site emploie déjà pour l'alerte, et non un jeton de
   thème — il n'y en a pas pour cela. Mesurée sur les seize thèmes contre leur
   papier : pire rapport 3,05, aucun sous 3:1, le seuil d'un élément d'interface. */
.cuis-l.alerte { border-inline-start: 3px solid var(--rouge); }

/* --- La pagination des listes -------------------------------------------
 *
 * Une seule rangée pour toutes les listes de données du site. Elle se cache
 * d'elle-même sous onze éléments : une liste de trois n'a pas besoin de six
 * commandes au-dessus d'elle.
 *
 * Les deux groupes de boutons — la façon d'afficher, le nombre par page — sont
 * des rangées segmentées, et c'est ce qui remplace le « select » interdit : une
 * liste de quatre valeurs fixes se montre, elle ne se tape pas. « aria-pressed »
 * porte l'état, faute de quoi un lecteur d'écran annoncerait quatre boutons
 * identiques.
 */
.pag {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 12px 0 4px;
}
.pag-etat { font-size: 13.5px; color: var(--ink-soft); }
/* La case « Tout voir » perd les marges verticales du formulaire : ici elle est
   dans une rangée, pas dans une pile de champs. */
.pag-tout { margin: 0; font-size: 14px; }
.pag-tout .marque { width: 19px; height: 19px; margin-top: 0; }

.pag-mode, .pag-tailles, .pag-nums { display: flex; align-items: center; gap: 0; }
.pag-mode button, .pag-tailles button, .pag-nums button {
  font: inherit; line-height: 1.2; font-size: 13.5px;
  min-height: var(--bt-petit); min-width: var(--bt-petit);
  padding: 0 12px; margin: 0;
  border: 1px solid var(--line-champ, var(--line));
  background: var(--paper); color: var(--ink-soft);
  cursor: pointer;
  transition: background .14s ease, color .14s ease;
}
/* Une seule bordure entre deux boutons voisins, et les coins arrondis
   seulement aux extrémités : c'est ce qui fait lire une rangée comme UN
   contrôle et non comme trois boutons collés. */
.pag-mode button + button, .pag-tailles button + button,
.pag-nums button + button { margin-inline-start: -1px; }
.pag-mode button:first-child, .pag-tailles button:first-child,
.pag-nums button:first-child { border-radius: 999px 0 0 999px; padding-inline-start: 15px; }
.pag-mode button:last-child, .pag-tailles button:last-child,
.pag-nums button:last-child { border-radius: 0 999px 999px 0; padding-inline-end: 15px; }
.pag-mode button:hover, .pag-tailles button:hover, .pag-nums button:hover {
  background: var(--cream); color: var(--ink);
}
.pag-mode button[aria-pressed=true], .pag-tailles button[aria-pressed=true],
.pag-nums button.on {
  background: var(--sage); color: var(--paper); border-color: var(--sage);
  position: relative; z-index: 1;
}
.pag-nums button[disabled] { opacity: .4; cursor: default; }
.pag-nums button[disabled]:hover { background: var(--paper); color: var(--ink-soft); }
.pag-saut { padding: 0 6px; color: var(--ink-soft); font-size: 13.5px; }
.pag-charger { flex: 0 0 auto; }

/* Sous 560 px, l'état passe à la ligne : six commandes et un décompte ne
   tiennent pas sur la largeur d'un téléphone, et c'est le décompte qui peut
   attendre. */
@media (max-width: 560px) {
  .pag { gap: 9px; }
  .pag-etat { flex: 1 0 100%; }
}

/* --- La photo d'un mot ---------------------------------------------------
 *
 * Trois endroits : l'aperçu avant l'envoi, la photo déjà déposée qu'on
 * revoit en reprenant sa note, et la grande dans la fenêtre de lecture.
 * Les trois ont la même contrainte — une photo de téléphone est haute, et
 * sans plafond elle pousse les boutons « Annuler / Valider » hors de
 * l'écran. On borne donc la HAUTEUR, pas la largeur.
 */
/* ⚠ Un sélecteur d'identifiant BAT la règle « [hidden] » du navigateur : sans
   cette ligne, « hidden » ne cacherait ni l'aperçu ni la photo de lecture. */
#photo-apercu[hidden], #photo-deja[hidden], .note-photo-grande[hidden] { display: none; }
#photo-apercu, #photo-deja, .note-photo-grande {
  display: block;
  width: 100%;
  max-height: 42vh;
  /* « contain » ici, contrairement au mur : on est venu LIRE ce qui est
     écrit sur le papier, et rogner un bord couperait une phrase. */
  object-fit: contain;
  object-position: center;
  margin-top: 10px;
  border-radius: 4px;
  background: var(--sage-voile);
}
/* Dans la carte de lecture, la photo est SUIVIE de la signature : la marge va
   donc dessous. Au-dessus, « lireMsg » porte déjà la sienne — et quand il n'y a
   pas de texte, il est masqué et n'en pose aucune. */
.note-photo-grande { margin-top: 0; margin-bottom: 12px;
  /* Même raison qu'en projection : dans la carte, « contain » laisse des bandes
     sur les côtés, et le papier de la note y est un meilleur fond qu'un gris. */
  background: none; }

/* --- La hauteur de ligne des familles de contrôles ----------------------
 *
 * ⚠ CETTE RÈGLE EST EN FIN DE FICHIER, et ce n'est pas un hasard.
 *
 * Deux raisons, la seconde étant la vraie :
 *   — sans « line-height », un contrôle hérite du 1,55 du corps de page : son
 *     contenu fait 24 px, son « min-height » ne gouverne plus, et il atterrit
 *     deux pixels au-dessus de son pas. Deux pixels ne se voient pas seuls ;
 *     ils se voient à côté d'un voisin qui tombe juste.
 *   — « font: inherit » RÉINITIALISE « line-height » : c'est une propriété
 *     raccourcie. Chaque famille de contrôles pose ce « font: inherit » — pour
 *     ne pas hériter de la police du système — et écrasait donc la hauteur de
 *     ligne posée plus haut dans le fichier. D'où la fin de fichier : à
 *     spécificité égale, c'est l'ordre qui tranche.
 *
 * Et « font: inherit » ici aussi : cinq boutons portaient encore la police du
 * système (Arial), invisible seulement parce qu'ils ne montrent qu'une icône.
 */
.gout, .anc, .pays-ici, .mot .oter, .aide-quoi, .q-mien-x, .carte-outil,
.tab-colonnes-titre, .perso-titre, .combo-fleche, .cal-ouvrir,
.lieux button, .jointe-ligne button,
#pm-cookies, #pm-a11y-bouton {
  font: inherit; line-height: 1.2;
}
/* Les DEUX pastilles rondes des coins d'écran : une famille à part, carrée et
   sans texte, dont 44 px est la cible recommandée au doigt.
   ⚠ Elles étaient trois — « #pm-haut » est parti le 31/08. */
#pm-cookies, #pm-a11y-bouton { font-size: var(--bt-petit-t); }
/* Une icône POSÉE DANS un champ : la flèche d'une liste, le calendrier d'une
   date. Elles étaient à 34 et 40 px dans des champs identiques. */
.combo-fleche, .cal-ouvrir { width: 36px; height: 36px; }

/* --- Les onglets des réglages ------------------------------------------
   Des pastilles, comme partout ailleurs : la rangée reprend « .rang »/« .piste »
   et défile donc déjà à 320 px. Rien de natif — des <button> dessinés au thème.
   ⚠ « [hidden] » explicite : une règle de classe l'emporte sur la règle
   navigateur, et un panneau caché resterait affiché. Leçon déjà payée. */
.onglets[hidden] { display: none; }
.onglets > .piste, .onglets > .defile-cadre > .piste { padding: 4px 0 14px; gap: 8px; }
.onglets .ong {
  display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto;
  min-height: var(--bt-petit); padding: 9px 20px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-soft);
  font: inherit; font-size: var(--bt-t); cursor: pointer;
}
.onglets .ong:hover { border-color: var(--sage); color: var(--sage); }
.onglets .ong[aria-selected="true"] {
  background: var(--sage); border-color: var(--sage); color: var(--paper);
}
.onglets .ong:focus-visible { outline: 2px solid var(--sage); outline-offset: 3px; }
/* Une section masquée par les onglets : même précaution que la rangée. */
.carte[hidden], .atelier > [hidden] { display: none; }

/* --- La vitrine des fonctions (page d'accueil) --------------------------
   Des APERÇUS, pas des icônes : un visiteur qui ne voit que le mur croit que
   le site ne sait faire qu'un mur. ⚠ Aucune couleur en dur — tout passe par
   les variables du thème, pour que ces vignettes se teintent comme le reste
   et n'échappent pas à l'audit de contraste. */
/* ⚠ « grid-auto-rows: 1fr » : TOUTES LES RANGÉES À LA MÊME HAUTEUR. Sans lui,
   chaque rangée se règle sur sa tuile la plus haute et l'on obtient trois
   hauteurs différentes — une description de deux lignes ici, de trois là. La
   scène de l'aperçu est déjà fixe ; c'est le texte qui variait. On lui réserve
   la place de trois lignes plutôt que de le rogner : mieux vaut un peu de blanc
   sous une phrase courte qu'une phrase coupée. */
.vitrine { display: grid; gap: 16px; margin-top: 16px;
           grid-auto-rows: 1fr;
           grid-template-columns: repeat(auto-fill, minmax(255px, 1fr)); }
.vitrine > * { min-width: 0; }
.vit { background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
       padding: 0 0 15px; overflow: hidden; display: flex; flex-direction: column; }
.vit > strong { font-family: var(--serif); font-weight: 400; font-size: 17px;
                margin: 13px 16px 3px; }
.vit > span { font-size: 13.5px; color: var(--ink-soft); margin: 0 16px; line-height: 1.5;
               min-height: calc(3 * 1.5em); }
/* Le QR d'aperçu : borné en hauteur, sinon son SVG occupe toute la scène. */
.vit-qr { display: flex; align-items: center; justify-content: center; }
.vit-qr svg { width: 92px; height: 92px; display: block; }
/* La scène de l'aperçu : un fond légèrement différent de la carte, pour qu'on
   voie qu'il s'agit d'une image de la chose et non de la chose. */
.vit-vue { display: flex; align-items: center; justify-content: center;
           height: 116px; padding: 12px 14px; background: var(--cream);
           border-bottom: 1px solid var(--line); }

/* la voix : une onde et une durée */
.vit-voix { display: flex; align-items: center; gap: 9px; }
.vit-jouer { width: 26px; height: 26px; border-radius: 50%; background: var(--sage);
             position: relative; flex: 0 0 auto; }
.vit-jouer::after { content: ""; position: absolute; left: 10px; top: 8px;
                    border-inline-start: 8px solid var(--paper);
                    border-top: 5px solid transparent; border-bottom: 5px solid transparent; }
.vit-onde { display: flex; align-items: center; gap: 3px; height: 32px; }
.vit-onde i { display: block; width: 3px; border-radius: 2px; background: var(--sage-pale); }
.vit-duree { font-size: 12.5px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* en un mot : un nuage à quatre tailles */
.vit-nuage { display: flex; flex-wrap: wrap; align-items: baseline;
             justify-content: center; gap: 2px 10px; font-family: var(--serif);
             line-height: 1.05; text-align: center; }
.vit-nuage .t1 { font-size: 25px; color: var(--sage); }
.vit-nuage .t2 { font-size: 18px; color: var(--sage); opacity: .85; }
.vit-nuage .t3 { font-size: 15px; color: var(--ink-soft); }
.vit-nuage .t4 { font-size: 13px; color: var(--ink-soft); opacity: .8; }

/* la ligne de vie : un rail et trois jalons datés */
/* ⚠ RÉPARTIS EN FLEX, PAS POSITIONNÉS EN POURCENTAGES. La première version
   plaçait les jalons à « left: 4% » avec « translateX(-50%) » : le libellé le
   plus à gauche sortait de 9 px hors de la scène, mesuré, et se faisait rogner.
   Un pourcentage ne connaît pas la largeur du texte qu'il centre.
   « space-between » ne peut pas déborder : le premier s'aligne à gauche, le
   dernier à droite, et rien ne dépend de la longueur des libellés. */
.vit-frise { position: relative; width: 100%; height: 66px; display: flex;
             align-items: flex-start; justify-content: space-between; }
.vit-rail { position: absolute; left: 7%; right: 7%; top: 12px; height: 2px;
            background: var(--sage-pale); border-radius: 2px; }
.vit-jalon { flex: 0 0 auto; text-align: center; white-space: nowrap; }
.vit-jalon::before { content: ""; display: block; width: 9px; height: 9px; margin: 8px auto 7px;
                     border-radius: 50%; background: var(--sage); }
.vit-jalon b { display: block; font-family: var(--serif); font-weight: 400; font-size: 13.5px; }
.vit-jalon i { display: block; font-style: normal; font-size: 11px; color: var(--ink-soft); }

/* une liste : « qui apporte quoi » et la musique */
.vit-liste { display: flex; flex-direction: column; gap: 7px; width: 100%; font-size: 13px; }
.vit-liste > span { display: flex; align-items: center; justify-content: space-between;
                    gap: 10px; padding-bottom: 6px; border-bottom: 1px solid var(--line); }
.vit-liste > span:last-child { border-bottom: 0; padding-bottom: 0; }
.vit-liste b { font-weight: 400; }
.vit-liste i { font-style: normal; color: var(--sage); font-size: 12.5px; }
.vit-liste .libre i { color: var(--ink-soft); font-style: italic; }
.vit-crans { display: flex; gap: 3px; flex: 0 0 auto; }
.vit-crans i { width: 7px; height: 7px; border-radius: 50%; border: 1px solid var(--sage-pale); }
.vit-crans i.on { background: var(--sage); border-color: var(--sage); }

/* le menu : deux services, et les allergies déclarées */
.vit-menu { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.vit-plat { display: flex; align-items: baseline; gap: 8px; font-size: 13px; }
.vit-plat i { font-style: normal; font-size: 11px; text-transform: uppercase;
              letter-spacing: .07em; color: var(--ink-soft); flex: 0 0 44px; }
.vit-plat b { font-weight: 400; }
.vit-marques { display: flex; gap: 9px; margin-top: 5px; flex-wrap: wrap; }
.vit-marque { display: inline-flex; align-items: center; gap: 4px; font-size: 11px;
              color: var(--sage); border: 1px solid var(--sage-pale);
              border-radius: 999px; padding: 2px 8px 2px 6px; }
.vit-marque i { font-style: normal; }
.vit-marque svg { flex: 0 0 auto; }

/* un sondage : la question et deux jauges */
.vit-sondage { display: flex; flex-direction: column; gap: 7px; width: 100%; }
.vit-sondage .q { font-family: var(--serif); font-size: 14.5px; }
.vit-sondage .r { display: flex; align-items: center; gap: 8px; font-size: 12px; }
.vit-sondage .r i { font-style: normal; flex: 0 0 62px; color: var(--ink-soft); }
.vit-sondage .j { flex: 1 1 auto; height: 8px; border-radius: 999px;
                  background: var(--sage-voile); overflow: hidden; }
.vit-sondage .j em { display: block; width: var(--p); height: 100%; background: var(--sage); }
.vit-sondage .r u { text-decoration: none; font-variant-numeric: tabular-nums;
                    color: var(--ink-soft); flex: 0 0 auto; }

/* la projection : un mot en grand, sur fond sombre */
.vit-projo { display: flex; align-items: center; justify-content: center; width: 100%;
             height: 100%; border-radius: 6px;
             background: color-mix(in srgb, var(--ink) 88%, transparent); padding: 10px; }
.vit-ecran { text-align: center; color: var(--paper); }
.vit-ecran b { display: block; font-family: var(--serif); font-weight: 400; font-size: 15px; }
.vit-ecran i { display: block; font-style: normal; font-size: 12px; opacity: .8;
               margin-top: 3px; }

/* --- Un texte et son voisin ---------------------------------------------
   ⚠ « .lede » reste borné à 34 em : une ligne de cent cinquante caractères ne
   se lit pas. Ce qui gênait était le VIDE à côté, pas la largeur du texte — on
   le remplit donc au lieu d'étirer les lignes. Sous 820 px il n'y a plus de
   vide à remplir : la colonne de droite passe dessous. */
.intro { display: grid; grid-template-columns: minmax(0, 1fr) auto;
         gap: 18px 30px; align-items: start; }
/* ⚠ TROIS ENFANTS : texte, visuel, texte. Sur grand écran le visuel occupe la
   colonne de droite sur les deux rangées ; les deux textes se suivent à gauche.
   Sur téléphone la grille passe à une colonne et l'ORDRE DU DOM devient l'ordre
   lu — donc le visuel se glisse entre les deux paragraphes, ce qu'aucun
   « order » n'aurait fait aussi simplement. */
.intro-tel > :nth-child(1) { grid-column: 1; grid-row: 1; }
.intro-tel > :nth-child(2) { grid-column: 2; grid-row: 1 / span 2; }
.intro-tel > :nth-child(3) { grid-column: 1; grid-row: 2; }
@media (max-width: 820px) {
  .intro { grid-template-columns: 1fr; gap: 22px; }
  .intro-tel > * { grid-column: 1 !important; grid-row: auto !important; }
}

/* le téléphone dessiné du bandeau */
.tel { width: 188px; flex: 0 0 auto; padding: 16px 13px 14px;
       border: 1px solid var(--line); border-radius: 22px; background: var(--paper);
       display: flex; flex-direction: column; align-items: center; gap: 12px;
       box-shadow: 0 3px 14px color-mix(in srgb, var(--ink) 10%, transparent); }
.tel-haut { width: 46px; height: 4px; border-radius: 3px; background: var(--line); }
/* ⚠ NI HAUTEUR IMPOSÉE NI RAPPORT CARRÉ — 20/08. « .pm-note » porte
   « aspect-ratio:1 », et cette vignette lui ajoutait une hauteur fixe : en
   pleine largeur sur téléphone, la note devenait une bande de 122 px de haut
   où l'on ne lisait plus que « Marion ». Or c'est l'image que l'on vient
   regarder pour comprendre le service. La note garde donc une largeur
   raisonnable et grandit en hauteur autant que son message l'exige. */
.tel .tel-note { position: relative; width: 148px; height: auto;
                 aspect-ratio: auto; min-height: 132px; }
/* ⚠ TROIS ICÔNES SUR LA NOTE. Les pastilles « Écrire / Dicter / Photographier »
   débordaient de la vignette, et surtout elles imitaient une barre de choix
   qu'on ne peut pas cliquer : un faux bouton sur une image est une promesse qui
   ne répond pas. Posées sur la note, les trois pictogrammes disent la même chose
   sans prétendre être une commande. */
/* ⚠ TOUT EN BAS, ET SANS PASTILLE. Les ronds blancs bordés faisaient trois
   boutons de plus — la chose qu'on venait justement de retirer — et ils
   chevauchaient la dernière ligne du message. Les pictogrammes seuls, à l'encre
   de la note et à mi-opacité, se lisent comme une mention et non comme une
   commande. La note gagne le bas de page qu'il leur faut. */
.tel-note { position: relative; padding-bottom: 42px; }
.tel-icones { position: absolute; left: 0; right: 0; bottom: 9px;
              display: flex; justify-content: center; gap: 18px; }
.tel-icones > span { display: inline-flex; color: var(--note-encre, var(--ink));
                     opacity: .48; }
/* ⚠ PLEINE LARGEUR SUR TÉLÉPHONE, et non un téléphone miniature centré : sur un
   écran de 375 px, une vignette de 260 px laisse deux marges inutiles alors que
   c'est justement l'image que l'on vient regarder. */
@media (max-width: 820px) {
  .tel { width: 100%; max-width: none; border-radius: 16px; }
  .tel .tel-note { width: min(100%, 300px); height: auto; min-height: 150px;
                   margin-inline: auto; }
  .tel-icones { gap: 22px; bottom: 11px; }
}

/* les trois gages de la section « vos données » */
/* Les trois gages : les MÊMES pastilles que les occasions, sur UNE ligne sous le
   texte. Une page cohérente réutilise ses formes. */
/* --- Les trois promesses (« En France », « Zéro traqueur », « Aucun compte ») --
 *
 * ⚠ ELLES NE SONT PLUS DES « .pastille » — 31/08, relevé par François : « les trois
 * cartes sont dans des sections à fond blanc avec bordure, et cela les confond avec
 * de vraies cartes ». Le défaut était dans le nom réutilisé : « .pastille » est la
 * carte CLIQUABLE des choix — un type d'événement, un thème, un style de note —
 * avec son fond papier, son filet, et un filet plus épais quand elle est cochée.
 * Ces trois-là ne se choisissent pas : ce sont des promesses. Leur donner l'habit
 * d'un bouton, c'est inviter à cliquer sur ce qui ne réagit pas.
 * ⚠ NI FOND NI FILET, donc : le pictogramme, le mot, la précision, posés sur le
 * fond de la page. C'est ce que le commentaire du format téléphone disait déjà de
 * son côté — « on les lit comme une liste de garanties, ce qu'elles sont ».
 */
 /* ⚠ 26 PX ET NON 18 AU-DESSUS — 31/08, demandé : « veiller à laisser suffisamment
    d'espace entre la phrase et les trois cartes ». Le bouton « Où vont vos données »
    tenait cet espace à lui seul (son paragraphe portait « margin-top: 16px ») ; en le
    retirant, la phrase et les gages se touchaient presque. Un espace tenu par un
    élément qu'on supprime n'est pas un espace, c'est une conséquence. */
.gages { display: grid; gap: 12px; margin-top: 26px; }
.gages.trois { grid-template-columns: repeat(3, 1fr); }
.gage { display: block; padding: 4px 8px; text-align: center; cursor: default; }
.gage .gros { font-size: 21px; display: block; margin-bottom: 4px; color: var(--sage); }
.gage strong { display: block; font-family: var(--serif); font-weight: 400;
               font-size: 16px; margin-bottom: 1px; }
.gage > span:last-child { display: block; color: var(--ink-soft); font-size: 12.5px; }
/* ⚠ SUR TÉLÉPHONE, TROIS RANGÉES COMPACTES ET NON TROIS CARTES EMPILÉES. À
   375 px, trois cartes côte à côte donnent 110 px chacune : ni le pictogramme ni
   les deux lignes de texte n'y tiennent. Et empilées telles quelles — icône
   centrée au-dessus de deux lignes, trois fois — elles font un bloc plus haut
   que l'écran pour trois promesses courtes. Icône à gauche, texte à droite : on
   les lit comme une liste de garanties, ce qu'elles sont. */
@media (max-width: 620px) {
  .gages.trois { grid-template-columns: 1fr; gap: 10px; }
  .gage { display: grid; grid-template-columns: auto 1fr;
          gap: 0 13px; text-align: start; align-items: center; padding: 4px 2px; }
  .gage .gros { grid-row: 1 / span 2; margin: 0; }
  .gage strong { align-self: end; }
  .gage > span:last-child { align-self: start; }
}

/* --- La fenêtre « Voir un exemple » -------------------------------------
   Sur ORDINATEUR une fenêtre centrée ; sur TÉLÉPHONE le plein écran, et la
   croix devient une flèche de retour. Le même élément, deux CSS : un second
   composant aurait divergé du premier au bout de trois lots. */
.dlg-exemple { border: 0; padding: 0; background: transparent;
               max-width: min(940px, 94vw); width: 100%; max-height: 92vh; }
.dlg-exemple::backdrop { background: color-mix(in srgb, var(--ink) 52%, transparent); }
.dlg-exemple-tete { display: flex; align-items: center; gap: 12px;
                    padding: 13px 16px; background: var(--paper);
                    border: 1px solid var(--line); border-bottom: 0;
                    border-radius: 10px 10px 0 0; position: sticky; top: 0; z-index: 2; }
.dlg-exemple-tete strong { font-family: var(--serif); font-weight: 400; font-size: 17px;
                           flex: 1 1 auto; }
.dlg-retour { display: none; }
.dlg-exemple-corps { background: var(--cream); border: 1px solid var(--line);
                     border-top: 0; border-radius: 0 0 10px 10px;
                     padding: 16px; overflow-y: auto; max-height: calc(92vh - 58px); }
.dlg-exemple-avis { margin: 0 0 14px; font-size: 13px; color: var(--ink-soft);
                    border-inline-start: 2px solid var(--sage-pale); padding-inline-start: 11px; }
/* ⚠ DU BLANC AU-DESSUS QUAND CE N'EST PAS LE PREMIER BLOC. Deux des trois
   espacements signalés le 20/08 venaient des onglets cassés — les trois vues
   étaient empilées, donc « Mes événements » suivait la carte du lieu et
   « Administration » suivait un encart. Les onglets réparés, elles ne se
   touchent plus. Celui-ci reste utile : dans une même vue, un en-tête qui suit
   un bloc doit respirer. */
.ex-tete { margin-bottom: 12px; }
.dlg-exemple-corps > * + .ex-tete { margin-top: 26px; }
/* Un titre de rôle qui suit un encart : même raison, même mesure. */
.dlg-exemple-corps .ex-note + * { margin-top: 22px; }
.ex-titre { display: block; font-family: var(--serif); font-size: 24px; line-height: 1.15; }
.ex-sous { display: block; font-size: 13px; color: var(--ink-soft); margin-top: 3px; }
.ex-mur { background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
          overflow: hidden; }
.ex-deux { display: grid; gap: 14px; margin-top: 14px;
           grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.ex-bloc > .ex-lib { display: block; font-size: 11.5px; text-transform: uppercase;
                     letter-spacing: .09em; color: var(--ink-soft); margin-bottom: 6px; }
.ex-fiche { background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
            padding: 14px 16px; min-height: 96px; display: flex; flex-direction: column;
            justify-content: center; gap: 4px; }
.ex-fiche b { font-family: var(--serif); font-weight: 400; font-size: 16px; }
.ex-fiche i { font-style: normal; font-size: 14px; color: var(--ink-soft); }
.ex-fiche u { text-decoration: none; font-size: 12px; color: var(--ink-soft); opacity: .8;
              margin-top: 4px; }
/* ⚠ Le plein écran du téléphone. « inset: 0 » et des dimensions en pourcentage :
   une <dialog> centrée par le navigateur ne s'étend pas d'elle-même. */
@media (max-width: 620px) {
  .dlg-exemple { max-width: none; width: 100%; height: 100%; max-height: none;
                 inset: 0; margin: 0; }
  .dlg-exemple-tete { border-radius: 0; border-inline-start: 0; border-inline-end: 0; }
  .dlg-exemple-corps { border-radius: 0; border-inline-start: 0; border-inline-end: 0;
                       max-height: calc(100vh - 58px); min-height: calc(100vh - 58px); }
  .dlg-exemple-tete .pm-fermer { display: none; }
  .dlg-retour { display: inline-flex; align-items: center; gap: 7px; font: inherit;
                font-size: 14px; color: var(--sage); background: transparent;
                border: 1px solid var(--line); border-radius: 999px;
                padding: 7px 14px; cursor: pointer; }
  .dlg-exemple-tete strong { font-size: 15px; }
}

/* --- Une rangée d'actions -------------------------------------------------
   ⚠ Dans un <p>, des boutons se suivent au fil du texte : alignés par hasard
   sur grand écran, et empilés à trois largeurs différentes sur téléphone, le
   bord droit en dents de scie. Ici ils partagent une ligne sur grand écran et
   la MÊME largeur sur téléphone. */
.actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
           margin-top: 26px; }
@media (max-width: 620px) {
  .actions { flex-direction: column; align-items: stretch; }
  .actions > .btn { width: 100%; }
}

/* --- Les trois exemples de la fenêtre -----------------------------------
   Les corps de section réutilisent les aperçus de la vitrine : ils ont été
   dessinés pour ressembler à la chose, il n'y a rien à redessiner. */
.dlg-exemple-corps .onglets > .piste,
.dlg-exemple-corps .onglets > .defile-cadre > .piste { padding: 0 0 14px; }
.ex-note { margin: 14px 0 0; font-size: 13px; color: var(--ink-soft); line-height: 1.55;
           border-inline-start: 2px solid var(--sage-pale); padding-inline-start: 11px; }
.ex-aller { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.ex-aller i { font-style: normal; font-size: 13px; }
.ex-aller span { display: flex; gap: 6px; flex-wrap: wrap; }
.ex-aller em { font-style: normal; font-size: 12px; color: var(--sage);
               border: 1px solid var(--line); border-radius: 999px; padding: 4px 11px; }

/* l'espace de l'organisateur */
.ex-ev { background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
         padding: 13px 15px; margin-bottom: 10px; display: grid; gap: 6px 14px;
         grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
.ex-ev-t b { font-family: var(--serif); font-weight: 400; font-size: 16.5px; }
.ex-ev-t i { font-style: normal; font-size: 12.5px; color: var(--ink-soft); margin-inline-start: 8px; }
/* ⚠ PLUS DE PASTILLE POUR UN STATUT — 20/08. Le cadre arrondi faisait
   ressembler « passé » à une étiquette cliquable ou à un filtre actif ; ce
   n'est ni l'un ni l'autre. En gras, de la couleur dominante du thème quand
   l'événement vient, en gris quand il est passé — et ce gris est une variable
   de thème, donc il s'éclaircit sur les thèmes sombres au lieu d'y disparaître. */
.ex-ev-e { font-size: 12.5px; font-weight: 700; color: var(--sage);
           justify-self: end; white-space: nowrap; }
.ex-ev-e.passe { color: var(--ink-soft); }
.ex-ev-c { display: flex; align-items: center; gap: 9px; font-size: 12px;
           grid-column: 1 / -1; }
.ex-ev-c .j { flex: 0 0 130px; height: 8px; border-radius: 999px;
              background: var(--sage-voile); overflow: hidden; }
.ex-ev-c .j em { display: block; width: var(--p); height: 100%; background: var(--sage); }
.ex-ev-c u { text-decoration: none; color: var(--ink-soft); }
.ex-ev-b { display: flex; gap: 6px; flex-wrap: wrap; grid-column: 1 / -1; }
.ex-ev-b em { font-style: normal; font-size: 12px; color: var(--sage);
              border: 1px solid var(--line); border-radius: 999px; padding: 5px 12px; }
.ex-ev-m { grid-column: 1 / -1; font-size: 12.5px; color: var(--ink-soft); }

/* l'écran de l'administrateur */
.ex-chiffres { display: flex; flex-wrap: wrap; gap: 8px; }
.ex-chiffre { background: var(--paper); border: 1px solid var(--line); border-radius: 8px;
              padding: 9px 14px; text-align: center; min-width: 84px; }
.ex-chiffre b { display: block; font-family: var(--serif); font-weight: 400; font-size: 20px; }
.ex-chiffre i { display: block; font-style: normal; font-size: 11.5px; color: var(--ink-soft); }
.ex-table { border-collapse: collapse; width: 100%; font-size: 13px; }
.ex-table th, .ex-table td { text-align: start; padding: 9px 14px;
                             border-bottom: 1px solid var(--line); }
.ex-table th { font-family: var(--serif); font-weight: 400; color: var(--ink-soft); }
.ex-table tr:last-child td { border-bottom: 0; }
.ex-table td { color: var(--ink-soft); }
@media (max-width: 620px) {
  .ex-ev { grid-template-columns: 1fr; }
  .ex-ev-e { justify-self: start; }
}

/* ⚠ « .lede » garde ses 34 em PARTOUT SAUF ICI. Une ligne de cent cinquante
   caractères ne se lit pas — mais « .wrap » borne déjà la page à 940 px, et un
   paragraphe de trois lignes qui annonce trois cartes posées dessous doit couvrir
   la même largeur qu'elles. Une exception nommée valant mieux qu'une règle
   affaiblie. */
.lede.pleine { max-width: none; }

/* --- Lisible sur téléphone -----------------------------------------------
   ⚠ Les tailles étaient calculées pour le confort du grand écran : « .lede » à
   15 px, les textes secondaires à 12,5 et 13,5. Sur un téléphone tenu à
   quarante centimètres, ce sont les tailles d'une note de bas de page. Elles
   montent d'un cran et demi — la lecture d'abord, la densité ensuite.
   La note du bandeau reçoit des tailles EXPLICITES : la sienne venait d'un
   « clamp » en vw plafonné à 13 px, prévu pour une vignette de mur, pas pour
   l'image qu'on vient regarder. */
@media (max-width: 620px) {
  .lede { font-size: 16.5px; line-height: 1.62; }
  h2 { font-size: 22px; }
  .vit > strong { font-size: 17.5px; }
  .vit > span { font-size: 14.5px; }
  .pastille strong { font-size: 16px; }
  .pastille > span:last-child { font-size: 13.5px; }
  .fleur span { font-size: 12.5px; letter-spacing: .14em; }
  .tel .tel-note { height: 176px; padding-bottom: 48px; }
  .ex-note, .dlg-exemple-avis { font-size: 14px; }
  .ex-fiche i { font-size: 15px; }
  .ex-titre { font-size: 22px; }
  .ex-sous { font-size: 14px; }
}

/* ⚠ LA NOTE DU BANDEAU PORTE SES PROPRES TAILLES, et il faut trois classes pour
   les imposer : « postits.css » est chargé APRÈS « site.css », donc à
   spécificité égale c'est lui qui gagne. Ses tailles — « calc(clamp(5px,
   1.05vw, 11.5px) × échelle) » — sont calculées pour une vignette de mur à six
   colonnes ; appliquées à l'image du bandeau, elles donnaient un message de
   6 px sur téléphone. Mesuré, pas supposé. */
.tel .tel-note .pm-qui { font-size: 17px; }
.tel .tel-note .pm-txt { font-size: 15px; line-height: 1.4; }
@media (max-width: 620px) {
  .tel .tel-note .pm-qui { font-size: 18px; }
  .tel .tel-note .pm-txt { font-size: 16px; line-height: 1.45; }
  .pastille .sous, .pastille > span:last-child { font-size: 13.5px; }
  .pastille strong { font-size: 16px; }
}

/* --- Le retour de l'administrateur ---------------------------------------
   Flottant en bas à gauche, au-dessus de tout : sur la page d'un invité comme
   sur un écran de projection, il n'existe aucune barre où le poser. Il ne
   s'affiche que si « ?de=admin » est dans l'adresse — voir lib/retour-admin.php,
   qui explique pourquoi un paramètre plutôt que la session. */
.retour-admin { position: fixed; inset-inline-start: 16px; bottom: 16px; z-index: 60;
                display: inline-flex; align-items: center; gap: 8px;
                min-height: var(--bt-petit); padding: 8px 16px; border-radius: 999px;
                background: var(--paper); color: var(--sage);
                border: 1px solid var(--sage); text-decoration: none;
                font-size: var(--bt-petit-t);
                box-shadow: 0 2px 10px color-mix(in srgb, var(--ink) 18%, transparent); }
.retour-admin:hover { background: var(--sage); color: var(--paper); }
/* ⚠ La projection est un écran noir : le bouton clair y disparaîtrait. Couleurs
   figées ici, et c'est voulu — la projection n'a pas de thème, elle a du noir. */
.retour-admin.sombre { background: rgba(255,255,255,.14); color: #fff;
                       border-color: rgba(255,255,255,.5); }
.retour-admin.sombre:hover { background: rgba(255,255,255,.28); color: #fff; }
/* Sur téléphone, les trois pastilles d'accessibilité occupent déjà ce coin. */
@media (max-width: 620px) { .retour-admin { inset-inline-start: 12px; bottom: 12px; padding: 7px 13px; } }

/* ⚠ LE GRAS D'UN PARAGRAPHE SECONDAIRE PREND L'ENCRE PLEINE. « .lede » est en
   « --ink-soft » : un « <strong> » qui hérite de cette couleur pèse plus sans
   ressortir davantage — on obtient du gris épais, pas de l'emphase. L'encre
   franche fait la différence, et elle gagne du contraste au lieu d'en perdre. */
.lede strong { color: var(--ink); font-weight: 600; }

/* Le rouge en CONTOUR : pour ce qui coûte sans détruire. Le plein reste à ce
   qui efface — trois degrés, et chacun garde son sens. */
.btn.danger-contour { border-color: var(--rouge); color: var(--rouge); background: transparent; }
.btn.danger-contour:hover { background: var(--rouge); color: var(--paper); border-color: var(--rouge); }

/* --- Les cellules d'un tableau : deux lignes, puis une bulle ---------------
   ⚠ « Manoir de Procé — Nantes » s'étalait sur CINQ lignes dans sa colonne, et
   la rangée entière prenait la hauteur du plus bavard : une ligne de données
   haute de cent pixels pour un tableau qu'on parcourt du regard.
   Deux lignes maximum, puis une bulle au survol et au clavier. Le contenu est
   enveloppé par tableaux.js dans un « .cel » — un « display:-webkit-box » posé
   sur le <td> lui-même casserait la mise en tableau. */
.cel { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
       line-clamp: 2; overflow: hidden; }
.cel.coupee { cursor: help; }
table.liste td, .ex-table td { max-width: 26ch; }
/* La bulle : dessinée, jamais l'infobulle du navigateur — « title » est un
   composant natif, impossible à teindre et lent à paraître. */
.bulle { position: fixed; z-index: 90; max-width: min(320px, 82vw);
         background: var(--ink); color: var(--paper); font-size: 13px; line-height: 1.45;
         padding: 8px 12px; border-radius: 6px; pointer-events: none;
         box-shadow: 0 3px 14px color-mix(in srgb, var(--ink) 34%, transparent); }
.bulle[hidden] { display: none; }

/* --- Le QR à faire scanner, depuis le mur d'un invité --------------------
   Il n'existait que dans les réglages de l'organisateur. Or « faire circuler »,
   dans une salle, c'est tendre son écran au voisin. */
.dlg-qr { border: 0; padding: 0; background: transparent; max-width: min(420px, 92vw); }
.dlg-qr::backdrop { background: color-mix(in srgb, var(--ink) 58%, transparent); }
.dlg-qr-tete { display: flex; align-items: center; gap: 12px; padding: 13px 16px;
               background: var(--paper); border: 1px solid var(--line); border-bottom: 0;
               border-radius: 10px 10px 0 0; }
.dlg-qr-tete strong { font-family: var(--serif); font-weight: 400; font-size: 17px;
                      flex: 1 1 auto; }
/* ⚠ FOND BLANC, ET NON LE PAPIER DU THÈME. Le contraste d'un QR n'est pas une
   question de goût : il est lu d'un écran à l'autre, à travers deux dalles et un
   reflet. Le blanc est la seule valeur qui ne coûte rien à la lecture. */
/* ⚠ BLANC EN DUR, ET IL DOIT LE RESTER — même en mode sombre. Un QR code se lit
   par le contraste de ses modules : posé sur un fond sombre, il ne se scanne plus.
   Ce n'est pas une couleur de thème, c'est une contrainte optique. */
.dlg-qr-cadre { background: #fff; border: 1px solid var(--line); border-top: 0;
                padding: 18px; display: flex; justify-content: center; }
.dlg-qr-cadre img { width: min(320px, 74vw); height: auto; display: block; }
.dlg-qr-adr { margin: 0; padding: 10px 16px 2px; background: var(--paper);
              border: 1px solid var(--line); border-top: 0; border-bottom: 0;
              font-size: 13.5px; text-align: center; word-break: break-all;
              /* Sélectionnable : quelqu'un préférera toujours taper l'adresse. */
              user-select: all; }
.dlg-qr-aide { margin: 0; padding: 4px 16px 16px; background: var(--paper);
               border: 1px solid var(--line); border-top: 0; border-radius: 0 0 10px 10px;
               font-size: 12.5px; color: var(--ink-soft); text-align: center;
               line-height: 1.5; }

/* ======================================================================
   Demandé le 20/08/2026 — trois règles qui se tiennent par la main
   ====================================================================== */

/* --- 1. AUCUNE BARRE DE DÉFILEMENT VISIBLE, NULLE PART ------------------
 *
 * ⚠ Ceci retire un indice, il faut donc le remplacer — voir la règle 2.
 * Une barre de défilement dit deux choses : « ça continue » et « où l'on
 * en est ». En la masquant on garde le défilement (molette, doigt,
 * clavier, barre d'espace) et l'on perd l'indice. L'estompe ci-dessous
 * rend le premier ; le second n'existe que pour les rangées, où il ne
 * manque pas.
 *
 * « scrollbar-width » couvre Firefox et les navigateurs récents,
 * « ::-webkit-scrollbar » couvre Safari et Chrome. Les deux sont
 * nécessaires : aucun ne suffit seul. */
* { scrollbar-width: none; }
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* --- 2. L'ESTOMPE AUX DEUX BOUTS D'UNE RANGÉE QUI DÉFILE ----------------
 *
 * Le cadre est posé par assets/rangs.js autour de chaque rangée défilante,
 * et il porte deux classes que le même fichier tient à jour :
 * « a-gauche » quand il reste quelque chose à gauche, « a-droite » à
 * droite. Sans classe, aucune estompe — un contenu qui tient en entier
 * n'est jamais voilé, ce qui était le reproche fait aux deux versions
 * précédentes.
 *
 * ⚠ L'estompe est posée sur le CADRE, jamais en masque sur le contenu :
 * elle passe AU-DESSUS d'un dégradé du fond vers le transparent, elle ne
 * gomme aucun bouton. Et « pointer-events:none » : on doit pouvoir
 * cliquer le bouton qui se trouve dessous. */
.defile-cadre { position: relative; }
/* ⚠ DISCRÈTE, ET C'EST UNE CORRECTION DU 20/08 : la première version faisait
   34 px avec une bande PLEINE sur ses douze premiers pour cent, et François l'a
   trouvée trop marquée — à raison, elle éteignait le bouton du bord au lieu de
   le laisser filer. 22 px, et un dégradé qui part de la couleur du fond SANS
   palier : au plus fort il masque le tiers d'une lettre. */
.defile-cadre::before,
.defile-cadre::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 22px;
  pointer-events: none; opacity: 0; transition: opacity .18s ease;
  z-index: 2;
}
.defile-cadre::before {
  left: 0;
  background: linear-gradient(to right, var(--pm-fond, var(--paper)), transparent);
}
.defile-cadre::after {
  right: 0;
  background: linear-gradient(to left, var(--pm-fond, var(--paper)), transparent);
}
.defile-cadre.a-gauche::before,
.defile-cadre.a-droite::after { opacity: 1; }
/* Une rangée posée sur une carte crème, et non sur le papier : l'estompe
   doit partir de la couleur qu'elle recouvre, sinon elle se voit comme une
   bande claire. */
.carte .defile-cadre, .feuille .defile-cadre { --pm-fond: var(--paper); }
.cadre-creme .defile-cadre, .abo .defile-cadre { --pm-fond: var(--cream); }

/* --- 3. LE MENU DE DÉPLACEMENT DANS LA PAGE -----------------------------
 *
 * ⚠ CECI REMPLACE LA RANGÉE D'ANCRES, et renverse deux décisions écrites :
 * le repli à la ligne en dessous de quatre ancres, et l'idée qu'un bouton
 * est la bonne forme pour une ancre. François a tranché le 20/08 : se
 * déplacer dans une page est un CHOIX dans une liste, pas une série
 * d'actions — et une liste ne prend qu'une ligne, quel que soit le nombre
 * de parties.
 *
 * Même mécanique que « Mentions légales » en pied de page : un bouton, un
 * « popover » natif, des liens dedans. Le navigateur gère l'ouverture, le
 * clic dehors, la touche Échap et le calque — trois tentatives en
 * JavaScript n'avaient pas suffi à le faire de façon fiable ailleurs dans
 * ce site, la leçon est reprise ici.
 *
 * La DIRECTION est calculée par assets/popmenu.js : vers le bas s'il y a
 * la place, vers le haut sinon. Un menu qui sort de l'écran est un menu
 * qu'on ne peut pas lire. */
.pop-menu { display: inline-flex; }
.pop-menu > button {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 15px; min-height: var(--bt-petit); border-radius: 999px;
  border: 1px solid var(--line-champ, var(--line));
  background: var(--paper); color: var(--ink); cursor: pointer;
  font: inherit; font-size: var(--bt-petit-t);
}
.pop-menu > button:hover { border-color: var(--sage); color: var(--sage); }
.pop-menu > button:focus-visible { outline: 2px solid var(--sage); outline-offset: 2px; }
/* ⚠ LA ROTATION NE VISE QUE LE CHEVRON, JAMAIS « svg ». Ces boutons portent
   DEUX pictogrammes : une icône de tête et le chevron. « … svg » les tournait tous
   les deux — sur le mur, la flèche de « Y aller » et le calendrier d'« Ajouter à
   mon agenda » partaient à l'envers dès l'ouverture du menu, et les puces de
   l'icône « liste » d'« Aller à… » passaient à droite.
   ⚠ ET PAS « svg:last-child » : viser par la POSITION est la faute que ce dépôt a
   payée trois fois le 31/08 (« form.querySelector('input') », « f.action »,
   « querySelector('span') »). Le chevron porte une CLASSE, posée là où il est
   dessiné — e.php, sommaire.js, choix.js, pied-legal.php. */
.pop-menu > button .pm-chevron,
.pied-menu button .pm-chevron,
.sel-bouton .pm-chevron { transition: transform .18s ease; }
.pop-menu > button[aria-expanded="true"] .pm-chevron { transform: rotate(180deg); }

.pop-liste, .pop-liste:popover-open {
  position: fixed; margin: 0; inset: auto; z-index: 40;
  display: flex; flex-direction: column; min-width: 232px;
  max-width: min(92vw, 340px); max-height: 70vh; overflow-y: auto;
  padding: 7px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 34px color-mix(in srgb, var(--ink) 24%, transparent);
}
.pop-liste:not(:popover-open):not([hidden]) { display: none; }
.pop-liste::backdrop { background: transparent; }
/* ⚠ « .som-haut » EST DANS LA MÊME DÉCLARATION QUE LES LIENS, pas dans une
   déclaration jumelle. Première version : une règle à part qui reprenait « la même
   facture » à la main avec « font: inherit ». Mesuré le 31/08 dans le menu du mur :
   16 px au lieu de 14, noir pur au lieu de « --ink-soft », interligne 24,8 au lieu
   de 16,8, hauteur 43 au lieu de 37 — « Début de page » sortait plus gros et plus
   noir que les six lignes en dessous, et François l'a vu tout de suite.
   « font: inherit » sur un bouton hérite du CONTENEUR, jamais des voisins qu'on
   voulait imiter. *Deux déclarations pour une même apparence divergent au premier
   changement ; celle-ci divergeait dès la naissance.* */
.pop-liste a, .pop-liste .som-haut {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 13px; border-radius: 6px; font-size: 14px;
  color: var(--ink-soft); text-decoration: none; text-align: start;
  /* Ce qu'un bouton apporte de trop et qu'il faut défaire : la mise du système,
     son fond, sa bordure et sa largeur d'ajustement. */
  font-family: inherit; font-weight: 400; line-height: 1.2;
  width: 100%; margin: 0; border: 0; background: none; cursor: pointer;
}
.pop-liste a:hover, .pop-liste .som-haut:hover { background: var(--cream); color: var(--sage); }
.pop-liste a:focus-visible, .pop-liste .som-haut:focus-visible {
  outline: 2px solid var(--sage); outline-offset: -2px;
}
.pop-liste a svg, .pop-liste .som-haut svg { flex: 0 0 auto; }
/* ⚠ LE LIBELLÉ PREND CE QUI RESTE, LA MARQUE GARDE SA PLACE. Sans « min-width:0 »,
   un libellé long ne peut pas se couper et pousse la marque hors de la boîte : la
   règle vaut pour les items du sommaire, dont les titres viennent des pages et font
   parfois quarante signes (« Les thèmes conseillés, occasion par occasion »). */
.pop-liste .som-l { min-width: 0; }
/* Un drapeau comme marque de tête : il ne s'étire ni ne se comprime, comme un
   pictogramme. La règle générale « .drapeau » le dit déjà, on ne la répète pas —
   sauf ce qu'elle ne dit pas : dans une liste, il ne doit jamais recevoir de texte
   (voir le défaut consigné dans assets/sommaire.js). */

/* --- UN SEUL BOUTON FLOTTANT POUR LES RÉGLAGES DU SITE ------------------
 *
 * ⚠ Deux pastilles empilées dans le coin gauche — accessibilité et
 * données — ressemblaient sur un téléphone à un défaut d'affichage plus
 * qu'à deux commandes. Elles sont réunies sous un bouton unique, qui les
 * déplie VERS LE HAUT : le groupe est en bas de l'écran, un tiroir vers le
 * bas sortirait de la fenêtre.
 *
 * Le tiroir est AVANT le bouton dans le document et la colonne se lit de
 * haut en bas : les réglages apparaissent donc au-dessus du bouton sans
 * qu'aucune position n'ait à être calculée. */
#pm-reglages {
  position: fixed; inset-inline-start: var(--page-bord); z-index: var(--z-flottant);
  bottom: calc(var(--flottant-axe) - var(--flottant-taille) / 2);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
#pm-reglages-bouton {
  width: var(--flottant-taille); height: var(--flottant-taille);
  border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--paper); color: var(--sage);
  box-shadow: 0 2px 10px var(--ombre);
  transition: transform .15s ease, background .15s ease, color .15s ease;
  font: inherit; line-height: 1.2;
}
#pm-reglages-bouton:hover { transform: scale(1.08); }
#pm-reglages.ouvert #pm-reglages-bouton {
  background: var(--sage); color: var(--paper); border-color: var(--sage);
}
#pm-reglages-bouton:focus-visible { outline: 2px solid var(--sage); outline-offset: 3px; }
#pm-reglages-tiroir {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  opacity: 0; transform: translateY(10px);
  transition: opacity .18s ease, transform .18s ease;
}
#pm-reglages.ouvert #pm-reglages-tiroir { opacity: 1; transform: none; }
/* ⚠ Deux identifiants, et c'est voulu : la feuille de pied-cookies.php est
   insérée APRÈS celle-ci, et une règle à un seul « #pm-cookies » y
   perdrait. Deux identifiants passent devant sans avoir besoin d'un
   « !important » — la spécificité, pas le poids. */
#pm-reglages #pm-a11y-bouton, #pm-reglages #pm-cookies {
  position: static; left: auto; right: auto; top: auto; bottom: auto;
}
@media print { #pm-reglages { display: none !important; } }

/* --- RIEN AU-DESSUS D'UNE FENÊTRE OUVERTE ------------------------------
 *
 * ⚠ Le calque nommé ne suffit pas, et les captures du 20/08 le montrent : les
 * pastilles flottantes restaient visibles par-dessus le voile d'une fenêtre.
 * Même rangées sous les menus, elles sont posées en « position:fixed » — donc
 * insensibles au voile, qui n'assombrit que ce qu'il recouvre.
 *
 * La garantie est donc écrite comme telle : dès qu'une fenêtre est ouverte,
 * les pastilles s'effacent. « :has() » lit l'état réel du document plutôt
 * qu'une classe qu'un script aurait à poser — et un script qui oublie de la
 * retirer laisse le site à moitié éteint.
 *
 * ⚠ DEUX FORMES, PAS TROIS. « body:has(:popover-open) #pm-haut » était dans
 * cette liste et n'avait rien à y faire : le retour en haut disparaissait dès
 * qu'on ouvrait le menu des mentions légales. Un popover natif n'est PAS une
 * fenêtre — les trois du site (.pied-liste, .pop-liste, .sel-liste) ont un
 * « ::backdrop » explicitement transparent. Il n'y a donc aucun voile
 * au-dessus duquel la pastille pourrait flotter, et rien à masquer.
 *
 * ⚠ Deux mesures l'ont établi plutôt qu'un raisonnement. D'abord : le popover
 * des mentions légales occupait [591, 655, 827, 866] et la pastille
 * [834, 868, 878, 912] — ils ne se TOUCHENT même pas, et la pastille passait
 * pourtant à « opacity: 0 ». Ensuite, pour le cas où ils se chevaucheraient un
 * jour : en déplaçant la pastille exprès sous le popover,
 * « document.elementFromPoint » au point de recouvrement rend un lien DU
 * POPOVER. Le calque supérieur du navigateur passe devant un « position:fixed »
 * quel que soit son z-index — un chevauchement ne cacherait donc rien non plus.
 *
 * ⚠ Et l'asymétrie disait déjà que la ligne n'était pas voulue : « #pm-reglages »
 * n'était PAS dans la clause « :popover-open », seul « #pm-haut » l'était. Si un
 * popover justifiait vraiment d'effacer les pastilles, il les effacerait toutes
 * les deux. */
/* ⚠ « :not([hidden]) » N'EST PAS UNE PRÉCAUTION, C'EST LA CONDITION. Première
   version : « body:has(#pm-a11y-voile) ». Le voile est créé une fois au
   chargement et masqué par l'attribut « hidden » — il est donc TOUJOURS dans le
   document, et la règle éteignait le bouton de réglages en permanence. Mesuré
   sur l'accueil : « visibility:hidden » avant même d'avoir rien ouvert.
   Un sélecteur d'existence là où il fallait un sélecteur d'ÉTAT. */
/* ⚠ « .sommaire » AJOUTÉ LE 22/08, le jour où il est devenu flottant. Le
   commentaire ci-dessus dit qu'une liste asymétrique trahit un oubli : elle en
   comptait deux sur trois dès que la troisième pastille a existé.
   ⚠ ET « #pm-haut » EN EST PARTI LE 31/08 avec la pastille elle-même. La liste
   compte donc DEUX éléments sur deux : c'est encore une liste complète, et c'est
   ce qu'il faut vérifier quand une pastille naît ou disparaît. */
body:has(dialog[open]) #pm-reglages,
body:has(dialog[open]) .sommaire,
body:has(#pm-a11y-voile:not([hidden])) #pm-reglages,
body:has(#pm-a11y-voile:not([hidden])) .sommaire {
  opacity: 0; pointer-events: none; visibility: hidden;
}

/* --- LES MENUS QUI S'OUVRENT VERS LE HAUT -------------------------------
 *
 * La classe est posée par assets/direction.js quand il n'y a pas la place
 * en dessous. ⚠ Chaque menu a son ancrage propre — « top:100% » pour l'un,
 * « top:calc(100% + 6px) » pour l'autre — donc chacun a besoin de sa règle :
 * une seule règle générique laisserait un menu accroché en haut ET en bas.
 * Le « auto » sur la propriété d'origine est indispensable, sinon les deux
 * s'appliquent et le menu s'étire. */
/* « .sel-liste » a quitté ce jeu de règles : elle vit dans le calque supérieur
   et c'est choix.js qui la place. Sa règle « vers-haut » a été retirée plutôt
   que laissée morte — une règle qui ne s'applique plus est un piège pour qui
   la lit. */
.combo-liste.vers-haut { top: auto; bottom: calc(100% + 5px); }
.cal.vers-haut { top: auto; bottom: calc(100% + 6px); }
.qr-fmt-menu.vers-haut { top: auto; bottom: calc(100% + 6px); }
.tab-colonnes .choix.vers-haut { top: auto; bottom: calc(100% + 6px); }
.ic-panneau.vers-haut { top: auto; bottom: calc(100% + 6px); }
/* L'ombre suit le sens : une ombre portée vers le bas sous un menu qui monte
   le fait flotter à l'envers. */
.sel-liste.vers-haut, .combo-liste.vers-haut, .cal.vers-haut,
.qr-fmt-menu.vers-haut, .tab-colonnes .choix.vers-haut, .ic-panneau.vers-haut {
  box-shadow: 0 -10px 26px color-mix(in srgb, var(--ink) 16%, transparent);
}

/* Le nombre de lignes au bout d'un titre de section : la même encre douce que
   les légendes, et jamais en gras — c'est une précision, pas une annonce. */
.h2-compte { font-family: var(--sans); font-size: .62em; font-weight: 400;
             color: var(--ink-soft); }

/* --- Les pictogrammes d'activité d'un tableau ---------------------------
 *
 * Une pastille de nombre n'apparaît que là où le nombre ajoute quelque chose :
 * « 4 morceaux », « 3 lieux ». Une pastille « 1 » ne dirait rien de plus que le
 * pictogramme lui-même — c'est pourquoi event_activites_pictos() ne la remplit
 * pas dans ce cas, plutôt que de la cacher ici : on ne dessine pas ce qu'on ne
 * veut pas montrer. */
.picto-act { position: relative; display: inline-flex; margin-inline-end: 5px;
             vertical-align: -2px; }
.picto-nb {
  position: absolute; inset-inline-end: -5px; top: -5px;
  min-width: 13px; height: 13px; padding: 0 2px;
  border-radius: 999px; background: var(--sage); color: var(--paper);
  font-family: var(--sans); font-size: 9px; font-weight: 700; line-height: 13px;
  text-align: center;
}

/* L'infobulle d'une icône seule. Posée par assets/infobulle.js.
   ⚠ « --z-modale » et non « --z-menu » : une infobulle peut naître AU-DESSUS
   d'un menu ouvert — le pictogramme d'une liste déroulante, par exemple — et
   une bulle sous ce qu'elle explique n'explique rien. */
.infobulle {
  position: fixed; z-index: var(--z-modale); pointer-events: none;
  max-width: min(260px, 80vw); padding: 6px 10px;
  border-radius: 6px; background: var(--ink); color: var(--paper);
  font-family: var(--sans); font-size: 12.5px; line-height: 1.35;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--ink) 28%, transparent);
}
/* ⚠ « help » SEULEMENT SUR CE QUI N'EST PAS CLIQUABLE. La règle valait pour tout
   porteur d'infobulle, et elle BATTAIT « .barre-nuit { cursor: pointer } » à
   spécificité égale (une classe contre un attribut) en étant écrite plus loin :
   le bouton clair/sombre montrait donc un point d'interrogation là où celui des
   langues montrait une main. Mesuré, pas déduit — « getComputedStyle » rendait
   « help ». Un bouton qui FAIT quelque chose se désigne à la main ; « help » est
   pour le pictogramme qui n'explique que lui-même. */
[data-infobulle]:not(button):not(a):not([role="button"]) { cursor: help; }

/* --- Le bouton qui coûte sans détruire ----------------------------------
 *
 * ⚠ CONTOUR, JAMAIS PLEIN, et c'est un arbitrage déjà tranché une fois : le
 * plein rouge est réservé à ce qui EFFACE. Se déconnecter ne détruit rien —
 * mais cela coûte la session, un lien magique à redemander et un courriel à
 * attendre. Le contour avertit sans crier.
 * La teinte ne vient pas du thème : un avertissement qui change de couleur
 * selon le thème n'est plus un avertissement. */
.btn.contour-rouge {
  border-color: var(--rouge-fort); color: var(--rouge-fort); background: var(--paper);
}
.btn.contour-rouge:hover {
  background: var(--rouge-fort); color: var(--sur-rouge); border-color: var(--rouge-fort);
}
.btn.contour-rouge svg { color: currentColor; }

/* --- Les cases à cocher d'un réglage, sur deux colonnes ------------------
 *
 * ⚠ « align-items:start » est ce qui les aligne à GAUCHE ET EN HAUT dans leur
 * cellule : sans lui, une case dont le libellé tient sur deux lignes étire
 * toute la rangée et sa voisine se centre verticalement — deux cases côte à
 * côte dont les carrés ne sont pas à la même hauteur se lisent mal.
 * Le « margin-top » qui espaçait la première case est retiré : c'est la grille
 * qui espace maintenant, et un décalage sur une seule case désalignerait sa
 * colonne. */
.cases-deux {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 26px; align-items: start; margin-top: 22px;
}
/* ⚠ UN PLANCHER DE 44 px SUR LA RANGÉE, ET C'EST UNE MESURE, PAS UN CONFORT.
   Relevé à 375 px le 31/08 : les libellés courts donnaient une cible de 23 px de
   haut — UN pixel sous le minimum de WCAG 2.2 AA (2.5.8 : 24 × 24 px). Depuis que
   ces cases s'enregistrent seules, un doigt qui rate la rangée ne rate plus un
   simple cochage : il touche la rangée voisine, et le réglage part aussitôt.
   ⚠ 44 et non 24 : les libellés longs faisaient DÉJÀ 45 px, donc le plancher rend
   la colonne régulière au lieu de l'allonger — un minimum choisi sur ce qui existe
   coûte zéro pixel de plus.
   ⚠ « align-items: flex-start » est conservé : un libellé sur deux lignes doit
   s'aligner en haut avec sa case, pas se centrer. */
.cases-deux > .champ-choix { margin: 0; min-height: 44px; }
@media (max-width: 700px) {
  /* Deux colonnes de libellés entiers ne tiennent pas : on empile. */
  .cases-deux { grid-template-columns: 1fr; gap: 10px; }
}

/* --- CE QUI DÉTRUIT SE VOIT ---------------------------------------------
 *
 * ⚠ Demandé le 20/08 : « tous les bandeaux de suppression ou purge doivent être
 * rouges ». Ils avaient l'apparence d'un formulaire ordinaire — seul le bouton
 * final était rouge, et on ne le voyait qu'après avoir lu. La bordure et le
 * titre le disent avant la lecture, ce qui est l'ordre dans lequel on regarde.
 * La teinte ne vient pas du thème : un avertissement qui change de couleur selon
 * le thème n'est plus un avertissement. */
.destructif { border-color: var(--rouge-bord) !important; }
.destructif > .feuille, .destructif .feuille { border-color: var(--rouge-bord); }
.destructif h2 { color: var(--rouge-fort); }
.destructif::before {
  content: ''; display: block; height: 3px; margin: -24px -24px 18px;
  background: var(--rouge-bord); border-radius: 5px 5px 0 0;
}
dialog.destructif .feuille::before {
  content: ''; display: block; height: 3px; margin: -22px -24px 18px;
  background: var(--rouge-bord);
}

/* Le bandeau des trente secondes : posé par assets/suppression.js, au-dessus de
   tout puisqu'il annonce l'irréversible. */
/* ⚠ MÊME GÉOMÉTRIE QUE LE TOAST DU SITE — accordé le 21/08 sur demande : « tous
   les toasts doivent être identiques et dans le thème ». Même bas (26 px), même
   largeur maximale (420 px), même rayon que « .flash.avec-bouton » (16 px), même
   rembourrage. Trois messages qui se ressemblent presque sont plus dérangeants
   que trois messages différents : l'œil cherche la règle et ne la trouve pas.
   ⚠ SEULE LA COULEUR DIFFÈRE, ET ELLE PORTE UN SENS : le rouge dit « quelque
   chose va être détruit ». La variante « calme » (un archivage) reprend, elle,
   exactement l'encre du toast — c'est le même objet, avec la même voix. */
.bandeau-suppr {
  position: fixed; z-index: var(--z-modale); left: 50%; bottom: 26px;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  width: max-content; max-width: min(92vw, 420px); padding: 14px 18px;
  background: var(--rouge-bord); color: var(--sur-rouge); border-radius: 16px;
  font-size: 14.5px; line-height: 1.4;
  box-shadow: 0 10px 30px color-mix(in srgb, var(--ink) 34%, transparent);
}
.bandeau-suppr .btn {
  background: var(--sur-rouge); border-color: var(--sur-rouge); color: var(--rouge-bord); flex: 0 0 auto;
}
.bandeau-suppr .btn:hover { background: var(--sur-rouge); filter: brightness(.94); }
/* ⚠ « CALME » : LE ROUGE EST RÉSERVÉ À CE QUI DÉTRUIT — 21/08. Le même bandeau
   sert maintenant à l'archivage programmé, qui ne perd rien : il RANGE. Lui
   donner le rouge de la suppression apprendrait à ignorer le rouge, et c'est le
   rouge qui doit encore alarmer le jour où il annonce une purge de compte.
   L'encre du site, donc : sérieux sans être alarmant. */
/* ⚠ AVEC UN REPLI, et ce n'est pas de la superstition : « --ink » n'est PAS
   déclaré dans cette feuille — il vient de theme_css() (lib/themes.php), écrit en
   ligne par chaque page. Les trois pages de l'organisateur l'émettent bien
   (vérifié), mais un bandeau en « position:fixed » dont le fond serait vide
   afficherait du texte blanc sur la page : illisible, et sur le seul message qui
   annonce qu'une chose va disparaître. */
.bandeau-suppr.calme { background: var(--ink, #2f2b27); color: var(--cream); }
.bandeau-suppr.calme .btn { color: var(--ink, #2f2b27); }

/* La note qui accompagne un plafond atteint. Posée par assets/limites.js.
   ⚠ Elle n'est PAS rouge : une limite par personne n'est pas une erreur, c'est
   une règle du jeu — et la peindre en rouge ferait croire à une faute de
   l'invité. Le gris des légendes, avec le bouton éteint à côté, suffit. */
.limite-note {
  margin: 8px 0 0; font-size: 13px; color: var(--ink-soft); line-height: 1.45;
}
/* deploi-20260823-094726 */
