/* Anticipation incendie — démonstrateur SDIS.
   Thème sombre haute lisibilité (UX de crise) : contrastes élevés,
   cibles tactiles >= 44 px, focus visibles, responsive < 900 px. */

/* PEAU STITCH (21/08/2026) — la référence visuelle Google Stitch entre par
   les POLICES et les VARIABLES, jamais par son code (doctrine CH9 : les
   écrans Stitch sont une référence, pas une base). Fichiers dans
   frontend/fonts/ (licences SIL OFL — voir fonts/LICENCES.txt) :
   Inter variable = le texte, JetBrains Mono = identifiants et données
   (un 0 et un O qui ne se confondent pas, sur un run_id ça compte). */
@font-face {
  font-family: "Inter";
  src: url("fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/JetBrainsMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/JetBrainsMono-Bold.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

:root {
  /* Palette Stitch : OBSIDIENNE en fond (#101319) déclinée sur panneaux,
     blocs et bords ; le jaune #ffd60a était déjà l'accent de l'outil. Les
     couleurs d'ALERTE (rouge, ambre) ne bougent pas : la peau habille,
     elle ne rejuge pas la sémantique opérationnelle. */
  --fond: #101319;
  --panneau: rgba(13, 15, 20, 0.94);
  --bloc: #171b23;
  --bord: #2b3140;
  --texte: #eceef1;
  --texte-2: #a9b0bb;
  --ambre: #f5a524;
  --rouge: #e5484d;
  --jaune: #ffd60a;
  --accent: #ffd60a;
  --rayon: 10px;
  --mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
}

* { box-sizing: border-box; }

/* L'attribut hidden prime toujours sur les display définis par nos règles */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--fond);
  color: var(--texte);
  font-family: "Inter", system-ui, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.35;
}

/* Focus clavier bien visible partout */
:focus-visible {
  outline: 3px solid var(--jaune);
  outline-offset: 2px;
}

/* ---------- Carte ---------- */
#carte {
  position: fixed;
  inset: 0;
  background: var(--fond);
}
#carte.armee .maplibregl-canvas-container { cursor: crosshair !important; }

/* Indicateur de chargement de la carte (lot H constat 8) : centré sur la carte
   noire, sous les panneaux (z-index 5 < 30) pour ne rien recouvrir d'utile.
   Retiré par app.js au premier rendu complet (`idle`). */
#carte-chargement {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 18px;
  border-radius: 999px;
  background: rgba(20, 22, 27, 0.92);
  border: 1px solid #ff9f0a;
  color: #ffd9a8;
  font-size: 14px;
  font-weight: 700;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5);
  pointer-events: none;   /* n'intercepte aucun geste carte */
}

/* ---------- En-tête (zone + fraîcheur) ---------- */
#entete {
  position: fixed;
  /* ZONES SÛRES (voir --marge-* plus bas) : encoche en haut en portrait, bord
     DROIT en paysage. Sans cela le nom de zone et la fraîcheur des données
     passaient sous l'encoche dès la rotation — et la fraîcheur est justement ce
     qui dit si la prévision affichée est encore valable.
     ATTENTION : --y-bandeau (section « BANDEAU UNIQUE » de cette feuille) vaut
     `--marge-haute + 10px + --haut-entete + 8px` et sert d'ancrage à tout ce qui
     se pose sous l'en-tête ; elle intègre la MÊME --marge-haute, sans quoi tout
     l'étage suivant repasserait par-dessus l'en-tête. */
  top: calc(10px + var(--marge-haute, 0px));
  right: calc(10px + var(--marge-droite, 0px));
  z-index: 20;
  max-width: min(46vw, 520px);
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 8px 12px;
  text-align: right;
}
#entete-zone { font-weight: 700; font-size: 14px; }
/* B10 : état CALCULÉ de la zone sous le titre (remplace l'ancien suffixe
   « — secteur actif détecté » figé dans le nom). Mêmes couleurs de palier que
   la liste des foyers (.section-palier), même lecture d'un coin d'œil. */
#entete-etat { font-size: 12px; margin-top: 2px; color: var(--texte-2); }
#entete-etat.etat-actif { color: #ff8f92; font-weight: 700; }
#entete-etat.etat-surveille { color: #fbbf24; }
#entete-fraicheur { color: var(--texte-2); font-size: 12px; margin-top: 2px; }

/* DONNÉES PÉRIMÉES SUR LA ZONE AFFICHÉE (seuils servis par /api/config) :
   l'en-tête EXISTANT bascule en alerte — aucun élément flottant n'est ajouté,
   c'est le même bloc qui change de couleur. Un âge de 22 h ne doit pas s'écrire
   dans le même gris discret qu'un âge de 6 minutes. */
#entete.perime {
  border-color: var(--ambre);
  background: rgba(62, 42, 8, 0.94);
}
#entete.perime #entete-fraicheur { color: #ffe1a8; }
.fraicheur-alerte {
  margin-top: 3px;
  color: var(--ambre);
  font-weight: 700;
}

/* Âge des données d'une AUTRE zone (bloc « Autres zones ») : même code visuel
   que l'en-tête, pour qu'on repère la zone figée sans la sélectionner. */
.age-zone {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--texte-2);
}
.age-zone.perime { color: var(--ambre); }

/* ---------- Bandeau HORS DOMAINE ---------- */
#bandeau-hd {
  position: fixed;
  /* SOUS l'en-tête, dont la hauteur VARIE (obs météo, alerte de fraîcheur, nom
     de zone replié) : `--haut-entete` est publiée par calerBarreActions().
     L'ancien `top: 64px` figé faisait chevaucher les deux dès que l'en-tête
     gagnait une ligne — les deux textes devenaient illisibles ensemble. */
  top: calc(var(--marge-haute, 0px) + 10px + var(--haut-entete, 54px)
            + var(--haut-puce, 0px) + 10px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  /* `width: max-content` est INDISPENSABLE : avec `left: 50%` sans `right`, la
     largeur disponible pour un bloc fixe n'est que la MOITIÉ de l'écran, et le
     bandeau se repliait en colonne de 185 px sur mobile (8 lignes illisibles)
     alors que max-width en autorisait 345. */
  width: max-content;
  max-width: min(92vw, 760px);
  background: #55131a;
  border: 2px solid var(--rouge);
  color: #ffd9db;
  font-weight: 700;
  padding: 10px 16px;
  border-radius: var(--rayon);
  text-align: center;
}
/* Même bandeau, réserve MOINS grave : données périmées SANS hors-domaine. Le
   rouge reste réservé à « la prévision sort du domaine de validité du modèle » ;
   une donnée vieille rend la prévision décalée, pas fausse. */
#bandeau-hd.fraicheur {
  background: #4a3408;
  border-color: var(--ambre);
  color: #ffe1a8;
}

/* ---------- Bouton panneau ---------- */
#btn-panneau {
  position: fixed;
  /* ZONES SÛRES : en portrait l'encoche mange le haut, EN PAYSAGE elle mange le
     côté GAUCHE (44 px). Ce bouton, collé à 10 px du bord gauche, passait donc
     sous l'encoche dès que le téléphone était tourné — et c'est le SEUL moyen
     d'ouvrir le tiroir de commande. Voir --marge-* dans la section
     « Empilement du bas de carte ». */
  top: calc(10px + var(--marge-haute, 0px));
  left: calc(10px + var(--marge-gauche, 0px));
  z-index: 32;
  width: 46px;
  height: 46px;
  font-size: 20px;
  background: var(--bloc);
  color: var(--texte);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  cursor: pointer;
}
#btn-panneau:hover { background: #2a2f39; }

/* ---------- Panneau latéral ---------- */
#panneau {
  position: fixed;
  /* Les 64 px sont la place du bouton ☰ (10 + 46 + 8). Celui-ci descend
     maintenant de la zone sûre de l'encoche : sans reporter --marge-haute ici,
     le tiroir remontait SOUS le bouton et son en-tête (« PYRONOS », le nom
     d'utilisateur, la déconnexion) passait dessous. Les deux se suivent. */
  top: calc(64px + var(--marge-haute, 0px));
  left: calc(10px + var(--marge-gauche, 0px));
  bottom: calc(76px + var(--marge-basse, 0px));   /* zone sûre : cf. #info-bulle */
  z-index: 30;
  width: 348px;
  max-width: calc(100vw - 20px);
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 14px;
  overflow-y: auto;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
body.panneau-ferme #panneau {
  transform: translateX(calc(-100% - 20px));
  opacity: 0;
  pointer-events: none;
}

/* HUB CONDUITE (plan onglets étape 2) : sommaire vivant de l'écran conduite,
   posé sur la carte. Visible sur le seul écran conduite. */
#hub-conduite { display: none; }
body.ecran-conduite #hub-conduite {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  position: absolute;
  /* BAS-DROITE : la seule région libre de l'écran conduite (mesuré deux
     fois — en haut, l'entête console, le bandeau SOS et la barre d'actions
     recouvraient les tuiles). Au-dessus des boutons France/Légende. */
  top: auto;
  bottom: 100px;
  right: 14px;
  width: min(430px, calc(100vw - 28px));
  max-height: 48%;
  overflow-y: auto;
  z-index: 24;
  padding: 14px;
  border-radius: var(--rayon);
  border: 1px solid var(--bord);
  background: rgba(16, 19, 25, 0.94);
}
body.ecran-conduite #hub-conduite.hub-gele { display: none; }
#hub-conduite .hub-titre {
  grid-column: 1 / -1;
  margin: 0;
  font-size: 15px;
  letter-spacing: 0.04em;
}
.hub-tuile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  cursor: pointer;
  text-align: left;
}
.hub-tuile:hover { border-color: var(--texte-2); }
.hub-tuile.hub-alerte { border-color: #e5484d; box-shadow: 0 0 0 1px #e5484d; }
.hub-emoji { font-size: 17px; }
.hub-nombre { font-family: var(--mono); font-size: 26px; font-weight: 700; }
.hub-tuile.hub-alerte .hub-nombre { color: #ff8589; }
.hub-libelle { font-size: 12.5px; font-weight: 700; }
.hub-detail { font-size: 11px; color: var(--texte-2); }
#hub-conduite .hub-note {
  grid-column: 1 / -1;
  margin: 2px 0 0;
  font-size: 10.5px;
  color: var(--texte-2);
}

/* PAGES RÉELLEMENT SÉPARÉES (retour Jérôme 29/08) : Analyses et
   Administration n'affichent QUE leur métier — la carte et tout son
   attirail (entête console, barre terrain, timeline, fil d'Ariane)
   disparaîssent. MASQUAGE seulement : tout le code vit, zéro perte —
   les badges et bandeaux transverses (SOS, évacuation reçue) restent.
   Conduite GARDE la carte : une conduite sans situation serait aveugle. */
body.ecran-analyses #carte, body.ecran-admin #carte { visibility: hidden; }
/* Le REJEU et le BAC À SABLE se jouent SUR LA CARTE : quand l'un d'eux
   démarre depuis la page Analyses, la carte revient (avec son entête — la
   démo la détourne — et la timeline du rejeu). À la sortie, la page
   redevient pure. Sans ça, la démo jouait sur une carte invisible. */
body.ecran-analyses.demo-active #carte,
body.ecran-analyses.bac-actif #carte { visibility: visible; }
body.ecran-analyses.demo-active #entete,
body.ecran-analyses.bac-actif #entete,
body.ecran-analyses.demo-active #timeline-bar,
body.ecran-analyses.bac-actif #timeline-bar {
  display: block !important;
}
/* Le bac joue les GESTES RÉELS : sa barre d'actions revient aussi. */
body.ecran-analyses.bac-actif #barre-intervention { display: flex !important; }
/* Et le sommaire s'efface pendant l'exercice (la carte est la scène). */
body.ecran-analyses.demo-active #hub-analyses-ecran,
body.ecran-analyses.bac-actif #hub-analyses-ecran { display: none; }
body.ecran-analyses #entete, body.ecran-admin #entete,
body.ecran-analyses #barre-intervention, body.ecran-admin #barre-intervention,
body.ecran-analyses #timeline-bar, body.ecran-admin #timeline-bar,
body.ecran-analyses #fil-ariane, body.ecran-admin #fil-ariane {
  display: none !important;
}
/* Le panneau se réduit à l'identité + le métier de la page : toutes les
   sections carto tombent ; Analyses garde son bloc (démo/archives/bilans/
   bac), Admin n'a besoin que de la tête (bouton ⚙ + construction). */
body.ecran-analyses #panneau section.bloc { display: none; }
body.ecran-analyses #panneau #bloc-demo { display: block; }
body.ecran-admin #panneau section.bloc { display: none; }
/* Les lignes CARTO de la tête de panneau tombent aussi sur ces pages
   (badges FWI/convectif, lutte, recherche de lieu, multi-affichage) — la
   ZONE COUVERTE reste : archives et construction sont par zone. */
body.ecran-analyses #panneau .champ-recherche,
body.ecran-admin #panneau .champ-recherche,
body.ecran-analyses #panneau .lutte-rapide,
body.ecran-admin #panneau .lutte-rapide,
body.ecran-analyses #panneau #bloc-autres-zones,
body.ecran-analyses #badge-fwi, body.ecran-admin #badge-fwi,
body.ecran-analyses #badge-fwi-demain, body.ecran-admin #badge-fwi-demain,
body.ecran-analyses #badge-conv, body.ecran-admin #badge-conv,
body.ecran-analyses #btn-france, body.ecran-admin #btn-france,
body.ecran-analyses #btn-legende, body.ecran-admin #btn-legende,
body.ecran-analyses #btn-apropos, body.ecran-admin #btn-apropos {
  display: none !important;
}

/* Hubs des écrans Analyses/Admin : en PLEINE PAGE centrée (pas de carte
   à ménager), grandes tuiles. */
.hub-ecran { display: none; }
body.ecran-analyses #hub-analyses-ecran,
body.ecran-admin #hub-admin-ecran {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  position: fixed;
  top: 50%;
  left: max(50%, calc(340px + (100vw - 340px) / 2));
  transform: translate(-50%, -50%);
  width: min(620px, calc(100vw - 380px));
  min-width: 300px;
  max-height: 80vh;
  overflow-y: auto;
  z-index: 24;
  padding: 22px;
  border-radius: var(--rayon);
  border: 1px solid var(--bord);
  background: rgba(16, 19, 25, 0.94);
}
@media (max-width: 900px) {
  body.ecran-analyses #hub-analyses-ecran,
  body.ecran-admin #hub-admin-ecran {
    left: 50%;
    width: min(620px, calc(100vw - 24px));
  }
}
.hub-ecran .hub-tuile { padding: 18px 16px; }

/* Sous-navigation des pages de contenu (Analyses) : pilules en tête,
   collées au défilement. Visible sur la page séparée seulement. */
.sous-nav { display: none; }
body.ecran-analyses .sous-nav {
  display: flex;
  gap: 6px;
  position: sticky;
  top: 0;
  z-index: 5;
  padding: 10px 14px;
  background: var(--panneau);
  border-bottom: 1px solid var(--bord);
}
.sous-nav button {
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte-2);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.sous-nav button:hover { color: var(--texte); }
.sous-nav button.actif {
  background: var(--accent);
  border-color: var(--accent);
  color: #101319;
  cursor: default;
}

/* NAVIGATION DE PAGE (retour Jérôme 29/08 : « pas des écrans dans
   l'écran ») : sur les pages séparées, les panneaux du métier ne sont
   plus des modales centrées — ils REMPLISSENT la zone de contenu à droite
   du panneau latéral, comme une page d'application moderne. Le ✕ les
   ferme et ramène au sommaire (les tuiles) : hub = accueil, panneau =
   page. MÊmes éléments, mêmes gestes — seul l'habillage change. */
body.ecran-analyses #panneau-archives,
body.ecran-analyses #panneau-analyses,
body.ecran-admin #panneau-admin {
  left: 372px;   /* panneau 348px + left 10px + respiration */
  top: 0;
  right: 0;
  bottom: 0;
  transform: none;
  width: auto;
  max-width: none;
  max-height: none;
  height: 100%;
  border-radius: 0;
  border: 0;
  border-left: 1px solid var(--bord);
}
@media (max-width: 900px) {
  body.ecran-analyses #panneau-archives,
  body.ecran-analyses #panneau-analyses,
  body.ecran-admin #panneau-admin {
    left: 0;
    border-left: 0;
  }
}
.hub-ecran .hub-titre { grid-column: 1 / -1; margin: 0; font-size: 15px; }
.hub-ecran .hub-nombre:empty { display: none; }

/* Bandeau inter-onglets (étape 5) : exercice ailleurs, session fermée,
   zone suivie — une seule ligne, en haut au centre, au-dessus de tout. */
#bandeau-onglets {
  position: fixed;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 90;
  max-width: min(680px, calc(100vw - 20px));
  padding: 7px 16px;
  border-radius: 999px;
  border: 1px solid #f5a524;
  background: rgba(43, 32, 8, 0.97);
  color: #ffd60a;
  font-size: 12.5px;
  font-weight: 700;
  text-align: center;
}

/* Barre des écrans spécialisés (plan onglets, 28/08). Des pilules sobres en
   tête de panneau ; l'écran courant est marqué, les autres ouvrent leur
   onglet navigateur. */
#barre-ecrans {
  display: flex;
  gap: 6px;
  padding: 8px 14px 0;
  flex-wrap: wrap;
}
#barre-ecrans a {
  padding: 4px 11px;
  border-radius: 999px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte-2);
  font-size: 12px;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
#barre-ecrans a:hover { color: var(--texte); border-color: var(--texte-2); }
#barre-ecrans a.actif {
  background: var(--accent);
  border-color: var(--accent);
  color: #101319;
  cursor: default;
}
/* Carte allégée : les entrées déménagées vers leurs onglets (archives,
   analyses, démo, bac, admin) quittent le panneau de l'écran de garde —
   elles restent PLEINEMENT accessibles sur leur écran dédié. */
body.barre-ecrans-active .demenage-onglet { display: none; }

.pan-tete h1 { margin: 0; font-size: 19px; }
.sous-titre { margin: 2px 0 10px; color: var(--texte-2); font-size: 12.5px; }

/* Sélecteur de zone (multi-zones national) */
.champ-zone { margin: 0 0 10px; }
.champ-zone label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--texte-2);
  margin-bottom: 3px;
}
.champ-zone select {
  width: 100%;
  padding: 6px 8px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  font: inherit;
  font-size: 13px;
}

/* Autres zones : multi-affichage + secteurs actifs France (bloc repliable) */
.autres-zones {
  margin: 0 0 10px;
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 6px 10px;
}
.autres-zones summary {
  cursor: pointer;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--texte-2);
  font-weight: 700;
  min-height: 30px;
  display: flex;
  align-items: center;
}
.autres-zones[open] summary { margin-bottom: 4px; }
.autres-zones .ligne-couche { min-height: 36px; font-size: 12.5px; }
.autres-zones code { font-size: 11px; color: var(--texte); }
.autres-zones-titre {
  margin: 10px 0 4px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--texte-2);
}
/* LIEN PRESENTE EN BOUTON — introduit pour « 🇫🇷 Vue France » (index.html,
   bloc « Autres zones »). REGLE NOUVELLE, aucune regle existante modifiee.
   Cible tactile >= 44 px comme le reste de la feuille : ce lien est pressé au
   doigt, sur le terrain. La Vue France (france.css) reprend cette classe. */
.bouton-lien {
  display: block;
  margin: 8px 0 2px;
  padding: 10px 12px;
  background: var(--bloc);
  color: var(--texte);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 700;
  min-height: 44px;
  line-height: 24px;
  text-align: center;
}
.bouton-lien:hover { border-color: var(--accent); }
.secteur-fr {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 4px 0;
  border-top: 1px solid var(--bord);
  font-size: 12.5px;
}
.secteur-fr:first-child { border-top: none; }
.secteur-fr-txt { font-variant-numeric: tabular-nums; }
.secteur-fr button {
  flex: none;
  padding: 4px 10px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #262a33;
  color: var(--texte);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.secteur-fr button:hover { filter: brightness(1.12); }

/* Construction de zone depuis l'interface : bouton 🔨 puis barre d'état */
.autres-zones .btn-construire { white-space: nowrap; }
.autres-zones .btn-construire:disabled { opacity: 0.55; cursor: not-allowed; }
.autres-zones .btn-construire.construction-suivi {
  flex: 1 1 100%;
  white-space: normal;
  text-align: left;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--texte-2);
  cursor: progress;
}

/* Sections des foyers d'autres zones dans la liste « Feux en cours » */
#liste-foyers .section-zone {
  margin: 10px 0 4px;
  text-align: center;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--texte-2);
  font-weight: 700;
  list-style: none;
}
#liste-foyers .foyer-check:disabled { opacity: 0.35; cursor: not-allowed; }

/* AUDIT DES RAYONS (29/07) — RÈGLE GÉNÉRALE DE LA FEUILLE.
   `border-radius: 999px` ne donne une PILULE que si la boîte reste PLUS LARGE
   QUE HAUTE. Dès qu'elle se replie sur deux lignes ou davantage, le rayon est
   ramené par le moteur à la moitié de la plus petite dimension et la boîte
   devient une ELLIPSE, puis un DISQUE — c'est le « énorme disque orange » de la
   capture Android du 29/07, dont le texte débordait sur le côté.
   Un 999px n'est donc légitime que dans DEUX cas, et il faut pouvoir le
   démontrer sur place :
     a) la hauteur est BORNÉE en dur (`height` fixe, ou `line-height` sur une
        pastille de taille fixe) ;
     b) le contenu ne peut pas se replier (`white-space: nowrap`), ce qui borne
        la hauteur à une ligne.
   Partout ailleurs — libellé libre, texte traduit, contenu venu du serveur — on
   met un RAYON FIXE MODESTE, qui reste une pilule sur une ligne et devient un
   cadre arrondi correct sur plusieurs. Chaque 999px restant dans cette feuille
   satisfait (a) ou (b) ; si vous en ajoutez un, vérifiez-le.

   `.badge-fwi` : « Indice FWI » + valeur + classe de danger, contenu SERVEUR de
   longueur inconnue, dans un panneau de 348 px qui tombe à 100vw−20 sur
   téléphone. Il se replie → rayon fixe. */
.badge-fwi {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  border-radius: 14px;
  font-weight: 800;
  margin-bottom: 6px;
}
.badge-fwi-titre { font-weight: 600; font-size: 12px; opacity: 0.85; }

.bloc {
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 12px;
  margin-top: 12px;
}
.bloc h2 {
  margin: 0 0 10px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--texte-2);
}

/* Champs du scénario */
.champ { margin-bottom: 14px; }
.champ:last-child { margin-bottom: 0; }
.champ-ligne {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.champ-ligne label, .etiquette { font-weight: 600; }
.manuel {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--texte-2);
  font-size: 12.5px;
  font-weight: 400 !important;
  cursor: pointer;
  min-height: 28px;
}
.champ-ctrl { display: flex; align-items: center; gap: 10px; }
.champ-ctrl output { min-width: 64px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }
.champ-rose { align-items: center; }
.ctrl-col { flex: 1; display: flex; align-items: center; gap: 10px; }
.indice { color: var(--texte-2); font-size: 11.5px; margin-top: 3px; }
.meteo-heure {
  margin-top: 4px;
  font-size: 13px;
  color: var(--texte-2);
  font-variant-numeric: tabular-nums;
}
.hors-domaine-val { color: var(--rouge); }

/* Curseurs : piste et poignée larges (usage ganté / stress) */
input[type="range"] {
  flex: 1;
  appearance: none;
  -webkit-appearance: none;
  height: 30px;
  background: transparent;
  cursor: pointer;
  min-width: 0;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 8px; border-radius: 4px; background: #3a404c;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 24px; height: 24px; margin-top: -8px;
  border-radius: 50%;
  background: var(--jaune);
  border: 2px solid #14161b;
}
input[type="range"]:disabled::-webkit-slider-thumb { background: #6a7180; }
input[type="range"]::-moz-range-track { height: 8px; border-radius: 4px; background: #3a404c; }
input[type="range"]::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--jaune); border: 2px solid #14161b;
}
input[type="range"]:disabled::-moz-range-thumb { background: #6a7180; }
input[type="range"]:disabled { cursor: not-allowed; }

input[type="checkbox"] {
  width: 20px; height: 20px;
  accent-color: var(--jaune);
  cursor: pointer;
}

/* Rose des vents */
#rose { width: 84px; height: 84px; flex: none; }
.rose-cercle { fill: #14161b; stroke: var(--bord); stroke-width: 2; }
.rose-txt { fill: var(--texte-2); font-size: 11px; text-anchor: middle; }
.rose-pointe { fill: var(--jaune); }
.rose-axe { fill: var(--texte); }
#rose-aiguille { transform-origin: 40px 40px; transition: transform 0.15s ease; }

/* Boutons durée */
#durees { display: flex; gap: 6px; flex-wrap: wrap; }
#durees button {
  flex: 1;
  min-height: 44px;
  min-width: 52px;
  font-size: 15px;
  font-weight: 700;
  color: var(--texte);
  background: #2a2f39;
  border: 1px solid var(--bord);
  border-radius: 8px;
  cursor: pointer;
}
#durees button.actif {
  background: var(--jaune);
  color: #14161b;
  border-color: var(--jaune);
}

/* Bouton principal de simulation */
#btn-sim {
  width: 100%;
  min-height: 54px;
  margin-top: 14px;
  font-size: 16px;
  font-weight: 800;
  color: #14161b;
  background: var(--ambre);
  border: 2px solid var(--ambre);
  border-radius: var(--rayon);
  cursor: pointer;
}
#btn-sim:hover { filter: brightness(1.08); }
#btn-depart-feu.armee {
  border-color: var(--accent) !important;
  color: var(--accent) !important;
  animation: none;
}
#btn-sim.armee {
  background: var(--rouge);
  border-color: var(--rouge);
  color: #fff;
  animation: pulser 1.2s ease-in-out infinite;
}
#btn-sim:disabled {
  background: #3a404c;
  border-color: #3a404c;
  color: var(--texte-2);
  cursor: wait;
  animation: none;
}
@keyframes pulser {
  0%, 100% { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.55); }
  50% { box-shadow: 0 0 0 9px rgba(229, 72, 77, 0); }
}

/* Couches + légendes */
.ligne-couche {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  font-weight: 600;
  cursor: pointer;
}
/* Retour Jérôme 27/08 : les cases à cocher des couches se peignent en
   INTERRUPTEURS à glissière (référence visuelle Stitch). Le DOM ne change
   pas — mêmes <input type="checkbox">, mêmes gestes, même clavier (Espace),
   mêmes états disabled ; seule la peinture change. */
.ligne-couche input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex-shrink: 0;
  width: 34px;
  height: 19px;
  margin: 0;
  border-radius: 999px;
  border: 1px solid var(--bord);
  background: #2b3140;
  position: relative;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s;
}
.ligne-couche input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: #97a0af;
  transition: transform 0.15s, background-color 0.15s;
}
.ligne-couche input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
.ligne-couche input[type="checkbox"]:checked::after {
  transform: translateX(15px);
  background: #101319;
}
.ligne-couche input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.ligne-couche input[type="checkbox"]:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Couche disponible mais VIDE à cet instant (cas du géostationnaire MTG : rien
   n'est vu sous ~10 MW ni sous couvert nuageux). La case reste ACTIVE et
   visible — une absence de détection est une information, pas une panne. */
.ligne-couche.couche-vide { opacity: 0.65; }
.ligne-couche.couche-vide::after {
  content: '— aucune détection en ce moment';
  font-size: 11px;
  font-weight: 400;
  color: var(--texte-2);
}
/* Mention discrète SOUS le libellé d'une couche (ex. « vent en altitude, pas au
   sol » pour le panache) : elle passe à la ligne au lieu d'allonger la rangée,
   sans toucher aux autres couches (classe posée explicitement, pas de :has()). */
.ligne-couche-note { flex-wrap: wrap; }
/* Le libellé doit rester SUR LA MÊME LIGNE que la case. `flex-basis: 0` est
   indispensable : la répartition en lignes flex se fait AVANT le rétrécissement,
   donc avec une base « auto » le texte (plus large que la place restante) part
   seul sous la case dès 375 px. Base nulle = il tient toujours, puis il s'étire
   sur la place libre et se replie à l'intérieur. */
.ligne-couche-note .libelle-couche { flex: 1 1 0; min-width: 0; }
.indice-couche {
  flex-basis: 100%;
  margin: -4px 0 0 30px;
  font-size: 11px;
  font-weight: 400;
  font-style: italic;
  color: var(--texte-2);
}
.legende { margin: 4px 0 8px; }
.legende-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--texte-2);
  padding: 2px 0;
}
.legende-pastille {
  width: 14px; height: 14px;
  border-radius: 3px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  flex: none;
}
.legende-iso {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--texte-2);
  margin-top: 8px;
}
.pastille-iso { width: 12px; height: 12px; border-radius: 50%; flex: none; }
.degrade-iso {
  flex: 1;
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(90deg, #ffd60a, #ff9f0a, #ff453a, #c42348, #7209b7);
}

/* Hypothèses du run */
#hypo-liste { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; }
#hypo-liste dt { color: var(--texte-2); font-size: 12.5px; }
#hypo-liste dd { margin: 0; font-size: 13px; font-weight: 600; text-align: right; overflow-wrap: anywhere; }

/* Historique */
#btn-histo {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  color: var(--texte);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  padding: 0;
  text-align: left;
}
#liste-runs { list-style: none; margin: 8px 0 0; padding: 0; }
#liste-runs li { margin-bottom: 6px; }
#liste-runs button {
  width: 100%;
  min-height: 44px;
  text-align: left;
  background: #2a2f39;
  color: var(--texte);
  border: 1px solid var(--bord);
  border-radius: 8px;
  padding: 6px 10px;
  cursor: pointer;
  font-size: 13px;
}
#liste-runs button:hover { background: #333a46; }
#liste-runs .run-id {
  font-family: var(--mono); color: var(--texte-2); font-size: 11.5px; }
.liste-vide { color: var(--texte-2); font-size: 13px; }

/* ---------- Marqueurs et étiquettes carte ---------- */
.marqueur-feu {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--rouge);
  border: 3px solid #fff;
  box-shadow: 0 0 0 4px rgba(229, 72, 77, 0.45);
}
.etiquette-iso {
  background: rgba(16, 18, 24, 0.85);
  border: 1.5px solid;
  border-radius: 999px;
  padding: 1px 8px;
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
  pointer-events: none;
  text-align: center;
}

/* T1.2 — mention « interpolation entre pas météo » SOUS l'échéance : garde-fou
   de l'étude du 10/08/2026, il doit se voir SANS survol (un title ne se voit
   pas sur le terrain). Seconde ligne discrète mais lisible, même couleur que
   l'étiquette (héritée de la bordure posée par app.js). */
.etiquette-iso .etiquette-interp {
  display: block;
  font-size: 8.5px;
  font-weight: 600;
  font-style: italic;
  letter-spacing: 0.2px;
  line-height: 1.15;
  opacity: 0.9;
}

/* Popup Seveso */
.maplibregl-popup-content {
  background: var(--bloc);
  color: var(--texte);
  border: 1px solid var(--bord);
  border-radius: 8px;
  padding: 12px 14px;
  font-family: inherit;
  min-width: 200px;
}
.maplibregl-popup-tip { border-top-color: var(--bloc) !important; border-bottom-color: var(--bloc) !important; }
.maplibregl-popup-close-button {
  color: var(--texte-2);
  font-size: 20px;
  width: 30px;
  height: 30px;
}
.popup-seveso h3 { margin: 0 0 6px; font-size: 14px; }
.popup-seveso p { margin: 2px 0; font-size: 12.5px; color: var(--texte-2); }
.popup-seveso .statut { font-weight: 800; }
.statut-haut { color: var(--rouge); }
.statut-bas { color: var(--ambre); }

/* ---------- Disclaimer permanent ---------- */
#bandeau-bas {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 50;
  background: #171410;
  border-top: 2px solid var(--ambre);
  color: #e8dcc3;
  font-size: 12px;
  line-height: 1.3;
  /* ZONE SÛRE DU BAS. Le bandeau garde son fond jusqu'au bord PHYSIQUE (sinon
     une bande noire apparaîtrait sous lui) mais son TEXTE s'arrête au bord
     ATTEIGNABLE : la mention de responsabilité passait sous l'indicateur
     d'accueil de l'iPhone, et c'est justement celle qu'il ne faut pas cacher.
     L'appui qui déplie le texte complet redevient possible sur toute la
     surface utile, --marge-basse comprise. Les côtés suivent pour le paysage. */
  padding: 8px calc(14px + var(--marge-droite, 0px))
           calc(8px + var(--marge-basse, 0px)) calc(14px + var(--marge-gauche, 0px));
  text-align: center;
}

/* ---------- Toasts (messages fugaces) ----------
   PLAFOND DUR ET CARTE TOUJOURS ATTEIGNABLE. Le nombre de bulles (deux) et la
   déduplication sont tenus par messages.js ; la GÉOMÉTRIE est tenue ici, et les
   deux barrières sont indépendantes exprès : même si un jour du code contourne
   le moteur et injecte dix bulles à la main, le `max-height` empêche la colonne
   de manger l'écran.

   pointer-events — LA RÈGLE NON NÉGOCIABLE. Le conteneur est une boîte de
   420 px de large ancrée au-dessus de la timeline : tant qu'il recevait les
   touchers, tout geste destiné à la carte dans cette zone était avalé, même
   quand aucune bulle n'était affichée. Il n'en reçoit plus AUCUN ; seules les
   bulles en reçoivent, et uniquement sur leur propre surface. */
#toasts {
  position: fixed;
  right: 12px;
  bottom: calc(var(--bas-commandes, 54px) + 118px);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(92vw, 420px);
  /* 30 % de la hauteur de l'écran, jamais plus. Sur un iPhone de 812 px cela
     fait 243 px : deux bulles de trois lignes y tiennent, une troisième non —
     et c'est bien la troisième qu'on ne veut plus voir.
     --plafond-toasts est LE MÊME plafond que celui appliqué par messages.js
     (30 % de l'écran, ou la bande disponible moins les 160 px de carte
     garantis — le plus bas des deux). Deux plafonds différents donneraient une
     bulle coupée en deux au lieu d'une bulle retirée. Le `30vh` reste le repli
     si le module n'est pas chargé.

     `vh` PUIS `dvh` — CONVENTION DE TOUTE LA FEUILLE DEPUIS LE 30/07.
     Sur Safari iOS, `vh` se rapporte à la GRANDE zone d'affichage, celle qu'on
     aurait si les barres d'outils étaient rétractées : 812 px là où l'on ne voit
     que ~715 px. Tout ce qui est dimensionné en `vh` est donc trop grand de la
     hauteur des barres, et son bas passe SOUS la barre du navigateur.
     `dvh` (dynamic viewport height) se rapporte à la zone RÉELLEMENT visible.
     Les deux déclarations se suivent exprès : un moteur qui ignore `dvh`
     invalide la seconde et garde la première — repli automatique, sans
     @supports, sans duplication de sélecteur. */
  max-height: min(30vh, var(--plafond-toasts, 30vh));
  max-height: min(30dvh, var(--plafond-toasts, 30dvh));
  overflow: hidden;
  pointer-events: none;
}
/* TÉLÉPHONE — LA COLONNE PASSE EN HAUT, SOUS LA PILE HAUTE.
   Mesuré à 375 × 812 avec un compte opérateur : ancrée en bas à droite, elle se
   posait EN PLEIN SUR les boutons d'action. #barre-intervention est ancrée en
   bas (bottom: 158px) et MONTE en passant à la ligne — elle occupait 530 → 654,
   pendant que les bulles occupaient 491 → 632, donc « 🛰 Se déclarer en
   intervention » (532 → 570) et « 🆘 SOS » (575 → 613) étaient recouverts.
   Aucune place ne reste au-dessus d'elle : « ? Légende » et « ℹ » tiennent le
   bord droit à mi-hauteur (398 → 486).

   La bande sous l'en-tête est la SEULE assez haute pour 30 % de l'écran sans
   croiser quoi que ce soit. Son ordonnée est MESURÉE (--y-toasts, publiée par
   messages.js : bas de l'en-tête, du bandeau unique, du bandeau de rejeu et de
   l'indicateur de calcul) — un `top` en dur finit toujours par se poser sur
   quelque chose. La vue citoyenne garde sa propre règle, plus étroite, plus bas
   dans ce fichier. */
@media (max-width: 900px) {
  #toasts {
    top: var(--y-toasts, 112px);
    /* FRONTIÈRE DURE — la garantie structurelle, celle qui ne dépend d'aucun
       plafond ni d'aucune arithmétique. La colonne est bornée EN HAUT par le bas
       de la pile haute et EN BAS par le haut de la barre d'actions : avec
       `overflow: hidden`, elle ne peut PHYSIQUEMENT pas franchir « 🛰 Se déclarer
       en intervention » ni « 🆘 SOS », même si un plafond était mal calculé, même
       si un futur appelant contournait le moteur. C'est la seule formulation qui
       tienne la règle non négociable sans rien devoir vérifier. */
    bottom: var(--bas-actions, 170px);
    /* Zones sûres LATÉRALES : en portrait elles valent 0 et rien ne change ;
       couché, l'encoche prend 47 px de chaque côté et la colonne s'étendait de
       x = 8 à x = 836 pour une bande atteignable de 47 à 797. Une bulle se
       ferme d'un toucher n'importe où sur sa surface : la partie passée sous
       l'encoche n'était plus refermable. */
    left: calc(8px + var(--marge-gauche, 0px));
    right: calc(8px + var(--marge-droite, 0px));
    max-width: none;
    /* La colonne ne remplit pas sa boîte : les bulles restent EN HAUT de la
       bande, la carte reste dégagée juste au-dessus de la barre d'actions. */
    justify-content: flex-start;
    /* TROIS barrières, dont deux qui ne dépendent d'aucune mesure JS :
         - 30 % de la hauteur (la règle du contrat) ;
         - jamais plus bas que le haut de la barre d'actions, MOINS les 160 px de
           carte garantis. C'est ce dernier terme qui manquait : mesuré à
           375 × 812, deux bulles d'erreur LONGUE tenaient les 30 % (222 px,
           27,3 %) et ne laissaient pourtant que 118 px de carte ;
         - --plafond-toasts, le plafond exact que messages.js fait respecter. */
    /* Le `max(72px, …)` est un PLANCHER délibéré : quand la pile haute et la
       barre d'actions laissent moins de 160 px de bande, le calcul devient
       négatif et la colonne disparaîtrait entièrement. Un pompier privé de tout
       message est pire qu'un message à l'étroit — une bulle courte doit toujours
       pouvoir passer. Même valeur que le plancher du moteur JS. */
    /* Le `100vh` de ce calcul était LE défaut le plus grave sur iPhone : il
       surestimait la bande disponible de la hauteur des barres du navigateur
       (~97 px à 375 × 812), la colonne était donc autorisée à descendre sous la
       barre d'outils et la première bulle sortait de l'écran. `100dvh` mesure ce
       qu'on voit. Repli `vh` conservé juste au-dessus. */
    max-height: min(30vh, var(--plafond-toasts, 30vh),
                    max(72px, calc(100vh - var(--y-toasts, 112px)
                     - var(--bas-actions, 170px) - 160px)));
    max-height: min(30dvh, var(--plafond-toasts, 30dvh),
                    max(72px, calc(100dvh - var(--y-toasts, 112px)
                     - var(--bas-actions, 170px) - 160px)));
  }
}
.toast {
  background: #55131a;
  border: 1.5px solid var(--rouge);
  color: #ffd9db;
  font-weight: 600;
  padding: 12px 14px;
  border-radius: var(--rayon);
  animation: apparaitre 0.15s ease;
  /* TOUTE LA BULLE EST LA CIBLE DE FERMETURE : avec des gants, on ne visait pas
     une croix de 20 px. On touche la bulle, elle part — le message reste dans
     le centre 🔔. */
  pointer-events: auto;
  cursor: pointer;
  /* Une SEULE bulle très longue suffirait à crever le plafond : la hauteur de
     chacune est donc bornée à TROIS lignes. Mesuré à 375 × 812 : les messages
     d'erreur les plus longs du projet (le texte de refus de position iOS, ~500
     caractères) faisaient 107 px à quatre lignes, donc 222 px à deux bulles — et
     il ne restait plus que 118 px de carte. À trois lignes ils font 84 px, donc
     176 px à deux, et la carte garde ses 160 px. Rien n'est perdu : le texte
     entier est dans le centre de messages, atteint par 🔔. */
  max-height: 11vh;
  max-height: 11dvh;   /* iOS : `vh` ignore les barres du navigateur (cf. #toasts) */
  overflow: hidden;
  /* flex: 0 0 auto — CORRIGE UN DÉFAUT MESURÉ. Le conteneur est un flex column
     avec un `max-height` : par défaut ses enfants ont `flex-shrink: 1` et se
     laissent ÉCRASER pour tenir dedans. Mesuré à 375 × 812 avec un bandeau de
     message en plus : deux bulles d'erreur longue passaient de 84 à 37 px, du
     texte coupé au milieu d'une ligne — et comme la colonne mesurait alors 82 px,
     sous le plafond, le moteur JS ne retirait RIEN. L'écrasement masquait le
     dépassement. Une bulle garde donc sa hauteur naturelle, la colonne dépasse
     franchement, et c'est le moteur qui retire la plus ancienne — vers le centre
     de messages, où le texte reste entier. */
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.toast-texte {
  flex: 1 1 auto;
  min-width: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  /* TROIS lignes, pas quatre : cohérent avec le `max-height: 11vh` de la bulle,
     pour qu'une ligne ne soit jamais coupée en deux par l'écrêtage. */
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
/* Compteur de répétitions (« ×3 ») : la déduplication doit se VOIR, sinon
   l'utilisateur croit que le message n'est passé qu'une fois. */
.toast-compte {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
}
/* Niveaux. `.info` est conservé pour tout code appelant qui poserait encore la
   classe historique. */
.toast.info,
.toast.niveau-info { background: #10241a; border-color: #2ecc71; color: #c9f3dc; }
.toast.niveau-important { background: #55131a; border-color: var(--rouge); color: #ffd9db; }
@keyframes apparaitre {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* ---------- Erreur d'initialisation ---------- */
#err-init {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(10, 11, 14, 0.9);
  display: flex;
  align-items: center;
  justify-content: center;
}
.err-boite {
  background: var(--bloc);
  border: 2px solid var(--rouge);
  border-radius: var(--rayon);
  padding: 26px;
  max-width: 420px;
  text-align: center;
}
.err-boite h2 { margin: 0 0 8px; color: var(--rouge); }
.err-boite button {
  margin-top: 14px;
  min-height: 46px;
  padding: 0 22px;
  font-size: 15px;
  font-weight: 700;
  background: var(--jaune);
  color: #14161b;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

/* ---------- Empilement du bas de carte (un seul point de vérité) ----------
   --bas-securite : hauteur réservée au bandeau bas (mesurée en JS si possible).
   --bas-commandes : hauteur de la rangée « ? Légende » + « i », placée
   au-dessus du zoom MapLibre. Les contrôles natifs (zoom, échelle, attribution)
   sont relevés d'autant : plus aucune superposition dans le coin bas-droite. */
:root {
  /* ZONES SÛRES DE L'IPHONE — UN SEUL POINT DE VÉRITÉ.
     `env(safe-area-inset-*)` ne renvoie une valeur non nulle QUE si le document
     porte `viewport-fit=cover` (ajouté dans index.html le 30/07). Le repli `0px`
     est indispensable : les moteurs qui ne connaissent pas `env()` invalident
     toute la déclaration qui l'emploie, et `calc(54px + env(...))` sans repli
     aurait fait disparaître --bas-securite sur un vieux navigateur.
     · en bas  : indicateur d'accueil, 34 px en portrait sur les iPhone à encoche ;
     · en haut : encoche / Dynamic Island, 44 à 59 px en portrait, 0 en paysage ;
     · à gauche et à droite : 44 px chacun EN PAYSAGE — c'est là que #btn-panneau
       (☰, collé à gauche) et #entete (collé à droite) disparaissaient sous
       l'encoche, et un tiroir de commande qu'on n'atteint plus est un tiroir mort. */
  --marge-basse: env(safe-area-inset-bottom, 0px);
  --marge-haute: env(safe-area-inset-top, 0px);
  --marge-gauche: env(safe-area-inset-left, 0px);
  --marge-droite: env(safe-area-inset-right, 0px);
  /* 54 px, et non 46 : le bandeau bas affiche maintenant DEUX lignes (la mention
     de responsabilité était coupée au milieu du mot « opérationnellement »),
     soit ≈ 49 px. 46 px laissaient l'attribution MapLibre passer dessous.
     + --marge-basse : SANS ELLE, tout l'empilement du bas (bandeau
     réglementaire, « ? Légende », « ℹ », zoom MapLibre, barre horaire, barre
     d'actions) était calculé depuis le bord PHYSIQUE de l'écran et non depuis le
     bord ATTEIGNABLE. Sur un iPhone à encoche, les 34 derniers pixels sont
     réservés à l'indicateur d'accueil : le geste y est capté par le système, pas
     par la page. Le bandeau de responsabilité passait donc sous cet indicateur
     et la rangée juste au-dessus devenait difficile à toucher. */
  --bas-securite: calc(54px + var(--marge-basse));
  --bas-commandes: calc(62px + var(--marge-basse));
}
/* Ordre voulu de bas en haut : bandeau · [? Légende][i] · zoom + / − */
.maplibregl-ctrl-bottom-right {
  bottom: calc(var(--bas-commandes) + 52px) !important;
}
.maplibregl-ctrl-bottom-left {
  bottom: var(--bas-securite) !important;
}

/* Contrôles MapLibre en sombre */
.maplibregl-ctrl-group {
  background: var(--bloc) !important;
  border: 1px solid var(--bord);
}
.maplibregl-ctrl-group button { background: var(--bloc); }
.maplibregl-ctrl-group button span { filter: invert(0.9); }
.maplibregl-ctrl-scale {
  background: rgba(16, 18, 24, 0.75);
  color: var(--texte);
  border-color: var(--texte-2);
}

/* ---------- Responsive : panneau en tiroir < 900 px ---------- */
@media (max-width: 900px) {
  #panneau {
    /* Mêmes zones sûres que la règle de base : le tiroir suit le bouton ☰ sous
       l'encoche, et son contenu ne se glisse pas sous l'encoche latérale en
       paysage (44 px à gauche) ni sous l'indicateur d'accueil en bas — les 90 px
       de réserve du bas s'ajoutent à la marge, ils ne la remplacent pas. */
    top: calc(64px + var(--marge-haute, 0px));
    left: 0;
    bottom: 0;
    width: min(86vw, 348px);
    max-width: none;
    border-radius: 0 var(--rayon) 0 0;
    padding-left: calc(14px + var(--marge-gauche, 0px));
    padding-bottom: calc(90px + var(--marge-basse, 0px));
  }
  #entete {
    /* 76 px = bouton ☰ (10 + 46) + 20 px d'air. En paysage l'encoche mange
       44 px à gauche ET à droite : sans les deux marges, l'en-tête débordait
       de l'écran utile alors que sa largeur maximale semblait respectée. */
    max-width: calc(100vw - 76px - var(--marge-gauche, 0px) - var(--marge-droite, 0px));
    padding: 6px 10px;
  }
  #entete-zone { font-size: 12.5px; }
  /* Le calage du bandeau sur --haut-entete (règle de base) vaut aussi ici :
     l'en-tête mobile fait 4 à 6 lignes selon la zone, un `top` figé ne peut
     pas suivre. */
  /* MOBILE — le bas de l'écran est disputé par le bandeau réglementaire, la
     barre timeline (hauteur VARIABLE selon les infos affichées) et la barre
     d'actions. Tout calage en pixels finit par se chevaucher. On sort donc les
     commandes de carte de cette zone : colonne sur le BORD DROIT, centrée
     verticalement — indépendante de la hauteur de la timeline. */
  /* Même règle qu'en base : la zone sûre du bas s'AJOUTE à la réserve, elle ne la
     remplace pas. Sans le `var(--marge-basse)` répété ici, cette redéfinition
     mobile écrasait purement et simplement celle de la base (une @media
     n'ajoute aucune spécificité, c'est l'ordre de la feuille qui tranche) et la
     correction des zones sûres ne s'appliquait QUE sur ordinateur — c'est-à-dire
     nulle part où elle sert. */
  :root {
    --bas-securite: calc(62px + var(--marge-basse));
    --bas-commandes: calc(70px + var(--marge-basse));
  }
  .maplibregl-ctrl-bottom-right {
    bottom: auto !important;
    top: 50% !important;
    transform: translateY(-96px);
  }
  /* !important : les règles de base de ces boutons sont déclarées PLUS BAS dans
     la feuille et l'emporteraient (même spécificité, ordre du cascade) — sans
     cela, `bottom` restait appliqué et l'élément s'étirait sur toute la hauteur. */
  /* --marge-droite : MESURÉ en paysage 812 × 375 avec les zones sûres d'un
     iPhone (47 px à gauche ET à droite). Ces deux commandes, collées à 8 px du
     bord droit, s'étendaient jusqu'à x = 804 alors que la zone atteignable
     s'arrête à 765 : « ? Légende » et « ℹ » étaient à 39 px SOUS l'encoche
     latérale. En paysage, l'encoche n'est plus en haut — elle est sur le côté,
     et c'est le piège que la seule marge du bas ne rattrape pas. */
  #btn-legende {
    bottom: auto !important;
    top: calc(50% - 8px) !important;
    right: calc(8px + var(--marge-droite, 0px)) !important;
  }
  #btn-apropos {
    bottom: auto !important;
    top: calc(50% + 46px) !important;   /* sous « ? Légende » (44 px) + 10 px d'air */
    right: calc(8px + var(--marge-droite, 0px)) !important;
  }
  /* Panneaux ouverts par ces boutons : recentrés, jamais collés au bas */
  #panneau-legende, #panneau-apropos {
    bottom: auto !important;
    top: 50% !important;
    transform: translateY(-50%);
    right: calc(8px + var(--marge-droite, 0px)) !important;
    left: calc(8px + var(--marge-gauche, 0px));
    width: auto;
    max-height: 62vh;
    max-height: 62dvh;   /* iOS : cf. #toasts, `vh` déborde sous la barre d'outils */
  }
  /* Tiroir OUVERT (lot H constat 5) : MESURÉ à 375 × 812 — « ? Légende »
     (x 267-367) recouvrait le tiroir de 323 px (même z-index 30, ordre DOM
     gagnant) et flottait par-dessus les commandes. Le tiroir se referme d'un
     geste (✕) : masquer les deux boutons pendant qu'il est ouvert ne cache
     rien d'inatteignable, ça retire deux faux boutons posés sur du texte. */
  body:not(.panneau-ferme) #btn-legende,
  body:not(.panneau-ferme) #btn-apropos {
    visibility: hidden;
  }
}

/* ---------- Mode réel : badge, observation, feux en cours ---------- */
/* 999px LÉGITIME par le cas (b) : `white-space: nowrap` borne la boîte à UNE
   ligne. Sans ce nowrap, « DONNÉES RÉELLES » se repliait en deux lignes dans un
   en-tête réduit à 46vw et la pastille devenait un ovale. */
#badge-mode {
  display: inline-block;
  background: var(--rouge);
  color: #fff;
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 0.08em;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
  margin-top: 4px;
}
#entete-obs { color: var(--texte); font-size: 12px; margin-top: 3px; }

/* CARTE DE ZONE (maquette Stitch nº 3, point 1) : registre « console » de
   l'entête — les LIGNES DE DONNÉES (état calculé, obs, fraîcheur) passent en
   mono légèrement espacé, comme les valeurs de la maquette ; le badge
   DONNÉES RÉELLES prend l'espacement large. Les CONTENUS ne changent pas, et
   le TITRE reste en Inter : les vrais noms de zones (60-70 caractères) ne
   supportent pas les capitales espacées de la maquette. */
#entete-etat, #entete-fraicheur, #entete-obs {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.02em;
}
#badge-mode { letter-spacing: 0.14em; }

#bloc-feux { border: 1px solid #6b2f33; }
#liste-foyers { list-style: none; margin: 8px 0; padding: 0; }
#liste-foyers .foyer {
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 8px 10px;
  margin-bottom: 8px;
  background: var(--bloc);
}
#liste-foyers .foyer.recent { border-color: var(--rouge); }
#liste-foyers .foyer.actif { outline: 2px solid var(--jaune); }

/* ══════════════════════════════════════════════════════════════════
   PALIERS DE RÉCENCE DES FOYERS (06/08/2026) — RÈGLES NOUVELLES SEULEMENT,
   aucune règle existante modifiée. CHURN VISUEL VOLONTAIREMENT MINIMAL :
     · palier 1 (< 6 h)  = EXACTEMENT l'apparence actuelle de `.recent`
       (rouge, ci-dessus) — rien ne change pour ce que l'opérateur connaît ;
     · palier 3 (> 24 h) = EXACTEMENT l'apparence par défaut (var(--bord)) ;
     · palier 2 (6-24 h) = ambre #fbbf24, teinte DÉJÀ employée dans cette
       feuille pour le non-revu (l. 1451). Aucune couleur nouvelle.
   La couleur ne porte JAMAIS seule l'information : le titre de section porte
   un SIGNE (● ◐ ○) et une PHRASE, et chaque foyer porte son âge en toutes
   lettres dans son titre.
   ══════════════════════════════════════════════════════════════════ */
#liste-foyers .foyer.palier-surveille { border-color: #fbbf24; }
#liste-foyers .foyer.palier-surveille .foyer-titre { color: #ffd89a; }

/* En-tête de section. Reprend les règles de `.section-zone` (l. 293) mais
   ALIGNÉE À GAUCHE : centré, un titre de trois lignes se lit mal. */
.section-palier {
  margin: 12px 0 4px;
  padding-top: 8px;
  border-top: 1px solid var(--bord);
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--texte-2);
  list-style: none;
}
.section-palier.palier-actif { color: #ff8f92; }
.section-palier.palier-surveille { color: #fbbf24; }
#liste-foyers .note-palier {
  list-style: none;
  font-size: 11.5px;
  color: var(--texte-2);
  margin: 0 0 6px;
  line-height: 1.3;
}
/* La référence H+0 de la prévision générale : discrète mais jamais tronquée. */
#ref-h0 { font-size: 11.5px; color: var(--texte-2); margin: 4px 0 6px; }
/* Report de réorganisation : ambre, comme tout ce qui signale « l'écran n'est
   pas tout à fait à jour » dans cette feuille. */
#avis-reorg {
  font-size: 11.5px;
  color: #ffe1a8;
  background: rgba(62, 42, 8, 0.9);
  border: 1px solid var(--ambre);
  border-radius: var(--rayon);
  padding: 6px 8px;
  margin: 4px 0 6px;
}
#liste-foyers .foyer-titre { font-weight: 700; }
#liste-foyers .foyer.recent .foyer-titre { color: #ff8f92; }
#liste-foyers .foyer-detail { color: var(--texte-2); font-size: 12.5px; margin: 2px 0 6px; }
#liste-foyers .foyer-actions { display: flex; gap: 8px; }
#liste-foyers .foyer-actions button {
  min-height: 44px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 14px;
  cursor: pointer;
}
#liste-foyers .foyer-actions button.principal {
  background: var(--rouge);
  border-color: var(--rouge);
  color: #fff;
  font-weight: 700;
  flex: 1;
}
#liste-foyers .foyer-actions button:hover { filter: brightness(1.12); }

button.secondaire {
  display: block;
  width: 100%;
  min-height: 44px;
  margin-top: 8px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 14px;
  cursor: pointer;
}
button.secondaire:hover { filter: brightness(1.12); }
button.secondaire:disabled { opacity: 0.55; cursor: wait; }

/* Marqueur lettre d un foyer sur la carte */
.marqueur-foyer {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: #8e4a10;
  color: #fff;
  font-weight: 800;
  font-size: 14px;
  cursor: pointer;
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.7);
  /* UNE DETECTION SATELLITE N'EST JAMAIS RECOUVERTE — voir le pave d'empilement
     de `.hypo-boite`. C'est le SEUL marqueur qui repose sur une mesure et non
     sur une supposition : ni un depart suppose, ni une etiquette d'heure ne
     passe devant lui, et le clic a l'endroit d'un feu reel ouvre TOUJOURS la
     prevision de ce feu reel. */
  z-index: 40;
}
/* La legende dessine les memes marqueurs en miniature, hors carte : l'ordre
   d'empilement n'y a aucun sens et un z-index eleve y ferait remonter la
   vignette au-dessus du panneau. */
.marqueur-foyer.leg-mini { z-index: auto; }
.marqueur-foyer.recent {
  background: var(--rouge);
  animation: pulse-foyer 1.6s ease-out infinite;
}
/* Foyer non revu au dernier passage satellite : extinction possible */
.marqueur-foyer.non-revu {
  opacity: 0.55;
  background: #6b7280;
  border: 2px dashed #e5e7eb;
  animation: none;
}

/* LIGNE DE FEU VALIDEE — la seule ambiguite de legende MESUREE le 14/08/2026.
   Sur 81 vignettes comparees (couleur de fond, image de fond, bordure, contour,
   ombre, animation, rayon, dimensions), 72 apparences distinctes et UN SEUL
   conflit reel : « Rouge : tres rapide >= 3 km/h » (echelle de vitesse du
   front) et « Rouge epais : ligne de feu VALIDEE » portaient le meme #e5484d
   et ne differaient que par 2 px de hauteur — 5 contre 7. Deux notions sans
   aucun rapport, separees par deux pixels, sur un ecran lu de loin.
   La difference passe donc par la FORME : extremites CARREES et lisere sombre,
   la ou l'echelle de vitesse garde ses traits arrondis. Lisible en niveaux de
   gris, et sans introduire une teinte de plus dans une palette deja chargee. */
.leg-trait.leg-trait-ligne-feu {
  height: 9px;
  border-radius: 0;
  box-shadow: 0 0 0 1px #14161b;
}

/* ---------------------------------------------------------------------------
   ETATS QUALIFIES DES FOYERS (CH7, 14/08/2026)

   L'ancien jeu ne connaissait que deux etats — vu / non revu — et les deux
   nuances qui comptent pour un COS tombaient dans le meme gris tirete : un
   foyer qu'on n'a PAS PU revoir (nuages, un seul passage) et un foyer que le
   satellite a survole QUINZE FOIS sans rien voir.

   DISTINCTION PAR LA FORME AUTANT QUE PAR LA COULEUR. Un ecran de salle
   operationnelle est lu de loin, parfois en plein jour sur un portable, et
   ~8 % des hommes distinguent mal le rouge du vert : une difference qui ne
   tient qu'a la teinte n'est pas une difference. D'ou, ici, trois SILHOUETTES
   differentes, lisibles en niveaux de gris :
     actif   -> disque PLEIN, bord continu, pulsation
     doute   -> bord TIRETE, pas de pulsation, teinte ambre
     eteint  -> disque BARRE d'une diagonale, gris, aplati
--------------------------------------------------------------------------- */
.marqueur-foyer.etat-doute {
  opacity: 0.85;
  /* P6 (21/08/2026) : #7a5a12 tombait au MEME gris que le fond de base
     #8e4a10 une fois desature (91 contre 88 sur 255) — en niveaux de gris,
     seul le tirete separait encore les deux. Fond nettement plus sombre
     (gris ~55) et tirete plus epais : la silhouette tient sans la couleur. */
  background: #4a3608;
  border: 3px dashed var(--orange, #ff9f0a);
  animation: none;
}
.marqueur-foyer.etat-eteint {
  opacity: 0.5;
  background: #4b5563;
  border: 2px solid #9ca3af;
  animation: none;
  /* ⚠ SURTOUT PAS `position: relative` ICI. MapLibre positionne ses marqueurs
     avec `.maplibregl-marker { position: absolute }` (specificite 0,1,0) et
     pose la classe sur CE bouton, passe directement en `element:`. Une regle
     `position` de specificite 0,2,0 la battait et rendait le bouton au flux
     normal : le 1er rond eteint restait en place, le 2e glissait de 30 px, le
     3e de 60 px — soit ~0,7 puis ~1,4 km au sol au zoom 12,2, avec leur cible
     de clic. Cas nominal du 14/08/2026 : les QUATRE foyers etaient eteints.
     Le ::after ci-dessous n'a pas besoin de cette regle : le marqueur est deja
     un bloc conteneur par le `absolute` du vendor. */
}
/* La barre diagonale : c'est ELLE qui porte l'information, la couleur ne fait
   que la confirmer. Visible en niveaux de gris et a 30 px. */
.marqueur-foyer.etat-eteint::after {
  content: '';
  position: absolute;
  left: -2px; right: -2px; top: 50%;
  height: 2px;
  background: #e5e7eb;
  transform: rotate(-45deg);
  transform-origin: center;
  pointer-events: none;
}
/* La legende dessine ces memes marqueurs en miniature : la barre doit suivre.
   ICI le `position: relative` est LEGITIME et NECESSAIRE — la vignette est un
   <span> statique hors carte, il lui faut son propre bloc conteneur. */
.marqueur-foyer.leg-mini.etat-eteint { position: relative; }
.marqueur-foyer.leg-mini.etat-eteint::after { left: -1px; right: -1px; }

/* Paragraphe d'etat dans la fiche de foyer : meme code couleur que le
   marqueur, pour qu'un aller-retour carte <-> fiche ne demande aucun effort. */
.popup-foyer .pf-evenements { margin: 4px 0 0; font-size: 11.5px;
  line-height: 1.45; color: #9fd0ff; }
.popup-foyer .pf-nonrevu.etat-actif { color: #ff8f92; }  /* A22 : meme rouge que l'en-tete */
.popup-foyer .pf-nonrevu.etat-doute { color: var(--orange, #ff9f0a); }
.popup-foyer .pf-nonrevu.etat-eteint { color: #9ca3af; }
@keyframes pulse-foyer {
  0% { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.65); }
  100% { box-shadow: 0 0 0 16px rgba(229, 72, 77, 0); }
}

/* ---------- Foyers hypothétiques — PARTAGÉS, ET DITS COMME TELS ----------
   Un foyer hypothetique partage qu'on prendrait pour une detection serait un
   defaut PIRE que celui que le partage corrige. Il porte donc TROIS differences
   simultanees avec une detection, et aucune n'est une nuance de couleur :
     1. LA FORME : losange. Le rond appartient aux detections (.marqueur-foyer).
        Un losange se distingue en niveaux de gris, en plein soleil et pour un
        daltonien — c'est LE signe, la couleur ne fait que confirmer.
     2. LE CONTENU : « ? », jamais une lettre (A/B/C… = detections satellite).
     3. LE MOT, EN PERMANENCE : pastille « HYPOTHESE » sous le marqueur, jamais
        une infobulle — une infobulle ne se lit pas au doigt.
   Bordure PLEINE de 3 px : le pointille de l'ancien marqueur disparaissait en
   plein soleil sur un telephone. Cible 46 px (l'ancien rond faisait 30 px,
   deja sous la cible). */
.hypo-boite {
  position: relative;
  width: 46px;
  height: 46px;
  /* Le losange deborde de la boite (diagonale 65 px) : la boite reste a 46 px
     pour que MapLibre centre le LOSANGE sur le point, pas la pastille. */
  overflow: visible;
  /* ═══ ORDRE D'EMPILEMENT — LA DETECTION REELLE PASSE TOUJOURS DEVANT ═══
     Ce marqueur doit passer devant les ETIQUETTES D'ISOCHRONE (mesure : une
     etiquette « H+4 · 07/08 00h49 » se posait EN PLEIN SUR le losange et
     masquait le « ? »), mais JAMAIS devant une DETECTION SATELLITE.
     ⚠ DEFAUT MESURE LE 06/08 ET CORRIGE ICI. Avec `z-index: 6` contre le
     `z-index: auto` de `.marqueur-foyer`, l'hypothese passait devant TOUTES les
     detections. Mesure : hypothese posee 49 px au-dessus du foyer A,
     `document.elementFromPoint(195, 429)` — le centre EXACT du rond de la
     detection — renvoyait le « ? » de l'hypothese. Consequences : (a) la lettre
     « A » du feu REEL n'etait plus a l'ecran ; (b) cliquer sur le feu reel
     ouvrait la prevision de l'HYPOTHESE. C'est l'ecran plausible et faux dans
     l'autre sens — un depart suppose qui cache un feu qui brule vraiment.
     Une supposition ne masque pas une mesure : hypotheses en 3..29 (rang par
     latitude, voir rendreHypotheses), detections a 40, bien au-dessus.
     Le RANG PAR LATITUDE regle un second defaut mesure : la pastille d'une
     hypothese (posee 58 px SOUS son losange) etait recouverte par le losange
     d'une hypothese voisine plus au sud, et le mot se lisait « YPOTHESE »
     (20,4 px ronges sur 109,5, soit la lettre initiale). `white-space: nowrap`
     empeche la troncature typographique, pas l'occlusion par un marqueur
     opaque. Les marqueurs etant chacun leur propre contexte d'empilement (
     MapLibre leur pose un `transform`), seul l'ordre des BOITES peut le
     resoudre : la plus au NORD passe devant, donc sa pastille couvre le
     losange du dessous, et non l'inverse. */
  z-index: 3;
}
.marqueur-hypothese {
  position: absolute;
  inset: 0;
  width: 46px;
  height: 46px;
  padding: 0;
  border-radius: 4px;
  border: 3px solid var(--jaune);
  background: #14161b;
  color: var(--jaune);
  cursor: pointer;
  transform: rotate(45deg);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.85);
}
.marqueur-hypothese-txt {
  display: block;
  transform: rotate(-45deg); /* contre-rotation : le « ? » reste droit */
  font-weight: 800;
  font-size: 20px;
  line-height: 1;
}
/* Hypothese que le SERVEUR n'a pas acceptee (repli hors reseau) : meme forme,
   mais la pastille DIT qu'elle n'est visible que d'ici. */
.marqueur-hypothese.locale {
  border-style: double;
  border-width: 5px;
}
.pastille-hypothese {
  position: absolute;
  top: 58px; /* sous la pointe basse du losange (23 + 32,5 px) */
  left: 50%;
  transform: translateX(-50%);
  padding: 3px 8px;
  border-radius: 6px;
  background: #3a2f04;
  border: 1.5px solid var(--jaune);
  color: #ffeaa0;
  font-weight: 800;
  font-size: 13px;
  letter-spacing: 0.02em;
  white-space: nowrap; /* AUCUNE troncature : le mot se lit en entier */
  pointer-events: none;
}
.pastille-hypothese.locale {
  background: #4a1210;
  border-color: var(--rouge);
  color: #ffd9d6;
}
#liste-manuels { list-style: none; margin: 8px 0 0; padding: 0; }
/* ⚠ `display: block` EXPLICITE, et il corrige une fiche coupee sur telephone.
   La classe generique `.manuel` (plus haut dans cette feuille) sert a un petit
   controle en ligne sans aucun rapport et vaut `display: inline-flex`. Cette
   regle-ci ne declarant pas `display`, la fiche d'hypothese l'heritait : ses
   cinq blocs (titre / avertissement / coordonnees / journal / actions) se
   mettaient en COLONNES. Mesure du 06/08 a 390 px : fiche de 344,6 px depuis
   x = 28 dans un panneau de 335,4 px — « Evolution prevue » et « Prolonger
   12 h » etaient coupes en plein mot. */
#liste-manuels .foyer.manuel { display: block; border: 1px dashed var(--jaune); border-radius: var(--rayon); padding: 8px 10px; margin-bottom: 8px; background: var(--bloc); }
#liste-manuels .foyer.manuel.non-partagee { border-color: var(--rouge); }
#liste-manuels .foyer-titre { font-weight: 700; color: var(--jaune); }
/* « Ce n'est PAS une detection satellite » : toujours affiche, jamais en
   infobulle, et jamais tronque — c'est la phrase qui protege. */
.hypo-avertissement {
  color: #ffeaa0;
  font-size: 12.5px;
  font-weight: 700;
  margin: 3px 0 2px;
}
#liste-manuels .foyer.manuel.non-partagee .hypo-avertissement { color: #ffd9d6; }
.hypo-journal { color: var(--texte-2); font-size: 12px; margin: 2px 0 6px; }
#liste-manuels .foyer-detail { color: var(--texte-2); font-size: 12.5px; margin: 2px 0 6px; }
#liste-manuels .foyer-actions { display: flex; gap: 8px; flex-wrap: wrap; }
#liste-manuels .foyer-actions button.hypo-prolonger {
  background: #2a2f39; border-color: var(--bord); font-weight: 600;
}
#liste-manuels .foyer-actions button {
  min-height: 44px; padding: 6px 12px; border-radius: 8px;
  border: 1px solid var(--bord); background: #2a2f39; color: var(--texte);
  font-size: 14px; cursor: pointer;
}
#liste-manuels .foyer-actions button.principal { background: #7a6410; border-color: #a3861a; color: #fff; font-weight: 700; flex: 1; }
#liste-manuels .foyer-actions button.supprimer { background: #55131a; border-color: var(--rouge); color: #ffd9db; font-weight: 800; min-width: 44px; }
#liste-manuels .foyer-actions button:hover { filter: brightness(1.12); }

/* ---------- Axe de propagation et enjeux menacés ---------- */
.fleche-tete {
  width: 0; height: 0;
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  border-left: 20px solid #ff2d55;
  filter: drop-shadow(0 0 3px rgba(0,0,0,0.8));
}
.marqueur-habitat {
  padding: 2px 7px;
  border-radius: 6px;
  font-weight: 800;
  font-size: 12.5px;
  color: #14161b;
  border: 1.5px solid #14161b;
  box-shadow: 0 1px 4px rgba(0,0,0,0.6);
  white-space: nowrap;
}
.marqueur-habitat.eta-rouge { background: #ff5a5f; color: #fff; }
.marqueur-habitat.eta-orange { background: #ffa540; }
.marqueur-habitat.eta-jaune { background: #ffd60a; }
/* « Je ne peux pas partir » (journal Stitch B) : action SECONDAIRE du
   panneau rouge — l'accusé « j'évacue » reste le geste principal. */
.evac-piege {
  width: 100%; min-height: 48px;
  margin-top: 8px;
  border-radius: 10px;
  border: 1.5px solid var(--rouge, #ff2d55);
  background: transparent;
  color: var(--rouge, #ff2d55);
  font-weight: 700; font-size: 14px;
}

/* F1 (journal flamap) : canvas des particules de vent — au-dessus des
   tuiles, sous toutes les commandes, jamais un clic volé à la carte. */
#vent-champ-canvas {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  transition: background-color 0.4s;
}
/* Le mode vent assombrit la carte (retour 27/08 : blanc sur IGN clair
   illisible) — un fond CSS sous les traînées, pas un changement de tuiles :
   décocher (ou sortir du champ) rend la carte claire à l'instant. */
#vent-champ-canvas.vent-voile { background-color: rgba(8, 11, 17, 0.52); }
/* Une imagerie EUMETSAT est cochée (foudre, thermique…) : voile allégé pour
   la laisser lisible sous les particules. */
#vent-champ-canvas.vent-voile.vent-voile-leger { background-color: rgba(8, 11, 17, 0.26); }

/* Mini-légende du niveau France (verdict P, zip Stitch nº 4). */
#legende-france {
  display: none;
  position: absolute;
  left: 10px;
  bottom: calc(34px + var(--marge-basse, 0px));
  z-index: 5;                    /* sous les commandes (30), sur la carte */
  flex-direction: column;
  gap: 3px;
  padding: 8px 12px;
  border-radius: var(--rayon);
  border: 1px solid var(--bord);
  background: var(--panneau);
  font-size: 11.5px;
  color: var(--texte-2);
  pointer-events: none;          /* jamais un clic volé à la carte */
}
#carte.niveau-france #legende-france { display: flex; }
#legende-france .lf-titre {
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 10.5px;
  color: var(--texte);
}
#legende-france .lf-ligne { display: flex; align-items: center; gap: 7px; }
#legende-france .lf-point {
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1px solid #fff;
  flex: none;
}
#legende-france .lf-note { font-size: 10.5px; opacity: 0.85; }

/* Feux en cours d'un coup d'œil (constat opérateur du 25/08/2026). */
#france-feux-liste {
  display: none;
  position: absolute;
  right: 10px;
  bottom: calc(96px + var(--marge-basse, 0px));
  z-index: 6;
  flex-direction: column;
  gap: 4px;
  padding: 9px 11px;
  border-radius: var(--rayon);
  border: 1px solid var(--bord);
  background: var(--panneau);
  max-width: min(310px, 78vw);
  max-height: 42vh;
  overflow-y: auto;
}
#carte.niveau-france #france-feux-liste { display: flex; }
.ffl-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.ffl-titre {
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 10.5px;
  color: var(--texte);
}
.ffl-fermer {
  flex: none;
  border: 0;
  background: transparent;
  color: var(--texte-2);
  font-size: 14px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
}
.ffl-fermer:hover, .ffl-fermer:focus-visible { color: var(--texte); }
.ffl-rouvrir {
  display: flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--bord);
  border-radius: 999px;
  background: transparent;
  color: var(--texte);
  font-family: var(--mono);
  font-size: 11.5px;
  padding: 4px 11px;
  cursor: pointer;
}
.ffl-rouvrir:hover { border-color: var(--accent); }
/* Légende France : barre de fraîcheur continue (rouge vif -> orangés ->
   gris, mêmes bornes que l'interpolation des couches) et rappel du pulse. */
.lf-degrade {
  display: inline-block;
  width: 64px;
  height: 8px;
  border-radius: 4px;
  background: linear-gradient(90deg,
    #ff2f2f, #e5484d 13%, #f76b15 25%, #f5a524 50%, #c98a3d 75%, #6b7280);
}
@media (prefers-reduced-motion: no-preference) {
  .lf-pulse { animation: lf-pulse-bat 1.6s ease-out infinite; }
}
@keyframes lf-pulse-bat {
  0%   { box-shadow: 0 0 0 0 rgba(255, 47, 47, 0.7); }
  100% { box-shadow: 0 0 0 8px rgba(255, 47, 47, 0); }
}
/* Bulle de pastille nationale : bouton d'entrée dans le secteur équipé
   (07/09/2026 — remplace le clic sur les carrés de zones, retirés). */
.popup-ouvrir-zone {
  display: block;
  width: 100%;
  margin-top: 7px;
  padding: 6px 10px;
  background: var(--accent);
  color: #101319;
  border: 0;
  border-radius: 7px;
  font-weight: 600;
  font-size: 12.5px;
  cursor: pointer;
}
.popup-ouvrir-zone:hover { filter: brightness(1.08); }
/* Halo éphémère « le feu est ICI » (vol depuis la liste France). */
.halo-feu {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 3px solid #e5484d;
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .halo-feu { animation: halo-feu-pulse 1.6s ease-out 4; }
}
@keyframes halo-feu-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.75); }
  100% { box-shadow: 0 0 0 42px rgba(229, 72, 77, 0); }
}
.ffl-ligne {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 4px 6px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--texte);
  font-family: var(--mono);
  font-size: 11.5px;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.ffl-ligne:hover { background: var(--bloc); }
.ffl-point {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid #fff;
  flex: none;
}
.ffl-note { font-size: 10.5px; color: var(--texte-2); }
.france-badge-feux {
  margin-left: 6px;
  padding: 0 7px;
  border-radius: 999px;
  background: #e5484d;
  color: #fff;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
}

/* Intertitres du panneau SOS (verdict H, zip Stitch nº 4). */
.sos-groupe {
  margin: 10px 0 2px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--texte-2);
}

/* Avis d'exposition au CHOIX du point d'eau (verdict F, zip Stitch nº 4). */
.entraide-expo-choix {
  display: block;
  margin-top: 4px;
  color: #ff8f92;
  font-weight: 700;
}

/* Fiche foyer (verdict N, zip Stitch nº 4) : le chiffre principal en gros. */
.pf-mw-gros {
  display: block;
  font-family: var(--mono);
  font-size: 21px;
  font-weight: 800;
  color: #ffd60a;
  line-height: 1.15;
}
.pf-mw-detail { display: block; font-size: 11.5px; color: var(--texte-2); }

/* Glissière « glisser pour confirmer » (journal Stitch A) — geste continu
   pour les ordres graves ; le rail vire au rouge quand la course est faite. */
.glissiere {
  position: relative;
  display: flex; align-items: center;
  width: 100%; min-height: 48px;
  border-radius: 24px;
  border: 1.5px solid var(--rouge, #ff2d55);
  background: var(--bloc);
  overflow: hidden;
  touch-action: none;
}
.glissiere-texte {
  flex: 1;
  text-align: center;
  font-weight: 700; font-size: 13.5px;
  color: var(--rouge, #ff2d55);
  pointer-events: none;
  user-select: none;
}
.glissiere-poignee {
  position: absolute; left: 4px; top: 4px; bottom: 4px;
  width: 56px;
  border: none; border-radius: 20px;
  background: var(--rouge, #ff2d55); color: #fff;
  font-size: 20px; font-weight: 800;
  cursor: grab;
  touch-action: none;
}
.glissiere-poignee:active { cursor: grabbing; }
.glissiere.glissiere-prete { background: rgba(255, 45, 85, 0.25); }
.glissiere.glissiere-validee .glissiere-texte::after { content: ' ✓'; }

.marqueur-seveso-menace {
  width: 34px; height: 34px;
  border-radius: 50%;
  border: 3px dashed #ff2d55;
  animation: pulse-foyer 1.4s ease-out infinite;
}
/* Zonage PPRT mordu (B15) : carré tireté — une SURFACE réglementaire, pas un
   point ; la couleur suit l'heure d'atteinte comme les marqueurs ⌂. */
.marqueur-pprt-menace {
  padding: 2px 7px;
  border-radius: 4px;
  font-weight: 800;
  font-size: 12.5px;
  color: #14161b;
  border: 2px dashed #14161b;
  box-shadow: 0 1px 4px rgba(0,0,0,0.6);
  white-space: nowrap;
}
.marqueur-pprt-menace.eta-rouge { background: #ff5a5f; color: #fff; }
.marqueur-pprt-menace.eta-orange { background: #ffa540; }
.marqueur-pprt-menace.eta-jaune { background: #ffd60a; }
#liste-menaces { list-style: none; margin: 8px 0 0; padding: 0; }
#liste-menaces li { margin-bottom: 6px; }
#liste-menaces button {
  display: flex; align-items: center; gap: 10px;
  width: 100%; min-height: 44px;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
#liste-menaces .menace-seveso button, #liste-menaces li.menace-seveso button { border-color: var(--rouge); }
#liste-menaces button:hover { filter: brightness(1.12); }
.menace-eta {
  flex: 0 0 auto;
  padding: 2px 7px;
  border-radius: 6px;
  font-weight: 800;
  color: #14161b;
}
.menace-eta.eta-rouge { background: #ff5a5f; color: #fff; }
.menace-eta.eta-orange { background: #ffa540; }
.menace-eta.eta-jaune { background: #ffd60a; }

/* ---------- Jonction de foyers (vue générale) ---------- */
.marqueur-fusion {
  padding: 3px 8px;
  border-radius: 6px;
  background: #7209b7;
  color: #fff;
  font-weight: 800;
  font-size: 12.5px;
  border: 1.5px solid #fff;
  box-shadow: 0 0 8px rgba(114, 9, 183, 0.9);
  white-space: nowrap;
  animation: pulse-foyer 1.4s ease-out infinite;
}
#liste-menaces li.menace-fusion button { border-color: #7209b7; background: #2a1440; }

/* ---------- Légende vitesse du front ---------- */
.legende-vitesse {
  display: flex; align-items: center; gap: 8px;
  color: var(--texte-2); font-size: 12px;
  margin: 2px 0 8px 28px;
}
.degrade-vitesse {
  flex: 1; height: 7px; border-radius: 4px;
  background: linear-gradient(90deg, #3a86ff, #2dc653, #ffd60a, #ff6d00, #e5484d);
}

/* ---------- Légende explicative ---------- */
#btn-legende {
  position: fixed;
  /* 58 px depuis le bord ATTEIGNABLE (à gauche du « ℹ », même rangée) :
     --marge-droite tient compte de l'encoche latérale en paysage. */
  right: calc(58px + var(--marge-droite, 0px));
  bottom: var(--bas-commandes, 54px);
  z-index: 30;
  min-height: 44px;
  padding: 8px 14px;
  /* 999px LÉGITIME par (b) : nowrap. « ? Légende » sur deux lignes dans un
     bouton de 44 px de haut aurait donné un galet, et la commande la plus
     souvent touchée n'a pas le droit d'être illisible. */
  border-radius: 999px;
  white-space: nowrap;
  border: 1px solid var(--bord);
  background: var(--panneau);
  color: var(--texte);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.5);
}
#btn-legende:hover { filter: brightness(1.15); }

/* CH8 (21/08/2026) — VUE FRANCE CONTINUE. Deux commandes, une doctrine :
   revenir à la France entière est un GESTE DE CARTE, jamais un menu. */
#btn-france {
  position: fixed;
  /* Rangée du bas, à gauche de « ? Légende » : mêmes règles d'atteignabilité
     (--marge-droite = encoche en paysage), même galet 44 px. */
  right: calc(160px + var(--marge-droite, 0px));
  bottom: var(--bas-commandes, 54px);
  z-index: 30;
  min-height: 44px;
  padding: 8px 14px;
  border-radius: 999px;
  white-space: nowrap;
  border: 1px solid var(--bord);
  background: var(--panneau);
  color: var(--texte);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.5);
}
#btn-france:hover { filter: brightness(1.15); }

/* Fil d'Ariane « France › zone » : pastille discrète EN HAUT À GAUCHE, à
   droite du bouton ☰ (46 px). TOUJOURS visible (c'est lui qui dit OÙ l'on
   est), cliquable côté France.
   REPOSITIONNÉ le 21/08/2026 (retour Jérémy, captures) : l'ancien
   top:--haut-pile le faisait DESCENDRE avec la pile (entête multi-lignes,
   SOS…) jusqu'au milieu de l'écran, et son left:10px/z-index:30 — les mêmes
   que #panneau, déclaré AVANT lui — le posait PAR-DESSUS le panneau ouvert. */
#fil-ariane {
  position: fixed;
  top: calc(19px + var(--marge-haute, 0px));
  left: calc(66px + var(--marge-gauche, 0px));
  z-index: 29;                    /* sous le panneau (30) et le ☰ (32) */
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--bord);
  background: var(--panneau);
  font-size: 12.5px;
  color: var(--texte-2);
  box-shadow: 0 2px 8px rgba(0,0,0,0.45);
}
#fil-ariane[hidden] { display: none; }
/* Panneau OUVERT (body sans .panneau-ferme) : le fil se MASQUE — le panneau
   affiche déjà « Zone couverte : … » (l'information serait en double), et à
   droite du panneau il entrerait en collision avec l'en-tête (mesuré à la
   recette du 21/08). Il revient dès que le panneau se replie. */
body:not(.panneau-ferme) #fil-ariane { display: none; }
/* Démo : le ☰ descend sous le bandeau (règle body.demo-active #btn-panneau),
   le fil le suit. */
body.demo-active #fil-ariane { top: calc(var(--h-demo, 92px) + 19px); }

/* Niveau France (zoom < 7, classe posée par app.js) : les marqueurs DOM de la
   zone — foyers lettrés, intervenants, démo… — s'empileraient en un tas
   illisible sur quelques pixels. On masque leur conteneur MapLibre commun :
   rien n'est retiré ni détruit, tout réapparaît dès qu'on repasse le seuil.
   Les popups (.maplibregl-popup) ne sont PAS touchées : la fiche d'une
   pastille nationale reste lisible. */
#carte.niveau-france .maplibregl-marker { display: none; }
#fil-france {
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  padding: 2px 2px;
  min-height: 32px; /* pastille compacte : la cible réelle reste ≥ 32 px */
}
#fil-france:hover { text-decoration: underline; }
.fil-sep { opacity: 0.6; }
/* Devenu un bouton (retour a la zone en un geste) : meme neutralisation que
   #fil-france, mais couleur texte — c'est le lieu COURANT, pas un lien accent. */
#fil-zone {
  border: 0;
  background: none;
  color: var(--texte);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  padding: 2px 2px;
  min-height: 32px;
}
#fil-zone:hover { text-decoration: underline; }

/* Vignettes de légende de la vue France : pastille pleine (détection nationale)
   et trait de contour de secteur — mêmes couleurs que les couches MapLibre. */
.leg-pastille-fr {
  display: inline-block;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1px solid #fff;
  flex: 0 0 auto;
  margin-top: 2px;
}
.leg-trait-secteur {
  display: inline-block;
  width: 22px; height: 0;
  border-top: 2px solid #ffd60a;
  flex: 0 0 auto;
  margin-top: 8px;
}

/* Téléphone : le fil d'Ariane passe sous la pile haute, le bouton France reste
   dans la rangée du bas — jamais deux rangées de galets. */
@media (max-width: 700px) {
  #btn-france { right: calc(150px + var(--marge-droite, 0px)); }
  #fil-ariane { font-size: 11.5px; max-width: 62vw; }
  #fil-zone { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

#panneau-legende {
  position: fixed;
  right: 10px;
  bottom: calc(var(--bas-commandes, 54px) + 118px);
  z-index: 35;
  width: min(92vw, 430px);
  max-height: min(72vh, 640px);
  max-height: min(72dvh, 640px);   /* iOS : cf. #toasts */
  overflow-y: auto;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 12px 16px 14px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.6);
}
.leg-tete { display: flex; align-items: center; justify-content: space-between; }
/* B13 : sommaire d'ancres de la legende — pastilles compactes ; le panneau
   reste le meme, le sommaire n'est qu'un index vers ses sections. */
.leg-filtre { display: flex; gap: 7px; align-items: baseline;
  margin: 8px 0 2px; font-size: 11.5px; color: var(--texte-2);
  cursor: pointer; }
.leg-sommaire { display: flex; flex-wrap: wrap; gap: 5px; margin: 8px 0 10px; }
.leg-sommaire button { background: #1c2230; border: 1px solid #333c52;
  color: var(--texte-2); border-radius: 999px; padding: 3px 9px;
  font-size: 11px; line-height: 1.3; cursor: pointer; }
.leg-sommaire button:hover { border-color: #4d9fff; color: #fff; }
.leg-tete h2 { margin: 0; font-size: 17px; }
#btn-legende-fermer {
  min-width: 44px; min-height: 44px;
  border-radius: 8px; border: 1px solid var(--bord);
  background: #2a2f39; color: var(--texte);
  font-size: 16px; cursor: pointer;
}
#panneau-legende h3 {
  margin: 12px 0 6px;
  font-size: 13px;
  color: var(--ambre);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.leg-ligne {
  display: flex; align-items: center; gap: 10px;
  margin: 5px 0; font-size: 13.5px; line-height: 1.35;
}
.leg-ligne > :first-child { flex: 0 0 auto; }
.leg-remplissage {
  width: 46px; height: 18px; border-radius: 4px;
  background: linear-gradient(90deg, rgba(255,214,10,0.55), rgba(255,105,58,0.55), rgba(114,9,183,0.55));
  border: 1px solid var(--bord);
}
.leg-trait { width: 46px; height: 5px; border-radius: 3px; }
.leg-axe {
  width: 46px; height: 0;
  border-top: 3px dashed #ff2d55;
}
.leg-point { width: 13px; height: 13px; border-radius: 50%; margin-left: 16px; }
.leg-point-g { border: 2px solid #fff; width: 15px; height: 15px; }
.leg-mini { transform: scale(0.85); cursor: default; animation: none !important; }
.marqueur-foyer.leg-mini.recent { animation: none; }
/* LEGENDE DE L'HYPOTHESE : le losange doit y etre reconnaissable AVANT d'etre
   rencontre sur la carte. Il tourne comme le vrai et garde son « ? » — une
   vignette ronde portant « M1 » (l'ancienne) se confondait avec une detection,
   ce qui est exactement le defaut que la forme existe pour empecher. */
.leg-hypo {
  position: relative;
  display: inline-block;
  width: 34px; height: 34px;
}
.leg-hypo .marqueur-hypothese {
  width: 34px; height: 34px;
  border-width: 2px;
  box-shadow: none;
  cursor: default;
}
.leg-hypo .marqueur-hypothese-txt { font-size: 15px; }
.leg-chip { transform: scale(0.92); cursor: default; animation: none !important; }
.leg-seveso-menace {
  width: 26px; height: 26px; border-radius: 50%;
  border: 3px dashed #ff2d55; margin-left: 8px;
}
.leg-note {
  margin: 12px 0 0; padding-top: 8px;
  border-top: 1px solid var(--bord);
  color: var(--texte-2); font-size: 12.5px;
}
@media (max-width: 900px) {
  #panneau-legende { bottom: calc(var(--bas-commandes, 126px) + 52px); }
}

/* ---------- Tag version beta ---------- */
/* 999px LÉGITIME par (b) : nowrap. « VERSION BÊTA » est en DEUX mots — sans
   nowrap il se repliait dans un panneau étroit et la pastille devenait un ovale
   bleu de deux lignes. */
.tag-beta {
  display: inline-block;
  background: #2a4d8f;
  color: #dbe7ff;
  font-weight: 800;
  font-size: 10.5px;
  letter-spacing: 0.07em;
  padding: 1px 7px;
  border-radius: 999px;
  white-space: nowrap;
  vertical-align: middle;
}

/* ---------- Alerte convectif (pyroCb) ---------- */
.tag-convectif {
  margin: 4px 0 6px;
  padding: 3px 8px;
  border-radius: 6px;
  background: #4a1240;
  border: 1.5px solid #c026d3;
  color: #f5d0fe;
  font-weight: 800;
  font-size: 11.5px;
  letter-spacing: 0.02em;
}

/* ---------- Marquage opérationnel + fiche mobile ---------- */
/* 999px LÉGITIME par (b) : nowrap ajouté. */
.tag-partage {
  display: inline-block; background: #155e75; color: #cffafe;
  font-weight: 800; font-size: 10px; letter-spacing: 0.07em;
  padding: 1px 7px; border-radius: 999px; vertical-align: middle;
  white-space: nowrap;
}
#modes-marquage { display: flex; gap: 6px; margin: 8px 0; }
#modes-marquage button {
  flex: 1; min-height: 44px; padding: 6px 4px; border-radius: 8px;
  border: 1px solid var(--bord); background: #2a2f39; color: var(--texte);
  font-size: 13px; cursor: pointer;
}
#modes-marquage button.actif { background: #155e75; border-color: #22d3ee; font-weight: 700; }
#liste-objets { list-style: none; margin: 4px 0 0; padding: 0; }
#liste-objets .objet {
  border: 1px solid #155e75; border-radius: var(--rayon);
  padding: 8px 10px; margin-bottom: 8px; background: var(--bloc);
}
#liste-objets .foyer-actions button {
  min-height: 44px; padding: 6px 10px; border-radius: 8px;
  border: 1px solid var(--bord); background: #2a2f39; color: var(--texte);
  font-size: 13.5px; cursor: pointer;
}
#liste-objets .foyer-actions button.principal { background: #155e75; border-color: #22d3ee; color: #fff; font-weight: 700; flex: 1; }
#liste-objets .foyer-actions button.supprimer { background: #55131a; border-color: var(--rouge); color: #ffd9db; min-width: 44px; }
.marqueur-objet { font-size: 24px; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.8)); cursor: pointer; }
.etiquette-ligne {
  background: #0c3444; border: 1.5px solid #22d3ee; color: #cffafe;
  padding: 2px 8px; border-radius: 6px; font-weight: 700; font-size: 12px; white-space: nowrap;
}
.menace-corps { flex: 1; }
.menace-sous { color: var(--texte-2); font-size: 11.5px; margin-top: 2px; }
#liste-menaces li { display: flex; gap: 6px; align-items: stretch; }
#liste-menaces li > button:first-child { flex: 1; }
.menace-envoyer {
  min-width: 44px; border-radius: 8px; border: 1px solid var(--bord);
  background: #2a2f39; color: var(--texte); font-size: 16px; cursor: pointer;
}

#info-bulle {
  /* ZONE SÛRE DU BAS (--marge-basse, section « Empilement du bas de carte ») :
     tous les ancrages `bottom` en pixels de cette feuille la reçoivent, sinon
     ils comptent depuis le bord PHYSIQUE de l'écran et non depuis le bord
     ATTEIGNABLE — l'indicateur d'accueil de l'iPhone occupe les 34 derniers
     pixels et y capte les gestes à la place de la page. */
  position: fixed; left: 10px; right: 10px; z-index: 60;
  bottom: calc(60px + var(--marge-basse, 0px));
  display: flex; gap: 10px; align-items: flex-start;
  background: var(--panneau); border: 1px solid var(--ambre);
  border-radius: var(--rayon); padding: 12px 14px;
  box-shadow: 0 -4px 24px rgba(0,0,0,0.5);
  font-size: 14px; line-height: 1.4;
}
#info-bulle .info-bulle-texte { flex: 1; }
#info-bulle button {
  min-width: 44px; min-height: 44px; border-radius: 8px;
  border: 1px solid var(--bord); background: #2a2f39; color: var(--texte);
  font-size: 15px; cursor: pointer;
}

/* Masquage des marqueurs de prévision quand seule la situation observée est affichée */
body.sans-prevision .etiquette-iso,
body.sans-prevision .marqueur-habitat,
body.sans-prevision .marqueur-fusion,
body.sans-prevision .marqueur-seveso-menace,
body.sans-prevision .etiq-sautes,
body.sans-prevision .fleche-tete { display: none !important; }

/* ---------- Fiche de partage de position ---------- */
#panneau-partage {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(54px + var(--marge-basse, 0px)); z-index: 70;   /* zone sûre : cf. #info-bulle */
  width: min(94vw, 520px); max-height: 78vh; overflow-y: auto;
  max-height: 78dvh;   /* iOS : cf. #toasts */
  background: var(--panneau); border: 1px solid var(--ambre);
  border-radius: var(--rayon); padding: 12px 14px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.7);
}
.partage-tete { display: flex; align-items: center; justify-content: space-between; }
.partage-tete h2 { margin: 0; font-size: 16px; }
.partage-tete button {
  min-width: 44px; min-height: 44px; border-radius: 8px;
  border: 1px solid var(--bord); background: #2a2f39; color: var(--texte);
  font-size: 15px; cursor: pointer;
}
.partage-message {
  background: var(--bloc); border: 1px solid var(--bord); border-radius: 8px;
  padding: 10px 12px; margin: 10px 0;
  font-family: inherit; font-size: 13.5px; line-height: 1.45;
  white-space: pre-wrap; word-break: break-word;
  user-select: all; -webkit-user-select: all;
}
.partage-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.partage-actions button, .partage-bouton {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 8px 14px; border-radius: 8px;
  border: 1px solid var(--bord); background: #2a2f39; color: var(--texte);
  font-size: 14px; cursor: pointer; text-decoration: none;
}
.partage-actions button:hover, .partage-bouton:hover { filter: brightness(1.15); }
.partage-image { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.partage-image img {
  max-width: 100%; border-radius: 8px; border: 1px solid var(--bord);
}

/* Liste des enjeux : pastille horaire compacte + corps qui se replie proprement */
#liste-menaces .menace-eta {
  white-space: nowrap;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  line-height: 1.15; font-size: 12.5px; min-width: 64px;
}
#liste-menaces .menace-eta-sous { font-size: 10px; font-weight: 700; opacity: 0.85; }
#liste-menaces .menace-corps { min-width: 0; }
#liste-menaces .menace-corps > div { overflow-wrap: anywhere; }

/* ---------- Liste des enjeux : lignes compactes + détails dépliables ---------- */
#liste-menaces li.menace { display: flex; flex-wrap: wrap; gap: 6px; align-items: stretch; }
#liste-menaces .menace-ligne {
  flex: 1 1 0; min-width: 0; width: auto;
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 6px 10px;
  border-radius: 8px; border: 1px solid var(--bord);
  background: var(--bloc); color: var(--texte);
  font-size: 13.5px; text-align: left; cursor: pointer;
}
#liste-menaces li.menace-seveso .menace-ligne { border-color: var(--rouge); }
#liste-menaces li.menace-fusion .menace-ligne { border-color: #7209b7; background: #2a1440; }
#liste-menaces .menace-eta {
  flex: 0 0 auto; min-width: 0; white-space: nowrap;
  display: inline-block; line-height: 1.2;
  padding: 3px 7px; font-size: 12px;
}
#liste-menaces .menace-titre {
  flex: 1 1 0; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#liste-menaces .menace-chevron { flex: 0 0 auto; color: var(--texte-2); }
#liste-menaces .menace-envoyer {
  flex: 0 0 44px; width: 44px; min-height: 44px;
  border-radius: 8px; border: 1px solid var(--bord);
  background: #2a2f39; color: var(--texte); font-size: 15px; cursor: pointer;
}
#liste-menaces .menace-detail {
  flex: 1 1 100%; width: 100%;
  border: 1px solid var(--bord); border-radius: 8px;
  background: #171a20; padding: 8px 10px;
  font-size: 12.5px; line-height: 1.4; color: var(--texte);
}
#liste-menaces .menace-detail > div { overflow-wrap: anywhere; margin-bottom: 4px; }
#liste-menaces .menace-detail .menace-sous { color: var(--texte-2); }
/* Raccourci « 🚨 Évacuer ce secteur » (lot H constat 14) — même langage visuel
   que les commandes d'évacuation : il OUVRE un formulaire, il ne diffuse rien. */
#liste-menaces .menace-evacuer {
  display: block;
  margin-top: 6px;
  min-height: 38px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1.5px solid var(--rouge);
  background: #3a1114;
  color: #ffd7d9;
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
}
#liste-menaces .menace-evacuer:hover { filter: brightness(1.15); }

/* Boutons des fiches points d''eau */
.popup-seveso .btn-acces, .popup-seveso .btn-envoi-eau {
  min-height: 34px; padding: 4px 10px; border-radius: 6px;
  border: 1px solid #4b5563; background: #1f2937; color: #e5e7eb;
  font-size: 12.5px; cursor: pointer;
}
.popup-seveso .acces-res { font-size: 12.5px; }

/* Fiche foyer au clic sur le marqueur lettré : consultation sans recalcul */
.popup-foyer .pf-actions { margin-top: 8px; }
.popup-foyer .pf-actions .principal {
  min-height: 38px; width: 100%; padding: 6px 12px; border-radius: 8px;
  border: 1px solid #b45309; background: #d97706; color: #fff;
  font-size: 13px; font-weight: 700; cursor: pointer;
}
.popup-foyer .pf-conv { font-weight: 800; color: #f87171; }
.popup-foyer .pf-nonrevu, .foyer-detail.non-revu-note {
  color: #fbbf24; font-weight: 600;
}

/* Échéances déjà écoulées (t0 = détection ancienne) : historique estimé, pas prévision */
.etiquette-iso.etiquette-passee {
  opacity: 0.55;
  border-style: dashed;
  font-style: italic;
}

/* Front estimé actuel : pastille blanche distinctive */
.etiquette-iso.etiquette-front {
  border-color: #ffffff !important;
  color: #ffffff !important;
  font-weight: 700;
}

/* Sélection des foyers pour la prévision générale */
.selection-foyers {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 8px;
  font-size: 0.82rem;
  color: #c8cdd6;
  cursor: pointer;
  user-select: none;
}
.selection-foyers input { accent-color: #ff453a; }
.foyer-titre { display: flex; align-items: center; gap: 8px; }
.foyer-check { accent-color: #ff453a; flex-shrink: 0; margin: 0; cursor: pointer; }
#btn-prev-generale { width: 100%; margin-top: 8px; }

/* Pastille du front estimé : contraste renforcé */
.etiquette-iso.etiquette-front {
  border-width: 2px;
  font-size: 0.8rem;
  box-shadow: 0 0 0 2px rgba(20, 22, 27, 0.9), 0 2px 10px rgba(0, 0, 0, 0.55);
}

/* Bouton de validation de la sélection au bas de la liste des foyers */
#btn-valider-selection { width: 100%; margin: 4px 0 6px; }

/* Puces de sélection des échéances affichées */
.ligne-echeances { margin: 2px 0 8px 26px; }
.echeances-titre {
  display: block;
  font-size: 0.72rem;
  color: #8b93a1;
  margin-bottom: 4px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Ruban temporel (zip Stitch nº 3-c) : les puces d'échéances se lisent
   comme les CRANS d'un ruban — un tiret les relie dans l'ordre du temps.
   Seuls les paliers CALCULÉS ont un cran : l'espace entre deux puces ne
   promet aucune heure intermédiaire (pas de règle graduée continue, elle
   mentirait). overflow:hidden coupe le tiret orphelin en début de ligne
   quand le ruban se replie sur plusieurs rangées (téléphone). */
#chips-echeances { display: flex; flex-wrap: wrap; gap: 5px; overflow: hidden; }
.chip-echeance { position: relative; }
.chip-echeance:not(:first-child)::before {
  content: '';
  position: absolute;
  left: -5px;
  top: 50%;
  width: 5px;
  height: 1px;
  background: var(--bord);
}
/* Palier lu par interpolation (sous l'heure, T1.2) : pointillé + ≈ dans le
   libellé — la précision affichée n'est pas la précision réelle. */
.chip-echeance.chip-interp { border-style: dotted; }
/* 999px LÉGITIME par (b) : nowrap ajouté (« + 2 h », « H + 12 » : deux jetons
   séparés par une espace, donc repliables). */
.chip-echeance {
  padding: 3px 9px;
  font-size: 0.74rem;
  border-radius: 999px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #8b93a1;
  cursor: pointer;
  white-space: nowrap;
}
.chip-echeance.actif {
  border-color: #ff9f0a;
  background: rgba(255, 159, 10, 0.16);
  color: #ffd60a;
  font-weight: 600;
}

/* Barre de timeline horaire (bas de carte) */
#timeline-bar {
  position: fixed;
  left: 50%;
  /* Repli seulement : calerTimeline() écrit `style.bottom` à partir de la hauteur
     RÉELLE du bandeau bas (qui intègre déjà --marge-basse). La zone sûre est
     ajoutée ici pour que le repli ne soit pas faux avant le premier calage. */
  bottom: calc(44px + var(--marge-basse, 0px));
  transform: translateX(-50%);
  z-index: 30;
  width: min(520px, calc(100vw - 32px));
  background: rgba(20, 22, 27, 0.92);
  border: 1px solid #3a4150;
  border-radius: 12px;
  padding: 8px 14px 10px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45);
}
#timeline-info {
  font-size: 0.8rem;
  font-weight: 600;
  color: #ffd60a;
  text-align: center;
  margin-bottom: 4px;
}
#timeline-curseur {
  width: 100%;
  accent-color: #ff9f0a;
  cursor: pointer;
  touch-action: none; /* mobile : le doigt tire le curseur, pas la carte */
}
/* « front hors écran — recadrer » : posé DANS la ligne d'information de la barre
   horaire, là où l'opérateur lit déjà l'heure et la surface. Cible tactile de
   28 px de haut minimum, comme les autres commandes de la barre. */
.puce-recadrer {
  display: inline-block;
  margin-left: 4px;
  padding: 2px 8px;
  min-height: 22px;
  font-size: 0.7rem;
  font-weight: 700;
  color: #14161b;
  background: #ff9f0a;
  border: 1px solid #ffb340;
  border-radius: 7px;
  cursor: pointer;
  vertical-align: middle;
}
.puce-recadrer:hover { background: #ffb340; }
/* Prévision décochée : le bouton de mode DIT que rien ne sera dessiné, au lieu
   de laisser croire à un curseur en panne (troisième état, cf. appliquerModeVue). */
#timeline-mode.eteint {
  border-color: #6b7280;
  background: #23262e;
  color: #9aa1ac;
  font-style: italic;
}
.fleche-timeline {
  font-size: 26px;
  color: #ff9f0a;
  text-shadow: 0 0 3px #14161b, 0 0 6px #14161b;
  line-height: 1;
  pointer-events: none;
}

/* Puce d'échéance non calculée sur le run courant */
.chip-echeance.indispo { opacity: 0.35; border-style: dashed; }

/* Contrôles de la barre timeline (bouton de mode + curseur) */
.timeline-controles { display: flex; align-items: center; gap: 10px; }
#timeline-mode {
  flex-shrink: 0;
  padding: 4px 10px;
  font-size: 0.72rem;
  border-radius: 8px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #c8cdd6;
  cursor: pointer;
  white-space: nowrap;
}
#timeline-mode.actif {
  border-color: #ff9f0a;
  background: rgba(255, 159, 10, 0.16);
  color: #ffd60a;
  font-weight: 600;
}

/* Flèche d'axe horaire : flèche + pastille vent (la pastille ne tourne pas) */
.fleche-timeline-boite {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  pointer-events: auto;
}
.fleche-timeline-pastille {
  font-size: 0.68rem;
  font-weight: 700;
  color: #ffd60a;
  background: rgba(20, 22, 27, 0.9);
  border: 1px solid #ff9f0a;
  border-radius: 999px;
  padding: 1px 8px;
  white-space: nowrap;
}

/* RAFALES : toujours dans une couleur/bordure DIFFÉRENTE du vent moyen (cyan
   contre ambre), pour qu'aucun coup d'œil ne confonde la pointe et la moyenne.
   La rafale n'entre dans aucun calcul de propagation (infobulle TITRE_RAFALE). */
.valeur-rafale {
  color: #67e8f9;
  font-weight: 700;
  cursor: help;
  white-space: nowrap;
}
/* LUTTE SUPPOSEE : posee A COTE DU CHIFFRE DE SURFACE, parce que la lutte est
   invisible sur la carte — ce n'est pas un marqueur, c'est une REDUCTION de
   l'enveloppe affichee. Deux etats, JAMAIS absente : « aucune lutte supposee »
   et « lutte supposee » ne se lisent pas de la meme facon. Patron de pastille
   .tag-convectif (fond sombre, bordure 1,5 px, texte 800, majuscules), et un
   MOT porte le sens — la couleur ne fait que confirmer. */
.puce-lutte {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 6px;
  border: 1.5px solid;
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 0.02em;
  white-space: nowrap;
  vertical-align: middle;
  cursor: help;
}
.puce-lutte.reduite {
  background: #4a1210;
  border-color: #ff453a;
  color: #ffd9d6;
}
.puce-lutte.libre {
  background: #14161b;
  border-color: #3a4150;
  color: #c8cdd6;
}
.fleche-timeline-pastille.pastille-rafale {
  color: #67e8f9;
  border-color: #22d3ee;
  border-style: dashed;
  font-weight: 600;
  cursor: help;
}
/* Vent de TRANSPORT (panache) : gris-bleu discret, ni ambre (vent moteur du
   feu) ni cyan (rafale) — c'est une troisième grandeur, pas une variante. */
.valeur-transport {
  color: #9fb3c8;
  cursor: help;
  white-space: nowrap;
}

/* Étiquette du SECTEUR de projection des brandons (sautes de feu) */
.etiq-sautes {
  font-size: 0.68rem;
  font-weight: 700;
  color: #ffc2cf;
  background: rgba(20, 22, 27, 0.88);
  border: 1px dashed #ff2d55;
  border-radius: 999px;
  padding: 1px 8px;
  white-space: nowrap;
  cursor: help;
  pointer-events: auto;
}

/* Bandeau « nouvelles données disponibles » (veille unifiée) et proposition de
   recalcul après modification des lignes de feu.

   `border-radius: 999px` NE FAIT UNE PILULE QUE SUR UNE SEULE LIGNE. Dès que le
   texte passe à la ligne sur un écran étroit, la boîte devient haute et le rayon
   la transforme en ELLIPSE : sur la capture Android du 29/07, le bandeau
   occupait la moitié supérieure de l'écran en un énorme disque orange, avec le
   texte qui en débordait sur la gauche. Le rayon est donc BORNÉ à un rayon fixe
   (14 px), qui reste une pilule sur une ligne et devient un cadre arrondi
   correct sur plusieurs. `flex-wrap` laisse les boutons descendre au lieu
   d'écraser le texte, et `top` se cale SOUS l'en-tête au lieu de le recouvrir.
   Ces deux identifiants ne sont plus créés par app.js (tout est passé par le
   centre de messages, règle « au plus UN bandeau visible ») : la règle est
   corrigée à la source parce qu'elle reste un piège pour tout futur bandeau. */
#maj-dispo, #recalc-dispo {
  position: fixed;
  /* GÉOMÉTRIE MESURÉE, jamais figée. `top: 12px` posait le bandeau PAR-DESSUS
     l'en-tête ; `top: 72px` ne valait que pour un en-tête de deux lignes et
     repassait dessus dès qu'il gagnait l'alerte de fraîcheur ou le mode démo.
     --y-bandeau est le bas réel de la pile haute (10 px + hauteur mesurée de
     #entete + 8 px, décalée de --h-demo en rejeu) : c'est le SEUL ancrage. */
  top: var(--y-bandeau);
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 10px;
  box-sizing: border-box;
  width: max-content;
  max-width: calc(100vw - 24px);
  padding: 8px 16px;
  border-radius: 14px;
  border: 1px solid #ff9f0a;
  background: rgba(255, 159, 10, 0.95);
  color: #14161b;
  font-weight: 700;
  font-size: 0.82rem;
  line-height: 1.35;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
}
/* Deux bandeaux ne s'EMPILENT plus l'un sur l'autre : le second se pose sous le
   premier. Sur la capture, « nouvelles données » recouvrait « recalcul
   conseillé » et l'on voyait deux boutons « ↻ Recalculer » superposés.
   76 px = deux lignes de texte + les boutons repliés + le gap : c'est un
   MINORANT sûr de la hauteur du premier bandeau, dont la hauteur exacte n'est
   pas mesurée (ces deux identifiants sont morts, voir plus haut). */
#maj-dispo ~ #recalc-dispo { top: calc(var(--y-bandeau) + 76px); }
.maj-dispo-txt { flex: 1 1 auto; min-width: 0; }
.maj-dispo-btn {
  flex: 0 0 auto;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid rgba(20, 22, 27, 0.55);
  background: rgba(20, 22, 27, 0.12);
  color: #14161b;
  font-weight: 700;
  font-size: 0.78rem;
  cursor: pointer;
  white-space: nowrap;
}
.maj-dispo-btn.principal {
  background: #14161b;
  color: #ffd60a;
  border-color: #14161b;
}
.maj-dispo-btn:hover { filter: brightness(1.12); }

/* Puce d'état de la prévision affichée : servie depuis le cache (navigation
   instantanée) ou calculée avant l'arrivée de nouvelles données. Discrète par
   défaut, ORANGE dès qu'un recalcul est conseillé. */
#puce-cache {
  position: fixed;
  left: 50%;
  bottom: calc(100px + var(--marge-basse, 0px));  /* repli ; recalculé par calerTimeline() */
  transform: translateX(-50%);
  z-index: 29;                /* juste sous la barre timeline */
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: min(520px, calc(100vw - 32px));
  padding: 4px 8px 4px 12px;
  /* PAS 999px : la puce porte un texte libre (« calculée à 22h06, données de
     15h59 ») qui passe à la ligne sur écran étroit, et un rayon de 999px
     transforme alors la boîte en ellipse (défaut de la capture du 29/07). */
  border-radius: 12px;
  border: 1px solid #3a4150;
  background: rgba(20, 22, 27, 0.92);
  color: #8b93a1;
  font-size: 0.72rem;
  line-height: 1.25;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.4);
}
#puce-cache[hidden] { display: none; }
/* MOBILE : le bas de l'écran est saturé (bandeau, timeline, barre d'actions) et
   la puce y était recalée AU-DESSUS de la timeline, dont la hauteur varie —
   elle finissait par recouvrir « Se déclarer en intervention » et le SOS.
   On la sort de cette zone : petite pastille en haut à droite, indépendante
   de la hauteur de la timeline (le !important neutralise le calage en ligne). */
@media (max-width: 900px) {
  #puce-cache {
    bottom: auto !important;
    /* SOUS l'en-tête ET SOUS le bandeau unique : --y-bandeau donne le bas de
       l'en-tête mesuré, --haut-bandeau la hauteur réellement occupée par
       #bandeau-messages (0 quand il est masqué). Sans le second terme, la puce
       et le bandeau se posaient à la MÊME ordonnée et la puce disparaissait
       derrière lui (z-index 29 contre 45). */
    top: calc(var(--y-bandeau) + var(--haut-bandeau, 0px));
    left: auto;
    /* --marge-droite : MESURÉ à 844 × 390 avec les zones sûres d'un iPhone
       couché (47 px de chaque côté). Collée à 8 px du bord PHYSIQUE, la puce
       s'étendait jusqu'à x = 836 alors que la zone atteignable s'arrête à 797 :
       39 px sous l'encoche latérale — et c'est « ↻ Recalculer » qui s'y
       trouvait. Même piège que « ? Légende » et « ℹ » : en paysage l'encoche
       n'est plus en haut, elle est sur le côté. */
    right: calc(8px + var(--marge-droite, 0px));
    transform: none;
    max-width: calc(62vw - var(--marge-gauche, 0px) - var(--marge-droite, 0px));
    font-size: 0.68rem;
    padding: 3px 6px 3px 10px;
  }
}
#puce-cache.alerte {
  border-color: #ff9f0a;
  color: #ffd60a;
  font-weight: 600;
}
#puce-cache-texte { min-width: 0; }
#puce-cache-recalc {
  flex: 0 0 auto;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #c8cdd6;
  font-size: 0.7rem;
  cursor: pointer;
  white-space: nowrap;
}
#puce-cache-recalc.principal {
  border-color: #ff9f0a;
  background: rgba(255, 159, 10, 0.18);
  color: #ffd60a;
  font-weight: 700;
}
#puce-cache-recalc:hover { filter: brightness(1.2); }

/* Surbrillance temporaire d'un réglage à activer (guidage lignes de défense) */
.champ.surbrillance {
  outline: 2px solid #ff9f0a;
  outline-offset: 3px;
  border-radius: 8px;
  animation: pulse-surbrillance 1.2s ease-in-out 5;
}
@keyframes pulse-surbrillance {
  50% { outline-color: rgba(255, 159, 10, 0.25); }
}

/* Aéronefs en direct (ADS-B) */
.aeronef {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  pointer-events: auto;
}
.aeronef-icone {
  font-size: 22px;
  color: #ffd60a;
  text-shadow: 0 0 3px #14161b, 0 0 6px #14161b;
  line-height: 1;
}
.aeronef-icone.au-sol { opacity: 0.55; }
.aeronef-pastille {
  font-size: 0.66rem;
  font-weight: 700;
  color: #ffd60a;
  background: rgba(20, 22, 27, 0.9);
  border: 1px solid #ffd60a;
  border-radius: 999px;
  padding: 0 7px;
  white-space: nowrap;
}

/* Statut de prise en compte des lignes de défense dans le calcul */
.statut-ligne {
  margin: 6px 0 2px;
  padding: 4px 10px;
  border-radius: 8px;
  font-size: 0.76rem;
  font-weight: 600;
}
.statut-ligne.statut-ok {
  background: rgba(45, 198, 83, 0.14);
  border: 1px solid #2dc653;
  color: #7ce8a4;
}
.statut-ligne.statut-attente {
  background: rgba(255, 159, 10, 0.14);
  border: 1px solid #ff9f0a;
  color: #ffd60a;
}
.statut-ligne.statut-off {
  background: rgba(139, 147, 161, 0.10);
  border: 1px dashed #8b93a1;
  color: #aab2bf;
  cursor: pointer;
}

/* États des aéronefs : en vol (jaune) / au sol (bleu acier) / position périmée (gris) */
.aeronef.statut-vol .aeronef-icone { color: #ffd60a; }
.aeronef.statut-vol .aeronef-pastille { color: #ffd60a; border-color: #ffd60a; }
.aeronef.statut-sol .aeronef-icone { color: #60a5fa; opacity: 0.85; }
.aeronef.statut-sol .aeronef-pastille { color: #93c5fd; border-color: #60a5fa; }
.aeronef.statut-perime .aeronef-icone { color: #8b93a1; opacity: 0.6; }
.aeronef.statut-perime .aeronef-pastille {
  color: #aab2bf;
  border-color: #8b93a1;
  border-style: dashed;
  opacity: 0.85;
}

/* Alerte sautes de feu (signature convective) */
.alerte-sautes {
  margin: 6px 0 10px;
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 0.8rem;
  font-weight: 600;
  background: rgba(255, 159, 10, 0.12);
  border: 1px solid #ff9f0a;
  color: #ffd60a;
}
/* Titre court (orienté, prudent) toujours visible ; le détail serveur (Haines,
   CAPE, facteur de vent) est replié pour ne pas noyer la phrase qui déclenche
   une action — surtout sur téléphone. */
.alerte-sautes-detail { margin-top: 6px; }
.alerte-sautes-detail > summary {
  font-size: 0.72rem;
  font-weight: 400;
  opacity: 0.85;
  cursor: pointer;
  min-height: 24px;
}
.alerte-sautes-detail > p {
  margin: 4px 0 0;
  font-size: 0.72rem;
  font-weight: 400;
  opacity: 0.85;
}
.alerte-sautes.niveau-eleve {
  background: rgba(255, 69, 58, 0.12);
  border-color: #ff453a;
  color: #ffb3ae;
}
.alerte-sautes.niveau-pyro {
  background: rgba(255, 45, 85, 0.16);
  border-color: #ff2d55;
  color: #ffc2cf;
  animation: pulse-surbrillance 1.6s ease-in-out infinite;
}

/* Réserve « bâti d'interface » — ce que le modèle ne sait pas dire dans le
   pavillonnaire (CLC 112). DÉLIBÉRÉMENT SOBRE : ni rouge, ni pulsation, ni
   pleine largeur. Ce n'est pas un danger imminent de plus, c'est une réserve
   de méthode sur des chiffres déjà affichés ; l'habiller en alerte apprendrait
   à l'opérateur à ignorer les alertes, y compris .alerte-sautes.niveau-pyro
   juste au-dessus. FRÉQUENCE MESURÉE, CHIFFRE CORRIGÉ LE 06/08/2026 : ~1
   prévision sur 7 en moyenne (14,2 % sur 632 prévisions de données de
   production, 8 zones) — et non « 1 sur 10 » comme écrit ici auparavant. Cette
   moyenne est dispersée : 59,5 % aux Calanques contre 3,8 % dans les Landes,
   d'où le seuil réglable par zone (config.json `seuil_pct_par_zone`).
   LA COULEUR NE PORTE RIEN SEULE : le signe ⌂ et le mot « BÂTI » en capitales
   sont dans la phrase servie par backend/app.py:_bati_interface. Le liseré
   gauche ne fait que distinguer le bloc de l'orange des sautes de feu. */
.alerte-bati {
  margin: 6px 0 10px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--bord, #3a4150);
  border-left: 3px solid #8b93a1;
  background: rgba(139, 147, 161, 0.10);
  color: var(--texte, #e6e9ef);
  font-size: 0.8rem;
}
.alerte-bati-titre { font-weight: 600; line-height: 1.35; }
.alerte-bati-detail { margin-top: 4px; }
/* CIBLE TACTILE 46 px du contrat. Le voisin `.alerte-sautes-detail > summary`
   est resté à 24 px : écart préexistant signalé à Jérémy, PAS recopié ici.
   Padding plutôt que flex pour ne pas supprimer le triangle de dépliage, seul
   indice qu'il y a quelque chose à ouvrir. */
.alerte-bati-detail > summary {
  font-size: 0.75rem;
  font-weight: 400;
  opacity: 0.9;
  cursor: pointer;
  min-height: 46px;
  padding: 14px 0;
  box-sizing: border-box;
}
.alerte-bati-detail > p {
  margin: 4px 0 0;
  font-size: 0.72rem;
  font-weight: 400;
  opacity: 0.9;
  line-height: 1.5;
}

/* ENJEUX RÉELS (T2.5) : bâtiments RNB + habitants Filosofi de l'enveloppe.
   Bloc INFORMATIF (pas une réserve de méthode) : même gabarit que
   .alerte-bati pour l'alignement, mais SANS liseré ni fond — deux blocs
   gris identiques côte à côte se liraient comme deux alertes, et celui-ci
   n'en est pas une. Les phrases viennent du serveur (textContent). */
.enjeux-reels {
  margin: 6px 0 10px;
  padding: 0 2px;
  font-size: 0.8rem;
  color: var(--texte, #e6e9ef);
}
.enjeux-reels-titre { font-weight: 600; line-height: 1.35; }
.enjeux-reels-detail { margin-top: 2px; }
/* Même cible tactile 46 px que .alerte-bati-detail (contrat du dépôt). */
.enjeux-reels-detail > summary {
  font-size: 0.75rem;
  font-weight: 400;
  opacity: 0.9;
  cursor: pointer;
  min-height: 46px;
  padding: 14px 0;
  box-sizing: border-box;
}
.enjeux-reels-detail > p {
  margin: 4px 0 0;
  font-size: 0.72rem;
  font-weight: 400;
  opacity: 0.9;
  line-height: 1.5;
}

/* Ligne de provenance des comptes dans le détail d'une menace (T2.5) :
   même famille que .menace-sous, légèrement plus discrète — c'est une
   étiquette épistémologique, pas une information tactique. */
.menace-provenance { opacity: 0.75; font-style: italic; }

/* Recherche de commune / adresse */
.champ-recherche { position: relative; margin: 6px 0 2px; }
#rech-lieu {
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--bord, #3a4150);
  background: #1c2029;
  color: var(--texte, #e6e9ef);
  font-size: 0.85rem;
}
#rech-lieu:focus { outline: 2px solid #ff9f0a; }
#rech-resultats {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 40;
  background: #14161b;
  border: 1px solid #3a4150;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}
.rech-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 12px;
  background: none;
  border: none;
  border-bottom: 1px solid #23272f;
  color: #e6e9ef;
  cursor: pointer;
}
.rech-item:hover, .rech-item:focus { background: #23272f; }
.rech-libelle { display: block; font-size: 0.82rem; font-weight: 600; }
.rech-contexte { display: block; font-size: 0.7rem; color: #8b93a1; }
.rech-vide { padding: 8px 12px; font-size: 0.8rem; color: #8b93a1; }
.marqueur-recherche {
  font-size: 30px;
  line-height: 1;
  cursor: pointer;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.7));
}

/* Marque PYRONOS */
.marque {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
}
.logo-pyronos { width: 40px; height: 40px; flex-shrink: 0; }
.marque-nom {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  background: linear-gradient(90deg, #ffd60a, #ff9f0a);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.pan-copyright {
  margin: 14px 0 4px;
  font-size: 0.68rem;
  color: #6b7280;
  text-align: center;
}

/* Bandeau bas compact : appui/clic pour déplier le texte complet.
   DEUX LIGNES, plus une seule. Sur la capture Android (412 px), l'unique ligne
   se coupait à « VERSION BÊTA (non validée opération… » : la mention de
   responsabilité s'arrêtait AU MILIEU DU MOT qui la porte. Deux lignes affichent
   « OUTIL D'AIDE À LA DÉCISION — VERSION BÊTA (non validée opérationnellement).
   Ne se substitue ni au jugement du COS… », c'est-à-dire la phrase qui engage.
   Le coût est borné et connu : 2 × 15,6 px de texte + 16 px de marges + 2 px de
   bordure ≈ 49 px, sous les 62 px que --bas-securite réserve sur téléphone (et
   sous les 54 px réservés sur ordinateur, relevés de 46 px pour l'occasion).
   `calerTimeline()` mesure `offsetHeight` à chaque fois : la barre horaire et la
   puce d'état remontent d'elles-mêmes. */
/* L'ÉCRÊTAGE N'EST PLUS LE MÉCANISME PRINCIPAL — il n'est plus qu'un filet.
   app.js n'envoie ici que la PREMIÈRE PHRASE du disclaimer, entière, suivie du
   signal « ▾ Avertissement complet ». Le line-clamp passe donc à trois lignes :
   il ne coupe plus rien dans le cas normal (mesuré : 2 lignes suffisent à 375 px
   pour la phrase de la configuration + le signal), et il reste une protection si
   un jour la configuration porte une première phrase beaucoup plus longue. Sans
   ce filet, un texte anormal pousserait le bandeau sur la moitié de la carte. */
#bandeau-bas {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  cursor: pointer;
}
/* LE SIGNAL DE TRONCATURE — VISIBLE, PAS DEVINÉ.
   Un texte écrêté sans signal se lit comme un texte complet : c'est ce qui
   faisait croire que la mention de responsabilité s'arrêtait à « ni a ». Ce
   marqueur dit qu'il y a une suite et où appuyer. `nowrap` pour qu'il ne se
   coupe pas lui-même — un signal de troncature tronqué serait cocasse. */
.bandeau-bas-suite {
  white-space: nowrap;
  margin-left: 6px;
  padding: 1px 7px;
  border: 1px solid var(--ambre);
  border-radius: 8px;   /* PAS 999px : voir l'audit des rayons en tête de feuille */
  color: var(--ambre);
  font-weight: 700;
}
#bandeau-bas.deplie .bandeau-bas-suite { display: none; }
/* Déplié : le texte ENTIER. `display: block` défait le -webkit-box, donc aussi
   le line-clamp — sans ce retour au flux normal, le clamp restait actif et
   l'appui ne révélait rien de plus. */
#bandeau-bas.deplie {
  display: block;
  -webkit-line-clamp: none;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

/* Bascule rapide lutte engagée (en-tête du panneau) */
.lutte-rapide {
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px solid #3a4150;
  border-radius: 10px;
  background: #1c2029;
}
.lutte-switch {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
}
.lutte-switch input { accent-color: #ff453a; width: 17px; height: 17px; }
.lutte-switch small { color: #8b93a1; font-weight: 400; }
.lutte-rapide select {
  margin-top: 6px;
  width: 100%;
  padding: 5px 8px;
  border-radius: 8px;
  border: 1px solid #3a4150;
  background: #14161b;
  color: #c8cdd6;
  font-size: 0.78rem;
}
/* Bouton lutte par foyer */
.btn-lutte {
  /* 46 px, pas 44 : la cible du depot. Mesure du 06/08 a 390 px de large, le
     bouton faisait 45 x 46 — un pixel manquait EN LARGEUR. */
  min-width: 46px;
  min-height: 46px;
  border-radius: 8px;
  border: 1px solid #3a4150;
  background: #2a2f39;
  cursor: pointer;
  font-size: 15px;
  opacity: 0.55;
}
/* ⚠ SPECIFICITE : `#liste-foyers .foyer-actions button` (1,1,1) ecrasait
   `.btn-lutte.actif` (0,2,0), et l'etat le plus important de ce bouton — celui
   qui REDUIT l'enveloppe chez tous les postes — ne se voyait plus. On monte
   donc a (1,2,1) pour reprendre la main, et on epaissit le trait : 2 px de
   bordure rouge pleine plutot qu'un halo de 1 px a 1,41:1. */
#liste-foyers .foyer-actions button.btn-lutte.actif,
.btn-lutte.actif {
  opacity: 1;
  border: 2px solid #ff453a;
  background: rgba(255, 69, 58, 0.28);
  box-shadow: 0 0 0 2px rgba(255, 69, 58, 0.45);
}
#liste-foyers .foyer-actions button.btn-lutte.non-partagee,
.btn-lutte.non-partagee {
  border: 2px dashed #ffd60a;
  background: rgba(255, 214, 10, 0.20);
}
/* Bascule que le SERVEUR n'a pas acceptee : ce poste voit une propagation que
   les autres ne voient pas. Le bouton porte deja un SIGNE (le glyphe passe a
   « 🚒⚠ ») et son titre le dit ; la bordure jaune ne fait que confirmer. */
.btn-lutte.non-partagee {
  opacity: 1;
  border-color: #ffd60a;
  border-style: dashed;
  background: rgba(255, 214, 10, 0.14);
  box-shadow: none;
}

/* Liste des aéronefs captés (panneau Couches) */
#liste-aeriens { margin: 4px 0 8px 26px; }
.aero-ligne {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  border-radius: 8px;
  margin-bottom: 3px;
  background: #1c2029;
  border: 1px solid #2a2f39;
}
.aero-mini { font-size: 16px; }
.aero-texte {
  flex: 1;
  min-width: 0;
  font-size: 0.74rem;
  font-weight: 600;
  color: #c8cdd6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.aero-ligne.statut-vol .aero-texte { color: #ffd60a; }
.aero-ligne.statut-sol .aero-texte { color: #93c5fd; }
.aero-ligne.statut-perime .aero-texte { color: #8b93a1; font-weight: 400; }
.aero-ligne button {
  min-height: 30px;
  padding: 2px 10px;
  border-radius: 7px;
  border: 1px solid #3a4150;
  background: #2a2f39;
  color: #e6e9ef;
  font-size: 0.72rem;
  cursor: pointer;
}
.aero-vide { font-size: 0.72rem; color: #8b93a1; padding: 4px 6px; }

/* Formulaire de moyens engagés par foyer */
.btn-moyens {
  min-width: 44px;
  border-radius: 8px;
  border: 1px solid #3a4150;
  background: #2a2f39;
  color: #c8cdd6;
  cursor: pointer;
  font-size: 15px;
}
.moyens-resume {
  margin: 4px 0 2px;
  padding: 4px 9px;
  border-radius: 8px;
  font-size: 0.72rem;
  font-weight: 600;
  color: #ffd60a;
  background: rgba(255, 159, 10, 0.10);
  border: 1px solid rgba(255, 159, 10, 0.45);
}
/* LUTTE ENGAGEE — LE MOT, PAS SEULEMENT LA COULEUR.
   Mesure du 06/08 : dans la liste des foyers, l'etat « lutte engagee » ne
   portait NI mot NI signe. Les deux declarations qui devaient le porter etaient
   MORTES — `#liste-foyers .foyer-actions button` (specificite 1,1,1) ecrasait
   `.btn-lutte.actif` (0,2,0) : il ne restait qu'une opacite (0,55 -> 1) et un
   halo mesure a 1,41:1 contre le fond du bouton, tres en dessous des 3:1
   attendus d'un indicateur d'etat non textuel. Meme glyphe 🚒 dans les deux
   etats. Sur un telephone en plein soleil, ce n'etait pas lisible.
   Ce bandeau porte desormais le MOT (« LUTTE ENGAGEE », ecrit par
   majResumesMoyens) ; la couleur ne fait que confirmer. */
.moyens-resume.en-lutte {
  color: #ffd9db;
  background: rgba(255, 69, 58, 0.18);
  border-color: var(--rouge);
  font-weight: 800;
}
.form-moyens {
  margin-top: 8px;
  padding: 10px;
  border: 1px solid #3a4150;
  border-radius: 10px;
  background: #14161b;
}
.fm-grille {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.fm-grille label, .fm-ligne {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 0.72rem;
  color: #aab2bf;
}
.fm-ligne { flex-direction: row; align-items: center; gap: 8px; margin-top: 8px; }
.form-moyens input[type=number], .form-moyens select {
  padding: 6px 8px;
  border-radius: 7px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #e6e9ef;
  font-size: 0.82rem;
}
.fm-suggestion {
  margin: 10px 0 8px;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 0.74rem;
  font-weight: 600;
  color: #ffd60a;
  background: rgba(255, 159, 10, 0.10);
  border: 1px dashed #ff9f0a;
}
.form-moyens .fm-save { width: 100%; min-height: 40px; }

/* Panneau À propos (mentions légales et licences) */
/* Rangée de commandes du coin bas-droite : « ? Légende » et « i » côte à côte
   AU-DESSUS du zoom MapLibre, lui-même relevé au-dessus du bandeau bas.
   Un seul point de vérité pour ces hauteurs — voir aussi #btn-legende. */
#btn-apropos {
  position: fixed;
  right: calc(10px + var(--marge-droite, 0px));
  bottom: var(--bas-commandes, 54px);
  z-index: 34;
  /* 40 × 34 mesuré : sous le seuil de 44 px, et c'est le bouton qui ouvre les
     mentions légales et l'avertissement bêta — celui qu'on cherche justement
     quand on doute de ce qu'on lit. Aligné sur « ? Légende », son voisin. */
  min-width: 44px;
  min-height: 44px;
  border-radius: 10px;
  border: 1px solid var(--bord);
  background: var(--panneau);
  color: var(--texte);
  cursor: pointer;
  font-size: 15px;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.45);
}
#panneau-apropos {
  position: fixed;
  right: 10px;
  bottom: calc(var(--bas-commandes, 54px) + 118px);
  z-index: 36;
  width: min(92vw, 430px);
  max-height: min(72vh, 640px);
  max-height: min(72dvh, 640px);   /* iOS : cf. #toasts */
  overflow-y: auto;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 12px 16px 14px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.6);
  font-size: 0.8rem;
}
#panneau-apropos h3 { margin: 12px 0 6px; font-size: 0.85rem; color: #ffd60a; }
#btn-apropos-fermer {
  min-width: 34px;
  min-height: 30px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}
.apropos-sources { margin: 4px 0; padding-left: 18px; }
.apropos-sources li { margin-bottom: 6px; line-height: 1.35; }

/* --- État de santé des sources (panneau « À propos ») ---------------------
   Volontairement SOBRE et textuel : cet écran se lit, il ne clignote pas.
   Le seul code couleur employé est celui du reste de l'outil (rouge = panne,
   ambre = retard/inconnu, vert = à jour) — un opérateur doit pouvoir balayer
   la liste et s'arrêter sur ce qui ne va pas. Les lignes sont triées côté
   serveur : ce qui est en défaut arrive en tête. */
.sante-resume {
  margin: 4px 0 8px;
  padding: 6px 8px;
  border-left: 3px solid var(--bord);
  border-radius: 4px;
  line-height: 1.35;
  background: rgba(255, 255, 255, 0.03);
}
.sante-resume.sante-ok { border-left-color: #4caf7d; }
.sante-resume.sante-degrade,
.sante-resume.sante-inconnu { border-left-color: #d8a03a; }
.sante-resume.sante-panne { border-left-color: #d9534f; color: #ffb3b0; }

.sante-pastille {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 9px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.03em;
  vertical-align: middle;
}
.sante-pastille.sante-ok { background: #1f4a37; color: #8fe0b6; }
.sante-pastille.sante-degrade,
.sante-pastille.sante-inconnu { background: #4a3c17; color: #f0cd82; }
.sante-pastille.sante-panne { background: #4d1f1e; color: #ffb3b0; }

.sante-liste { margin: 0 0 6px; }
.sante-ligne {
  padding: 4px 6px;
  margin-bottom: 3px;
  border-left: 2px solid transparent;
  border-radius: 3px;
  font-size: 0.76rem;
  line-height: 1.35;
}
.sante-ligne.sante-ok { color: var(--texte-2); }
.sante-ligne.sante-retard,
.sante-ligne.sante-inconnu {
  border-left-color: #d8a03a;
  background: rgba(216, 160, 58, 0.07);
}
.sante-ligne.sante-panne {
  border-left-color: #d9534f;
  background: rgba(217, 83, 79, 0.09);
  color: #ffc9c7;
}
/* Une source ESSENTIELLE en défaut fausse la carte opérationnelle : elle doit
   se distinguer d'une couche d'appoint absente. */
.sante-ligne.sante-critique.sante-panne b,
.sante-ligne.sante-critique.sante-retard b { text-decoration: underline; }

/* Couche dont la CHAÎNE est en panne — à ne pas confondre avec « couche-vide »,
   qui signale un résultat nul RÉELLEMENT observé. La case reste visible et
   utilisable : une chaîne morte ne doit jamais effacer sa propre commande. */
.ligne-couche.couche-panne { color: #ffb3b0; }
.ligne-couche.couche-panne::after {
  content: '— SOURCE EN PANNE, couche vide';
  font-size: 11px;
  font-weight: 700;
  color: #d9534f;
}
/* Bandeau d'échec dans la liste des aéronefs : « aucun appareil capté » et
   « aucune source joignable » ne doivent pas se ressembler. */
.aero-vide.aero-panne {
  color: #ffc9c7;
  background: rgba(217, 83, 79, 0.09);
  border-left: 2px solid #d9534f;
  border-radius: 3px;
  font-weight: 600;
}

/* Indicateur de calcul en cours, centré sur la carte */
#calc-carte {
  position: fixed;
  /* `top: 84px` figé recouvrait l'en-tête dès qu'il gagnait une ligne ; la
     règle de bas de feuille le recale sur --haut-pile (mesurée). */
  top: calc(var(--haut-pile, 120px) + 10px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 55;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: calc(100vw - 24px);
  box-sizing: border-box;
  padding: 10px 18px;
  /* PAS 999px : le libellé (« Calcul de la propagation… », zone comprise) passe
     à la ligne sur écran étroit et le rayon en ferait une ellipse. */
  border-radius: 14px;
  background: rgba(20, 22, 27, 0.94);
  border: 1px solid #ff9f0a;
  color: #ffd60a;
  font-weight: 700;
  font-size: 0.86rem;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.5);
}
.calc-spinner {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 3px solid rgba(255, 159, 10, 0.25);
  border-top-color: #ff9f0a;
  animation: calc-rotation 0.9s linear infinite;
}
@keyframes calc-rotation { to { transform: rotate(360deg); } }

/* ==========================================================================
   AUTHENTIFICATION : écran de connexion, changement de mot de passe,
   ligne utilisateur, panneau d'administration, masquages par rôle
   ========================================================================== */

/* ---------- Écrans plein page (connexion / changement de mdp) ---------- */
#ecran-connexion, #ecran-mdp, #ecran-inscription {
  position: fixed;
  inset: 0;
  z-index: 200; /* au-dessus de tout : l'app n'est utilisable qu'en session */
  background:
    radial-gradient(1200px 600px at 50% -10%, rgba(245, 165, 36, 0.10), transparent 60%),
    #101218;
  display: flex;
  align-items: center;
  justify-content: center;
  /* ZONES SÛRES — c'est LE PREMIER écran, celui qui décide de la première
     impression et surtout de l'accès. MESURÉ en paysage 812 × 375 : le
     formulaire mesure 450 px pour 375 px visibles, il défile donc
     (`overflow-y: auto`, correct) — mais sans marge basse, le dernier bouton
     (« Créer un compte ») s'arrêtait pile sur l'indicateur d'accueil, qui capte
     le geste avant la page : on pouvait voir le bouton sans pouvoir l'atteindre.
     Les quatre côtés sont traités : en paysage l'encoche est latérale. */
  padding: calc(18px + var(--marge-haute, 0px)) calc(18px + var(--marge-droite, 0px))
           calc(18px + var(--marge-basse, 0px)) calc(18px + var(--marge-gauche, 0px));
  overflow-y: auto;
  /* `-webkit-overflow-scrolling: touch` : sans lui, un conteneur fixe qui défile
     s'arrête net sur iOS au lieu de suivre l'inertie du doigt — on croit que la
     page est bloquée. */
  -webkit-overflow-scrolling: touch;
}
.connexion-boite {
  width: min(94vw, 380px);
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: 14px;
  padding: 28px 26px 22px;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.65);
  display: flex;
  flex-direction: column;
}
.connexion-logo {
  width: min(70%, 230px);
  margin: 0 auto 6px;
  display: block;
}
.connexion-titre { margin: 4px 0 2px; font-size: 17px; text-align: center; }
.connexion-sous-titre {
  margin: 0 0 16px;
  text-align: center;
  color: var(--texte-2);
  font-size: 13px;
}
.connexion-boite label {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--texte-2);
  margin: 10px 0 4px;
}
.connexion-boite input {
  min-height: 46px;
  padding: 8px 12px;
  font-size: 15px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}
.connexion-boite input:focus { border-color: var(--jaune); outline: none; }
.connexion-boite button[type="submit"] {
  margin-top: 18px;
  min-height: 48px;
  font-size: 15px;
  font-weight: 800;
  background: var(--jaune);
  color: #14161b;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}
.connexion-boite button[type="submit"]:hover { filter: brightness(1.08); }
.connexion-boite button[type="submit"]:disabled { opacity: 0.6; cursor: wait; }
.connexion-erreur {
  margin-top: 12px;
  padding: 10px 12px;
  background: #55131a;
  border: 1.5px solid var(--rouge);
  border-radius: 8px;
  color: #ffd9db;
  font-weight: 600;
  font-size: 13.5px;
}
.connexion-note {
  margin: 14px 0 0;
  color: var(--texte-2);
  font-size: 12px;
  text-align: center;
  line-height: 1.4;
}

/* ---------- Ligne utilisateur dans l'en-tête du panneau ---------- */
#ligne-utilisateur {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  margin: 8px 0 2px;
  padding: 6px 8px;
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: 8px;
}
#utilisateur-info { font-size: 13px; font-weight: 700; overflow-wrap: anywhere; }
/* flex-wrap (lot H constat 4) : MESURÉ à 375 × 812, panneau ouvert (323 px
   utiles) — la rangée admin compte cinq boutons et s'étirait jusqu'à x = 454 :
   « Déconnexion » était ENTIÈREMENT hors écran. Sans retour à la ligne, tout
   bouton ajouté ici repousse le dernier hors du panneau. */
.utilisateur-actions { display: flex; gap: 6px; flex-wrap: wrap; }
#btn-deconnexion, #btn-admin, #btn-profil, #btn-mdp {
  min-height: 34px;
  padding: 4px 10px;
  font-size: 12.5px;
  font-weight: 700;
  border-radius: 7px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}
#btn-admin { border-color: #7a6410; color: var(--jaune); }
#btn-deconnexion:hover, #btn-admin:hover { filter: brightness(1.15); }

/* ---------- Panneau d'administration (même style que la légende) ---------- */
/* Écran ARCHIVES DES FEUX — rendu Stitch (cartes sombres, badge d'ampleur,
   vignettes = vrais contours EFFIS). 1er écran du chantier « rendu ». */
#panneau-archives {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 36;
  width: min(96vw, 920px);
  max-height: min(86vh, 780px);
  max-height: min(86dvh, 780px);
  overflow-y: auto;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 14px 18px 16px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.65);
}
#panneau-archives h2 { display: flex; align-items: baseline; gap: 8px; }
.arch-zone { font-size: 12.5px; font-weight: 400; color: var(--texte-2); }
.arch-filtres {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 10px 0 14px;
}
.arch-filtres input[type="search"] {
  flex: 1 1 180px;
  min-width: 140px;
  padding: 7px 11px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  font-size: 13px;
}
.arch-filtres select {
  padding: 7px 9px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  font-size: 13px;
}
.arch-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
  gap: 13px;
}
.arch-carte {
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: 13px;
  overflow: hidden;
  transition: border-color 0.15s;
}
.arch-carte:hover { border-color: var(--accent); }
.arch-vignette {
  position: relative;
  height: 118px;
  background:
    radial-gradient(circle at 30% 30%, rgba(229, 72, 77, 0.06), transparent 60%),
    #0b0d11;
}
.arch-vignette svg { width: 100%; height: 100%; display: block; }
/* Vraie carte sous le contour : mosaïque de tuiles figée 236×118, centrée. */
.arch-fond-carte {
  position: absolute;
  left: 50%;
  top: 0;
  width: 236px;
  height: 118px;
  margin-left: -118px;
  overflow: hidden;
}
.arch-fond-carte img {
  position: absolute;
  width: 256px;
  height: 256px;
  max-width: none;
  filter: saturate(0.75) brightness(0.9);
  user-select: none;
  pointer-events: none;
}
.arch-fond-carte svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.arch-badge {
  position: absolute;
  top: 8px;
  left: 8px;
  padding: 2px 9px;
  border-radius: 999px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
/* Fond OPAQUE : la pastille vit SUR la vignette IGN claire — un fond alpha
   pense pour le panneau sombre y devenait un filigrane (retour 26/08). */
.arch-badge-majeur { background: rgba(16, 19, 25, 0.9); color: #ff8589; border: 1px solid #e5484d; }
.arch-badge-important { background: rgba(16, 19, 25, 0.9); color: #f5a524; border: 1px solid #f5a524; }
.arch-badge-localise { background: rgba(16, 19, 25, 0.9); color: #d7dbe2; border: 1px solid #97a0af; }
.arch-corps { padding: 10px 13px 12px; }
.arch-titre { margin: 0; font-size: 14.5px; font-weight: 700; }
.arch-sous { margin: 2px 0 10px; font-size: 12px; color: var(--texte-2); }
.arch-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.arch-stat {
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: 9px;
  padding: 6px 9px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.arch-stat-l {
  font-family: var(--mono);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--texte-2);
}
.arch-stat-v {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.arch-ems { margin: 9px 0 0; font-size: 11px; color: #30d158; }
.arch-vide { grid-column: 1 / -1; color: var(--texte-2); font-size: 13px; }
.arch-plus {
  grid-column: 1 / -1;
  padding: 13px;
  border: 1px dashed var(--bord);
  border-radius: 13px;
  background: transparent;
  color: var(--accent);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.arch-plus:hover { border-color: var(--accent); }
.arch-note {
  margin: 13px 0 0;
  font-size: 11px;
  color: var(--texte-2);
  line-height: 1.5;
}

/* Écran ANALYSES — même famille visuelle que les archives. */
#panneau-analyses {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 36;
  width: min(96vw, 860px);
  max-height: min(86vh, 740px);
  max-height: min(86dvh, 740px);
  overflow-y: auto;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 14px 18px 16px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.65);
}
.ana-tuiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
  margin: 12px 0 16px;
}
.ana-tuile {
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: 12px;
  padding: 10px 13px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ana-tuile-v {
  font-family: var(--mono);
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.ana-tuile-d { font-size: 11px; color: var(--texte-2); }
.ana-graphes { display: flex; flex-direction: column; gap: 16px; }
.ana-graphe {
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: 12px;
  padding: 12px 14px 8px;
  overflow-x: auto;
}
.ana-titre {
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--texte-2);
}
.ana-graphe svg { width: 100%; height: auto; max-height: 190px; }
.ana-val {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  fill: var(--texte);
  text-anchor: middle;
}
.ana-axe {
  font-family: var(--mono);
  font-size: 10px;
  fill: var(--texte-2);
  text-anchor: middle;
}
.ana-unite { text-anchor: end; }

#panneau-admin {
  /* Habillage du 25/08/2026 (choix de Jérôme) : même famille visuelle que
     les écrans archives/analyses — centré, large, cartes en grille. Les
     ACTIONS et le JS sont inchangés : habillage pur. */
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 36;
  width: min(96vw, 780px);
  max-height: min(86vh, 740px);
  max-height: min(86dvh, 740px);   /* iOS : cf. #toasts */
  overflow-y: auto;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 12px 16px 14px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.6);
}
#panneau-admin h3 {
  margin: 12px 0 6px;
  font-size: 13px;
  color: var(--ambre);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
#btn-admin-fermer {
  min-width: 44px; min-height: 44px;
  border-radius: 8px; border: 1px solid var(--bord);
  background: #2a2f39; color: var(--texte);
  font-size: 16px; cursor: pointer;
}
.admin-form { display: flex; flex-wrap: wrap; gap: 8px; }
.admin-form input, .admin-form select {
  flex: 1 1 130px;
  min-height: 42px;
  padding: 6px 10px;
  font-size: 13.5px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}
.admin-form button {
  flex: 1 1 100%;
  min-height: 44px;
  font-weight: 800;
  font-size: 14px;
  background: var(--jaune);
  color: #14161b;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}
.admin-form button:disabled { opacity: 0.6; cursor: wait; }
.mdp-genere {
  margin-top: 10px;
  padding: 10px 12px;
  background: #10241a;
  border: 1.5px solid #2ecc71;
  border-radius: 8px;
  color: #c9f3dc;
  font-size: 13px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.mdp-genere code {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: #fff;
  background: rgba(0, 0, 0, 0.35);
  padding: 2px 7px;
  border-radius: 6px;
}
.mdp-genere .btn-copier {
  margin-left: 8px;
  min-height: 32px;
  padding: 3px 10px;
  font-size: 12.5px;
  font-weight: 700;
  border-radius: 7px;
  border: 1px solid #2ecc71;
  background: transparent;
  color: #c9f3dc;
  cursor: pointer;
}
#liste-comptes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 10px;
}
#liste-comptes .compte {
  border: 1px solid var(--bord);
  border-radius: 13px;
  background: var(--bloc);
  padding: 10px 13px;
  margin-bottom: 0;
  transition: border-color 0.15s;
}
#liste-comptes .compte:hover { border-color: var(--accent); }
#liste-comptes .compte.inactif { opacity: 0.62; border-style: dashed; }
#liste-comptes .compte-titre { font-weight: 700; font-size: 14px; }
/* 999px LÉGITIME par (b) : nowrap ajouté (rôle venu du serveur). */
#liste-comptes .compte-titre .tag-role {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.03em;
  background: #2a2f39;
  border: 1px solid var(--bord);
  white-space: nowrap;
}
#liste-comptes .tag-role.role-tag-superadmin { background: #55131a; border-color: var(--rouge); color: #ffd9db; }
#liste-comptes .tag-role.role-tag-admin { background: #4a3a10; border-color: var(--ambre); color: #ffe3ae; }
#liste-comptes .tag-role.role-tag-operateur { background: #15303f; border-color: #3b82f6; color: #cfe5ff; }
#liste-comptes .compte-detail {
  color: var(--texte-2);
  font-family: var(--mono);
  font-size: 11.5px;
  margin: 3px 0 8px;
  line-height: 1.5;
}
#liste-comptes .compte-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
#liste-comptes .compte-actions button, #liste-comptes .compte-actions select {
  min-height: 36px;
  padding: 4px 10px;
  font-size: 12.5px;
  border-radius: 7px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}
#liste-comptes .compte-actions button:hover { filter: brightness(1.15); }
#liste-comptes .compte-actions .danger { border-color: var(--rouge); color: #ff8f92; }

/* ---------- Masquage par RÔLE (le droit reste appliqué côté serveur) ----------
   lecteur   : ni marquage, ni moyens, ni actualisation, ni construction 🔨 ;
   operateur : tout sauf construction 🔨 ;
   admin     : tout sauf ⚙ Administration (géré en JS) ; superadmin : tout. */
/* NB : le bandeau #maj-dispo N'EST PLUS masqué au lecteur — la veille unifiée
   ne propose que du rechargement de couches observées et un recalcul de
   prévision (POST /api/simulate), l'un et l'autre ouverts au rôle lecteur. */
body.role-lecteur #modes-marquage,
body.role-lecteur #bloc-marquage .indice,
body.role-lecteur #btn-refresh,
body.role-lecteur .btn-moyens,
body.role-lecteur #liste-objets .supprimer,
body.role-lecteur .btn-construire,
body.role-operateur .btn-construire {
  display: none !important;
}

/* ---------- PRÉSENCE TERRAIN : intervention GPS, ligne de feu, intervenants ----- */

/* Barre d'intervention : bien visible, utilisable au doigt (gros boutons) */
/* Barre d'actions : UNE SEULE LIGNE défilante horizontalement.
   Empilés verticalement, ces boutons (intervention, SOS, évacuation, eau) et
   leurs bandeaux recouvraient la carte entière — la lecture opérationnelle
   passe avant le confort de lecture d'un libellé. */
#barre-intervention {
  position: fixed;
  /* 64 px = la rangée du bouton ☰, dont la barre est le voisin de droite sur
     ordinateur. --marge-haute la fait descendre avec lui (encoche). Sur
     téléphone cette valeur est écrasée par `bottom: 158px` (voir plus bas). */
  top: calc(64px + var(--marge-haute, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 26;
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  max-width: min(96vw, 900px);
  overflow-x: auto;
  overflow-y: hidden;
  padding: 2px 4px;
  scrollbar-width: none;          /* la barre défile au doigt, sans ascenseur */
  pointer-events: none; /* la barre laisse passer les gestes carte entre les boutons */
}
#barre-intervention::-webkit-scrollbar { display: none; }
/* SUR ORDINATEUR, le bandeau « données réelles » (#entete) occupe le coin haut
   DROIT sur jusqu'à 46vw : une barre centrée passait dessous et son dernier
   bouton était coupé. On l'ancre donc à GAUCHE du bandeau plutôt qu'au centre
   de l'écran — le bouton du panneau occupe les 60 premiers pixels. */
@media (min-width: 901px) {
  #barre-intervention {
    /* Panneau de paramètres OUVERT (348 px + 10 px de marge) : la barre commence
       après lui, sinon il recouvrait SOS, évacuation et entraide. La règle
       body.panneau-ferme plus bas la ramène à gauche quand le tiroir est replié. */
    left: 372px;
    transform: none;
    justify-content: flex-start;
    /* --largeur-entete est mesurée en JS (calerBarreActions) ; le repli
       min(46vw, 520px) couvre le cas où le script n'a pas encore tourné. */
    max-width: calc(100vw - 372px - var(--largeur-entete, min(46vw, 520px)) - 24px);
  }
  /* Tiroir replié : la barre récupère toute la largeur, à droite du bouton ☰ */
  body.panneau-ferme #barre-intervention {
    left: 64px;
    max-width: calc(100vw - 64px - var(--largeur-entete, min(46vw, 520px)) - 24px);
  }
}
/* Boutons compacts : hauteur tactile conservée, libellé ENTIER.
   `white-space: nowrap` + `max-width: 46vw` + ellipsis donnaient, sur la
   capture Android du 29/07 (412 px de large, donc 46vw ≈ 189 px), des
   commandes illisibles : « 🛰 Se déclarer en intervent… » et
   « SOS — je suis en diffic… ». Un bouton d'urgence dont on ne lit pas le
   libellé ne sert à rien. La barre passe à la ligne aux DEUX paliers
   (flex-wrap: wrap en ≤ 900 px comme en ≥ 901 px) : un bouton large descend
   d'une rangée au lieu de couper son texte. Deux lignes au plus, hauteur
   tactile inchangée. */
/* CIBLES TACTILES — 44 px MINIMUM, ET C'EST UN PLANCHER, PAS UN OBJECTIF.
   MESURÉ à 375 × 812 : « 🛰 Se déclarer en intervention » et « 🆘 SOS — je suis
   en difficulté » faisaient 229 × 38 et 237 × 38. 38 px, c'est sous le seuil
   d'accessibilité (44 px) — et Jérémy manœuvre AVEC DES GANTS DE FEU, dont
   l'épaisseur ajoute plusieurs millimètres d'imprécision au point de contact.
   Les deux commandes vitales de l'outil ne peuvent pas être les plus difficiles
   à toucher. Le coût est de 6 px par rangée : sur cinq rangées (compte
   opérateur), la barre monte de 30 px. Elle est ancrée EN BAS et grandit vers le
   haut, donc rien n'est poussé hors de l'écran, et --bas-actions (mesurée par
   messages.js) fait remonter la colonne de toasts d'autant. */
#barre-intervention > button {
  flex: 0 0 auto;
  min-height: 44px !important;
  padding: 6px 12px !important;
  font-size: 12.5px !important;
  white-space: normal;
  overflow-wrap: break-word;
  line-height: 1.15;
  text-align: center;
  max-width: min(92vw, 420px);
}
/* Bandeaux et sous-barres embarqués : une ligne, jamais plus */
#barre-intervention > div {
  flex: 0 0 auto;
  display: flex;
  flex-direction: row;      /* #barre-actions-terrain empilait ses boutons */
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  max-width: 60vw;
  max-height: 40px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#barre-intervention > div > button {
  flex: 0 0 auto;
  min-height: 34px !important;
  padding: 5px 10px !important;
  font-size: 12px !important;
  white-space: nowrap;
  max-width: 40vw;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* LES COMMANDES DE SECTEUR NE SE TRONQUENT PAS.
   La règle générale ci-dessus (`> div` : une ligne, ellipsis) a été écrite pour
   les BANDEAUX embarqués — #bandeau-sos, qui doit tenir sur une ligne. Elle
   s'applique aussi, par ricochet, à #barre-actions-terrain, qui n'est pas un
   bandeau mais un GROUPE DE COMMANDES. Mesuré sur 375 px : `max-width: 40vw`
   = 150 px, et « 🚨 Ordre d'évacuation » à 12 px demande ~168 px — le libellé
   se coupait en « 🚨 Ordre d'évacuati… ». Un ordre d'évacuation qu'on ne lit
   pas ne s'engage pas. Ce groupe hérite donc du même traitement que la barre
   qui le contient : il passe à la ligne au lieu de couper.
   Sélecteur à DEUX identifiants : `#barre-intervention > div` compte un
   identifiant ET un type, il l'emporterait sur `#barre-actions-terrain` seul. */
#barre-intervention > #barre-actions-terrain {
  flex-wrap: wrap;
  row-gap: 5px;
  max-width: min(92vw, 640px);
  max-height: none;
  overflow: visible;
  white-space: normal;
}
#barre-intervention > #barre-actions-terrain > button {
  /* 44 px comme les boutons de premier rang : « 🚨 Ordre d'évacuation »,
     « 💧 Demander de l'eau » et « 🧭 Itinéraire » sont des COMMANDES, pas des
     badges d'état — elles héritaient du 34 px des bandeaux embarqués
     (#barre-intervention > div > button), qui vaut pour « Annuler (fausse
     alerte) » dans un bandeau borné à 40 px de haut, pas pour un ordre
     d'évacuation qu'on engage avec des gants. */
  min-height: 44px !important;
  white-space: normal;
  overflow-wrap: break-word;
  overflow: visible;
  text-overflow: clip;
  line-height: 1.15;
  text-align: center;
  max-width: min(92vw, 420px);
}
#barre-intervention > * { pointer-events: auto; }
/* RAYON FIXE, PAS 999px : ces commandes portent un libellé long qui passe
   maintenant à la ligne — un rayon de 999px sur une boîte de deux lignes ne
   donne pas une pilule mais une ELLIPSE (le « disque orange » de la capture). */
#btn-intervention {
  min-height: 48px;
  padding: 10px 18px;
  border-radius: 16px;
  border: 1.5px solid #2ecc71;
  background: rgba(16, 36, 26, 0.94);
  color: #c9f3dc;
  font-size: 14.5px;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
}
#btn-intervention:hover { filter: brightness(1.12); }
#btn-intervention.actif {
  border-color: var(--rouge);
  background: #55131a;
  color: #ffd9db;
  animation: pulser 1.6s infinite;
}
/* TROIS ÉTATS DISTINCTS À L'ŒIL (géolocalisation) :
   · en-attente      — ambre, position DEMANDÉE, rien n'est partagé ;
   · position-perdue — bleu-gris, en intervention MAIS position indisponible.
   Ni l'un ni l'autre ne doit ressembler au rouge « position partagée ». */
#btn-intervention.en-attente,
#btn-citoyen-position.en-attente {
  border-color: #ff9f0a;
  background: #3a2a06;
  color: #ffdfa8;
  animation: none;
}
#btn-intervention.actif.position-perdue,
#btn-citoyen-position.actif.position-perdue {
  border-color: #6ea8d8;
  background: #16303f;
  color: #cfe6f6;
  animation: none;
}
#btn-ligne-feu {
  min-height: 46px;
  padding: 8px 16px;
  border-radius: 16px;   /* voir #btn-intervention : libellé multi-lignes */
  border: 1.5px solid #ff9f0a;
  background: rgba(42, 30, 8, 0.94);
  color: #ffd9a8;
  font-size: 14px;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
}
#btn-ligne-feu:hover { filter: brightness(1.12); }
#btn-ligne-feu.actif {
  background: #4a2a08;
  border-color: var(--jaune);
  color: var(--jaune);
  animation: pulser 1.6s infinite;
}
/* Hors intervention (lot H constat 13) : le bouton restait MASQUÉ sans un mot —
   impossible de deviner que le relevé exige de se déclarer d'abord. Il est
   désormais visible mais éteint, et son infobulle dit le prérequis. */
#btn-ligne-feu:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  filter: none;
  animation: none;
}
#etat-trace {
  padding: 4px 12px;
  border-radius: 12px;   /* texte d'état libre : peut passer à la ligne */
  background: rgba(20, 22, 27, 0.92);
  border: 1px solid #ff9f0a;
  color: #ffd9a8;
  font-size: 12.5px;
  font-weight: 700;
}
#badge-lignes-attente {
  min-height: 34px;
  padding: 4px 12px;
  border-radius: 12px;   /* « n lignes en attente de recalcul » : multi-lignes */
  border: 1.5px solid #ff9f0a;
  background: rgba(255, 159, 10, 0.92);
  color: #14161b;
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.45);
}
@media (max-width: 900px) {
  /* Mobile : à portée de pouce, au-dessus de la timeline, du disclaimer
     et du bouton « ? Légende » (bottom 108px + ~44px de hauteur) */
  #barre-intervention {
    top: auto;
    /* 158 px au-dessus du bord ATTEIGNABLE, pas du bord physique : sans
       --marge-basse, « 🛰 Se déclarer en intervention » et « 🆘 SOS » —
       les deux commandes vitales — descendaient de 34 px sur un iPhone à
       encoche, et la rangée du bas se retrouvait à cheval sur l'indicateur
       d'accueil, qui capte le geste avant la page.
       --bas-pile est la hauteur MESURÉE de la pile basse (bandeau
       d'avertissement + barre horaire + puce d'état, cf. calerTimeline). Les
       158 px ne sont plus qu'un repli pour le premier rendu : figés, ils
       posaient la dernière rangée de la barre SUR la barre horaire dès que
       celle-ci gagnait une ligne — mesuré à 375 × 812, recouvrement de
       243 × 10 px. Une commande à cheval sur une autre se manque avec des
       gants. */
    bottom: calc(var(--bas-pile, 158px) + var(--marge-basse, 0px));
    /* … ET ELLE PASSE À LA LIGNE, comme sur ordinateur. Le correctif de
       @media (min-width: 901px) — « une commande qu'on ne voit pas ne sert à
       personne » — n'avait été appliqué qu'au bureau. Mesuré sur un écran de
       375 px avec un compte opérateur : la barre débordait de 445 px,
       « 🤝 Demandes » commençait à x = 600 et « Intervenants » à x = 744, tous
       deux hors de l'écran, atteignables au seul défilement horizontal d'une
       barre dont l'ascenseur est justement masqué (scrollbar-width: none).
       Sur un téléphone de terrain, la console d'entraide était donc
       introuvable. La barre étant ancrée en BAS ici, les rangées
       supplémentaires montent : elles ne poussent rien vers le bas de l'écran
       et --bas-securite / --bas-commandes restent respectés. */
    flex-wrap: wrap;
    row-gap: 5px;
    overflow-x: visible;
    justify-content: center;
  }
  /* LA COLONNE DE COMMANDES DU BORD DROIT EST RÉSERVÉE.
     Conséquence du `flex-wrap` ci-dessus, mesurée et non supposée : une barre
     qui passe à la ligne GRANDIT VERS LE HAUT, et sur un compte opérateur elle
     atteint cinq rangées (« Se déclarer en intervention », « SOS », « Tracer la
     ligne de feu », les trois commandes de secteur, « Intervenants »). Or
     « ? Légende » et « ℹ » sont posés sur le bord droit à la MOITIÉ de la
     hauteur d'écran, et le zoom MapLibre juste au-dessus. Mesuré à 375 × 812
     avec toutes les commandes visibles : la barre occupait y = 366 → 654 et
     « ? Légende » y = 398 → 442, x = 267 → 367 ; « 🛰 Se déclarer en
     intervention » (x = 73 → 302) et « 🆘 SOS » (x = 69 → 306) le recouvraient.
     Un bouton SOS partiellement caché par la légende n'est pas acceptable.
     On cesse donc de CENTRER la barre sur l'écran entier : elle s'arrête avant
     la colonne de droite (116 px = « ? Légende » de 100 px + 8 px de marge +
     8 px de garde) et ses enfants sont bornés en conséquence. La barre reste
     centrée, mais dans l'espace qui lui appartient réellement. */
  /* Les 116 px réservés à droite sont mesurés depuis le bord ATTEIGNABLE : en
     paysage, l'encoche latérale en prend 47 de plus. MESURÉ à 812 × 375 avec les
     zones sûres d'un iPhone : « 🛰 Se déclarer en intervention » commençait à
     x = 41 alors que la zone utile commence à 47 — ses six premiers pixels
     étaient sous l'encoche. Un bouton dont le bord est mangé se manque avec des
     gants. Le padding gauche part lui aussi du bord utile. */
  #barre-intervention {
    left: var(--marge-gauche, 0px);
    right: calc(116px + var(--marge-droite, 0px));
    transform: none;
    max-width: none;
    padding-left: 8px;
  }
  /* Les enfants ne débordent plus de la boîte : `overflow-x: visible` les
     laisserait sinon ressortir à droite, dans la colonne qu'on vient de
     réserver. 132 = 116 + 8 (padding gauche) + 8 (garde), plus les deux marges
     latérales des zones sûres. */
  #barre-intervention > *,
  #barre-intervention > #barre-actions-terrain {
    max-width: calc(100vw - 132px - var(--marge-gauche, 0px) - var(--marge-droite, 0px));
  }
  #barre-intervention > #barre-actions-terrain > button {
    max-width: calc(100vw - 148px - var(--marge-gauche, 0px) - var(--marge-droite, 0px));
  }
}
/* TÉLÉPHONE COUCHÉ — le seul cas où la HAUTEUR, et non la largeur, manque, et
   le seul endroit de cette feuille où il faut arbitrer un budget.
   Compté à 812 × 375, toutes commandes visibles pour un opérateur :
     en haut  : 10 (marge) + 93 (en-tête) + 8 + 32 (bandeau) = la bande libre
                commence à y = 143 ;
     en bas   : 49 (bandeau de responsabilité) + 12 + 74 (barre horaire)
                = 135 px, donc la bande libre s'arrête à y = 240 ;
     à placer : la barre d'actions, deux rangées de 44 px = 96 px.
   Bilan : 97 px disponibles pour 96 px à poser — une marge d'UN pixel. La barre
   remontait donc sur le bandeau (mesuré : recouvrement y = 137 → 143).
   ARBITRAGE. On ne rabote PAS les cibles tactiles : 44 px, gants compris, ce
   n'est pas négociable sur un écran de terrain. On rend au contraire les 16 px
   que coûte la SECONDE ligne du bandeau de responsabilité — qui reste lisible
   d'un appui, et dont la première ligne porte déjà « OUTIL D'AIDE À LA DÉCISION
   — VERSION BÊTA (non validée opérationnellement) ». La bande libre passe à
   113 px, la barre y tient avec 10 px au-dessus et 7 px en dessous. */
/* `-webkit-line-clamp: 1` était posé ici sur #bandeau-bas pour lui rendre sa
   seconde ligne en paysage. LA RÈGLE ÉTAIT INERTE, et c'est mesuré : `line-clamp`
   n'a d'effet que sur une boîte `display: -webkit-box` avec
   `-webkit-box-orient: vertical` et `overflow: hidden` ; #bandeau-bas est en
   `flow-root`. Mesuré à 812 × 375 le 30/07/2026 : le bandeau bas faisait toujours
   34 px sur DEUX lignes, --bas-pile valait 152 px et non 126, et la barre
   d'actions remontait à y = 127 sur un bandeau de messages occupant 109 → 141,
   soit 14 px de recouvrement — dont 13 px sur le bouton « ↻ Recalculer »
   lui-même. On ne garde donc pas une règle qui ne fait rien, et on corrige le
   budget vertical là où il se joue vraiment : la hauteur de la barre d'actions. */
/* ÉCRAN COURT, TOUTE LARGEUR CONFONDUE : la barre d'actions tient sur UNE rangée.
   La condition porte sur la seule HAUTEUR, volontairement. Une tablette Android en
   paysage fait 915 px de large : elle passe par la branche « ordinateur »
   (min-width: 901px), où la barre est ancrée EN HAUT, et le garde-fou réservé au
   téléphone ne la protégeait pas. Mesuré à 915 × 412 le 30/07/2026 : la barre
   d'actions croisait la barre horaire sur 520 × 29 px. Même arbitrage que
   ci-dessous — une rangée qui défile, cibles de 44 px intactes. */
@media (max-height: 520px) {
  #barre-intervention,
  #barre-intervention > #barre-actions-terrain { flex-wrap: nowrap; }
}
@media (max-width: 900px) and (max-height: 520px) {
  #barre-intervention {
    /* La hauteur MESURÉE de la pile basse (--bas-pile) l'emporte sur la
       constante, qui ne reste que le repli du premier rendu. */
    bottom: calc(var(--bas-pile, 126px) + var(--marge-basse, 0px));
    row-gap: 4px;
    /* PAYSAGE COURT SUR TÉLÉPHONE : UNE SEULE RANGÉE, QUI DÉFILE.
       Compte du budget vertical à 812 × 375 : la pile haute (en-tête + bandeau
       de messages) finit à 149 px, la pile basse en réclame 152, il reste donc
       74 px — et la barre repliée sur deux rangées en demande 96. Elle débordait
       vers le haut, faute de place, et recouvrait le bandeau.
       ARBITRAGE, le même qu'en portrait : on ne rabote PAS les 44 px de cible
       tactile (Jérémy manœuvre avec des gants de feu). On revient au parti pris
       de la règle de base — « UNE SEULE LIGNE défilante horizontalement » — qui
       ramène la barre à ~48 px : elle tient dans les 74 px avec de la marge, et
       ce choix reste vrai si la pile basse regrossit (c'est la troisième fois que
       ce budget casse par le bas). Aucune commande n'est perdue : elles défilent
       au doigt, `overflow-x: auto` étant déjà posé sur la barre. */
    flex-wrap: nowrap;
  }
  #barre-intervention > #barre-actions-terrain {
    row-gap: 4px;
    flex-wrap: nowrap;
  }
}

/* Marqueurs des intervenants : pastille émoji, BORDURE = fraîcheur de la position.
   La pastille occupe SEULE le point GPS (38 × 38, ancrée au centre par MapLibre) et
   les étiquettes sont posées À DROITE : elles ne peuvent plus se superposer à celles
   d'un autre marqueur situé au même endroit (marqueur de trace historique…). */
.intervenant {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  cursor: pointer;
}
.intervenant-etiq {
  position: absolute;
  left: calc(100% + 5px);
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  pointer-events: none; /* le clic reste capté par la pastille */
}
.intervenant-pastille {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--bloc);
  border: 3px solid #2ecc71;
  font-size: 19px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
}
.intervenant.frais-direct .intervenant-pastille {
  border-color: #2ecc71;
  animation: pulse-direct 1.8s infinite;
}
@keyframes pulse-direct {
  0%, 100% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.55); }
  50% { box-shadow: 0 0 0 10px rgba(46, 204, 113, 0); }
}
.intervenant.frais-recent .intervenant-pastille { border-color: var(--ambre); }
.intervenant.frais-perime .intervenant-pastille { border-color: #8b93a1; opacity: 0.8; }
.intervenant-nom {
  font-size: 10.5px;
  font-weight: 800;
  color: #fff;
  text-shadow: 0 0 3px #14161b, 0 0 3px #14161b, 0 0 4px #14161b;
  white-space: nowrap;
}
.intervenant-age {
  padding: 1px 7px;
  border-radius: 999px;
  background: #55131a;
  border: 1px solid var(--rouge);
  color: #ffd9db;
  font-size: 10px;
  font-weight: 800;
  white-space: nowrap;
}

/* Chips de catégories + liste des intervenants (même famille que les aéronefs) */
#chips-intervenants {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 2px 0 6px 26px;
}
#liste-intervenants { margin: 4px 0 8px 26px; }
.interv-ligne {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  border-radius: 8px;
  margin-bottom: 3px;
  background: #1c2029;
  border: 1px solid #2a2f39;
}
.interv-emoji { font-size: 16px; }
.interv-texte {
  flex: 1;
  min-width: 0;
  font-size: 0.74rem;
  font-weight: 600;
  color: #c8cdd6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.interv-ligne.frais-direct .interv-texte { color: #7ee2a8; }
.interv-ligne.frais-recent .interv-texte { color: #ffd9a8; }
.interv-ligne.frais-perime .interv-texte { color: #ff8f92; }
.interv-ligne button {
  min-height: 34px;
  padding: 2px 10px;
  font-size: 12px;
  border-radius: 7px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}

/* Fiche intervenant (popup carte) */
.fiche-intervenant .fi-frais { font-weight: 800; font-size: 12.5px; }
.fiche-intervenant .fi-frais.frais-direct { color: #2ecc71; }
.fiche-intervenant .fi-frais.frais-recent { color: var(--ambre); }
.fiche-intervenant .fi-frais.frais-perime { color: var(--rouge); }
.fiche-intervenant .fi-liste {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  margin: 6px 0 0;
  font-size: 12.5px;
}
.fiche-intervenant .fi-liste dt { color: #7c8494; font-weight: 700; }
.fiche-intervenant .fi-liste dd { margin: 0; overflow-wrap: anywhere; }
.fiche-intervenant .fi-liste a { color: #7ee2a8; font-weight: 800; }

/* Fiche ligne de feu (popup carte) : état + journal + décisions opérateur */
.fiche-ligne-feu .fl-detail { font-size: 12.5px; }
.fiche-ligne-feu .fl-etat { font-weight: 800; font-size: 12.5px; }
.fiche-ligne-feu .fl-etat.fl-en_attente { color: #ff9f0a; }
.fiche-ligne-feu .fl-etat.fl-active { color: var(--rouge); }
.fiche-ligne-feu .fl-etat.fl-inactive { color: #aab2bf; }
.fiche-ligne-feu .fl-etat.fl-rejetee { color: #8b93a1; }
.fiche-ligne-feu .fl-journal {
  margin: 4px 0;
  padding: 4px 8px;
  background: rgba(0, 0, 0, 0.25);
  border-radius: 6px;
  font-size: 11.5px;
  color: #aab2bf;
  max-height: 90px;
  overflow-y: auto;
}
.fiche-ligne-feu .fl-journal:empty { display: none; }
.fiche-ligne-feu .fl-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 6px;
}
.fiche-ligne-feu .fl-actions select {
  min-height: 40px;
  padding: 4px 8px;
  font-size: 12.5px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}
.fiche-ligne-feu .fl-actions button {
  min-height: 44px;
  padding: 6px 10px;
  font-size: 13px;
  font-weight: 800;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
}
.fiche-ligne-feu .fl-actions button:hover { filter: brightness(1.12); }
.fiche-ligne-feu .fl-integrer { background: #55131a; border-color: var(--rouge); color: #ffd9db; }
.fiche-ligne-feu .fl-valider { background: #2a2f39; border-color: #8b93a1; }
.fiche-ligne-feu .fl-rejeter { border-color: #6b7280; color: #aab2bf; }
.fiche-ligne-feu .fl-supprimer {
  min-height: 36px;
  background: transparent;
  border-style: dashed;
  border-color: var(--rouge);
  color: #ff8f92;
  font-size: 12px;
}

/* Panneau « Mon profil » (même famille que le panneau d'administration) */
#panneau-profil {
  position: fixed;
  right: calc(10px + var(--marge-droite, 0px));
  bottom: calc(148px + var(--marge-basse, 0px));   /* zone sûre : cf. #info-bulle */
  z-index: 36;
  width: min(94vw, 440px);
  max-height: min(78vh, 660px);
  max-height: min(78dvh, 660px);   /* iOS : cf. #toasts */
  overflow-y: auto;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 12px 16px 14px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.6);
}
#btn-profil-fermer {
  min-width: 44px;
  min-height: 44px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 16px;
  cursor: pointer;
}
.profil-form label {
  display: block;
  margin: 10px 0 4px;
  font-size: 13px;
  font-weight: 700;
}
.profil-form input,
.profil-form select,
.profil-form textarea {
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  font-size: 14px;
  font-family: inherit;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}
.profil-form textarea { min-height: 64px; resize: vertical; }
.profil-form button[type="submit"] {
  width: 100%;
  min-height: 48px;
  margin-top: 14px;
  font-size: 15px;
  font-weight: 800;
  background: var(--jaune);
  color: #14161b;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}
.profil-form button[type="submit"]:disabled { opacity: 0.6; cursor: wait; }
/* FICHE ADAPTATIVE : un bloc sans objet pour la catégorie choisie est
   simplement retiré du flux (l'attribut `hidden` est posé par app.js). Le
   `!important` protège des règles d'affichage plus spécifiques ajoutées plus
   loin dans la feuille — un champ masqué ne doit JAMAIS réapparaître. */
.profil-form .pr-bloc[hidden] { display: none !important; }
.profil-form .pr-bloc { margin: 0; }
.profil-form #pr-engin-detail[hidden] { display: none !important; }
/* ALERTES CRITIQUES (audit B5, lot G2) : bloc HORS du formulaire de profil —
   l'abonnement push appartient à l'appareil, pas à la fiche partagée. Séparé
   visuellement du formulaire par un filet, boutons pleine largeur comme le
   reste du panneau (pouces gantés : mêmes 44 px minimum). */
#bloc-push {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--bord);
}
#bloc-push h3 { margin: 0 0 6px; font-size: 14px; }
#bloc-push .push-boutons { display: flex; flex-direction: column; gap: 8px; margin: 8px 0; }
#bloc-push .push-boutons button {
  width: 100%;
  min-height: 44px;
  font-size: 14px;
  font-weight: 700;
  border: 1px solid var(--bord);
  border-radius: 8px;
  background: var(--bloc);
  color: var(--texte);
  cursor: pointer;
}
#bloc-push #btn-push-activer {
  background: var(--jaune);
  color: #14161b;
  border: none;
}
/* ENGIN PARTAGÉ : la ligne d'état dit si l'on REJOINT un engin existant (et qui
   est déjà à bord) ou si l'on va en créer un — c'est ce qui évite les camions
   fantômes créés par une faute de frappe. */
.pr-engin-etat {
  margin: 6px 0 0;
  padding: 7px 9px;
  font-size: 12.5px;
  line-height: 1.35;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #1b1f27;
}
.pr-engin-etat:empty { display: none; }
.pr-engin-etat.rejoint { border-color: #2f7d4f; background: #16241c; }
.pr-engin-etat.cree { border-color: #7d6a2f; background: #242116; }
/* Équipage dispersé : la position de l'engin est incertaine, on le dit. */
.ent-cand-alerte { color: var(--jaune); font-weight: 700; }
/* Bandeau de rappel de la catégorie en cours : dit en une ligne pourquoi le
   formulaire n'affiche que ces champs-là. */
#pr-indice-cat:empty { display: none; }
@media (max-width: 900px) {
  #panneau-profil {
    left: 10px;
    right: 10px;
    width: auto;
    bottom: calc(76px + var(--marge-basse, 0px));   /* zone sûre : cf. #info-bulle */
    max-height: 76vh;
    max-height: 76dvh;   /* iOS : cf. #toasts */
  }
}

/* Modale « côté du feu » : plein écran mobile, gros boutons au doigt */
#modale-cote-feu {
  position: fixed;
  inset: 0;
  z-index: 95;
  background: rgba(8, 9, 12, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px;
}
.modale-boite {
  width: min(96vw, 460px);
  /* 94vh sur iPhone = 763 px pour 715 px visibles : la modale dépassait par le
     BAS, c'est-à-dire là où sont ses boutons « Valider » / « Annuler ». Sous les
     barres de Safari, on ne pouvait plus ni valider ni annuler — d'où
     « les affichages d'erreur ne permettent plus de retourner à la carte ». */
  max-height: 94vh;
  max-height: 94dvh;
  overflow-y: auto;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 18px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.7);
}
.modale-boite h2 { margin: 0 0 6px; font-size: 19px; }
.mc-choix {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 12px;
}
.mc-choix button {
  min-height: 56px;
  padding: 10px 14px;
  font-size: 16px;
  font-weight: 800;
  border-radius: 10px;
  cursor: pointer;
  border: 1.5px solid #ff9f0a;
  background: #4a2a08;
  color: #ffd9a8;
}
.mc-choix button:hover { filter: brightness(1.12); }
.mc-choix .mc-secondaire {
  min-height: 48px;
  background: #2a2f39;
  border-color: var(--bord);
  color: var(--texte);
  font-weight: 700;
}
.mc-choix .mc-danger {
  min-height: 42px;
  background: transparent;
  border-style: dashed;
  border-color: var(--rouge);
  color: #ff8f92;
  font-size: 13px;
  font-weight: 700;
}

/* Pastille de légende « intervenant » */
.leg-interv {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 3px solid #2ecc71;
  background: var(--bloc);
  font-size: 14px;
}

/* ---------- ALERTES intervenants : badge global, pastilles, fiche, liste ------- */

@keyframes pulse-alerte {
  0%, 100% { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.65); }
  50% { box-shadow: 0 0 0 12px rgba(229, 72, 77, 0); }
}

/* Badge global « 🚨 N alerte(s) » — même famille que le badge lignes-à-valider,
   mais visible de TOUS les rôles */
#badge-alertes {
  min-height: 40px;
  padding: 6px 14px;
  border-radius: 14px;   /* « 🚨 N alerte(s) intervenant » : peut se replier */
  border: 1.5px solid var(--rouge);
  background: rgba(229, 72, 77, 0.94);
  color: #fff;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.45);
  animation: pulse-alerte 1.6s infinite;
}
#badge-alertes:hover { filter: brightness(1.1); }

/* Pastille carte : le halo ROUGE pulsant d'alerte PRIME sur la fraîcheur
   (défini APRÈS les .frais-* — même spécificité, l'ordre tranche) */
.intervenant.en-alerte .intervenant-pastille {
  border-color: var(--rouge);
  opacity: 1;
  animation: pulse-alerte 1.4s infinite;
}
.intervenant.en-alerte .intervenant-nom { color: #ffd9db; }

/* Liste : section « 🚨 Alertes en cours » en tête + lignes rouges */
.interv-section {
  margin: 6px 0 3px;
  padding: 4px 8px;
  border-radius: 6px;
  font-size: 0.76rem;
  font-weight: 800;
  background: #55131a;
  border: 1px solid var(--rouge);
  color: #ffd9db;
}
.interv-ligne.en-alerte {
  background: #2a1013;
  border-color: var(--rouge);
}
.interv-ligne.en-alerte .interv-texte { color: #ffb3b6; }
.interv-ligne .btn-alerter {
  border-color: var(--rouge);
  background: #55131a;
  color: #ffd9db;
  font-weight: 800;
}

/* Fiche intervenant : encart alerte + boutons (gros — mobile d'abord) */
.fiche-intervenant .fi-arrete {
  margin: 6px 0 0;
  font-size: 12px;
  font-weight: 800;
  color: #ff8f92;
}
.fiche-intervenant .fi-alerte {
  margin-top: 8px;
  padding: 8px;
  border-radius: 8px;
  background: #2a1013;
  border: 1.5px solid var(--rouge);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fiche-intervenant .fi-alerte-titre {
  font-size: 13px;
  font-weight: 900;
  color: #ff8f92;
}
.fiche-intervenant .fi-alerte-detail { font-size: 12.5px; color: #ffd9db; }
.fiche-intervenant .fi-lancer,
.fiche-intervenant .fi-recherche,
.fiche-intervenant .fi-lancer-zone {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.fiche-intervenant .fi-lancer,
.fiche-intervenant .fi-recherche { margin-top: 8px; }
.fiche-intervenant .fi-lancer-zone[hidden] { display: none; }
.fi-champ {
  min-height: 42px;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}
.fi-btn {
  min-height: 46px;
  padding: 8px 12px;
  font-size: 13.5px;
  font-weight: 800;
  border-radius: 9px;
  cursor: pointer;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
}
.fi-btn:hover { filter: brightness(1.12); }
.fi-btn-alerte { background: #55131a; border-color: var(--rouge); color: #ffd9db; }
.fi-btn-lever { background: #10241a; border-color: #2ecc71; color: #c9f3dc; }

/* Marqueurs de la TRACE historique (recherche opérateur) */
.trace-depart {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: rgba(56, 189, 248, 0.35);
  border: 2px solid #38bdf8;
}
.trace-dernier {
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: default;
}
/* VIOLET, jamais rouge pulsant : le rouge pulsant reste RÉSERVÉ aux vraies
   alertes — une dernière position connue n'est pas une alerte. L'étiquette est
   posée EN DESSOUS (les pastilles live étiquettent à droite : plus de chevauchement). */
.trace-dernier-rond {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  font-size: 22px;
  background: #241b3d;
  border: 3px solid #a78bfa;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.6), 0 0 0 4px rgba(167, 139, 250, 0.18);
}
.trace-dernier-heure {
  margin-top: 2px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #241b3d;
  border: 1px solid #a78bfa;
  color: #ded1ff;
  font-size: 11px;
  font-weight: 800;
  white-space: nowrap;
}
/* Repère DISCRET : l'intervenant est visible en direct au même endroit (< 50 m),
   son étiquette live suffit — on ne double pas le texte sur la carte. */
.trace-dernier.compact .trace-dernier-rond {
  width: 24px;
  height: 24px;
  font-size: 12px;
  border-width: 2px;
}
.trace-dernier.compact .trace-dernier-heure { display: none; }

/* Panneau de la trace : période, stats, sélecteur 6/24/72 h (gros boutons) */
#panneau-trace {
  position: fixed;
  right: calc(10px + var(--marge-droite, 0px));
  bottom: calc(96px + var(--marge-basse, 0px));   /* zone sûre : cf. #info-bulle */
  z-index: 40;
  width: min(94vw, 360px);
  padding: 12px;
  border-radius: 12px;
  background: rgba(20, 22, 27, 0.97);
  border: 1.5px solid #38bdf8;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
#panneau-trace h2 { margin: 0; font-size: 15px; }
#panneau-trace .trace-info { font-size: 12.5px; color: #c8cdd6; }
#panneau-trace .trace-periodes { display: flex; gap: 6px; }
#panneau-trace .trace-periode {
  flex: 1;
  min-height: 44px;
  font-size: 13.5px;
  font-weight: 800;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}
#panneau-trace .trace-periode.actif {
  background: #123a4f;
  border-color: #38bdf8;
  color: #bae6fd;
}
#panneau-trace .trace-actions { display: flex; flex-direction: column; gap: 6px; }
#panneau-trace .trace-fermer { border-style: dashed; }

/* Pastille de légende « sous alerte » */
.leg-interv-alerte {
  border-color: var(--rouge);
  animation: pulse-alerte 1.4s infinite;
}
/* Pastille de légende « dernière position connue (trace) » — violette, sans pulsation */
.leg-interv-trace {
  border-color: #a78bfa;
  background: #241b3d;
}

/* ================================================================
   MODE DÉMONSTRATION — rejeu des 7 derniers jours
   Le bandeau rayé rouge/orange doit être INRATABLE : on ne confond
   jamais un rejeu du passé avec la situation en cours.
   ================================================================ */

#bandeau-demo {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 46; /* au-dessus du bandeau HORS DOMAINE (40), sous les toasts (60) */
  padding: 8px 12px 9px;
  border-bottom: 3px solid var(--rouge);
  background:
    repeating-linear-gradient(135deg,
      rgba(229, 72, 77, 0.92) 0 22px,
      rgba(245, 165, 36, 0.92) 22px 44px);
  color: #fff;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.55);
}
.demo-titre-ligne {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.demo-titre {
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75), 0 0 6px rgba(0, 0, 0, 0.55);
}
.demo-titre b { text-decoration: underline; }
#btn-demo-quitter {
  flex-shrink: 0;
  min-height: 34px;
  padding: 4px 12px;
  font-size: 12.5px;
  font-weight: 800;
  border-radius: 12px;   /* « Quitter la démonstration » : peut se replier */
  border: 1px solid rgba(255, 255, 255, 0.85);
  background: rgba(20, 22, 27, 0.82);
  color: #fff;
  cursor: pointer;
}
#btn-demo-quitter:hover { background: rgba(20, 22, 27, 0.95); }

/* Puces de jour (tactiles : 34 px de haut minimum) */
.demo-jours {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 7px;
}
/* Sélecteur de scénario : rejeu glissant ou feu de référence archivé */
.demo-scenario {
  display: inline-flex; align-items: center; gap: 6px;
  margin-right: 10px; font-size: 12.5px; font-weight: 700; color: #f4f6f8;
}
.demo-scenario select {
  min-height: 32px; max-width: min(62vw, 340px);
  padding: 3px 8px; border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.7);
  background: rgba(20, 22, 27, 0.85); color: #f4f6f8;
  font-size: 12.5px; font-weight: 700; cursor: pointer;
}

.demo-chip {
  min-height: 32px;
  padding: 4px 11px;
  font-size: 12.5px;
  font-weight: 700;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.7);
  background: rgba(20, 22, 27, 0.7);
  color: #f4f6f8;
  cursor: pointer;
  white-space: nowrap;
}
.demo-chip.actif {
  background: #14161b;
  border-color: var(--jaune);
  color: var(--jaune);
  box-shadow: 0 0 0 2px rgba(255, 214, 10, 0.35);
}
.demo-resume {
  margin-top: 6px;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}

/* Décalage des éléments fixes sous le bandeau (hauteur mesurée en JS) */
body.demo-active #entete { top: calc(var(--h-demo, 92px) + 10px); }
body.demo-active #btn-panneau { top: calc(var(--h-demo, 92px) + 10px); }
body.demo-active #panneau { top: calc(var(--h-demo, 92px) + 64px); }
body.demo-active #bandeau-hd { top: calc(var(--h-demo, 92px) + 12px); }
/* Rejeu ISOLÉ : ni bandeau de nouvelles données, ni puce d'état du cache du
   direct (filet de sécurité — le JS les retire déjà à l'entrée en démo) */
body.demo-active #maj-dispo,
body.demo-active #recalc-dispo,
body.demo-active #puce-cache { display: none !important; }

/* Pendant le rejeu, TOUS les marqueurs du direct disparaissent (foyers,
   étiquettes, menaces, intervenants, aéronefs, objets partagés…) : seuls les
   marqueurs du rejeu restent. Le !important bat les styles inline du direct. */
body.demo-active .maplibregl-marker:not(.marqueur-demo) { display: none !important; }

/* Tag « DÉMO » collé sous la pastille d'un acteur rejoué */
.demo-tag {
  padding: 0 6px;
  border-radius: 999px;
  background: var(--rouge);
  border: 1px solid #ffb3b6;
  color: #fff;
  font-size: 9.5px;
  font-weight: 900;
  letter-spacing: 0.06em;
  white-space: nowrap;
  pointer-events: none;
}
.demo-tag-inline { display: inline-block; margin: 0 2px; vertical-align: middle; }
.acteur-demo { cursor: pointer; }

/* Badge de VALIDITÉ, dans la barre timeline juste au-dessus du curseur */
.demo-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  margin: -2px 0 6px;
  padding: 5px 10px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #23262d;
  text-align: center;
}
.demo-badge-l1 { font-size: 14px; font-weight: 900; letter-spacing: 0.01em; }
.demo-badge-l2 { font-size: 11px; font-weight: 700; opacity: 0.92; }
.demo-badge.vert { background: rgba(45, 198, 83, 0.16); border-color: #2dc653; color: #8ef0b1; }
.demo-badge.jaune { background: rgba(255, 214, 10, 0.16); border-color: var(--jaune); color: #ffe97a; }
.demo-badge.rouge { background: rgba(229, 72, 77, 0.18); border-color: var(--rouge); color: #ffb3b6; }
.demo-badge.attente { background: rgba(139, 147, 161, 0.14); border-color: #8b93a1; color: #c3c9d3; }

/* Bloc « Démonstration » du panneau */
#btn-demo.actif {
  border-color: var(--rouge);
  background: rgba(229, 72, 77, 0.18);
  color: #ffd9db;
  font-weight: 800;
}
.demo-resume-panneau {
  margin-top: 8px;
  padding: 7px 9px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #1c2029;
  font-size: 12px;
  color: var(--texte-2);
}
.demo-legende {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  font-size: 12px;
  color: var(--texte-2);
}
.demo-legende li {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 5px;
}
.demo-puce {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border-radius: 4px;
}
.demo-puce-prevu { background: rgba(255, 159, 10, 0.35); border: 2px solid #ff9f0a; }
.demo-puce-obs {
  border-radius: 50%;
  background: #fff;
  border: 3px solid #38bdf8;
  box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.28);
}
.demo-puce-acteur { background: #2ecc71; border-radius: 50%; }

/* Fiche d'acteur rejoué */
.fiche-demo .pp-src { color: #ffb3b6; font-weight: 700; }
.fiche-demo .pp-instant { color: var(--texte-2); }

/* ----- Mobile : bandeau compact, puces défilables au doigt ----- */
@media (max-width: 720px) {
  #bandeau-demo { padding: 5px 8px 6px; }
  .demo-titre { font-size: 11.5px; line-height: 1.22; }
  #btn-demo-quitter { font-size: 11px; padding: 3px 8px; min-height: 30px; }
  .demo-jours {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-top: 5px;
    padding-bottom: 2px;
  }
  .demo-jours::-webkit-scrollbar { display: none; }
  .demo-chip { min-height: 34px; font-size: 12px; }
  /* Résumé borné à 2 lignes : le bandeau ne mange pas la carte sur téléphone */
  .demo-resume {
    margin-top: 4px;
    font-size: 10.5px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .demo-badge-l1 { font-size: 12.5px; }
  .demo-badge-l2 { font-size: 10.5px; }
  body.demo-active #panneau { top: calc(var(--h-demo, 110px) + 60px); }
}

/* ================================================================
   DÉMO GUIDÉE (CH4) — visite commentée posée PAR-DESSUS le rejeu
   Priorité au 375 × 812 : Jérémy la montre sur son téléphone de
   service. Toutes les commandes font 44 px de haut minimum.
   ================================================================ */

/* Pendant la visite, le bandeau rayé se réduit à sa ligne de titre : le
   sélecteur de scénario, les puces de jour et le résumé appartiennent à
   l'exploration LIBRE (le séquenceur pilote le jour et l'heure lui-même).
   Idem pour la ligne d'info et le bouton de mode de la barre horaire : le
   cartouche narratif les remplace (spec « bulle unique »). */
body.demo-guidee #demo-jours,
body.demo-guidee #demo-scenario,
body.demo-guidee #demo-resume,
body.demo-guidee #timeline-info,
body.demo-guidee #timeline-mode,
/* Les flottants ℹ et « ? Légende » chevauchaient le compteur « étape N/18 »
   quand le cartouche est haut (épreuve CH4 en 375x812) : pendant la visite
   guidée, le cartouche EST la seule parole — les flottants se retirent et
   reviennent d'eux-mêmes au retrait de la classe. */
body.demo-guidee #btn-legende,
body.demo-guidee #btn-apropos { display: none; }

/* Bouton de RETOUR vers la visite, dans le bandeau rayé (exploration libre) */
#btn-demo-guidee {
  flex-shrink: 0;
  min-height: 34px;
  padding: 4px 12px;
  font-size: 12.5px;
  font-weight: 800;
  border-radius: 12px;
  border: 1px solid var(--jaune);
  background: rgba(20, 22, 27, 0.82);
  color: var(--jaune);
  cursor: pointer;
}
#btn-demo-guidee:hover { background: rgba(20, 22, 27, 0.95); }

/* Badge de validité masqué par les étapes qui n'en ont pas l'usage */
#demo-badge.dg-masque { display: none; }

/* ----- Cartouche narratif (bulle unique au-dessus de la barre horaire) ----- */
#dg-cartouche {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 170px; /* repli avant le premier calage — calerTimeline() écrit style.bottom */
  z-index: 31;   /* juste au-dessus de la barre horaire (30) */
  width: min(560px, calc(100vw - 20px));
  padding: 9px 12px 8px;
  border-radius: 14px;
  border: 1px solid #3a4150;
  border-left: 4px solid var(--jaune);
  background: rgba(20, 22, 27, 0.95);
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.55);
}
.dg-tete {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 20px;
}
.dg-role-chip {
  flex-shrink: 0;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #c8cdd6;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.dg-titre {
  font-size: 14px;
  font-weight: 900;
  color: var(--jaune);
  letter-spacing: 0.01em;
}
.dg-compteur {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--texte-2);
  white-space: nowrap;
}
/* Verdict D (enrichissement) : marqueur de demande surligné pendant le
   survol de sa ligne dans la console — halo seulement, JAMAIS de transform
   (MapLibre s'en sert en style inline pour positionner le marqueur). */
/* #carte en ancre : la règle box-shadow des marqueurs de demande (même
   spécificité, plus bas dans le fichier) écrasait le halo sans lui. */
#carte .marq-entraide.marq-surligne {
  box-shadow: 0 0 0 4px var(--accent), 0 0 18px 6px rgba(255, 214, 10, 0.5);
  border-radius: 50%;
  filter: brightness(1.25);
  z-index: 10;
  /* evac-pulse (etat-en_attente) ANIME box-shadow : une animation gagne sur
     toute règle statique — on la suspend le temps du survol, le halo jaune
     prend sa place puis la pulsation reprend seule. */
  animation: none;
}

/* Verdict K : chronologie cliquable des étapes du rejeu. */
#dg-chrono-btn {
  flex-shrink: 0;
  margin-left: 6px;
  padding: 1px 7px;
  font-size: 13px;
  line-height: 1.4;
  border-radius: 7px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  cursor: pointer;
}
#dg-chrono-btn.actif { border-color: var(--accent); color: var(--accent); }
#dg-chrono {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 6px 0 2px;
  max-height: 168px;
  overflow-y: auto;
  border: 1px solid var(--bord);
  border-radius: 9px;
  background: var(--bloc);
  padding: 4px;
}
.dg-chrono-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 5px 8px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--texte);
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
}
.dg-chrono-item:hover { background: var(--panneau); }
.dg-chrono-item.actif {
  background: var(--panneau);
  box-shadow: inset 2px 0 0 var(--accent);
  font-weight: 700;
}
.dg-chrono-num {
  flex-shrink: 0;
  min-width: 18px;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--texte-2);
}
.dg-chrono-titre {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dg-chrono-h {
  flex-shrink: 0;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--texte-2);
}

/* Le texte défile s'il est long : la bulle ne mange jamais la carte */
.dg-texte {
  margin: 5px 0 8px;
  font-size: 13px;
  line-height: 1.45;
  color: #f4f6f8;
  max-height: 30vh;
  overflow-y: auto;
}
.dg-nav {
  display: flex;
  align-items: stretch;
  gap: 6px;
}
.dg-nav button {
  min-height: 44px; /* seuil tactile : Jérémy pilote au pouce, parfois ganté */
  min-width: 44px;  /* idem en LARGEUR : ◀/⏸/▶ tombaient à 29-31 px (épreuve
                       CH4 mobile) — une cible tactile est un carré, pas une
                       bande */
  padding: 0 10px;
  border-radius: 10px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #e8ebf0;
  font-size: 14px;
  font-weight: 800;
  cursor: pointer;
  white-space: nowrap;
}
.dg-nav button:hover { filter: brightness(1.15); }
.dg-nav button:disabled { opacity: 0.35; cursor: default; }
.dg-nav button.actif {
  border-color: #ff9f0a;
  background: rgba(255, 159, 10, 0.18);
  color: #ffd60a;
}
#dg-suiv { flex: 1; font-size: 13px; }
.dg-nav .dg-principal,
.dg-fin-actions .dg-principal,
.dg-ecran-actions .dg-principal {
  border-color: #ff9f0a;
  background: rgba(255, 159, 10, 0.14);
  color: #ffd60a;
}
/* « explorer librement » : discret mais TACTILE (zone de 44 px assurée par
   le padding, l'allure reste celle d'un lien) */
.dg-pied {
  display: flex;
  justify-content: flex-end;
  margin: -2px -4px -6px 0;
}
.dg-lien {
  min-height: 40px;
  padding: 8px 6px;
  border: none;
  background: none;
  color: var(--texte-2);
  font-size: 11.5px;
  font-weight: 600;
  text-decoration: underline dotted;
  cursor: pointer;
}
.dg-lien:hover { color: #e8ebf0; }

/* ----- Annonce de POINT DE VUE (plein écran, brève) ----- */
#dg-role {
  position: fixed;
  inset: 0;
  z-index: 58; /* au-dessus de tout sauf des toasts (60) */
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 10, 14, 0.68);
  cursor: pointer;
  opacity: 1;
  transition: opacity 0.22s ease;
}
#dg-role.dg-role-sortie { opacity: 0; }
.dg-role-carte {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  max-width: min(88vw, 420px);
  padding: 22px 28px;
  border-radius: 18px;
  border: 2px solid #ff9f0a;
  background: rgba(20, 22, 27, 0.96);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
  text-align: center;
  animation: dg-role-entree 0.3s ease;
}
@keyframes dg-role-entree {
  from { transform: scale(0.92); opacity: 0.4; }
  to { transform: scale(1); opacity: 1; }
}
.dg-role-emoji { font-size: 42px; line-height: 1; }
.dg-role-titre {
  font-size: 20px;
  font-weight: 900;
  letter-spacing: 0.05em;
  color: #ffd60a;
}
.dg-role-detail { font-size: 13px; font-weight: 600; color: var(--texte-2); }
.dg-role-tag {
  margin-top: 4px;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--rouge);
  border: 1px solid #ffb3b6;
  color: #fff;
  font-size: 10.5px;
  font-weight: 900;
  letter-spacing: 0.06em;
}

/* ----- Écrans SIMULÉS (mécanisme du moteur, remplis par les scènes) ----- */
#dg-ecrans {
  position: fixed;
  left: 10px;
  right: 10px;
  top: calc(var(--marge-haute, 0px) + var(--h-demo, 92px) + 64px);
  bottom: calc(var(--bas-pile, 170px) + 8px);
  z-index: 33;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  overflow: hidden;
  pointer-events: none; /* la carte reste manipulable autour des cartes posées */
}
#dg-ecrans[hidden] { display: none; }
.dg-ecran {
  pointer-events: auto;
  width: min(92vw, 390px);
  max-height: 100%;
  overflow-y: auto;
  border-radius: 14px;
  border: 1.5px solid #3a4150;
  background: rgba(20, 22, 27, 0.97);
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6);
}
.dg-ecran-barre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 10px;
  border-bottom: 1px solid #3a4150;
  background: #1c2029;
  border-radius: 14px 14px 0 0;
}
.dg-ecran-role {
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.04em;
  color: #ffd60a;
}
.dg-ecran-tag {
  flex-shrink: 0;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--rouge);
  border: 1px solid #ffb3b6;
  color: #fff;
  font-size: 9.5px;
  font-weight: 900;
  letter-spacing: 0.06em;
}
.dg-ecran-titre {
  padding: 8px 12px 0;
  font-size: 13.5px;
  font-weight: 800;
  color: #f4f6f8;
}
.dg-ecran-corps {
  padding: 6px 12px 10px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--texte-2);
}
.dg-ecran-actions {
  display: flex;
  gap: 8px;
  padding: 0 12px 12px;
}
.dg-ecran-actions button {
  flex: 1;
  min-height: 44px;
  padding: 6px 10px;
  border-radius: 10px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #e8ebf0;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
}

/* ----- Lot scènes : panneau RÉEL hôte d'une scène (téléphone d'acteur) -----
   Le panneau garde son code et son contenu ; cette classe le recadre au
   format « téléphone posé sur la carte » pour que le cartouche et les
   commandes de la visite restent visibles et touchables en dessous.
   Spécificité volontairement supérieure à .page-plein ET à sa variante
   desktop (@media 901px) : body.demo-guidee + double classe. */
body.demo-guidee .page-plein.dg-hote {
  position: fixed;
  inset: auto;
  top: calc(var(--marge-haute, 0px) + var(--h-demo, 92px) + 56px);
  bottom: calc(var(--bas-pile, 170px) + 8px);
  left: 50%;
  right: auto;
  width: min(92vw, 400px);
  transform: translateX(-50%);
  padding: 10px 12px 12px;
  border: 1.5px solid #3a4150;
  border-radius: 14px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.6);
  /* SOUS les écrans simulés (33) : #dg-ecrans est déclaré AVANT les panneaux
     dans index.html — à z égal le panneau les couvrirait. Une notification ou
     une confirmation simulée doit se poser PAR-DESSUS le téléphone hôte. */
  z-index: 32;
}
/* Verre de scène : bloque les gestes du spectateur sur le panneau réel (le
   déroulé est joué par l'horloge de la visite) et porte l'étiquette. */
.dg-verre {
  position: absolute;
  inset: 0;
  z-index: 6;
  border-radius: inherit;
  background: transparent;
}
.dg-verre-tag {
  position: absolute;
  top: 6px;
  right: 46px; /* laisse lisible le ✕ du panneau, même s'il est sous verre */
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--rouge);
  border: 1px solid #ffb3b6;
  color: #fff;
  font-size: 9.5px;
  font-weight: 900;
  letter-spacing: 0.06em;
  white-space: nowrap;
  max-width: calc(100% - 56px);
  overflow: hidden;
  text-overflow: ellipsis;
}
/* « Appui » scripté : le bouton réel que l'acteur fictif est en train de
   toucher s'illumine — le spectateur suit le geste sans le faire. */
.dg-appui {
  outline: 3px solid var(--jaune) !important;
  outline-offset: 1px;
  animation: dg-appui-pulse 0.85s ease-in-out infinite alternate;
}
@keyframes dg-appui-pulse {
  from { filter: brightness(1); }
  to { filter: brightness(1.5); }
}
/* Encart « notification système » (écran simulé) : liseré jaune, façon
   bannière de téléphone verrouillé. */
.dg-ecran.dg-notif { border-color: var(--jaune); }
.dg-ecran.dg-notif .dg-ecran-titre { color: var(--jaune); }
/* « Bandeau » d'alerte simulé : le vrai centre de messages retire son bandeau
   pendant la démonstration (choix d'audit) — les scènes rejouent donc le leur
   en écran simulé compact, au même code couleur que les vrais niveaux. */
.dg-ecran.dg-bandeau { border-width: 2px; }
.dg-ecran.dg-bandeau .dg-ecran-titre { font-size: 12.5px; padding-top: 6px; }
.dg-ecran.dg-bandeau .dg-ecran-corps { font-size: 11.5px; padding-bottom: 8px; }
.dg-ecran.dg-bandeau-critique { border-color: var(--rouge); }
.dg-ecran.dg-bandeau-critique .dg-ecran-titre { color: #ffb3b6; }
.dg-ecran.dg-bandeau-important { border-color: #ffb020; }
.dg-ecran.dg-bandeau-important .dg-ecran-titre { color: #ffd27a; }

/* ----- Écran FINAL ----- */
#dg-fin {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 10, 14, 0.72);
}
.dg-fin-carte {
  width: min(94vw, 470px);
  max-height: 86vh;
  overflow-y: auto;
  padding: 18px 20px;
  border-radius: 16px;
  border: 2px solid #ff9f0a;
  background: rgba(20, 22, 27, 0.97);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
}
#dg-fin-titre {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 900;
  color: #ffd60a;
}
#dg-fin-texte { margin: 0 0 8px; font-size: 13px; line-height: 1.45; color: #f4f6f8; }
#dg-fin-stats { margin: 0 0 8px; font-size: 14px; font-weight: 800; color: #8ef0b1; }
.dg-fin-invariant { margin: 0 0 12px; font-size: 12px; color: var(--texte-2); }
.dg-fin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.dg-fin-actions button {
  flex: 1 1 44%;
  min-height: 46px;
  padding: 6px 10px;
  border-radius: 10px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: #e8ebf0;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
}
.dg-fin-actions button:hover { filter: brightness(1.15); }

/* ----- Marqueurs des étapes : enjeux menacés (D) et décision (E) ----- */
/* column-reverse + anchor:bottom : le PICTO reste posé sur le point exact,
   l'étiquette flotte au-dessus. */
.dg-menace, .dg-eau {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 3px;
}
.dg-menace-picto {
  display: grid;
  place-items: center;
  width: 27px;
  height: 27px;
  border-radius: 50%;
  border: 2px solid var(--rouge);
  background: rgba(43, 15, 16, 0.92);
  color: #ffd9db;
  font-size: 15px;
  line-height: 1;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.55);
}
.dg-menace-seveso .dg-menace-picto {
  border-color: var(--jaune);
  color: #ffe97a;
  background: rgba(56, 44, 5, 0.92);
}
.dg-menace-etiq {
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid #3a4150;
  background: rgba(20, 22, 27, 0.92);
  color: #f4f6f8;
  font-size: 10.5px;
  font-weight: 800;
  white-space: nowrap;
}
.dg-eau-picto {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid #38bdf8;
  background: rgba(12, 34, 51, 0.92);
  font-size: 15px;
  line-height: 1;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.55);
}
.dg-secteur-etiq {
  padding: 3px 9px;
  border-radius: 8px;
  border: 1px solid #ffb3b6;
  background: rgba(229, 72, 77, 0.92);
  color: #fff;
  font-size: 11px;
  font-weight: 900;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* ----- LÉGENDE DES ÉCHÉANCES (visite guidée) -----
   Posée en haut à gauche de la carte, sous le bandeau de démonstration : elle
   ne doit croiser NI le cartouche (en bas), NI la colonne de scène (à droite
   sur PC). Sans elle, les bandes colorées de la prévision ne veulent rien
   dire pour qui découvre l'outil. */
#dg-legende {
  position: fixed;
  /* 66 px, pas 10 : le bouton ☰ (#btn-panneau, 46 px de large à left:10px,
     opaque et en z-index 32 contre 31) mangeait le début de la légende — donc
     le mot « ATTEINDRE » et la première pastille. Mesuré en relecture le
     12/08/2026, à toute largeur, PC compris. */
  left: 66px;
  top: calc(var(--marge-haute, 0px) + var(--h-demo, 92px) + 12px);
  z-index: 31;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  max-width: min(52vw, 430px);
  padding: 6px 9px;
  border-radius: 12px;
  border: 1px solid #3a4150;
  background: rgba(20, 22, 27, 0.92);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
  pointer-events: none; /* la carte reste manipulable dessous */
}
#dg-legende[hidden] { display: none; }
.dg-leg-titre {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.03em;
  color: var(--texte-2);
  text-transform: uppercase;
}
.dg-leg-puce {
  padding: 2px 8px;
  border-radius: 999px;
  border: 2px solid;
  color: #f4f6f8;
  font-size: 11px;
  font-weight: 800;
  white-space: nowrap;
}
/* Échéance pas encore atteinte par l'animation : présente mais en retrait —
   le spectateur voit d'avance jusqu'où la prévision va aller. */
.dg-leg-attente { opacity: 0.3; }

/* ================================================================
   PC / VIDÉOPROJECTEUR (≥ 1100 px) — LA CARTE D'ABORD
   La démonstration se joue en salle, devant le SDIS : la carte est le
   sujet, les écrans d'acteurs sont l'illustration. Mesure du 12/08/2026
   AVANT ce bloc, en 1440x900 : carte visible 56 à 72 %, et surtout le
   panneau hôte (400x473) posé AU CENTRE, exactement là où fitBounds
   centre le feu — 4 acteurs sur 4 invisibles à l'étape « évacuation
   côté opérateur », 2 sur 2 à l'étape « côté citoyen ».
   On DÉPLACE donc tout ce qui n'est pas la carte : la scène dans une
   colonne à droite, le récit en bas à gauche. Le centre reste au feu.
   ================================================================ */
@media (min-width: 900px) {
  body.demo-guidee {
    /* Largeur de la colonne de scène — lue AUSSI par margesCadrageDemo()
       (app.js) pour que fitBounds décale le feu vers la gauche : une seule
       valeur, deux usages, jamais deux chiffres qui divergent.
       SEUIL À 900 px, la largeur où l'application bascule déjà en disposition
       large (cf. margesCadrageDemo). À 1100 px, une fenêtre de 1000 px de large
       — un PC portable en demi-écran, une tablette en paysage — retombait dans
       la disposition téléphone : panneau hôte centré, exactement sur le feu que
       fitBounds venait de centrer. Défaut trouvé en relecture le 12/08/2026.
       La colonne se rétrécit avec la fenêtre (32 vw) pour que la carte garde la
       majorité de l'écran même à 900 px. */
    --dg-colonne: min(360px, 32vw);
  }
  /* Écrans simulés : colonne DROITE, empilés du haut. Le conteneur reste
     transparent et `pointer-events: none` : seules les cartes posées comptent. */
  body.demo-guidee #dg-ecrans {
    left: auto;
    right: 16px;
    width: var(--dg-colonne);
    top: calc(var(--marge-haute, 0px) + var(--h-demo, 92px) + 64px
              + (100vh - var(--marge-haute, 0px) - var(--h-demo, 92px) - 104px) * 0.56
              + 8px);
    bottom: 24px;
    align-items: stretch;
    justify-content: flex-end; /* les cartes s'empilent vers le BAS de la colonne,
                                  sous le téléphone hôte qui occupe le haut */
    overflow-y: auto;
  }
  body.demo-guidee .dg-ecran { width: 100%; }
  /* Panneau réel hôte (téléphone de l'acteur) : MÊME colonne, plus au centre.
     `bottom: auto` + `max-height` : le panneau ÉPOUSE son contenu au lieu de
     tirer une bande pleine hauteur — un écran d'entraide de 300 px n'a aucune
     raison de manger 720 px de carte. Il défile s'il déborde. */
  body.demo-guidee .page-plein.dg-hote {
    left: auto;
    right: 16px;
    transform: none;
    width: var(--dg-colonne);
    bottom: auto;
    /* BUDGET PARTAGÉ avec #dg-ecrans : le téléphone de l'acteur prend au plus
       la moitié haute de la colonne, les écrans simulés (notifications,
       confirmations) s'empilent dans la moitié basse. Sans ce partage, les
       deux piles étaient ancrées aux deux extrémités sans se voir, et une
       confirmation un peu longue recouvrait le bas du panneau qu'elle
       commente (relecture du 12/08/2026). */
    max-height: calc((100vh - var(--marge-haute, 0px) - var(--h-demo, 92px)
                      - 104px) * 0.56);
    overflow-y: auto;
  }
  /* Récit : en bas à GAUCHE, borné pour ne jamais toucher la colonne */
  body.demo-guidee #dg-cartouche {
    left: 16px;
    transform: none;
    width: min(620px, calc(100vw - var(--dg-colonne) - 56px));
  }
  body.demo-guidee .dg-texte { max-height: 22vh; }
  /* Légende : elle a toute la largeur disponible à gauche de la colonne */
  body.demo-guidee #dg-legende { max-width: calc(100vw - var(--dg-colonne) - 56px); }
  /* Annonce de rôle : encart posé DANS la colonne, l'écran ne s'éteint plus.
     Neuf bascules de point de vue x 2,4 s de noir plein écran, c'était
     l'équivalent de 22 s de démonstration passées à ne rien montrer. */
  body.demo-guidee #dg-role {
    inset: auto;
    right: 16px;
    top: calc(var(--marge-haute, 0px) + var(--h-demo, 92px) + 64px);
    width: var(--dg-colonne);
    display: block;
    background: none;
  }
  body.demo-guidee .dg-role-carte { max-width: 100%; }
  /* Écran final : carte posée à droite elle aussi, le bilan se lit avec la
     carte du feu encore sous les yeux. */
  body.demo-guidee #dg-fin {
    inset: auto;
    right: 16px;
    top: calc(var(--marge-haute, 0px) + var(--h-demo, 92px) + 64px);
    bottom: 24px;
    width: var(--dg-colonne);
    display: block;
    background: none;
    overflow-y: auto;
  }
  body.demo-guidee .dg-fin-carte { width: 100%; max-height: none; }
  /* PANNEAU DE GUIDAGE (frontend/itineraire.css) pendant la scène de l'eau :
     il rejoint la colonne comme les autres écrans d'acteur. Sa règle de base
     le centre en bas (`left: 50%` + translate), ce qui le poserait sur le
     tracé qu'il commente. Sélecteur `body.demo-guidee #itin-guidage` : un id
     dans une classe de corps l'emporte sur l'id seul, sans `!important`. */
  body.demo-guidee #itin-guidage {
    left: auto;
    right: 16px;
    bottom: 24px;
    transform: none;
    width: var(--dg-colonne);
    max-height: calc(100vh - var(--marge-haute, 0px) - var(--h-demo, 92px) - 120px);
  }
}

/* ----- Téléphone (≤ 720 px) : la bulle colle aux bords, texte borné ----- */
@media (max-width: 720px) {
  #dg-cartouche {
    width: calc(100vw - 12px);
    padding: 8px 10px 7px;
  }
  .dg-titre { font-size: 12.5px; }
  /* 24vh = 195 px sur un 812 : avec la tête, les commandes et le lien, le
     cartouche montait à 301 px (mesuré le 12/08/2026 en 375x812), et il ne
     restait plus que 9 % de carte aux scènes d'acteur — le défaut même que ce
     chantier corrige. Le récit est DIT à voix haute pendant la démonstration ;
     à l'écran, trois lignes suffisent et le reste défile. La carte reprend
     110 px. Sur PC le bloc ≥ 1100 px rend 22vh : là, la place existe. */
  .dg-texte { font-size: 12.5px; max-height: 4.7em; margin: 4px 0 7px; }
  /* Le téléphone d'acteur : borné à 34 % de la hauteur pour qu'une bande de
     carte survive AU-DESSUS du récit. Il défile si son écran est plus long —
     c'est déjà ce que fait le vrai panneau sur un vrai téléphone.
     BUDGET MESURÉ en 375x812 : en-tête 56 + hôte 276 + récit 197 + barre
     horaire 54 + barre d'actions 49 = 632 px d'habillage. Il reste 180 px de
     carte, soit un quart de l'écran. C'est la LIMITE GÉOMÉTRIQUE du format :
     un téléphone ne peut pas montrer en même temps l'écran d'un acteur, le
     récit et la carte. Sur PC (bloc >= 1100 px) la colonne latérale règle le
     problème ; ici on choisit de garder une bande de carte lisible plutôt
     qu'un panneau entier. */
  body.demo-guidee .page-plein.dg-hote {
    max-height: 34vh;
    overflow-y: auto;
  }
  .dg-nav { gap: 5px; }
  .dg-nav button { padding: 0 8px; font-size: 13px; } /* min-width 44px hérité */
  #dg-suiv { font-size: 12px; }
  .dg-role-carte { padding: 16px 18px; }
  .dg-role-emoji { font-size: 36px; }
  .dg-role-titre { font-size: 17px; }
  .dg-fin-carte { padding: 14px 14px; }
  .dg-fin-actions button { font-size: 12px; min-height: 44px; }
}
/* Téléphone COUCHÉ : la hauteur est comptée — le récit reste une ligne ou
   deux, la carte garde la place (le texte défile si besoin). */
@media (max-height: 450px) {
  .dg-texte { max-height: 17vh; }
  #dg-ecrans { top: calc(var(--marge-haute, 0px) + var(--h-demo, 60px) + 46px); }
}

/* ================================================================
   PRÉSENCE TERRAIN — SOS PERSONNEL + PAGE DES INTERVENANTS
   Mobile d'abord : tout est atteignable au pouce, rien ne défile
   horizontalement, et le rouge reste réservé à ce qui est vital.
   ================================================================ */

/* ----- Barre d'intervention : bouton SOS + compteur d'intervenants ----- */

#btn-sos {
  min-height: 54px;
  padding: 10px 22px;
  /* « 🆘 SOS — je suis en difficulté » tient sur deux lignes sur un téléphone :
     rayon FIXE, sinon le bouton le plus vital de l'application devient un
     disque rouge dont le texte déborde. */
  border-radius: 18px;
  border: 2px solid #ff6b6b;
  background: #b3121a;
  color: #fff;
  font-size: 16px;
  font-weight: 900;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.55);
}
#btn-sos:hover { filter: brightness(1.12); }
#btn-sos:active { transform: scale(0.98); }
#btn-page-intervenants {
  min-height: 38px;
  padding: 5px 14px;
  border-radius: 14px;   /* libellé « 👥 Intervenants (n) » : peut se replier */
  border: 1.5px solid #38bdf8;
  background: rgba(18, 32, 45, 0.94);
  color: #bae6fd;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.45);
}
#btn-page-intervenants:hover { filter: brightness(1.14); }
#btn-page-intervenants.avec-alerte {
  border-color: var(--rouge);
  background: #55131a;
  color: #ffd9db;
  animation: pulse-alerte 1.6s infinite;
}

/* Bandeau SOS permanent, DANS la barre d'intervention : toujours visible
   (en haut sur grand écran, à portée de pouce sur téléphone) */
#bandeau-sos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  max-width: min(94vw, 470px);
  padding: 8px 14px;
  border-radius: var(--rayon);
  background: #b3121a;
  border: 2px solid #ff6b6b;
  color: #fff;
  font-size: 13.5px;
  font-weight: 800;
  text-align: center;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.55);
  animation: pulse-alerte 1.8s infinite;
}
#bandeau-sos[hidden] { display: none; }
#bandeau-sos-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
#bandeau-sos button {
  min-height: 36px;
  padding: 4px 12px;
  border-radius: 8px;
  border: 1px solid #ffd9db;
  background: rgba(0, 0, 0, 0.28);
  color: #fff;
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
}
#bandeau-sos button:hover { background: rgba(0, 0, 0, 0.45); }

/* ----- Pages plein écran (intervenants, SOS) ----- */

.page-plein {
  position: fixed;
  inset: 0;
  z-index: 58;
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* ZONES SÛRES — mesuré le 30/07 à 375 × 812 sur « 👥 Intervenants ».
     `inset: 0` + `padding-bottom: 12px` : le fond couvrait bien tout l'écran,
     mais les DERNIERS ENFANTS aussi. La rangée « 🗺 Tout voir sur la carte » /
     « ⬇ Exporter CSV » occupait y 748 → 802 pour une zone atteignable qui
     s'arrête à 778 : 24 px des deux boutons tombaient SOUS l'indicateur
     d'accueil de l'iPhone, là où iOS capte le glissement à la place de la page.
     Le bouton se voit et ne se touche pas — le défaut le plus traître.
     Cette classe porte HUIT surfaces plein écran, dont `#panneau-sos` et
     `#panneau-evacuation` : les deux écrans où un bouton injoignable coûte le
     plus cher. Une seule règle les corrige toutes.
     Les quatre côtés sont repris : en PAYSAGE l'encoche prend 47 px à gauche ET
     à droite, et ces pages vont d'un bord à l'autre. */
  padding: calc(10px + var(--marge-haute, 0px)) calc(12px + var(--marge-droite, 0px))
           calc(12px + var(--marge-basse, 0px)) calc(12px + var(--marge-gauche, 0px));
  background: rgba(14, 16, 20, 0.985);
  overflow: hidden;
}
.page-plein[hidden] { display: none; }
@media (min-width: 901px) {
  /* Verdict D (journal Stitch) : sur PC les consoles (eau, SOS, ordres,
     intervenants) sont une COLONNE LATÉRALE droite, plus une modale centrée
     qui mangeait la carte — l'opérateur garde carte + liste sous les yeux,
     et la carte reste MANIPULABLE à gauche (aucun voile). Largeur dans la
     fourchette de la maquette (380-450 px) : tout le contenu de ces pages
     est déjà conçu pour 375 px de large (téléphone), la colonne est sûre
     par construction. AUCUNE action ne bouge : même DOM, même JS. */
  .page-plein {
    inset: 14px 14px 14px auto;
    transform: none;
    width: 430px;
    border: 1px solid var(--bord);
    border-radius: 14px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.7);
  }
  /* L'ordre d'évacuation REÇU n'est pas une console de travail : c'est une
     alerte grave — il garde la modale centrée plein regard. */
  .page-plein.evac-recu {
    inset: 14px auto 14px 50%;
    transform: translateX(-50%);
    width: min(96vw, 1080px);
  }
}
.page-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex: 0 0 auto;
}
.page-tete h2 { margin: 0; font-size: 17px; }
.page-tete-actions { display: flex; gap: 6px; }
.page-tete button {
  min-width: 46px;
  min-height: 46px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 16px;
  font-weight: 800;
  cursor: pointer;
}
.page-tete button:hover { filter: brightness(1.15); }
.page-tete .pi-sos { border-color: #ff6b6b; background: #b3121a; color: #fff; }

/* ----- Page intervenants : bandeau d'alerte, compteurs, outils ----- */

.pi-bandeau-alertes {
  flex: 0 0 auto;
  padding: 8px 12px;
  border-radius: 9px;
  background: #b3121a;
  border: 1.5px solid #ff6b6b;
  color: #fff;
  font-size: 13.5px;
  font-weight: 800;
  animation: pulse-alerte 1.8s infinite;
}
.pi-bandeau-alertes[hidden] { display: none; }
.pi-compteurs {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.pi-compteur {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 76px;
  min-height: 48px;
  padding: 4px 10px;
  border-radius: 10px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte-2);
  font-size: 10.5px;
  font-weight: 700;
  cursor: pointer;
}
.pi-compteur b { font-size: 16px; font-weight: 900; color: var(--texte); }
.pi-compteur:hover { filter: brightness(1.18); }
.pi-compteur.actif { border-color: #38bdf8; background: #123a4f; color: #bae6fd; }
.pi-compteur.actif b { color: #e0f2fe; }
.pi-compteur.pi-c-alerte { border-color: var(--rouge); color: #ffb3b6; }
.pi-compteur.pi-c-alerte b { color: #ffd9db; }
.pi-compteur.pi-c-alerte.actif { background: #55131a; }

.pi-outils {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.pi-recherche {
  min-height: 46px;
  padding: 6px 12px;
  font-size: 14px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 9px;
}
.pi-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.pi-selects { display: flex; flex-wrap: wrap; gap: 6px; }
.pi-select {
  flex: 1 1 150px;
  /* min-width AUTO par défaut sur un élément flex/grille : un <select> refuse
     alors de descendre sous la largeur de sa plus longue option (« Distance au
     foyer le plus proche ») et déborde de l'écran. On l'autorise à rétrécir. */
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--texte-2);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.pi-select[hidden] { display: none; }
.pi-select > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.pi-select select {
  min-width: 0;
  max-width: 100%;
  min-height: 44px;
  padding: 4px 8px;
  font-size: 13px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 9px;
}

/* ----- Page intervenants : liste (le seul bloc qui défile) ----- */

.pi-liste {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-right: 2px;
}
.pi-vide {
  padding: 18px 10px;
  text-align: center;
  color: var(--texte-2);
  font-size: 13px;
  line-height: 1.45;
}
.pi-groupe-tete {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 42px;
  margin-top: 4px;
  padding: 5px 10px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #232833;
  color: var(--texte);
  font-size: 13px;
  font-weight: 800;
  text-align: left;
  cursor: pointer;
}
.pi-groupe-tete .pi-fleche { width: 14px; color: var(--texte-2); }
/* 999px LÉGITIME par (b) : nowrap ajouté (« 12 agents » : deux jetons). */
.pi-groupe-tete .pi-effectif {
  margin-left: auto;
  padding: 1px 8px;
  border-radius: 999px;
  background: #14161b;
  border: 1px solid var(--bord);
  color: var(--texte-2);
  font-size: 11.5px;
  white-space: nowrap;
}
.pi-groupe-tete.pi-groupe-priorite {
  background: #55131a;
  border-color: var(--rouge);
  color: #ffd9db;
}
.pi-groupe-tete.pi-groupe-priorite .pi-effectif {
  color: #ffd9db;
  border-color: var(--rouge);
}

.pi-ligne {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 9px;
  background: #1c2029;
  border: 1px solid #2a2f39;
  border-left: 4px solid #8b93a1;
}
.pi-ligne.frais-direct { border-left-color: #2ecc71; }
.pi-ligne.frais-recent { border-left-color: var(--ambre); }
.pi-ligne.frais-perime { border-left-color: #8b93a1; }
.pi-ligne.en-alerte {
  background: #2a1013;
  border-color: var(--rouge);
  border-left-color: var(--rouge);
}
.pi-emoji { font-size: 19px; flex: 0 0 auto; }
.pi-corps { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.pi-nom {
  font-size: 13.5px;
  font-weight: 800;
  color: var(--texte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pi-ligne.en-alerte .pi-nom { color: #ffd9db; }
.pi-detail {
  font-size: 11.5px;
  color: var(--texte-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pi-etats { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.pi-etat {
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--bord);
  background: #14161b;
  color: var(--texte-2);
  font-size: 10.5px;
  font-weight: 700;
  white-space: nowrap;
}
.pi-etat.pi-e-direct { border-color: #2ecc71; color: #7ee2a8; }
.pi-etat.pi-e-recent { border-color: var(--ambre); color: #ffd9a8; }
.pi-etat.pi-e-perime { border-color: #8b93a1; color: #cfd4dc; }
.pi-etat.pi-e-alerte { border-color: var(--rouge); background: #55131a; color: #ffd9db; }
.pi-etat.pi-e-feu { border-color: #ff9f0a; color: #ffd9a8; }
.pi-etat.pi-e-arrete { border-color: var(--rouge); color: #ff8f92; }
.pi-actions {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  justify-content: flex-end;
}
.pi-actions button,
.pi-actions a {
  min-height: 38px;
  padding: 3px 10px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 12px;
  font-weight: 700;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}
.pi-actions button:hover, .pi-actions a:hover { filter: brightness(1.15); }
.pi-actions .pi-a-alerte { border-color: var(--rouge); background: #55131a; color: #ffd9db; }
.pi-actions .pi-a-lever { border-color: #2ecc71; background: #10241a; color: #c9f3dc; }
.pi-actions .pi-a-tel { border-color: #2ecc71; color: #c9f3dc; }

.pi-actions-globales {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.pi-actions-globales button {
  flex: 1 1 150px;
  min-height: 46px;
  padding: 8px 12px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 13.5px;
  font-weight: 800;
  cursor: pointer;
}
.pi-actions-globales button:hover { filter: brightness(1.15); }

/* Sous 700 px : plus de ligne à colonnes mais des CARTES empilées lisibles au
   doigt — aucun défilement horizontal, boutons pleine largeur */
@media (max-width: 700px) {
  .pi-ligne { flex-wrap: wrap; align-items: flex-start; }
  .pi-emoji { order: 1; font-size: 22px; }
  .pi-corps { flex: 1 1 100%; order: 2; }
  .pi-nom { white-space: normal; }
  .pi-detail { white-space: normal; }
  .pi-actions { order: 3; flex: 1 1 100%; justify-content: stretch; }
  .pi-actions button, .pi-actions a { flex: 1 1 auto; justify-content: center; min-height: 42px; }
  /* Barre d'outils DENSE : sur téléphone, la place doit aller à la LISTE.
     LES ZONES SÛRES SONT REPRISES ICI AUSSI — et c'est le seul endroit qui
     compte vraiment. Cette règle écrase le `padding` de `.page-plein` sur tout
     écran de moins de 700 px, donc sur TOUS les téléphones : corriger la règle
     de base sans corriger celle-ci ne changeait rien là où le défaut se produit.
     Mesuré à 375 × 812 : `padding-bottom: 10px` laissait la rangée
     « 🗺 Tout voir sur la carte » / « ⬇ Exporter CSV » finir à y = 802 pour une
     zone atteignable s'arrêtant à 778. */
  .page-plein {
    gap: 6px;
    padding: calc(8px + var(--marge-haute, 0px)) calc(8px + var(--marge-droite, 0px))
             calc(10px + var(--marge-basse, 0px)) calc(8px + var(--marge-gauche, 0px));
  }
  .page-tete h2 { font-size: 15px; }
  .page-tete button { min-width: 42px; min-height: 42px; font-size: 15px; }
  .pi-bandeau-alertes { padding: 6px 9px; font-size: 12px; }
  .pi-compteur {
    flex: 1 1 88px;
    flex-direction: row;
    gap: 5px;
    align-items: baseline;
    justify-content: center;
    min-width: 0;
    min-height: 40px;
    padding: 3px 6px;
    font-size: 10px;
  }
  .pi-compteur b { font-size: 14px; }
  .pi-recherche { min-height: 42px; font-size: 13px; }
  /* minmax(0, 1fr) et non 1fr : une piste « 1fr » a pour minimum le CONTENU,
     ce qui laissait le sélecteur de tri pousser la barre d'outils hors de
     l'écran (défilement horizontal interdit sur mobile). */
  .pi-selects { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .pi-select { font-size: 9.5px; }
  .pi-select select { min-height: 40px; font-size: 12px; }
  #pi-champ-equipe { grid-column: 1 / -1; }
  .pi-actions-globales button { min-height: 42px; font-size: 12.5px; }
}

/* ----- Résumé inline dans le tiroir (remplace la liste détaillée) ----- */

.interv-resume { display: flex; flex-direction: column; gap: 6px; }
.interv-resume-texte {
  font-size: 0.76rem;
  font-weight: 700;
  color: #c8cdd6;
  line-height: 1.35;
}
.interv-resume-texte.en-alerte { color: #ffb3b6; }
.interv-resume-ouvrir {
  min-height: 40px;
  padding: 6px 10px;
  border-radius: 9px;
  border: 1px solid #38bdf8;
  background: #123a4f;
  color: #bae6fd;
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
}
.interv-resume-ouvrir:hover { filter: brightness(1.15); }

/* ----- Panneau SOS ----- */

.sos-tete h2 { color: #ff8f92; }
.sos-intro {
  flex: 0 0 auto;
  margin: 0;
  font-size: 13px;
  line-height: 1.4;
  color: var(--texte-2);
}
.sos-types {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sos-type {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 62px;
  padding: 10px 14px;
  border-radius: 12px;
  border: 2px solid var(--bord);
  background: #1c2029;
  color: var(--texte);
  font-size: 15px;
  font-weight: 800;
  text-align: left;
  cursor: pointer;
}
.sos-type .sos-type-emoji { font-size: 24px; flex: 0 0 auto; }
.sos-type:hover { filter: brightness(1.15); }
.sos-type.actif {
  border-color: #ff6b6b;
  background: #55131a;
  color: #fff;
}
.sos-precision-label {
  flex: 0 0 auto;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--texte-2);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.sos-precision {
  flex: 0 0 auto;
  min-height: 46px;
  padding: 6px 12px;
  font-size: 14px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 9px;
}
.sos-erreur {
  flex: 0 0 auto;
  padding: 8px 12px;
  border-radius: 9px;
  background: #55131a;
  border: 1.5px solid var(--rouge);
  color: #ffd9db;
  font-size: 13px;
  font-weight: 700;
}
.sos-erreur[hidden] { display: none; }
.sos-actions { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; }
.sos-envoyer {
  min-height: 60px;
  border-radius: 12px;
  border: 2px solid #ff6b6b;
  background: #b3121a;
  color: #fff;
  font-size: 17px;
  font-weight: 900;
  letter-spacing: 0.03em;
  cursor: pointer;
}
.sos-envoyer:disabled { opacity: 0.45; cursor: not-allowed; animation: none; }
.sos-envoyer:not(:disabled) { animation: pulse-alerte 1.6s infinite; }
.sos-retour {
  min-height: 46px;
  border-radius: 9px;
  border: 1px dashed var(--bord);
  background: transparent;
  color: var(--texte-2);
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
}

/* =====================================================================
   VEILLE SIGNAL & RÉSEAU
   Principe de lecture : l'ALERTE (rouge pulsant) reste seule à signifier
   l'urgence. Le niveau de VEILLE se lit sur un registre séparé — un liseré
   progressif — et tout ce qui s'explique par le RÉSEAU ou par un ARRÊT
   VOLONTAIRE prend un habillage neutre : jamais l'apparence d'un danger.
   ===================================================================== */

/* ---------- Tampon hors-ligne (barre d'intervention) ---------- */
#etat-tampon {
  padding: 4px 12px;
  border-radius: 12px;   /* « n mesures en tampon, envoi à la reconnexion » */
  font-size: 12.5px;
  font-weight: 800;
  text-align: center;
  background: rgba(20, 22, 27, 0.94);
  border: 1px solid var(--bord);
  color: var(--texte-2);
}
#etat-tampon.tampon-attente { border-color: #7dd3fc; color: #bae6fd; }
#etat-tampon.tampon-suspendu { border-color: #8b93a1; color: #cfd4dc; }
#etat-tampon.tampon-ok { border-color: #2ecc71; color: #c9f3dc; }

/* ---------- Bandeau « trou de réseau » : INFORMATIF, jamais alarmant ---------- */
#bandeau-reseau {
  display: flex;
  flex-wrap: nowrap;      /* une seule ligne : la carte doit rester lisible */
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 5px 10px;
  font-size: 12px;
  border-radius: 12px;
  background: rgba(18, 34, 46, 0.95);
  border: 1px solid #7dd3fc;
  color: #cfe8f7;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.45);
}
#bandeau-reseau[hidden] { display: none; }
#btn-reseau-cadrer {
  min-height: 34px;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid #7dd3fc;
  background: rgba(0, 0, 0, 0.28);
  color: #bae6fd;
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
  white-space: nowrap;
}
#btn-reseau-cadrer:hover { background: rgba(125, 211, 252, 0.18); }

/* ---------- Pastilles carte : liseré de veille (outline = registre distinct
   de la bordure de fraîcheur et du halo rouge d'alerte, aucun conflit) ---------- */
.intervenant.veille-n1 .intervenant-pastille {
  outline: 2px solid rgba(245, 165, 36, 0.8);
  outline-offset: 2px;
}
.intervenant.veille-n2 .intervenant-pastille {
  outline: 3px solid rgba(255, 159, 10, 0.95);
  outline-offset: 2px;
}
.intervenant.veille-n3 .intervenant-pastille {
  outline: 4px double #e5484d;
  outline-offset: 2px;
}
.intervenant.veille-reseau .intervenant-pastille {
  outline: 2px dashed rgba(125, 211, 252, 0.9);
  outline-offset: 3px;
}
.intervenant.veille-arret .intervenant-pastille {
  outline: 2px dotted rgba(139, 147, 161, 0.9);
  outline-offset: 2px;
}

/* Étiquette de veille sous le nom (surcharge le rouge de .intervenant-age) */
.intervenant-veille.veille-n1 {
  background: #3a2a08; border-color: var(--ambre); color: #ffd9a8;
}
.intervenant-veille.veille-n2 {
  background: #4a2a08; border-color: #ff9f0a; color: #ffd9a8;
}
.intervenant-veille.veille-n3 {
  background: #55131a; border-color: var(--rouge); color: #ffd9db;
}
.intervenant-veille.veille-reseau {
  background: #12222e; border-color: #7dd3fc; color: #bae6fd;
}
.intervenant-veille.veille-arret {
  background: #23262d; border-color: #8b93a1; color: #cfd4dc;
}

/* ---------- Page « 👥 Intervenants » ---------- */
/* Liseré gauche de veille — jamais au détriment d'une ligne SOUS ALERTE */
.pi-ligne.veille-n1:not(.en-alerte) { border-left-color: var(--ambre); }
.pi-ligne.veille-n2:not(.en-alerte) { border-left-color: #ff9f0a; border-left-width: 5px; }
.pi-ligne.veille-n3:not(.en-alerte) { border-left-color: var(--rouge); border-left-width: 6px; }
.pi-ligne.veille-reseau:not(.en-alerte) { border-left-color: #7dd3fc; }
.pi-ligne.veille-arret:not(.en-alerte) { border-left-color: #8b93a1; }

.pi-etat.pi-e-veille.veille-n1 { border-color: var(--ambre); color: #ffd9a8; }
.pi-etat.pi-e-veille.veille-n2 {
  border-color: #ff9f0a; background: #3a2408; color: #ffd9a8;
}
.pi-etat.pi-e-veille.veille-n3 {
  border-color: var(--rouge); background: #55131a; color: #ffd9db; font-weight: 900;
}
.pi-etat.pi-e-veille.veille-reseau {
  border-color: #7dd3fc; background: #12222e; color: #bae6fd;
}
.pi-etat.pi-e-veille.veille-arret { border-color: #8b93a1; color: #cfd4dc; }
.pi-etat.pi-e-reseau { border-color: #7dd3fc; background: #12222e; color: #bae6fd; }
/* Alerte AUTOMATIQUE : même famille rouge, mais visiblement « machine » */
.pi-etat.pi-e-alerte.pi-e-auto {
  border-style: dashed;
  background: #3d1a1e;
}

/* Phrase du serveur, en toutes lettres */
.pi-veille-detail {
  margin-top: 2px;
  font-size: 11.5px;
  line-height: 1.3;
  color: var(--texte-2);
  border-left: 2px solid var(--bord);
  padding-left: 6px;
}
.pi-veille-detail.veille-n1 { border-left-color: var(--ambre); color: #ffd9a8; }
.pi-veille-detail.veille-n2 { border-left-color: #ff9f0a; color: #ffd9a8; }
.pi-veille-detail.veille-n3 { border-left-color: var(--rouge); color: #ffc9cb; }
.pi-veille-detail.veille-reseau { border-left-color: #7dd3fc; color: #bae6fd; }
.pi-veille-detail.veille-arret { border-left-color: #8b93a1; color: #cfd4dc; }

.pi-compteur.pi-c-veille { border-color: var(--ambre); color: #ffd9a8; }
.pi-compteur.pi-c-veille b { color: #ffe6bf; }
.pi-compteur.pi-c-veille.actif { background: #3a2a08; }
.pi-compteur.pi-c-reseau { border-color: #7dd3fc; color: #bae6fd; }
.pi-compteur.pi-c-reseau b { color: #e0f2fe; }
.pi-compteur.pi-c-reseau.actif { background: #12222e; }

/* ---------- Fiche intervenant : encart de veille + origine de l'alerte ---------- */
.fiche-intervenant .fi-veille {
  margin-top: 8px;
  padding: 7px 8px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.28);
  border: 1.5px solid var(--bord);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fiche-intervenant .fi-veille-titre { font-size: 12.5px; font-weight: 900; }
.fiche-intervenant .fi-veille-detail { font-size: 12.5px; color: var(--texte); }
.fiche-intervenant .fi-veille-reseau { font-size: 11.5px; color: var(--texte-2); }
.fiche-intervenant .fi-veille.veille-n1 { border-color: var(--ambre); }
.fiche-intervenant .fi-veille.veille-n1 .fi-veille-titre { color: #ffd9a8; }
.fiche-intervenant .fi-veille.veille-n2 { border-color: #ff9f0a; background: #2a1c06; }
.fiche-intervenant .fi-veille.veille-n2 .fi-veille-titre { color: #ffd9a8; }
.fiche-intervenant .fi-veille.veille-n3 { border-color: var(--rouge); background: #2a1013; }
.fiche-intervenant .fi-veille.veille-n3 .fi-veille-titre { color: #ff8f92; }
.fiche-intervenant .fi-veille.veille-reseau { border-color: #7dd3fc; background: #10202b; }
.fiche-intervenant .fi-veille.veille-reseau .fi-veille-titre { color: #bae6fd; }
.fiche-intervenant .fi-veille.veille-arret { border-color: #8b93a1; }
.fiche-intervenant .fi-veille.veille-arret .fi-veille-titre { color: #cfd4dc; }

/* Alerte AUTOMATIQUE (système) : liseré tireté, teinte moins « sang » que le
   SOS d'un humain — l'origine se lit d'un coup d'œil */
.fiche-intervenant .fi-alerte.fi-alerte-auto {
  border-style: dashed;
  background: #2b1a12;
  border-color: #ff9f0a;
}
.fiche-intervenant .fi-alerte.fi-alerte-auto .fi-alerte-titre { color: #ffd9a8; }
.fiche-intervenant .fi-alerte.fi-alerte-sos { border-width: 2px; }
.fiche-intervenant .fi-alerte-requal {
  padding: 5px 7px;
  border-radius: 6px;
  background: #10202b;
  border: 1px solid #7dd3fc;
  color: #bae6fd;
  font-size: 12px;
  font-weight: 700;
}
.fiche-intervenant .fl-journal {
  margin: 2px 0;
  padding: 4px 8px;
  background: rgba(0, 0, 0, 0.3);
  border-radius: 6px;
  font-size: 11.5px;
  color: #aab2bf;
  max-height: 96px;
  overflow-y: auto;
}
.fiche-intervenant .fl-journal:empty { display: none; }
.fiche-intervenant .fl-journal-ligne.journal-auto { color: #ffd9a8; }
.fiche-intervenant .fl-journal-ligne.journal-requal { color: #bae6fd; font-weight: 700; }

/* ---------- Couche « zones sans réseau » (tiroir des couches) ---------- */
.reseau-sous-couches {
  margin: 2px 0 6px 26px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.reseau-sous-couches[hidden] { display: none; }
.ligne-sous-couche {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--texte-2);
  cursor: pointer;
  min-height: 30px;
}
.ligne-sous-couche input { min-width: 18px; min-height: 18px; }
.puce-reseau {
  display: inline-block;
  width: 16px;
  height: 12px;
  border-radius: 3px;
  flex: 0 0 auto;
}
.puce-reseau-constate {
  border: 1px dashed #aeb6c2;
  background: repeating-linear-gradient(45deg,
    rgba(190, 199, 212, 0.85) 0 2px, transparent 2px 5px);
}
.puce-reseau-officiel {
  border-radius: 50%;
  width: 12px;
  background: #a855f7;
  border: 1px solid #f3e8ff;
}
#reseau-indice { margin: 2px 0 0; font-size: 11.5px; }
.profil-indice { margin: 2px 0 8px; }

/* Popup des zones sans réseau (même famille que les autres fiches) */
.fiche-reseau .fr-sous { font-size: 12px; color: var(--texte-2); margin: 2px 0 6px; }
.fiche-reseau .fr-note {
  margin: 6px 0 0;
  font-size: 11.5px;
  color: #bae6fd;
  border-top: 1px solid var(--bord);
  padding-top: 5px;
}

/* ---------- Légende ---------- */
.leg-veille {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--bloc);
  border: 2px solid var(--bord);
  font-size: 12px;
  font-weight: 900;
  flex: 0 0 auto;
}
.leg-veille-1 { outline: 2px solid rgba(245, 165, 36, 0.8); outline-offset: 2px; color: #ffd9a8; }
.leg-veille-2 { outline: 3px solid rgba(255, 159, 10, 0.95); outline-offset: 2px; color: #ffd9a8; }
.leg-veille-3 { outline: 4px double #e5484d; outline-offset: 2px; color: #ff8f92; }
.leg-veille-reseau { outline: 2px dashed rgba(125, 211, 252, 0.9); outline-offset: 3px; }
.leg-veille-arret { outline: 2px dotted rgba(139, 147, 161, 0.9); outline-offset: 2px; }
.leg-reseau {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 22px;
  border-radius: 4px;
  font-size: 13px;
  flex: 0 0 auto;
}
.leg-reseau-constate {
  border: 1px dashed #aeb6c2;
  background: repeating-linear-gradient(45deg,
    rgba(190, 199, 212, 0.85) 0 3px, transparent 3px 7px);
}
.leg-reseau-officiel { background: #3b1d5e; border: 1px solid #a855f7; }
.leg-reseau-tampon { background: #12222e; border: 1px solid #7dd3fc; }

/* ==========================================================================
   ÉQUIPES, INVITATIONS, INSCRIPTION ET VUE CITOYENNE
   (parties 2, 3 et 4 du contrat — mobile d'abord, cibles tactiles >= 44 px)
   ========================================================================== */

/* ---------- Panneau d'administration : onglets et sections ---------- */
.admin-onglets {
  display: flex;
  gap: 6px;
  margin: 6px 0 4px;
  flex-wrap: wrap;
}
.admin-onglet {
  flex: 1 1 auto;
  min-height: 42px;
  padding: 6px 10px;
  font-size: 13px;
  font-weight: 700;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte-2);
  cursor: pointer;
}
.admin-onglet:hover { filter: brightness(1.15); }
.admin-onglet.actif {
  background: rgba(255, 214, 10, 0.13);
  border-color: var(--accent);
  color: var(--accent);
}
.admin-section { margin-top: 4px; }

/* Champs étiquetés (filtres, invitation) : le libellé au-dessus du contrôle */
.admin-champ {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1 1 150px;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--texte-2);
}
.admin-champ-large { flex: 1 1 100%; }
.admin-champ select,
.admin-champ input,
.admin-champ textarea {
  min-height: 42px;
  padding: 6px 10px;
  font-size: 13.5px;
  font-weight: 400;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
  width: 100%;
}
.admin-champ textarea { min-height: 64px; resize: vertical; font-family: inherit; }

/* Filtres et tri de la liste des comptes */
.admin-filtres {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0;
}
.admin-filtre-large {
  flex: 1 1 100%;
  min-height: 42px;
  padding: 6px 10px;
  font-size: 13.5px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}

/* Barre de RÉAFFECTATION EN MASSE */
.admin-masse {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 8px 0 10px;
  padding: 8px 10px;
  background: var(--bloc);
  border: 1px dashed var(--bord);
  border-radius: var(--rayon);
}
.admin-masse-tout {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
  min-height: 34px;
}
.admin-masse-tout input { width: 20px; height: 20px; accent-color: var(--jaune); }
.admin-masse-compteur { font-size: 12px; color: var(--texte-2); }
#cm-equipe {
  flex: 1 1 180px;
  min-height: 42px;
  padding: 6px 10px;
  font-size: 13.5px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}
#cm-deplacer {
  flex: 1 1 100%;
  min-height: 44px;
  font-weight: 800;
  font-size: 13.5px;
  border-radius: 8px;
  border: 1px solid var(--ambre);
  background: #4a3a10;
  color: #ffe3ae;
  cursor: pointer;
}
#cm-deplacer:disabled { opacity: 0.45; cursor: not-allowed; }

/* Case à cocher et champ d'équipe dans chaque ligne de compte */
#liste-comptes .compte-titre { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
#liste-comptes .compte-coche {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  accent-color: var(--jaune);
}
#liste-comptes .compte-actions .compte-equipe {
  flex: 1 1 140px;
  min-width: 120px;
  min-height: 36px;
  padding: 4px 10px;
  font-size: 12.5px;
  border-radius: 7px;
  border: 1px solid var(--bord);
  background: #171a21;
  color: var(--texte);
}
#liste-comptes .tag-role.role-tag-citoyen {
  background: #1d2b33; border-color: #7dd3fc; color: #d6f0ff;
}

/* ---------- Liste des ÉQUIPES ---------- */
#liste-equipes .equipe {
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--bloc);
  padding: 8px 10px;
  margin-bottom: 8px;
}
#liste-equipes .equipe-sans { border-style: dashed; opacity: 0.9; }
#liste-equipes .equipe-titre {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-weight: 700;
  font-size: 14px;
}
/* 999px LÉGITIME par (b) : nowrap ajouté sur les deux (« 4 membres »,
   « aucun membre » : libellés en plusieurs mots). */
#liste-equipes .equipe-effectif {
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  background: #2a2f39;
  border: 1px solid var(--bord);
  color: var(--texte-2);
  white-space: nowrap;
}
#liste-equipes .equipe-vide {
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  background: #33240a;
  border: 1px solid var(--ambre);
  color: #ffe3ae;
  white-space: nowrap;
}
#liste-equipes .equipe-detail {
  color: var(--texte-2);
  font-size: 12px;
  margin: 3px 0 6px;
  overflow-wrap: anywhere;
}
#liste-equipes .equipe-actions { display: flex; flex-wrap: wrap; gap: 6px; }
#liste-equipes .equipe-actions button {
  min-height: 36px;
  padding: 4px 10px;
  font-size: 12.5px;
  border-radius: 7px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}
#liste-equipes .equipe-actions .danger { border-color: var(--rouge); color: #ff8f92; }

/* ---------- INVITATIONS ---------- */
.inv-compteur-erreur { color: #ffb4b6 !important; font-weight: 700; }
.inv-resultat {
  margin: 10px 0;
  padding: 10px 12px;
  background: #10241a;
  border: 1.5px solid #2ecc71;
  border-radius: 8px;
}
.inv-titre { font-size: 12.5px; font-weight: 700; color: #c9f3dc; margin-bottom: 8px; }
.invitation {
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  background: var(--bloc);
  padding: 8px 10px;
  margin-bottom: 8px;
}
.invitation.etat-revoquee, .invitation.etat-expiree, .invitation.etat-epuisee {
  opacity: 0.62;
  border-style: dashed;
}
.invitation-titre { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.invitation-titre code {
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.08em;
  color: #fff;
  background: rgba(0, 0, 0, 0.35);
  padding: 2px 8px;
  border-radius: 6px;
}
/* 999px LÉGITIME par (b) : nowrap ajouté. */
.invitation-titre .tag-role {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  background: #2a2f39;
  border: 1px solid var(--bord);
  white-space: nowrap;
}
.invitation-etat {
  padding: 1px 8px;
  border-radius: 10px;   /* « révoquée le 12/08 » : peut se replier */
  font-size: 11px;
  font-weight: 800;
  border: 1px solid var(--bord);
}
.inv-etat-active { background: #10241a; border-color: #2ecc71; color: #c9f3dc; }
.inv-etat-epuisee, .inv-etat-expiree { background: #2a2f39; color: var(--texte-2); }
.inv-etat-revoquee { background: #55131a; border-color: var(--rouge); color: #ffd9db; }
.invitation-detail { color: var(--texte-2); font-size: 12px; margin: 4px 0; }
.invitation-lien {
  font-size: 11.5px;
  color: #cfe5ff;
  background: rgba(0, 0, 0, 0.28);
  border-radius: 6px;
  padding: 5px 7px;
  margin-bottom: 6px;
  overflow-wrap: anywhere;
  user-select: all;
}
.invitation-actions, .inv-actions-globales { display: flex; flex-wrap: wrap; gap: 6px; }
.inv-actions-globales { margin-top: 8px; }
.inv-btn {
  min-height: 38px;
  padding: 4px 12px;
  font-size: 12.5px;
  font-weight: 700;
  border-radius: 7px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}
.inv-btn:hover { filter: brightness(1.15); }
.inv-btn.danger { border-color: var(--rouge); color: #ff8f92; }
.inv-btn:disabled { opacity: 0.6; cursor: wait; }

/* ---------- Écran d'INSCRIPTION ---------- */
.connexion-lien {
  margin-top: 12px;
  min-height: 44px;
  background: transparent;
  border: 1px solid var(--bord);
  border-radius: 8px;
  color: var(--jaune);
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
}
.connexion-lien:hover { filter: brightness(1.2); }
.ins-invitation {
  margin: 4px 0 10px;
  padding: 10px 12px;
  background: #10241a;
  border: 1.5px solid #2ecc71;
  border-radius: 8px;
  color: #c9f3dc;
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.4;
}
.ins-invitation-ko {
  background: #55131a;
  border-color: var(--rouge);
  color: #ffd9db;
}
.ins-force { margin-top: 8px; display: flex; align-items: center; gap: 8px; }
.ins-force-barre {
  flex: 1 1 auto;
  height: 7px;
  border-radius: 999px;
  background: #2a2f39;
  overflow: hidden;
}
/* 999px LÉGITIME par le cas (a), indirectement : `height: 100%` d'un parent de
   7 px de haut donne 7 px. Cette barre de force de mot de passe ne porte AUCUN
   texte, elle ne peut donc pas se replier — c'est le seul 999px de la feuille
   qui ne déclare ni `nowrap` ni `height` en dur, et il est sûr pour cette
   raison-là. Si un jour on y met un libellé, il faudra un rayon fixe. */
.ins-force-barre span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  transition: width 0.2s ease;
}
.ins-force-n0 { background: #55131a; }
.ins-force-n1 { background: var(--rouge); }
.ins-force-n2 { background: var(--ambre); }
.ins-force-n3 { background: #9fd356; }
.ins-force-n4 { background: #2ecc71; }
#ins-force-texte { font-size: 11.5px; color: var(--texte-2); min-width: 110px; }
.ins-securite {
  margin-top: 14px;
  padding: 10px 12px;
  background: #12222e;
  border: 1.5px solid #7dd3fc;
  border-radius: 8px;
  color: #d6f0ff;
  font-size: 12.5px;
  line-height: 1.45;
}
.ins-securite p { margin: 6px 0 8px; }
.ins-accepte {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-weight: 700;
  cursor: pointer;
  min-height: 34px;
}
.ins-accepte input { width: 20px; height: 20px; flex: 0 0 auto; accent-color: var(--jaune); }

/* ---------- VUE CITOYENNE ---------- */
#barre-citoyen {
  position: fixed;
  left: 50%;
  /* Au-dessus de la rangée « ? Légende / ℹ » : sur 375 px la barre occupe
     toute la largeur, elle ne doit jamais recouvrir ces boutons. */
  bottom: calc(var(--bas-commandes, 54px) + 52px);
  transform: translateX(-50%);
  z-index: 31;
  width: min(96vw, 520px);
  /* 62vh SEUL ne borne rien : en PAYSAGE l'écran ne fait que 375 px de haut, et
     ancrée à 122 px du bas la barre montait de y = 253 à y = 21 — elle passait
     PAR-DESSUS l'en-tête (y = 10 → 101) et par-dessus le bandeau unique
     (y = 109 → 141), mesuré à 812 × 375. On la borne à la place réellement
     libre sous la pile haute, avec les variables MESURÉES : elle défile déjà
     (`overflow-y: auto`), donc rien n'est perdu — seulement rien n'est recouvert. */
  max-height: min(62vh, calc(100vh - var(--y-bandeau) - var(--haut-bandeau, 0px)
                             - var(--bas-commandes, 54px) - 52px - 8px));
  max-height: min(62dvh, calc(100dvh - var(--y-bandeau) - var(--haut-bandeau, 0px)
                             - var(--bas-commandes, 54px) - 52px - 8px));
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  background: var(--panneau);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.6);
}
.citoyen-securite {
  border: 1px solid #7dd3fc;
  border-radius: 8px;
  background: #12222e;
}
.citoyen-securite-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  background: transparent;
  border: none;
  border-radius: 8px;
  color: #d6f0ff;
  font-size: 13px;
  font-weight: 800;
  text-align: left;
  cursor: pointer;
}
.citoyen-securite-texte {
  margin: 0;
  padding: 0 10px 10px;
  font-size: 12.5px;
  line-height: 1.5;
  color: #cfe5ff;
}
.citoyen-evacuations:empty { display: none; }
.citoyen-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.citoyen-position, .citoyen-sos {
  flex: 1 1 180px;
  min-height: 52px;
  padding: 8px 14px;
  font-size: 15px;
  font-weight: 800;
  /* « 🆘 Je suis en difficulté » sur deux lignes en 375 px : rayon FIXE. */
  border-radius: 16px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  cursor: pointer;
}
.citoyen-position.actif {
  background: #10241a;
  border-color: #2ecc71;
  color: #c9f3dc;
}
.citoyen-sos { background: var(--rouge); border-color: var(--rouge); color: #fff; }
.citoyen-situation-titre {
  display: block;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--texte-2);
  margin-bottom: 5px;
}
.citoyen-situation-choix {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.citoyen-situation-btn {
  min-height: 56px;
  padding: 8px 10px;
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.25;
  border-radius: 10px;
  border: 1.5px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  cursor: pointer;
  text-align: left;
}
.citoyen-situation-btn:hover { filter: brightness(1.15); }
.citoyen-situation-btn.actif {
  border-color: var(--jaune);
  background: #33280a;
  color: #ffe9a8;
}
.citoyen-situation-btn.sit-victime { border-color: rgba(229, 72, 77, 0.8); }
.citoyen-situation-btn.sit-victime.actif {
  background: #55131a;
  border-color: var(--rouge);
  color: #ffd9db;
}
.citoyen-situation-btn:disabled { opacity: 0.55; cursor: wait; }
.citoyen-situation-etat {
  margin-top: 6px;
  font-size: 12px;
  color: var(--texte-2);
  line-height: 1.4;
}
.citoyen-situation-etat.urgent { color: #ffb4b6; font-weight: 700; }

/* Masquage par rôle CITOYEN : aucun outil opérateur, aucun calcul, aucun
   marquage — la carte montre la situation observée. Le serveur applique de
   toute façon les droits ; ceci n'est qu'un confort d'interface.
   On CONSERVE volontairement : l'en-tête du panneau (déconnexion, profil,
   choix de zone, recherche de commune), les couches d'observation, la
   légende, et #barre-intervention pour le bandeau de SON propre SOS. */
body.role-citoyen #bloc-feux,
body.role-citoyen #bloc-manuels,
body.role-citoyen #bloc-marquage,
body.role-citoyen #bloc-menaces,
body.role-citoyen #bloc-hypo,
body.role-citoyen #bloc-demo,
body.role-citoyen .bloc[aria-labelledby="titre-scenario"],
body.role-citoyen .bloc[aria-labelledby="titre-histo"],
body.role-citoyen #bloc-autres-zones,
body.role-citoyen #ligne-couche-intervenants,
body.role-citoyen #chips-intervenants,
body.role-citoyen #liste-intervenants,
body.role-citoyen #btn-intervention,
body.role-citoyen #btn-sos,
body.role-citoyen #btn-ligne-feu,
body.role-citoyen #badge-lignes-attente,
body.role-citoyen #badge-alertes,
body.role-citoyen #btn-page-intervenants,
body.role-citoyen #timeline-bar,
body.role-citoyen #puce-cache,
body.role-citoyen #maj-dispo {
  display: none !important;
}
/* Le panneau latéral reste accessible mais se replie par défaut : l'écran du
   citoyen doit s'ouvrir sur la CARTE et sa barre, pas sur des réglages.

   `top: 10px` FIGÉ, LE MÊME DÉFAUT QUE LE `top: 12px` DE #maj-dispo. Ce sélecteur
   (0,2,1) bat les deux règles de #barre-intervention posées dans les media
   queries (0,1,1) — une media query n'ajoute AUCUNE spécificité. Conséquences
   mesurées : sur ordinateur la barre du citoyen se posait à 10 px du haut, donc
   PAR-DESSUS #entete (lui aussi à top: 10px, à droite) ; sur téléphone la règle
   mobile ne pose que `bottom: 158px`, et comme `top: 10px` gagnait, la boîte
   s'étirait de y = 10 à y = hauteur − 158 et traversait toute la carte (c'est le
   symptôme que messages.js décrit dans caler() et ne masquait que lorsqu'elle
   était VIDE — un citoyen qui reçoit un ordre d'évacuation a un enfant visible,
   donc la barre était bien là, à travers l'écran).
   On repasse par les variables MESURÉES, et on rend la main à la règle mobile. */
body.role-citoyen #barre-intervention {
  top: calc(var(--y-bandeau) + var(--haut-bandeau, 0px));
}
@media (max-width: 900px) {
  /* Même spécificité que la règle ci-dessus, déclarée APRÈS : sur téléphone la
     barre reste ancrée en bas (bottom: 158px) et ne s'étire plus. */
  body.role-citoyen #barre-intervention { top: auto; }
}

/* Profil : case à cocher « je peux aider » */
.profil-case {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  min-height: 38px;
}
.profil-case input { width: 20px; height: 20px; accent-color: var(--jaune); }

/* ---------- Badges CITOYEN / EN ZONE / SITUATION (page intervenants) ---------- */
.pi-etat.pi-e-citoyen { background: #1d2b33; border-color: #7dd3fc; color: #d6f0ff; }
.pi-etat.pi-e-zone { background: #33280a; border-color: var(--ambre); color: #ffe9a8; }
.pi-etat.pi-e-situation { background: #23262e; border-color: var(--bord); color: var(--texte-2); }
.pi-etat.pi-e-victime { background: #55131a; border-color: var(--rouge); color: #ffd9db; font-weight: 800; }
.pi-groupe-tete.pi-groupe-citoyens {
  background: #1d2b33;
  border-color: #7dd3fc;
  color: #d6f0ff;
}
.chip-echeance.chip-citoyens { border-color: #7dd3fc; }

/* ================================================================
   ORDRES D'ÉVACUATION (partie 5) et ENTRAIDE EAU (partie 6)
   ================================================================ */

/* ---------- Boutons d'action et pastilles (barre haute) ---------- */
.barre-actions-terrain {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
}
.barre-actions-terrain:empty { display: none; }
#btn-evacuation, #btn-entraide {
  min-height: 44px;
  padding: 8px 14px;
  border-radius: 14px;   /* libellés longs (« 🤝 Demandes d'entraide ») */
  border: 1.5px solid var(--bord);
  background: rgba(20, 22, 27, 0.94);
  color: var(--texte);
  font-size: 13.5px;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
}
#btn-evacuation { border-color: #b3454a; color: #ffd9db; }
#btn-entraide { border-color: #38bdf8; color: #d6f0ff; }
#btn-evacuation:hover, #btn-entraide:hover { filter: brightness(1.18); }

#badge-evacuation {
  min-height: 48px;
  padding: 10px 16px;
  border-radius: 16px;   /* « ⚠ ORDRE D'ÉVACUATION — à confirmer » : 2 lignes */
  border: 2px solid var(--rouge);
  background: #55131a;
  color: #ffe1e2;
  font-size: 14.5px;
  font-weight: 900;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.55);
}
#badge-evacuation.a-confirmer { animation: evac-pulse 1.25s ease-in-out infinite; }
@keyframes evac-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.65); }
  50% { box-shadow: 0 0 0 10px rgba(229, 72, 77, 0); }
}
#badge-entraide {
  min-height: 44px;
  padding: 8px 14px;
  border-radius: 14px;   /* « 🤝 n demande(s) d'entraide » : peut se replier */
  border: 1.5px solid #38bdf8;
  background: #10222e;
  color: #d6f0ff;
  font-size: 13.5px;
  font-weight: 800;
  cursor: pointer;
}
#badge-entraide.urgente { border-color: var(--ambre); background: #33280a; color: #ffe9a8; }

/* ---------- Barre flottante de tracé / de choix de point ----------
   Elle occupe le bas de la carte SANS recouvrir « ? Légende / ℹ » :
   même empilement que la barre citoyenne (--bas-commandes). */
.barre-flottante-bas {
  position: fixed;
  left: 50%;
  bottom: calc(var(--bas-commandes, 54px) + 52px);
  transform: translateX(-50%);
  z-index: 34;
  width: min(96vw, 560px);
  max-height: 46vh;
  max-height: 46dvh;   /* iOS : cf. #toasts */
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  background: var(--panneau);
  border: 1px solid var(--ambre);
  border-radius: var(--rayon);
  box-shadow: 0 6px 26px rgba(0, 0, 0, 0.65);
}
.barre-flottante-bas[hidden] { display: none; }
.bf-consigne { font-size: 13.5px; font-weight: 700; line-height: 1.4; }
.bf-rayon { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.bf-rayon input[type="range"] { flex: 1 1 auto; min-height: 32px; }
.bf-rayon output { min-width: 74px; text-align: right; font-weight: 800; }
.bf-compte {
  font-size: 12.5px;
  color: var(--texte-2);
  line-height: 1.4;
  min-height: 18px;
}
.bf-compte.bf-compte-plein { color: #ffe9a8; font-weight: 700; }
.bf-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.bf-actions button {
  flex: 1 1 150px;
  min-height: 46px;
  padding: 8px 14px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
.bf-actions button.bf-principal {
  background: var(--rouge);
  border-color: var(--rouge);
  color: #fff;
  font-weight: 800;
}
.bf-actions button:disabled { opacity: 0.5; cursor: not-allowed; }
/* Champs de saisie de la barre de marquage (B8) : ils remplacent window.prompt,
   qui s'annulait EN SILENCE quand le navigateur le bloque (PWA mobile). Mêmes
   cotes que .admin-form input pour un rendu déjà connu de l'utilisateur. */
.bf-saisie { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.bf-saisie > span { flex: 0 0 auto; font-weight: 700; }
.bf-saisie input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 42px;
  padding: 6px 10px;
  font-size: 14px;
  color: var(--texte);
  background: #171a21;
  border: 1px solid var(--bord);
  border-radius: 8px;
}
.bf-saisie[hidden] { display: none; }

/* ---------- Réception d'un ordre : panneau ROUGE plein écran ---------- */
.page-plein.evac-recu {
  z-index: 59;              /* au-dessus des autres pages, sous les toasts (60) */
  background: rgba(58, 10, 14, 0.985);
  border-color: var(--rouge);
  overflow-y: auto;
}
.evac-recu-tete h2 {
  font-size: 21px;
  letter-spacing: 0.04em;
  color: #ffe1e2;
}
.evac-recu-corps {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.evac-nav { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 12.5px; }
.evac-nav-btn {
  min-height: 36px;
  padding: 4px 10px;
  border-radius: 12px;   /* libellé de navigation : peut se replier */
  border: 1px solid #b3454a;
  background: #40121a;
  color: #ffd9db;
  font-weight: 700;
  cursor: pointer;
}
.evac-nav-btn.actif { background: var(--rouge); color: #fff; }
.evac-recu-consigne {
  margin: 0;
  font-size: 17px;
  font-weight: 900;
  line-height: 1.35;
  color: #fff;
}
.evac-message {
  border: 2px solid #ff8f92;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.28);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.evac-message-titre { font-size: 11.5px; letter-spacing: 0.08em; color: #ffb4b6; font-weight: 800; }
.evac-message-texte { margin: 0; font-size: 16px; font-weight: 700; line-height: 1.4; color: #fff; }
.evac-recu-motif { margin: 0; font-size: 13px; color: #ffd9db; }
.evac-direction {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1.5px solid #ffd166;
  border-radius: 10px;
  background: rgba(255, 209, 102, 0.10);
  padding: 10px 12px;
}
.evac-direction-inconnue { border-color: var(--bord); background: rgba(255, 255, 255, 0.05); }
.evac-fleche {
  font-size: 34px;
  line-height: 1;
  color: #ffd166;
  flex: 0 0 auto;
  display: inline-block;
}
.evac-direction-texte { font-size: 14.5px; font-weight: 700; line-height: 1.4; color: #ffeec2; }
.evac-direction-inconnue .evac-direction-texte { color: var(--texte-2); font-weight: 600; }
.evac-recu-axes {
  margin: 0;
  font-size: 14px;
  font-weight: 800;
  line-height: 1.4;
  color: #ffd9db;
}
.evac-recu-securite {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: #f3d3d4;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  padding-top: 8px;
}
.evac-recu-pied {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 11.5px;
  color: #e0b7b9;
}
.evac-recu-actions { flex: 0 0 auto; padding-top: 4px; }
.evac-accuse {
  width: 100%;
  min-height: 62px;
  border-radius: 12px;
  border: 2px solid #2ecc71;
  background: #10381f;
  color: #c9f3dc;
  font-size: 18px;
  font-weight: 900;
  cursor: pointer;
}
.evac-accuse:hover { filter: brightness(1.15); }
.evac-accuse.deja { border-color: var(--bord); background: #23262e; color: var(--texte-2); font-size: 15px; }
.evac-accuse:disabled { opacity: 0.55; cursor: wait; }

/* ---------- Outil opérateur : tracé, récapitulatif, suivi ---------- */
.evac-outil-corps, .entraide-corps {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 6px;
}
.evac-section, .ent-section { display: flex; flex-direction: column; gap: 8px; }
.evac-section h3, .ent-section h3 {
  margin: 6px 0 0;
  font-size: 14px;
  color: var(--jaune);
}
.ent-section h3.ent-titre-urgent { color: #7dd3fc; }
.evac-label, .ent-label { font-size: 12px; font-weight: 800; color: var(--texte-2); }
.evac-formes { display: flex; flex-wrap: wrap; gap: 8px; }
.evac-forme-btn {
  flex: 1 1 200px;
  min-height: 56px;
  border-radius: 10px;
  border: 1.5px solid #b3454a;
  background: #2a1418;
  color: #ffd9db;
  font-size: 15px;
  font-weight: 800;
  cursor: pointer;
}
.evac-forme-btn:hover { filter: brightness(1.18); }
.evac-cibles { display: flex; flex-wrap: wrap; gap: 6px; }
.evac-cible {
  min-height: 44px;
  padding: 6px 12px;
  border-radius: 14px;   /* nom de commune / de secteur : peut se replier */
  border: 1px solid var(--bord);
  background: #23262e;
  color: var(--texte-2);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
.evac-cible.actif { border-color: var(--jaune); background: #3a3213; color: #ffe9a8; }
.evac-champ, .ent-champ {
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--bord);
  background: #14161b;
  color: var(--texte);
  font: inherit;
  font-size: 14px;
}
textarea.evac-champ { min-height: 74px; resize: vertical; }
.evac-recap {
  border: 2px solid var(--rouge);
  border-radius: 10px;
  background: rgba(229, 72, 77, 0.10);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.evac-recap.evac-recap-vide { border-color: var(--bord); background: rgba(255, 255, 255, 0.04); }
.evac-recap-nombre { font-size: 18px; font-weight: 900; color: #ffd9db; }
.evac-recap.evac-recap-vide .evac-recap-nombre { color: var(--texte-2); }
.evac-recap-detail { font-size: 13px; color: var(--texte); }
.evac-recap-note { font-size: 11.5px; color: var(--texte-2); line-height: 1.4; }
/* F4 : frise des passages satellite (Analyses) — le SVG garde sa largeur
   reelle, le conteneur defile ; la page ne s'elargit jamais. */
.ana-graphe-defile { overflow-x: auto; padding-bottom: 4px; }
.ana-sous { display: block; margin-top: 4px; font-size: 11px; color: var(--texte-2); line-height: 1.4; }
/* Verdict C (zip Stitch nº 4) : ligne d'enjeux RNB/Filosofi du secteur. */
.evac-recap-rnb {
  font-size: 12px;
  color: var(--texte);
  line-height: 1.4;
  border-top: 1px dashed var(--bord);
  padding-top: 6px;
  margin-top: 2px;
}
.evac-actions-recap, .evac-ordre-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.evac-envoyer {
  flex: 1 1 100%;
  min-height: 56px;
  border-radius: 10px;
  border: 2px solid var(--rouge);
  background: var(--rouge);
  color: #fff;
  font-size: 16px;
  font-weight: 900;
  cursor: pointer;
}
.evac-envoyer:disabled { opacity: 0.55; cursor: wait; }
.evac-secondaire, .evac-lever {
  flex: 1 1 140px;
  min-height: 46px;
  padding: 6px 12px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
}
.evac-lever { border-color: #2ecc71; background: #10241a; color: #c9f3dc; }
.evac-ordre {
  border: 1px solid var(--bord);
  border-radius: 10px;
  background: var(--bloc);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.evac-ordre.actif { border-color: var(--rouge); }
.evac-ordre.leve { opacity: 0.72; }
.evac-ordre-tete { display: flex; flex-direction: column; gap: 2px; }
.evac-ordre-titre { font-size: 14.5px; font-weight: 800; }
.evac-ordre.actif .evac-ordre-titre { color: #ffb4b6; }
.evac-ordre-meta, .evac-ordre-cibles { font-size: 11.5px; color: var(--texte-2); }
.evac-ordre-motif, .evac-ordre-message { margin: 0; font-size: 13px; line-height: 1.4; }
.evac-ordre-message { font-weight: 700; }
.evac-jauge { display: flex; flex-direction: column; gap: 4px; }
.evac-jauge-barre {
  height: 10px;
  border-radius: 999px;
  background: #23262e;
  border: 1px solid var(--bord);
  overflow: hidden;
}
.evac-jauge-barre span {
  display: block;
  height: 100%;
  background: #2ecc71;
  transition: width 0.3s ease;
}
.evac-jauge-texte { font-size: 12px; color: var(--texte-2); }
.evac-liste-noms { margin: 0; font-size: 12px; color: var(--texte-2); line-height: 1.45; }
.evac-liste-accuses { color: #c9f3dc; }

/* ---------- Vue citoyenne : cartes d'ordre d'évacuation ---------- */
.citoyen-evac {
  border: 1.5px solid var(--bord);
  border-radius: 8px;
  background: #23262e;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-bottom: 6px;
}
.citoyen-evac.me-concerne { border-color: var(--rouge); background: #40121a; }
/* B4 : la LEVÉE de mon secteur — carte verte, lisible après rechargement */
.citoyen-evac.levee { border-color: #34c759; background: #10281a; }
.citoyen-evac.levee .citoyen-evac-titre { color: #b7f0c6; }
.citoyen-evac.levee .citoyen-evac-texte { color: #d9f5e0; }
.citoyen-evac-titre { font-size: 13px; font-weight: 900; color: var(--texte-2); }
.citoyen-evac.me-concerne .citoyen-evac-titre { color: #ffd9db; }
.citoyen-evac-texte { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--texte-2); }
.citoyen-evac.me-concerne .citoyen-evac-texte { color: #ffe1e2; }
.citoyen-evac-message { margin: 0; font-size: 13px; font-weight: 700; line-height: 1.4; }
.citoyen-evac-btn {
  min-height: 48px;
  border-radius: 8px;
  border: 2px solid var(--rouge);
  background: var(--rouge);
  color: #fff;
  font-size: 14.5px;
  font-weight: 900;
  cursor: pointer;
}

/* ---------- Entraide eau : panneau ---------- */
.ent-point { margin: 0; font-size: 13px; font-weight: 700; }
.ent-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.ent-btn {
  min-height: 46px;
  padding: 8px 14px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
}
.ent-btn:hover { filter: brightness(1.15); }
.ent-btn.ent-btn-large { width: 100%; border-color: #38bdf8; color: #d6f0ff; }
.ent-btn.ent-btn-mini { min-height: 42px; padding: 6px 10px; font-size: 12.5px; }
.ent-btn.ent-btn-mini.actif { border-color: #2ecc71; background: #10241a; color: #c9f3dc; }
.ent-btn.ent-oui { border-color: #2ecc71; background: #10241a; color: #c9f3dc; flex: 1 1 140px; font-weight: 800; }
.ent-btn.ent-non { border-color: var(--bord); flex: 1 1 140px; }
.ent-cand-liste { display: flex; flex-direction: column; gap: 6px; }
.ent-cand {
  border: 1px solid var(--bord);
  border-radius: 9px;
  background: var(--bloc);
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.ent-cand.ent-cand-intervenant { border-color: #f5a524; }
.ent-cand.ent-cand-tonne_lisier { border-color: #d0a35c; }
.ent-cand-tete { display: flex; align-items: center; gap: 8px; }
.ent-cand-emoji { font-size: 18px; flex: 0 0 auto; }
.ent-cand-nom { flex: 1 1 auto; font-size: 13.5px; font-weight: 800; }
.ent-cand-dist { flex: 0 0 auto; font-size: 12.5px; font-weight: 800; color: var(--jaune); }
.ent-cand-detail { font-size: 11.5px; color: var(--texte-2); line-height: 1.4; }
.ent-cand-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.ent-urgences { display: flex; gap: 8px; }
.ent-urgence {
  flex: 1 1 140px;
  min-height: 48px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #23262e;
  color: var(--texte-2);
  font-size: 14px;
  font-weight: 800;
  cursor: pointer;
}
.ent-urgence.actif { border-color: var(--jaune); background: #3a3213; color: #ffe9a8; }
.ent-urgence.ent-urgence-urgente.actif { border-color: var(--rouge); background: #55131a; color: #ffd9db; }
.ent-recap {
  border: 1.5px solid #38bdf8;
  border-radius: 10px;
  background: rgba(56, 189, 248, 0.09);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ent-recap-titre { font-size: 15px; font-weight: 900; color: #d6f0ff; }
.ent-recap-detail { font-size: 12px; color: var(--texte-2); line-height: 1.45; }
.ent-envoyer {
  width: 100%;
  min-height: 56px;
  border-radius: 10px;
  border: 2px solid #38bdf8;
  background: #10384d;
  color: #d6f0ff;
  font-size: 16px;
  font-weight: 900;
  cursor: pointer;
}
.ent-envoyer:disabled { opacity: 0.5; cursor: not-allowed; }
/* 0 compte notifiable : l'envoi reste permis (assumé) mais le bouton ne doit
   plus ressembler à l'envoi normal — l'œil bute dessus avant le pouce. */
.ent-envoyer.sans-notifiable {
  border-color: var(--jaune);
  background: #3a3213;
  color: #ffe9a8;
}
/* D1 — coordonnées « radio » : toujours là, mais en second et en petit. */
.ent-coords { margin: 0; font-size: 11px; color: var(--texte-2); }
/* ---------- Point conseillé (spec_eau) ---------- */
.ent-btn-conseil { border-color: var(--jaune); color: #ffe9a8; }
.ent-conseil { display: flex; flex-direction: column; gap: 6px; }
.ent-conseil:empty { display: none; }
.ent-conseil-refus { margin: 0; font-size: 12.5px; font-weight: 700; color: var(--jaune); }
.ent-conseil-avert { margin: 0; font-size: 12px; font-weight: 700; color: var(--jaune); }
.ent-cand.ent-conseil-cand { border-color: #38bdf8; }
/* La meilleure proposition : pré-sélectionnée, elle doit se voir comme telle. */
.ent-cand.ent-conseil-cand.preselection {
  border-color: var(--jaune);
  box-shadow: 0 0 0 1px var(--jaune) inset;
}
.ent-demande {
  border: 1px solid var(--bord);
  border-radius: 10px;
  background: var(--bloc);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.ent-demande.prioritaire { border-color: #38bdf8; background: #12222e; }
.ent-demande.urgente { border-left: 5px solid var(--ambre); }
.ent-demande.etat-terminee, .ent-demande.etat-annulee { opacity: 0.7; }
.ent-demande-tete { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.ent-demande-titre { flex: 1 1 auto; font-size: 14.5px; font-weight: 800; }
.ent-demande-etat { flex: 0 0 auto; font-size: 12px; font-weight: 800; color: var(--texte-2); }
.ent-demande-meta, .ent-demande-dests { margin: 0; font-size: 11.5px; color: var(--texte-2); line-height: 1.45; }
.ent-demande-note { margin: 0; font-size: 13px; line-height: 1.4; }
.ent-demande-accepte { margin: 0; font-size: 13px; font-weight: 800; color: #c9f3dc; }
.ent-demande-actions { display: flex; flex-wrap: wrap; gap: 6px; }

/* ---------- Entraide : entrée dans la page « 👥 Intervenants » ---------- */
.pi-entraide {
  border: 1px solid var(--bord);
  border-left: 5px solid #38bdf8;
  border-radius: 9px;
  background: #12222e;
  padding: 8px 10px;
  margin-bottom: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.pi-entraide.pi-entraide-urgent { border-color: #38bdf8; }
.pi-entraide-ligne { font-size: 12.5px; line-height: 1.45; color: #d6f0ff; }
.pi-entraide-btn {
  align-self: flex-start;
  min-height: 44px;
  padding: 6px 14px;
  border-radius: 9px;
  border: 1px solid #38bdf8;
  background: #10384d;
  color: #d6f0ff;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
}

/* Marqueur PROVISOIRE du marquage opérationnel (B8) : l'emplacement touché
   (ou « À ma position ») s'affiche AVANT la validation — même famille visuelle
   que le point d'entraide, en ambre pour dire « pas encore partagé ». */
.marq-provisoire {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 2px solid var(--ambre);
  background: #33280a;
  font-size: 17px;
  line-height: 1;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
}

/* ---------- Marqueurs carte de l'entraide ---------- */
.marq-entraide {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid #38bdf8;
  background: rgba(16, 34, 46, 0.95);
  color: #d6f0ff;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
}
.marq-entraide.marq-entraide-point { border-color: var(--jaune); background: #3a3213; width: 34px; height: 34px; font-size: 17px; }
.marq-cand-intervenant { border-color: #f5a524; background: rgba(58, 40, 10, 0.95); }
.marq-cand-tonne_lisier { border-color: #d0a35c; background: rgba(45, 36, 22, 0.95); }
.marq-cand-hydrant { border-color: #f43f5e; background: rgba(58, 16, 26, 0.95); }
.marq-cand-point_eau { border-color: #3b82f6; background: rgba(14, 28, 52, 0.95); }
/* Point sûr conseillé (spec_eau) : ⭐ — un repère de sécurité, pas un porteur. */
.marq-entraide.marq-conseil { border-color: var(--jaune); background: rgba(58, 50, 19, 0.95); }
.marq-demande.etat-en_attente { border-color: #38bdf8; animation: evac-pulse 1.6s ease-in-out infinite; }
.marq-demande.etat-acceptee, .marq-demande.etat-en_route { border-color: #2ecc71; background: rgba(16, 36, 26, 0.95); }
.marq-demande.urgente { border-width: 3px; border-color: var(--ambre); }

/* ---------- Légende : évacuation et entraide ---------- */
.leg-evac {
  flex: 0 0 auto;
  width: 26px;
  height: 16px;
  border-radius: 3px;
}
.leg-evac-actif {
  border: 2px solid #ff4d4f;
  background: repeating-linear-gradient(45deg,
    rgba(255, 77, 79, 0.85) 0 3px, rgba(229, 72, 77, 0.12) 3px 7px);
}
.leg-evac-leve { border: 2px dashed #8b93a1; background: transparent; }
.leg-evac-brouillon { border: 2px dashed #ffb020; background: rgba(255, 176, 32, 0.14); }
.leg-entraide {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid #38bdf8;
  background: rgba(16, 34, 46, 0.95);
  font-size: 12px;
}
.leg-entraide-tonne { border-color: #f5a524; background: rgba(58, 40, 10, 0.95); }
.leg-entraide-eau { border-color: #3b82f6; background: rgba(14, 28, 52, 0.95); }

/* ---------- Mobile étroit (375 px) ---------- */
@media (max-width: 430px) {
  .citoyen-situation-choix { grid-template-columns: 1fr; }
  .citoyen-position, .citoyen-sos { flex: 1 1 100%; }
  /* Même bornage mesuré que la règle de base : 58vh seul laisserait la barre
     repasser sur l'en-tête en paysage. */
  #barre-citoyen {
    width: 96vw;
    padding: 8px 10px;
    max-height: min(58vh, calc(100vh - var(--y-bandeau) - var(--haut-bandeau, 0px)
                               - var(--bas-commandes, 54px) - 52px - 8px));
    max-height: min(58dvh, calc(100dvh - var(--y-bandeau) - var(--haut-bandeau, 0px)
                               - var(--bas-commandes, 54px) - 52px - 8px));
  }
  .admin-onglet { flex: 1 1 100%; }
  .admin-champ { flex: 1 1 100%; }
  #liste-comptes .compte-actions select,
  #liste-comptes .compte-actions .compte-equipe { flex: 1 1 100%; }
  /* Évacuation / entraide : tout passe sur une colonne, cibles ≥ 44 px */
  .barre-flottante-bas {
    width: 96vw; padding: 8px 10px;
    max-height: 42vh;
    max-height: 42dvh;   /* iOS : cf. #toasts */
  }
  .bf-actions button { flex: 1 1 100%; }
  /* Les deux actions de secteur passent l'une sous l'autre, comme les autres
     boutons de la barre : côte à côte, 375 px ne laisse que ~90 px chacun et le
     libellé partirait sur trois lignes. */
  .barre-actions-terrain { flex-direction: column; align-self: stretch; }
  #btn-evacuation, #btn-entraide { width: 100%; font-size: 13px; }
  .evac-forme-btn { flex: 1 1 100%; }
  .evac-secondaire, .evac-lever { flex: 1 1 100%; }
  .ent-urgences { flex-wrap: wrap; }
  .ent-urgence { flex: 1 1 100%; }
  .ent-btn.ent-oui, .ent-btn.ent-non { flex: 1 1 100%; }
  .evac-recu-consigne { font-size: 16px; }
  .evac-message-texte { font-size: 15px; }
  .evac-accuse { min-height: 58px; font-size: 16.5px; }
  #badge-evacuation { font-size: 13px; padding: 9px 12px; }
}

/* ---------- IMAGERIE SATELLITE EUMETSAT (contexte) -----------------------------
   Cinq couches d'AIDE À LA LECTURE regroupées dans un bloc repliable du panneau
   « Couches » : elles ne doivent jamais occuper la place des couches
   opérationnelles. Bloc entièrement dans le flux du panneau latéral — aucun
   élément flottant, donc rien à réserver sur --bas-securite / --bas-commandes. */
.couches-sat {
  margin: 8px 0 4px;
  background: var(--bloc);
  border: 1px solid var(--bord);
  border-radius: var(--rayon);
  padding: 6px 10px;
}
.couches-sat summary {
  cursor: pointer;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--texte-2);
  font-weight: 700;
  min-height: 34px;
  display: flex;
  align-items: center;
}
.couches-sat[open] summary { margin-bottom: 4px; }
.couches-sat .ligne-couche { min-height: 40px; font-size: 12.5px; font-weight: 600; }
.couches-sat .indice { margin: 2px 0 6px; }

/* Légende : les entrées satellite portent une vignette d'échelle servie par le
   serveur EUMETSAT — le pictogramme se cale en haut, pas au milieu du bloc. */
.leg-emoji {
  width: 22px;
  text-align: center;
  font-size: 16px;
  align-self: flex-start;
  text-shadow: 0 0 3px #14161b;
}
.leg-avert {
  width: 22px;
  text-align: center;
  font-size: 16px;
  align-self: flex-start;
  color: #ffd60a;
}
.leg-wms {
  display: block;
  margin: 5px 0 0;
  max-width: 100%;
  height: auto;
  background: #f4f5f7;   /* les légendes WMS ont un texte sombre sur fond transparent */
  border: 1px solid var(--bord);
  border-radius: 4px;
  padding: 3px;
}
@media (max-width: 520px) {
  .couches-sat .ligne-couche { min-height: 44px; }
  .leg-wms { max-height: 150px; object-fit: contain; object-position: left top; }
}

/* ================================================================
   CENTRE DE MESSAGES (frontend/messages.js)

   Le client a demandé cinq fois, captures à l'appui, qu'on cesse
   d'empiler les bandeaux. La règle est désormais STRUCTURELLE :
   AU PLUS UN BANDEAU VISIBLE, tout le reste dans le centre.

   Géométrie MESURÉE et non figée : --haut-entete (publiée par
   calerBarreActions) et --haut-bandeau (publiée par messages.js)
   décrivent la pile haute. Aucun `top` en pixels ne peut suivre un
   en-tête qui gagne une ligne d'alerte de fraîcheur.
   ================================================================ */

:root {
  --haut-bandeau: 0px;   /* hauteur du bandeau unique, gap compris (0 = masqué) */
  --haut-pile: 120px;    /* bas de la pile haute, mesuré */
  /* Bande verticale libre sous l'en-tête : point d'ancrage unique de tout ce
     qui flotte en haut. max() protège du cas où l'en-tête est plus court que
     le bouton du panneau (46 px) — le bandeau passerait alors dessus.
     --marge-haute : #entete et #btn-panneau sont eux-mêmes descendus de la zone
     sûre de l'encoche. Sans la reporter ici, TOUT ce qui se cale sur
     --y-bandeau (bandeau unique, barre d'actions, colonne de toasts en vue
     citoyenne) remontait de 47 px et repassait par-dessus l'en-tête. */
  --y-bandeau: calc(var(--marge-haute, 0px) + 10px
                    + max(var(--haut-entete, 54px), 46px) + 8px);
}
/* MODE DÉMONSTRATION : le bandeau rayé occupe le haut de l'écran et pousse
   l'en-tête vers le bas (--h-demo). La bande du bandeau unique et de la barre
   d'actions descend d'autant — sans cette ligne, la barre d'actions revenait
   se poser SUR l'en-tête pendant un rejeu. */
body.demo-active {
  --y-bandeau: calc(var(--marge-haute, 0px) + var(--h-demo, 92px) + 10px
                    + max(var(--haut-entete, 54px), 46px) + 8px);
}

/* ----- Bouton du centre, DANS l'en-tête -----
   PAS de `position: relative` ici : #entete est déjà `position: fixed` plus
   haut dans la feuille, et un élément fixe est DÉJÀ le bloc conteneur de ses
   descendants absolus. Le redéclarer `relative` l'aurait décollé du coin haut
   droit — c'est exactement le genre de régression qu'on ne voit qu'à la
   mesure des rectangles. */
#entete.avec-messages {
  padding-left: 58px;
  min-height: 56px;
  box-sizing: border-box;
}
#btn-centre-messages {
  position: absolute;
  left: 8px;
  top: 8px;
  /* 44 px, pas 42 : c'est le seuil tactile, et c'est le bouton qui donne accès à
     TOUS les messages qu'une bulle a déjà fait disparaître. Deux pixels sous le
     seuil, avec des gants, cela se manque. */
  width: 44px;
  height: 44px;
  border-radius: 12px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte);
  font-size: 18px;
  cursor: pointer;
  padding: 0;
}
#btn-centre-messages[hidden] { display: none; }
#btn-centre-messages:hover { filter: brightness(1.2); }
.cm-icone { display: block; line-height: 1; }
.cm-pastille {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 999px;
  background: #3a4150;
  border: 1.5px solid #0e1014;
  color: #fff;
  font-size: 11.5px;
  font-weight: 800;
  line-height: 17px;
  text-align: center;
}
.cm-pastille[hidden] { display: none; }
/* Couleur de la pastille = niveau LE PLUS ÉLEVÉ présent. On lit l'urgence
   sans ouvrir : c'est tout l'intérêt d'un compteur unique. */
#btn-centre-messages.cm-niveau-info .cm-pastille { background: #2a4d8f; }
#btn-centre-messages.cm-niveau-important .cm-pastille { background: #b46b00; }
#btn-centre-messages.cm-niveau-important { border-color: var(--ambre); }
#btn-centre-messages.cm-niveau-critique .cm-pastille { background: #b3121a; }
#btn-centre-messages.cm-niveau-critique {
  border-color: var(--rouge);
  animation: cm-pulse 1.5s ease-in-out infinite;
}
@keyframes cm-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.55); }
  50% { box-shadow: 0 0 0 7px rgba(229, 72, 77, 0); }
}

/* ----- LE bandeau unique : UNE ligne, 34 px au plus ----- */
#bandeau-messages {
  position: fixed;
  top: var(--y-bandeau);
  /* ZONES SÛRES LATÉRALES — mesuré le 30/07 à 812 × 375 (iPhone en PAYSAGE).
     En paysage l'encoche n'est plus en haut : elle prend 47 px À GAUCHE ET À
     DROITE. Avec `left: 10px; right: 10px`, le contenu de ce bandeau s'étendait
     de x = 18 à x = 794, pour une zone atteignable de 47 à 765 : le DÉBUT et la
     FIN d'un message critique passaient sous l'encoche et les coins arrondis.
     C'est le bandeau des messages CRITIQUES, et depuis le plancher BULLE_MINI
     de messages.js c'est le SEUL canal encore affiché en paysage sur téléphone
     (une bulle y serait rabotée à 14 px, donc supprimée) : il ne peut pas se
     permettre de perdre ses deux extrémités. */
  left: calc(10px + var(--marge-gauche, 0px));
  right: calc(10px + var(--marge-droite, 0px));
  z-index: 45;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding: 0 8px;
  /* RAYON FIXE, pas 999px. La hauteur est bornée en dur (34 px, 32 px sur
     téléphone) : un 999px serait donc légitime AUJOURD'HUI. Mais c'est
     exactement l'hypothèse qui a lâché sur #maj-dispo — le jour où quelqu'un
     remplace `height` par `min-height` pour faire tenir un libellé, le bandeau
     redevient une ellipse. 17 px = la moitié de 34 : le rendu est celui d'une
     pilule, et la géométrie ne dépend plus d'aucune promesse. */
  border-radius: 17px;
  border: 1.5px solid var(--ambre);
  background: rgba(62, 42, 8, 0.97);
  color: #ffe1a8;
  box-shadow: 0 3px 14px rgba(0, 0, 0, 0.55);
  overflow: hidden;
  box-sizing: border-box;
}
#bandeau-messages[hidden] { display: none; }
#bandeau-messages.niveau-critique {
  border-color: var(--rouge);
  background: #7a1119;
  color: #ffe3e4;
  animation: cm-pulse 1.5s ease-in-out infinite;
}
/* ═══ AUCUNE TRONCATURE SUR UN MESSAGE CRITIQUE — mesure du 06/08/2026 ═══
   Le bandeau est une PILULE D'UNE LIGNE, et c'est le bon choix pour une info
   ou un avertissement. Mais au niveau CRITIQUE il porte la phrase qui dit que
   la carte affichée est fausse, par exemple :
     « 🚒 duo-b a engagé la lutte sur le foyer B (palier « non précisé ») à
       21h55 — La prévision affichée ne le prend pas en compte : relancez. »
   Mesuré : 918 px de texte dans 839 px de place à 1 280 px de large, et
   57 caractères sur 136 à 390 px — soit 69 % du message masqué. Le mot
   « relancez », c'est-à-dire la seule ACTION demandée, était invisible.
   Le dépôt pose « aucune troncature ». La hauteur du bandeau est MESURÉE et
   publiée dans --haut-bandeau (messages.js) : tout ce qui se cale dessous suit
   donc l'agrandissement, sans réglage en dur à retoucher.
   Le rayon de 17 px est fixe et non `999px` — précisément pour que ce cas-là ne
   transforme pas le bandeau en ellipse (voir le commentaire de #bandeau-messages).
   Le niveau critique est rare et non réductible : ce n'est pas lui qui encombre. */
#bandeau-messages.niveau-critique {
  height: auto;
  min-height: 34px;
  padding-top: 5px;
  padding-bottom: 5px;
  align-items: flex-start;
}
#bandeau-messages.niveau-critique .bm-emoji,
#bandeau-messages.niveau-critique .bm-action { margin-top: 1px; }
#bandeau-messages.niveau-critique .bm-texte {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  line-height: 1.3;
  padding: 2px 0;
}
.bm-emoji { flex: 0 0 auto; font-size: 15px; }
/* Le texte est TRONQUÉ, jamais replié : une deuxième ligne, c'est un
   bandeau qui grandit, et c'est exactement ce qu'on supprime ici.
   ⚠ SAUF AU NIVEAU CRITIQUE — voir `#bandeau-messages.niveau-critique` plus
   bas : un message qui dit que la carte affichée est FAUSSE ne se tronque pas. */
.bm-texte {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
  background: none;
  border: none;
  color: inherit;
  font-size: 12.5px;
  font-weight: 800;
  cursor: pointer;
  padding: 0;
}
.bm-action {
  flex: 0 0 auto;
  /* 28 px, pas 26 : avec l'extension tactile de 9 px (voir plus bas) la bande
     sensible fait 28 + 18 = 46 px. À 26 px elle tombait à 44 px PILE et le test
     de toucher au pixel entier n'en retrouvait que 42, la position sous-pixel du
     bandeau mangeant les deux bords. 28 tient dans les 31 px intérieurs du
     bandeau d'ordinateur (34 − 2 × 1,5 de bordure). */
  min-height: 28px;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid currentColor;
  background: rgba(0, 0, 0, 0.35);
  color: inherit;
  font-size: 11.5px;
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
}
.bm-action:hover { background: rgba(0, 0, 0, 0.6); }
.bm-reduire {
  flex: 0 0 auto;
  width: 28px;    /* même raison que .bm-action : 28 + 18 = 46 px de bande sensible */
  height: 28px;
  border-radius: 999px;
  border: 1px solid currentColor;
  background: transparent;
  color: inherit;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
  padding: 0;
}
.bm-verrou { flex: 0 0 auto; width: 2px; }

/* CIBLE TACTILE DE 44 PX DANS UN BANDEAU DE 32 — le compromis, expliqué.
   Le contrat demande deux choses incompatibles À LA LETTRE : un bandeau d'au
   plus 32 px de haut sur téléphone, et un bouton d'action d'au moins 44 px.
   Un bouton de 44 px n'entre pas dans une boîte de 32.
   On tient les deux en séparant la SURFACE PEINTE de la SURFACE SENSIBLE : le
   bouton se DESSINE à l'intérieur du bandeau (28 px sur ordinateur, 29 px sur
   téléphone) et un pseudo-élément étend sa zone de TOUCHER de 9 px au-dessus et
   9 px en dessous — 28 + 18 = 46, 29 + 18 = 47. L'œil voit une ligne fine ;
   le doigt ganté vise 44 px et plus.
   Ces 9 px débordent du bandeau de 5 à 7,5 px, et ils ne volent le toucher de
   PERSONNE : le bandeau est seul dans sa bande, avec 8 px de vide au-dessus
   (--y-bandeau réserve 8 px sous l'en-tête) et 8 px en dessous
   (--haut-bandeau = hauteur mesurée + 8).
   `overflow: visible` sur le bandeau est OBLIGATOIRE — avec `hidden`, la partie
   débordante serait découpée, y compris pour le test de toucher, et l'extension
   ne servirait à rien. La troncature du texte n'en dépend pas : `.bm-texte` a
   son propre `overflow: hidden` + `text-overflow: ellipsis`.
   Déclaré ICI, hors de toute media query : une tablette Android en paysage fait
   915 px de large, elle passe donc par la branche « ordinateur » — et on la
   touche avec les doigts, pas avec une souris. */
#bandeau-messages { overflow: visible; }
.bm-action, .bm-reduire { position: relative; }
.bm-action::after, .bm-reduire::after {
  content: '';
  position: absolute;
  left: -4px;
  right: -4px;
  top: -9px;
  bottom: -9px;
}

/* Sur ORDINATEUR, le panneau de commande occupe la colonne de gauche :
   le bandeau commence après lui, sinon il le recouvrait. */
@media (min-width: 901px) {
  /* Un téléphone Android en paysage fait 915 px de large et tombe donc dans
     cette règle « ordinateur » : les zones sûres latérales doivent y être
     reportées aussi, sinon la correction ci-dessus ne couvre que les iPhone. */
  #bandeau-messages {
    left: calc(372px + var(--marge-gauche, 0px));
    right: calc(12px + var(--marge-droite, 0px));
  }
  body.panneau-ferme #bandeau-messages { left: calc(64px + var(--marge-gauche, 0px)); }
  /* La barre d'actions descend d'autant : c'est LA raison pour laquelle
     --haut-bandeau est mesurée. Elle remplace aussi l'ancien `top: 64px`
     figé, qui passait sous l'en-tête des qu'il gagnait une ligne. */
  #barre-intervention {
    top: calc(var(--y-bandeau) + var(--haut-bandeau, 0px));
    /* La barre n'est plus dans la bande de l'en-tête (elle est DESSOUS) : elle
       n'a plus à lui réserver --largeur-entete et récupère toute la largeur.
       Sans cela, le bouton « 🤝 Demandes » restait hors écran, atteignable au
       seul défilement horizontal — donc invisible en pratique. */
    max-width: calc(100vw - 372px - 24px);
    /* … et elle a le droit de passer à la ligne. La règle « une seule ligne
       défilante » avait été prise quand la barre portait AUSSI les bandeaux
       (SOS, réseau, alertes, évacuation, entraide) : empilés, ils mangeaient la
       carte. Ces bandeaux sont partis au centre de messages ; il ne reste que
       cinq COMMANDES, et une commande qu'on ne voit pas — le bouton
       « 🤝 Demandes » sortait de l'écran — ne sert à personne. Deux rangées de
       42 px sur un écran de bureau coûtent moins qu'un bouton introuvable. */
    flex-wrap: wrap;
    row-gap: 5px;
    overflow-x: visible;
  }
  body.panneau-ferme #barre-intervention {
    max-width: calc(100vw - 64px - 24px);
  }
}
@media (max-width: 900px) {
  /* MOBILE : 32 px au plus, comme demandé — hauteur EXACTE, pas un minimum.
     Le rayon suit (16 = 32 / 2) pour garder l'aspect pilule sans dépendre du
     999px. */
  #bandeau-messages {
    height: 32px;
    border-radius: 16px;
    font-size: 12px;
    overflow: visible;   /* voir la cible tactile de 44 px, plus bas */
  }
  /* SUR TÉLÉPHONE AUSSI, un message critique se lit EN ENTIER. C'est même là
     que la troncature était la pire : 57 caractères sur 136 à 390 px de large.
     La règle « 32 px au plus » vaut pour un bandeau qui informe, pas pour celui
     qui annonce que la carte affichée est fausse — et c'est précisément l'écran
     où l'opérateur n'a aucun autre canal (en paysage, les bulles sont écartées
     par BULLE_MINI, le bandeau est le SEUL qui reste). */
  #bandeau-messages.niveau-critique { height: auto; min-height: 32px; }
  .bm-texte { font-size: 12px; }
  /* Le bouton occupe TOUTE la hauteur intérieure du bandeau (32 − 2 × 1,5 de
     bordure = 29 px) : avec l'extension de 9 px déclarée plus haut, la bande
     sensible fait 47 px et le test de toucher au pixel entier la retrouve
     franchement, sans dépendre de la position sous-pixel du bandeau. */
  .bm-action, .bm-reduire { min-height: 29px; height: 29px; }
  .bm-action { padding: 0 12px; }
  .bm-reduire { width: 29px; }
}

/* ----- Panneau du centre ----- */
#centre-messages {
  position: fixed;
  z-index: 59;
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: rgba(14, 16, 20, 0.985);
  border: 1px solid var(--bord);
  border-radius: 14px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.75);
  padding: 10px 12px 12px;
  overflow: hidden;
}
#centre-messages[hidden] { display: none; }
@media (min-width: 901px) {
  #centre-messages {
    top: var(--y-bandeau);
    right: 12px;
    width: min(94vw, 430px);
    max-height: min(74vh, 620px);
    max-height: min(74dvh, 620px);   /* iOS : cf. #toasts */
  }
}
@media (max-width: 900px) {
  /* Feuille basse PLEIN ÉCRAN : sur téléphone, un panneau flottant de plus
     serait une régression. Respect de --bas-securite / --bas-commandes. */
  #centre-messages {
    left: 0;
    right: 0;
    bottom: 0;
    top: 30%;
    border-radius: 16px 16px 0 0;
    padding-bottom: calc(var(--bas-securite, 62px) + 10px);
  }
}
.cm-tete {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.cm-tete h2 { margin: 0; font-size: 16px; }
.cm-tete-actions { display: flex; gap: 6px; align-items: center; }
.cm-btn-tete {
  min-height: 44px;
  padding: 6px 10px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.cm-fermer {
  min-width: 46px;
  min-height: 46px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 16px;
  font-weight: 800;
  cursor: pointer;
}
.cm-liste {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.cm-liste:focus { outline: none; }
.cm-vide { color: var(--texte-2); font-size: 13px; margin: 12px 4px; }
.cm-pied {
  flex: 0 0 auto;
  margin: 0;
  color: var(--texte-2);
  font-size: 11px;
  line-height: 1.35;
}
.cm-msg {
  border: 1px solid var(--bord);
  border-left-width: 4px;
  border-radius: 10px;
  background: var(--bloc);
  padding: 7px 10px 8px;
}
.cm-msg.niveau-info { border-left-color: #2a4d8f; }
.cm-msg.niveau-important { border-left-color: var(--ambre); }
.cm-msg.niveau-critique { border-left-color: var(--rouge); }
.cm-msg.lu { opacity: 0.62; }
.cm-msg-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 10.5px;
  font-weight: 800;
  color: var(--texte-2);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.cm-msg-titre { margin: 3px 0 0; font-size: 13px; font-weight: 700; line-height: 1.3; }
.cm-msg-texte { margin: 3px 0 0; font-size: 12px; color: var(--texte-2); line-height: 1.35; }
.cm-msg-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.cm-msg-btn {
  min-height: 44px;
  padding: 6px 12px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
}
.cm-msg-btn.principal { background: #1d3a24; border-color: #2ecc71; color: #c9f3dc; }
.cm-msg-btn.discret { background: transparent; color: var(--texte-2); }
.cm-msg-btn:hover { filter: brightness(1.18); }

/* L'indicateur de calcul se cale SOUS toute la pile haute mesurée : c'est le
   dernier élément qui pouvait encore chevaucher la barre d'actions. */
#calc-carte { top: calc(var(--haut-pile, 120px) + 10px); }

/* ================================================================
   COUVERTURE D'UNE DEMANDE D'ENTRAIDE (A bis point 3)
   « 12 / 20 m³ engagés — 2 engins » et la liste des contributeurs.
   La phrase vient du SERVEUR : l'interface ne recalcule JAMAIS la
   jauge de son côté (elle mentirait au premier arrondi).
   ================================================================ */

.ent-couverture { margin: 6px 0 2px; }
.ent-jauge {
  position: relative;
  height: 14px;
  border-radius: 999px;
  background: #1c2029;
  border: 1px solid var(--bord);
  overflow: hidden;
}
.ent-jauge-remplie {
  position: absolute;
  inset: 0 auto 0 0;
  background: linear-gradient(90deg, #2a6ed4, #2ecc71);
  transition: width 0.25s ease;
}
/* Part DÉJÀ LIVRÉE : l'eau qui est dans la cuve ne se compare pas à une
   promesse. Deux teintes, une seule barre. */
.ent-jauge-livree {
  position: absolute;
  inset: 0 auto 0 0;
  background: #1f8f4d;
}
.ent-jauge.complete .ent-jauge-remplie { background: linear-gradient(90deg, #1f8f4d, #2ecc71); }
.ent-jauge.vide { border-color: var(--ambre); }
.ent-couverture-txt {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin: 4px 0 0;
  font-size: 12px;
  font-weight: 800;
  color: var(--texte);
}
.ent-couverture-restant { color: var(--ambre); }
.ent-couverture-ok { color: #7ee2a8; }
.ent-contributeurs { margin: 5px 0 0; display: flex; flex-direction: column; gap: 4px; }
.ent-contrib {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  padding: 4px 8px;
  border-radius: 8px;
  background: #1c2029;
  border: 1px solid var(--bord);
  font-size: 12px;
}
.ent-contrib.etat-livre { border-color: #2ecc71; }
.ent-contrib.etat-annule { opacity: 0.55; text-decoration: line-through; }
.ent-contrib-nom { font-weight: 800; }
.ent-contrib-part { color: #7ee2a8; font-weight: 800; }
.ent-contrib-equipage { color: var(--texte-2); font-size: 11.5px; }
.ent-contrib-etat { color: var(--texte-2); font-size: 11.5px; }
.ent-avert {
  margin: 4px 0 0;
  padding: 4px 8px;
  border-radius: 8px;
  border: 1px dashed var(--ambre);
  background: rgba(255, 159, 10, 0.10);
  color: #ffe1a8;
  font-size: 11.5px;
  font-weight: 600;
}

/* ================================================================
   CONSOLE D'ENTRAIDE (frontend/entraide-console.js) — page plein
   écran sur le modèle de la page « Intervenants ».
   ================================================================ */

/* 999px LÉGITIME par (b) : nowrap ajouté (compteur, mais rien ne garantissait
   qu'il reste d'un seul jeton). */
.ce-pastille {
  display: inline-block;
  min-width: 19px;
  margin-left: 5px;
  padding: 0 5px;
  border-radius: 999px;
  background: #3a4150;
  color: #fff;
  font-size: 11px;
  font-weight: 800;
  line-height: 17px;
  text-align: center;
  white-space: nowrap;
}
.ce-pastille.attente { background: #b46b00; }
/* Au-delà de 3 minutes sans réponse, la pastille passe en ORANGE VIF et
   pulse : une demande d'eau sans réponse est un engin qui n'arrive pas. */
.ce-pastille.tardive {
  background: #ff8c00;
  color: #14161b;
  animation: cm-pulse 1.5s ease-in-out infinite;
}
.ce-compteurs { flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 6px; }
.ce-compteur {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 84px;
  min-height: 48px;
  padding: 4px 10px;
  border-radius: 10px;
  border: 1px solid var(--bord);
  background: var(--bloc);
  color: var(--texte-2);
  font-size: 10.5px;
  font-weight: 700;
}
.ce-compteur b { font-size: 17px; color: var(--texte); }
.ce-compteur.alerte { border-color: var(--ambre); }
.ce-compteur.alerte b { color: var(--ambre); }
.ce-liste {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-right: 2px;
}
.ce-demande {
  border: 1px solid var(--bord);
  border-left-width: 4px;
  border-radius: 11px;
  background: var(--bloc);
  padding: 8px 10px 9px;
}
.ce-demandé.sans-réponse { border-left-color: var(--ambre); }
.ce-demandé.urgente { border-left-color: var(--rouge); }
.ce-demandé.terminee { opacity: 0.6; }
.ce-tete {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 10px;
}
.ce-titre { font-size: 14px; font-weight: 800; }
.ce-etat { font-size: 12px; font-weight: 800; color: var(--texte-2); }
.ce-meta { margin: 3px 0 0; font-size: 12px; color: var(--texte-2); line-height: 1.35; }
.ce-meta a { color: #8ab4ff; font-weight: 700; }
.ce-alerte-tardive {
  margin: 5px 0 0;
  padding: 4px 8px;
  border-radius: 8px;
  background: rgba(255, 140, 0, 0.14);
  border: 1px dashed #ff8c00;
  color: #ffd9a8;
  font-size: 11.5px;
  font-weight: 800;
}
.ce-dests { margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 4px; }
.ce-dest {
  padding: 3px 8px;
  border-radius: 10px;   /* nom de destinataire : longueur libre */
  border: 1px solid var(--bord);
  background: #1c2029;
  font-size: 11.5px;
  color: var(--texte-2);
}
.ce-dest.accepte { border-color: #2ecc71; color: #c9f3dc; }
.ce-dest.refuse { border-color: var(--rouge); color: #ffd9db; }
.ce-dest.vu { border-color: #2a4d8f; color: #cfe0ff; }
.ce-dest.retire { opacity: 0.5; text-decoration: line-through; }
.ce-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.ce-btn {
  min-height: 44px;
  padding: 6px 12px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
}
.ce-btn.principal { background: #1d3a24; border-color: #2ecc71; color: #c9f3dc; }
.ce-btn.danger { background: #55131a; border-color: var(--rouge); color: #ffd9db; }
.ce-btn:hover { filter: brightness(1.18); }
.ce-btn[disabled] { opacity: 0.5; cursor: not-allowed; }
.ce-formulaire {
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: 10px;
  border: 1px dashed var(--ambre);
  background: rgba(255, 159, 10, 0.07);
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.ce-formulaire h4 { margin: 0; font-size: 12.5px; color: var(--ambre); }
.ce-champ { display: flex; flex-direction: column; gap: 3px; font-size: 12px; }
.ce-champ input, .ce-champ select, .ce-champ textarea {
  min-height: 40px;
  padding: 6px 9px;
  border-radius: 8px;
  border: 1px solid #3a4150;
  background: #1c2029;
  color: var(--texte);
  font-size: 13px;
}
.ce-cands { display: flex; flex-direction: column; gap: 4px; max-height: 210px; overflow-y: auto; }
.ce-cand {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-radius: 8px;
  background: #1c2029;
  border: 1px solid var(--bord);
  font-size: 12px;
  cursor: pointer;
}
.ce-cand input { width: 20px; height: 20px; flex: 0 0 auto; }
.ce-cand-detail { color: var(--texte-2); font-size: 11.5px; }
.ce-avert-retrait {
  padding: 6px 9px;
  border-radius: 8px;
  background: #55131a;
  border: 1px solid var(--rouge);
  color: #ffd9db;
  font-size: 12px;
  font-weight: 700;
}
.ce-vide { color: var(--texte-2); font-size: 13px; margin: 14px 4px; }

/* ================================================================
   PARCOURS D'ACCUEIL (frontend/accueil.js + accueil-contenu.js)
   Surlignage de la cible, bulle, « Précédent / Suivant / Passer ».
   ================================================================ */

#accueil-halo {
  position: fixed;
  z-index: 81;
  border-radius: 12px;
  border: 3px solid var(--jaune);
  box-shadow: 0 0 18px 4px rgba(255, 214, 10, 0.7);
  pointer-events: none;
}
#accueil-halo[hidden] { display: none; }
/* ASSOMBRISSEMENT AUTOUR DE LA CIBLE — quatre rectangles pleins, et surtout
   PAS une ombre portée de 9999 px. Cette ombre géante (technique courante du
   « trou dans un voile ») faisait repeindre une surface gigantesque à chaque
   déplacement du halo : sur une page qui porte déjà une carte WebGL, chaque
   étape du parcours coûtait près d'une seconde. Quatre rectangles opaques se
   composent, eux, sans effort. */
.accueil-voile {
  position: fixed;
  z-index: 80;
  background: rgba(6, 8, 12, 0.62);
  pointer-events: none;
}
.accueil-voile[hidden] { display: none; }
#accueil-bulle {
  position: fixed;
  z-index: 82;
  width: min(94vw, 420px);
  background: var(--panneau);
  border: 1.5px solid var(--jaune);
  border-radius: 14px;
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.8);
  padding: 12px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-sizing: border-box;
}
#accueil-bulle[hidden] { display: none; }
.ac-compteur {
  font-size: 11px;
  font-weight: 800;
  color: var(--texte-2);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.ac-titre { margin: 0; font-size: 16px; font-weight: 800; color: var(--jaune); }
.ac-texte { margin: 0; font-size: 13.5px; line-height: 1.42; color: var(--texte); }
.ac-actions { display: flex; flex-wrap: wrap; gap: 7px; justify-content: flex-end; }
.ac-btn {
  min-height: 44px;
  min-width: 44px;
  padding: 8px 14px;
  border-radius: 10px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
}
.ac-btn.principal { background: #4a3a08; border-color: var(--jaune); color: var(--jaune); }
.ac-btn.discret { background: transparent; color: var(--texte-2); }
.ac-btn:hover { filter: brightness(1.2); }
@media (max-width: 900px) {
  /* Feuille basse : la bulle ne court jamais après la cible sur téléphone. */
  #accueil-bulle {
    left: 0 !important;
    right: 0;
    top: auto !important;
    bottom: 0;
    width: auto;
    border-radius: 16px 16px 0 0;
    padding-bottom: calc(var(--bas-securite, 62px) + 8px);
  }
}

/* Les toasts de la VUE CITOYENNE remontent au-dessus de la barre citoyenne,
   dont la hauteur est mesurée par messages.js (--bas-citoyen). Un message
   fugace ne doit jamais recouvrir le bouton SOS de quelqu'un. */
body.role-citoyen #toasts {
  bottom: calc(var(--bas-citoyen, 300px) + 4px);
}

/* Barre d'actions repliée tant qu'elle n'a AUCUN enfant visible (classe posée
   par messages.js). C'est le cas de la vue citoyenne : sans cela, une boîte
   vide de 8 px traversait la carte du haut en bas sur téléphone. */
#barre-intervention.barre-vide { display: none; }

@media (max-width: 900px) {
  /* VUE CITOYENNE SUR TÉLÉPHONE — les commandes « ? Légende » et « i », calées
     à mi-hauteur du bord droit pour les autres rôles, tombaient sur la barre
     citoyenne (consigne de sécurité, position, situation, SOS).

     ELLES DESCENDENT SOUS LA BARRE, elles ne remontent PAS au-dessus. La
     tentative précédente les posait à `--bas-citoyen + 4px`, c'est-à-dire juste
     au-dessus de la barre — en supposant que celle-ci « occupe la moitié basse
     de l'écran ». MESURE À 375 × 812 (compte citoyen) : la barre est en fait
     ancrée JUSTE SOUS la pile haute et descend jusqu'à 58vh, soit 219 → 690 px.
     Au-dessus d'elle il ne restait donc que 12 px, et les deux commandes de
     44 px se posaient à 163 → 207 px, en plein sur le bandeau de messages
     (175 → 207) et sur l'en-tête (10 → 167) : trois rectangles qui
     s'intersectaient.

     Sous la barre, en revanche, la bande réservée aux commandes flottantes est
     libre par construction (--bas-commandes au-dessus de --bas-securite et du
     bandeau du bas) : c'est la convention de tout le reste de l'interface. */
  body.role-citoyen #btn-legende {
    top: auto !important;
    bottom: var(--bas-commandes, 54px) !important;
    right: calc(58px + var(--marge-droite, 0px)) !important;   /* encoche latérale en paysage */
  }
  body.role-citoyen #btn-apropos {
    top: auto !important;
    bottom: var(--bas-commandes, 54px) !important;
    right: calc(8px + var(--marge-droite, 0px)) !important;     /* encoche latérale en paysage */
  }
  /* Les toasts, eux, ne rentrent plus dans le peu de place qui reste entre le
     bandeau et ces deux commandes : on les pose EN HAUT À GAUCHE, sous le
     bandeau unique et à gauche de la colonne des commandes. Deux axes
     séparés : aucun recouvrement possible, quel que soit le nombre de
     messages fugaces. */
  body.role-citoyen #toasts {
    top: calc(var(--y-bandeau) + var(--haut-bandeau, 0px) + 4px);
    bottom: auto;
    left: 8px;
    right: auto;
    max-width: min(54vw, 200px);
    /* La colonne de toasts ne peut pas descendre sur la barre citoyenne :
       elle est bornée à la place réellement libre. Rien n'est perdu — tout
       toast est aussi versé au centre de messages, où il reste consultable.
       `min(...30vh)` : le plafond des 30 % reste le plafond, même quand la
       barre citoyenne est repliée et laisse beaucoup de place. */
    max-height: min(30vh, calc(100vh - var(--y-bandeau) - var(--haut-bandeau, 0px)
                     - var(--bas-citoyen, 300px) - 12px));
    max-height: min(30dvh, calc(100dvh - var(--y-bandeau) - var(--haut-bandeau, 0px)
                     - var(--bas-citoyen, 300px) - 12px));
    overflow: hidden;
  }
}

/* ================================================================
   ZOOM AUTOMATIQUE iOS SUR LES CHAMPS DE SAISIE — « TOUT EST DÉFORMÉ »
   ================================================================
   LE PIÈGE, ET IL EST EXACTEMENT CELUI DE LA CAPTURE DU 29/07.
   Safari iOS agrandit la PAGE ENTIÈRE dès qu'on met le curseur dans un champ de
   saisie dont la `font-size` est INFÉRIEURE À 16 px. Il ne s'agit pas d'un zoom
   sur le champ : c'est une mise à l'échelle de tout le document. La largeur de
   mise en page reste 375 px mais l'affichage passe à 375 × 16/13 ≈ 460 px de
   contenu pour 375 px d'écran — l'en-tête sort à droite, les boutons du bas
   sortent en bas, et Safari NE REVIENT PAS à l'échelle 1 quand le champ perd le
   focus. L'interface reste déformée jusqu'au rechargement. C'est mot pour mot
   « tout est déformé on voit mal tout ».
   Et c'est aussi pourquoi le retour à la carte semblait impossible : les
   commandes n'avaient pas disparu, elles étaient hors du cadre visible.

   RELEVÉ AVANT CORRECTION (analyse des règles de cette feuille) — 17 déclarations
   sous le seuil, dont l'écran de CONNEXION lui-même (`.connexion-boite input`,
   15 px) : le tout premier champ que Jérémy touche déformait déjà l'application
   avant même d'avoir vu la carte. Puis 13,5 px (profil, administration),
   13 px (console d'entraide, sélecteur de zone), 12 px et jusqu'à 9,5 px.

   CE QU'ON NE FAIT PAS : `user-scalable=no` / `maximum-scale=1`. iOS les ignore
   depuis la version 10 pour les gestes de pincement, ils n'empêchent PAS ce zoom
   au focus, et ils interdisent à un pompier d'agrandir un nom de commune ou un
   numéro de téléphone. La seule correction qui fonctionne est le seuil de 16 px.

   PORTÉE : uniquement les champs de SAISIE DE TEXTE (ce sont les seuls qui
   déclenchent le zoom ; un `<select>` ouvre une roue native et ne zoome pas), et
   uniquement au pointeur grossier — sur ordinateur les formulaires denses gardent
   leur typographie compacte. `!important` est nécessaire : les règles visées sont
   plus spécifiques (`.connexion-boite input`, `.ce-champ input`…) et l'ordre de la
   feuille ne suffirait pas.
   COÛT ASSUMÉ : les formulaires d'administration gagnent quelques pixels de
   hauteur sur téléphone. Une fiche un peu plus longue se défile ; une interface
   déformée ne se répare pas. */
@media (pointer: coarse), (hover: none), (max-width: 900px) {
  input[type="text"],
  input[type="password"],
  input[type="email"],
  input[type="tel"],
  input[type="url"],
  input[type="number"],
  input[type="search"],
  input[type="date"],
  input[type="time"],
  input[type="datetime-local"],
  input:not([type]),
  textarea {
    font-size: 16px !important;
  }
}
/* CEINTURE ET BRETELLES POUR iOS, QUELLE QUE SOIT LA LARGEUR.
   La règle ci-dessus repose sur `pointer: coarse` / `max-width: 900px`. Un iPhone
   récent en PAYSAGE fait 915 px de large : la seconde condition ne s'applique
   plus, et tout repose alors sur la première. MESURÉ dans le harnais de recette
   (Chromium redimensionné à 915 × 412) : `(pointer: coarse)` ne matche pas et
   29 champs repassaient sous 16 px. Sur un vrai iPhone `pointer: coarse` matche
   toujours — le défaut est donc propre au harnais, PAS au produit. Mais le zoom
   au focus n'est pas un défaut qu'on laisse dépendre d'une seule condition.
   `-webkit-touch-callout` n'existe QUE dans les moteurs WebKit mobiles : c'est
   le test le plus direct de « je suis sur le navigateur qui fait ce zoom ». */
@supports (-webkit-touch-callout: none) {
  input[type="text"],
  input[type="password"],
  input[type="email"],
  input[type="tel"],
  input[type="url"],
  input[type="number"],
  input[type="search"],
  input[type="date"],
  input[type="time"],
  input[type="datetime-local"],
  input:not([type]),
  textarea {
    font-size: 16px !important;
  }
}

/* ============================================================================
   RENDRE LA CARTE AU POMPIER — TÉLÉPHONE (30/07/2026)
   ----------------------------------------------------------------------------
   CONSTAT MESURÉ sur la capture de Jérémy (Android 412 px, compte opérateur) :
   l'habillage FIXE occupait ~85 % de la hauteur — en-tête ~340 px, bandeau de
   sautes ~60 px, et SIX boutons d'action empilés chacun sur SA rangée pleine
   largeur, ~600 px. Il ne restait de la carte que de minces bandes latérales.

   POURQUOI, ET C'EST LA LEÇON : aucune décision antérieure n'était fausse.
   Les libellés avaient été gardés ENTIERS (« un bouton d'urgence dont on ne lit
   pas le libellé ne sert à rien »), donc la barre passait À LA LIGNE plutôt que
   de tronquer, les cibles étaient à 44 px (gants de feu), et la colonne de
   droite était réservée pour ne pas recouvrir « ? Légende ». Chaque règle est
   juste ; leur CUMUL est inutilisable. Ce qui manquait, c'est un BUDGET DE
   HAUTEUR global.

   ARBITRAGE DE JÉRÉMY (30/07) : « garder le SOS visible, raccourcir les
   libellés ». Un libellé COURT est choisi, entier et lisible ; il n'a rien à
   voir avec une ELLIPSE, qui coupe au hasard et reste interdite sur une
   commande. Le texte long demeure dans le bouton pour les lecteurs d'écran et
   réapparaît au-delà de 900 px.

   EFFET DE BORD BÉNÉFIQUE, et c'est lui qui débloque tout : les 116 px réservés
   à droite l'étaient PARCE QUE la barre montait jusqu'à y = 366 sur cinq
   rangées et recouvrait « ? Légende » (y = 398 → 442). Ramenée à DEUX rangées,
   elle s'arrête vers y = 554 sur un écran de 812 : la collision est
   géométriquement impossible, la réservation n'a plus d'objet, et la largeur
   utile passe de 243 à ~359 px — ce qui permet justement de tenir en deux
   rangées. La cause et le remède se répondent.
   ========================================================================== */
@media (max-width: 900px) {
  /* 1. LIBELLÉ COURT À LA PLACE DU LONG.
     `font-size: 0` neutralise le texte du bouton (émoji compris, d'où l'émoji
     répété dans `data-court`) sans le RETIRER du DOM : les lecteurs d'écran
     continuent d'annoncer la commande en entier, et `aria-busy` reste valable. */
  #barre-intervention button[data-court] {
    font-size: 0 !important;
    letter-spacing: 0 !important;
  }
  #barre-intervention button[data-court]::before {
    content: attr(data-court);
    font-size: 0.86rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    white-space: nowrap;
  }

  /* 2. CHAQUE BOUTON SE DIMENSIONNE À SON CONTENU, il ne s'étire plus pour
     remplir la ligne : c'est ce qui permet d'en mettre trois par rangée. */
  #barre-intervention > button,
  #barre-intervention > #barre-actions-terrain > button {
    flex: 0 0 auto !important;
    width: auto !important;
    max-width: none !important;
    padding: 0 12px !important;
    min-height: 44px;            /* plancher tactile — gants de feu */
  }
  /* Le groupe des commandes de secteur peut passer à la ligne LUI AUSSI, au lieu
     de former un unique bloc large qui monopoliserait une rangée. */
  #barre-intervention > #barre-actions-terrain {
    flex: 0 1 auto !important;
    max-width: none !important;
    flex-wrap: wrap;
    row-gap: 6px;
  }

  /* 3. LE SOS EST LA COMMANDE DE DÉTRESSE : jamais renvoyé en seconde rangée
     (l'ordre du balisage le place en tête) et un peu plus large au doigt.
     Sa HAUTEUR reste celle du plancher tactile commun (44 px, imposé plus haut
     en `!important` — vérifié à la mesure : une valeur de 48 px ici serait
     silencieusement ignorée, donc du code mort qui aurait l'air d'agir). */
  #barre-intervention > #btn-sos {
    padding: 0 16px !important;
  }

  /* 4. LA RÉSERVATION DE LA COLONNE DE DROITE EST LEVÉE (voir l'en-tête de ce
     bloc) : la barre récupère la largeur, ce qui la fait tenir en deux rangées.
     8 px de garde suffisent, les zones sûres restant respectées. */
  #barre-intervention {
    right: calc(8px + var(--marge-droite, 0px));
    column-gap: 6px;
    row-gap: 6px;
  }
  #barre-intervention > *,
  #barre-intervention > #barre-actions-terrain {
    max-width: calc(100vw - 24px - var(--marge-gauche, 0px) - var(--marge-droite, 0px));
  }
}

/* TÉLÉPHONE DEBOUT UNIQUEMENT — l'en-tête. Le paysage est traité ailleurs (une
   tâche dédiée s'en occupe) : `min-height: 501px` évite de lui marcher dessus. */
@media (max-width: 900px) and (min-height: 501px) {
  /* AUCUNE INFORMATION N'EST SUPPRIMÉE : le titre et l'observation de station
     sont ramenés à une ligne avec ellipse — c'est légitime sur un TITRE, qui
     n'est pas une commande — et le texte complet reste dans le DOM, donc
     accessible aux lecteurs d'écran et au panneau « ℹ ». */
  #entete {
    padding: 6px 10px;
    gap: 2px;
  }
  #entete-zone {
    font-size: 0.82rem;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Le badge de mode occupait une rangée pour deux mots : il redevient une
     pastille en ligne. */
  #badge-mode {
    display: inline-block;
    font-size: 0.6rem;
    padding: 1px 6px;
    margin: 0;
  }
  #entete-obs {
    font-size: 0.7rem;
    line-height: 1.2;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* LA FRAÎCHEUR RESTE LISIBLE, et l'AVERTISSEMENT de péremption reprend deux
     lignes quand il est actif : c'est une alerte, elle ne se tronque pas.
     (Le mécanisme `#entete.perime` vient du chantier « fraîcheur visible » —
     ne pas le défaire.) */
  #entete-fraicheur {
    font-size: 0.68rem;
    line-height: 1.2;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  #entete.perime #entete-fraicheur {
    -webkit-line-clamp: 2;
  }
}

/* ============================================================================
   TIROIR DES COMMANDES — TÉLÉPHONE (30/07/2026, demande de Jérémy)
   ----------------------------------------------------------------------------
   Même à libellés courts, sept commandes tenaient sur TROIS rangées : 148 px de
   carte confisqués, mesurés à 375 × 812. Sa demande : « le bouton SOS plus un
   bouton action qui ouvre les autres boutons ». Une seule rangée reste donc en
   permanence, soit ~48 px, et le tiroir se déplie à la demande.

   CE QUI NE SE REPLIE JAMAIS — ce sont des ÉTATS et des ALERTES, pas des
   commandes, et un état caché est un état qu'on croit éteint :
     · #bandeau-sos           « Annuler (fausse alerte) » — vital ;
     · #bandeau-reseau        trou de couverture en cours ;
     · #etat-trace, #etat-tampon  relevé et tampon hors-ligne ;
     · #btn-intervention      DÈS qu'il est .actif / .en-attente / .position-perdue :
                              c'est le seul moyen d'arrêter le partage de position.
   Les BADGES repliés (eau, évacuation, alertes, lignes à valider) ne perdent
   rien : `majPastilleActions()` allume un point orange sur « Actions » tant que
   l'un d'eux est actif.
   ========================================================================== */
#btn-actions { display: none; }        /* ordinateur : le tiroir n'a pas lieu d'être */

@media (max-width: 900px) {
  #btn-actions {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    padding: 0 14px;
    position: relative;
    /* Hauteur : plancher tactile commun de 44 px (imposé plus haut en
       `!important`). Ne pas déclarer 48 px ici — ce serait ignoré. */
  }
  /* Point orange : quelque chose attend derrière le tiroir. */
  #btn-actions.a-du-neuf::after {
    content: '';
    position: absolute;
    top: 6px; right: 6px;
    width: 10px; height: 10px;
    border-radius: 50%;                /* hauteur bornée en dur : rayon légitime */
    background: #ff9f0a;
    box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.55);
  }
  /* TIROIR FERMÉ : on replie les COMMANDES, et elles seules. */
  body:not(.actions-ouvertes) #barre-intervention > #btn-ligne-feu,
  body:not(.actions-ouvertes) #barre-intervention > #barre-actions-terrain,
  body:not(.actions-ouvertes) #barre-intervention > #btn-page-intervenants,
  body:not(.actions-ouvertes) #barre-intervention > #badge-entraide,
  body:not(.actions-ouvertes) #barre-intervention > #badge-evacuation,
  body:not(.actions-ouvertes) #barre-intervention > #badge-alertes,
  body:not(.actions-ouvertes) #barre-intervention > #badge-lignes-attente {
    display: none !important;
  }
  /* Le bouton d'intervention ne se replie QUE s'il n'est pas engagé. */
  body:not(.actions-ouvertes) #barre-intervention > #btn-intervention:not(.actif):not(.en-attente):not(.position-perdue) {
    display: none !important;
  }
  /* TIROIR OUVERT : tout revient, la barre s'élargit et grandit vers le HAUT
     (elle est ancrée en bas), donc rien n'est poussé hors de l'écran. */
  body.actions-ouvertes #barre-intervention {
    justify-content: flex-start;
  }
  /* Le tiroir ouvert reste au-dessus des commandes du bord droit, sinon
     « ? Légende » et « ℹ » recevraient le toucher destiné à une commande. */
  body.actions-ouvertes #barre-intervention { z-index: 30; }
}

/* TIROIR OUVERT — le groupe des commandes de secteur devient TRANSPARENT à la
   mise en page (`display: contents`) : ses trois boutons deviennent des éléments
   de la barre et se rangent avec les autres, au lieu de former un bloc de 298 px
   qui monopolisait une rangée. MESURÉ à 375 × 812 : 4 rangées et 198 px avant,
   3 rangées et 148 px après. Le conteneur ne portant aucun fond ni bordure, le
   faire disparaître visuellement ne change rien à l'apparence, et les sélecteurs
   `#barre-intervention > #barre-actions-terrain > button` continuent de
   s'appliquer (`>` est structurel, pas visuel). */
@media (max-width: 900px) {
  body.actions-ouvertes #barre-intervention > #barre-actions-terrain {
    display: contents;
  }
}

/* ============================================================================
   TÉLÉPHONE COUCHÉ — LA BARRE D'ACTIONS QUITTE LE MILIEU DE LA CARTE (04/08/2026)
   ----------------------------------------------------------------------------
   CE QUI N'ALLAIT PAS, MESURÉ À 844 × 390 (iPhone couché, compte opérateur,
   tiroir replié, zones sûres 0/21/47/47, échantillonnage `elementFromPoint`
   tous les 2 px sur 188 colonnes) :

     · la barre traversait la largeur utile À MI-HAUTEUR (y 164 → 212,
       x 47 → 789) et coupait la carte en deux tranches ;
     · elle RECOUVRAIT « ? Légende » sur 100 × 25 px ;
     · dès qu'un message critique s'affiche, elle remonte avec la pile basse et
       se pose SUR le bandeau de messages — recouvrement mesuré 730 × 30 px,
       c'est-à-dire la quasi-totalité du bandeau ;
     · tiroir OUVERT, « 🤝 Demandes » (x 792 → 901) et « 👥 Intervenants »
       (x 907 → 1041) sortaient PUREMENT ET SIMPLEMENT de l'écran de 844 px,
       atteignables au seul défilement horizontal d'une barre dont l'ascenseur
       est masqué. Deux commandes perdues, sans le moindre signe.
     · médiane des colonnes de carte : 112 px.

   POURQUOI CE N'EST PAS UN RÉGLAGE. Couchée, la barre garde le parti pris du
   portrait : une RANGÉE ancrée en bas qui grandit vers le haut. En portrait ce
   choix est juste — la largeur abonde, la hauteur aussi. Couché, les deux
   s'inversent : 390 px de hauteur dont 91 d'en-tête et 55 de bandeau
   réglementaire, mais 844 px de largeur. Une rangée horizontale dépense la
   ressource RARE (la hauteur) pour ménager la ressource ABONDANTE (la largeur).
   La colonne fait l'inverse : ~100 px de large sur le bord gauche, et la
   hauteur revient à la carte.

   POURQUOI LE BORD GAUCHE, et pas le droit : le bord droit porte déjà #entete
   (haut), « ? Légende » et « ℹ » (à mi-hauteur) et le zoom MapLibre. Sous le
   bouton ☰, la lisière gauche est le seul couloir vertical libre — et c'est
   aussi la seule bande que #entete ne couvre pas, donc la moins « carte » de
   l'écran.

   CE QUE ÇA COÛTE, ET C'EST ASSUMÉ : la lisière gauche perdait déjà peu, mais
   elle offrait la plus haute bande de l'écran (278 px sur 116 px de large). On
   l'échange contre une bande plus basse sur TOUTE la largeur restante. Une
   fenêtre de 520 × 171 px vaut mieux qu'une fente de 116 × 278 : on lit une
   carte en largeur, pas dans une meurtrière.
   ========================================================================== */
/* LA CONDITION NE PORTE PAS SUR LA LARGEUR, ET C'EST MESURÉ. Un iPhone Pro Max
   couché fait 932 × 430 : il passait par la branche « ordinateur »
   (min-width: 901px), barre ancrée en haut et étalée sur une seule rangée
   large, tiroir de paramètres DÉPLIÉ faute d'atteindre le seuil de 900 px de
   `initPanneau()`. Relevé à 932 × 430 avant correctif : barre de 195 px de haut
   (y 166 → 361) croisant la barre horaire sur 354 × 72 px, « ? Légende » sur
   100 × 44, le bandeau réglementaire sur 536 × 28, débordant de la zone sûre à
   droite, et une MÉDIANE de carte de 64 px par colonne. C'est le pire cas de
   tout le chantier, et il concerne un téléphone courant. La hauteur et
   l'orientation décrivent le problème ; la largeur ne le décrit pas. */
/* B10 (21/08/2026) : COMPACTION DE #entete EN PAYSAGE — la « tâche dédiée »
   annoncée par le chantier d'habillage paysage, jamais faite. Sur 375-450 px
   de HAUT, l'en-tête complet (zone + observations + fraîcheur) mangeait le
   tiers de l'écran : police resserrée, lignes secondaires sur UNE ligne
   tronquée (le détail complet reste au panneau), largeur bornée à la moitié
   droite — la colonne d'actions vit à gauche. */
@media (max-height: 450px) and (orientation: landscape) {
  #entete {
    max-width: min(52vw, 480px);
    font-size: 11px;
    line-height: 1.25;
    padding: 4px 8px;
  }
  #entete-obs,
  #entete-fraicheur {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

@media (max-height: 450px) and (orientation: landscape) {
  #barre-intervention {
    /* ANCRÉE EN HAUT, sous la pile haute mesurée. --y-actions est publiée par
       messages.js : bas de l'en-tête, du bandeau de démonstration, du bouton ☰
       et du bandeau de messages. Ni --haut-pile (qui inclut cette barre, donc
       boucle) ni --y-toasts (qui inclut l'indicateur de calcul, donc déplace
       le SOS pendant un calcul) ne conviennent — cf. le commentaire de
       messages.js là où les trois variables sont publiées. */
    top: calc(var(--y-actions, 110px) + 6px);
    /* Bornée en bas par la pile basse : la colonne ne peut PHYSIQUEMENT pas
       atteindre la barre horaire, quelle que soit sa hauteur. */
    bottom: calc(var(--bas-pile, 126px) + var(--marge-basse, 0px));
    left: var(--marge-gauche, 0px);
    right: auto;
    transform: none;
    /* `column wrap` et non `column` seul : la hauteur de la boîte est DÉFINIE
       (top ET bottom posés), une commande de trop forme donc une SECONDE
       colonne au lieu d'être écrêtée ou renvoyée derrière un défilement
       invisible. C'est exactement le défaut relevé sur le tiroir ouvert. */
    flex-flow: column wrap;
    align-items: flex-start;
    align-content: flex-start;
    justify-content: flex-start;
    row-gap: 6px;
    column-gap: 6px;
    /* Repliée, la colonne fait la largeur de son plus large bouton (~90 px) ;
       le plafond ne sert qu'aux bandeaux d'état, qui sont plus larges. */
    width: max-content;
    max-width: min(46vw, 340px);
    /* PLANCHER DE HAUTEUR — un bouton ne déborde JAMAIS de sa propre boîte.
       `top` et `bottom` posés ensemble donnent une hauteur qui peut fondre :
       avec un bandeau de messages EN HAUT et une barre horaire haute EN BAS,
       elle est tombée à 36 px à la mesure, soit moins qu'un bouton de 44, et
       les commandes sortaient de la boîte par le bas. 50 px = la cible tactile
       plus les 6 px d'écart entre colonnes. Au-delà, `flex-flow: column wrap`
       range les commandes en colonnes supplémentaires vers la droite — elles
       s'écartent de la carte, elles ne la recouvrent pas par le bas. */
    min-height: 50px;
    padding-left: 8px;
    overflow: visible;
  }
  #barre-intervention > *,
  #barre-intervention > #barre-actions-terrain {
    max-width: min(44vw, 324px);
  }
  #barre-intervention > #barre-actions-terrain > button {
    max-width: min(44vw, 324px);
  }
  /* NEUTRALISATION DES RÈGLES « ORDINATEUR », question de spécificité et non de
     goût : `body.panneau-ferme #barre-intervention` (0,1,1 + un identifiant)
     l'emporte sur `#barre-intervention` seul, quel que soit l'ordre de la
     feuille. Sans ces deux sélecteurs, un téléphone couché de plus de 900 px
     gardait `left: 64px` et surtout un `max-width` calculé pour une RANGÉE
     horizontale posée à côté de l'en-tête — 415 px mesurés à 932 px de large,
     c'est-à-dire un plafond qui n'a aucun sens pour une colonne. */
  body.panneau-ferme #barre-intervention,
  body:not(.panneau-ferme) #barre-intervention {
    left: var(--marge-gauche, 0px);
    right: auto;
    max-width: min(46vw, 340px);
  }
  /* TIROIR OUVERT : la colonne reste une colonne, mais elle a le droit de
     s'étendre sur toute la largeur en formant autant de colonnes que
     nécessaire, et de descendre jusqu'au bandeau réglementaire. Elle recouvre
     alors une partie de la carte — c'est un état TRANSITOIRE, voulu par
     l'utilisateur, et le prix à payer pour que les neuf commandes soient
     TOUTES à l'écran. Aucune ne dépend plus d'un défilement. */
  body.actions-ouvertes #barre-intervention {
    /* LE TIROIR OUVERT PREND TOUTE LA HAUTEUR — et c'est ce qui garantit que
       les neuf commandes tiennent EN LARGEUR. Le nombre de colonnes est
       l'inverse de la hauteur offerte : à 568 × 320 (iPhone SE couché), les
       96 px laissés sous l'en-tête n'acceptaient que deux boutons par colonne,
       il en fallait donc cinq, soit 550 px — et QUATRE commandes sortaient de
       l'écran. En repartant du haut de la zone sûre, la même liste tient en
       deux colonnes de 230 px. Le tiroir recouvre alors une partie de
       l'en-tête : c'est un panneau ouvert exprès, refermé par n'importe quelle
       commande ou par un toucher sur la carte. Rien n'est perdu, tout est
       atteignable.
       62 px ET NON 6 : le bouton ☰ occupe y 10 → 56. Parti de 6 px, le tiroir
       le recouvrait ENTIÈREMENT (mesuré 46 × 46) — un toucher destiné au
       panneau de paramètres serait tombé sur une commande d'intervention. On
       peut recouvrir l'en-tête, qui affiche ; pas un bouton, qui agit. */
    top: calc(var(--marge-haute, 0px) + 62px);
    bottom: calc(var(--bas-securite, 62px) + 6px);
    width: auto;
    max-width: calc(100vw - 16px - var(--marge-gauche, 0px) - var(--marge-droite, 0px));
    /* Descendue jusqu'au bandeau réglementaire, la colonne ouverte croise la
       barre horaire — MESURÉ 285 × 44 px. C'est le seul recouvrement assumé de
       ce chantier : neuf commandes, dont deux étaient purement hors écran,
       valent mieux qu'un curseur d'heure dégagé pendant les trois secondes où
       le tiroir est ouvert. z-index 31 (la barre horaire est à 30) pour que le
       toucher aille à la commande et non au curseur. */
    z-index: 31;
  }

  /* ---- BANDEAU RÉGLEMENTAIRE : la même phrase, moins d'air --------------
     Le bandeau était déjà sur UNE ligne (mesuré : 17,6 px de texte) ; ses
     55 px se décomposent en 2 de bordure + 8 + 17,6 + 8 + 21 de zone sûre du
     bas. Les 21 px appartiennent à l'indicateur d'accueil de l'iPhone : ils ne
     se récupèrent pas, ils ne sont de toute façon pas atteignables. Restent
     les marges, ramenées de 8 à 4 px. Le texte n'est ni raccourci, ni tronqué,
     et « ▾ Avertissement complet » reste là où il était : la mention de
     responsabilité engage, elle ne se compacte pas au-delà de son air. */
  #bandeau-bas {
    padding-top: 4px;
    padding-bottom: calc(4px + var(--marge-basse, 0px));
  }

  /* ---- BARRE HORAIRE : le vrai plafond de la bande de carte -------------
     Ce n'est pas la barre d'actions qui bornait la carte par le bas, c'est
     elle. Mesurée à 74 px pour une ligne d'information et une rangée de
     commandes, posée 12 px au-dessus du bandeau : à elle seule, elle
     interdisait les 160 px de carte réclamés (bande 101 → 249 = 148 px). On
     lui retire son air, PAS ses commandes.
     DÉCOMPOSITION MESURÉE de ces 74 px (844 × 390, ligne d'information sur une
     seule ligne) : 18 de marges intérieures, 2 de bordure, 16,2 de texte,
     4 de marge basse du texte, 34 de rangée de commandes — dont 30 pour le
     curseur lui-même et 4 pour ses marges par défaut. On rend les marges, on
     garde les 30 px du curseur : c'est LUI qu'on tire avec des gants, il ne
     rétrécit pas, et le bouton de mode garde sa taille. Total rendu : 18 px,
     la barre passe de 74 à 56. */
  #timeline-bar {
    padding: 3px 12px 4px;
  }
  #timeline-info {
    margin-bottom: 1px;
  }
  #timeline-curseur {
    margin: 0;
  }
}

/* ============================================================================
   TÉLÉPHONE COUCHÉ DE PLUS DE 900 px — CE QUE LA BRANCHE « ORDINATEUR » NE
   DONNAIT PAS (04/08/2026)
   ----------------------------------------------------------------------------
   Un iPhone Pro Max couché (932 × 430) est un TÉLÉPHONE : on le tient à la
   main, on le manœuvre avec des gants, et il n'a que 430 px de haut. La feuille
   le traitait pourtant en ordinateur, parce que le seul critère était la
   largeur. Il n'héritait donc ni du tiroir « ⋯ Actions », ni des libellés
   courts, ni du plancher tactile — les NEUF commandes s'affichaient d'un coup,
   sur 195 px de haut, par-dessus la carte, la barre horaire et le bandeau.

   On ne déplace pas le seuil de 900 px : il est juste pour ce qu'il décrit (la
   largeur disponible pour un tiroir latéral de 348 px). On ajoute la poignée de
   règles dont la colonne paysage a besoin, et elles seules. Le reste de la
   branche « ordinateur » continue de s'appliquer.

   MESURE APRÈS CORRECTIF à 932 × 430, zones sûres 0/21/47/47, compte
   opérateur : médiane par colonne 64 → 116 px, bande atteignable 166 px sur
   344 px de large, plus AUCUN recouvrement (il y en avait quatre), plus rien
   hors zone sûre, et les neuf commandes du tiroir ouvert toutes à l'écran.
   ========================================================================== */
@media (min-width: 901px) and (max-height: 450px) and (orientation: landscape) {
  /* 1. LE TIROIR EXISTE. Sans lui, aucune des règles de repli ci-dessous n'a
     de sens : elles masqueraient des commandes sans offrir le moyen de les
     rouvrir. */
  #btn-actions {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    padding: 0 14px;
    position: relative;
  }
  /* 2. LIBELLÉS COURTS — même mécanique qu'en portrait : le texte long reste
     dans le DOM pour les lecteurs d'écran, `data-court` s'affiche à sa place. */
  #barre-intervention button[data-court] {
    font-size: 0 !important;
    letter-spacing: 0 !important;
  }
  #barre-intervention button[data-court]::before {
    content: attr(data-court);
    font-size: 0.86rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    white-space: nowrap;
  }
  /* 3. PLANCHER TACTILE ET DIMENSIONNEMENT AU CONTENU. */
  #barre-intervention > button,
  #barre-intervention > #barre-actions-terrain > button {
    flex: 0 0 auto !important;
    width: auto !important;
    padding: 0 12px !important;
    min-height: 44px;
  }
  /* 4. CE QUI SE REPLIE, ET CE QUI NE SE REPLIE JAMAIS — la liste est la même
     qu'en portrait, pour la même raison : on replie des COMMANDES, jamais un
     ÉTAT ni une ALERTE (bandeau SOS, trou de réseau, relevé en cours, tampon
     hors-ligne, et le bouton d'intervention dès qu'il est engagé). */
  body:not(.actions-ouvertes) #barre-intervention > #btn-ligne-feu,
  body:not(.actions-ouvertes) #barre-intervention > #barre-actions-terrain,
  body:not(.actions-ouvertes) #barre-intervention > #btn-page-intervenants,
  body:not(.actions-ouvertes) #barre-intervention > #badge-entraide,
  body:not(.actions-ouvertes) #barre-intervention > #badge-evacuation,
  body:not(.actions-ouvertes) #barre-intervention > #badge-alertes,
  body:not(.actions-ouvertes) #barre-intervention > #badge-lignes-attente {
    display: none !important;
  }
  body:not(.actions-ouvertes) #barre-intervention > #btn-intervention:not(.actif):not(.en-attente):not(.position-perdue) {
    display: none !important;
  }
  /* 5. Groupe des commandes de secteur transparent à la mise en page quand le
     tiroir est ouvert : ses trois boutons se rangent avec les autres au lieu de
     former un bloc qui monopoliserait une colonne entière. */
  body.actions-ouvertes #barre-intervention > #barre-actions-terrain {
    display: contents;
  }
}

/* ---------- T2.6 : couche HAUTEUR DE VÉGÉTATION (MNH LiDAR) --------------
   Outils repliés avec la case (motif légende combustible). Le PROFIL EN
   TRAVERS s'affiche dans un panneau TRANSITOIRE de la famille
   .barre-flottante-bas : il recouvre, il ne pousse rien — il n'entre donc
   pas dans le budget de hauteur mesuré de l'habillage fixe en paysage
   téléphone (voir la règle du chantier « habillage paysage »). */
#mnh-outils { margin: 2px 0 8px 26px; }
#mnh-outils[hidden] { display: none; }
.mnh-opacite {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  color: var(--texte-2);
}
.mnh-opacite input[type="range"] {
  flex: 1 1 auto;
  min-height: 32px;   /* cible tactile : même plancher que .bf-rayon */
  max-width: 220px;
}
.mnh-boutons { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.mnh-boutons button { flex: 1 1 100%; }
#btn-mnh-profil.actif {
  border-color: var(--ambre);
  color: #ffe9a8;
}

/* Panneau du profil : l'en-tête porte le résumé, la croix fait 44 px. */
.profil-mnh-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
#btn-profil-mnh-fermer {
  flex: 0 0 auto;
  min-width: 44px;
  min-height: 44px;
  border-radius: 9px;
  border: 1px solid var(--bord);
  background: #2a2f39;
  color: var(--texte);
  font-size: 16px;
  cursor: pointer;
}
#profil-mnh-svg svg {
  display: block;
  width: 100%;
  height: 128px;   /* preserveAspectRatio=none : la hauteur est fixée ici */
}

/* PAYSAGE TÉLÉPHONE (mêmes bornes que la colonne d'actions du chantier
   habillage) : la hauteur est la ressource RARE — le panneau s'écarte vers
   la DROITE (la colonne d'actions tient le bord gauche) et plafonne pour
   laisser la carte lisible au-dessus. */
@media (max-height: 450px) and (orientation: landscape) {
  #panneau-profil-mnh {
    left: auto;
    right: calc(8px + var(--marge-droite, 0px));
    transform: none;
    width: min(62vw, 470px);
    max-height: min(72dvh, calc(100dvh - 64px));
  }
  #profil-mnh-svg svg { height: 96px; }
}

/* ================================================================
   BAC À SABLE DE DÉMONSTRATION (CH6, 13/08/2026)
   Le SEUL habillage ajouté par ce mode, et il est délibéré.

   Le cahier des charges demande un écran NU : pas de cartouche narratif,
   pas d'écran « simulé » posé à côté, pas de verre sur les panneaux — ce
   que le SDIS voit doit être l'application telle qu'elle est. Reste ce
   bandeau, et lui seul.

   POURQUOI IL EXISTE MALGRÉ TOUT : cet outil affiche des ordres
   d'évacuation et des positions d'intervenants. Sans repère permanent,
   une capture d'écran de démonstration devient indiscernable d'une
   situation réelle — et une capture, ça circule. Le bandeau est donc
   sobre (il ne vole pas la vedette à la carte) mais il ne se referme pas.
   ================================================================ */
#bac-bandeau {
  position: fixed;
  top: var(--marge-haute, 0px);
  left: 0;
  right: 0;
  z-index: 46;          /* au-dessus du bandeau de zone, sous les toasts (60) */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 4px 10px;
  background: repeating-linear-gradient(135deg,
    #2b2350 0 14px, #241d45 14px 28px);
  border-bottom: 2px solid #7c6cf0;
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.5);
}
#bac-bandeau[hidden] { display: none; }
/* Verdict L (zip Stitch nº 4) : le signal d'irréalité ENVELOPPE tout l'écran
   — un cadre rayé sur le pourtour, pas seulement le bandeau du haut (sur un
   grand écran de salle, le bandeau sort du champ dès qu'on regarde le bas de
   la carte). Violet des hypothèses, PAS le jaune/noir de la maquette : dans
   PYRONOS le violet EST le langage du « pas réel » (losanges ◆, bandeau bac).
   pointer-events:none — le cadre ne vole aucun clic. */
body.bac-actif::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 45;
  border: 5px solid transparent;
  border-image: repeating-linear-gradient(135deg,
    #7c6cf0 0 12px, #241d45 12px 24px) 5;
}
.bac-bandeau-titre {
  font-size: 11.5px;
  font-weight: 900;
  letter-spacing: 0.02em;
  color: #cfc6ff;
  white-space: nowrap;
}
/* Le détail dit CE QUI EST VRAI et CE QUI NE L'EST PAS — c'est la phrase
   que le présentateur n'aura pas à répéter à chaque diapositive. */
/* Le détail passe SOUS la première ligne (flex-basis 100 %, order tardif) :
   posé sur la même ligne que les neuf outils, il poussait le bandeau à 165 px
   de haut — un tiers de la carte mangé, mesuré le 14/08/2026. */
.bac-bandeau-detail {
  flex: 1 1 100%;
  order: 9;
  min-width: 0;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--texte-2);
}
/* BARRE D'OUTILS DE LA SÉANCE. Elle vit dans le bandeau et non dans le tiroir :
   le bouton d'entrée du bac est à 3 730 px du haut du panneau (mesuré), et
   chercher un bouton devant une salle casse la démonstration. L'outil ARMÉ est
   visiblement enfoncé — sans quoi l'orateur ne sait plus ce que fera son
   prochain clic sur la carte. */
#bac-outils {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  flex: 0 1 auto;
}
.bac-outil {
  min-height: 28px;
  padding: 2px 8px;
  border-radius: 9px;
  border: 1px solid rgba(124, 108, 240, 0.55);
  background: rgba(124, 108, 240, 0.1);
  color: #e4dfff;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.bac-outil:hover { background: rgba(124, 108, 240, 0.24); }
.bac-outil.actif {
  background: #7c6cf0;
  border-color: #b9aeff;
  color: #12101f;
  box-shadow: 0 0 0 2px rgba(185, 174, 255, 0.45);
}

#bac-quitter {
  flex-shrink: 0;
  margin-left: auto;
  min-height: 28px;
  padding: 2px 10px;
  border-radius: 10px;
  border: 1px solid #7c6cf0;
  background: rgba(124, 108, 240, 0.16);
  color: #e4dfff;
  font-size: 12px;
  font-weight: 800;
  cursor: pointer;
}
#bac-quitter:hover { background: rgba(124, 108, 240, 0.3); }

/* La pile fixe descend sous le bandeau : sans cela il recouvrirait l'en-tête
   de zone et le bouton ☰, exactement le défaut qu'on vient de corriger
   ailleurs. La valeur ci-dessous n'est qu'un DÉPART : bacCalerBandeau() mesure
   la hauteur réelle du bandeau à l'ouverture et à chaque redimensionnement,
   puis l'écrit en style en ligne sur <body> — c'est elle qui fait foi. */
body.bac-actif { --h-bac: 44px; }
body.bac-actif #entete,
body.bac-actif #btn-panneau,
body.bac-actif #panneau { top: calc(var(--marge-haute, 0px) + var(--h-bac)); }

/* Bouton d'entrée, dans le tiroir : même allure que « Mode démonstration »,
   couleur distincte pour qu'on ne confonde pas les deux modes. */
#btn-bac.actif {
  border-color: #7c6cf0;
  background: rgba(124, 108, 240, 0.18);
  color: #cfc6ff;
}

/* TÉLÉPHONE. Mesuré le 14/08/2026 à 375×812 : le bandeau montait à 157 px
   (19 % de l'écran) et le titre débordait de 27 px, coupé sur « ce n'est pas
   l… ». Trois corrections : le détail disparaît (déjà), le titre se raccourcit
   et s'autorise deux lignes, et les outils défilent HORIZONTALEMENT sur une
   seule rangée plutôt que d'empiler quatre rangées. --h-bac reste MESURÉE par
   bacCalerBandeau : la valeur ci-dessous n'est qu'un départ. */
@media (max-width: 720px) {
  .bac-bandeau-detail { display: none; }   /* le titre suffit, la place manque */
  body.bac-actif { --h-bac: 38px; }
  .bac-bandeau-titre {
    flex: 1 1 100%;
    white-space: normal;
    font-size: 10.5px;
    line-height: 1.2;
  }
  #bac-outils {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  #bac-outils::-webkit-scrollbar { display: none; }
  .bac-outil { font-size: 11px; padding: 2px 7px; }
  #bac-quitter { margin-left: 0; }
}

/* Séparateur « vue : » entre les outils de pose et les boutons de point de vue.
   Sans lui, huit boutons alignés se lisent comme une seule famille, alors que
   les uns POSENT des éléments et les autres CHANGENT d'écran. */
.bac-sep {
  align-self: center;
  padding-left: 6px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #9d92d8;
}

/* A38 (21/08/2026) : cibles tactiles étendues SANS changer la mise en page —
   même motif que l'extension documentée plus haut (surface peinte fine,
   surface sensible >= 44 px). Ces trois éléments sont des commandes d'écran
   fixes, PAS des marqueurs MapLibre : le position:relative y est sans danger
   (cf. le pavé d'avertissement de .marqueur-foyer.etat-eteint). */
.puce-recadrer, #timeline-mode, #puce-cache-recalc { position: relative; }
.puce-recadrer::after, #timeline-mode::after, #puce-cache-recalc::after {
  content: '';
  position: absolute;
  left: -6px; right: -6px; top: -11px; bottom: -11px;
}

/* A34 : pendant une séance de bac à sable, le bouton démo disparaît (la garde
   JS refuse de toute façon — ceci évite d'offrir un bouton qui refuse). */
body.bac-actif #btn-demo { display: none; }
