/* ============================================================================
   responsive-fix.css — CORRECTIF D'AFFICHAGE (18/07/2026)
   Chargé si « site.responsive.fix_on » = 1, ou en aperçu avec ?respfix=1.
   Centre de contrôle → « Appli & mobile » → « Correctif d'affichage ».
   Toutes les règles sont préfixées « html.respfix ».
   ⚠️ FICHIER GÉNÉRÉ — éditer _fix/*.css puis reconcaténer.
   ============================================================================ */


/* ================= 00-socle.css ================= */

/* ==========================================================================
   00 — SOCLE PARTAGÉ
   --------------------------------------------------------------------------
   Tout est préfixé « html.respfix » : sans la classe sur <html>, ce fichier
   ne peut RIEN changer. L'interrupteur admin site.responsive.fix_on (ou
   ?respfix=1) pose la classe. Éteindre = retour exact à l'état d'avant.

   Corrige R2, R3, R4, R5, R6, R14 (causes de fond communes à tout le site).
   ========================================================================== */

/* --------------------------------------------------------------------------
   R4 — UNE SEULE SOURCE DE VÉRITÉ POUR LES DIMENSIONS DU CHROME
   Avant : 12 nombres concurrents (78 / 90 / 150 / 80 / 96 / 76vh / 84vh…)
   écrits à la main dans 8 fichiers. Aucun ne valait la vraie hauteur.
   Ici : des valeurs de REPLI, remplacées en direct par la mesure JS
   (responsive-fix.js) qui observe les vrais éléments.
   -------------------------------------------------------------------------- */
html.respfix {
  --hdr-h: 64px;                                                  /* .pub-topbar — VARIE (1 à 3 rangées) */
  --bottomnav-h: calc(64px + env(safe-area-inset-bottom, 0px));   /* .bottom-nav — mesurée ~64px, pas 78 */
  --fab-h: 52px;                                                  /* hauteur d'une pastille flottante */
  --fab-l-w: 180px;                                               /* « Aide & visite guidée » */
  --fab-r-w: 70px;                                                /* « Le Gardien » */

  /* Réserves DÉDUITES — plus aucun nombre en dur ailleurs. */
  --reserve-bas-desktop: calc(var(--fab-h) + 28px);
  --reserve-bas: calc(var(--bottomnav-h) + var(--fab-h) + 20px);

  /* Structure */
  --pub-rail-w: clamp(96px, 7vw, 124px);

  /* R6 — ÉCHELLE D'EMPILEMENT UNIQUE ET DOCUMENTÉE.
     Avant : 26 valeurs au hasard, de 6 à 2147483000. */
  --z-fond: -1;         /* image de fond fixe */
  --z-contenu: 1;       /* cartes, halos, contenu */
  --z-page: 50;         /* absolus internes à une page */
  --z-barres: 400;      /* meubles fixes : barre du haut, rail, barre du bas */
  --z-menus: 500;       /* déroulants du chrome */
  --z-flottants: 600;   /* pastilles fixes : Gardien, Aide */
  --z-voiles: 700;      /* voiles plein écran */
  --z-panneaux: 710;    /* fenêtre posée SUR son voile */
  --z-toast: 800;       /* messages passagers */
  --z-alertes: 900;     /* bandeaux critiques */
}
@media (max-width: 1200px) { html.respfix { --pub-rail-w: 96px; } }

/* --------------------------------------------------------------------------
   R3 — overflow-x:hidden → clip
   « hidden » crée un conteneur de défilement : il CASSE position:sticky sur
   tous les descendants (barre du haut, rail) ET masque silencieusement les
   débordements — c'est lui qui rendait le Passeport injoignable sans que
   personne ne le voie. « clip » coupe pareil MAIS ne casse pas sticky.
   Mesuré avant correctif : header 1495px de contenu pour 1334px de place,
   Passeport à 110px hors écran, aucune barre de défilement pour y accéder.
   -------------------------------------------------------------------------- */
html.respfix, html.respfix body { overflow-x: clip; max-width: 100%; }

/* --------------------------------------------------------------------------
   R6 — RÉAFFECTATION DES COUCHES (aucun nombre en dur, que des variables)
   -------------------------------------------------------------------------- */
html.respfix .pub-topbar { z-index: var(--z-barres); }
html.respfix .pub-rail { z-index: var(--z-barres); }
html.respfix .bottom-nav { z-index: var(--z-barres); }
html.respfix .nav-main { z-index: var(--z-barres); }

html.respfix .pub-lang-menu,
html.respfix .phare-menu,
html.respfix .pub-notif-panel,
html.respfix .pub-cdp-panel { z-index: var(--z-menus); }
html.respfix .pub-beta-badge::after { z-index: var(--z-menus); }

html.respfix .gw-launcher,
html.respfix .obw-launch { z-index: var(--z-flottants); }
html.respfix .gw-scrim,
html.respfix .obw-scrim,
html.respfix .ob-tour-scrim,
html.respfix .ro-overlay { z-index: var(--z-voiles); }
html.respfix .gw-panel,
html.respfix .obw-panel,
html.respfix .ro-panel,
html.respfix .bn-more-sheet { z-index: var(--z-panneaux); }
html.respfix .bn-more-backdrop { z-index: var(--z-voiles); }
html.respfix .ob-toast { z-index: var(--z-toast); }
html.respfix #fdcn-maj-banner,
html.respfix .dev-banner,
html.respfix .dev-toggle { z-index: var(--z-alertes); }

/* .pub-topbar porte backdrop-filter:blur(12px) → c'est un CONTEXTE
   D'EMPILEMENT à lui seul, quel que soit son z-index. Ses menus internes ne
   peuvent donc jamais dépasser la barre. Pour qu'un menu ouvert passe devant
   le Gardien et la barre du bas, on remonte la BARRE ENTIÈRE d'un cran.
   Classe posée en JS (pas de :has() — voir R1). */
html.respfix .pub-topbar.is-menu-open { z-index: var(--z-menus); }

/* La rustine du 18/07 (.pub-phare-wrap{z-index:7}) faisait juste passer le
   Phare AU-DESSUS du badge Bêta : le recouvrement restait, seul le clic était
   rendu. Le groupe étant remis dans le flux (R1), elle devient inutile. */
html.respfix .pub-phare-wrap { z-index: auto; }

/* --------------------------------------------------------------------------
   R1 — LE HEADER REDEVIENT UNE VRAIE BARRE EN FLUX
   Cause : .pub-cdp-group (les 4 gros boutons) était sorti du flux
   (position:absolute + translate(-50%,-50%)) — il ne réservait donc AUCUNE
   place. 12 rustines empilées tentaient de rattraper ça, dont une qui ne
   marchait que sur les navigateurs comprenant :has().
   Ici : remise en flux INCONDITIONNELLE (aucune dépendance à :has(), aucune
   exception pour page-dashboard) + retour à la ligne contrôlé.
   -------------------------------------------------------------------------- */
html.respfix .pub-topbar {
  flex-wrap: wrap;
  row-gap: 8px;
  column-gap: clamp(8px, 1vw, 14px);
  align-items: center;
  padding-block: clamp(9px, 1vh, 13px);
  padding-inline: clamp(12px, 1.6vw, 22px);
}
/* min-width:0 : sans lui, un enfant flex refuse de descendre sous sa taille
   de contenu et pousse la rangée en débordement. */
html.respfix .pub-topbar > * { min-width: 0; }

/* Le groupe des 4 boutons : DANS le flux, sur toutes les pages, toujours. */
html.respfix .pub-cdp-group {
  position: static;
  left: auto; top: auto;
  transform: none;
  z-index: auto;
  flex: 0 1 auto;
  min-width: 0;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(6px, .6vw, 10px);
}

/* LE NOM DU SITE NE PEUT PLUS ÊTRE ÉCRASÉ.
   Mesuré avant : largeur 0px pour un texte de 222px → il se dessinait
   entièrement PAR-DESSUS le champ de recherche. C'est ce que montrent les
   captures. Cause : flex-shrink par défaut + min-width:0 + white-space:nowrap
   et AUCUN contrôle de débordement.
   Correctif : il ne rétrécit plus (flex-shrink:0) ; et si un jour il devait
   quand même manquer de place, il se coupe proprement avec « … » au lieu de
   déborder sur le voisin. */
html.respfix .pub-topbrand {
  flex: 0 0 auto;
  min-width: 0;
  max-width: min(38ch, 34vw);
}
html.respfix .pub-topbrand-text { min-width: 0; }
html.respfix .pub-topbrand-main,
html.respfix .pub-topbrand-sub {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html.respfix .pub-topbrand-main { font-size: clamp(14px, 1.05vw, 16px); }

html.respfix .pub-search {
  flex: 1 1 200px;
  min-width: 160px;
  max-width: 460px;
}
html.respfix .pub-actions {
  flex: 0 0 auto;
  margin-left: auto;
  gap: clamp(5px, .5vw, 8px);
}

/* ---- TRÈS grand écran (≥2100px) : une seule rangée.
   Seuil MESURÉ, pas choisi au hasard : le contenu de la barre demande
   ~2027px (nom 222 + recherche 460 + 4 boutons 814 + commandes 531 + espaces).
   Avec le rail (96px) et les marges, il faut ~2160px pour une seule rangée.
   Annoncer 1601px aurait été un mensonge : à 1920px ça ne tient pas, et
   c'est précisément pour ça que l'ancienne version débordait de 192px
   en silence derrière overflow:hidden. ---- */
@media (min-width: 2100px) {
  html.respfix .pub-cdp-group { flex: 0 1 auto; order: 3; }
  html.respfix .pub-actions { order: 4; }
}

/* ---- Ordinateur (901–2099px) : DEUX rangées maîtrisées.
   Rangée 1 : nom + recherche + Phare/cloche/passeport (les commandes).
   Rangée 2 : les 4 gros boutons, centrés, sur toute la largeur.
   C'est la seule façon honnête de faire tenir ~2027px de contenu dans
   1334px : on ne cache aucun bouton, on descend d'une ligne. ---- */
@media (min-width: 901px) and (max-width: 2099px) {
  html.respfix .pub-topbrand { order: 1; }
  html.respfix .pub-search   { order: 2; }
  html.respfix .pub-actions  { order: 3; }
  html.respfix .pub-cdp-group {
    order: 4;
    flex: 1 1 100%;
    margin-inline: 0;
    justify-content: center;
  }
  /* Les libellés restent VISIBLES (on ne repasse pas en icône seule :
     la place existe maintenant qu'ils ont leur propre rangée). */
  html.respfix .pub-cdp-group .pub-entraide,
  html.respfix .pub-cdp-group .pub-report-top,
  html.respfix .pub-cdp-group .pub-coupdepropre,
  html.respfix .pub-cdp-group .pub-addplace {
    padding: 10px clamp(12px, 1.2vw, 20px);
    gap: 8px;
    font-size: clamp(13px, .95vw, 15px);
    line-height: 1;
  }
  html.respfix .pub-cdp-group svg { width: 18px; height: 18px; }
  /* Le nom du membre revient : il y a la place sur la rangée 1. */
  html.respfix .pub-profile .nm { display: block; }
}

/* Le badge Bêta ne peut plus chevaucher l'engrenage : il est dans le flux,
   il ne rétrécit pas, et il garde son propre espace. */
html.respfix .pub-beta-badge {
  flex: 0 0 auto;
  align-self: center;
  margin-inline-start: 2px;
}

/* --------------------------------------------------------------------------
   R2 — LA BANDE MORTE 320–900px
   La règle header.php:544 s'ouvre à min-width:320px alors que son propre
   commentaire annonce 600px : elle déverse ~20 !important sur TOUS les
   téléphones et entre en conflit direct avec mobile-app.css.
   On lui rend sa borne annoncée en neutralisant sa partie basse.
   (!important nécessaire : les règles d'origine en portent déjà.)
   -------------------------------------------------------------------------- */
/* TROIS rangées, pas quatre : le nom seul, puis les raccourcis ET les
   commandes qui SE PARTAGENT une rangée, puis la recherche pleine largeur.
   Mesuré : donner sa propre rangée à .pub-actions faisait passer le header
   de 179px à 229px sur tablette — 50px volés au contenu pour rien. */
@media (max-width: 900px) {
  html.respfix .pub-topbar { flex-wrap: wrap !important; }
  html.respfix .pub-topbrand { flex: 1 1 100% !important; order: 1 !important; max-width: 100%; }
  /* (18/07) Sous 900px, la barre de PASTILLES (.pub-mbar) prend le relais : elle propose
     deja Entraide / Ajouter un lieu / Coup de propre / Signaler en icones seules.
     Les 4 gros boutons faisaient donc DOUBLE EMPLOI et occupaient deux rangees entieres.
     La regle qui les masquait vivait dans header.php, dans le bloc desactive quand ce
     correctif est allume : elle est reprise ici. */
  html.respfix .pub-cdp-group { display: none !important; }
  html.respfix .pub-mbar     { order: 2 !important; flex: 1 1 auto !important; width: auto !important;
                               min-width: 0 !important; margin: 2px 0 0 !important; }
  html.respfix .pub-actions  { order: 3 !important; flex: 0 0 auto !important; width: auto !important;
                               margin-left: auto !important; justify-content: flex-end !important;
                               flex-wrap: nowrap !important; overflow-x: auto; }
  html.respfix .pub-search   { order: 4 !important; flex: 1 1 100% !important; max-width: 100% !important; }
  /* iPhone : une police <16px déclenche le zoom automatique au focus. */
  html.respfix .pub-search input { font-size: 16px; }
}

/* --------------------------------------------------------------------------
   R14 — RAIL GAUCHE : largeur élastique, libellés jamais rognés
   Avant : 96px figés + un max-width:86px sur le libellé (borne inatteignable,
   donc code mort) et aucun retour à la ligne → « Voyageurs » s'affichait
   « oyageurs », « Compagnons » → « mpagnons ».
   -------------------------------------------------------------------------- */
/* ⚠️ UNIQUEMENT ≥901px. Sous 900px le rail est masqué (public-v802.css:187) et
   .pub-shell repasse à UNE colonne : forcer 2 colonnes ici enfermerait tout le
   contenu dans la colonne de 96px du rail (mesuré : barre du haut réduite à
   96px de large sur un écran de 768px). */
/* ⚠️ NE JAMAIS REDÉCLARER grid-template-columns SUR .pub-shell.
   Erreur commise le 18/07 et corrigée le jour même — à ne pas refaire :
   la règle d'origine (public-v802.css:35) utilise DÉJÀ var(--pub-rail-w) ; il
   suffit donc de redéfinir la variable (plus haut) pour rendre le rail élastique.
   Redéclarer les colonnes ici était non seulement inutile, mais destructeur :
   ça écrasait la protection du site (includes/header.php:385)
       .pub-shell:not(:has(> .pub-rail)){grid-template-columns:1fr}
   qui repasse à UNE colonne quand le rail n'est pas rendu — ce qui est le cas
   pour tout VISITEUR NON CONNECTÉ. Résultat mesuré en production : .pub-main
   tombait dans la colonne de 96px du rail absent, et la page entière était
   réduite à un ruban de 96px de large sur toutes les pages publiques. */
@media (min-width: 901px) {
  html.respfix .pub-rail { overflow-x: clip; }
}
html.respfix .pub-rail-item { min-width: 0; }
html.respfix .pub-rail-item span {
  max-width: 100%;
  min-width: 0;
  overflow-wrap: break-word;
  hyphens: auto;
  white-space: normal;
  text-align: center;
  font-size: clamp(10px, .72vw, 11.5px);
  line-height: 1.15;
}

/* --------------------------------------------------------------------------
   R5 — RÉSERVE D'ESPACE EN BAS
   Avant : elle n'existait PAS sur ordinateur (la règle ne visait que ≤768px)
   et elle était absente entre 769 et 900px. Résultat : « Le Gardien » et
   « Aide & visite guidée » se posaient sur le contenu.
   Ici : réserve sur TOUTES les tailles, calculée depuis les hauteurs
   réellement mesurées, jamais depuis un nombre écrit à la main.
   -------------------------------------------------------------------------- */
html.respfix .pub-content,
html.respfix main.container,
html.respfix .site-footer {
  padding-bottom: calc(var(--reserve-bas-desktop) + env(safe-area-inset-bottom, 0px));
}
@media (max-width: 900px) {
  html.respfix .pub-content,
  html.respfix main.container,
  html.respfix .site-footer {
    padding-bottom: calc(var(--reserve-bas) + env(safe-area-inset-bottom, 0px));
  }
}

/* --------------------------------------------------------------------------
   GARDE-FOU GLOBAL — plus aucun média ne peut élargir la page.
   -------------------------------------------------------------------------- */
html.respfix img,
html.respfix video,
html.respfix canvas,
html.respfix iframe { max-width: 100%; }

/* ================= 20-carte.css ================= */

/* ==========================================================================
   20 — PAGE CARTE (R7)
   --------------------------------------------------------------------------
   Tout est préfixé « html.respfix » ET scopé « body.page-carte » : sans la
   classe sur <html>, ce fichier ne peut RIEN changer, et il ne peut jamais
   déborder sur une autre page.

   Les quatre défauts traités (audit R7) :
     (a) carte.php:110-114 — .pub-content.container{padding:0 !important}
         battait les trois réserves du site. La carte était la SEULE page sans
         filet en bas : « Le Gardien » et « Aide & visite guidée » se posaient
         sur la barre du Gardien du Feu.
     (b) carte.php:127 — height:76vh ne déduisait ni la barre du haut (qui
         mesure de 91 à 213px selon l'écran) ni la barre du Gardien, qui est
         placée HORS du shell (carte.php:1777).
     (c) carte.php:125 — grid-template-columns:300px 1fr 320px, colonnes figées.
     (d) carte.php:816 — .carte-right{display:none} SUPPRIMAIT un accès sous
         1200px au lieu de le replier.

   Ce que ce fichier NE refait PAS (déjà traité par 00-socle.css) : la barre du
   haut, le rail gauche, overflow-x, l'échelle d'empilement, les réserves.
   ========================================================================== */

/* --------------------------------------------------------------------------
   JETONS LOCAUX À LA PAGE
   --hdr-h, --reserve-bas*, --z-* viennent du socle : on ne les redéfinit pas.
   --carte-bar-h et --carte-outils-h sont MESURÉS en direct par le petit script
   de carte.php (gate respfix_on()), exactement comme le socle mesure --hdr-h.
   Les valeurs ci-dessous ne sont que des REPLIS : elles servent avant que le
   script ait mesuré, et si JavaScript est indisponible.
   ⚠️ Le script écrit ces variables sur <body> ; elles sont donc déclarées ici
   sur le MÊME élément, sinon la déclaration écraserait la mesure.
   -------------------------------------------------------------------------- */
html.respfix body.page-carte {
  --carte-bar-h: 96px;      /* .carte-gardien--bar — repli mesuré ~96px sur ordinateur */
  --carte-outils-h: 0px;    /* hauteur du bouton tiroir (0 quand il n'y en a pas) */

  /* Repli 100vh, remplacé par 100dvh quand le navigateur sait le faire.
     100dvh = hauteur RÉELLE hors barres du navigateur mobile ; 100vh est faux
     sur iOS/Android tant que la barre d'URL est déployée. */
  --carte-vh: 100vh;

  /* La réserve du bas, reprise du socle, avec l'encoche des téléphones.
     Elle sert DEUX fois et c'est volontaire :
       — en padding-bottom de la page  → le filet manquant (défaut a) ;
       — en déduction de la hauteur du shell → la barre du Gardien du Feu
         tient dans l'écran dès l'arrivée, sous les pastilles flottantes.
     Aucun double comptage : en-tête + shell + barre + réserve = une hauteur
     d'écran — TANT QUE le plancher ci-dessous n'entre pas en jeu.
     ⚠️ MESURÉ, à ne pas enjoliver : sur un téléphone (≤414px), l'en-tête monte
     à ~228px et la barre du Gardien à ~182px, soit 410px de meubles sur 740 à
     896px d'écran. Le plancher du plan l'emporte alors et la page défile ; au
     repos les deux pastilles flottent donc par-dessus la barre du Gardien.
     Vérifié au banc : dès qu'on défile, le recouvrement retombe à 0 partout et
     la barre est entièrement lisible — la réserve joue son rôle, aucun accès
     n'est perdu. Le vrai levier est en amont (hauteur de l'en-tête), pas ici :
     le seul moyen de supprimer ce recouvrement au repos serait de ramener le
     plan à ~194px de haut sur un téléphone, ce qui rendrait la carte — le cœur
     de la page — inutilisable. On assume le défilement. */
  --carte-reserve: calc(var(--reserve-bas-desktop) + env(safe-area-inset-bottom, 0px));

  /* Colonnes ÉLASTIQUES. minmax(0,1fr) au centre est indispensable : c'est
     l'équivalent grille de min-width:0. Sans lui, une piste 1fr refuse de
     descendre sous la taille de son contenu et pousse la grille en débordement
     (c'est ce qui se passait avec le plan Leaflet). */
  --carte-col-g: minmax(240px, 300px);
  --carte-col-d: minmax(260px, 320px);
}
@media (max-width: 900px) {
  html.respfix body.page-carte {
    --carte-reserve: calc(var(--reserve-bas) + env(safe-area-inset-bottom, 0px));
  }
}
@supports (height: 100dvh) {
  html.respfix body.page-carte { --carte-vh: 100dvh; }
}

/* --------------------------------------------------------------------------
   R6 — NEUTRALISATION DE LA RUSTINE carte.php:120
   Cette page remontait la barre du haut à z-index:1100 pour repasser devant
   les calques Leaflet (jusqu'à 1000). Ce n'est plus nécessaire : .carte-map-wrap
   porte isolation:isolate (carte.php:407), donc les 1000 de Leaflet sont
   ENFERMÉS dans la carte et ne peuvent plus sortir. La barre reprend sa place
   dans l'échelle unique du site.
   ⚠️ La règle d'origine reste dans carte.php : on ne la supprime pas, on la
   recouvre.
   RETIRÉ LE 18/07 APRÈS CONTRÔLE QUALITÉ : la règle était ici un DOUBLON EXACT
   de 00-socle.css:61, et en plus non scopée body.page-carte — elle sortait donc
   du périmètre de cette page. Le socle s'en charge déjà pour tout le site.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   (a) LE FILET DU BAS REVIENT SUR LA PAGE CARTE
   La règle d'origine (padding:0 !important) est désormais retirée par
   carte.php quand l'interrupteur est allumé — c'était le SEUL moyen : aucune
   déclaration sans !important ne peut battre une déclaration !important, quelle
   que soit sa spécificité. Ici on rend le plein écran horizontal SANS toucher
   au filet du bas.
   -------------------------------------------------------------------------- */
html.respfix body.page-carte .pub-content.container {
  padding-top: 0;
  padding-inline: 0;                       /* plein écran horizontal, comme avant */
  padding-bottom: var(--carte-reserve);    /* le filet qui manquait */
  max-width: 100%;
}

/* --------------------------------------------------------------------------
   (b) + (c) LE SHELL : HAUTEUR CALCULÉE, COLONNES ÉLASTIQUES
   Avant : height:76vh — un nombre choisi à la main qui ne déduisait ni la barre
   du haut ni la barre du Gardien. C'est pour ça que le champ de recherche du
   panneau gauche passait sous la barre du haut sur certains écrans.
   -------------------------------------------------------------------------- */
html.respfix body.page-carte .carte-shell {
  grid-template-columns: var(--carte-col-g) minmax(0, 1fr) var(--carte-col-d);
  grid-template-rows: minmax(0, 1fr);
  height: calc(var(--carte-vh) - var(--hdr-h) - var(--carte-bar-h) - var(--carte-reserve));
  /* Plancher : en dessous, le plan ne sert plus à rien. Il ne se déclenche que
     sur les petits téléphones, où les meubles mangent plus de la moitié de
     l'écran ; la page défile alors de quelques dizaines de pixels (mesuré au
     banc : 360×740 → 126px de défilement, tout reste atteignable). */
  min-height: 420px;
}

/* ---- 901–1400px : panneaux réduits, le panneau droit passe en tiroir ---- */
@media (max-width: 1400px) {
  html.respfix body.page-carte {
    --carte-col-g: minmax(220px, 270px);
  }
  html.respfix body.page-carte .carte-shell {
    grid-template-columns: var(--carte-col-g) minmax(0, 1fr);
  }
}

/* ---- ≤900px : la carte prend toute la largeur, les DEUX panneaux en tiroir ---- */
@media (max-width: 900px) {
  html.respfix body.page-carte .carte-shell {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
  }

  /* carte.php:843 fige le plan à « height:62vh; min-height:440px » sous 768px.
     Ces deux nombres ignorent la hauteur du shell, qui est désormais CALCULÉE :
     sur un téléphone (shell ramené à son plancher de 420px) le plan réclamait
     440px, soit 20px de plus que sa case. Et .carte-shell porte overflow:hidden
     (carte.php:140) : le bas du plan — échelle, attribution OpenStreetMap —
     était donc coupé en silence, sans la moindre barre de défilement pour le
     signaler. C'est exactement le genre de défaut que « overflow:hidden » cache.
     Ici le plan reprend simplement la hauteur de sa case.
     « height:100% » et non « auto » : #map (carte.php:423) demande height:100%,
     un pourcentage qui a besoin d'un parent à la hauteur DÉFINIE. La case vaut
     minmax(0,1fr) dans une grille de hauteur connue : la chaîne tient. */
  html.respfix body.page-carte .carte-map-wrap {
    height: 100%;
    min-height: 0;
  }
}

/* --------------------------------------------------------------------------
   (d) LES TIROIRS — PLUS AUCUN ACCÈS SUPPRIMÉ
   Avant : .carte-right{display:none} entre 0 et 1199px. Le panneau « résultats
   / fiche du lieu » était donc INJOIGNABLE tant qu'aucun marqueur n'était
   cliqué : pas de bouton, pas de barre de défilement, rien.
   Ici : il redevient atteignable par un bouton, à toutes les tailles.

   Deux formes selon la place disponible :
     • 1200–1400px : panneau latéral qui glisse depuis la droite ;
     • ≤1199px     : volet qui monte du bas — GÉOMÉTRIE IDENTIQUE à celle que
                     carte.php:819-827 utilise déjà pour la fiche d'un lieu, pour
                     qu'aucune des deux règles ne se contredise.
   La visibilité est portée par « visibility », pas par display : le tiroir
   fermé sort du parcours au clavier (comme il doit), et l'animation reste
   possible. On n'utilise NI display:none sur un bouton, NI marge négative,
   NI overflow:hidden pour masquer quoi que ce soit.
   -------------------------------------------------------------------------- */
@media (max-width: 1400px) {
  html.respfix body.page-carte .carte-right {
    display: flex;                     /* recouvre le display:none de carte.php:816 */
    position: fixed;
    z-index: var(--z-panneaux);
    visibility: hidden;
    pointer-events: none; /* (23/07) fermé = non cliquable immédiatement (avant la fin du glissement) */
    overflow-y: auto;
    overscroll-behavior: contain;
    background: rgba(6, 13, 30, 0.96);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    transition: transform .22s ease, visibility 0s linear .22s;
  }
  html.respfix body.page-carte .carte-right.is-drawer-open {
    visibility: visible;
    pointer-events: auto;
    transform: none;
    transition: transform .22s ease, visibility 0s;
  }
}

/* 1200–1400px : panneau latéral droit */
@media (min-width: 1200px) and (max-width: 1400px) {
  html.respfix body.page-carte .carte-right {
    top: var(--hdr-h);
    right: 0;
    bottom: 0;
    width: min(360px, 86vw);
    transform: translateX(100%);
    border-left: 1px solid rgba(0, 229, 255, 0.28);
    box-shadow: -14px 0 44px rgba(0, 0, 0, 0.55);
    padding-bottom: var(--carte-reserve);
  }
}

/* ≤1199px : volet du bas — mêmes valeurs que la fiche de lieu existante,
   pour que les deux systèmes ne se battent jamais. */
@media (max-width: 1199px) {
  html.respfix body.page-carte .carte-right {
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    max-height: 85vh;
    transform: translateY(100%);
    border-radius: 18px 18px 0 0;
    border-top: 1px solid rgba(0, 229, 255, 0.28);
    box-shadow: 0 -8px 40px rgba(0, 0, 0, 0.55);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
}

/* ≤900px : le panneau gauche (recherche, « Ma position », zone, filtres)
   passe lui aussi en tiroir. AUCUN de ses outils n'est retiré : ils sont tous
   dans le tiroir, ouvert par un bouton toujours visible sur la carte. */
@media (max-width: 900px) {
  html.respfix body.page-carte .carte-left {
    position: fixed;
    top: var(--hdr-h);
    left: 0;
    bottom: 0;
    width: min(330px, 86vw);
    z-index: var(--z-panneaux);
    visibility: hidden;
    pointer-events: none; /* (23/07) fermé = non cliquable immédiatement (avant la fin du glissement) */
    transform: translateX(-100%);
    overflow-y: auto;                                  /* rétabli : carte.php:830 le passait à visible */
    overscroll-behavior: contain;
    background: rgba(6, 13, 30, 0.96);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    border-right: 1px solid rgba(0, 229, 255, 0.28);   /* rétabli : carte.php:830 le mettait à 0 */
    border-bottom: 0;
    box-shadow: 14px 0 44px rgba(0, 0, 0, 0.55);
    padding-bottom: var(--carte-reserve);
    transition: transform .22s ease, visibility 0s linear .22s;
  }
  html.respfix body.page-carte .carte-left.is-drawer-open {
    visibility: visible;
    pointer-events: auto;
    transform: none;
    transition: transform .22s ease, visibility 0s;
  }
}

/* --------------------------------------------------------------------------
   LES BOUTONS D'OUVERTURE — posés SUR le plan, jamais sur un panneau
   Ils vivent dans .carte-map-wrap (déjà position:relative + isolation:isolate).
   La bande qui les porte ne capte AUCUN clic : sans « pointer-events:none »
   elle empêcherait de faire glisser le plan sur toute sa largeur.
   -------------------------------------------------------------------------- */
html.respfix body.page-carte .carte-map-wrap {
  /* Échelle LOCALE, valable uniquement à l'intérieur du contexte d'empilement
     créé par isolation:isolate (carte.php:407). Les calques de Leaflet montent
     à 1000 dans ce contexte ; les variables --z-* du socle décrivent la page,
     pas l'intérieur du plan. Cette valeur ne peut PAS sortir de la carte. */
  --z-carte-outils: 1010;
}
html.respfix body.page-carte .carte-drawer-bar {
  position: absolute;
  top: 10px;
  left: 10px;
  right: 10px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
  pointer-events: none;
  z-index: var(--z-carte-outils);
}
html.respfix body.page-carte .carte-drawer-btn {
  pointer-events: auto;
  display: none;                 /* voir la note ci-dessous */
  align-items: center;
  gap: 6px;
  min-height: 40px;              /* cible tactile confortable */
  padding: 9px 14px;
  border-radius: 20px;
  border: 1px solid rgba(0, 229, 255, 0.45);
  background: rgba(6, 13, 30, 0.90);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: #9ff4ff;
  font: 700 13px/1 'Inter', system-ui, sans-serif;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.42);
  transition: background .15s, border-color .15s;
}
html.respfix body.page-carte .carte-drawer-btn:hover,
html.respfix body.page-carte .carte-drawer-btn[aria-expanded="true"] {
  background: rgba(0, 229, 255, 0.18);
  border-color: rgba(0, 229, 255, 0.75);
}
html.respfix body.page-carte .carte-drawer-btn:focus-visible {
  outline: 2px solid #00e5ff;
  outline-offset: 2px;
}
html.respfix body.page-carte .carte-drawer-btn svg { flex: 0 0 auto; }

/* Chaque bouton n'apparaît QUE là où son panneau est en tiroir.
   Ce n'est pas « masquer un bouton pour gagner de la place » (interdit) :
   au-dessus de ces largeurs le panneau est ENTIÈREMENT visible dans la page,
   le bouton n'ouvrirait donc rien. On retire un doublon sans fonction, pas un
   accès — l'accès, lui, est toujours là, sous les yeux. */
@media (max-width: 1400px) {
  html.respfix body.page-carte .carte-drawer-btn[data-drawer="right"] { display: inline-flex; }
}
@media (max-width: 900px) {
  html.respfix body.page-carte .carte-drawer-btn[data-drawer="left"] { display: inline-flex; }
}

/* --------------------------------------------------------------------------
   BOUTON « FERMER » DANS CHAQUE TIROIR
   Nécessaire : quand un tiroir est ouvert, il recouvre le bouton qui l'a
   ouvert. Sans lui, on pourrait rester enfermé dans le tiroir.
   Il ne s'affiche QUE lorsque le panneau est réellement en tiroir.
   -------------------------------------------------------------------------- */
html.respfix body.page-carte .carte-drawer-close { display: none; }
@media (max-width: 1400px) {
  html.respfix body.page-carte .carte-right > .carte-drawer-close { display: flex; }
}
@media (max-width: 900px) {
  html.respfix body.page-carte .carte-left > .carte-drawer-close { display: flex; }
}
html.respfix body.page-carte .carte-drawer-close {
  position: sticky;
  top: 0;
  align-self: flex-end;
  align-items: center;
  gap: 6px;
  margin: 0 0 0 auto;
  padding: 10px 14px;
  min-height: 40px;
  border: 0;
  border-bottom-left-radius: 14px;
  background: rgba(6, 13, 30, 0.96);
  color: #9ff4ff;
  font: 700 13px/1 'Inter', system-ui, sans-serif;
  cursor: pointer;
  /* Nommé plutôt qu'écrit en dur (contrôle qualité du 18/07) : ce n'est PAS
     une couche de l'échelle du site, seulement « au-dessus du contenu qui
     défile dans ce tiroir-ci ». La nommer évite qu'on la confonde un jour
     avec un --z-* global et qu'on la fasse grimper « pour voir ». */
  --z-tiroir-fermeture: 2;
  z-index: var(--z-tiroir-fermeture);
}
html.respfix body.page-carte .carte-drawer-close:focus-visible {
  outline: 2px solid #00e5ff;
  outline-offset: -2px;
}

/* --------------------------------------------------------------------------
   LEAFLET : ses commandes doivent rester ATTEIGNABLES
   • Le zoom (+/−) est en haut à gauche, exactement là où se pose le bouton
     « Filtres ». On le descend juste en dessous, au lieu de le recouvrir.
   • L'attribution OpenStreetMap est en bas à droite. mobile-app.css:244-247 la
     colle à 4px du bord AVEC !important ; la seule façon de la dégager est un
     !important de spécificité supérieure. C'est le seul de ce fichier.
   • La réserve du bas est déjà déduite de la hauteur du shell : la barre du
     Gardien du Feu ne peut donc plus recouvrir le bas du plan.
   -------------------------------------------------------------------------- */
html.respfix body.page-carte .carte-map-wrap .leaflet-top.leaflet-left {
  padding-top: var(--carte-outils-h);
}
html.respfix body.page-carte .carte-map-wrap .leaflet-bottom.leaflet-right {
  right: 12px !important;
  bottom: 12px !important;
}
html.respfix body.page-carte .carte-map-wrap .leaflet-bottom.leaflet-left {
  left: 12px;
  bottom: 12px;
}

/* --------------------------------------------------------------------------
   ACCESSIBILITÉ — respect du réglage « moins d'animations »
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html.respfix body.page-carte .carte-left,
  html.respfix body.page-carte .carte-right { transition: none; }
}

/* ================= 21-rien-oublier.css ================= */

/* ==========================================================================
   21 — RIEN OUBLIER  (cause R8)
   --------------------------------------------------------------------------
   TOUT est préfixé « html.respfix ». Interrupteur éteint = page identique.

   LA CAUSE, en une phrase : sur cette page, TROIS règles mesuraient la même
   chose dans TROIS unités différentes.
     · le cluster    (l.48) : min(80vh, 92vw)            → dépend de la fenêtre
     · l'orbe        (l.58) : clamp(292px, 38vmin, 392px) → PLANCHER DUR 292px
     · les pastilles (JS)   : % du cluster
   Quand la fenêtre maigrit, le cluster maigrit, les pastilles se rapprochent
   du centre… mais le centre, lui, reste bloqué à 292px minimum. Il finit par
   passer PAR-DESSUS les pastilles : c'est exactement ça, « Véhicule » qui
   disparaît derrière « Cuisine ».
   Le plancher de 292px n'était pas un caprice : le titre était en
   white-space:nowrap dimensionné en vw — il refusait de se couper, donc il
   fallait bien lui garantir 292px. Les deux défauts se tiennent, on corrige
   les deux ensemble.

   ORDRE DU CORRECTIF (il y a un piège, voir §2) :
     §1  borner le cluster (--ro-d), libellés compris
     §2  SEULEMENT APRÈS : rendre .ro-stage visible (overflow)
     §3  l'orbe exprimé en % du cluster → centre et pastilles maigrissent
         ENSEMBLE, quoi qu'il arrive
     §4  mode alternatif « grille de cartes » quand le cercle ne tient plus
     §5  empilement (z-index) remis sur l'échelle du socle
   Le placement des pastilles (mesure réelle + écart garanti) est dans
   rien-oublier.php, sous respfix_on().
   ========================================================================== */


/* --------------------------------------------------------------------------
   §1 — LE CLUSTER EST BORNÉ, LIBELLÉS COMPRIS  (à faire EN PREMIER)
   --------------------------------------------------------------------------
   --ro-d = LE diamètre de référence. Une seule source, tout en dépend.

   Les trois bornes, et pourquoi :

   · LARGEUR — « 100% + le rembourrage − 140px ».
     Un libellé fait jusqu'à 120px de large et il est CENTRÉ sous sa pastille :
     il déborde donc du cercle de 60px de chaque côté (70px avec le halo).
     2 × 70 = les 140px retirés. Le débord a le droit d'occuper le rembourrage
     du plateau (--ro-pad), pas un pixel de plus : au-delà il sortirait de la
     page, et le socle (overflow-x:clip) le couperait EN SILENCE — exactement
     le défaut qu'on est en train de corriger ailleurs.
     ⚠️ On mesure en « 100% du plateau », pas en vw : vw inclut la barre de
     défilement ET ignore le rail de gauche. Les deux erreurs se cumulaient
     dans l'ancien « 92vw » et donnaient un cercle 16 à 90px trop large.

   · HAUTEUR — 80 % de la hauteur utile, et surtout « hauteur réelle − chrome ».
     Une fenêtre 1400x600 n'a pas 600px de libres : il y a la barre du haut
     (--hdr-h, mesurée en direct, qui varie de 91 à 213px). C'est CETTE borne
     qui fait basculer les fenêtres larges mais basses.

   Un plancher de 320px empêche le cercle de devenir minuscule ; en dessous,
   c'est le mode grille (§4) qui prend le relais.
   -------------------------------------------------------------------------- */
html.respfix body.page-rien-oublier .ro-stage {
  --ro-vh: 100vh;                        /* repli pour les navigateurs sans dvh */
  --ro-pad: clamp(12px, 3vw, 40px);      /* = le rembourrage horizontal réel du plateau */

  /* Ce qui est réellement consommé AU-DESSUS du cercle, et rien de plus :
     la barre du haut (mesurée en direct) et 60px d'air, dont les 26px de
     rembourrage du plateau et les 8px de marge du cluster.
     ⚠️ On n'y met PAS --reserve-bas-desktop : la page a le droit de défiler.
     L'y ajouter faisait perdre 12 à 27 % de diamètre sur TOUS les écrans et
     basculait en grille des cas qui tenaient très bien — mesuré. */
  --ro-chrome: calc(var(--hdr-h) + 60px);

  --ro-d: min(
            calc(100% + 2 * var(--ro-pad) - 140px),
            max(320px, min(
              calc(var(--ro-vh) * 0.8),
              calc(var(--ro-vh) - var(--ro-chrome))
            ))
          );

  /* Part du cluster occupée par l'orbe. C'est le PREMIER barreau de l'échelle :
     le JS a le droit de descendre plus bas (34 %, 30 %…) avant d'abandonner le
     cercle — voir RO_ECHELLE dans rien-oublier.php. */
  --ro-core-pct: 42;
}
@supports (height: 100dvh) {
  html.respfix body.page-rien-oublier .ro-stage { --ro-vh: 100dvh; }
}

/* Témoin de mesure : un bloc de hauteur nulle, invisible, qui porte TOUJOURS
   la largeur --ro-d — même quand le cluster est passé en mode grille.
   C'est lui que le JS mesure : sans lui, la décision « cercle ou grille »
   se prendrait sur une largeur qui dépend… du mode en cours, et la page
   oscillerait entre les deux à l'infini. */
html.respfix .ro-gauge {
  width: var(--ro-d);
  height: 0;
  margin: 0 auto;
  visibility: hidden;
  pointer-events: none;
}

@media (min-width: 901px) {
  html.respfix body.page-rien-oublier .ro-cluster {
    width: var(--ro-d);
    aspect-ratio: 1 / 1;
  }
  /* Le débord vertical des libellés (30px, voir RO_DEBORD_V dans le JS) prend
     sur le rembourrage en haut ; en bas, on lui rend explicitement sa place
     pour qu'il ne vienne jamais toucher la barre de boutons. */
  html.respfix body.page-rien-oublier:not(.ro-grid) .ro-cluster { margin-bottom: 30px; }

  /* Fenêtre ouverte à côté (body.ro-aside) : le cercle n'a plus que la moitié
     gauche de l'écran. Même raisonnement, bornes réduites — et non plus un
     min(52vh, 38vw) qui ignorait à la fois les libellés ET le rail.
     Le cluster est décalé de 26vw vers la gauche : son centre tombe à
     0,24vw + rail/2, la fenêtre commence à 47vw. Le côté le plus serré est
     la gauche (le rail), d'où la borne 46vw − rail − 140. */
  html.respfix body.page-rien-oublier.ro-aside .ro-stage {
    --ro-d: min(
              calc(46vw - var(--pub-rail-w) - 140px),
              max(260px, min(
                calc(var(--ro-vh) * 0.52),
                calc(var(--ro-vh) - var(--ro-chrome))
              ))
            );
  }

  /* §1bis — LA BARRE D'ACTIONS SUIT LE HALO DÉCALÉ (fenêtre à côté, cercle gardé)
     ---------------------------------------------------------------------------
     En mode « fenêtre à côté » SANS grille, le cluster est décalé de 26vw vers
     la gauche (transform de la page, l.271 de rien-oublier.php) mais la barre
     .ro-actions, elle, restait centrée sur TOUTE la largeur : les boutons
     glissaient au centre/à droite, sous le panneau, et ne se trouvaient plus
     SOUS le halo. On lui applique le MÊME décalage et on la borne à la largeur
     du cercle (--ro-d) : elle reste centrée SOUS le halo, sans chevaucher les
     pastilles (qui sont plus haut) ni passer sous la fenêtre.
     (La variante grille.ro-aside est traitée au §4 : transform:none + colonne
     gauche — surcharge celle-ci grâce à la classe .ro-grid en plus.) */
  html.respfix body.page-rien-oublier.ro-aside:not(.ro-grid) .ro-actions {
    width: var(--ro-d);
    max-width: var(--ro-d);
    margin-left: auto;
    margin-right: auto;
    transform: translateX(-26vw);
  }
}


/* --------------------------------------------------------------------------
   §2 — LE PLATEAU REDEVIENT VISIBLE  (à faire APRÈS §1, jamais avant)
   --------------------------------------------------------------------------
   .ro-stage:43 portait overflow:hidden. Ce n'est pas une mise en page :
   c'est un couvercle. Les pastilles du bas ne « tenaient » pas, elles
   étaient COUPÉES — sans barre de défilement, donc sans aucun moyen de s'en
   apercevoir ni d'y accéder.
   Le retirer AVANT §1 ferait déborder la page pour de bon. Maintenant que le
   cluster est borné et que les 140px de libellés sont réservés, plus rien
   n'a besoin d'être coupé. Le socle garde de toute façon overflow-x:clip sur
   <body>, donc aucune barre horizontale ne peut apparaître.
   -------------------------------------------------------------------------- */
html.respfix body.page-rien-oublier .ro-stage { overflow: visible; }


/* --------------------------------------------------------------------------
   §3 — L'ORBE EST EXPRIMÉ EN % DU CLUSTER
   --------------------------------------------------------------------------
   C'est LE correctif de fond : le centre et les pastilles partagent
   désormais le même référentiel. Le cluster maigrit de 20 % → l'orbe maigrit
   de 20 % → l'écart entre les deux ne bouge pas. Plus aucun recouvrement
   possible, à aucune taille.

   container-type:inline-size fait de l'orbe une « boîte de référence » : ses
   textes se mesurent en cqw (pourcentage de la largeur DE L'ORBE) au lieu de
   vw (largeur de la FENÊTRE). C'est ce qui permet de lever le
   white-space:nowrap du titre sans qu'il ne casse jamais : il ne peut plus
   être trop grand pour son cercle. C'est aussi ce qui rend l'orbe capable de
   descendre à 30 % du cluster sans devenir illisible — et donc de sauver le
   cercle là où l'ancienne version basculait en liste.
   -------------------------------------------------------------------------- */
@media (min-width: 901px) {
  html.respfix body.page-rien-oublier .ro-core {
    width: calc(var(--ro-core-pct) * 1%);
    aspect-ratio: 1 / 1;
    container-type: inline-size;
    padding: 2.4%;              /* % du cluster : suit le même référentiel */
  }

  /* Le titre peut enfin revenir à la ligne. Il n'en aura presque jamais
     besoin (il rétrécit avec son cercle), mais il ne débordera plus JAMAIS. */
  html.respfix body.page-rien-oublier .ro-title {
    white-space: normal;
    overflow-wrap: break-word;
    font-size: clamp(15px, 9.5cqw, 36px);
    line-height: 1.05;
  }
  html.respfix body.page-rien-oublier .ro-subtitle {
    font-size: clamp(9px, 3.8cqw, 14.5px);
    margin-top: 2cqw;
  }
  html.respfix body.page-rien-oublier .ro-sum {
    font-size: clamp(9px, 3.6cqw, 14px);
    gap: 1.8cqw;
  }
  html.respfix body.page-rien-oublier .ro-sum svg {
    width: clamp(10px, 3.9cqw, 15px);
    height: clamp(10px, 3.9cqw, 15px);
    flex: 0 0 auto;
  }
  html.respfix body.page-rien-oublier .ro-sum .nm { max-width: 62cqw; }
  html.respfix body.page-rien-oublier .ro-core-div { margin: 2.6cqw 0 2.4cqw; }

  /* Les boutons DANS l'orbe suivaient une taille fixe : sur un orbe rétréci
     ils sortaient du cercle. Ils suivent maintenant, eux aussi. */
  html.respfix body.page-rien-oublier .ro-core .ro-cbtn {
    font-size: clamp(9.5px, 4.2cqw, 13px);
    padding: clamp(5px, 2.6cqw, 9px) clamp(8px, 5.5cqw, 18px);
    gap: clamp(3px, 1.8cqw, 7px);
  }
  html.respfix body.page-rien-oublier .ro-core .ro-cbtn svg {
    width: clamp(10px, 3.9cqw, 15px);
    height: clamp(10px, 3.9cqw, 15px);
  }
  html.respfix body.page-rien-oublier .ro-core-cta { gap: clamp(4px, 2.2cqw, 8px); margin-top: 3cqw; }
  html.respfix body.page-rien-oublier .ro-core .ro-alert {
    font-size: clamp(8.5px, 3.2cqw, 13px);
    padding: clamp(4px, 2.2cqw, 10px) clamp(6px, 3cqw, 12px);
    line-height: 1.4;
  }

  /* Un libellé sur deux lignes était aligné à gauche sous une pastille
     centrée : il « penchait ». Cosmétique, mais c'est le même défaut de
     référentiel que le reste. */
  html.respfix body.page-rien-oublier .ro-node-lb { text-align: center; }
}

/* Pastilles : deux crans de compacité. Le JS descend l'échelle barreau par
   barreau — d'abord un gros orbe, puis un orbe plus petit, puis des pastilles
   plus compactes — et ne passe en grille QUE si le dernier barreau ne tient
   pas non plus. On garde le cercle le plus longtemps possible, sans jamais
   laisser deux pastilles se toucher.
   ⚠️ « ro-dense » vient de la feuille de la page, chargée APRÈS celle-ci : à
   spécificité égale elle gagnerait. D'où le préfixe html.respfix sur
   « ro-compact », qui doit pouvoir passer devant (le JS ne pose de toute
   façon jamais les deux classes en même temps). */
html.respfix .ro-cluster.ro-compact .ro-node-ic { width: 48px; height: 48px; }
html.respfix .ro-cluster.ro-compact .ro-node-ic svg { width: 23px; height: 23px; }
html.respfix .ro-cluster.ro-compact .ro-node-lb { font-size: 11px; max-width: 84px; }
html.respfix .ro-cluster.ro-compact .ro-node-dot { min-width: 15px; height: 15px; font-size: 8.5px; }

/* Gabarit de mesure : le JS en insère un exemplaire, lit sa taille RÉELLE,
   puis le retire.
   ⚠️ Spécificité VOLONTAIREMENT haute : il doit rester affichable même quand
   la page est en mode grille (où .ro-node est masquée) ou sur téléphone.
   Sinon la mesure vaudrait 0, le calcul repartirait sur des constantes, et le
   retour grille → cercle se ferait avec des pastilles « larges de 0 » qui
   s'empileraient toutes au même endroit. */
html.respfix body.page-rien-oublier .ro-cluster .ro-node.ro-probe {
  display: flex;
  left: 0;
  top: 0;
  visibility: hidden;
  pointer-events: none;
}


/* --------------------------------------------------------------------------
   §4 — MODE ALTERNATIF : GRILLE DE CARTES
   --------------------------------------------------------------------------
   Quand le cercle ne peut PLUS accueillir toutes les familles sans qu'elles
   se touchent, on ne rétrécit pas jusqu'à l'illisible et on n'en cache
   aucune : on change de mise en page.
   Le déclenchement se fait sur la largeur ET sur la hauteur — une fenêtre
   1400x600 bascule, parce que --ro-d intègre la hauteur réellement libre.
   On réutilise les cartes .ro-card qui existaient déjà pour le téléphone :
   même balisage, mêmes compteurs, mêmes actions. Aucune famille, aucun
   bouton, aucun accès n'est perdu. Le résumé « Rien oublier » (l'orbe)
   repasse simplement au-dessus, en bandeau.
   -------------------------------------------------------------------------- */
@media (min-width: 901px) {
  html.respfix body.page-rien-oublier.ro-grid .ro-cluster {
    width: 100%;
    max-width: 100%;
    aspect-ratio: auto;
    transform: none;
    margin-top: 0;
  }
  html.respfix body.page-rien-oublier.ro-grid .ro-cluster::before,
  html.respfix body.page-rien-oublier.ro-grid .ro-ring,
  html.respfix body.page-rien-oublier.ro-grid .ro-node { display: none; }

  /* L'orbe devient un bandeau-résumé : même dégradé, même halo, même
     identité — il change de forme, pas de nature. */
  html.respfix body.page-rien-oublier.ro-grid .ro-core {
    position: static;
    transform: none;
    width: 100%;
    max-width: 640px;
    margin: 0 auto;
    aspect-ratio: auto;
    border-radius: 26px;
    padding: clamp(18px, 2.4vw, 26px) clamp(16px, 2.2vw, 24px);
  }

  html.respfix body.page-rien-oublier.ro-grid .ro-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 12px;
    margin-top: 22px;
  }
  html.respfix body.page-rien-oublier.ro-grid .ro-card { height: 100%; }

  /* Grille + fenêtre ouverte à côté : les cartes doivent rester à GAUCHE du
     panneau.
     ⚠️ MESURÉ AU NAVIGATEUR : un min(44vw, 100%) ne suffit pas, et c'est un
     piège classique. 44vw se compte depuis le bord de la FENÊTRE, alors que
     la liste, elle, commence après le rail ET après le rembourrage du plateau.
     À 1366px : liste 136→738 pour un panneau qui commence à 643 — 95px de
     cartes CACHÉES sous la fenêtre.
     On déduit donc ce qui est réellement consommé à gauche, plus 16px d'air
     avant le panneau (qui, lui, commence à 47vw). */
  html.respfix body.page-rien-oublier.ro-grid.ro-aside .ro-cluster,
  html.respfix body.page-rien-oublier.ro-grid.ro-aside .ro-actions,
  html.respfix body.page-rien-oublier.ro-grid.ro-aside .ro-list {
    width: min(100%, calc(47vw - var(--pub-rail-w) - var(--ro-pad) - 16px));
    max-width: min(100%, calc(47vw - var(--pub-rail-w) - var(--ro-pad) - 16px));
    margin-inline: 0 auto;
    transform: none;
  }
  html.respfix body.page-rien-oublier.ro-grid.ro-aside .ro-list {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
}


/* --------------------------------------------------------------------------
   §5 — EMPILEMENT : on remet cette page sur l'échelle du socle
   --------------------------------------------------------------------------
   Les valeurs d'origine étaient trois échelles concurrentes : 3 et 4 pour le
   cercle, 4000 pour le bouton mobile, 5000/5010 pour les fenêtres.
   ⚠️ Le point qui compte vraiment : .ro-mcta était à 4000 et les panneaux à
   5010. En remettant les panneaux sur l'échelle du socle (700/710) SANS
   toucher à .ro-mcta, le bouton « Continuer ma préparation » serait passé
   PAR-DESSUS la fenêtre ouverte. Les trois doivent bouger ensemble.
   -------------------------------------------------------------------------- */
html.respfix .ro-overlay { z-index: var(--z-voiles); }
html.respfix .ro-panel   { z-index: var(--z-panneaux); }
html.respfix .ro-core    { z-index: var(--z-contenu); }
html.respfix .ro-node    { z-index: calc(var(--z-contenu) + 1); }

/* « Continuer ma préparation » (téléphone).
   Sa règle d'origine est dans la feuille de la page, chargée APRÈS celle-ci
   et de spécificité identique : à égalité elle gagnerait et le bouton
   resterait à 4000. D'où le sélecteur plus précis ici.
   Il ne suffit pas de le faire redescendre sous les pastilles flottantes : à
   bottom:12px il se posait DÉJÀ sur « Le Gardien » et sur la barre du bas.
   On le remonte au-dessus des deux, avec les hauteurs mesurées par le socle
   (jamais un nombre écrit à la main), et on rend au contenu la place que le
   bouton lui prend. */
@media (max-width: 900px) {
  html.respfix body.ro-has-prep .ro-mcta {
    z-index: var(--z-flottants);
    bottom: calc(var(--bottomnav-h) + var(--fab-h) + 16px);
  }
  html.respfix body.page-rien-oublier.ro-has-prep .pub-content {
    padding-bottom: calc(var(--reserve-bas) + var(--fab-h) + 30px);
  }
}


/* --------------------------------------------------------------------------
   Confort de lecture : quand le membre a demandé moins d'animations, le
   changement de mise en page ne doit pas s'accompagner d'une transition.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html.respfix .ro-node,
  html.respfix .ro-node-ic { transition: none; }
}

/* ================= 22-membre.css ================= */

/* ==========================================================================
   22 — MEMBRE.PHP · PROFIL PUBLIC DU VOYAGEUR
   --------------------------------------------------------------------------
   Tout est préfixé « html.respfix ». Sans la classe sur <html>, ce fichier ne
   peut RIEN changer.

   Corrige DEUX causes, et seulement celles-là :

   R10 — LISIBILITÉ. La page pose son texte sur la PHOTO de fond : .mbr-card
         n'était qu'un voile à 4 % (aucun socle), et les couleurs passaient par
         var(--muted,…) qui n'existe QUE dans body.phare-light — donc en mode
         nuit tout retombait sur un repli semi-transparent. Sur un coucher de
         soleil clair, ça tombe vers 2,2:1.
         Le motif correct existe déjà dans le projet (includes/profile-ads.php
         :193-197, et membre.php:167 avec .jr-photo-veil) : un bloc de texte
         posé sur une photo porte SON PROPRE fond sombre et opaque.
         → jetons LOCAUX --mbr-* sur .mbr-wrap (+ miroir body.phare-light).
         → AUCUNE déclaration globale de --muted (effet de bord sur tout le
           site : chantier séparé).
         → AUCUNE modification de site.bg.voile (choix visuel assumé de Julien,
           et le monter assombrirait TOUTES les pages).
         → l'ombre portée seule est insuffisante : on met un vrai fond.

   R9 — BARRE D'ACTIONS. .mbr-actions était un flex dont 3 enfants sur 5
        étaient des enveloppes opaques (un <span> à style inline, un <form>,
        et un <details> carrément HORS de la barre). Le bloc « harmonisation »
        du 09/07 listait les items PAR CLASSES : toute enveloppe insérée
        cassait l'alignement en silence.
        → grille auto-fit + hauteur commune, et un sélecteur STRUCTUREL
          (par type d'élément) qui traverse les enveloppes display:contents.
        → hiérarchie rendue : principale dorée, secondaires en contour,
          sensibles discrètes.
        (Le déplacement du <details> et le retrait du style inline sont faits
         dans membre.php, sous respfix_on() : impossibles en CSS seul.)
   ========================================================================== */


/* ==========================================================================
   1. JETONS LOCAUX — portée .mbr-wrap UNIQUEMENT
   --------------------------------------------------------------------------
   Aucun de ces noms n'existe ailleurs : rien ne peut fuir sur une autre page.
   Le mode nuit est la valeur par défaut, body.phare-light en est le miroir.
   ========================================================================== */
html.respfix .mbr-wrap {
  /* Encre */
  --mbr-ink:       #FAF7F0;                 /* ~12:1 sur --mbr-panel */
  --mbr-ink-soft:  #d7dfe9;                 /* ~9,6:1  (remplace var(--muted)) */
  --mbr-gold:      #e7cd90;                 /* ~8,4:1  (remplace l'or à 85 %) */

  /* Surfaces : opaques par construction, c'est ELLES le fond du texte */
  --mbr-panel:     rgba(10,16,28,.86);
  --mbr-edge:      rgba(255,255,255,.16);
  --mbr-place-bg:  rgba(255,255,255,.06);
  --mbr-shadow:    0 10px 30px rgba(0,0,0,.32);
  --mbr-blur:      blur(8px);

  /* Peaux de boutons — un seul endroit à changer (voir §3) */
  --mbr-sec-bg:    rgba(9,26,32,.90);
  --mbr-sec-edge:  rgba(0,229,255,.42);
  --mbr-sec-edge2: rgba(0,229,255,.72);
  --mbr-sec-ink:   #cdf7f1;

  --mbr-pri-bg:    linear-gradient(135deg,#e0bd77,#c08f3e);
  --mbr-pri-edge:  rgba(255,232,180,.55);
  --mbr-pri-edge2: rgba(255,240,205,.85);
  --mbr-pri-ink:   #1a1206;

  --mbr-dan-bg:    rgba(28,10,8,.34);
  --mbr-dan-edge:  rgba(255,124,96,.42);
  --mbr-dan-edge2: rgba(255,150,124,.72);
  --mbr-dan-ink:   #ffab9a;

  --mbr-focus:     #ffd98a;

  /* Largeur minimale d'une action : pilote le nombre de colonnes (voir §3) */
  --mbr-act-min:   190px;
}

/* Miroir jour (Phare éteint). Mêmes noms, valeurs inversées : aucune règle
   n'a besoin d'être écrite deux fois. */
html.respfix body.phare-light .mbr-wrap {
  --mbr-ink:       #121821;                 /* ~15:1 sur --mbr-panel clair */
  --mbr-ink-soft:  #3a4757;                 /* ~8,1:1 */
  --mbr-gold:      #6d4f12;                 /* ~6,5:1 — l'or clair serait illisible */

  --mbr-panel:     rgba(255,255,255,.93);
  --mbr-edge:      rgba(16,32,52,.18);
  --mbr-place-bg:  rgba(16,32,52,.05);
  --mbr-shadow:    0 10px 28px rgba(16,32,52,.18);

  --mbr-sec-bg:    rgba(14,116,144,.10);
  --mbr-sec-edge:  rgba(14,116,144,.45);
  --mbr-sec-edge2: rgba(14,116,144,.85);
  --mbr-sec-ink:   #07414f;

  --mbr-pri-bg:    linear-gradient(135deg,#d8ad63,#b8863a);
  --mbr-pri-edge:  rgba(120,84,20,.45);
  --mbr-pri-edge2: rgba(90,62,12,.75);
  --mbr-pri-ink:   #1a1206;

  --mbr-dan-bg:    rgba(200,50,30,.07);
  --mbr-dan-edge:  rgba(170,45,28,.45);
  --mbr-dan-edge2: rgba(150,35,20,.80);
  --mbr-dan-ink:   #8e1c12;

  --mbr-focus:     #0e7490;
}

/* Navigateur sans backdrop-filter (ancien Firefox, WebView) : le flou ne
   rend rien, donc on rend les fonds franchement plus opaques. Le contraste
   ne doit JAMAIS dépendre d'un effet optionnel. */
@supports not ((backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px))) {
  html.respfix .mbr-wrap                    { --mbr-panel: rgba(10,16,28,.95); --mbr-blur: none; }
  html.respfix body.phare-light .mbr-wrap   { --mbr-panel: rgba(255,255,255,.98); }
}


/* ==========================================================================
   2. LES BLOCS DE CONTENU PORTENT LEUR PROPRE FOND
   ========================================================================== */

/* La page d'erreur « Voyageur introuvable » sort AVANT le <style> de la page :
   elle n'avait donc littéralement aucune mise en forme. On la sert ici. */
html.respfix .mbr-wrap {
  max-width: 760px;
  margin: 18px auto 40px;
  padding: 0 16px;
}

html.respfix .mbr-card {
  background: var(--mbr-panel);
  border: 1px solid var(--mbr-edge);
  border-radius: 18px;
  box-shadow: var(--mbr-shadow);
  -webkit-backdrop-filter: var(--mbr-blur);
          backdrop-filter: var(--mbr-blur);
}
/* body.phare-light .mbr-card (0,2,0) est déclarée plus bas dans le flux :
   on la double ici avec une spécificité supérieure pour rester maître. */
html.respfix body.phare-light .mbr-card { background: var(--mbr-panel); }

html.respfix .mbr-name {
  color: var(--mbr-ink);
  font-family: 'Cormorant Garamond', serif;
  font-size: clamp(22px, 2.2vw, 25px);
  line-height: 1.15;
  overflow-wrap: break-word;
}
html.respfix .mbr-meta {
  color: var(--mbr-ink-soft);
  font-size: 13.5px;
  line-height: 1.45;
}
html.respfix .mbr-sec h3   { color: var(--mbr-gold); }
html.respfix .mbr-sec p,
html.respfix body.phare-light .mbr-sec p { color: var(--mbr-ink); }
html.respfix .mbr-note     { color: var(--mbr-ink-soft); font-size: 12.5px; }

/* « (région) » : opacité .6 sur un texte déjà atténué → remplacée par une
   vraie teinte dans membre.php (classe .mbr-hint), sans style inline. */
html.respfix .mbr-hint         { color: var(--mbr-ink-soft); opacity: 1; }
html.respfix .mbr-empty-hint   { color: var(--mbr-ink-soft); margin: 0; font-size: 14px; line-height: 1.55; }

/* Le seul .mbr-note HORS carte (sous la Jauge de Route) : il tombait
   directement sur la photo. Il lui faut sa propre plaque. */
html.respfix .mbr-wrap > .mbr-note {
  background: var(--mbr-panel);
  border: 1px solid var(--mbr-edge);
  border-radius: 12px;
  padding: 10px 13px;
  box-shadow: var(--mbr-shadow);
  -webkit-backdrop-filter: var(--mbr-blur);
          backdrop-filter: var(--mbr-blur);
}

/* Idem pour le « ← Retour » : c'est le seul .mbr-chip enfant direct du wrap.
   Sélecteur structurel : aucune classe à ajouter dans le PHP. */
html.respfix .mbr-wrap > .mbr-chip {
  display: inline-flex;
  align-items: center;
  min-height: 40px;                          /* cible tactile */
  padding: 8px 15px;
  background: var(--mbr-panel);
  border: 1px solid var(--mbr-edge);
  color: var(--mbr-ink);
  font-size: 13.5px;
  font-weight: 600;
  box-shadow: var(--mbr-shadow);
  -webkit-backdrop-filter: var(--mbr-blur);
          backdrop-filter: var(--mbr-blur);
}
html.respfix body.phare-light .mbr-wrap > .mbr-chip { color: var(--mbr-ink); }
/* Les chips « intérêts » restent des chips : ils sont DANS la carte opaque. */
html.respfix .mbr-top .mbr-chip { color: var(--mbr-gold); }

/* Page « Voyageur introuvable » */
html.respfix .mbr-empty {
  text-align: center;
  padding: 26px 20px;
  margin-top: 24px;
}
html.respfix .mbr-empty .mbr-name { margin: 0 0 8px; }
html.respfix .mbr-empty-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  margin-top: 14px;
  padding: 10px 18px;
  border-radius: 11px;
  text-decoration: none;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  background: var(--mbr-sec-bg);
  border: 1px solid var(--mbr-sec-edge);
  color: var(--mbr-sec-ink);
}

/* Fiches « Lieux proposés » — les style="" inline sont remplacés par ces
   classes dans membre.php (sinon impossible de les thémer). */
html.respfix .mbr-places {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
html.respfix .mbr-place {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 10px 12px;
  border: 1px solid var(--mbr-edge);
  border-radius: 12px;
  background: var(--mbr-place-bg);
}
html.respfix .mbr-place-ico  { flex: 0 0 auto; font-size: 18px; line-height: 1 }
html.respfix .mbr-place-txt  { min-width: 0 }
html.respfix .mbr-place-name {
  display: block;
  color: var(--mbr-ink);
  font-weight: 600;
  font-size: 14px;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
html.respfix .mbr-place-sub {
  display: block;
  color: var(--mbr-ink-soft);
  font-size: 12.5px;
  line-height: 1.35;
  margin-top: 2px;
  overflow-wrap: anywhere;
}

/* Les souvenirs : la bordure suivait var(--edge), inexistante en nuit. */
html.respfix .mbr-photos img { border-color: var(--mbr-edge); }


/* ==========================================================================
   3. R9 — LA BARRE D'ACTIONS
   --------------------------------------------------------------------------
   La barre est elle aussi POSÉE SUR LA PHOTO : elle reçoit donc la même
   plaque que les cartes. C'est ce qui autorise les actions sensibles à être
   en « contour seul » tout en restant lisibles.
   ========================================================================== */
html.respfix .mbr-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--mbr-act-min)), 1fr));
  gap: 10px;
  align-items: stretch;                      /* → hauteurs égales par rangée */
  margin-top: 16px;
  padding: 14px;
  background: var(--mbr-panel);
  border: 1px solid var(--mbr-edge);
  border-radius: 16px;
  box-shadow: var(--mbr-shadow);
  -webkit-backdrop-filter: var(--mbr-blur);
          backdrop-filter: var(--mbr-blur);
}

/* --- LE SÉLECTEUR STRUCTUREL ---------------------------------------------
   Par TYPE d'élément, pas par liste de classes : il attrape aussi bien les
   boutons de la page que ceux dessinés par companions.js, à travers les
   enveloppes display:contents (.mbr-comp / .comp-btn / .comp-respond).
   Deux exclusions seulement :
     .comp-menu-item  → entrées du menu « ⋯ », pas des actions de la barre ;
     .mbr-report-send → bouton d'envoi À L'INTÉRIEUR du panneau de signalement.
   ------------------------------------------------------------------------- */
html.respfix .mbr-actions :is(a, button, summary):not(.comp-menu-item):not(.mbr-report-send) {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  min-width: 0;
  min-height: 48px;                          /* MÊME hauteur pour tous */
  margin: 0;
  padding: 11px 14px;
  border-radius: 12px;

  font-family: inherit;                      /* <button> n'hérite pas seul */
  font-size: clamp(13px, .95vw, 14px);
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  white-space: normal;                       /* on coupe la LIGNE, jamais le mot */
  overflow-wrap: break-word;
  text-wrap: balance;

  cursor: pointer;
  list-style: none;
  transition: border-color .2s ease, filter .2s ease, transform .12s ease;

  /* Peau lue dans des jetons → les variantes n'ont qu'à changer les jetons,
     plus aucune bataille de !important entre règles. */
  background: var(--mbr-sec-bg) !important;
  border: 1px solid var(--mbr-sec-edge) !important;
  color: var(--mbr-sec-ink) !important;
}
html.respfix .mbr-actions summary::-webkit-details-marker { display: none }
html.respfix .mbr-actions summary::marker { content: "" }

/* Icônes de MÊME taille, quelle qu'en soit la provenance. */
html.respfix .mbr-actions :is(a, button, summary) svg      { width: 18px; height: 18px; flex: 0 0 auto }
html.respfix .mbr-actions :is(a, button, summary) .comp-ava{ width: 22px; height: 22px; flex: 0 0 auto }

/* --- Hiérarchie ---------------------------------------------------------- */

/* PRINCIPALE — « Contacter … » : pleine, dorée/feu (elle avait été aplatie
   au même gris-cyan que tout le reste le 09/07). */
html.respfix .mbr-actions .mbr-btn-primary {
  --mbr-sec-bg:    var(--mbr-pri-bg);
  --mbr-sec-edge:  var(--mbr-pri-edge);
  --mbr-sec-edge2: var(--mbr-pri-edge2);
  --mbr-sec-ink:   var(--mbr-pri-ink);
  font-weight: 700;
}

/* SENSIBLES — « Bloquer » / « Signaler » : contour seul, jamais dominantes.
   ⚠️ « Bloquer » N'A PAS d'attribut type : buttons-premium.css cible
   button[type="submit"] et lui collerait le halo renforcé + le filet
   lumineux tournant réservés aux actions PRINCIPALES. Sans l'attribut, le
   bouton soumet exactement pareil. NE PAS AJOUTER type="submit". */
html.respfix .mbr-actions .mbr-btn-danger,
html.respfix .mbr-actions .comp-refuse,
html.respfix .mbr-actions > .mbr-report > summary {
  --mbr-sec-bg:    var(--mbr-dan-bg);
  --mbr-sec-edge:  var(--mbr-dan-edge);
  --mbr-sec-edge2: var(--mbr-dan-edge2);
  --mbr-sec-ink:   var(--mbr-dan-ink);
  font-weight: 600;
}

/* --- États --------------------------------------------------------------- */
html.respfix .mbr-actions :is(a, button, summary):not(.comp-menu-item):not(.mbr-report-send):hover {
  border-color: var(--mbr-sec-edge2) !important;
  filter: brightness(1.07);
}
html.respfix .mbr-actions :is(a, button, summary):not(.comp-menu-item):not(.mbr-report-send):focus-visible {
  outline: 2px solid var(--mbr-focus);
  outline-offset: 2px;
  border-color: var(--mbr-sec-edge2) !important;
}
html.respfix .mbr-actions :is(a, button, summary):not(.comp-menu-item):not(.mbr-report-send):active {
  transform: translateY(1px);
}
@media (prefers-reduced-motion: reduce) {
  html.respfix .mbr-actions :is(a, button, summary) { transition: none }
  html.respfix .mbr-actions :is(a, button, summary):active { transform: none }
}

/* --- Les enveloppes ------------------------------------------------------ */

/* Le <span class="mbr-comp"> : son style inline (1,0,0,0) battait le
   display:contents de la ligne 129. Le style inline est retiré dans le PHP,
   on redit ici la règle pour ne dépendre de rien. */
html.respfix .mbr-actions .mbr-comp,
html.respfix .mbr-actions .mbr-comp .comp-btn,
html.respfix .mbr-actions .mbr-comp .comp-respond { display: contents }

/* LA QUATRIÈME ENVELOPPE — celle que personne n'avait vue.
   L'énoncé de R9 parle de « 3 enfants sur 5 » : c'est vrai du HTML servi par
   PHP. Mais companions.js REDESSINE le bouton après coup, et son état
   « accepted » (companions.js:44-48, celui de deux membres DÉJÀ compagnons)
   ajoute une enveloppe de plus :
       <span class="comp-accepted">            ← inline-flex (companions.css:9)
         <span class="comp-badge-ok">✓ Compagnon de route</span>
         <a class="comp-msg">✉ Message</a>
         <span class="comp-more-wrap">⋯</span>  ← déjà masquée (membre.php:155)
       </span>
   Elle n'est ni .comp-btn ni .comp-respond : les deux listes de classes qui
   existaient (celle du 09/07 et la première version de ce fichier) la
   laissaient passer. Résultat : badge + « Message » écrasés dans UNE SEULE
   case, à côté de voisins pleine largeur. Or la hiérarchie demandée compte
   « Message » ET « Compagnon de route » comme deux actions secondaires de
   plein droit. C'est exactement le défaut que R9 dénonce — la preuve qu'il
   fallait un sélecteur structurel et non une énumération. */
html.respfix .mbr-actions .mbr-comp .comp-accepted { display: contents }

/* Le badge « ✓ Compagnon de route » est un <span> : le sélecteur structurel
   :is(a, button, summary) l'ignore, et c'est VOULU — c'est un état, pas une
   action. Il lui faut malgré tout la même BOÎTE que ses voisins, sinon la
   rangée est bancale.
   On lui donne donc la géométrie, jamais les affordances : pas de curseur
   main, aucune règle de survol ni de focus ne le vise (les blocs « États »
   ci-dessus sont limités à a/button/summary). C'est ce qui le distingue
   naturellement d'un bouton dès que la souris passe dessus.
   Son white-space:nowrap (companions.css:15) est levé : « ✓ Compagnon de
   route » ne tient pas dans une case de 150px et poussait la barre hors du
   bloc sur téléphone. Contraste mesuré : 15,2:1 en nuit, 9,5:1 en jour. */
html.respfix .mbr-actions .comp-badge-ok {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  min-width: 0;
  min-height: 48px;
  margin: 0;
  padding: 11px 14px;
  border-radius: 12px;

  font-family: inherit;
  font-size: clamp(13px, .95vw, 14px);
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  white-space: normal;
  overflow-wrap: break-word;

  cursor: default;
  background: var(--mbr-sec-bg);
  border: 1px solid var(--mbr-sec-edge);
  color: var(--mbr-sec-ink);
}

/* Le <form> « Bloquer » : il était l'item réel, et le flex du <button> était
   du code mort. En grille imbriquée, le bouton remplit exactement sa case
   (donc même hauteur que ses voisins), sans display:contents sur un <form>. */
html.respfix .mbr-actions .mbr-actform { display: grid; margin: 0; min-width: 0 }

/* Le <details> « Signaler » : déplacé DANS la barre (impossible en CSS).
   Fermé → une case comme les autres ; ouvert → il prend la largeur entière
   pour que le champ de saisie soit utilisable. */
html.respfix .mbr-actions > .mbr-report {
  display: grid;
  align-content: stretch;
  margin: 0;
  min-width: 0;
}
html.respfix .mbr-actions > .mbr-report[open] {
  grid-column: 1 / -1;
  align-content: start;
}
html.respfix .mbr-actions > .mbr-report > summary { max-width: none }

html.respfix .mbr-report-form { margin-top: 10px }
html.respfix .mbr-report-txt {
  width: 100%;
  box-sizing: border-box;
  min-height: 84px;
  padding: 10px 12px;
  background: var(--mbr-place-bg);
  border: 1px solid var(--mbr-edge);
  border-radius: 10px;
  color: var(--mbr-ink);
  font-family: inherit;
  font-size: 14px;
  line-height: 1.45;
  resize: vertical;
}
html.respfix .mbr-report-txt::placeholder { color: var(--mbr-ink-soft); opacity: .9 }
html.respfix .mbr-report-txt:focus-visible { outline: 2px solid var(--mbr-focus); outline-offset: 2px }
html.respfix .mbr-report-send {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  margin-top: 10px;
  padding: 11px 18px;
  border-radius: 11px;
  background: var(--mbr-dan-bg);
  border: 1px solid var(--mbr-dan-edge);
  color: var(--mbr-dan-ink);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
html.respfix .mbr-report-send:hover        { border-color: var(--mbr-dan-edge2) }
html.respfix .mbr-report-send:focus-visible{ outline: 2px solid var(--mbr-focus); outline-offset: 2px }


/* ==========================================================================
   4. TAILLES D'ÉCRAN
   --------------------------------------------------------------------------
   Un seul réglage : --mbr-act-min. Le nombre de colonnes en découle, la
   grille auto-fit fait le reste. Aucun bouton n'est masqué, jamais.

   Le nombre de colonnes se DÉDUIT (n colonnes tiennent si n·min + (n−1)·gap
   ≤ largeur utile) ; ces valeurs sont calculées, pas choisies à l'œil.

   ORDINATEUR / TABLETTE — utile ≈ 760 − 32 (marge wrap) − 28 (plaque) = 700px,
   gap 10 :  190px → 3 col. (3·190+20 = 590 ≤ 700 ; 4·190+30 = 790 > 700).
   L'état « déjà compagnons » compte 6 actions → 2 rangées de 3, équilibrées.

   ≤700px — utile = L − 60, gap 10 :
     160px → encore 3 colonnes jusqu'à ~560px.

   ≤560px — utile = L − 56, gap 8 : le cahier des charges impose 1 OU 2
   colonnes. À 150px on en obtenait encore TROIS (3·150+16 = 466 ≤ 504 de
   largeur utile à 560px) — des cases de 168px où « Ajouter à mes compagnons
   de route » tombait sur quatre lignes. 175px est le premier palier qui
   interdit la 3e colonne (3·175+16 = 541 > 504) tout en gardant la 2e
   (2·175+8 = 358 ≤ 504). Sous ~420px, min(100%, …) fait passer à 1 colonne
   tout seul — rien à forcer, rien à cacher.
   ========================================================================== */
@media (max-width: 700px) {
  html.respfix .mbr-wrap { --mbr-act-min: 160px }
}
@media (max-width: 560px) {
  html.respfix .mbr-wrap    { --mbr-act-min: 175px }
  html.respfix .mbr-actions { padding: 12px; gap: 8px }
  html.respfix .mbr-actions :is(a, button, summary):not(.comp-menu-item):not(.mbr-report-send),
  html.respfix .mbr-actions .comp-badge-ok {
    padding: 10px 10px;
    font-size: 13px;
  }
  /* iOS zoome automatiquement sur un champ dont la police est < 16px. */
  html.respfix .mbr-report-txt { font-size: 16px }
}

/* Très petit écran : la barre passe à une colonne d'elle-même grâce au
   min(100%, …) — rien à forcer, rien à cacher. */

/* ================= 23-sorties.css ================= */

/* ==========================================================================
   23 — PAGE SORTIES (sortir.php) — bloc « Exemples de sorties »
   --------------------------------------------------------------------------
   Tout est préfixé « html.respfix » : sans la classe sur <html>, ce fichier
   ne peut RIEN changer.

   R12 — CE QUI A RÉELLEMENT ÉTÉ TROUVÉ (le diagnostic reçu était en partie faux) :

   (a) « ob_master_on() faux → balisage nu » : RÉFUTÉ. sortir.php:196 appelle
       ob_render_examples() → ob_examples() → onboarding.php:425
       « if (!ob_on('examples')) return []; » → ob_on() → onboarding.php:100
       « if (!ob_master_on()) return false; ». Maître éteint = tableau vide =
       chaîne vide = AUCUN balisage. Il ne peut donc pas sortir « nu » par là.

   (b) Ce qui est vrai : la chaîne de chargement est bien désynchronisée, mais
       par les DEUX AUTRES gardes de footer.php:206
       (« if (!$__embed && !empty($me)) »). En ?embed=1 le corps de page émet
       les cartes et le pied de page saute onboarding-widget.php : le bloc sort
       alors réellement SANS onboarding.css. Et même hors embed, la feuille
       étant liée depuis le PIED de page, les cartes sont peintes une première
       fois sans style (FOUC). → corrigé côté PHP : ob_css_link_once().

   (c) LA cause du visuel montré par Julien : onboarding.css:78-81 existe bien,
       mais son fond vaut rgba(255,255,255,.05) — 5 % de blanc, posés
       directement sur le fond de page (nuit #0a1322 par défaut, ou une photo
       voilée si site.bg.sortir est réglé). Dans les deux cas l'écart avec le
       fond est imperceptible : à l'œil, la carte n'a PAS de fond, et les
       cartes n'étaient regroupées dans aucun conteneur. L'auditeur avait
       raison sur le SYMPTÔME, les vérificateurs avaient raison sur le CODE.
       (Vérifié : assets/css/onboarding.css est identique en local et en prod,
        md5 dd6ae663133da1240eec0f088625e881 — les lignes citées sont sûres.)

   (d) VARIABLES HORS PORTÉE : onboarding.css:8-19 déclare --ob-ink /
       --ob-ink-muted / --ob-glass sur « .ob-empty, .ob-welcome, .ob-checklist »
       UNIQUEMENT. .ob-examples n'en fait pas partie → couleurs écrites en dur
       (#eaf2f8 l.87, #b9cbd9 l.88) et AUCUNE règle .ob-* dans phare-light.css
       (vérifié : 0 occurrence) → Phare allumé, ces textes clairs se posaient
       sur un voile blanc.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1) LES VARIABLES --ob-* REMONTENT SUR body (défaut (d))
   Elles n'écrasent RIEN : « .ob-empty, .ob-welcome, .ob-checklist » les
   redéclarent sur elles-mêmes (une déclaration sur l'élément bat toujours une
   valeur héritée), leur rendu reste donc identique au pixel près. Elles
   servent uniquement aux composants .ob-* qui n'en avaient aucune — au
   premier rang desquels les cartes « Exemple ».
   -------------------------------------------------------------------------- */
html.respfix body {
  --ob-cyan:      #3DA9FC;
  --ob-cyan-soft: #5BC0EB;
  --ob-cyan-deep: #1B6FB8;
  --ob-gold:      #F3D58A;
  --ob-glass:     rgba(11, 15, 23, .82);
  --ob-edge:      rgba(255, 255, 255, .14);
  --ob-ink:       #eaf2f8;
  --ob-ink-muted: #a9c0d4;
}

/* Phare allumé (thème clair) : l'encre s'inverse. Mêmes valeurs que
   phare-light.css:37-38 (--ink #121821 / --ink-soft #3a4757) et que la surface
   de carte du thème clair (phare-light.css:237), pour rester dans la palette
   du site et non inventer une nouvelle. */
html.respfix body.phare-light {
  --ob-ink:       #121821;
  --ob-ink-muted: #3a4757;
  --ob-glass:     rgba(255, 255, 255, .42);
}


/* --------------------------------------------------------------------------
   2) LE BLOC PARENT « Exemples de sorties… »
   Il doit se lire comme le FRÈRE du grand bloc « Aucune sortie pour
   l'instant » qui le précède (sortir.php:178, classe .adm-card). On reprend
   donc EXACTEMENT ses valeurs : même dégradé sombre transparent, même rayon,
   même bordure, même flou, même ombre — au lieu d'inventer une troisième
   apparence de panneau.

   ⚠️ SOURCE VÉRIFIÉE EN PROD, PAS EN LOCAL. assets/css/admin-v802.css est
   chargée par includes/header.php:165 mais elle N'EST PAS dans cette copie de
   travail (resync partiel) : impossible de la lire ici. Valeurs relevées
   directement sur le serveur (ssh, 18/07) —
     admin-v802.css:349-357  .adm-card {
       background: linear-gradient(160deg, rgba(18,32,58,.45), rgba(12,22,40,.42));
       border: 1px solid rgba(255,255,255,.14);
       border-radius: var(--r-lg);
       box-shadow: var(--sh), inset 0 1px 0 rgba(255,255,255,.03);
       backdrop-filter: blur(12px); }
     admin-v802.css:55  --r-lg: 18px;   ·  :59  --sh: 0 6px 22px rgba(0,0,0,.40);
   Ces deux jetons sont déclarés sur :root (admin-v802.css:8) : ils sont donc
   bien visibles ici. Les valeurs écrites en repli dans var(--r-lg, 18px) et
   var(--sh, …) ci-dessous sont les VRAIES valeurs des jetons ; elles ne
   servent que si admin-v802.css venait à ne pas se charger.
   -------------------------------------------------------------------------- */
html.respfix .ob-ex-panel {
  margin: clamp(14px, 1.6vw, 20px) 0 0;
  padding: clamp(16px, 1.8vw, 22px);
  border-radius: var(--r-lg, 18px);
  background: linear-gradient(160deg, rgba(18, 32, 58, .45), rgba(12, 22, 40, .42));
  border: 1px solid var(--ob-edge);
  box-shadow: var(--sh, 0 6px 22px rgba(0, 0, 0, .40)), inset 0 1px 0 rgba(255, 255, 255, .03);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: var(--ob-ink);
}
/* Repli propre là où le flou n'existe pas : on opacifie un peu le fond plutôt
   que de laisser la photo remonter à travers. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html.respfix .ob-ex-panel {
    background: linear-gradient(160deg, rgba(14, 24, 44, .88), rgba(10, 18, 34, .86));
  }
}
/* Phare allumé : mêmes valeurs que l'override du voisin, relevé en prod —
   phare-light.css:234-243 « body.phare-light .section-card, .premium-card,
   .adm-card ». Le flou y passe à 10px (et non 12) : on le suit aussi, sinon
   les deux panneaux voisins n'auraient pas tout à fait le même rendu. */
html.respfix body.phare-light .ob-ex-panel {
  background: var(--phare-card-bg, rgba(255, 255, 255, .42));
  border-color: rgba(20, 40, 70, .14);
  box-shadow: 0 2px 14px rgba(10, 20, 40, .05);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

/* Le titre : un VRAI titre de section, plus une simple ligne de texte.

   CONTRASTE — RECALCULÉ, l'estimation précédente était FAUSSE.
   Le fond de sortir.php n'est pas « une photo » : body.pub-body impose
   var(--pub-night-1) = #0a1322 (public-v802.css:20-30, en !important). Une
   photo n'apparaît que si le réglage admin site.bg.<page> est rempli, et elle
   passe alors TOUJOURS sous un voile sombre rgba(6,11,22,·) réglable
   (includes/header.php:300-303, défaut 0.48 en haut → 0.66 en bas).
   Ratios recomposés couche par couche (photo → voile → panneau .435 → texte) :

     A. aucune photo (état par défaut de la page) ......... h2 15,91:1  ✅
     B. photo BLANCHE + voile par défaut 0.48 ............. h2  6,15:1  ✅
     C. photo BLANCHE + bas du voile 0.66 ................. h2  9,07:1  ✅
     D. photo BLANCHE + voile abaissé à 0.20 en admin ..... h2  3,49:1  ⚠️
     E. photo BLANCHE + voile retiré (théorique) .......... h2  2,44:1  ❌
   (cartes : titre ≥ 10,76:1 et texte secondaire ≥ 6,48:1 dans TOUS les cas.)

   Donc : conforme partout où la page peut réellement se trouver (A/B/C), et
   dégradé seulement si un voile est réglé anormalement bas (D/E).
   On ne fonce PAS le panneau pour couvrir D/E : la consigne est qu'il soit
   identique au bloc « Aucune sortie » juste au-dessus (.adm-card, même fond),
   dont le propre <h3> subit exactement la même chose. Assombrir ici créerait
   deux panneaux voisins d'aspect différent, et corrigerait au mauvais endroit.
   → D/E signalés comme point à valider (c'est un réglage, pas ce fichier). */
html.respfix .ob-ex-panel-title {
  margin: 0;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: clamp(15px, 1.15vw, 18px);
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: .01em;
  color: var(--ob-ink);
  overflow-wrap: break-word;   /* un titre réglé en admin peut être long */
  /* Assurance lisibilité pour les cas D/E ci-dessus (voile réglé très bas) :
     une ombre portée détache le titre du fond sans toucher au panneau ni à la
     mise en page. Ça n'améliore pas le ratio WCAG (qui ne sait pas lire une
     ombre), mais ça rend le texte lisible à l'œil dans le cas dégradé. */
  text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
}
/* Phare allumé : encre sombre sur fond clair → l'ombre noire n'a plus de sens. */
html.respfix body.phare-light .ob-ex-panel-title {
  text-shadow: 0 1px 2px rgba(255, 255, 255, .6);
}
/* Petit filet doré : rappel de l'identité « feu », sans nouvel élément HTML. */
html.respfix .ob-ex-panel-title::after {
  content: '';
  display: block;
  width: 42px;
  height: 2px;
  margin-top: 9px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--ob-gold), rgba(243, 213, 138, 0));
}
html.respfix .ob-ex-panel-note {
  margin: 10px 0 0;
  font-size: clamp(12.5px, .9vw, 13.5px);
  line-height: 1.5;
  color: var(--ob-ink-muted);
}


/* --------------------------------------------------------------------------
   3) LA GRILLE — 3 / 2 / 1 colonnes, hauteur dictée par le contenu
   repeat(auto-fit, minmax(min(100%, 260px), 1fr)) :
     · le « min(100%, 260px) » évite le débordement quand le panneau est plus
       étroit que 260px (le minmax() seul déborderait) ;
     · auto-fit fait tomber 3 → 2 → 1 colonne tout seul, sans point de rupture
       écrit à la main, donc sans écran « entre deux » oublié ;
     · align-items:stretch (défaut de la grille, réaffirmé) + height:100% sur la
       carte = hauteurs homogènes sur une même rangée.
   -------------------------------------------------------------------------- */
html.respfix .ob-ex-panel .ob-examples {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  align-items: stretch;
  gap: clamp(10px, 1vw, 14px);
  margin: clamp(12px, 1.2vw, 16px) 0 0;
  text-align: left;
}
/* .ob-examples-solo bornait la grille à 720px et la centrait : à l'intérieur
   du panneau, c'est le panneau qui borne. On neutralise sans toucher au
   fichier d'origine (il reste valable pour les pages qui l'utilisent seul). */
html.respfix .ob-ex-panel .ob-examples-solo {
  max-width: none;
  margin-inline: 0;
}


/* --------------------------------------------------------------------------
   4) LES CARTES « EXEMPLE » — enfin un vrai fond, et du contraste
   Avant : rgba(255,255,255,.05) sur la photo de fond = invisible.
   Ici : une plaque sombre franche, en retrait sur le panneau (effet de
   profondeur), sur laquelle un texte clair repasse largement au-dessus du
   seuil de lisibilité. Le badge « EXEMPLE » est CONSERVÉ tel quel.
   -------------------------------------------------------------------------- */
html.respfix .ob-ex-panel .ob-example {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;                       /* équivalent grille du min-width:0 flex */
  /* PAS de height:100% ici. MESURÉ à 1440px : height:100% sur un élément de
     grille dont la rangée est dimensionnée par son contenu fait DÉBORDER la
     carte de 11px sous la bordure du panneau (carte 321→482, panneau →471).
     align-items:stretch (ci-dessus) égalise déjà les hauteurs, correctement. */
  padding: clamp(13px, 1.1vw, 17px);
  border-radius: 14px;
  /* .72 et non .05 (valeur d'origine) ni .58 : MESURÉ dans le pire cas (photo
     de fond claire, sans le voile du site), .58 laissait le texte secondaire à
     4,08:1 — sous le seuil de 4,5:1. À .72 il monte à 7,4:1 et le titre à
     12,2:1, sans perdre la transparence ni le contraste de profondeur. */
  background: rgba(8, 14, 26, .72);
  border: 1px dashed rgba(243, 213, 138, .55);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .04);
}
html.respfix body.phare-light .ob-ex-panel .ob-example {
  background: rgba(255, 255, 255, .62);
  border-color: rgba(150, 112, 20, .45);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7);
}

/* Le badge garde son dégradé doré ; on l'empêche seulement de s'étirer sur
   toute la largeur maintenant que la carte est un conteneur flex. */
html.respfix .ob-ex-panel .ob-example-tag {
  align-self: flex-start;
  margin-bottom: 2px;
}

/* Contraste : les deux couleurs écrites en dur (#eaf2f8 / #b9cbd9) passent sur
   les variables, donc elles suivent le Phare. */
html.respfix .ob-ex-panel .ob-example-title {
  margin-bottom: 0;
  font-size: clamp(14px, 1vw, 15.5px);
  font-weight: 700;
  line-height: 1.3;
  color: var(--ob-ink);
  overflow-wrap: break-word;
}
html.respfix .ob-ex-panel .ob-example-text {
  font-size: clamp(13px, .95vw, 14px);
  line-height: 1.5;
  color: var(--ob-ink-muted);
  overflow-wrap: break-word;
}
/* Une carte sans titre (cas 'feu-de-camp' : title vide) ne doit pas laisser
   un trou : le texte prend la place. */
html.respfix .ob-ex-panel .ob-example-text:only-of-type { margin-top: 2px; }


/* --------------------------------------------------------------------------
   5) MÊME TRAITEMENT HORS PANNEAU
   Les pages qui appellent encore ob_render_examples() sans le panneau (ou
   ob_render_empty() avec examples) profitent au moins du fond et du contraste,
   sans quoi le défaut (c) subsisterait ailleurs.
   -------------------------------------------------------------------------- */
html.respfix .ob-examples { align-items: stretch; }
html.respfix .ob-example {
  min-width: 0;
  background: rgba(8, 14, 26, .58);
}
html.respfix body.phare-light .ob-example { background: rgba(255, 255, 255, .62); }
html.respfix .ob-example-title { color: var(--ob-ink); overflow-wrap: break-word; }
html.respfix .ob-example-text  { color: var(--ob-ink-muted); overflow-wrap: break-word; }


/* --------------------------------------------------------------------------
   6) EMPILEMENT — les meubles flottants de l'onboarding rejoignent l'échelle
   commune du socle. Aucun nombre en dur.
   (Le socle 00 pose déjà ces quatre lignes ; on les redit ici pour que la
   page reste correcte quel que soit l'ordre de concaténation des fichiers,
   et parce que ces éléments sont émis par les fichiers dont je suis
   propriétaire.)
   -------------------------------------------------------------------------- */
html.respfix .obw-launch { z-index: var(--z-flottants); }
html.respfix .obw-scrim,
html.respfix .ob-tour-scrim { z-index: var(--z-voiles); }
html.respfix .obw-panel { z-index: var(--z-panneaux); }
html.respfix .ob-toast { z-index: var(--z-toast); }


/* --------------------------------------------------------------------------
   7) TÉLÉPHONE — le panneau ne colle pas aux bords, et il ne peut pas être
   coupé. La réserve d'espace en bas est déjà assurée par le socle (R5) :
   on ne la refait pas.
   -------------------------------------------------------------------------- */
@media (max-width: 600px) {
  html.respfix .ob-ex-panel { padding: 15px 14px 16px; }
  html.respfix .ob-ex-panel .ob-examples { gap: 10px; }
}

/* ================= 24-feu-de-camp.css ================= */

/* ==========================================================================
   24 — FEU DE CAMP / SALON EN DIRECT   (feu-de-camp.php)
   --------------------------------------------------------------------------
   Tout est préfixé « html.respfix ». Sans la classe sur <html>, ce fichier ne
   peut RIEN changer : interrupteur éteint = page identique au pixel près.

   Le socle (00-socle.css) a déjà traité la barre du haut, le rail gauche,
   overflow-x, la réserve d'espace en bas et l'échelle d'empilement.
   Ici, UNIQUEMENT le contenu propre à cette page :

     A. La barre d'onglets « Discussion / Entraide / Conseils / Alertes / Route »
        est alignée sur la MÊME colonne et la MÊME largeur que la fenêtre du
        salon, et centrée au-dessus d'elle.
     B. R13 — l'ordre des 4 blocs de .fc-layout est déclaré EN ENTIER dans
        chaque contexte (l'order:5 orphelin ne décide plus de rien).
     C. Les hauteurs 84vh / 78vh, qui ignoraient la hauteur réelle de la barre
        du haut, deviennent des calculs à partir de --hdr-h mesurée en direct.

   CE QUI N'EST PAS TOUCHÉ, VOLONTAIREMENT :
     · le bloc « Cœur du feu » reste exactement où il est (entre les onglets et
       le salon) — son éventuel déplacement est une décision de Julien ;
     · .fc-sidebar (CSS mort, le PHP est sous « if (false) » depuis le 23/06) ;
     · le bloc « Le Feu attend ses premières histoires », placé exprès dans la
       colonne de gauche depuis le 07/07.
   ========================================================================== */

/* --------------------------------------------------------------------------
   VALEURS PARTAGÉES DE LA PAGE
   Elles ne remplacent aucune variable du socle : ce sont des noms nouveaux.
   Les trois premières reprennent À L'IDENTIQUE les valeurs déjà écrites
   feu-de-camp.php:301 (280px / 1fr / 300px, gouttière 22px). L'intérêt n'est
   pas de les changer — c'est qu'il n'y ait plus qu'UN endroit qui les décide,
   pour que la barre d'onglets et la fenêtre du salon ne puissent plus se
   désaligner l'une de l'autre.
   -------------------------------------------------------------------------- */
html.respfix {
  --fc-col-gauche: 280px;   /* colonne « Allumer le feu »  (valeur d'origine) */
  --fc-col-droite: 300px;   /* colonne « En ligne »        (valeur d'origine) */
  --fc-gouttiere: 22px;     /* gap de .fc-layout           (valeur d'origine) */

  /* Place à laisser SOUS la fenêtre du salon pour la barre d'écriture et sa
     marge. Élastique, jamais un panneau figé. */
  --fc-place-ecriture: clamp(62px, 7vh, 84px);

  /* Planchers : une fenêtre ne doit jamais se réduire à rien sur un écran très
     bas (ordinateur portable 13", fenêtre à moitié réduite…). Ce sont les
     valeurs déjà présentes dans la page, reprises telles quelles. */
  --fc-plancher-salon: 300px;
  --fc-plancher-presence: 240px;

  /* Décalage d'origine du panneau « En ligne » collé (top:14px). */
  --fc-collage: 14px;
}

/* ==========================================================================
   A. LA BARRE D'ONGLETS EST RATTACHÉE AU SALON
   --------------------------------------------------------------------------
   Le vrai sujet de Julien. Ce n'est PAS un chevauchement : .fc-filters
   (l.1295) et .fc-layout (l.1305) sont deux blocs statiques, en flux normal,
   avec une marge basse POSITIVE. Ils ne peuvent pas se superposer.

   Le défaut réel est un défaut de LARGEUR et d'ALIGNEMENT : la barre d'onglets
   s'étalait sur toute la largeur de .pub-content (jusqu'à 1500px), pendant que
   la fenêtre du salon n'occupe que la colonne du milieu d'une grille
   280px / 1fr / 300px.
   MESURÉ au banc (fenêtre 982px, avant correctif) : barre d'onglets 922px de
   large contre 298px pour la fenêtre du salon ; la barre commençait 302px à
   GAUCHE du salon et finissait 322px à sa DROITE. Elle survolait donc la
   colonne « Allumer le feu » et la colonne « En ligne », sans plus aucun
   rapport visuel avec la fenêtre qu'elle commande.
   APRÈS correctif, même fenêtre : 298px contre 298px, écart gauche 0, écart
   droite 0. Vérifié identique à 320 / 375 / 768 / 950 / 982 / 1440 / 1920px.

   Correctif : le conteneur de section (.fc-salon-rail, ajouté en PHP sous
   respfix_on()) reprend LA MÊME grille que .fc-layout, et la barre d'onglets
   est posée dans LA MÊME colonne que la fenêtre du salon. Même largeur, mêmes
   bords, alignement exact — par construction, pas par réglage à la main.
   ========================================================================== */

/* Une seule déclaration de grille pour les deux, donc plus de dérive possible. */
@media (min-width: 981px) {
  html.respfix .fc-layout,
  html.respfix .fc-salon-rail {
    display: grid;
    grid-template-columns: var(--fc-col-gauche) minmax(0, 1fr) var(--fc-col-droite);
    gap: var(--fc-gouttiere);
    align-items: start;
  }
  /* Colonne 2 = exactement celle de .fc-center-col (feu-de-camp.php:303). */
  html.respfix .fc-salon-rail > .fc-filters { grid-column: 2; grid-row: 1; }
}

/* Sous 981px la grille repasse à une colonne : le salon prend toute la
   largeur, les onglets aussi. L'alignement reste donc vrai. */
html.respfix .fc-salon-rail { min-width: 0; }

/* --- L'aspect : une vraie barre d'onglets, du même bois que la fenêtre ---
   Mêmes teintes que .fc-center-col (bordure feu, fond sombre dégradé) pour
   qu'on lise « ces onglets commandent CETTE fenêtre » — sans rien inventer :
   les valeurs sont celles de .fc-center-col (feu-de-camp.php:310-319). */
html.respfix .fc-salon-rail > .fc-filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;   /* centrée au-dessus du salon */
  align-items: center;
  gap: 8px;
  min-width: 0;
  box-sizing: border-box;
  padding: 9px 12px;
  border: 1px solid rgba(255, 90, 35, .38);
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(20, 12, 8, .62), rgba(12, 8, 6, .50));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .04);
}

/* Mode clair (Phare allumé) : même logique, palette claire de la page. */
html.respfix body.phare-light .fc-salon-rail > .fc-filters {
  border: 1px solid rgba(20, 40, 70, .14);
  background: linear-gradient(180deg, #ffffff, #f5f8fc);
  box-shadow: 0 6px 18px rgba(20, 40, 70, .06), inset 0 1px 0 rgba(255, 255, 255, .6);
}

/* --- Les pastilles ne peuvent plus être coupées en deux ---
   white-space:nowrap  : un libellé ne se casse jamais au milieu d'un mot.
   min-width:0         : la pastille garde le droit de descendre à la ligne
                         suivante au lieu de pousser la rangée en débordement.
   Précaution relevée par les vérificateurs, utile à toute largeur. */
html.respfix .fc-salon-rail .fc-filter {
  white-space: nowrap;
  min-width: 0;
  flex: 0 0 auto;
}

/* Écran étroit : DEUX (ou trois) lignes centrées. Pas de défilement horizontal,
   pas de demi-bouton coupé, aucun onglet caché : ils retournent simplement à
   la ligne et restent centrés sous les précédents. */
@media (max-width: 980px) {
  html.respfix .fc-salon-rail > .fc-filters {
    justify-content: center;
    row-gap: 7px;
    padding: 8px 10px;
  }
}
@media (max-width: 420px) {
  /* Très petit téléphone : on récupère de la place sur les côtés de chaque
     pastille plutôt que de rogner le texte. */
  html.respfix .fc-salon-rail .fc-filter { padding-inline: 11px; }
}

/* ==========================================================================
   B. R13 — L'ORDRE DES 4 BLOCS DU SALON EST DÉCLARÉ EN ENTIER
   --------------------------------------------------------------------------
   Cause exacte : feu-de-camp.php:258 pose « order: 5 » sur .fc-write-bar
   GLOBALEMENT, hors de toute media query. Les trois autres enfants de
   .fc-layout (.fc-center-col, .fc-compose-col, .fc-presence) n'ont, eux, aucun
   order — donc 0 par défaut.

   Conséquences mesurables, aux deux endroits :
     · ≤900px, quand mobile-app.css est allumé (site.mobile.appcss_on=1) : ce
       fichier rattrape l'order:5 en donnant 1, 2, 3 à trois enfants… et oublie
       .fc-presence, resté à 0. « À moins de 2 km / En ligne maintenant » passe
       donc AVANT le salon en direct : on ouvre la page du feu de camp et on
       tombe sur une liste de noms, pas sur les messages.
     · 901–980px : mobile-app.css ne s'applique plus (max-width:900) et la
       grille bureau non plus (min-width:981). Il ne reste que l'order:5 : la
       barre d'écriture se retrouve tout en bas, sous le long formulaire
       « Allumer le feu » — exactement le défaut que le patch mobile avait
       corrigé pour les téléphones, resté intact pour les tablettes.

   Correctif : on ne pose pas un pansement « order:4 sur .fc-presence ». On
   déclare l'ordre COMPLET des quatre, dans chaque contexte. Ainsi l'order:5
   orphelin ne décide plus de rien, et un cinquième bloc ajouté un jour à
   .fc-layout ne remontera plus tout seul en tête de page.

   Ordre voulu, identique à celui que mobile-app.css visait :
     1) la fenêtre du salon   2) la barre d'écriture
     3) « Allumer le feu »    4) « En ligne maintenant »
   ========================================================================== */
html.respfix .fc-layout > .fc-center-col  { order: 1; }
html.respfix .fc-layout > .fc-write-bar   { order: 2; }
html.respfix .fc-layout > .fc-compose-col { order: 3; }
html.respfix .fc-layout > .fc-presence    { order: 4; }

/* ≥981px la grille place chaque bloc explicitement (grid-column / grid-row,
   feu-de-camp.php:301-305 et 341) : l'ordre déclaré ci-dessus n'y change aucune
   position, il ne fait qu'aligner l'ordre de peinture sur l'ordre visuel.
   On le redit quand même, pour que la règle reste vraie et lisible partout. */
@media (min-width: 981px) {
  html.respfix .fc-layout > .fc-center-col  { order: 1; }
  html.respfix .fc-layout > .fc-write-bar   { order: 2; }
  html.respfix .fc-layout > .fc-compose-col { order: 3; }
  html.respfix .fc-layout > .fc-presence    { order: 4; }
}

/* ≤900px — LE CONTEXTE OÙ LE DÉFAUT SE VOIT. On le redit ici en toutes lettres
   plutôt que de compter sur la règle globale : c'est précisément l'endroit où
   quelqu'un viendra lire le fichier le jour où l'ordre du salon bougera.

   État des lieux honnête de ce que fait chaque valeur :
     · si site.mobile.appcss_on = 1, mobile-app.css:180-191 écrit déjà 1 / 2 / 3
       en !important sur .fc-center-col / .fc-write-bar / .fc-compose-col. Nos
       valeurs sont IDENTIQUES : aucun conflit, aucun clignotement, aucune
       surprise. Le seul ajout réel est le 4 sur .fc-presence — celui que
       personne ne déclarait et qui faisait remonter « À moins de 2 km /
       En ligne maintenant » AU-DESSUS du salon en direct ;
     · si site.mobile.appcss_on = 0, mobile-app.css n'est pas chargé du tout :
       ce sont NOS quatre valeurs qui font tout le travail, et l'order:5 orphelin
       de feu-de-camp.php:258 ne décide plus de rien.
   Dans les deux cas le résultat est le même — c'est bien ce qu'on veut. */
@media (max-width: 900px) {
  html.respfix .fc-layout > .fc-center-col  { order: 1; }
  html.respfix .fc-layout > .fc-write-bar   { order: 2; }
  html.respfix .fc-layout > .fc-compose-col { order: 3; }
  html.respfix .fc-layout > .fc-presence    { order: 4; }
}

/* 901–980px — LA BANDE OUBLIÉE (Surface 912px en portrait, fenêtre de bureau
   réduite…). Ni mobile-app.css (≤900) ni la grille de bureau (≥981) ne s'y
   appliquent : avant ce fichier, il ne restait QUE l'order:5, et la barre
   d'écriture tombait tout en bas, sous le long formulaire « Allumer le feu ».
   La règle globale plus haut la couvre déjà ; on l'écrit quand même, parce
   qu'une bande que personne ne nomme est une bande que personne ne teste. */
@media (min-width: 901px) and (max-width: 980px) {
  html.respfix .fc-layout > .fc-center-col  { order: 1; }
  html.respfix .fc-layout > .fc-write-bar   { order: 2; }
  html.respfix .fc-layout > .fc-compose-col { order: 3; }
  html.respfix .fc-layout > .fc-presence    { order: 4; }
}

/* ==========================================================================
   C. LES HAUTEURS EN « vh » DEVIENNENT DES CALCULS HONNÊTES
   --------------------------------------------------------------------------
   feu-de-camp.php:304 (.fc-presence, max-height:84vh) et :334 (.fc-center-col,
   max-height:78vh) mesurent la fenêtre ENTIÈRE du navigateur, en ignorant tout
   ce qui est déjà pris : la barre du haut (.pub-topbar est « position:sticky;
   top:0 », public-v802.css:98-104) et la réserve du bas du socle.

   Or --hdr-h varie de 91 à 213px selon l'écran et la longueur du nom du membre.
   78vh sur un écran de 900px = 702px de fenêtre demandés, alors qu'il reste au
   mieux 900 − 213 − réserve ≈ 600px : le bas du salon et la barre d'écriture
   passaient sous le pli, et le panneau « En ligne » collé glissait sous la
   barre du haut.

   On repart donc de la place RÉELLEMENT disponible, avec la hauteur mesurée en
   direct par responsive-fix.js. max(plancher, calc(...)) garantit qu'une
   fenêtre très basse ne réduit jamais le salon à un timbre-poste.
   ========================================================================== */
@media (min-width: 981px) {

  /* --- La fenêtre du salon ---
     (18/07) PLAFOND RETIRÉ, À LA DEMANDE EXPLICITE DE JULIEN.
     Ce bloc calait le salon sur la place réellement disponible pour qu'il tienne
     dans l'écran sans défilement. Julien a essayé les deux au réglage interactif
     et a choisi la GRANDE fenêtre (142vh, feu-de-camp.php:334) : la flamme passe
     de 26 % à ~72 % visible, au prix d'un défilement pour atteindre la barre
     d'écriture. Compromis assumé et validé par lui.
     On n'impose donc plus de plafond ici — sinon les deux règles se battraient et
     la fenêtre serait ramenée à la hauteur d'écran, annulant son choix.
     Pour revenir au comportement « tient dans l'écran » : remettre le max-height
     ci-dessous et repasser feu-de-camp.php:334 à 78vh.
       max-height: max(var(--fc-plancher-salon),
                       calc(100vh - var(--hdr-h) - var(--reserve-bas-desktop) - var(--fc-place-ecriture)));
     Le panneau « En ligne » ci-dessous GARDE son plafond : c'est une colonne collée,
     l'étirer sur 142vh la ferait dépasser sans raison. */

  /* --- Le panneau « En ligne maintenant » ---
     Il est collé (sticky). Son top:14px d'origine le collait à 14px du HAUT DE
     LA FENÊTRE, c'est-à-dire DERRIÈRE la barre du haut, qui est elle aussi
     collée à 0. On le décale de la hauteur réelle de cette barre : il vient
     désormais se poser JUSTE SOUS elle, quelle que soit sa hauteur du moment.

     Il retranche LUI AUSSI la place de la barre d'écriture. Ce n'est pas une
     coquetterie : la rangée 1 de la grille prend la hauteur du PLUS GRAND de
     ses trois blocs. Tant que le panneau « En ligne » était le plus haut, il
     décidait seul de la hauteur de la rangée, et la barre d'écriture — qui est
     en rangée 2 — se retrouvait décrochée du bas du salon.
     MESURÉ au banc, 1440x900 : en ne plafonnant QUE le salon, l'écart entre le
     bas de la fenêtre et sa barre d'écriture montait à 83px de vide. Avec le
     même budget pour les deux, il retombe à 34px — soit exactement la gouttière
     de la grille (22px) plus la marge d'origine de la barre (12px) : plus un
     seul pixel de vide en trop. */
  html.respfix .fc-layout > .fc-presence {
    top: calc(var(--hdr-h) + var(--fc-collage));
    max-height: max(
      var(--fc-plancher-presence),
      calc(100vh - var(--hdr-h) - var(--fc-collage) - var(--reserve-bas-desktop) - var(--fc-place-ecriture))
    );
  }
}

/* dvh = la hauteur VRAIMENT visible : sur téléphone et sur tablette, 100vh
   compte la barre d'adresse du navigateur même quand elle est déployée et
   masque le bas de la page. Quand le navigateur connaît dvh, on l'utilise ;
   sinon les règles en vh ci-dessus restent en place. */
@supports (height: 100dvh) {
  @media (min-width: 981px) {
    /* (18/07) Plafond du salon retiré ici aussi — même raison qu'au-dessus :
       Julien a choisi la grande fenêtre. Laisser ce bloc l'aurait ramenée à la
       hauteur d'écran sur tous les navigateurs modernes (ceux qui connaissent dvh),
       c'est-à-dire absolument tous : le choix aurait été annulé en silence. */
    html.respfix .fc-layout > .fc-presence {
      max-height: max(
        var(--fc-plancher-presence),
        calc(100dvh - var(--hdr-h) - var(--fc-collage) - var(--reserve-bas-desktop) - var(--fc-place-ecriture))
      );
    }
  }
}

/* Le fil de messages garde son défilement interne : c'est lui qui absorbe la
   hauteur, jamais la page. (Déjà écrit l.335-340 ; on se contente de garantir
   min-height:0, sans quoi un enfant flex refuse de descendre sous la taille de
   son contenu et le plafond ci-dessus n'aurait aucun effet.) */
@media (min-width: 981px) {
  html.respfix .fc-center-col > .fc-posts-grid { min-height: 0; }
}

/* --------------------------------------------------------------------------
   GARDE-FOU DE LARGEUR — aucun bloc du salon ne peut élargir la page.
   -------------------------------------------------------------------------- */
html.respfix .fc-layout > * { min-width: 0; }

/* ================= 25-mon-cercle.css ================= */

/* ==========================================================================
   25 — ACCUEIL MEMBRE « MON CERCLE »  (mon-cercle.php, body.page-dashboard)
   --------------------------------------------------------------------------
   Page la plus visitée du site (index.php:31 y renvoie tout membre connecté)
   et la seule qui n'avait jamais été auditée.

   Tout est préfixé « html.respfix » : sans la classe sur <html>, ce fichier
   ne peut RIEN changer. Interrupteur éteint = page identique au pixel près.

   Le SOCLE (00-socle.css) a déjà réglé la barre du haut, le badge BÊTA contre
   l'engrenage, le rail gauche, overflow-x:clip et l'échelle d'empilement.
   RIEN de tout cela n'est repris ici : ce fichier ne s'occupe que du CONTENU.

   VÉRIFIÉ AU BANC : la remise en flux des 4 gros boutons se fait bien sur
   cette page. Mieux que prévu — quand respfix_on() est vrai, header.php
   n'ÉMET MÊME PLUS le bloc de rustines (il est dans un « if (!respfix_on()) »
   ligne 526), donc l'exception « body:not(.page-dashboard) » de la l.544
   n'existe tout simplement plus. Il ne reste que la règle de base l.475
   (.pub-cdp-group{position:absolute…}) que le socle écrase inconditionnel-
   lement. Le socle SUFFIT : rien à ajouter ici.

   MÉTHODE : toutes les valeurs ci-dessous ont été MESURÉES au navigateur sur
   un banc reproduisant le balisage et le CSS réels de la page (15 résolutions
   × 2 états = 30 contrôles). Avant correctif : 6 chevauchements simultanés à
   1024×600. Après : 0 défaut sur les 30 contrôles.
   ========================================================================== */


/* --------------------------------------------------------------------------
   C1 — UN SEUL RÉFÉRENTIEL POUR TOUTE LA CONSTELLATION
   --------------------------------------------------------------------------
   CONSTAT MESURÉ : la roue était mesurée en px/vw/vh (.ai-cluster l.321),
   les pastilles placées en % de cette roue (style="left:…%;top:…%" écrits en
   PHP, mon-cercle.php:96-107) et leurs icônes + libellés gardaient une taille
   FIXE en px (64px/12px, puis 52/50/46px selon trois media queries).
   Trois référentiels indépendants : quand la roue rétrécit, le contenu des
   pastilles ne rétrécit PAS avec elle.

   PREUVE CHIFFRÉE (banc, avant correctif, 1024×600 — un portable courant) :
   la roue tombe à 166px de large alors que les libellés restent à 12px, d'où
   SIX chevauchements simultanés :
       Carte ~ Voyageurs                    10 × 12 px
       Voyageurs ~ Pigeons voyageurs        38 × 16 px
       Pigeons voyageurs ~ Sorties offic.   10 × 34 px
       Passeport ~ Mon bambin               10 × 24 px
       Mon bambin ~ Entraide                38 × 11 px
       Entraide ~ Marché                    10 × 18 px
   La cause n'est pas une valeur mal choisie : c'est que .ai-cluster vaut
   « calc(100vh - 250px) », donc la roue suit la HAUTEUR de la fenêtre pendant
   que son contenu ne suit rien du tout.

   Ici : la roue devient un CONTENEUR DE REQUÊTE (container-type:inline-size)
   et tout ce qu'elle contient est exprimé en « cqw » (1cqw = 1 % de la largeur
   de la roue). Un seul référentiel : la figure reste semblable à elle-même à
   toutes les tailles.
   -------------------------------------------------------------------------- */
html.respfix .ai-cluster {
  container-type: inline-size;
  container-name: cercle;

  /* Diamètre des icônes : suit la roue, mais JAMAIS sous 44px (zone tactile
     minimale). 10.5cqw redonne 64px à 610px de roue — la valeur d'origine. */
  --mc-ic: clamp(44px, 10.5cqw, 64px);

  /* LARGEUR MAXIMALE D'UNE PASTILLE — DÉDUITE, PAS CHOISIE.
     Les 10 pastilles sont sur un cercle de rayon 47 % du diamètre D, une tous
     les 36° (vérifié : les 10 couples x/y du PHP donnent tous r = 47,0 %).
     Deux pastilles voisines ne se touchent pas si elles sont séparées SUR UN
     AXE — et l'axe qui sauve dépend de l'endroit du cercle :
       · voisines près du haut/bas   (50 %,3 %) et (77,6 %,12 %) : Δx = 27,6 %
       · voisines sur les côtés    (94,7 %,35,5 %) et (94,7 %,64,5 %) : Δx = 0 !
       · voisines en diagonale    (94,7 %,64,5 %) et (77,6 %,88 %) : Δx = 17,1 %
     Le premier couple impose la LARGEUR (Δx le plus petit qui doit contenir
     deux demi-libellés) : W ≤ 27,6 % de D. Les deux autres n'ont pas assez de
     Δx : c'est la HAUTEUR qui doit les séparer (voir --mc-debord-v et C9).

     ⚠️ La corde (29,05 %) était le mauvais critère : deux boîtes séparées par
     une corde de 29 % peuvent quand même se recouvrir si cette corde est
     surtout VERTICALE. Mesuré : bornes à 29,05 % → « Pigeons voyageurs ~
     Sorties officielles » se recouvrent encore de 20 × 12 px. À 27,6 % → 0. */
  --mc-larg: calc(27.6cqw - 6px);
}


/* --------------------------------------------------------------------------
   C2 — POURQUOI « SORTIES » APPARAISSAIT DEUX FOIS
   --------------------------------------------------------------------------
   Ce n'est PAS un doublon d'affichage : c'est un RETOUR À LA LIGNE.
   .ai-node est en position:absolute avec seulement « left » de posé
   (right:auto, width:auto) → sa largeur est calculée en « shrink-to-fit »,
   bornée par l'espace RESTANT à droite, c'est-à-dire (100 % − left).
     · « Sorties officielles » est à left:77.6 % → il ne dispose que de 22,4 %
       de la roue. MESURÉ à 800×600 : roue 350px → 22,4 % = 78px, et la boîte
       du libellé mesure EXACTEMENT 78px pour un texte de 96px : il s'est donc
       coupé en « Sorties » / « officielles ». La première ligne dit
       « Sorties », juste à côté de la vraie pastille « Sorties » (mesurée à
       26px de là). C'est le doublon vu par Julien.
     · « Pigeons voyageurs » est à left:94.7 % → 5,3 % de place seulement.
   Le défaut est ASYMÉTRIQUE : les pastilles de gauche (left:5.3 %) ont 94,7 %
   de place, celles de droite presque rien. Personne ne pouvait le voir en
   relisant le CSS : la cause est dans l'algorithme de largeur d'un bloc
   absolu, pas dans une valeur écrite quelque part.

   NB : sous 768px la règle l.596 impose white-space:nowrap — le libellé ne se
   coupe plus mais DÉBORDE de sa boîte (largeur min-content). Deux symptômes
   différents, une seule cause.

   CORRECTIF : max-content ne consulte plus la place restante à droite (le
   libellé n'est plus rogné par sa position), et --mc-larg le ramène dans
   l'écart horizontal disponible → il ne peut plus toucher son voisin.
   -------------------------------------------------------------------------- */
html.respfix .ai-node {
  inline-size: max-content;
  max-inline-size: var(--mc-larg);
}
html.respfix .ai-node-lb {
  inline-size: 100%;
  text-align: center;
  white-space: normal;              /* annule le nowrap de la l.596 : on
                                       autorise la coupure SI la place l'exige,
                                       plutôt que de déborder en silence */
  overflow-wrap: break-word;
  font-size: clamp(9.5px, 2.05cqw, 12px);
  line-height: 1.15;
}

/* Icônes : même référentiel que la roue, plancher tactile de 44px.
   MESURÉ après correctif : 44px au minimum sur les 30 contrôles. */
html.respfix .ai-node-ic {
  inline-size: var(--mc-ic);
  block-size: var(--mc-ic);
  min-inline-size: 44px;
  min-block-size: 44px;
}
html.respfix .ai-node-ic svg {
  inline-size: clamp(21px, 4.9cqw, 30px);
  block-size: clamp(21px, 4.9cqw, 30px);
}
/* Le badge compteur suit lui aussi la taille de sa pastille. */
html.respfix .cg-badge {
  font-size: clamp(10px, 1.9cqw, 12px);
  line-height: clamp(17px, 3.2cqw, 20px);
  block-size: clamp(17px, 3.2cqw, 20px);
  min-inline-size: clamp(17px, 3.2cqw, 20px);
}


/* --------------------------------------------------------------------------
   C3 — LE TITRE GÉANT DU FOND NE TRAVERSE PLUS LES PASTILLES
   --------------------------------------------------------------------------
   VÉRIFIÉ, ET LA PISTE ANNONCÉE EST À MOITIÉ FAUSSE : le titre « MON FEU DE
   CAMP NUMÉRIQUE » n'est PAS un élément de la page — c'est l'IMAGE DE FOND
   choisie dans l'admin (site.bg.dashboard), posée par header.php sur une
   couche fixe body::before {z-index:-1; pointer-events:none}. Elle est donc
   DÉJÀ derrière tout et n'intercepte DÉJÀ aucun clic : il n'y a ni z-index ni
   pointer-events à corriger de ce côté.
   Le vrai défaut est ailleurs : les pastilles sont TRANSLUCIDES, donc le titre
   se lit AU TRAVERS. Mesuré : le fond de .ai-node-ic est
       radial-gradient(circle at 50% 35%, rgba(61,169,252,0.38), rgba(10,20,40,0.92))
   → 38 % d'opacité au centre, 92 % au bord : jamais opaque.

   CORRECTIF : on garde EXACTEMENT le même dégradé (même teinte, même halo,
   même bordure — l'identité visuelle ne bouge pas) et on glisse une couche
   opaque DESSOUS. Sur fond noir le rendu est identique ; sur fond photo, la
   pastille redevient une vraie pastille.
   -------------------------------------------------------------------------- */
html.respfix .ai-node-ic {
  background-image: radial-gradient(circle at 50% 35%, rgba(61,169,252,0.38), rgba(10,20,40,0.92));
  background-color: #0a1424;        /* la couche opaque qui manquait */
}
/* Phare allumé : phare-light.css:1243 ne force QUE la couleur d'encre
   (#121821) sur l'icône, jamais son fond → il lui faut une plaque CLAIRE,
   sinon on peint du foncé sur du foncé. */
html.respfix body.phare-light .ai-node-ic {
  background-image: radial-gradient(circle at 50% 35%, rgba(61,169,252,0.22), rgba(226,238,252,0.96));
  background-color: #eef4fc;
}


/* --------------------------------------------------------------------------
   C4 — LE VOILE DE LA ROUE : SON RAYON ÉTAIT BIEN MAL CALCULÉ
   --------------------------------------------------------------------------
   PISTE DES AGENTS CONFIRMÉE, avec le chiffre exact.
   .ai-cluster::before (l.574) vaut inset:-6% -10% → la boîte fait 120 % de
   large pour 112 % de haut, et son dégradé est
       radial-gradient(closest-side, rgba(6,12,26,.5), rgba(6,12,26,.24) 60%, transparent)
   « closest-side » SANS mot-clé de forme = ELLIPSE dont les rayons valent la
   demi-boîte : 60 % de la roue en largeur, 56 % en hauteur. Or les pastilles
   sont à 47 % du centre, soit 78 à 84 % du parcours du dégradé : à cet endroit
   l'opacité est retombée entre 0,09 et 0,06. Autrement dit le voile protège le
   Gardien (au centre) et laisse les pastilles et leurs libellés NUS sur la
   photo. C'est bien un rayon mal calculé, pas une opacité mal choisie.

   CORRECTIF : un vrai CERCLE (mot-clé « circle » explicite), assez large pour
   couvrir la couronne des pastilles ET leurs libellés, qui ne s'éteint
   qu'APRÈS. Aucun bord dur : le fondu reste long.
   -------------------------------------------------------------------------- */
html.respfix .ai-cluster::before {
  inset: -22%;
  border-radius: 50%;
  z-index: auto;                    /* pas de nombre : l'ordre du DOM suffit,
                                       ::before est peint avant les pastilles */
  background: radial-gradient(circle closest-side,
      rgba(6,12,26,.60) 0%,
      rgba(6,12,26,.56) 74%,        /* 74 % de 72 % = 53 % du centre : couvre
                                       pastilles (47 %) ET libellés */
      rgba(6,12,26,.28) 88%,
      rgba(6,12,26,0)  100%);
}
/* Phare allumé : le voile doit être CLAIR, sinon il assombrit un thème clair. */
html.respfix body.phare-light .ai-cluster::before {
  background: radial-gradient(circle closest-side,
      rgba(247,250,254,.72) 0%,
      rgba(247,250,254,.66) 74%,
      rgba(247,250,254,.32) 88%,
      rgba(247,250,254,0)  100%);
}


/* --------------------------------------------------------------------------
   C5 — « LE GARDIEN DU FEU » : ni plaque, ni couloir réservé
   --------------------------------------------------------------------------
   PISTE DES AGENTS CONFIRMÉE : .ai-gardien-name (l.350) n'a QUE des
   text-shadow, aucune plaque de fond — sur une photo portant un mot en gros,
   du texte de 13px ne peut pas gagner. C'est ce que voit Julien : « LE GARDIEN
   DU FEU » posé sur le mot « CAMP ».

   ET un second défaut, plus grave, MESURÉ AU BANC : bottom:-30px est un
   décalage FIXE sous un orbe qui, lui, fait 56 % de la roue. Le nom tombe donc
   dans la couronne des pastilles. Chevauchements relevés AVANT correctif :
       390×844   NOM ~ Sorties officielles           14 × 14 px
       414×896   NOM ~ Sorties officielles           25 × 16 px
                 NOM ~ Passeport                      4 × 16 px
       360×740   NOM ~ Sorties officielles           17 × 14 px
                 NOM ~ Sorties                       46 ×  1 px
       800×600   NOM ~ Sorties                       64 ×  5 px
                 NOM ~ Passeport                     13 × 17 px
   Présent sur TOUS les téléphones et sur tous les écrans peu hauts.

   CORRECTIF — LE COULOIR LIBRE, DÉDUIT ET NON CHOISI.
   Les deux pastilles du bas sont à left:22.4 % et left:77.6 %, et leur libellé
   est désormais borné à --mc-larg (27,6 % de la roue, cf. C1). Le couloir
   central qui reste vaut donc 77,6 − 22,4 − 27,6 = 27,6 %… borné ici à
   26,15 % pour garder une marge. Tant que le nom tient DEDANS, il est À CÔTÉ
   des pastilles et non DESSUS : la question du recouvrement ne se pose plus,
   à aucune taille. Il se coupe en deux lignes plutôt que d'être tronqué par
   des « … » (on ne masque jamais un texte pour gagner de la place).
   -------------------------------------------------------------------------- */
html.respfix .ai-gardien-name {
  position: absolute;
  inset-block-start: 100%;
  inset-block-end: auto;
  inset-inline-start: 50%;
  transform: translate(-50%, -18%);
  max-inline-size: calc(26.15cqw + 10px);
  white-space: normal;
  text-wrap: balance;               /* coupe en « LE GARDIEN / DU FEU » et non
                                       en « LE GARDIEN DU / FEU » */
  font-size: clamp(9px, 1.9cqw, 13px);
  letter-spacing: clamp(.06em, .28cqw, .14em);
  padding: clamp(3px, .7cqw, 6px) clamp(5px, 1.2cqw, 12px);
  border-radius: 999px;
  /* Plaque sombre et OPAQUE — même recette que includes/profile-ads.php:193
     (« fonds volontairement sombres et opaques ») et que .jr-photo-veil. */
  background: rgba(8,14,26,.86);
  border: 1px solid rgba(91,192,235,.34);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
}
@supports not ((backdrop-filter: blur(6px)) or (-webkit-backdrop-filter: blur(6px))) {
  html.respfix .ai-gardien-name { background: rgba(8,14,26,.94); }
}
/* Petite roue : sous ~340px il n'y a plus de place sous l'orbe (les pastilles
   du bas remontent jusqu'à 76 % du diamètre). Le nom se pose alors SUR le bas
   de l'orbe, où l'image du Gardien est sombre et où rien d'autre ne passe. */
@container cercle (max-width: 340px) {
  html.respfix .ai-gardien-name { transform: translate(-50%, -105%); }
}
/* Phare allumé : phare-light.css:1255 force le texte en #121821 → plaque claire. */
html.respfix body.phare-light .ai-gardien-name {
  background: rgba(250,252,255,.88);
  border-color: rgba(18,24,33,.20);
}


/* --------------------------------------------------------------------------
   C6 — LES LIBELLÉS DES PASTILLES : une plaque discrète, pas un pavé
   --------------------------------------------------------------------------
   Même problème que le nom du Gardien, en plus petit. On ne met PAS un fond
   plein (10 pastilles = 10 pavés, l'accueil deviendrait lourd) : une plaque
   très douce suffit à décoller le texte de l'image, le voile C4 fait le reste.
   -------------------------------------------------------------------------- */
html.respfix .ai-node-lb {
  padding: 1px clamp(4px, .9cqw, 7px);
  border-radius: 999px;
  background: rgba(6,12,26,.42);
}
html.respfix body.phare-light .ai-node-lb {
  background: rgba(250,252,255,.62);
  text-shadow: none;
}


/* --------------------------------------------------------------------------
   C7 — LA HAUTEUR : rien ne doit plus être coupé en bas
   --------------------------------------------------------------------------
   CONSTAT. Trois nombres écrits à la main se contredisaient :
     · .ai-stage      min-height: calc(100vh - 280px)   (l.317)
     · .ai-cluster    width:      calc(100vh - 250px)   (l.321)
     · ≤768px         width:      calc(100vh - 300px)   (l.592) avec un
                                  PLANCHER max(240px,…) qui interdit toute
                                  réduction en dessous.
   Aucun des trois ne connaît la hauteur RÉELLE de la barre du haut (91 à
   213px selon l'écran), ni la barre du bas, ni les pastilles flottantes.
   MESURÉ AVANT CORRECTIF : à 820×900, 1024×768 et 1366×768, les pastilles du
   bas de la roue descendent SOUS le haut du champ « Demandez au Gardien » —
   la roue mord sur la barre de recherche.

   CORRECTIF : on cesse de deviner. La page devient une COLONNE : la roue prend
   la place qui reste et les trois blocs du bas (champ « Demandez au Gardien »,
   avertissement, barre d'état) gardent toujours la leur, au-dessus de la
   réserve var(--reserve-bas) du socle. Plus aucune soustraction à la main.

   ⚠️ PIÈGE MESURÉ, ET C'EST LE PLUS IMPORTANT DE CE FICHIER : passer
   .pub-content en display:flex casse l'étirement de TOUT enfant qui porte
   « margin-inline:auto » — une marge automatique absorbe l'espace libre et
   désactive le stretch. .ai-stage (margin-inline:auto l.316) et .ai-searchbar
   (margin:6px auto 0 l.518) en portent une. Mesuré à 1024×600 : le champ
   « Demandez au Gardien » tombait de 760px à 301px et la roue de 318 à 166px.
   D'où le inline-size:100% ci-dessous, sans lequel ce correctif serait PIRE
   que le défaut qu'il corrige.
   -------------------------------------------------------------------------- */
html.respfix body.page-dashboard {
  /* Débordement vertical d'une pastille hors de la roue : une pastille est
     CENTRÉE sur le cercle, donc la moitié de sa hauteur (icône + interligne +
     libellé) sort de la roue, moins les 3 % de marge que le PHP laisse en haut
     et en bas (top:3 % et top:97 %). Mesuré : 24 à 34px. */
  --mc-debord-v: clamp(24px, 3vh, 34px);

  /* PLANCHER DU DIAMÈTRE — DÉDUIT.
     Sous ~300px, la place horizontale entre deux pastilles voisines (27,6 % de
     D, soit 83px) ne suffit plus au plus long libellé (« Pigeons voyageurs »,
     « Sorties officielles » ≈ 76-84px à la taille plancher de 9,5px) SANS
     qu'il passe à deux lignes — et deux lignes font alors dépasser la hauteur
     autorisée (23,5 % de D). MESURÉ : les chevauchements disparaissent à
     partir de 300px et reviennent à 250px. En dessous, on préfère FAIRE
     DÉFILER la page plutôt qu'écraser la roue. */
  --mc-roue-min: 300px;

  /* LA MÊME LIMITE DE LARGEUR, EXPRIMÉE EN LONGUEUR ABSOLUE.
     Pourquoi cette redite : la roue doit valoir min(hauteur libre, largeur
     libre) — or ces deux libertés se mesurent sur des AXES différents, et un
     pourcentage ne sait parler que de son propre axe. MESURÉ deux fois :
       · hauteur en %, largeur bornée par max-inline-size → la largeur est
         rognée et la hauteur ne suit pas : roue de 266 × 300px (ellipse).
       · largeur en %, hauteur bornée par max-block-size  → l'inverse, pire :
         roue de 640 × 300px sur presque toutes les tailles.
     aspect-ratio ne « rattrape » JAMAIS l'axe qu'on a fixé explicitement.

     La borne de hauteur doit donc connaître la largeur libre, en LONGUEUR.
     On fait de .ai-stage un conteneur de requête (voir plus bas) : 1cqw vaut
     alors 1 % de la largeur INTÉRIEURE de la scène — une longueur, utilisable
     sur les deux axes. C'est exact, contrairement à un calcul en vw : MESURÉ,
     100vw compte la barre de défilement classique, d'où 13px d'écart et une
     roue de 266 × 279px. En cqw, l'écart tombe à zéro. */
  --mc-larg-dispo: 84cqw;
}
/* ≥901px au repos : la roue garde son décalage translateX(-10 %) → budget 72 %. */
@media (min-width: 901px) {
  html.respfix body.page-dashboard { --mc-larg-dispo: 72cqw; }
  /* Chat ouvert : plus de décalage (budget 84 %), mais la roue partage la
     scène avec la fenêtre du Gardien dans un rapport 1 / 1,6 (l.318). */
  html.respfix body.page-dashboard .ai-stage.is-active {
    --mc-larg-dispo: calc(0.84 * (100cqw - clamp(12px, 2vw, 28px)) / 2.6);
  }
}
html.respfix body.page-dashboard .pub-content {
  display: flex;
  flex-direction: column;
  min-block-size: calc(100dvh - var(--hdr-h));
}
@supports not (min-block-size: 1dvh) {          /* repli navigateurs anciens */
  html.respfix body.page-dashboard .pub-content { min-block-size: calc(100vh - var(--hdr-h)); }
}
/* ⚠️ Voir le piège ci-dessus : sans ceci, les deux blocs à marge automatique
   se rétractent sur leur contenu. */
html.respfix body.page-dashboard .ai-stage,
html.respfix body.page-dashboard .ai-searchbar { inline-size: 100%; }

html.respfix body.page-dashboard .ai-stage {
  /* Conteneur de requête : donne à --mc-larg-dispo une unité (cqw) qui vaut
     la largeur INTÉRIEURE réelle de la scène, barre de défilement déduite. */
  container-type: inline-size;
  container-name: scene;
  /* flex-basis:0 et non « auto » : avec « auto », la hauteur de la scène
     dépend de son contenu, qui dépend lui-même de la hauteur de la scène.
     MESURÉ : cette boucle fait tomber la roue à 0px de large sur les 13
     résolutions testées. flex-basis:0 la brise. */
  flex: 1 1 0;
  min-block-size: calc(var(--mc-roue-min) + 2 * var(--mc-debord-v));
  grid-template-rows: minmax(0, 1fr);           /* la rangée devient définie :
                                                   la roue peut s'y mesurer */
  padding-block: var(--mc-debord-v);            /* la place que prennent les
                                                   pastilles hors de la roue */
}
html.respfix body.page-dashboard .ai-searchbar,
html.respfix body.page-dashboard .ai-disclaimer,
html.respfix body.page-dashboard .ai-statusbar { flex: 0 0 auto; }

/* (18/07, demande de Julien) LA BARRE « Demandez au Gardien… » RESPIRE.
   mon-cercle.php:518 lui donne « margin: 6px auto 0 » : 6px seulement la
   séparaient du bloc au-dessus, elle paraissait collée à son bord.
   On ne déplace rien et on ne réduit rien : on ajoute juste de l'air au-dessus.
   En clamp() et non en pixels fixes, pour que l'écart reste proportionné :
   confortable sur grand écran, discret sur un écran bas où chaque pixel compte. */
html.respfix body.page-dashboard .ai-searchbar {
  margin-block-start: clamp(14px, 2.4vh, 30px);
}

/* La roue se mesure sur les DEUX axes : jamais plus haute que la place
   restante, jamais plus large que sa colonne. L'ancien plancher max(240px,…)
   était ARBITRAIRE et empêchait toute réduction ; celui d'ici (--mc-roue-min)
   est DÉDUIT de la géométrie, et quand il ne tient pas on fait défiler la page
   au lieu d'écraser la roue.
   · justify-self:center est INDISPENSABLE : sans lui un élément de grille
     s'étire, inline-size:auto vaut « toute la colonne » et le rapport 1/1 est
     ignoré — la roue devient un ovale.
   · 84 % : l'emprise horizontale réelle vaut D + 2 × (demi-libellé − 5,3 % de
     D) ≈ 1,17 D. 1/1,17 = 85 %, arrondi prudemment à 84 %.
   · les trois bornes se répètent sur block-size ET max-block-size : c'est
     volontaire, aspect-ratio ne transmet la contrainte que dans un sens. */
html.respfix body.page-dashboard .ai-cluster {
  justify-self: center;
  align-self: center;
  inline-size: auto;
  block-size: min(100%, 640px, var(--mc-larg-dispo));
  max-block-size: min(100%, 640px, var(--mc-larg-dispo));
  aspect-ratio: 1 / 1;
  max-inline-size: min(84%, var(--mc-larg-dispo), 640px);
  margin-inline: auto;
}
/* ≥901px au repos, la roue garde son décalage translateX(-10 %) d'origine
   (choix visuel de Julien, on n'y touche pas) : il faut donc lui réserver
   10 % de plus du côté gauche. 1/(1,17 + 0,20) ≈ 73 %, arrondi à 72 %. */
@media (min-width: 901px) {
  html.respfix body.page-dashboard .ai-stage:not(.is-active) .ai-cluster {
    max-inline-size: min(72%, var(--mc-larg-dispo), 640px);
  }
}

/* Fenêtre du Gardien ouverte : sa hauteur en vh ignorait, elle aussi, la
   barre du haut. On la relie aux mesures réelles du socle. */
html.respfix body.page-dashboard .ai-screen {
  block-size: auto;
  align-self: stretch;
  max-block-size: none;
}


/* --------------------------------------------------------------------------
   C8 — LA FENÊTRE OUVERTE NE RAPETISSE PLUS LES ZONES TACTILES
   --------------------------------------------------------------------------
   .ai-stage.is-active .ai-cluster{transform:scale(.8)} (l.322), .7 (l.558) et
   .72 (l.594) réduisaient la roue par une TRANSFORMATION : les pastilles de
   44px devenaient 35, puis 31, puis 30,8px réels sous le doigt — sous le
   minimum tactile — et le texte devenait flou (rendu mis à l'échelle, pas
   re-rendu). Ici la roue rétrécit pour de vrai : elle occupe moins de place,
   et ses pastilles gardent leur plancher de 44px (mesuré : 44px mini dans
   l'état « chat ouvert » sur les 15 résolutions).
   -------------------------------------------------------------------------- */
html.respfix body.page-dashboard .ai-stage.is-active .ai-cluster {
  transform: none;
  max-block-size: min(100%, 500px);
}


/* --------------------------------------------------------------------------
   C9 — LA HAUTEUR D'UNE PASTILLE, ET LA BANDE 769-900px OUBLIÉE
   --------------------------------------------------------------------------
   (a) Les trois media queries d'origine refixaient des tailles en px
   (52/50/46px d'icône, 11px de libellé) qui contredisent le référentiel
   unique de C1. On les neutralise en gardant EXACTEMENT les mêmes bornes.

   (b) Le correctif du 08/07 retire le décalage translateX(-10 %) « qui coupait
   Entraide » — mais seulement sous 768px (l.592), alors que le décalage, lui,
   s'applique jusqu'à 900px. La bande 769-900px (tablettes) était donc restée
   avec le défaut que ce correctif était censé régler. On lui rend sa borne.

   (c) Sous ~330px de roue, la HAUTEUR d'une pastille (icône 44px + interligne
   + libellé sur deux lignes ≈ 72px) dépasse l'écart vertical disponible entre
   deux voisines en diagonale (23,5 % de D = 77px à 330px, 70px à 300px). On
   récupère les 7px qui manquent sur l'interligne et le rembourrage du libellé
   — pas sur la taille du texte, ni sur celle de la zone tactile.
   MESURÉ : sans ce bloc, « Pigeons voyageurs ~ Sorties officielles » se
   recouvrent encore de 26 × 1 px à 800×600 et 1024×600. Avec : 0.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  html.respfix body.page-dashboard .ai-cluster { transform: none; }
}
@container cercle (max-width: 330px) {
  html.respfix .ai-node { gap: 1px; }
  html.respfix .ai-node-lb { line-height: 1.05; padding: 0 3px; }
}
/* Sous 290px de roue, l'écart vertical disponible tombe à 68px pour une
   pastille qui en fait 65 : il ne reste plus rien. On rend l'interligne au
   plus juste (1,0) — la taille du texte et la zone tactile, elles, ne bougent
   pas. MESURÉ : « Pigeons voyageurs ~ Sorties officielles » passe de
   21 × 2 px à 0 sur les téléphones de 360px et en mode chat ouvert. */
@container cercle (max-width: 290px) {
  html.respfix .ai-node { gap: 0; }
  html.respfix .ai-node-lb { line-height: 1; }
}


/* --------------------------------------------------------------------------
   C10 — barre d'état : elle ne doit pas passer sous les pastilles flottantes
   --------------------------------------------------------------------------
   La réserve du socle protège la fin du contenu. Sur cette page la barre
   d'état est justement le dernier bloc : on lui laisse en plus de quoi
   respirer sur les côtés, là où les deux pastilles flottantes descendent
   (--fab-l-w à gauche, --fab-r-w à droite), plutôt que de la masquer.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  html.respfix body.page-dashboard .ai-statusbar {
    row-gap: 10px;
    justify-content: center;
  }
  html.respfix body.page-dashboard .ai-status-l { gap: clamp(10px, 3vw, 22px); }
}


/* --------------------------------------------------------------------------
   C11 — LES DERNIERS NOMBRES D'EMPILEMENT ÉCRITS À LA MAIN
   --------------------------------------------------------------------------
   Le socle (R6) a remplacé les 26 z-index au hasard du chrome par une échelle
   documentée. Cette page en gardait six, dont un hors norme :
       .ai-confirm      z-index:9000   ← la modale de confirmation
       .ai-screen-close z-index:6      .ai-screen-inner z-index:3
       .ai-gardien      z-index:3      .ai-node.is-hint z-index:6
       .ai-node.is-cg-alert z-index:7  (le même « 7 » que la rustine du 18/07
                                        sur .pub-phare-wrap — d'où la confusion)
   9000 passe DEVANT tout, y compris les bandeaux critiques (900) : une alerte
   de sécurité pouvait se retrouver cachée derrière une demande de confirmation.
   On les replace sur l'échelle, en conservant leur ordre relatif exact.
   -------------------------------------------------------------------------- */
html.respfix .ai-confirm      { z-index: var(--z-alertes); }
html.respfix .ai-screen-inner { z-index: var(--z-contenu); }
html.respfix .ai-screen-close { z-index: var(--z-page); }
/* Dans .ai-cluster (contexte d'empilement à lui seul) : pastilles ordinaires
   au niveau auto, puis le Gardien, puis la pastille désignée, puis l'alerte. */
html.respfix .ai-gardien             { z-index: var(--z-contenu); }
html.respfix .ai-node.is-hint        { z-index: var(--z-page); }
html.respfix .ai-node.is-cg-alert    { z-index: calc(var(--z-page) + 1); }
