/* explorer.css - v1.1 - 28.07.2026 */

/* =========================================================
   Pull-to-refresh et rebond de page
   =========================================================
   Un glissement vers le bas recharge la page sur Chrome Android et sur Safari
   iOS. Dans une app d'évaluation, c'est la perte de l'itinéraire en cours et de
   la saisie non envoyée — au geste le plus banal qui soit sur un écran tactile.

   `overscroll-behavior-y: none` supprime à la fois le rechargement et l'effet
   de rebond. Posé sur html ET body : selon la plateforme, ce n'est pas le même
   élément qui porte le défilement du document.

   Support : Chrome et Firefox de longue date, Safari depuis la 16. En dessous,
   la règle est simplement ignorée et le geste reste actif — d'où le filet de
   sécurité complémentaire côté données (persistance localStorage, voir
   evaluations-store.js). */
html,
body {
  overscroll-behavior-y: none;
}

#map-container {
  position: fixed;
  top: 0; left: 0; right: 0;
  width: 100%;
  height: calc(100% - var(--bottom-bar-h));
}

:root {
  color-scheme: light;

  /* Hauteur de la barre applicative fixe en bas de l'écran. Tous les éléments
     ancrés en bas s'y réfèrent : changer cette seule valeur les déplace tous. */
  --bottom-bar-h: 70px;

  /* Decalage a appliquer aux elements du haut quand le bandeau de navigation
     occupe l'ecran : 12px d'ancrage + 84px de bandeau + 12px d'ecart.
     Utilisee par le toast, qui doit passer sous le bandeau.
     A tenir en accord avec le min-height de .nav-banner. */
  --nav-banner-h: 108px;

  /* Largeur RÉSERVÉE à droite par la colonne de contrôles de carte : 50px pour
     le bouton principal, plus ses 16px d'ancrage au bord. Ce qui flotte
     au-dessus de la carte s'y réfère pour ne pas venir sous ses boutons — le
     toast, au premier chef, qui les recouvrait sur ses placements bas. */
  --map-controls-w: 66px;

  /* Rouge d'alerte. Le même que le repère des chemins à évaluer posé sur un
     itinéraire (TODO_FILL, navigation.js) : cette application n'a qu'un rouge,
     et il sert à ce qui doit arrêter le regard. */
  --color-danger: #E02020;
}

#map {
  height: 100%;
  width: 100%;

  /* APPUI LONG SUR LA CARTE (voir map_explorer/destination.js).
     Sur iOS, un appui prolongé déclenche la loupe de sélection et le menu
     contextuel de Safari : ils volent le geste et laissent parfois du texte
     surligné en travers de la carte. Aucun preventDefault ne les empêche —
     seul le CSS le fait, et il faut les deux propriétés : `touch-callout` pour
     le menu, `user-select` pour la sélection. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* Même vert que la charte : le fond n'apparaît qu'entre deux tuiles, le temps
     qu'elles arrivent, et le voir changer de teinte au chargement se remarque. */
  background-color: var(--color-header-dark);
  position: relative;
  z-index: -1;
}

/* Bouton « Enregistrer ». Il vit HORS de #eval-ui, en enfant direct de
   #map-container ; getElementById ne voit que celui-ci, le premier des deux
   #btn-finish du HTML (voir REPRISE-2 §5).

   PLUS DE position: absolute — le bouton suit désormais le flux. `left` et
   `bottom` n'ont donc aucun effet en l'état : ils ne redeviendront actifs que
   si un positionnement est reposé ici, et sont conservés pour ça. */
#btn-finish {
  /* inline-flex, uniquement pour aligner le pictogramme et le libellé. Ni fond
     ni couleur ne sont imposés ici : ils viennent de .menu-item-gpx et, sur le
     site, de la feuille du thème. Le pictogramme étant en currentColor, il suit
     la couleur du texte quelle qu'elle soit. */
  display: inline-flex;
  align-items: center;
  gap: 7px;

  left: 5px;
  bottom: 15px;
  font-weight: 500;
}

#btn-finish svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

/* --- Libellé long ou court, selon la largeur ---
   Les DEUX libellés sont dans le HTML, et c'est le CSS qui choisit lequel
   s'affiche. Le faire en JS obligerait à écouter le redimensionnement et à
   réécrire le bouton à chaque rotation d'écran, pour un texte qui ne dépend de
   rien d'autre que de la place disponible. */
.short-label-finish {
  display: none;
}

/* --- Conteneur de l'entrée de menu ---
   Largeur FIXE et non `auto` : cette entrée voisine avec d'autres dans la barre
   du site, et une largeur qui suit son contenu ferait bouger tout le menu au
   moment précis où le bouton apparaît — c'est-à-dire à la première évaluation. */
.menu-end-exploration {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  width: 216px;
}

/* UN SEUL point de rupture, et donc un seul bloc.
   Les deux réglages ci-dessous vont ensemble : le libellé raccourcit parce que
   le conteneur rétrécit. Les séparer en deux media queries de même largeur
   garantissait qu'un jour l'une bouge sans l'autre, et que le texte long se
   retrouve dans le conteneur étroit. */
@media (max-width: 380px) {
  .full-label-finish {
    display: none;
  }

  .short-label-finish {
    display: inline;
  }

  .menu-end-exploration {
    width: 116px;
  }
}

/* Entrée de menu portant le bouton « Enregistrer » (voir #btn-finish plus haut).
   C'est ELLE que le JS masque, pas le bouton — sans quoi la pastille resterait
   affichée, et vide.

   `!important` : cette pastille vit dans le menu WordPress, dont la feuille de
   style habille déjà ses entrées. Sans lui, la règle du thème l'emporte selon
   l'ordre de chargement, qui n'est pas de notre ressort.

   #d8ff00 est exactement --color-lime : la variable plutôt que la valeur, pour
   que la pastille suive la charte si celle-ci bouge. */
.menu-item-gpx {
  background: var(--color-lime) !important;
  color: var(--color-header-dark);
  padding: 5px 9px;
  border-radius: 6px;
  height: 44px;
}

  :root {
  /* Couleur de la charte. Elle habille les en-têtes de modale, les boutons
     pleins, le tracé du panneau et le fond de la carte : la changer ici la
     change partout — À CINQ ENDROITS PRÈS, hors de portée d'une variable CSS,
     où la valeur est écrite en dur parce que MapLibre peint ses couches depuis
     le JS et ne sait pas lire var() :

         map_explorer/tracks-layer.js   ENDPOINT_ACCENT
         map_explorer/pois-layer.js     MAIN_POI_COLORS
         map_explorer/destination.js    PIN_COLOR
         map_explorer/entry-zone.js     fill-color et line-color

     ⚠ CES CINQ-LÀ PORTENT ENCORE #006C57, LE VERT D'AVANT. Ils n'ont pas été
     déplacés avec la variable : ce qui se peint SUR la carte — extrémités de
     chemin, points de la base, épingle de destination, contour du secteur
     d'arrivée — n'a pas forcément à suivre la teinte de l'habillage, et les
     confondre était peut-être le vrai défaut. À trancher ; en attendant, la
     divergence est écrite ici plutôt que découverte à l'écran. */
  --color-header-dark: #4c156b;
  --color-active-gps: #1565C0;

  /* Couleur du marqueur de position, sur la carte comme dans le panneau de
     trace. Distincte de --color-active-gps, qui habille les boutons.

     ATTENTION : elle vaut TOUJOURS LA MÊME CHOSE que --color-header-dark. Les
     deux ont suivi ensemble le vert d'origine, puis ensemble le passage au
     violet : elles restent séparées parce qu'elles répondent à des questions
     différentes — « la charte » et « où je suis » — mais elles ne se
     distinguent pas à l'écran.

     C'EST POURQUOI LA BULLE DE POSITION EST BLANCHE CERCLÉE DE CETTE
     COULEUR, et non pleine (.track-gps-bubble). Peinte en aplat, elle avait
     la teinte exacte de la courbe qu'elle parcourt et n'en tenait que par son
     liseré ; inversée, elle se pose dessus au lieu de s'y fondre. Si le
     marqueur doit reprendre du relief autrement, c'est CETTE variable qu'il
     faut décaler, pas la charte. */
  --color-position: #4c156b;

  /* HAUTEUR DE LA BARRE DU PANNEAU DE PARCOURS, ET DU BOUTON QUI LA JOUXTE.
     Déclarée ici parce que TROIS règles en dépendent et qu'elles vivent loin
     les unes des autres : la barre elle-même, le bouton « terminer » posé à sa
     droite, et la pile « Recentrer » calée juste au-dessus. Elles se réglaient
     chacune de son côté, avec trois nombres qui n'étaient égaux que par
     habitude — et le bouton, resté à 46 px, était visiblement plus petit que
     la barre à laquelle il devait s'aligner.
     LA BARRE N'A PAS DE HAUTEUR PROPRE : elle est faite de deux lignes de texte
     et de son remplissage, soit environ 55 px. La valeur ci-dessous est donc
     posée en `min-height`, un cheveu au-dessus, ce qui la fixe sans jamais la
     rogner. */
  --nav-panel-bar-h: 56px;

  /* Chemin selectionne. ATTENTION : cette valeur est DUPLIQUEE dans
     tracks-layer.js (TRACK_PURPLE) — une expression de style MapLibre ne peut
     pas lire une variable CSS. Les deux doivent bouger ensemble. */
  --color-selected: #8E24AA;
  --color-cream: #efeade;
  --color-cream-2: #e6e1cf;
  --color-lime: #D8FF00;
  /* Version assombrie pour les survols (:hover de .btn-primary). Suit le lime
     ci-dessus : les deux doivent être modifiés ensemble. */
  --color-lime-dark: #BCDE00;
  --color-text-dark: #1c2b25;
  --color-muted: #7d8a80;
  --color-border: #d8d4c4;

  /* Qualité */
  --q-roulant: #d9bf9c;
  --q-gravier: #b8c9d7;
  --q-irregulier: #f3caca;
  --q-engage: #ddc2e3;
  --q-accidente: #de8080;
  --q-bitume: #c8caee;
  --q-brut: #4e5a3d;
  --q-brut-text: #f2f2ea;
  --q-escalier: #f6cba3;

  /* Pente */
  --p-plat: #d7e6cf;
  --p-moderee: #e7dcb0;
  --p-elevee: #f0c4b5;
  --p-abrupte: #74392a;
  --p-abrupte-text: #f2f2ea;
  --p-mur: #bb3a30;
  --p-mur-text: #f2f2ea;

  --radius-lg: 20px;
  --radius-pill: 999px;
  --shadow-card: 0 12px 30px rgba(20, 30, 25, 0.18);
}

#eval-ui, #eval-ui * {
  box-sizing: border-box;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.hidden { display: none !important; }

/* =========================================================
   Icônes
   ========================================================= */
.icon-eye {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

/* =========================================================
   Backdrop générique (modales 1 et 5)
   ========================================================= */
.overlay-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(10, 20, 15, 0.35);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 1000;
  /* Le voile reste plein écran (sinon une bande claire apparaîtrait derrière
     la barre) ; seule la zone de placement de la carte est réduite. */
  padding: 16px 16px calc(16px + var(--bottom-bar-h));
}

@media (min-width: 600px) {
  .overlay-backdrop { align-items: center; }
}

.modal.card {
  /* Le défilement s'arrête à ce conteneur : sans quoi la fin de la liste
     transmet le geste au document, qui repart en pull-to-refresh. */
  overscroll-behavior: contain;
  background: #fff;
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 420px;
  /* dvh et non vh : sur mobile, vh se calcule barre d'URL masquée, ce qui
     faisait déborder les modales hautes par le haut. */
  max-height: calc(100dvh - var(--bottom-bar-h) - 32px);
  overflow-y: auto;
  box-shadow: var(--shadow-card);
  padding-bottom: 20px;
}

/* --- Modale plein écran ---
   A ajouter sur l'overlay (.overlay-backdrop.fullscreen). Le fond n'est plus
   un voile centré mais occupe tout l'écran, et la carte .modal.card à
   l'intérieur s'étire au maximum. */
.overlay-backdrop.fullscreen {
  align-items: stretch;
  justify-content: stretch;
  background: #fff;
  /* Plein écran, mais toujours au-dessus de la barre applicative. */
  padding: 0 0 var(--bottom-bar-h);
}

@media (min-width: 600px) {
  /* Sur grand écran, on garde le plein écran (pas de recentrage). */
  .overlay-backdrop.fullscreen { align-items: stretch; }
}

.overlay-backdrop.fullscreen .modal.card {
  max-width: none;
  max-height: none;
  width: 100%;
  height: 100%;
  border-radius: 0;
  display: flex;
  flex-direction: column;

  /* AUCUN padding en bas, contrairement aux modales centrées (.modal.card).
     Cette carte-ci est le conteneur DÉFILANT sous une barre d'actions collante,
     et `position: sticky; bottom: 0` se cale sur le bord de la boîte de
     REMPLISSAGE, pas sur celui du contenu : un padding ici laissait la barre
     flotter d'autant au-dessus du bas de l'écran, avec une bande blanche
     dessous pendant tout le défilement.

     La marge du bas et la safe-area sont donc portées par .modal-actions-sticky
     lui-même, qui est le dernier élément et le seul à toucher le bord. */
  padding-bottom: 0;
}

.overlay-backdrop.fullscreen .modal-header {
  border-radius: 0;
  padding-top: max(20px, env(safe-area-inset-top));
}

/* Le corps de la modale prend l'espace restant et défile si besoin, en
   gardant les boutons d'action visibles en bas. */
.overlay-backdrop.fullscreen .modal-body {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.modal-header {
  background: var(--color-header-dark);
  color: #fff;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;

  /* Marge droite élargie : elle RÉSERVE la place du pictogramme posé en absolu
     dans le coin, qui ne pousse donc rien. Elle valait aussi pour la croix de la
     modale de validation, qui a depuis rejoint le gabarit du panneau de choix ;
     56px restent la géométrie de cette croix (44px + 8px d'écart au bord), et
     laissent au pictogramme plus d'air qu'il n'en demande. */
  padding: 18px 56px 20px 20px;

  position: relative;
}

.modal-header.dark { background: var(--color-header-dark); }

.eyebrow {
  display: block;
  color: var(--color-lime);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
}

/* Couleur RÉPÉTÉE ici alors qu'elle est déjà héritée de .modal-header : les
   navigateurs mobiles qui appliquent un thème sombre automatique réécrivent les
   couleurs de texte non déclarées, et le titre repassait en foncé sur son fond
   vert. Voir aussi `color-scheme` sur :root. */
.modal-header h2 {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  color: #fff;
}

/* Agrandi de 22 à 32px. C'est le seul élément coloré de l'en-tête et il dit de
   quoi la modale parle avant qu'on ait lu le titre ; à 22px il se lisait comme
   une décoration. Toute modification de cette taille doit se répercuter sur le
   padding droit de .modal-header ci-dessus, qui lui réserve sa place. */
.modal-header-icon {
  position: absolute;
  top: 18px;
  right: 20px;
  width: 32px;
  height: 32px;
  color: var(--color-lime);
}

/* CROIX POSÉE DANS LE COIN D'UN EN-TÊTE DE MODALE.

   Elle emprunte tout à .choice-close — gabarit de 44 px, teinte, réaction à
   l'appui — et n'ajoute que son placement : dans le panneau de choix la croix
   est le dernier élément d'une rangée en flex, ici il n'y a pas de rangée où
   la loger.

   `margin-right` REMIS À ZÉRO. Celui de .choice-close (−10 px) rattrape le
   retrait droit d'un bandeau en flux ; sur un élément posé en absolu il
   s'ajouterait au `right` et décalerait la croix hors de l'en-tête. */
.modal-header-close {
  position: absolute;
  top: 12px;
  right: 10px;
  margin-right: 0;
}

/* =========================================================
   1. Modale d'introduction
   ========================================================= */

/* LE DÉFILEMENT EST DESCENDU D'UN CRAN, DANS LA LISTE.
   Il vivait sur .modal.card, qui emportait tout : l'en-tête vert disparaissait
   par le haut et les deux boutons attendaient sous la ligne de flottaison,
   sans que rien ne dise qu'il fallait dérouler pour les atteindre. La carte
   devient donc une COLONNE FLEX — en-tête figé, liste défilante, actions
   figées — et c'est la liste seule qui bouge.

   Même patron que #summary-modal, pour les mêmes raisons ; voir le commentaire
   détaillé qui l'accompagne plus bas. */
#intro-modal .modal.card {
  display: flex;
  flex-direction: column;

  /* `hidden` et non `auto` : la carte ne doit plus défiler du tout, et c'est
     ce qui fait tenir ses angles arrondis autour du contenu. */
  overflow: hidden;
}

/* Tout ce qui n'est pas la liste garde sa hauteur. Sans ça, les enfants d'une
   colonne flex se laissent comprimer AVANT que la liste ne commence à
   défiler : les boutons s'écrasent et le texte de la note se chevauche. */
#intro-modal .modal.card > *:not(.steps-list) {
  flex-shrink: 0;
}

.steps-list {
  list-style: none;
  margin: 0;
  padding: 14px 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;

  /* Seule zone élastique de la carte : elle prend ce qui reste, et défile. */
  flex: 1 1 auto;

  /* INDISPENSABLE. La hauteur minimale par défaut d'un enfant flex est celle
     de son contenu : sans ce zéro, la liste refuse de rétrécir, la carte
     dépasse sa max-height et les boutons ressortent de l'écran — exactement
     ce qu'on cherchait à corriger. */
  min-height: 0;

  overflow-y: auto;

  /* Le geste s'arrête ici plutôt que de repartir en pull-to-refresh une fois
     la fin de liste atteinte. .modal.card le porte déjà, mais c'est désormais
     la liste qui est la boîte défilante : c'est sur elle que ça se joue. */
  overscroll-behavior: contain;

  /* --- MARQUE DE DÉFILEMENT, SANS JAVASCRIPT ---
     Quatre calques. Deux ombres en `scroll`, fixes par rapport à la boîte.
     Par-dessus, deux masques COULEUR DE FOND en `local`, qui défilent avec le
     contenu. En haut de liste, le masque du haut recouvre son ombre ; dès
     qu'on descend d'un pixel il s'en va et l'ombre apparaît. Celle du bas
     s'efface de la même façon en fin de liste.

     Résultat : une ombre visible exactement du côté où il reste quelque chose
     à lire, et rien du tout quand la liste tient entière — ce qui évite de
     signaler un défilement sur les grands écrans, où il n'y en a pas.

     LE BLANC EST EN DUR et doit valoir celui de .modal.card : un masque d'une
     autre teinte laisserait une bande visible en haut et en bas de la liste. */
  background:
    linear-gradient(#fff 30%, rgba(255, 255, 255, 0))
      top / 100% 26px no-repeat local,
    linear-gradient(rgba(255, 255, 255, 0), #fff 70%)
      bottom / 100% 26px no-repeat local,
    radial-gradient(farthest-side at 50% 0, rgba(28, 43, 37, 0.25), rgba(28, 43, 37, 0))
      top / 100% 13px no-repeat scroll,
    radial-gradient(farthest-side at 50% 100%, rgba(28, 43, 37, 0.25), rgba(28, 43, 37, 0))
      bottom / 100% 13px no-repeat scroll;

  /* Barre de défilement fine et permanente sur ordinateur, où l'ombre seule
     reste un signal discret. Sans effet sur iOS, qui n'affiche la sienne qu'au
     moment du geste — d'où l'ombre, qui est là pour ce cas-là. */
  scrollbar-width: thin;
  scrollbar-color: var(--color-border) transparent;
}

/* FILET DE SÉPARATION SOUS LA CONSIGNE DE SÉCURITÉ.

   Elle ouvre la liste sans en faire partie : les trois points qui suivent sont
   numérotés et s'enchaînent, elle non. Sans rien entre eux, on lisait quatre
   entrées d'une même énumération dont la première aurait perdu son numéro.

   À MI-LARGEUR ET CENTRÉ, pas sur toute la ligne : un filet pleine largeur
   sépare deux blocs, celui-ci ponctue à l'intérieur d'un seul. C'est la même
   nuance qu'entre un changement de chapitre et un blanc typographique. */
.steps-list li.step-divider {
  position: relative;
  padding-bottom: 14px;
  margin-bottom: 4px;
}

.steps-list li.step-divider::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 50%;
  height: 1px;
  background: var(--color-border);
}

.steps-list li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
}

.step-num {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--color-lime);
  color: var(--color-text-dark);
  font-weight: 500;
  font-size: 14px;
  /* Liseré vert : sur le lime très clair, il détache la pastille du fond crème
     de la modale. */
  border: 1.5px solid var(--color-header-dark);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* PREMIER POINT DE LA LISTE : un avertissement, pas une étape.

   ROUGE, ET NON PLUS VERT FONCÉ. La pastille inversée le distinguait des trois
   autres, mais dans la même famille de couleurs : vert foncé et lime sont
   celles de toute l'application, y compris de ses boutons d'action. Une
   consigne de sécurité qui reprend la couleur de « COMMENCER » se lit comme une
   quatrième étape aux couleurs un peu différentes. Le rouge la sort de la
   charte, ce qui est exactement son rôle.

   PICTOGRAMME BLANC, et non lime : sur ce rouge, le lime tire au jaune-vert et
   perd le contraste que la pastille vient de gagner. */
.step-num.step-alert {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: #FFFFFF;
  font-size: 0;   /* la pastille ne porte qu'un SVG */
}

.step-num.step-alert svg {
  width: 17px;
  height: 17px;
}

.steps-list strong {
  display: block;
  font-size: 14.5px;
  color: var(--color-text-dark);
  margin-bottom: 1px;
}

.steps-list p {
  margin: 0;
  font-size: 14px;
  color: var(--color-muted);
  line-height: 1.35;
}

.steps-list mark {
  display: inline-block;
  margin-top: 4px;
  background: var(--color-lime);
  color: var(--color-text-dark);
  font-size: 14px;
  padding: 1px 4px;
  border-radius: 3px;
}

#intro-default-actions {
  display: flex;
  flex-direction: column;
}

/* FILET ENTRE LES DEUX ACTIONS DU BAS.
   Les deux boutons n'ont pas le même poids — on en commence une vraie, ou on
   s'entraîne — et rien ne les séparait qu'un écart de 8px. Le trait dit qu'il
   s'agit d'un second choix et non de la suite du premier.

   POSÉ EN PSEUDO-ÉLÉMENT, et non en border-top sur le bouton : une bordure
   ferait toute la largeur du bouton et se collerait à son bord haut, alors
   qu'on veut un trait plus court, centré dans l'écart. Il est absolu, donc
   hors du flux flex du bouton (.btn est en display: flex) : il ne s'y insère
   pas comme un enfant.

   LES DEUX PAIRES sont traitées : les boutons par défaut, et le couple
   « Reprendre / Nouvelle évaluation » qui les remplace quand des évaluations
   sont en attente (voir evaluation-ui.js). C'est le même bas de modale. */
#intro-default-actions > .btn + .btn,
#btn-new-evaluation {
  position: relative;

  /* ÉCART ET FILET SONT LIÉS : le trait se pose au milieu de cet écart, et
     c'est le blanc de part et d'autre qui le rend lisible. Les 8px que .btn
     pose entre deux boutons ne suffisaient pas — le filet s'y lisait comme un
     bord de bouton plutôt que comme une séparation.

     Une seule valeur à changer pour ajuster les deux : le décalage du
     pseudo-élément s'en déduit ci-dessous. */
  --intro-actions-gap: 26px;

  margin-top: var(--intro-actions-gap);
}

#intro-default-actions > .btn + .btn::before,
#btn-new-evaluation::before {
  content: '';
  position: absolute;

  /* La moitié de l'écart, vers le haut : le trait tombe pile entre les deux.
     Calé sur le bouton du dessous plutôt qu'en pourcentage du conteneur, pour
     rester juste même si un libellé passait sur deux lignes. */
  top: calc(var(--intro-actions-gap) / -2);

  /* 60% de la largeur du bouton, centrés. */
  left: 20%;
  right: 20%;

  border-top: 1px solid var(--color-border);
}

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: calc(100% - 40px);
  margin: 8px 20px 0;
  padding: 14px 20px;
  border-radius: var(--radius-pill);
  border: none;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
}

.btn-primary {
  background: var(--color-lime);
  color: var(--color-text-dark);
}
.btn-primary:hover { background: var(--color-lime-dark); }
.btn-primary:disabled {
  background: #d8d4c4;
  color: var(--color-muted);
  cursor: not-allowed;
}

.btn-outline {
  background: transparent;
  border: 1.5px solid var(--color-header-dark);
  color: var(--color-header-dark);
}
.btn-outline:hover { background: rgba(11, 74, 60, 0.06); }

/* --- APPUI SUR LES BOUTONS DE MODALE ---
   Les boutons de carte s'enfoncent sous le doigt (.map-control-btn:active),
   ceux des modales ne bougeaient pas : sur un écran tactile, où il n'y a pas
   de survol, rien ne confirmait que l'appui avait porté — et un appui qui ne
   répond pas se répète.

   MÊME LANGAGE QUE LA CARTE, à un degré près : 0,97 au lieu de 0,94, parce
   qu'un bouton pleine largeur se déforme visiblement là où une pastille de
   36px encaisse le même facteur sans qu'on le remarque.

   LA TRANSITION NE PORTE QUE SUR LE RETOUR. 0s à l'appui : l'enfoncement doit
   suivre le doigt, une durée d'entrée le ferait traîner. 0,12s au relâchement,
   pour que le bouton remonte au lieu de sauter — c'est ce retour-là qui se
   voit et qui donne la sensation d'avoir appuyé sur quelque chose.

   .btn n'est utilisée QUE dans les modales (vérifié dans page-explorer.html) :
   cette règle ne peut pas déborder sur la feuille de tracé ou la barre du
   haut, qui ont leurs propres classes. */
.btn,
.poi-type-btn,
.poi-photo-btn {
  transition: transform 0.12s ease;
}

.btn:active,
.poi-type-btn:active,
.poi-photo-btn:active {
  transform: scale(0.97);
  transition-duration: 0s;
}

/* Un bouton désactivé ne se laisse pas appuyer : lui donner le même retour
   ferait croire que le clic est passé. */
.btn:disabled:active {
  transform: none;
}

.modal-footnote {
  font-size: 14px;
  color: var(--color-muted);
  text-align: center;
  line-height: 1.4;
  padding: 14px 24px 4px;
  margin: 0;
}

.pill {
  display: flex;
  align-items: center;
  gap: 8px;
  border: none;
  border-radius: 16px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}

.pill-choice {
  background: #fff;
  color: var(--color-muted);
  /* Rangée et non colonne : le texte occupe la largeur, le chevron se cale à
     droite. */
  flex-direction: row;
  align-items: center;
  /* Le groupe icône + texte est centré d'un bloc ; le chevron, posé en absolu,
     ne participe pas à ce centrage et ne le déséquilibre donc jamais. */
  justify-content: center;
  gap: 8px;
  /* Constant quel que soit l'état : sans lui, une pastille remplie (avec son
     icône) et une pastille vide n'ont pas la même marge intérieure. */
  padding-left: 12px;
  padding-right: 12px;
  /* Repère du chevron, posé en absolu. */
  position: relative;
  /* Largeur pilotée par .track-choices, qui les répartit à parts égales. */
  width: 100%;
  height: 52px;
  margin-bottom: 0;
  box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2);
  border: 2px solid transparent; /* reserve pour l'etat "rempli" */
}

/* Pastille vide : un filet gris dit qu'il y a quelque chose à remplir.
   En BORDER, et non en box-shadow interne comme je l'avais d'abord fait : le
   trait inset se dessinait à l'intérieur des 2px de bordure transparente, donc
   en retrait du bord, tandis que l'ombre portée marquait ce bord — d'où deux
   liserés concentriques.
   La bordure de 2px étant réservée dans les deux états, lui donner une couleur
   ne décale rien : seule la teinte change au moment du choix. */
.pill-choice[data-state="unset"] {
  border-color: #D9DEDC;
}

/* Le texte occupe tout l'espace laissé libre et s'y centre : même position
   qu'il y ait ou non une icône et un chevron de part et d'autre. */
.pill-text {
  min-width: 0;
  text-align: center;
  /* Plafonné pour ne jamais passer sous le chevron, qui occupe les 30px de
     droite. */
  max-width: calc(100% - 30px);
}

.pill-choice .pill-label { display: block; font-size: 12px; font-weight: 500; }

/* La valeur choisie reste noire quel que soit l'etat et quelle que soit
   l'option : le fond de la pill ne change plus, donc rien ne justifie un
   texte clair. */
.pill-choice .pill-sub {
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: var(--color-text-dark);
}

/* Etat "a choisir" : libelle dans la charte plutot qu'en gris */
.pill-choice[data-state="unset"] .pill-label { color: var(--color-header-dark); }

/* Etat "rempli" : meme rendu que les options du panneau de choix — pastille
   d'icone a gauche, libelle a droite, fond et bordure aux couleurs de
   l'option. Les variables --option-* sont posees par le JS (setPillValue) ;
   les fallbacks ci-dessous les derivent du fond si l'option ne les definit
   pas, exactement comme pour .choice-option. */
.pill-choice[data-state="set"] {
  --option-title: color-mix(in srgb, var(--option-bg) 35%, #000);
  --option-border: color-mix(in srgb, var(--option-bg) 60%, #000);
  --option-icon-bg: color-mix(in srgb, var(--option-bg) 70%, #fff);

  /* Fond blanc en permanence (herite de .pill-choice) : seules la bordure et
     la pastille d'icone portent desormais la couleur de l'option. */
  border-color: var(--option-border);

  /* Disposition horizontale : pastille + libelle */
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
}

/* Le surtitre "Terrain"/"Pente" RESTE visible une fois l'option choisie : dans
   la feuille de tracé, les deux pastilles sont côte à côte et rien d'autre ne
   dirait laquelle porte quoi. Il passe simplement en second plan. */
.pill-choice[data-state="set"] .pill-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  opacity: 0.65;
}

/* Une seule ligne, coupée si besoin : deux pastilles côte à côte dans la
   feuille n'ont pas la largeur d'un libellé complet, et un panneau qui change
   de hauteur selon la valeur choisie serait pire que la troncature. */
.pill-choice[data-state="set"] .pill-sub {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* 1.15 rognait les jambages (le « g » d'Engagé) : overflow:hidden coupe à la
     boîte de ligne, qui doit donc les contenir. */
  line-height: 1.4;
  padding-bottom: 1px;
}

/* Options a fond sombre (Brut, Pente abrupte, Mur).
   La pill etant blanche, --option-title ne sert plus qu'a l'icone de repli
   posee dans la pastille (dont le fond, lui, reste sombre). Les deux autres
   variables sont retirees : sur fond blanc, une bordure eclaircie et une
   pastille a 18% de blanc etaient invisibles. */
.pill-choice[data-state="set"].text-light {
  --option-title: #fff;
}

/* =========================================================
   3 & 4. Panneau de choix qualité / pente — thème clair
   -----------------------------------------------------------------
   La rangée n'est plus une pastille colorée : fond blanc, titre noir,
   description grise. La couleur de l'option ne subsiste que sur la
   barre verticale de gauche et sur la pastille d'icône. Les variables
   --option-* posées par le JS sont inchangées.
   ========================================================= */

.choice-backdrop {
  position: fixed;
  inset: 0;
  z-index: 940; /* sous le panneau (950), au-dessus de la carte/topbar */
  background: transparent;
}

.choice-panel {
  /* Le défilement s'arrête à ce conteneur : sans quoi la fin de la liste
     transmet le geste au document, qui repart en pull-to-refresh. */
  overscroll-behavior: contain;
  position: fixed;
  inset: 0;                 /* plein écran : couvre toute la fenêtre */
  z-index: 950;
  background: #fff;
  box-shadow: none;
  border-radius: 0;
  overflow-y: auto;
  /* L'en-tête gère lui-même l'encoche du haut. Ici, seulement le bas :
     barre applicative + safe-area, sans quoi la dernière option de la liste
     tombe derrière la barre. */
  padding: 0 0 calc(var(--bottom-bar-h) + max(24px, env(safe-area-inset-bottom)));
}

/* --- En-tête sombre --- */

/* .choice-back A ÉTÉ RETIRÉ D'ICI, ET DE CENT LIGNES PLUS BAS.

   La flèche de retour de l'étape « pente » était déclarée DEUX FOIS dans cette
   feuille, avec deux gabarits différents — 34px sur fond translucide ici, 44px
   sans fond plus bas — et deux commentaires qui justifiaient chacun le sien.
   Seule la seconde s'appliquait. Le balisage souffrait du même mal : deux
   boutons portant le même identifiant, dont le second n'a jamais rien reçu.

   La frise d'avancement (.choice-stepper) remplace la flèche : elle va
   n'importe où en arrière, pas seulement d'un cran. */
/* .modal-header-close ET .modal-header.dark ONT ÉTÉ RETIRÉS D'ICI.

   Ils n'habillaient qu'un seul élément : la croix de la modale de validation,
   qui reprend maintenant .choice-close et .choice-header comme les deux écrans
   de choix qui la précèdent. Tout ce que ces règles faisaient — 44px de cible,
   26px de trait, ni rond ni fond, blanc à 70 % — consistait précisément à
   recopier .choice-close de mémoire, et c'est ce doublon qui laissait les deux
   croix diverger dès qu'on ne touchait qu'à l'une des deux.

   Si le template WordPress porte encore <div class="modal-header dark"> et sa
   croix, c'est ce balisage-là qu'il faut reprendre, pas ces règles. */

.choice-header {
  /* Reste visible pendant le défilement de la liste. */
  position: sticky;
  top: 0;
  z-index: 1;

  /* EN COLONNE depuis que la frise s'y trouve : le texte, puis l'avancement.
     C'était une rangée, parce qu'il fallait loger une flèche à gauche et une
     croix à droite du titre — les deux ont disparu avec la frise. */
  display: flex;
  flex-direction: column;
  gap: 16px;

  background: var(--color-header-dark-alt, #006C57);
  color: #fff;
  border-radius: 0;
  box-shadow: none;
  margin: 0 0 6px;
  /* LA CONSIGNE TIENT SUR DEUX OU TROIS LIGNES — « Ne tenez pas compte des
     conditions temporaires (passage d'engins, boue…). » — d'où
     le bas plus généreux : à 22px, la dernière ligne venait toucher le bord du
     bandeau.

     LE RETRAIT DROIT REVIENT À 20px. Il avait été réduit à 8 pour la croix,
     qui portait déjà son propre espacement interne ; sans elle, le bandeau
     retrouve des marges symétriques.

     LE BAS PASSE DE 26 À 18px : la frise occupe désormais cette place, et elle
     porte son propre espacement. */
  padding: max(20px, env(safe-area-inset-top)) 20px 18px;
}

/* Le retrait droit retiré au bandeau est rendu au titre, qui ne doit pas venir
   sous la croix. */
/* LE RETRAIT DROIT DU TEXTE A ÉTÉ RETIRÉ. Il réservait la place de la croix, qui
   était son voisine de rangée ; l'en-tête est maintenant une colonne, et le
   texte occupe toute sa largeur. */
/* Rangée haute : le texte, et la croix à son bout. La frise vient en dessous,
   sur toute la largeur — c'est pour elle que l'en-tête est passé en colonne. */
.choice-header-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.choice-header-text {
  min-width: 0;
}

/* --- Frise d'avancement ---

   Composée par renderStepper (evaluation-ui.js), la même dans les deux en-têtes.

   ELLE OCCUPE TOUTE LA LARGEUR, les traits de liaison absorbant le reste : les
   trois étapes se répartissent alors d'un bord à l'autre du bandeau, et la
   position du repère courant se lit comme une position sur une règle. */
.choice-stepper {
  display: flex;
  align-items: center;
  gap: 6px;
}

.choice-stepper-step {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-shrink: 0;

  padding: 3px 9px 3px 3px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;

  font: inherit;
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;

  /* Terne par défaut : une étape ni faite ni courante est une promesse, pas une
     commande. */
  color: rgba(255, 255, 255, 0.5);
  cursor: default;
}

/* Une étape ATTEIGNABLE se signale comme telle : pleine opacité et curseur de
   main. C'est la seule marque qui la distingue de celles où l'on ne peut pas
   aller, et elle doit se voir sans avoir à essayer. */
.choice-stepper-step:not(:disabled) {
  color: #fff;
  cursor: pointer;
}

.choice-stepper-step:not(:disabled):active {
  background: rgba(255, 255, 255, 0.16);
}

.choice-stepper-mark {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  box-sizing: border-box;

  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
}

.choice-stepper-mark svg {
  width: 14px;
  height: 14px;
}

/* Étape franchie : pastille remplie d'un voile clair. Elle a été faite, elle
   n'attend plus rien — mais on peut y revenir. */
.choice-stepper-step.is-past .choice-stepper-mark {
  background: rgba(255, 255, 255, 0.2);
}

/* Étape COURANTE : lime plein, la seule couleur d'accent du bandeau. C'est le
   même repère que la pastille « 1/2 » qu'elle remplace (voir .choice-step). */
.choice-stepper-step.is-current {
  color: var(--color-lime);
}

.choice-stepper-step.is-current .choice-stepper-mark {
  background: var(--color-lime);
  border-color: var(--color-lime);
  color: var(--color-header-dark);
}

/* Étape SAUTÉE — « Disparu », ou « Ne plus demander la pente ». Elle reste
   affichée plutôt que retirée : la frise doit garder la même forme d'un chemin
   à l'autre, sinon elle cesse d'être un repère. Mais elle s'efface, et elle
   n'est pas atteignable : y renvoyer rouvrirait une question qui ne se pose
   pas. */
.choice-stepper-step.is-skipped {
  opacity: 0.32;
  text-decoration: line-through;
}

/* Trait de liaison. `flex: 1` : c'est lui qui étire la frise sur la largeur, et
   lui qui se comprime en premier sur un écran étroit. */
.choice-stepper-bar {
  flex: 1 1 6px;
  min-width: 6px;
  height: 1.5px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.28);
}

/* Surtitre : pastille "1/2" + libellé de l'étape. Les deux morceaux sont
   produits par openChoicePanel (evaluation-ui.js). */
.choice-title {
  margin: 0 0 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Le numéro d'étape en pastille lime : les deux écrans de choix se ressemblent
   beaucoup, c'est la seule chose qui dit où l'on en est. */
.choice-step {
  flex-shrink: 0;
  background: var(--color-lime);
  color: var(--color-header-dark);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.02em;
  padding: 3px 9px;
  border-radius: var(--radius-pill, 999px);
}

/* IL PORTE LE TITRE DEPUIS QUE LA QUESTION A DISPARU. « Quelle est la qualité
   du terrain ? » occupait la ligne en gros juste en dessous et répétait ce que
   « Terrain » dit déjà : les options répondent à une question qu'on n'a pas
   besoin de lire. Le libellé d'étape reprend donc sa taille — sans quoi
   l'en-tête n'aurait plus de titre du tout. */
.choice-step-label {
  font-size: 21px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  /* Opaque, là où le surtitre était à 62% : c'est une information de
     progression, pas une mention secondaire. */
  color: #fff;
}

/* Titre principal : la QUESTION de l'étape.
   Elle occupe la place que tenait la consigne, et pour cause : c'est à elle que
   les options répondent, la consigne ne disant que la manière de choisir. */
.choice-question {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  line-height: 1.2;
  color: #fff;
}

/* Consigne, sous la question. Blanc atténué et non pleine opacité : elle
   précise, elle ne demande pas. */
.choice-desc {
  margin: 6px 0 0;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.35;
  color: rgba(255, 255, 255, 0.82);
}



/* Sollicitation de photo, dans l'en-tête d'une fiche de la base sans image.
   Ambre comme le témoin hors ligne : ce n'est ni une erreur ni une consigne,
   c'est une occasion — quelque chose à faire, maintenant, parce qu'on est
   devant. */
/* SUR FOND VERT, ET NON SUR FOND BLANC. Ce bloc vit dans .modal-header, dont
   le fond est --color-header-dark : un brun sombre à 10 % d'opacité sur du vert
   foncé donnait un texte presque invisible. Le citron de la marque est la seule
   teinte qui tranche franchement sur ce vert, et c'est déjà celle qui signale
   une action attendue ailleurs dans l'application. */
.poi-photo-call {
  margin: 0;
  padding: 8px 12px;
  border-radius: 10px;
  background: var(--color-lime);
  color: var(--color-header-dark);
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.35;
}

/* --- Témoin hors ligne ---

   Ancré à GAUCHE, à la hauteur des contrôles de carte : c'est le seul bord
   libre, et celui où rien ne se touche. Il informe, il n'attend pas de geste —
   d'où `pointer-events: none`, qui lui interdit de voler le glissement de la
   carte.

   AMBRE ET NON ROUGE. Le rouge de cette application veut dire « quelque chose
   ne va pas et vous devez agir » — le repère de retour au tracé, les chemins à
   décrire sur un itinéraire. Être hors connexion n'appelle aucune action : tout
   continue de fonctionner, seul l'envoi attend. */
.offline-badge {
  position: fixed;
  left: 12px;
  top: max(14px, env(safe-area-inset-top));

  /* SOUS LE TOAST, à dessein (z-index 1100). Les deux occupent la même bande
     et se recouvrent d'une trentaine de pixels ; c'est le message éphémère qui
     doit passer devant, le témoin étant là en permanence et n'ayant rien à dire
     qu'on ne puisse lire trois secondes plus tard. */
  z-index: 900;

  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;

  background: #8A5A00;
  color: #fff;
  box-shadow: 0 0 0 2.5px #fff, 0 2px 8px rgba(0, 0, 0, 0.28);

  pointer-events: none;
}

/* Guidage en cours : le bandeau occupe le haut, le témoin se glisse dessous.
   `--nav-banner-h` est la constante nominale ; contrairement au toast, ce
   témoin ne colle pas au bandeau — les 8px d'écart absorbent le dépassement
   d'une instruction passée à la ligne. */
body:has(#nav-banner:not(.hidden)) .offline-badge {
  top: calc(var(--nav-banner-h) + 8px);
}

.offline-badge svg {
  width: 20px;
  height: 20px;
}

/* --- Chargement des chemins ---

   Même gabarit et même colonne que le témoin hors ligne, une place plus bas.
   Il dit ce que la carte ne dit pas : entre le moment où l'on déplace la vue et
   celui où les chemins apparaissent, il s'écoule jusqu'à deux secondes — 700 ms
   d'anti-rafale, puis la requête — pendant lesquelles une zone vide est
   indiscernable d'une zone sans chemin à évaluer.

   VERT ET NON AMBRE : rien ne va mal, quelque chose est en cours. L'ambre est
   réservé à ce qui empêche (voir .offline-badge). */
.loading-badge {
  position: fixed;
  left: 12px;
  top: max(14px, env(safe-area-inset-top));
  z-index: 900;

  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;

  background: var(--color-header-dark);
  color: #fff;
  box-shadow: 0 0 0 2.5px #fff, 0 2px 8px rgba(0, 0, 0, 0.28);

  pointer-events: none;
}

/* HORS LIGNE EN MÊME TEMPS : celui-ci descend d'un cran. 44px = 36 de pastille
   plus 8 d'écart. C'est `body.is-offline`, posée par connection.js, qui porte
   l'information — ce module n'a pas à connaître l'autre. */
body.is-offline .loading-badge {
  top: calc(max(14px, env(safe-area-inset-top)) + 44px);
}

/* Guidage : les deux glissent sous le bandeau, dans le même ordre. */
body:has(#nav-banner:not(.hidden)) .loading-badge {
  top: calc(var(--nav-banner-h) + 8px);
}

body.is-offline:has(#nav-banner:not(.hidden)) .loading-badge {
  top: calc(var(--nav-banner-h) + 8px + 44px);
}

/* Anneau tournant, même construction que celui du bouton de position : un
   anneau pâle complet, un quart opaque par-dessus. Voir .map-control-btn
   .is-locating pour le détail de l'empilement. */
.loading-badge-spinner {
  position: relative;
  width: 20px;
  height: 20px;
}

.loading-badge-spinner::before,
.loading-badge-spinner::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2.5px solid transparent;
}

.loading-badge-spinner::before {
  border-color: currentColor;
  opacity: 0.3;
}

.loading-badge-spinner::after {
  border-top-color: currentColor;
  animation: map-control-spin 0.85s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .loading-badge-spinner::after {
    animation: map-control-pulse 1.6s ease-in-out infinite;
  }
}

/* --- Repère de retour au tracé ---

   Créé par navigation.js et posé dans le conteneur du canevas, au bord de
   l'écran, dans la direction de l'itinéraire quitté.

   ROUGE, comme tout ce qui appelle une correction dans cette application — le
   repère des chemins à évaluer sur un itinéraire porte déjà cette teinte. Il
   n'est visible que hors tracé, c'est-à-dire dans le seul cas où quelque chose
   ne va pas.

   `pointer-events: none` : il flotte par-dessus la carte, il ne doit pas
   intercepter le geste qui la fait glisser — c'est justement le geste qu'on
   fait en le regardant. */
.nav-rejoin-pointer {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;

  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px 7px 9px;
  border-radius: var(--radius-pill);

  background: var(--color-danger);
  color: #fff;
  /* Liseré blanc : le rouge et le fond de carte se valent en zone urbaine. */
  box-shadow: 0 0 0 2.5px #fff, 0 2px 8px rgba(0, 0, 0, 0.3);

  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;

  pointer-events: none;
}

/* Seule la FLÈCHE tourne, pas la pastille : un libellé pivoté serait illisible
   dès qu'il pointe vers le bas. */
.nav-rejoin-arrow {
  display: flex;
  flex-shrink: 0;
  transform-origin: 50% 50%;
  transition: transform 0.2s ease;
}

.nav-rejoin-arrow svg {
  width: 20px;
  height: 20px;
}

/* --- Carte recouverte ---
   Posée sur <body> par map-access-guard.js. Neutralise les commandes qui
   vivent hors de l'application — au premier chef « Terminer l'exploration »,
   dans l'en-tête WordPress — tant qu'une modale occupe l'écran.

   `pointer-events: none` plutôt qu'un masquage : le bouton reste à sa place et
   ne fait pas sauter la mise en page de l'en-tête à chaque ouverture de
   feuille. L'opacité dit qu'il n'est pas disponible.

   `data-map-guard` EST UN ATTRIBUT, PAS UNE CLASSE. Écrit dans un `class=`, il
   ne correspond à aucun de ces sélecteurs et n'a donc aucun effet. À poser sur
   le CONTENEUR de la commande — le lien ET son pictogramme s'éteignent alors
   ensemble, ce qu'un attribut posé sur le seul lien ne peut pas faire. */
body.map-covered [data-map-guard],
body.map-covered #btn-finish,
body.map-covered .icon-end-exploration {
  pointer-events: none;
  opacity: 0.4;
}

/* ENVOI EN COURS. Même traitement que la carte recouverte, et pour une raison
   voisine : le geste n'est pas disponible, mais le bouton reste à sa place
   plutôt que de disparaître sous les doigts.

   `pointer-events` EST LE SEUL QUI COMPTE VRAIMENT ici : la cible est un <a>
   dans le menu WordPress, où l'attribut `disabled` n'a aucun effet. Le script
   coupe aussi le clic de son côté (voir setFinishBusy, evaluation-ui.js) — deux
   verrous parce que celui-ci tombe si le thème remplace le balisage, et l'autre
   si un gabarit oublie cette feuille. */
[data-map-guard][aria-busy="true"],
#btn-finish[aria-busy="true"],
[data-map-guard]:has(#btn-finish[aria-busy="true"]) {
  pointer-events: none;
  opacity: 0.55;
  cursor: progress;
}

/* L'OPACITÉ NE S'APPLIQUE QU'UNE FOIS, AU PLUS EXTÉRIEUR DES ÉLÉMENTS GARDÉS.

   Les opacités se MULTIPLIENT : un conteneur à 0,4 portant un lien lui-même à
   0,4 rend ce lien à 0,16, c'est-à-dire presque effacé — et à côté d'un
   pictogramme resté à 0,4, deux fois et demie plus lisible que le texte qu'il
   accompagne. C'est le défaut qu'on voit dès qu'on garde le conteneur en plus
   de ce qu'il contient, ce qui est précisément la bonne façon de le faire. */
body.map-covered [data-map-guard] [data-map-guard],
body.map-covered [data-map-guard] #btn-finish,
body.map-covered [data-map-guard] .icon-end-exploration {
  opacity: 1;
}

/* Croix d'abandon, la même sur les trois écrans du parcours — c'est ce qui avait
   fait diverger leurs deux teintes de blanc du temps où chacune avait sa règle.
   44px : la cible tactile recommandée, la même que .track-close. */
.choice-close {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  /* Rattrape le retrait droit du bandeau : le bouton porte son propre
     espacement interne, et sans cette marge il flotterait loin du bord. */
  margin-right: -10px;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.choice-close svg {
  width: 26px;
  height: 26px;
}

.choice-close:active {
  transform: scale(0.92);
}

/* --- Liste des options --- */

.choice-options {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 16px 0;
}

/* .choice-actions A ÉTÉ RETIRÉ. Les étapes 1 et 2 n'ont plus de barre de
   boutons : on n'y vient pas valider — choisir une option fait passer à la
   suite d'elle-même — et renoncer se fait par la croix de l'en-tête, où le
   regard la cherche. Deux boutons en bas pour un écran dont aucun des deux
   n'est le geste attendu ajoutaient une rangée à traverser. */

.choice-option {
  /* Fallbacks dérivés de --option-bg : un style inline posé par le JS les
     écrase (spécificité plus forte). */
  --option-title: color-mix(in srgb, var(--option-bg) 35%, #000);
  --option-text: color-mix(in srgb, var(--option-bg) 50%, #000);
  --option-border: color-mix(in srgb, var(--option-bg) 60%, #000);
  --option-icon-bg: color-mix(in srgb, var(--option-bg) 70%, #fff);

  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  /* HAUTEUR ÉGALE POUR TOUTES, quelle que soit la longueur de la description.
     `min-height` ne l'assurait pas : une description sur deux lignes poussait sa
     rangée plus haut que les autres, et les filets de séparation tombaient à
     des intervalles inégaux. La liste étant une colonne flex, `stretch` sur
     l'axe transversal aligne les hauteurs sur la plus grande — c'est l'égalité
     demandée, sans hauteur fixe à deviner ni troncature. */
  min-height: 64px;
  align-self: stretch;
  border: none;
  /* Angles droits : le filet de séparation est rectiligne, et un fond arrondi
     au-dessus de lui laissait voir la courbe à ses deux extrémités. */
  border-radius: 0;
  background: transparent;
  padding: 12px 6px 12px 0;
  text-align: left;
  cursor: pointer;
}

.choice-option:active {
  background: rgba(0, 0, 0, 0.045);
}

/* FILET DE SÉPARATION, entre deux options seulement.
   Posé en border-top sur le sélecteur adjacent plutôt qu'en border-bottom sur
   toutes : pas de ligne sous la dernière, et pas de règle à écrire pour l'en
   retirer. L'intertitre « Cas particuliers » rompt naturellement la suite —
   il n'est pas une .choice-option, donc l'option qui le suit n'hérite d'aucun
   filet, et le titre fait la séparation à sa place. */
.choice-option + .choice-option {
  border-top: 1px solid #E7EAE9;
}

/* --- Pastille d'icône ---
   ATTENTION : .choice-option-icon n'est PAS réservée au panneau de choix. Le
   JS la réutilise pour la pastille des pills de la topbar (setPillValue) et
   pour le récap de la modale de validation (fillSummaryItem). D'où une taille
   de base, puis un agrandissement ciblé sur les deux contextes qui le veulent.
   Le fond reste --option-icon-bg, qui porte la couleur du type. */
.choice-option-icon {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: 8px;
  background: var(--option-icon-bg);
  display: flex;
  align-items: center;
  justify-content: center;

  /* GLYPHE BLANC, PARTOUT.
     Chaque icône portait sa propre teinte, accordée au fond pastel de son
     option : sur la pastille, plus dense, ces teintes moyennes se noyaient, et
     huit couleurs différentes se disputaient l'attention pour dire une chose
     que le fond disait déjà. Le blanc les uniformise et laisse la couleur au
     seul endroit qui la porte utilement — la pastille elle-même.
     Les tracés sont désormais tous en currentColor (voir evaluation-options.js) :
     cette seule ligne suffit, ici comme dans le récap de la modale de
     validation et dans les types de la modale de point d'intérêt, qui
     réutilisent tous cette classe. */
  color: #fff;
}

.choice-option-icon svg {
  width: 18px;
  height: 18px;
}

/* Grande pastille : rangées du panneau de choix ET récap de la modale de
   validation. Les pills de la topbar gardent la taille de base. */
.choice-option .choice-option-icon,
.summary-item .choice-option-icon {
  width: 46px;
  height: 46px;
  border-radius: 12px;
}

/* 22 px SAUF MENTION CONTRAIRE. Deux pictogrammes sont tracés au contour, fin
   et détaillé, et disparaissaient à cette taille : ils déclarent un `iconSize`
   (voir evaluation-options.js), posé en variable par le JS sur la rangée comme
   sur le récap. La pastille mesurant 46 px, la valeur n'a de sens que jusqu'à
   34 environ — au-delà le dessin touche ses bords. */
.choice-option .choice-option-icon svg,
.summary-item .choice-option-icon svg {
  width: var(--option-icon-size, 22px);
  height: var(--option-icon-size, 22px);
}

.choice-option-body {
  flex: 1;
  min-width: 0;
}

.choice-option strong {
  display: block;
  font-size: 17px;
  font-weight: 700;
  margin-bottom: 2px;
  color: #14181a;
}

.choice-option p {
  margin: 0;
  font-size: 14px;
  line-height: 1.3;
  color: #5f6b66;
}

/* ACCENT DANS LE DESCRIPTIF (voir `descEmphasis`, evaluation-options.js).
   La graisse seule, sans changement de couleur : le descriptif est une phrase,
   et deux teintes à l'intérieur la couperaient en deux morceaux qu'on lirait
   séparément. */
.choice-option p strong {
  font-weight: 700;
}

/* LE RANG A ÉTÉ RETIRÉ DES DEUX ÉCHELLES.

   Un disque gris numéroté fermait chaque rangée, terrain comme pente. Il
   situait une option parmi les autres — ce que l'ordre de la liste dit déjà,
   sans qu'on ait à le lire — et il coûtait à chaque rangée la largeur qui
   manque au descriptif, seul à faire choisir entre deux degrés voisins.

   La colonne des pratiques (trois repères barrés reliés par un trait) a occupé
   la même place quelque temps, sur le terrain seulement, et a été retirée pour
   la même raison. Voir evaluation-options.js, où le raisonnement est conservé
   au cas où la question reviendrait. */

/* --- Ligne "Ne plus demander la pente" ---
   Presente uniquement sur l'etape pente (masquee par le JS sur la qualite).
   Elle est SOEUR de .choice-options, pas enfant : elle reste donc active quand
   les options, elles, sont desactivees. */

/* MASQUÉE. Le parcours est devenu obligatoire — qualité, puis pente, puis
   validation — et un réglage dont le rôle est justement de sauter l'étape 2/2
   ne s'y accorde plus.

   Règle isolée, et ciblée par l'id pour n'avoir à se soucier d'aucun ordre de
   déclaration : la SUPPRIMER suffit à tout réactiver. Le balisage, la case et
   toute la logique de evaluation-ui.js (state.alwaysFlat,
   applyAlwaysFlatToCurrentTrack, syncAlwaysFlatStyle, et le raccourci vers la
   validation quand la qualité est déjà connue) restent en place et
   fonctionnels — rien n'a été retiré, seulement rendu inatteignable. */
#slope-auto { display: none; }


.slope-auto {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin: 10px 16px 0;
  padding: 16px 14px;
  border-top: 1px solid #e6e4dd;
  cursor: pointer;
}

/* Etat actif : fond creme et barre sombre a gauche, comme une option retenue */
.slope-auto.checked {
  background: var(--color-cream);
  border-top-color: transparent;
  border-radius: 12px;
  box-shadow: inset 5px 0 0 #14181a;
}

.slope-auto input { display: none; }

.slope-auto-box {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  border: 2.5px solid #14181a;
  background: #fff;
  position: relative;
}

.slope-auto.checked .slope-auto-box {
  background: #14181a;
}

.slope-auto.checked .slope-auto-box::after {
  content: "";
  position: absolute;
  left: 9px;
  top: 3px;
  width: 6px;
  height: 12px;
  border: solid #fff;
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg);
}

.slope-auto-text strong {
  display: block;
  font-size: 16px;
  font-weight: 700;
  color: #14181a;
  margin-bottom: 2px;
}

.slope-auto-text span {
  font-size: 14px;
  line-height: 1.3;
  color: #5f6b66;
}

/* Options grisees quand le reglage automatique est actif. L'attribut disabled
   est pose par syncAlwaysFlatStyle et bloque deja le clic ; le style ne fait
   que le rendre visible. */
.choice-option:disabled {
  opacity: 0.4;
  cursor: default;
}

.choice-option:disabled:active {
  background: transparent;
}

/* Intertitre "Cas particuliers" */
.choice-section-label {
  margin: 18px 0 2px;
  padding-top: 16px;
  border-top: 1px solid #e6e4dd;
  font-size: 14px;
  color: #7d8a80;
}

/* Options à fond sombre (Disparu, Pente abrupte, Mur).
   IMPORTANT : la rangée étant désormais blanche, l'ancienne variante "texte
   clair" rendrait le titre blanc sur blanc, donc invisible. Elle ne s'applique
   plus qu'à la pastille, dont le fond reste sombre et dont les icônes sont
   déjà tracées en clair. */
.choice-option.text-light {
  --option-title: #fff;
}

.choice-option.text-light strong { color: #14181a; }
.choice-option.text-light p { color: #5f6b66; }

/* =========================================================
   5. Modale de validation finale
   ========================================================= */
.modal-desc {
  font-size: 14px;
  color: var(--color-text-dark);
  line-height: 1.5;
  padding: 16px 20px 0;
  margin: 0;
}

.summary-row {
  display: flex;
  gap: 10px;
  /* UN CADRE PLUTÔT QU'UN FOND. Le bandeau crème empilait deux niveaux de fond
     sous des pastilles déjà colorées ; un filet délimite aussi bien le
     récapitulatif sans rien peindre derrière lui. */
  border: 1px solid darkgrey;
  border-radius: 20px;
  margin: 16px 20px 0;
  padding: 12px 16px;
}

.summary-item {
  flex: 1;
  display: flex;
  align-items: center;
  /* Centré, et non collé à gauche : les deux moitiés du récapitulatif se
     regardent, et un libellé court laissait la sienne flotter. */
  justify-content: center;
  gap: 8px;
}

/* La pastille du recap reutilise .choice-option-icon (classe posee par le JS
   dans fillSummaryItem). Sa taille agrandie est definie plus haut, avec les
   autres regles de .choice-option-icon. L'ancienne regle .summary-icon (rond
   de couleur) n'est plus utilisee. */

.summary-label {
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: var(--color-muted);
  letter-spacing: 0.03em;
}

.summary-item strong {
  font-size: 14px;
  color: var(--color-text-dark);
}

.modal-actions {
  display: flex;
  gap: 10px;
  padding: 16px 20px 0;
}

/* BOUTONS TOUJOURS VISIBLES DANS LA MODALE DE POI.
   Elle est la plus longue des modales flottantes — titre, type, photos,
   description — et c'est la seule dont la carte défile en entier : « AJOUTER »
   sortait de l'écran dès qu'on touchait le champ de description, au moment
   précis où l'on vient de finir de le remplir. La modale de démarrage n'a
   jamais eu le problème parce que seule sa liste d'étapes défile, le reste
   étant figé (voir #intro-modal .modal.card).

   `sticky` plutôt qu'une restructuration en trois zones : la carte est déjà la
   boîte défilante, et une barre collante suffit à en tenir le bas.

   Le padding du bas passe de la CARTE à la BARRE. Sur un élément collant, la
   position se cale sur le bord de la boîte de remplissage : laisser les 20px à
   la carte aurait fait flotter les boutons d'autant au-dessus du bord, avec une
   bande blanche dessous pendant tout le défilement. Même piège que
   .modal-actions-sticky, où il est décrit en détail. */
#poi-modal .modal.card {
  padding-bottom: 0;
}

#poi-modal .modal-actions {
  position: sticky;
  bottom: 0;
  z-index: 1;

  /* Fond opaque, sans quoi le champ de description transparaîtrait derrière les
     boutons ; le dégradé dit qu'il reste du contenu au-dessus. */
  background: #fff;
  padding-bottom: 20px;
  box-shadow: 0 -8px 12px -8px rgba(0, 0, 0, 0.18);
}

.modal-actions .btn {
  width: auto;
  flex: 1;
  margin: 0;
}

.optional-divider {
  text-align: center;
  font-size: 14px;
  font-weight: 700;
  color: var(--color-muted);
  letter-spacing: 0.03em;
  margin: 20px 20px 8px;
  border-top: 1px solid var(--color-border);
  padding-top: 14px;
}

.options-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* 10px de moins que les 20px d'origine : .option-row les reprend en padding
     interne, pour que le fond de la ligne cochée déborde du texte sans
     déplacer celui-ci. */
  padding: 0 10px;
}

.option-row {
  /* Repère du séparateur ci-dessous. */
  position: relative;

  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 10px;

  /* La rangée cochée est un bloc de couleur pleine, dont les angles se voient —
     ce qui n'était pas le cas d'un fond presque blanc. */
  border-radius: 10px;

  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

/* SÉPARATEUR DROIT SOUS UNE RANGÉE ARRONDIE.

   Il était porté par `border-bottom`, et une bordure épouse le rayon : dès que
   la rangée s'est arrondie, le trait s'est incurvé à ses deux bouts et pendait
   sous elle comme une anse. Un trait de séparation ne suit pas la forme de ce
   qu'il sépare — il traverse.

   D'où un rectangle posé à part, qui ne connaît pas le rayon. Il vit DANS
   l'écart de 4px entre deux rangées plutôt que collé au bas de la première :
   c'est une frontière entre deux, pas un soulignement de l'une.

   Retiré des 10px de padding à chaque bout, pour qu'il s'aligne sur le texte
   et non sur le fond coloré, qui déborde. */
.option-row::after {
  content: '';
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: -2.5px;
  height: 1px;
  background: var(--color-border);
  pointer-events: none;
}

/* Le texte change de couleur en même temps que le fond ; sans ces transitions,
   il basculerait d'un coup sur un fond encore en train de virer. */
.option-row strong,
.option-row p {
  transition: color 0.15s ease;
}

/* Rien à séparer après la dernière, ni sous une rangée cochée : le bloc vert
   plein se suffit, et le filet gris qui l'ourlait par en dessous se lisait
   comme un défaut d'alignement. */
.option-row:last-child::after,
.option-row:has(input[type="checkbox"]:checked)::after {
  display: none;
}

.option-row strong {
  display: block;
  font-size: 14px;
  color: var(--color-text-dark);
}

.option-row p {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--color-muted);
  line-height: 1.35;
}

/* CASE DESSINÉE, ET NON LA CASE DU SYSTÈME.
   `accent-color` ne permettait que d'en teinter le remplissage : la forme, elle,
   reste dessinée par le navigateur, et `border-radius` n'a aucun effet dessus.
   Arrondir la case impose donc `appearance: none`, qui vide complètement le
   contrôle — d'où le fond, la bordure et la coche à reposer ci-dessous.

   L'input garde son rôle : c'est toujours lui qui porte l'état coché, le focus
   clavier et la sémantique. Seul son dessin change. */
.option-row input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;

  width: 22px;
  height: 22px;
  flex-shrink: 0;
  margin: 0;

  border: 1.5px solid var(--color-border);
  border-radius: 8px;
  background-color: #fff;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 14px 14px;

  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

/* La coche est une IMAGE DE FOND et non un ::after : les pseudo-éléments sur un
   <input> ne sont pas garantis par la spécification, l'input étant un élément
   remplacé. Un background, lui, marche partout.

   Le tracé est en BLANC, une constante — les couleurs de la charte ne peuvent
   pas entrer dans une data-URI, où `var()` n'est pas interprété. C'est donc la
   case qui porte la couleur, jamais la coche : rien à resynchroniser le jour où
   la charte bouge. */
.option-row input[type="checkbox"]:checked {
  background-color: var(--color-header-dark);
  border-color: var(--color-header-dark);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'%3E%3Cpath d='M5 12.5L10 17.5L19 7' stroke='%23ffffff' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Anneau de focus, que `appearance: none` avait emporté avec le reste. */
.option-row input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--color-lime);
  outline-offset: 2px;
}

/* =========================================================
   6. Modale récap de fin
   ========================================================= */
.summary-card {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 14px;
  margin: 16px 20px 0;
  padding: 18px 18px 14px;
}

.summary-card-title {
  text-align: center;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--color-text-dark);
  margin: 0 0 14px;
}

.summary-total {
  margin: 0 0 14px;
  font-size: 15px;
  color: var(--color-muted);
}

.summary-total #summary-total-km {
  font-size: 30px;
  font-weight: 700;
  color: var(--color-header-dark);
}

.summary-total-unit {
  font-size: 15px;
  color: var(--color-muted);
}

/* --- Points d'exploration ---
   CARTE À PART, au même niveau que le résumé et juste au-dessus des boutons.
   Mêmes fond, bordure et marges que .summary-card : les deux se lisent comme
   deux blocs de même rang, et non comme une ligne perdue dans l'autre. */
/* HORS DE LA ZONE DÉFILANTE, entre le corps et les boutons.

   `flex-shrink: 0` est ce qui l'y maintient : dans une colonne flex, un enfant
   se laisse comprimer avant que son voisin ne commence à défiler. Sans lui, le
   bloc s'écraserait au lieu de garder sa hauteur, et le corps continuerait de
   remplir l'écran — même piège que celui documenté sur
   #intro-modal .modal.card > *:not(.steps-list). */
.summary-score {
  flex-shrink: 0;

  display: flex;
  align-items: center;
  gap: 14px;

  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: 14px;
  margin: 14px 20px 0;
  padding: 14px 16px;
}

/* Pastille lime : le seul aplat de couleur vive du bilan. Il signale une
   récompense là où tout le reste est un constat. */
.summary-score-icon {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--color-lime);
  color: var(--color-text-dark);
  display: flex;
  align-items: center;
  justify-content: center;
}

.summary-score-icon svg {
  width: 26px;
  height: 26px;
}

.summary-score-text {
  min-width: 0;
}

.summary-score-gain {
  margin: 0;
  font-size: 15px;
  color: var(--color-muted);
}

/* Le nombre porte seul l'emphase, et se lit sur la même ligne que son unité :
   c'est lui qu'on cherche, le reste dit seulement de quoi il s'agit. */
#summary-score-session {
  font-size: 22px;
  font-weight: 700;
  color: var(--color-text-dark);
  font-variant-numeric: tabular-nums;
}

.summary-score-total {
  margin: 2px 0 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}

/* Barème des points, sous le score (voir ensureScoreNote, evaluation-ui.js).

   PLUS PETITE ET PLUS PÂLE QUE LE TOTAL, volontairement : elle explique le
   chiffre, elle ne le concurrence pas. L'œil doit trouver le gain d'abord et
   ne descendre ici que s'il se demande d'où il vient.

   L'INTERLIGNE EST AUGMENTÉ parce que cette ligne-ci est la seule du bloc à
   passer sur deux ou trois lignes sur un téléphone : au pas des voisines, les
   phrases se colleraient. */
.summary-score-note {
  margin: 6px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--color-muted);
  opacity: 0.85;
}

.summary-section-label {
  font-size: 14px;
  color: var(--color-muted);
  margin: 12px 0 6px;
}

.summary-lines {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.summary-line {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text-dark);
}

/* --- Actions du bilan, toujours visibles ---
   Le récapitulatif dépasse facilement la hauteur d'un téléphone : les deux
   boutons se retrouvaient sous la ligne de flottaison, et rien ne disait qu'il
   fallait faire défiler pour sortir de la modale.

   PAS DE position: sticky ICI, contrairement à la modale de validation. Une
   barre collante reste dans le flux du conteneur défilant : elle en épouse le
   bord, mais reste soumise à son rognage — angles arrondis et fin de boîte
   comprises — et le bas du bouton s'y faisait couper.

   La carte devient donc une COLONNE FLEX à trois étages : en-tête figé, corps
   défilant, barre d'actions figée. C'est le patron de la modale plein écran
   (.overlay-backdrop.fullscreen .modal-body), et il ne peut rien rogner :
   la barre n'est plus dans la zone qui défile. */
#summary-modal .modal.card {
  display: flex;
  flex-direction: column;

  /* Le défilement descend d'un cran, dans .modal-body. `hidden` et non `auto` :
     la carte ne doit plus défiler du tout, et c'est ce qui fait tenir ses
     angles arrondis autour du contenu. */
  overflow: hidden;
  padding-bottom: 0;
}

#summary-modal .modal-body {
  flex: 1;

  /* INDISPENSABLE. La hauteur minimale par défaut d'un enfant flex est celle de
     son contenu : sans ce zéro, le corps refuse de rétrécir, la carte dépasse
     sa max-height et c'est de nouveau la barre d'actions qui sort de l'écran. */
  min-height: 0;

  overflow-y: auto;
  /* Le défilement s'arrête ici plutôt que de repartir en pull-to-refresh. */
  overscroll-behavior: contain;
}

.summary-actions {
  flex-shrink: 0;
  background: #fff;
  padding: 6px 0 18px;
  /* Ombre portée vers le haut : elle signale qu'il y a du contenu dessous. */
  box-shadow: 0 -8px 12px -8px rgba(0, 0, 0, 0.18);
}

.summary-line .summary-line-value {
  color: var(--color-text-dark);
  font-variant-numeric: tabular-nums;
}

.summary-extras {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--color-border);
  font-size: 14px;
  color: var(--color-header-dark);
  font-weight: 600;
}

/* Agrandi de 16 à 22px. La ligne des points d'intérêt est la seule du
   récapitulatif à porter un pictogramme, et elle devient la seule ligne tout
   court quand aucun chemin n'a été évalué : à 16px, l'épingle se lisait comme
   une puce de liste plutôt que comme le symbole de ce qu'on vient d'ajouter. */
.summary-pin {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}

.btn-text {
  background: transparent;
  border: none;
  color: var(--color-muted);
  font-weight: 600;
  font-size: 14px;
  margin-top: 4px;
}

/* =========================================================
   Pile basse : bouton "Recentrer" + panneau du tracé sélectionné
   -----------------------------------------------------------------
   Le haut du panneau n'est pas à une position fixe (hauteur dictée par
   le contenu en mode compact, 55vh en mode agrandi). Empiler le bouton
   et le panneau dans un conteneur ancré en bas est donc le seul moyen
   de garder le bouton juste au-dessus du panneau dans les deux états.
   ========================================================= */

/* La feuille de tracé est ANCRÉE au bas de la zone carte, sans marge : c'est ce
   qui la distingue de l'ancienne carte flottante. Le bouton « Recentrer », lui,
   garde son espacement grâce au gap et à la marge posée plus bas. */
.bottom-stack {
  position: fixed;
  left: 0;
  right: 0;
  bottom: var(--bottom-bar-h);
  z-index: 910; /* au-dessus de la carte, sous les panneaux de choix (940+) */

  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;

  max-width: 560px;
  margin: 0 auto;

  /* Le conteneur occupe toute la largeur : sans ça, il intercepterait les
     gestes destinés à la carte dans les zones vides autour du bouton. */
  pointer-events: none;
}

/* Le bouton flotte encore, lui : il garde sa respiration au-dessus de la
   feuille, qui est collée au bord. */
.bottom-stack .btn-recenter {
  margin-bottom: 4px;
}

/* Sans feuille en bas — ni tracé, ni destination — la pile est collée au bas de
   la carte et le bouton avec elle. Il retrouve alors la marge qu'il avait quand
   la feuille la lui donnait.
   LES DEUX PANNEAUX comptent : ils occupent la même place et ne s'ouvrent
   jamais ensemble, mais l'un ou l'autre suffit à coller le bouton. */
.bottom-stack:not(:has(#track-panel:not(.hidden))):not(:has(#destination-panel:not(.hidden))) .btn-recenter {
  margin-bottom: 16px;
}

.bottom-stack > * {
  pointer-events: auto;
}

.btn-recenter {
  border: none;
  border-radius: var(--radius-pill);
  background: #fff;
  color: var(--color-header-dark);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.02em;
  /* 44px de haut : cible tactile confortable, comme .track-close. */
  padding: 13px 24px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  cursor: pointer;
  white-space: nowrap;
}

.btn-recenter:active {
  transform: scale(0.97);
}

/* Le panneau ne porte plus son positionnement : il redevient un élément de
   flux dans .bottom-stack, ce qui permet au bouton de se placer au-dessus de
   lui quelle que soit sa hauteur. */
/* Effacé le temps qu'une feuille le recouvre (voir setBehindSheet dans
   track-panel.js). `display: none` et non une opacité : les deux occupent le
   même bas d'écran, et un panneau translucide dessous se voyait encore par
   transparence sur les bords arrondis de la feuille. */
.track-panel.is-behind-sheet {
  display: none;
}

.track-panel {
  width: 100%;
  background: #fff;
  /* Ancrée en bas : les angles hauts seuls sont arrondis, et la feuille s'étend
     jusqu'au bord. */
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-card);
  padding: 10px 16px 16px;
}

/* --- Pastilles d'évaluation, descendues de l'ancienne barre du haut --- */
.track-choices {
  display: flex;
  gap: 10px;
  margin-top: 12px;
}

.track-choices > * {
  flex: 1;
  min-width: 0;
}

/* --- Bouton de validation, pleine largeur --- */
.track-validate {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  margin-top: 10px;
  padding: 14px;

  border: none;
  border-radius: 12px;
  background: var(--color-header-dark);
  color: #fff;

  font-size: 16px;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}

.track-validate svg {
  width: 16px;
  height: 13px;
  flex-shrink: 0;
}

.track-validate:active {
  transform: scale(0.99);
}

/* Désactivé : l'évaluation est incomplète, ou le chemin hors de portée. */
.track-validate:disabled {
  background: #E3E7E5;
  color: #9AA5A0;
  cursor: default;
}

/* --- Ligne 1 : longueur du chemin, puis les actions --- */
.track-panel-top {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  height: 44px;
}

/* La longueur du chemin est redevenue l'information de tête du panneau : c'est
   la seule qui ne dépend ni du GPS ni de l'état d'évaluation, donc la seule
   toujours lisible. */
.track-length {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  color: var(--color-text-dark);
}

/* Porte désormais le PICTOGRAMME D'ÉTAT (voir renderEvalCard, track-panel.js),
   et non plus le mètre ruban — celui-ci est passé au bouton de détail.

   Un badge, donc un disque plein : d'où les 28px, contre 24 pour le ruban qui
   était un tracé au trait, et --badge-glyph qui peint le dessin en blanc sur ce
   disque sombre. Sans cette variable, le glyphe hériterait de la valeur de
   secours (#fff) — juste ici, mais par accident : chaque contexte qui affiche un
   badge déclare la sienne. */
.track-length-icon {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  color: var(--color-header-dark);
  --badge-glyph: #fff;
}

.track-length-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

.track-length-value {
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Approche de la fin : le badge d'alerte prend la place de la longueur plutôt
   que de s'ajouter à sa droite. Les deux ensemble ne tiennent pas à côté de
   « Y aller » et de la croix sur un écran de 360px, et à cet instant précis
   savoir que le chemin se termine vaut mieux que savoir combien il mesure. */
.track-panel.near-end .track-length {
  display: none;
}

.track-panel-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}

/* Bouton "Y aller". Son contenu (fleche + libelle) est injecte par
   track-panel.js depuis POSITION_ARROW_SVG. */
.track-goto {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex-shrink: 0;
  background: var(--color-header-dark);
  color: #fff;
  border: none;
  border-radius: var(--radius-pill);
  height: 40px;
  padding: 0 18px;
  font-size: 15px;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}

.track-goto svg {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}

/* La fleche herite de .position-arrow, prevue pour la carte : on neutralise
   son ombre portee et sa transition de rotation, inutiles sur un bouton.
   fill et stroke sont en currentColor, donc elle suit le blanc du bouton. */
.track-goto .position-arrow {
  filter: none;
  transition: none;
}

.track-close {
  /* 36px de cercle dans une cible tactile qui reste large grâce au padding :
     le contour rend la croix repérable sur un fond blanc chargé. */
  width: 36px;
  height: 36px;
  border: 1.5px solid #D4D9D6;
  border-radius: 50%;
  background: transparent;
  color: var(--color-text-dark);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.track-close svg {
  width: 24px;
  height: 24px;
}

/* =========================================================
   Ligne 2 : carte d'état, et seule entrée du parcours de saisie
   =========================================================
   Elle remplace les deux pastilles Terrain / Pente, qui posaient deux
   questions là où l'utilisateur n'en a qu'une : « est-ce que ce chemin est
   déjà décrit, et est-ce que je touche à ça ? ». Le résumé répond à la
   première, le libellé de l'action à la seconde.

   DEUX APPARENCES, pas quatre : un chemin non évalué appelle une action, c'est
   la carte pleine ; un chemin déjà décrit n'appelle rien, c'est la carte
   teintée. Les quatre états d'évaluation continuent de se distinguer par le
   surtitre et le pictogramme, comme sur la carte. */
.track-eval {
  display: flex;
  align-items: center;
  /* Libellé et chevron centrés ensemble. Le chevron collé au bord droit tirait
     l'œil vers un coin vide : le bouton ne porte plus qu'un mot d'ordre, et
     c'est lui qu'on lit, pas l'endroit où il finit. */
  justify-content: center;
  gap: 8px;

  /* Toute la place restante à droite du bouton de détail. `margin-top` est
     passé sur .track-eval-row, qui porte désormais l'écart avec la ligne du
     dessus. */
  flex: 1;
  min-width: 0;
  padding: 14px 14px 14px 18px;

  font-size: 15.5px;
  font-weight: 700;

  /* BORDURE PLUTÔT QU'UN APLAT NU.
     Sans contour, la carte se confondait avec le fond du panneau : rien ne
     disait qu'elle a un bord, donc rien ne disait qu'on peut appuyer dessus.
     Un liseré referme la forme et la fait exister comme objet.

     Déclarée transparente ici, et COLORÉE PAR ÉTAT juste en dessous : chaque
     fond a son liseré, toujours une nuance plus soutenue que lui. Une couleur
     unique aurait juré sur l'un des trois. */
  border: 2px solid transparent;
  border-radius: 14px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}

.track-eval:active {
  transform: scale(0.995);
}

/* Chemin jamais évalué, ET À PORTÉE : c'est l'action principale du panneau, et
   elle est possible maintenant.
   VERT PLEIN, TEXTE BLANC. Le bouton était blanc cerclé de vert, par crainte
   qu'un aplat de couleur n'écrase la longueur et la ligne de progression. C'est
   le voisin qui a changé : le bouton de détail passe au blanc et laisse celui-ci
   seul à porter du plein. Deux boutons contournés côte à côte ne hiérarchisaient
   plus rien — le remplissage dit lequel des deux est l'action.

   `:not(.is-disabled)` EST INDISPENSABLE ICI, et n'était pas nécessaire avant :
   la règle grise plus bas suffisait à recouvrir un fond blanc, elle ne suffirait
   pas à recouvrir proprement un vert plein dans tous les navigateurs où
   `--badge-glyph` reste hérité. On dit donc explicitement que le vert est
   réservé au cas où l'évaluation est réellement ouverte. */
.track-eval[data-state="none"]:not(.is-disabled) {
  background: var(--color-header-dark);
  border-color: var(--color-header-dark);
  color: #fff;
  /* Peint le glyphe du pictogramme (voir badgeIcon dans track-panel.js) : le
     disque prend currentColor, le dessin prend cette variable — ici le fond du
     bouton, désormais vert. */
  --badge-glyph: var(--color-header-dark);
}

/* Chemin déjà décrit : rien à faire, la carte se retire au second plan.
   Son liseré est la même teinte, juste assez soutenue pour dessiner le bord
   sans ramener la carte au premier plan.
   ⚠ #BBD5C6 EST ENCORE UN VERT, et il cercle maintenant un fond parme. Il n'a
   pas été déplacé faute de valeur demandée ; son équivalent dans la nouvelle
   gamme serait aux environs de #D9C0E6. */
.track-eval:not([data-state="none"]) {
  background: #F1EAF4;
  border-color: #BBD5C6;
  color: var(--color-header-dark);
  --badge-glyph: #F1EAF4;
}

/* Trop loin, ou position inconnue : le bouton reste CLIQUABLE (c'est lui qui
   affiche le message expliquant pourquoi), il change seulement d'aspect. */
/* Placée APRÈS les deux règles ci-dessus, de spécificité égale : c'est l'ordre
   qui tranche, et l'état « hors de portée » doit primer sur l'état d'évaluation.
   Le liseré suit le gris du fond, sans rien promettre. */
.track-eval.is-disabled {
  background: #EEF0EF;
  border-color: #D9DEDC;
  color: #8E9A93;
  --badge-glyph: #EEF0EF;
}

/* --- LA LIGNE D'ACTIONS ---

   Deux boutons : LIRE, puis ÉCRIRE. Le premier ouvre le détail, le second le
   parcours de saisie.

   CE QUI A DISPARU D'ICI : .track-eval-icon, .track-eval-text,
   .track-eval-eyebrow, .track-eval-title et ses deux moitiés. Ils habillaient
   le résumé d'une ligne que portait cette carte — surtitre, qualité tronquée à
   210px exactement, pente incompressible. Ce résumé n'existe plus : il ne
   pouvait montrer qu'UNE évaluation, et le détail a maintenant son propre
   écran, où les deux coexistent. */
.track-eval-row {
  display: flex;
  align-items: stretch;
  gap: 10px;
  margin-top: 12px;
}

/* Le bouton de détail garde une largeur FIXE : c'est le bouton d'évaluation qui
   prend la place restante, et son libellé change de longueur selon l'état
   (« Apporter » / « Modifier »). L'inverse ferait bouger le pictogramme d'un
   chemin à l'autre. */
.track-details {
  flex-shrink: 0;
  width: 56px;

  display: flex;
  align-items: center;
  justify-content: center;

  /* BLANC CERCLÉ DE VERT, EN TOUTES CIRCONSTANCES. Les deux boutons ont encore
     échangé : le voisin porte maintenant le vert plein, celui-ci reste creux.
     C'est le REMPLISSAGE qui hiérarchise — plein pour l'action, contour pour la
     lecture — et non plus deux nuances de vert qu'un écran en plein soleil ne
     départage pas (même raison qu'en tête de tracks-layer.js).

     Le liseré est pris à mi-chemin plutôt qu'au vert de la charte : à pleine
     force, un contour de 2px sur 56px de large pèse autant qu'un aplat, et
     l'action ne se distinguerait plus. */
  border: 2px solid #ca93e8;
  border-radius: 14px;
  background: #fff;
  color: var(--color-header-dark);
  /* Peint le glyphe du pictogramme (voir badgeIcon dans track-panel.js) : le
     disque prend currentColor, le dessin prend cette variable — ici le fond du
     bouton, pour que le « i » soit ajouré dans le disque vert. */
  --badge-glyph: #fff;
  cursor: pointer;
}

/* CE BOUTON NE S'ÉTEINT PLUS AVEC SON VOISIN. Il le suivait au gris quand le
   chemin était hors de portée, pour que la ligne se lise d'un bloc. Mais les
   deux ne disent pas la même chose : être trop loin empêche d'ÉVALUER, et
   n'empêche en rien de CONSULTER ce qu'on sait déjà du chemin. Un bouton gris
   qui répond quand même est un mensonge de plus que celui qu'on voulait éviter.

   Il garde donc son blanc et son vert dans tous les cas — sauf à l'approche de
   la fin du chemin, où le panneau entier change de fond (voir plus bas). */

.track-details:active {
  transform: scale(0.97);
}

/* Le disque du badge « i » déborde volontairement de son carré de 24 (rayon 13
   au lieu de 11, voir BADGE_ICON_INFO) : sans `overflow: visible`, le SVG le
   rognerait sur ses quatre côtés. */
.track-details-icon {
  width: 32px;
  height: 32px;
}

.track-details-icon svg {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}

/* Le chevron suit le libellé : blanc sur le vert plein. `currentColor` plutôt
   qu'un blanc écrit en dur, pour que les règles d'état plus bas (hors de portée,
   fin du chemin) l'emportent sans avoir à le redéclarer chacune. */
.track-eval[data-state="none"] svg {
  color: currentColor;
}

/* Chemin hors de portée : le bouton est gris, et le lime ci-dessus y devenait
   illisible tout en promettant une action impossible. Le chevron rentre dans le
   rang et reprend le gris du bloc — même rendu qu'un chemin déjà évalué, ce qui
   est bien ce que dit l'état : il n'y a rien à faire ici, maintenant.
   Même spécificité que la règle précédente, donc c'est l'ORDRE qui tranche :
   celle-ci doit rester en dessous. */
.track-eval.is-disabled svg {
  color: inherit;
}

.track-eval > svg {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
}

/* =========================================================
   Ligne 3 : schéma du tracé
   =========================================================
   Le trait droit est devenu une COURBE dessinée en SVG. Un rectangle de 6px
   ne se lisait pas comme un chemin : il fallait le libellé d'extrémité pour
   comprendre ce qu'on regardait.

   Le viewBox garde son ratio (pas de preserveAspectRatio="none") : le trait
   conserve une épaisseur régulière et les deux pastilles restent rondes quelle
   que soit la largeur. La hauteur du dessin suit donc la largeur du panneau,
   d'où `height: auto` — c'est le SVG qui dicte la hauteur de la ligne, il n'y
   a plus de valeur en dur à rogner. */
.track-line-labels {
  display: flex;
  justify-content: space-between;
  margin-top: 15px;
  padding: 0 2px;

  font-size: 10px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* La bulle de position fait plus du double de la hauteur du dessin : centrée sur
   la courbe, elle déborde largement au-dessus. L'espace qui la sépare des
   libellés Début / Fin se règle donc à DEUX endroits — cette marge, et celle de
   .track-line-labels juste au-dessus. Les baisser tous les deux ferait remonter
   la bulle sur le texte. */
.track-line-wrap {
  position: relative;
  margin-top: 4px;
}

/* PAS d'overflow: visible ici. Le dessin tient entièrement dans son viewBox
   (contrôlé : trait et pastilles, épaisseurs comprises), et sur le <svg>
   racine un débordement autorisé fait entrer dans getBoundingClientRect() des
   éléments qui n'ont rien à y faire — la largeur mesurée par layoutLine
   (track-panel.js) s'en trouvait faussée, et le marqueur de position posé hors
   du panneau. */
.track-line {
  display: block;
  width: 100%;
  height: auto;
}

.track-line-path {
  fill: none;
  stroke: var(--color-header-dark);
  stroke-width: 3;
  stroke-linecap: round;
}

.track-line-cap {
  stroke: var(--color-header-dark);
  stroke-width: 1.8;
}

/* Départ : anneau creux. Arrivée : pastille pleine. La différence de remplissage
   dit le sens de lecture sans qu'aucun libellé n'ait à le répéter — et elle
   tient sur un trait de quelques pixels, contrairement au bleu/rouge précédent
   qui demandait de connaître un code. */
.track-line-cap.start { fill: #fff; }
.track-line-cap.end   { fill: var(--color-header-dark); }

/* L'utilisateur n'est pas sur le chemin sélectionné : le tracé n'a plus de
   position à porter ni de distance restante à découper. Il se réduit à un
   repère de forme, discret. Classe posée par setGpsFraction (track-panel.js). */
.track-panel.off-track .track-line-path {
  stroke: #C9D2CD;
  stroke-width: 1.8;
}

.track-panel.off-track .track-line-cap {
  stroke: #C9D2CD;
}

.track-panel.off-track .track-line-cap.end {
  fill: #C9D2CD;
}

/* Position de l'utilisateur SUR la courbe.
   .track-gps-dot n'a pas de dimension propre : c'est un POINT D'ANCRAGE dont
   les coordonnées en pixels sont posées par layoutLine (track-panel.js) à
   partir de getPointAtLength sur le path. C'est ce qui garantit que le
   marqueur suit la courbe et non une corde entre ses extrémités. */
.track-gps-dot {
  position: absolute;
  left: var(--gps-x, 0px);
  top: var(--gps-y, 0px);
  width: 0;
  height: 0;
  color: var(--color-position);
  transition: left 0.4s ease, top 0.4s ease;
}

/* LA BULLE EST LE MARQUEUR. Elle porte la distance restante, et sa pointe
   latérale donne le sens de marche — une flèche séparée en plus faisait deux
   objets pour une seule information, sur un trait de quelques pixels de haut. */
.track-gps-bubble {
  position: absolute;
  /* Centrée sur le point d'ancrage, donc à cheval sur la courbe, comme un
     curseur qui la parcourt.

     --gps-shift la ramène dans le cadre quand le point d'ancrage tombe trop
     près d'un bord : sans lui, stationner au départ d'un chemin coupait la
     moitié de la bulle. La valeur est calculée par layoutLine (track-panel.js),
     qui seul connaît la largeur du dessin et celle du texte affiché. */
  left: 0;
  top: 0;
  transform: translate(calc(-50% + var(--gps-shift, 0px)), -50%);

  /* Le chiffre et la pointe côte à côte DANS la pilule : c'est une boîte à deux
     éléments, plus une forme à laquelle on accroche un appendice. */
  display: flex;
  align-items: center;
  gap: 7px;

  padding: 6px 13px;
  border-radius: var(--radius-pill);

  /* APLAT VERT, CHIFFRE BLANC. On y revient.

     Elle avait été inversée — blanche cerclée de vert — au motif qu'un aplat de
     la même teinte que la courbe s'y fondait. L'objection ne tenait pas
     l'épreuve du terrain : le liseré blanc extérieur détache déjà la bulle de
     tout ce qui passe dessous, courbe comprise, et une pastille pleine se
     repère de bien plus loin qu'un contour — c'est le seul objet mobile du
     panneau, et on le cherche du regard avant de le lire.

     LE CHIFFRE NE PERD RIEN À PASSER EN BLANC : sur ce vert, il tient à plus de
     dix contre un, très au-delà de ce qu'exige un texte de cette taille.

     LE BORD RESTE DÉCLARÉ, à la couleur du fond, et il n'est pas inutile : il
     épaissit la pilule de 2,5px sur tout son pourtour, et c'est sur cette
     épaisseur que le liseré blanc du box-shadow vient se poser. Le retirer
     collerait le blanc au chiffre. */
  background: var(--color-position);
  color: #fff;
  border: 2.5px solid var(--color-position);

  /* 20px et non 18 : c'est la distance qui reste, la seule valeur qu'on lise en
     roulant, et elle est lue sur un trait de quinze pixels de haut au milieu
     d'un panneau chargé. */
  font-size: 20px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;

  /* Le liseré blanc reste, mais il est passé À L'EXTÉRIEUR du trait vert :
     c'est lui qui sépare la bordure de la bulle du trait de la courbe, qui
     est de la même couleur. */
  box-shadow: 0 0 0 2.5px #fff, 0 2px 7px rgba(0, 0, 0, 0.28);
}

/* --- Pointe de direction, À L'INTÉRIEUR DE LA BULLE ---

   ELLE ÉTAIT DEHORS, ET C'ÉTAIT CHER PAYÉ. Un triangle CSS n'a ni contour ni
   forme propre — ce n'est qu'un jeu de bordures sur une boîte vide — et lui
   donner un liseré imposait d'en empiler un plus grand dessous. La pilule
   n'offrant que deux pseudo-éléments, il avait fallu créer un <span> intérieur
   pour en obtenir un troisième, puis accorder à la main les cotes des trois
   triangles pour que leurs bases tiennent dans un flanc arrondi. Tout cela pour
   un appendice qui, aux extrémités du tracé, sortait du cadre et devait être
   rattrapé par --gps-shift.

   DEDANS, PLUS RIEN DE TOUT CELA N'EST NÉCESSAIRE. La pilule verte fournit le
   fond, le triangle blanc s'y détache seul, et il n'a besoin d'aucun liseré
   puisqu'il ne touche à rien d'autre. Un seul pseudo-élément suffit, posé dans
   le flux à côté du chiffre.

   LE CÔTÉ DIT LE SENS DE MARCHE. À droite du chiffre quand on va vers la fin,
   à gauche quand on revient vers le début : la pointe désigne le bout du chemin
   qu'on rejoint. C'est le seul indice d'un demi-tour, la courbe ne se
   retournant jamais. data-heading est posé par updateHeading (track-panel.js).

   GÉOMÉTRIE : 6px de demi-hauteur pour 9px de portée. Plus effilé qu'un
   équilatéral, comme le sont les têtes de lecture, et lisible à côté d'un
   chiffre de 20px sans lui disputer la place. */
.track-gps-bubble::after {
  content: '';
  width: 0;
  height: 0;
  flex-shrink: 0;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-left: 9px solid #fff;
}

/* Retour vers le début : la pointe passe DEVANT le chiffre et se retourne.
   `row-reverse` la déplace, les bordures la retournent — l'un sans l'autre
   donnerait une pointe à gauche qui montre encore la droite. */
.track-gps-dot[data-heading="back"] .track-gps-bubble {
  flex-direction: row-reverse;
}

.track-gps-dot[data-heading="back"] .track-gps-bubble::after {
  border-left: 0;
  border-right: 9px solid #fff;
}

/* Le <span> intérieur ne portait qu'un triangle, et n'a plus rien à porter. Il
   reste dans le balisage : c'est lui qui reçoit le texte, et l'écrire dans la
   pilule elle-même effacerait le pseudo-élément au premier rafraîchissement
   (voir track-panel.js). */
.track-gps-value::before {
  content: none;
}

/* Note "chemins non évalués sur le trajet" dans la modale de confirmation */
.route-note {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 14px 20px 0;
  padding: 12px 14px;
  background: #fdf6e3;
  border-radius: 12px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--color-text-dark);
}

.route-note svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: #c9a227;
  margin-top: 1px;
}

/* Bandeau de navigation en cours */
.nav-banner {
  position: fixed;
  top: 12px;
  left: 12px;
  right: 12px;
  z-index: 930;
  max-width: 560px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  background: var(--color-header-dark);
  color: #fff;
  border-radius: var(--radius-lg);
  padding: 12px 16px;
  box-shadow: var(--shadow-card);

  /* HAUTEUR PLANCHER, et non hauteur fixe. Le bandeau doit tenir 80px pour que
     la fleche de manoeuvre et les deux lignes de texte respirent, mais il doit
     pouvoir grandir : le message de sortie d'itineraire passe sur deux ou trois
     lignes, et une hauteur figee l'aurait tronque.
     Toute modification ici se reporte sur --nav-banner-h. */
  min-height: 84px;
}

.nav-banner-main {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
  /* Dégagement pour la croix, posée en absolu dans le coin. */
  padding-right: 30px;
}

/* RÈGLE SUPPRIMÉE : le dégagement à droite est maintenu hors itinéraire.
   La croix ne redescend plus dans le flux quand « RECALCULER » apparaît — elle
   reste à sa place, dans le coin, exactement où elle est sur fond vert. Elle
   sautait d'un endroit à l'autre selon l'état du bandeau, ce qui obligeait à la
   chercher au moment précis où l'on est occupé à autre chose. */

.nav-maneuver strong {
  font-size: 20px;
  font-weight: 700;
  white-space: nowrap;
}

.nav-maneuver span {
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Croix d'arrêt, calée dans le COIN HAUT DROIT du bandeau. Sortie du flux :
   elle ne dispute plus sa largeur ni à l'instruction ni au bouton de recalcul,
   et le bandeau garde la même mise en page dans les deux états.

   10px et non 6 : à six pixels du coin, la croix touchait presque l'arrondi du
   bandeau, et le doigt qui la visait mordait sur le bord de l'écran.

   ELLE EST REVENUE, POUR L'EXPLORATION SEULE. Elle avait été retirée parce
   qu'elle occupe le coin d'une zone qu'on regarde en roulant — vrai en guidage,
   où le bandeau est le sujet de l'écran des heures durant, et où le carré rouge
   de la colonne de contrôles a pris le relais. En exploration, le bandeau n'est
   qu'un trajet d'approche vers un chemin à décrire : il passe, et la façon de
   l'interrompre doit être sur lui. C'est `.only-exploration` qui fait le
   partage, et `.mode-navigation .nav-stop` plus bas qui le redit. */
.nav-stop {
  position: absolute;
  top: 10px;
  right: 10px;

  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;

  border: none;
  border-radius: 50%;
  /* Un voile clair sur le fond du bandeau, et non une couleur à elle : le
     bandeau change de fond selon l'état — vert, rouge, blanc, lime — et une
     teinte fixe se serait trouvée illisible sur l'un des quatre. Les deux
     états clairs posent leur propre voile sombre plus bas. */
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
  cursor: pointer;
}

/* ARRÊT DE LA NAVIGATION, dans la colonne de contrôles.
   CARRÉ ROUGE PLEIN, et non une croix : la croix est le vocabulaire de la
   fermeture — on la trouve sur chaque feuille et chaque modale, où elle ne
   défait rien. Arrêter un guidage en cours n'est pas fermer un panneau, et le
   carré d'arrêt se lit sans avoir été appris. */
.map-control-stop {
  pointer-events: auto;

  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 50px;
  padding: 0;

  background: #C1121F;
  color: #fff;
  border: none;
  border-radius: 50%;
  box-shadow: var(--shadow-card);
  cursor: pointer;
}

/* LE CARRÉ OCCUPE LA SURFACE QU'ON LUI DONNE. Le dessin ne remplit que dix
   unités sur vingt-quatre de son viewBox : à 24 px de SVG, le carré visible en
   mesurait dix au milieu d'un bouton de cinquante — un cinquième de la surface,
   soit une pastille posée sur un disque rouge plutôt qu'un signe d'arrêt. À 30,
   il en mesure douze et demi, la proportion qu'avait le bouton du bas avant son
   retrait. Le viewBox est intact : c'est le même dessin, vu de plus près. */
.map-control-stop svg {
  width: 30px;
  height: 30px;
}

.map-control-stop:active {
  background: #9E0E19;
}

.nav-stop svg {
  width: 17px;
  height: 17px;
}

/* Recalcul proposé : la croix redescend dans le flux, à côté du bouton, et
   retrouve un alignement vertical au centre. */
/* La croix garde sa géométrie dans tous les états : voir la règle .nav-stop
   plus haut. Ce bloc lui donnait une autre taille et une autre position sur
   fond rouge, ce qui en faisait un second bouton là où il n'y en a qu'un. */

.nav-stop:hover {
  background: rgba(255,255,255,0.25);
}

.poi-label {
  display: block;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--color-text-dark);
  margin: 16px 20px 6px;
}

.poi-label small {
  font-weight: 500;
  color: var(--color-muted);
  text-transform: none;
  letter-spacing: 0;
}

/* ZONE D'AVIS, à la fin d'une sortie (voir #feedback-modal).

   ELLE REPREND LES MARGES DE .poi-label, qu'elle suit, pour que le champ
   s'aligne sur son intitulé. Le reste vient du formulaire de point d'intérêt —
   même gabarit, même rayon, même liseré : ce sont les deux seuls endroits de
   l'application où l'on écrit du texte libre, et deux dessins différents pour
   le même geste n'auraient rien apporté.

   `resize: vertical` ET NON `none` : cinq lignes suffisent le plus souvent, et
   celui qui a beaucoup à dire doit pouvoir voir ce qu'il écrit. L'horizontale,
   elle, est bloquée — elle ferait déborder la modale. */
.feedback-text {
  display: block;
  width: calc(100% - 40px);
  margin: 0 20px;
  padding: 12px 14px;

  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 12px);
  background: #fff;
  color: var(--color-text-dark);

  font-family: inherit;
  font-size: 15px;
  line-height: 1.4;

  resize: vertical;
}

.feedback-text:focus {
  outline: none;
  border-color: var(--color-header-dark);
}

/* Le bouton d'envoi reste en place quand il est éteint : il dit « il manque
   quelque chose », il ne disparaît pas. Même traitement que le bouton d'ajout
   d'un point d'intérêt tant qu'aucune photo n'est jointe. */
.btn.btn-primary:disabled {
  opacity: 0.45;
  cursor: default;
}

/* CE QUI MANQUE À LA FICHE, en tête du formulaire (voir #poi-photo-need).

   Les marges reprennent celles de .poi-label pour que la phrase s'aligne sur
   les libellés qu'elle précède, avec un écart resserré en dessous : elle
   introduit tout le formulaire, elle ne coiffe pas le seul champ suivant. */
.poi-photo-need {
  margin: 16px 20px 2px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--color-danger);
}

.poi-input {
  display: block;
  width: calc(100% - 40px);
  margin: 0 20px;
  padding: 12px 14px;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  font-size: 14px;
  background: #fff;
  color: var(--color-text-dark);
  box-sizing: border-box;
}

.poi-input::placeholder { color: var(--color-muted); }

/* 140px contre 80. La description est devenue obligatoire : on n'y écrit plus
   une précision facultative mais ce qui fait l'intérêt du lieu, et trois lignes
   visibles cachaient la moitié de ce qu'on venait de taper. Reste
   redimensionnable pour les textes longs. */
.poi-textarea {
  min-height: 140px;
  resize: vertical;
  font-family: inherit;
  line-height: 1.45;
}

/* Mention posée dans l'en-tête vert d'une fiche de la base. Discrète : elle
   explique une absence d'action, elle n'annonce pas un problème. */
.poi-readonly-note {
  margin: 6px 0 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-lime);
  letter-spacing: 0.02em;
}

/* Consultation : un seul type reste affiché (voir showOnlySelectedType dans
   poi-form.js). La grille devient une colonne unique, sinon la pastille garde
   le tiers de largeur d'une case de grille et flotte contre le bord gauche. */
.poi-types.single {
  grid-template-columns: minmax(0, 1fr);
}

.poi-types {
  display: grid;

  /* minmax(0, 1fr) et NON 1fr.
     Un `1fr` nu vaut `minmax(auto, 1fr)` : la colonne ne descend jamais sous la
     largeur min-content de son contenu. Le mot « Accidenté », et surtout le
     sous-titre « (point de vue, …) » du bouton Autre, imposaient donc leur
     largeur à leur colonne, qui prenait la place des deux autres — d'où les six
     cases de largeurs toutes différentes sur un écran de téléphone. Le zéro en
     borne basse autorise la colonne à être plus étroite que son contenu, et les
     trois redeviennent égales. */
  /* DEUX COLONNES, ET NON TROIS. Sur trois, chaque case faisait moins d'un
     tiers de l'écran : « Gravier roulant » et « Accidenté » passaient à la
     ligne, et les six cases se lisaient comme un pavé de mots coupés. À deux,
     chaque libellé tient sur une seule ligne sous son pictogramme — six types
     se parcourent aussi bien en trois rangées qu'en deux. */
  grid-template-columns: repeat(2, minmax(0, 1fr));

  /* Chaque rangée à la hauteur de la plus haute de ses cases : « Gravier
     roulant » sur deux lignes ne doit pas laisser ses voisines plus courtes. */
  grid-auto-rows: 1fr;
  align-items: stretch;

  gap: 8px;
  margin: 0 20px;
}

.poi-type-btn {
  /* Memes variables que .choice-option : fallbacks derives de --option-bg,
     ecrases par les valeurs inline posees par le JS si l'option les definit. */
  --option-title: color-mix(in srgb, var(--option-bg) 35%, #000);
  --option-icon-bg: color-mix(in srgb, var(--option-bg) 70%, #fff);

  /* Occupe toute sa case, quoi qu'il arrive. `align-items: stretch` sur la
     grille devrait suffire, mais un bouton reste un élément remplacé de fait
     dans certains moteurs, et se contentait de sa hauteur de contenu. */
  width: 100%;
  height: 100%;

  display: flex;
  flex-direction: column;
  align-items: center;
  /* Contenu centré verticalement : les libellés d'une ou deux lignes occupent
     alors la même case sans la déséquilibrer. */
  justify-content: center;
  min-height: 84px;
  gap: 4px;
  padding: 10px 6px;

  /* NEUTRE TANT QU'ON N'A PAS CHOISI.
     Les six cases arrivaient chacune avec son fond pastel : la grille entière
     ressemblait à une série de boutons déjà cochés, et le seul signe distinguant
     la case retenue était une bordure de 2px. On lisait donc six sélections au
     lieu d'une question.
     La couleur devient la RÉPONSE : elle n'apparaît que sur la case choisie. */
  border: 2px solid var(--color-border);
  border-radius: 12px;
  background: #fff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;

  text-align: center;
}

/* Le texte suit le fond : sombre sur blanc tant que la case est neutre, dans la
   teinte du type une fois qu'elle est retenue (voir .selected plus bas). */
.poi-type-label {
  color: var(--color-text-dark);

  /* UNE SEULE LIGNE. Les colonnes sont assez larges depuis le passage à deux :
     le libellé le plus long, « Gravier roulant », y tient. Le nowrap le garantit
     plutôt que de l'espérer — et si un type plus long apparaissait un jour, il
     se tronquerait proprement au lieu de déséquilibrer la rangée entière, dont
     la hauteur est celle de sa case la plus haute (grid-auto-rows: 1fr). */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.poi-type-btn small {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-muted);
}

/* La pastille du pictogramme garde SA couleur même sur une case neutre : c'est
   elle qui permet de reconnaître un type d'un coup d'œil, et une petite tache
   de couleur ne se lit pas comme une sélection — contrairement à une case
   entièrement remplie. */

/* Types a fond sombre : textes clairs */
.poi-type-btn.text-light {
  --option-title: #fff;
  --option-icon-bg: rgba(255, 255, 255, 0.18);
}

/* SÉLECTION : la case prend enfin sa couleur.
   Fond, bordure et texte basculent ensemble — trois signaux plutôt qu'un, ce
   qui rend le choix visible sans avoir à comparer les cases entre elles. */
.poi-type-btn.selected {
  background: var(--option-bg);
  border-color: var(--option-title);
}

.poi-type-btn.selected .poi-type-label {
  color: var(--option-title);
}

.poi-type-btn.selected small {
  color: var(--option-title);
  opacity: 0.75;
}

/* Les deux entrées côte à côte : « Photographier » et « Ma galerie ».
   La marge latérale est passée du bouton à leur conteneur, sinon chacun aurait
   emporté la sienne et creusé un écart au milieu. */
.poi-photo-actions {
  display: flex;
  gap: 10px;
  margin: 0 20px;
}

.poi-photo-btn {
  display: flex;
  flex: 1;
  min-width: 0;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 10px;
  border: 1.5px dashed var(--color-border);
  border-radius: 12px;
  background: #fff;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text-dark);
  cursor: pointer;
}

/* INVISIBLE, MAIS PAS `display: none`.
   Un <input type="file"> retiré de la mise en page ne s'ouvre pas de façon
   fiable sur certaines versions d'iOS, même déclenché par le <label> qui
   l'entoure — l'appareil photo ne s'ouvrait alors jamais. Il reste donc dans le
   flux, réduit à un pixel transparent. */
.poi-file-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
}

.poi-photo-btn svg { width: 18px; height: 18px; }

.poi-photos-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 10px 20px 0;
}

.poi-photo-thumb {
  position: relative;
  width: 72px;
  height: 72px;
  border-radius: 10px;
  overflow: hidden;
}

/* La vignette s'ouvre en grand : 72px suffisent à compter les photos, pas à
   reconnaître un lieu. */
.poi-photo-thumb img {
  cursor: zoom-in;
}

/* --- Visionneuse plein écran --- */

/* Fond très sombre et non le voile habituel des modales : une photo se regarde
   d'autant mieux que rien ne l'entoure. */
.poi-photo-viewer {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(10, 14, 12, 0.94);
}

.poi-photo-viewer img {
  max-width: 100%;
  /* Bornée en hauteur ET en largeur : une photo en portrait déborderait sinon
     sous la barre applicative. `dvh` suit la barre d'adresse mobile, qui se
     rétracte au défilement. */
  max-height: calc(100dvh - 32px);
  object-fit: contain;
  border-radius: 8px;
}

.poi-photo-viewer-close {
  position: absolute;
  top: max(16px, env(safe-area-inset-top));
  right: 16px;

  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;

  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  cursor: pointer;
}

.poi-photo-viewer-close svg {
  width: 22px;
  height: 22px;
}

.poi-photo-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Croix de suppression d'une photo.

   DEUX CAUSES DE DÉCENTRAGE, DEUX CORRECTIFS.

   1. `padding: 0`. Les navigateurs posent d'eux-mêmes un rembourrage sur les
      boutons, asymétrique selon les moteurs (1px 6px chez Chrome) : dans une
      pastille de 20px, il poussait tout le contenu vers la droite.

   2. La croix est un <span> (voir addPhotoThumb, poi-form.js) et non un nœud de
      texte. Un texte nu se cale sur la ligne de base de la police, que
      `align-items: center` ne déplace pas ; un élément est une boîte, que le
      centrage flex traite pour ce qu'elle est. `line-height: 1` réduit cette
      boîte au strict nécessaire, sinon elle emporterait l'interligne par
      défaut et redescendrait le glyphe. */
.poi-photo-thumb button {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.poi-photo-thumb button span {
  display: block;
  font-size: 16px;
  line-height: 1;
}

/* =========================================================
   Contrôles de carte custom
   ========================================================= */
.map-controls {
  position: absolute;
  right: 16px;
  /* Position de repos, feuille de tracé fermée.
     Remontée de 70px, puis de 106 : au ras de la barre applicative, la colonne
     se retrouvait dans la zone que la paume couvre en tenant le téléphone, et
     le bouton du bas était le plus difficile à viser alors que c'est le
     principal. Les deux règles de décalage plus bas (feuille de tracé, panneau
     de destination) partent de valeurs absolues et doivent RESTER AU-DESSUS de
     celle-ci : une feuille qui s'ouvre ne doit jamais faire redescendre les
     contrôles. */
  bottom: calc(var(--bottom-bar-h) + 106px);
  transition: bottom 0.25s ease;
  z-index: 800;  /* au-dessus des tuiles, sous les panneaux/modales */
  display: flex;
  flex-direction: column;
  /* CENTRÉS, et non alignés à droite : le bouton principal (58px) est plus
     large que les capsules (50px), et caler leurs bords droits décalait leurs
     axes de 4px — assez pour que la colonne paraisse de travers. */
  align-items: center;
  /* Écart ENTRE GROUPES. À l'intérieur d'un groupe, les boutons sont serrés
     dans leur capsule — c'est ce contraste d'espacement qui fait la lecture en
     trois familles, sans qu'aucun trait ne les sépare. */
  gap: 14px;

  /* Le conteneur est devenu large : il englobe le libellé du bouton principal,
     et ses gouttières entre groupes. Sans ça, il intercepterait les gestes
     destinés à la carte dans toutes ces zones vides. Même précaution que pour
     .bottom-stack. */
  pointer-events: none;
}

.map-control-group,
.map-control-btn {
  pointer-events: auto;
}

/* --- Capsule d'un groupe ---
   Une seule surface blanche pour plusieurs boutons, au lieu d'un contour par
   bouton. Six cercles cerclés alignés le long du bord se lisaient comme six
   cibles équivalentes ; deux capsules et un bouton isolé se lisent comme trois
   familles, hiérarchisées par la taille. */
.map-control-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
  border-radius: var(--radius-pill);
  background: #fff;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
  /* La pastille de comptage du bilan déborde volontairement du bouton. */
  overflow: visible;
}

/* 38px de cible dans une capsule de 4px de rembourrage : la capsule mesure donc
   46px de large, contre 50 auparavant. C'est le rembourrage qui fixe l'écart
   entre les deux — changer l'un sans l'autre décale la colonne entière, les
   groupes étant centrés sur un axe commun.

   ÉLARGIS DE DEUX PIXELS, LA HAUTEUR INCHANGÉE. Ces boutons n'ont ni fond ni
   contour — c'est la capsule qui porte la surface — et leur forme ne se voit
   donc pas : les deux pixels vont à la CIBLE et à la largeur de la capsule, pas
   à un cercle qui deviendrait ovale.

   #btnGeolocation ET #btn-map-orientation NE SUIVENT PAS : ils portent
   .map-control-btn-primary, qui les fixe à 50px plus bas dans cette feuille. Ils
   étaient déjà les plus larges, et c'est précisément ce qui les désigne comme
   le bouton qu'on cherche en roulant — les rapprocher des autres effacerait
   cette hiérarchie. */
.map-control-btn {
  width: 38px;
  height: 36px;
  border-radius: 50%;
  /* Ni fond ni contour : c'est la capsule du groupe qui porte la surface.
     Les états qui doivent se signaler (suivi actif, chemins oubliés, nord
     verrouillé) reposent un fond plus bas, et ressortent d'autant mieux qu'ils
     sont alors les seuls à en avoir un. */
  background: transparent;
  border: none;
  color: var(--color-header-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
}

.map-control-btn svg {
  width: 20px;
  height: 20px;
}

/* --- AJOUT D'UN POINT D'INTÉRÊT, SEUL ET PLEIN ---

   IL ÉTAIT DANS LA CAPSULE « exploration », avec la recherche de lieu et le
   menu de session. Ces trois-là se consultent à l'arrêt ; lui ne se consulte
   pas, il PRODUIT quelque chose, et c'est le seul geste de contribution qu'on
   fasse depuis la carte. Rangé parmi des commandes de consultation, il avait le
   même poids qu'un bouton de zoom.

   SOUS LE BOUTON DE POSITION, et non plus en bas de colonne : c'est le voisin
   qu'on vient de toucher pour savoir où l'on est, et poser un point est ce
   qu'on fait juste après.

   PLEIN LÀ OÙ LES AUTRES SONT CREUX. Toute la colonne est faite de
   pictogrammes sombres sur capsule blanche ; l'aplat de la charte ne se lit
   donc pas comme une couleur de plus mais comme un changement de nature — la
   même hiérarchie que dans le panneau de tracé, où le remplissage distingue
   l'action de la lecture. */
.map-control-poi {
  pointer-events: auto;

  display: flex;
  align-items: center;
  justify-content: center;
  /* 46px : la largeur exacte d'une capsule de groupe (38 de cible + 4 de
     rembourrage de chaque côté). Le bouton est seul, mais il reste dans la
     colonne — un diamètre à lui l'aurait fait dépasser d'un côté. */
  width: 46px;
  height: 46px;
  padding: 0;

  background: var(--color-header-dark);
  color: #fff;
  border: none;
  border-radius: 50%;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
  cursor: pointer;
}

/* 26 et non 20 : sur un aplat, un pictogramme de la taille des autres paraît
   plus petit qu'eux — le fond mange ses contours au lieu de les détacher. */
.map-control-poi svg {
  width: 26px;
  height: 26px;
}

.map-control-poi:active {
  transform: scale(0.94);
}

.map-control-btn:active {
  transform: scale(0.94);
}

/* --- Attente d'un premier point GPS ---

   Posée par setLocating (map-controls.js) entre l'appui et la première
   position, qui peut se faire attendre une quinzaine de secondes. Sans elle, le
   bouton avait basculé sur la boussole et plus rien ne disait qu'on attendait.

   L'ANNEAU REMPLACE LE PICTOGRAMME plutôt que de s'ajouter à lui : sur 50px, un
   témoin posé à côté de la flèche donnerait deux petites formes au lieu d'une
   lisible. `visibility` et non `display` — le SVG garde sa place, et le bouton
   sa taille.

   `position: relative` est posé ici et non sur .map-control-btn : seul cet état
   a besoin d'un repère, et le déclarer pour tous changerait l'empilement de
   boutons qui n'en demandent pas. */
.map-control-btn.is-locating {
  position: relative;
  cursor: progress;
}

.map-control-btn.is-locating svg {
  visibility: hidden;
}

/* DEUX CALQUES, ET DANS CET ORDRE-LÀ. L'anneau pâle complet, puis le quart
   opaque par-dessus : c'est le contraste entre les deux qui rend la rotation
   lisible. Un seul élément ne peut pas porter deux opacités, et ::before est
   peint AVANT ::after — les intervertir mettrait le pâle sur le plein et
   l'éteindrait.

   currentColor pour les deux : le témoin suit la couleur du bouton, blanche sur
   le bouton principal, verte sur les capsules. */
.map-control-btn.is-locating::before {
  content: '';
  position: absolute;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2.5px solid currentColor;
  opacity: 0.3;
}

.map-control-btn.is-locating::after {
  content: '';
  position: absolute;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2.5px solid transparent;
  border-top-color: currentColor;
  animation: map-control-spin 0.85s linear infinite;
}

@keyframes map-control-spin {
  to { transform: rotate(360deg); }
}

/* Mouvement réduit demandé : l'anneau cesse de tourner et se contente de
   respirer. Un témoin d'attente doit rester perceptible — le supprimer
   laisserait un bouton muet pendant quinze secondes. */
@media (prefers-reduced-motion: reduce) {
  .map-control-btn.is-locating::after {
    animation: map-control-pulse 1.6s ease-in-out infinite;
  }

  @keyframes map-control-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.2; }
  }
}

/* --- Bouton principal : position, puis boussole ---
   Plus grand, plein, et seul de son groupe. C'est celui qu'on cherche en
   roulant, parfois sans regarder : la taille et l'isolement font plus pour
   l'atteindre que n'importe quel pictogramme. Le liseré blanc le détache du
   fond de carte, qui peut être de n'importe quelle teinte sous lui.

   Le conteneur ne sert plus qu'à réunir les deux boutons qui se relaient dans
   cette case (position et boussole) : il n'en montre jamais qu'un à la fois. */
.map-control-primary {
  display: flex;
  align-items: center;
}

/* Toujours plus grand que les autres — c'est ce qui le désigne comme le bouton
   qu'on cherche en roulant — mais l'écart n'a plus besoin d'être aussi large
   depuis que les capsules ont maigri : 50 contre 46. */
.map-control-btn-primary {
  width: 50px;
  height: 50px;
  background: var(--color-header-dark);
  color: #fff;
  border: 3px solid #fff;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.28);
}

.map-control-btn-primary svg {
  width: 23px;
  height: 23px;
}

/* SUIVI À L'ARRÊT. Le vert foncé des autres commandes se lisait comme un état
   « allumé » sur ce bouton-ci, alors qu'il désigne justement l'inverse : le GPS
   n'est pas lancé. Gris clair au repos, coloré une fois actif — la couleur
   devient un indicateur d'état, et non plus une simple identité visuelle.
   Seul #btnGeolocation est concerné : le bouton de boussole, qui occupe la même
   case, ne s'affiche QUE pendant le suivi, donc toujours en état actif. */
#btnGeolocation:not(.active) {
  background: #D6DAD8;
  color: var(--color-header-dark);
}

/* Suivi GPS actif : le bouton passe dans le vert du marqueur de position —
   c'est le meme reperage, il doit porter la meme couleur. */
#btnGeolocation.active {
  background: var(--color-position);
  color: #fff;
}

/* Le halo blanc detache la fleche du fond de carte, quelle que soit la couleur
   sous elle. Le disque tourne avec le marqueur (MapLibre applique la rotation
   sur cet element), ce qui ne se voit pas puisqu'il est circulaire. */
.position-marker {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-position);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.95);
  box-shadow:
    0 0 0 3px rgba(255, 255, 255, 0.5),
    0 2px 5px rgba(0, 0, 0, 0.28);
}

/* Le remplissage et l'arrondi sont portés par le SVG lui-même (fill et stroke
   en currentColor, stroke-linejoin rond) — voir position-arrow.js. */
.position-arrow {
  width: 26px;
  height: 26px;
  transition: transform 0.2s ease-out;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.4));
}

/* =========================================================
   Panneau de tracé : approche de la fin du chemin
   ========================================================= */

/* LE PANNEAU ENTIER VIRE À L'AMBRE quand l'utilisateur approche de l'extrémité
   vers laquelle il progresse (voir END_OF_TRACK_FRACTION dans track-panel.js).
   Il était rosé, et seul le badge était coloré : le message se lisait alors comme
   une étiquette de plus dans un panneau inchangé, et il fallait le chercher. Un
   panneau qui change entièrement de couleur se remarque sans être lu — c'est
   ce qu'on demande à un signal qu'on reçoit en roulant.

   AMBRE ET NON ROUGE : arriver au bout d'un chemin n'est pas un incident, c'est
   une échéance. Le rouge est réservé à ce qui a échoué — et il servait déjà à
   cela ailleurs dans cette feuille, ce qui rendait les deux indiscernables.
   L'ambre a de surcroît un avantage décisif ici : le noir s'y lit à 7:1, ce qui
   permet de poser de l'encre sombre sur le panneau — impossible sur le rouge,
   où tout devait rester blanc. */
.track-panel.near-end {
  background: #C98A00;
  color: #fff;
  box-shadow: var(--shadow-card);
  transition: background 0.4s ease;
}

/* PLAQUE BLANCHE PLEINE, ENCRE NOIRE, PAS DE BORDURE.
   Le bouton repasse en blanc quel que soit son état — le vert plein sur de
   l'ambre serait illisible, et la nuance d'état n'a plus d'importance à cet
   instant : ce qui compte est qu'on arrive au bout.

   LA BORDURE DISPARAÎT parce que le blanc suffit à découper la forme sur
   l'ambre. Un liseré en plus ne ferait que la refermer deux fois. Elle reste
   déclarée transparente et non retirée : la largeur de 2px doit être conservée,
   sans quoi la hauteur du bouton changerait au passage en fin de chemin — et la
   ligne entière sauterait. */
.track-panel.near-end .track-eval,
.track-panel.near-end .track-eval[data-state="none"] {
  background: #fff;
  border-color: transparent;
  color: var(--color-text-dark);
  /* Le disque du pictogramme prend currentColor et son glyphe cette variable
     (voir badgeIcon, track-panel.js) : sur un disque redevenu sombre, le dessin
     doit repasser en blanc. */
  --badge-glyph: #fff;
}

/* Le chevron suit l'encre du libellé, qui vient de passer au noir. */
.track-panel.near-end .track-eval[data-state="none"] svg {
  color: inherit;
}

/* LE BOUTON DE DÉTAIL RESTE POSÉ À MÊME L'AMBRE, avec une bordure.
   C'est ce qui le distingue de son voisin sans introduire une couleur de plus :
   même encre noire, même absence de vert, mais l'un est une PLAQUE et l'autre un
   CONTOUR. La hiérarchie tient au relief, pas à la teinte — et elle se lit
   d'aussi loin.

   Le blanc ne pouvait de toute façon pas servir d'encre ici : sur #C98A00 il
   plafonne à 2,9:1, sous le seuil lisible. Il ne vaut que comme surface, et
   cette surface est déjà prise par l'action principale.

   LISERÉ PLUS FIN QUE LES 2px HABITUELS. Le noir tranche beaucoup plus sur
   l'ambre que le vert sur le blanc ; à épaisseur égale, ce bouton pèserait plus
   que la plaque voisine, alors qu'il est le geste secondaire. `box-sizing:
   border-box` (voir #eval-ui *) garde les 56px de large inchangés. */
.track-panel.near-end .track-details {
  background: transparent;
  border-width: 1px;
  border-color: var(--color-text-dark);
  color: var(--color-text-dark);
  /* Le disque du « i » prend currentColor, donc le noir : son glyphe doit
     laisser voir l'ambre du panneau au travers. */
  --badge-glyph: #C98A00;
}

.track-panel.near-end .track-close {
  border-color: rgba(255, 255, 255, 0.55);
  color: #fff;
}

/* Le schéma du tracé passe en blanc : le vert foncé de la charte n'a presque
   aucun contraste sur ce rouge. */
.track-panel.near-end .track-line-path,
.track-panel.near-end .track-line-cap {
  stroke: #fff;
}

.track-panel.near-end .track-line-cap.start { fill: #C98A00; }
.track-panel.near-end .track-line-cap.end   { fill: #fff; }

/* La longueur du chemin se masque (voir plus haut) et le badge sort du flux :
   il ne reste qu'un seul élément dans une barre en space-between, qui filerait
   à gauche. */
.track-panel.near-end .track-panel-actions {
  margin-left: auto;
}

/* Mention « Fin du segment ».
   
   PLUS D'ENCADRÉ, ET CENTRÉE. La pastille rouge à coins arrondis, calée à
   gauche à hauteur de la croix, avait toutes les apparences d'un bouton : on
   cherchait à appuyer dessus. Elle n'en est pas un — c'est un constat, et le
   panneau tout entier porte désormais la couleur qu'elle portait seule.
   
   Ne restent donc que le pictogramme et le mot, centrés dans la barre.

   POSITIONNÉE EN ABSOLU, et non en flex : centrer un élément entre un frère
   masqué et un autre de 36px l'aurait laissé décalé de la moitié de ce dernier.
   .track-panel-top est déjà en position: relative. */
.track-end-badge {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);

  display: flex;
  align-items: center;
  gap: 8px;

  background: none;
  border-radius: 0;
  padding: 0;
  height: auto;
  color: #fff;

  font-size: 17px;
  font-weight: 700;
  /* Incompressible : « Fin du segment » doit tenir sur une ligne. */
  white-space: nowrap;
  flex-shrink: 0;
}

.track-end-badge svg {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}

/* =========================================================
   Bouton bilan d'exploration + bottom sheet
   ========================================================= */

/* Gabarit, fond, forme et ombre viennent de .map-control-btn, que ce bouton
   porte désormais aussi : ne restent ici que ses particularités. */
.btn-session-summary {
  /* Repère de #session-summary-badge, posé en absolu. Le contour vient de
     .map-control-btn, qui en porte déjà un : le surcharger ici ne ferait que
     désaligner ce bouton du reste de la colonne. */
  position: relative;
}

.btn-session-summary svg {
  width: 22px;
  height: 22px;
}

/* --- Bouton d'orientation de la carte ---
   Occupe la même case que #btnGeolocation dans .map-controls : l'un remplace
   l'autre selon que le suivi de position est lancé ou non (syncPrimaryButton,
   map-controls.js). Gabarit hérité de .map-control-btn. */
.btn-map-orientation {
  /* Le repère « N » est en absolu : le bouton établit son contexte.
     overflow reste visible, la pastille débordant volontairement. */
  position: relative;
  overflow: visible;
}

.btn-map-orientation:active {
  transform: scale(0.94);
}

/* --- Aiguille ---
   Meme trace que le marqueur de position (voir position-arrow.js) : fill et
   stroke de la meme couleur, jointures rondes, ce qui adoucit les angles.
   La rotation de -45deg compense l'orientation naturelle du SVG et fait
   pointer l'aiguille vers le haut, donc vers le nord. */
/* Réduite avec le bouton, et un peu au-delà : le repère « N » a besoin de
   place autour de l'aiguille pour rester lisible. */
.compass-needle {
  width: 22px;
  height: 22px;
  color: #fff;
  transform: rotate(-45deg);
  transition: color 0.2s ease;
}

/* --- Reperes cardinaux, a CHEVAL sur la bordure ---
   Poses en absolu autour du bouton plutot qu'a l'interieur : le bouton fait
   46px, y loger quatre pastilles ET une aiguille lisible n'etait pas tenable.
   Le bouton ne doit donc pas masquer ce qui deborde. */
.compass-mark {
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  color: var(--color-muted);
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: background 0.2s ease, color 0.2s ease;
}

/* Remonté hors du bouton : le repère chevauche son bord plutôt que de flotter
   dedans, et les trois bordures blanches referment le disque sur la moitié qui
   dépasse. Pas de bordure BASSE : c'est le côté qui rentre dans le bouton, et un
   trait l'y détacherait au lieu de l'y fondre. */
/* LA BORDURE BASSE EST TRANSPARENTE, ET NON ABSENTE.
   Sans elle, la pastille portait un trait en haut et rien en bas. Tout étant en
   `box-sizing: border-box` (voir #eval-ui *), sa boîte de contenu perdait ce
   pixel en HAUT seulement : le « N », centré dans cette boîte et non dans le
   disque, se posait un demi-pixel trop bas.

   Transparente plutôt que blanche : le fond se peint sous les bordures, donc
   rien ne change à l'œil — c'est bien le côté qui rentre dans le bouton, et un
   trait blanc l'y détacherait au lieu de l'y fondre. Seule la géométrie de la
   boîte redevient symétrique. */
.compass-mark-n {
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  border: 1px solid white;
  border-bottom-color: transparent;
}

/* Verrouille vers le nord : le bouton passe en bleu plein, l'aiguille en blanc.
   Les reperes E/S/O restent neutres, seul le N s'allume. */
.btn-map-orientation.north-up {
  background: var(--color-active-gps);
}

.btn-map-orientation.north-up .compass-needle {
  color: #fff;
}

.btn-map-orientation.north-up .compass-mark-n {
  background: var(--color-active-gps);
  color: #fff;
}

/* Icône pointillée quand des chemins ont été oubliés */
.btn-session-summary .icon-forgotten { display: none; }
.btn-session-summary.has-forgotten .icon-done { display: none; }
.btn-session-summary.has-forgotten .icon-forgotten { display: block; }

.btn-session-summary.has-forgotten {
  background: #e8e8e8;
  color: #7d8a80;
}

/* Pastille de comptage des chemins oubliés */
/* PASTILLE MASQUÉE, CALCUL CONSERVÉ.
   `refresh()` (session-summary.js) continue de compter les chemins oubliés et
   d'écrire le nombre ici : c'est la même valeur qui nourrit le bilan, et la
   couper en JS aurait demandé de démêler deux usages qui n'en font qu'un. Seul
   l'affichage est retiré — une ligne à supprimer pour le rétablir. */
.session-summary-badge {
  display: none !important;

  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  border-radius: 12px;
  background: #8fada4;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* --- Bottom sheet du bilan --- */

.session-sheet {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(10, 20, 15, 0.35);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  /* La feuille se cale au-dessus de la barre applicative, pas sous elle. */
  padding-bottom: var(--bottom-bar-h);
}

.session-sheet-content {
  /* La feuille se referme par sa croix ou par le fond : le geste de défilement
     ne doit pas se propager au document derrière elle. */
  overscroll-behavior: contain;
  width: 100%;
  max-width: 560px;
  background: #fff;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  overflow: hidden;
  box-shadow: var(--shadow-card);
}

.session-sheet-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 20px 20px 16px;
}

.session-sheet-header h2 {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--color-text-dark);
}

.session-sheet-header p {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--color-muted);
}

/* Même cible que la croix de la feuille de tracé (.track-close), et pour la
   même raison : c'est le seul moyen de refermer cette feuille, et une croix
   de 20px sans contour se cherchait du regard sur un fond blanc chargé. Le
   cercle lui donne un bord, et 40px la ramènent au-dessus du minimum tactile
   confortable. */
.session-sheet-close {
  width: 40px;
  height: 40px;
  border: 1.5px solid #D4D9D6;
  border-radius: 50%;
  background: transparent;
  color: var(--color-text-dark);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.session-sheet-close svg { width: 26px; height: 26px; }

/* =========================================================
   Feuille d'une zone à évaluer
   ========================================================= */

.zone-sheet-content {
  padding-bottom: 16px;
}

/* --- Pictogramme du secteur, à côté du titre --- */

/* Le titre et sa ligne de contexte forment un bloc, le pictogramme se pose
   devant : `align-items: flex-start` pour que le dessin s'aligne sur la
   PREMIÈRE ligne du titre et non sur le milieu du bloc, qui descendrait quand
   le titre passe sur deux lignes. */
.zone-sheet-heading {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-width: 0;
}

/* MÊME SILHOUETTE QUE L'ÉPINGLE DE LA CARTE : un disque clair cerclé d'encre.
   C'est ce cercle qui fait reconnaître le repère, la couleur ne disant que
   « fait » ou « à faire » — voir buildPinUrl, zones-layer.js. */
.zone-sheet-icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 2px solid var(--color-text-dark);
  border-radius: 50%;
  background: #fff;
  color: var(--color-text-dark);
}

.zone-sheet-icon svg {
  width: 22px;
  height: 22px;
}

/* --- Les deux actions --- */

.zone-action {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  width: calc(100% - 40px);
  margin: 0 20px 12px;
  padding: 16px;

  border-radius: 16px;
  text-align: left;
  font-family: inherit;
  cursor: pointer;

  transition: transform 0.12s ease;
}

.zone-action:active {
  transform: scale(0.98);
  transition-duration: 0s;
}

/* « Explorer » est l'action pour laquelle cette feuille existe : c'est elle qui
   fait quelque chose que rien d'autre dans l'application ne sait faire. Elle est
   pleine, comme toutes les actions principales de la charte. */
.zone-action.is-primary {
  border: 2px solid var(--color-header-dark);
  background: var(--color-header-dark);
  color: #fff;
}

/* « Aller » mène au même endroit par un chemin ordinaire : la modale
   d'itinéraire existe déjà ailleurs. Fond blanc cerclé de vert — présente sans
   se disputer l'attention avec l'action du dessus. */
.zone-action.is-secondary {
  border: 2px solid var(--color-header-dark);
  background: #fff;
  color: var(--color-text-dark);
}

.zone-action-icon {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.zone-action-icon svg {
  width: 30px;
  height: 30px;
}

.zone-action.is-primary .zone-action-icon { color: var(--color-lime); }

/* VERT PLEIN, ET NON PLUS UNE TEINTE DOUCE.
   L'écart de saturation servait à distinguer deux boussoles identiques ; les
   deux dessins diffèrent maintenant — boussole pour « Explorer », flèche de
   navigation pour « Aller » — et la teinte pâle ne faisait plus que ranger
   l'action au second plan. Or c'est la même que celle du bouton « Aller » de la
   feuille de tracé, qui est en vert plein. */
.zone-action.is-secondary .zone-action-icon { color: var(--color-header-dark); }

.zone-action-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
  min-width: 0;
}

.zone-action-text strong {
  font-size: 17px;
  font-weight: 700;
}

.zone-action-text em {
  font-style: normal;
  font-size: 14px;
  line-height: 1.35;
  color: var(--color-muted);
}

.zone-action.is-primary .zone-action-text em {
  color: rgba(255, 255, 255, 0.82);
}

/* --- Demande de distance --- */

.zone-distance-question {
  margin: 4px 20px 4px;
  font-size: 17px;
  font-weight: 700;
  color: var(--color-text-dark);
}

.zone-distance-hint {
  margin: 0 20px 14px;
  font-size: 14px;
  line-height: 1.4;
  color: var(--color-muted);
}

/* Quatre choix plutôt qu'un champ de saisie : on répond d'un pouce, sans
   clavier, ce qui est la seule façon utilisable au moment où l'on part rouler.
   Une valeur au kilomètre près n'aurait de toute façon aucun sens — le parcours
   réel sera plus long que le total demandé. */
/* ⚠ SÉLECTEUR PAR IDENTIFIANT, ET LARGEUR IMPOSÉE AU BOUTON LUI-MÊME.
   La règle par classe ci-dessous avait corrigé le défaut, puis il est revenu :
   une feuille du thème WordPress chargée APRÈS celle-ci, ou portant un
   sélecteur plus spécifique, reprend la main sur `display` ou sur
   `grid-template-columns`, et les quatre boutons retrouvent quatre largeurs.
   Un identifiant (0-1-0-0) passe devant tout sélecteur de classes, et le
   `width: 100%` posé sur les boutons les égalise même si le conteneur se
   retrouve en flex. Deux verrous plutôt qu'un, parce que le premier a déjà
   lâché une fois. */
#zone-distance-choices {
  display: grid;
  /* Quatre colonnes pour quatre paliers — 5, 10, 15 et 20. Le nombre de
     colonnes suit celui des boutons : à trois boutons dans quatre colonnes, ils
     paraissaient collés à gauche, une colonne vide restant à droite. */
  /* `minmax(0, 1fr)` ET NON `1fr`. Une piste `1fr` vaut en réalité
     `minmax(auto, 1fr)` : elle refuse de descendre sous la largeur de son
     contenu, et « 20 km » est plus large que « 5 km ». Les quatre boutons
     avaient donc quatre largeurs. Le zéro autorise la compression, et les
     colonnes redeviennent égales. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  margin: 0 20px 16px;
}

#zone-distance-choices > .zone-distance-choice {
  /* Voir le bloc ci-dessus : ces trois lignes sont ce qui tient si le
     conteneur cesse d'être une grille. `min-width: 0` autorise « 20 km » à se
     comprimer comme « 5 km ». */
  width: 100%;
  min-width: 0;
  flex: 1 1 0;
}

.zone-distance-choice {
  padding: 16px 4px;
  border: 2px solid var(--color-border);
  border-radius: 14px;
  background: #fff;
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  color: var(--color-text-dark);
  cursor: pointer;
  transition: transform 0.12s ease;
}

.zone-distance-choice:active {
  transform: scale(0.96);
  transition-duration: 0s;
}

/* =========================================================
   Détail de l'évaluation d'un segment
   ========================================================= */

/* Reprend .session-sheet / .session-sheet-content, comme le menu et le bilan.
   Le corps défile : deux évaluations complètes dépassent la hauteur d'un
   téléphone, et c'est le seul contenu de cette feuille. */
/* RESSERRÉE PAR RAPPORT AUX AUTRES FEUILLES, et seulement elle.
   Le bilan et le menu alignent quelques lignes qu'on parcourt ; celle-ci est une
   FICHE, densément remplie, qu'on ouvre pour comparer deux évaluations. Les
   espaces hérités y séparaient des choses qui se lisent ensemble — le titre
   flottait à seize pixels de la première section, et rien ne se passait entre
   les deux.

   Toutes les valeurs ci-dessous descendent d'environ un tiers. Les baisser à
   des endroits isolés aurait déséquilibré la fiche : ce sont des rapports, pas
   des mesures indépendantes. */
.eval-details-content {
  max-height: calc(100dvh - var(--bottom-bar-h) - 40px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: 12px;
}

/* L'en-tête commun réserve la place d'un sous-titre que cette feuille n'a pas :
   son titre est seul, et les seize pixels du bas s'ajoutaient à un vide déjà
   ouvert. Portée limitée à cette feuille — le bilan, lui, a bien son
   sous-titre. */
.eval-details-content .session-sheet-header {
  padding: 18px 20px 4px;
}

.eval-details-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 20px;
}

/* ⚠ AU SINGULIER — `eval-detail-`, ET NON `eval-details-`.
   C'est ce que pose eval-details-sheet.js, et le pluriel écrit ici ne
   correspondait à rien : ni ce filet, ni la mise en ligne de l'en-tête plus
   bas. D'où deux défauts qui n'avaient l'air d'en faire qu'un — un vide non
   maîtrisé entre les sections, et l'intitulé collé à sa date, les deux <span>
   se suivant en flux normal faute du flex qui devait les écarter. */
/* REMBOURRAGE POSÉ EXPLICITEMENT, ET C'EST UNE REPRISE EN MAIN.

   Ces blocs sont des <section>, et le thème WordPress qui héberge la page en
   habille le TYPE D'ÉLÉMENT — trente-huit pixels en haut comme en bas, pour des
   sections d'article. Rien ici ne les demandait, et rien ici ne les annulait :
   deux évaluations affichées l'une sous l'autre s'écartaient de soixante-seize
   pixels, dans une feuille qu'on consulte en roulant.

   UNE CLASSE L'EMPORTE SUR UN SÉLECTEUR D'ÉLÉMENT, quel que soit l'ordre des
   feuilles : c'est la spécificité qui tranche, et il n'y a donc rien à faire de
   plus que de l'écrire. La même précaution vaudrait pour tout autre élément
   sémantique posé par ces écrans. */
.eval-detail-section {
  padding: 10px 0;
}

.eval-detail-section + .eval-detail-section {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--color-border);
}

/* Intitulé de section et sa précision de droite, sur une même ligne : le
   « quoi » et le « quand » se lisent ensemble. */
.eval-detail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  /* Assez d'air pour que l'intitulé et la date ne se touchent jamais, même
     quand le premier occupe presque toute la largeur. */
  gap: 16px;
  /* Le titre de section appartient aux lignes qu'il annonce : il doit en être
     plus proche que de la section précédente, dont il est séparé par un filet
     et quinze pixels. */
  margin-bottom: 8px;
}

.eval-detail-title {
  /* Sans lui, un intitulé long refuse de se comprimer et pousse la date hors
     de la feuille. */
  min-width: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
}

.eval-detail-meta {
  flex-shrink: 0;
  font-size: 12px;
  color: var(--color-muted);
}

.eval-detail-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.eval-detail-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  /* La pastille fait 44px : c'est elle qui donne sa hauteur à la ligne, et dix
     pixels au-dessus et en dessous suffisent à l'aérer. */
  padding: 10px 0;
}

.eval-detail-row + .eval-detail-row {
  border-top: 1px solid var(--color-border);
}

/* Pastille à la couleur de l'option, pictogramme blanc. Même code couleur que
   les écrans de choix et que le tracé sur la carte : il est déjà appris au
   moment où l'on arrive ici. */
.eval-detail-icon {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}

.eval-detail-icon svg {
  width: 24px;
  height: 24px;
}

.eval-detail-text {
  min-width: 0;
}

/* Le CRITÈRE au-dessus de la RÉPONSE, et en plus petit : on cherche « la
   pente », on lit « Portage ponctuel ». L'ordre inverse obligerait à retrouver
   de quoi parle chaque bloc de couleur. */
.eval-detail-kind {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--color-text-dark);
}

.eval-detail-label {
  margin: 1px 0 0;
  font-size: 17px;
  font-weight: 700;
  line-height: 1.2;
}

.eval-detail-desc {
  margin: 5px 0 0;
  font-size: 14px;
  line-height: 1.35;
  color: var(--color-muted);
}

.eval-details-empty {
  margin: 4px 20px 0;
  font-size: 14px;
  line-height: 1.4;
  color: var(--color-muted);
}

/* =========================================================
   Feuille d'aide à l'utilisation
   ========================================================= */

/* PLEINE PAGE, ET NON UNE FEUILLE DU BAS.

   C'est la seule surface de ce projet qu'on vienne LIRE, et la seule à porter
   trois sections. En feuille du bas, elle prenait déjà presque tout l'écran
   sans en avoir les moyens : l'en-tête défilait avec le contenu, et la croix —
   sa seule sortie — s'en allait par le haut dès la première section.

   Même construction que .choice-panel, pour la même raison : un en-tête qui ne
   bouge pas, un corps qui défile seul.

   LA POIGNÉE A ÉTÉ RETIRÉE. Elle promettait un geste que cet écran n'a plus à
   rendre — on ne tire pas une page pleine — et son `touch-action: none` prenait
   le doigt sur une bande de 26px en haut du contenu. */
.help-panel {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #fff;

  display: flex;
  flex-direction: column;

  /* Au-dessus de la barre applicative, comme toutes les surfaces plein écran. */
  padding-bottom: var(--bottom-bar-h);
}

/* EN-TÊTE FIXE. `flex-shrink: 0` et non `position: sticky` : le corps est déjà
   la boîte défilante, l'en-tête n'a donc qu'à refuser d'être comprimé. Un
   sticky aurait le même rendu et une règle de plus à comprendre. */
.help-panel-header {
  flex-shrink: 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: max(20px, env(safe-area-inset-top)) 16px 16px 20px;

  /* Le filet dit où s'arrête ce qui reste et où commence ce qui défile. Sans
     lui, l'en-tête et la première section se lisent comme un seul bloc, et le
     contenu semble glisser sous rien. */
  border-bottom: 1px solid var(--color-border);
}

.help-panel-header h2 {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--color-text-dark);
}

.help-panel-header p {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--color-muted);
}

/* SEULE BOÎTE DÉFILANTE DE L'ÉCRAN. `min-height: 0` est indispensable : la
   hauteur minimale par défaut d'un enfant flex est celle de son contenu, et
   sans ce zéro le corps refuse de rétrécir — il déborde alors sous la barre
   applicative au lieu de défiler. Même piège que .steps-list dans la modale
   de démarrage. */
.help-panel-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;

  /* Le geste s'arrête ici plutôt que de repartir en pull-to-refresh une fois le
     bas atteint. */
  overscroll-behavior: contain;

  /* Le bas ne porte plus le retrait de sécurité : le pied fixe est en dessous,
     et c'est lui qui touche le bord de l'écran désormais. Il reste ce qu'il
     faut pour que la dernière ligne de la légende ne colle pas au filet. */
  padding: 20px 0 16px;
}

/* PIED FIXE, symétrique de l'en-tête et pour la même raison : la sortie doit
   rester à l'écran quelle que soit la position dans la page.
   `flex-shrink: 0` et non `position: sticky` — le corps est déjà la boîte
   défilante, le pied n'a qu'à refuser d'être comprimé.
   Le retrait de sécurité est ici, et pas dans .btn : le bouton porte une marge
   de 8px qui vaut pour toutes ses apparitions du projet, elle n'a pas à
   dépendre de l'encoche d'un modèle de téléphone. */
.help-panel-footer {
  flex-shrink: 0;
  padding: 8px 0 max(12px, env(safe-area-inset-bottom));

  /* Le filet dit la même chose qu'en haut : ce qui suit ne défile pas. Sans
     lui, le bouton semble posé sur la dernière ligne de la légende, qui passe
     dessous en défilant. */
  border-top: 1px solid var(--color-border);
  background: #fff;
}

/* --- Sections --- */

.help-section {
  padding: 0 20px;
  margin-bottom: 22px;
}

/* Surtitre, et non titre de niveau visuel égal à celui de la feuille : ces
   sections découpent une même page d'aide, elles n'en ouvrent pas trois. */
.help-section-title {
  margin: 0 0 12px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* LA LISTE D'ÉTAPES EST RECOPIÉE DE LA MODALE DE DÉMARRAGE, ET ELLE ARRIVE AVEC
   TOUT CE QUI ALLAIT AVEC — dont sa propre boîte défilante.

   Dans la modale, .steps-list EST la zone élastique : `flex: 1 1 auto`,
   `overflow-y: auto`, et un dégradé de fond qui signale qu'il reste à lire. Ici
   elle n'est qu'un paragraphe parmi d'autres, et ce défilement imbriqué
   capturait le geste sur toute la hauteur de la section : le doigt posé sur
   « Comment ça marche » faisait défiler une liste déjà entière, donc rien du
   tout, au lieu de la page.

   Les quatre remises à zéro ci-dessous sont donc indissociables : `flex` et
   `overflow` suppriment la boîte défilante, `background` le dégradé qui n'a
   plus rien à annoncer, `padding` le retrait qui doublerait celui de la
   section. */
.help-steps .steps-list {
  margin: 0;
  padding: 0;
  flex: 0 0 auto;
  overflow: visible;
  background: none;
}

/* --- Légende --- */

.help-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.help-legend li {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* Largeur FIXE pour la vignette : les libellés s'alignent tous sur la même
   colonne, quelle que soit la largeur du dessin qui les précède. */
.help-legend-figure {
  flex: 0 0 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
}

.help-legend-figure svg {
  display: block;
  height: 38px;
  width: auto;
}

/* Les POI de la base sont RÉELLEMENT plus petits sur la carte — 24px contre
   36 pour ceux de la session (MAIN_POI_SCALE et SESSION_POI_SCALE,
   pois-layer.js). Les montrer à la même taille ici effacerait la moitié de
   ce qui les distingue, et c'est celle qu'on perçoit en premier. */
.help-legend-figure svg.is-small {
  height: 26px;
}

.help-legend-text strong {
  display: block;
  font-size: 15px;
  font-weight: 700;
  color: var(--color-text-dark);
}

.help-legend-text span {
  display: block;
  margin-top: 1px;
  font-size: 14px;
  line-height: 1.35;
  color: var(--color-muted);
}

/* LA MARQUE « À VENIR » A ÉTÉ RETIRÉE, avec la règle qui la dessinait.
   Elle accompagnait la légende des secteurs déjà décrits, du temps où ces
   repères n'existaient pas encore sur la carte. Ils y sont, jauge comprise :
   la marque annonçait donc comme à venir quelque chose qu'on voit à l'écran.

   La règle est supprimée et non gardée en réserve — un style sans utilisateur
   se transmet de version en version sans que personne n'ose y toucher, faute
   de savoir ce qui s'en sert. */

/* --- Vignettes de tracé ---

   Les couleurs sont RECOPIÉES de tracks-layer.js — TRACK_TODO, CASING_COLOR,
   TRACK_DONE, EXPLORED_DASH_COLOR, TRACK_SELECTED, SELECTED_HALO. Doublon
   assumé, exactement comme pour .toast-swatch juste en dessous : une feuille
   de style ne peut pas importer un module. Les deux séries doivent bouger
   ensemble. */
.help-line {
  width: 40px;
  border-radius: 2px;
  box-sizing: border-box;
}

/* À évaluer : trait blanc épais dans un contour sombre. */
.help-line.is-todo {
  height: 14px;
  background: #FFFFFF;
  border: 3.5px solid #1A1A1A;
}

/* Décrit par la communauté : vert, plus fin, et cerclé comme le précédent. Le
   contour leur est commun — c'est ce qui les désigne tous deux comme des
   chemins où l'on peut vouloir aller ; l'écart d'emprise et la couleur disent
   ce qu'on en sait. */
.help-line.is-db {
  height: 11px;
  background: #64D058;
  border: 3.5px solid #1A1A1A;
}

/* Les miens : filet gris, plus fin, et SANS contour. L'absence de contour est
   l'essentiel — je les ai déjà faits, ils n'ont plus à m'appeler. */
.help-line.is-mine {
  height: 6px;
  background: #5F6663;
}

/* Décrits dans cette session : un trait NOIR de la même finesse, ponctué de
   tirets blancs plus fins que lui. Les tirets sont CONTENUS dans le trait — le
   noir reste continu au-dessus et au-dessous d'eux — pour qu'il se suive du
   regard comme n'importe quel autre chemin.

   Le pointillé ne dit pas « à moi », le trait s'en charge : il dit « pas encore
   envoyé au serveur », et disparaît à la synchronisation. */
.help-line.is-session {
  height: 6px;
  background:
    repeating-linear-gradient(to right, #FFFFFF 0 6px, transparent 6px 12px)
      center / 100% 3px no-repeat,
    #12100E;
}

/* Sélectionné : trait sombre dans un halo lime. Le halo est en box-shadow pour
   ne pas élargir la vignette, dont la colonne est de largeur fixe. */
.help-line.is-selected {
  height: 8px;
  background: #111111;
  box-shadow: 0 0 0 4px #D8FF00;
}

/* =========================================================
   Menu de session (feuille ouverte par le bouton à trois points)
   ========================================================= */

/* Reprend .session-sheet / .session-sheet-content, et n'ajoute que ce qui lui
   est propre : le menu est une variante de la feuille de bilan, pas une
   deuxième façon de faire une feuille. */
.session-menu-content {
  padding-bottom: 16px;
  /* Un menu peut dépasser sur un petit écran. Il défile alors seul, sans
     emporter la carte derrière lui. */
  max-height: calc(100dvh - var(--bottom-bar-h) - 24px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* --- Distance parcourue --- */

.session-menu-stat {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 20px 16px;
  padding: 14px 16px;
  border: 1px solid var(--color-border);
  border-radius: 14px;
  background: #FBFAF6;
}

.session-menu-stat svg {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  color: var(--color-header-dark);
}

.session-menu-distance {
  font-size: 22px;
  font-weight: 700;
  color: var(--color-text-dark);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Filet vertical entre le chiffre et son libellé : il sépare deux natures
   d'information sans ajouter de fond ni de cadre. */
.session-menu-stat-sep {
  width: 1px;
  align-self: stretch;
  background: var(--color-border);
  flex-shrink: 0;
}

.session-menu-stat-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.session-menu-stat-text strong {
  font-size: 15px;
  font-weight: 600;
  color: var(--color-text-dark);
}

.session-menu-stat-text em {
  font-style: normal;
  font-size: 14px;
  color: var(--color-muted);
}

/* --- Entrées du menu --- */

.session-menu-item {
  display: flex;
  align-items: center;
  gap: 14px;
  width: calc(100% - 40px);
  margin: 0 20px 12px;
  padding: 16px;

  border: none;
  border-radius: 16px;
  background: #EDF3EF;
  color: var(--color-text-dark);
  text-align: left;
  font-family: inherit;
  cursor: pointer;

  transition: transform 0.12s ease;
}

.session-menu-item:active {
  transform: scale(0.98);
  transition-duration: 0s;
}

.session-menu-icon {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-header-dark);
}

.session-menu-icon svg {
  width: 34px;
  height: 34px;
}

.session-menu-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.session-menu-text strong {
  font-size: 16px;
  font-weight: 700;
}

.session-menu-text em {
  font-style: normal;
  font-size: 14px;
  color: var(--color-muted);
}

.session-menu-chevron {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--color-header-dark);
}

/* --- Interrupteur --- */

/* Dessiné et non natif : un <input type="checkbox"> imposerait sa forme, et la
   rangée entière doit rester cliquable — c'est elle la cible tactile, pas la
   pastille. D'où un <button role="switch"> dont l'état vit dans aria-checked,
   qui sert à la fois à l'accessibilité et au style. */
.session-menu-switch {
  flex-shrink: 0;
  width: 52px;
  height: 30px;
  padding: 3px;
  border-radius: 999px;
  background: #C6CFC9;
  transition: background 0.2s ease;
}

.session-menu-switch span {
  display: block;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s ease;
}

[role="switch"][aria-checked="true"] .session-menu-switch {
  background: var(--color-header-dark);
}

[role="switch"][aria-checked="true"] .session-menu-switch span {
  transform: translateX(22px);
}

/* --- Retour --- */

/* .btn porte déjà la géométrie et le retour à l'appui ; il ne reste qu'à
   remonter le pictogramme au niveau du texte. */
.session-menu-back {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 4px;
}

.session-menu-back svg {
  width: 20px;
  height: 20px;
}

.session-row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 16px 20px;
  border-top: 1px solid var(--color-border);
}

.session-row-value {
  font-size: 26px;
  font-weight: 700;
  color: var(--color-text-dark);
  white-space: nowrap;
}

.session-row-value small {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-muted);
  margin-left: 2px;
}

.session-row-label {
  font-size: 17px;
  color: var(--color-text-dark);
  /* Sans lui, l'enfant flex refuse de passer sous la largeur de son contenu et
     la précision ci-dessous ne se tronque jamais : elle pousse la ligne. */
  min-width: 0;
}

/* Précision « dont X km déjà évalués par la communauté ». Sur sa PROPRE LIGNE,
   sous le libellé, et tronquée si l'écran est trop étroit : la phrase se
   comprend à son début, et la couper vaut mieux que de la voir bousculer la
   mise en page ou revenir à un mot de jargon. */
.session-row-label small {
  display: block;
  font-size: 14px;
  color: var(--color-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.session-row-label p {
  margin: 2px 0 0;
  font-size: 14px;
}

/* Bloc des chemins non évalués : fond vert-gris pour attirer l'attention */
.session-row.forgotten {
  background: #8fada4;
}

.session-row.forgotten .session-row-value,
.session-row.forgotten .session-row-label {
  color: #fff;
}

.session-row.forgotten .session-row-value small,
.session-row.forgotten .session-row-label p {
  color: rgba(255, 255, 255, 0.85);
}

/* =========================================================
   Formulaire POI en consultation (POI de la BDD)
   ========================================================= */

/* Champs verrouillés : apparence non éditable */
.readonly .poi-input {
  background: var(--color-cream-2);
  border-color: transparent;
  cursor: default;
}

/* Consultation : plus rien à choisir.
   L'estompage des autres types n'a plus lieu d'être — ils ne sont plus affichés
   du tout (voir showOnlySelectedType, poi-form.js), et seule la case du type de
   la fiche reste, avec sa couleur. Ne subsiste que le curseur, qui ne doit pas
   promettre un clic. */
.readonly .poi-type-btn {
  cursor: default;
}

/* =========================================================
   Bandeau de navigation : états de sortie d'itinéraire
   ========================================================= */

/* Écart détecté : alerte rouge pendant le recalcul */
.nav-banner.off-route {
  background: #C62828;
  transition: background 0.3s ease;
}

/* ARRIVÉE. Le bandeau reste quelques secondes après le dernier mètre, pour dire
   que le guidage s'est terminé comme prévu — et non parce qu'on l'a arrêté ou
   qu'il a échoué. Le lime, réservé aux confirmations dans toute l'application,
   suffit à faire passer le message avant même qu'on lise la phrase. */
.nav-banner.arrived {
  background: var(--color-lime);
  color: var(--color-header-dark);
  transition: background 0.3s ease;
}

.nav-banner.arrived .nav-stop {
  background: rgba(0, 0, 0, 0.10);
  color: var(--color-header-dark);
}

/* LE BLANC NE VEUT DIRE QU'UNE SEULE CHOSE : L'ITINÉRAIRE EST RETROUVÉ.

   Il l'a un temps signifié deux : la sortie prolongée le prenait aussi, et l'on
   avait donc le même blanc pour « vous êtes dehors depuis une minute » et pour
   « vous venez de revenir ». Une couleur qui dit une chose et son contraire ne
   dit plus rien, et c'est celle qu'on regarde en roulant.

   TROIS COULEURS, TROIS SENS, SANS RECOUVREMENT :
     rouge  — hors du tracé, quelle que soit la durée
     blanc  — retrouvé, en attente de confirmation (ROUTE_RECOVERY_METERS)
     vert   — sur le tracé

   .recovering dure les quinze mètres de confirmation, puis onRecovered la
   retire. Un nouvel écart pendant ce temps repasse au rouge par le chemin
   ordinaire, sans règle particulière ici. */
.nav-banner.recovering {
  background: #fff;
  color: var(--color-text-dark);
  transition: background 0.4s ease;
}

.nav-banner.recovering .nav-stop {
  background: rgba(0, 0, 0, 0.08);
  color: var(--color-text-dark);
}

/* Bouton de recalcul, proposé quand l'utilisateur s'est écarté. Incompressible :
   c'est l'instruction, à sa gauche, qui se tronque pour lui faire place. */
.nav-rejoin {
  display: flex;
  align-items: center;
  flex-shrink: 0;

  /* RÉSERVE LA PLACE DE LA CROIX, qui est posée en absolu dans le coin (voir
     .nav-stop) et ne pousse donc rien devant elle. Sans cette marge, le bouton
     s'étend jusqu'au bord du bandeau et passe dessous : les deux se
     superposaient, et c'est la croix qu'on touchait en visant « RECALCULER ».
     30px de croix, 6 d'ancrage au bord, 6 d'écart entre les deux. */
  margin-right: 42px;

  background: #fff;
  color: #C62828;
  border: none;
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}

.nav-rejoin:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Le bouton n'existe plus que sur le ROUGE — c'est-à-dire hors du tracé, alerte
   en cours ou prolongée. Il n'apparaît jamais sur le blanc, où l'itinéraire est
   retrouvé et où il n'y aurait rien à recalculer. La règle ci-dessus, écrite
   pour le fond rouge, suffit donc seule. */

/* =========================================================
   Destination libre
   =========================================================
   Panneau compact, dans la même pile que la feuille de tracé et avec le même
   bouton « Aller » : c'est la même action, elle se présente pareil. Il est
   volontairement plus bas et plus léger que la feuille — il ne porte qu'un
   libellé, là où celle-ci porte une évaluation. */
/* HAUTEUR FIXÉE À 100px. Le panneau ne porte qu'une ligne de libellé et un
   bouton : sa hauteur variait avec la longueur du nom trouvé par la recherche,
   et il sautait d'une taille à l'autre d'un résultat au suivant. */
.destination-panel {
  height: 100px;

  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;

  /* Hauteur FIXE : le panneau ne porte qu'un libellé, dont la longueur varie
     selon le lieu trouvé par la recherche. Sans hauteur imposée, il se tasse ou
     s'étire d'une destination à l'autre, sous la feuille de tracé qui, elle, ne
     bouge pas. Le libellé se tronque déjà (voir .destination-label), il n'a donc
     jamais à pousser cette valeur. */
  height: 80px;
  padding: 0 16px;

  background: #fff;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-card);
}

.destination-icon {
  flex-shrink: 0;
  color: var(--color-header-dark);
}

.destination-icon svg {
  width: 26px;
  height: 26px;
  display: block;
}

.destination-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}

.destination-eyebrow {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* Le libellé vient de la recherche de lieu : il peut être long, et c'est lui
   qui cède la place au bouton, jamais l'inverse. */
.destination-label {
  font-size: 16px;
  font-weight: 700;
  color: var(--color-text-dark);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* =========================================================
   Recherche de lieu (géocodage)
   ========================================================= */

/* Même parti que .choice-panel : une surface blanche pleine page plutôt qu'une
   carte posée sur un voile. Le champ de saisie appelle le clavier, qui mange
   déjà la moitié de l'écran — une modale flottante n'aurait plus de place. */
.geosearch-modal {
  position: fixed;
  inset: 0;
  z-index: 960;
  background: #fff;

  display: flex;
  flex-direction: column;

  padding:
    max(16px, env(safe-area-inset-top))
    20px
    calc(var(--bottom-bar-h) + max(16px, env(safe-area-inset-bottom)));
}

/* UNE SEULE RANGÉE POUR LE CHAMP ET LA SORTIE.
   La croix vivait sur sa propre ligne, centrée : près de soixante pixels pris
   en haut d'un écran dont le clavier mange déjà la moitié, pour une action
   qu'on ne fait qu'une fois. */
.geosearch-bar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}

/* LE CHAMP SE LIT COMME UN CHAMP, et non comme un titre souligné. Le simple
   trait du dessous ne disait pas où appuyer : sur un fond blanc pleine page,
   rien ne distinguait la zone de saisie du reste de l'écran. Une surface crème
   arrondie porte le même vocabulaire que les autres entrées de l'application. */
.geosearch-field {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  border-radius: 14px;
  background: var(--color-cream);
}

.geosearch-field-icon {
  flex-shrink: 0;
  display: flex;
  width: 20px;
  height: 20px;
  color: var(--color-muted);
}

.geosearch-field-icon svg {
  width: 100%;
  height: 100%;
}

.geosearch-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  padding: 13px 0;
  color: var(--color-text-dark);

  /* 17px et jamais moins de 16 : en dessous, iOS zoome sur le champ à la prise
     de focus et la carte reste décadrée au retour. */
  font-size: 17px;
}

.geosearch-input::placeholder {
  color: var(--color-muted);
}

.geosearch-clear {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  padding: 3px;
  background: none;
  border: none;
  color: var(--color-muted);
  cursor: pointer;
}

.geosearch-clear svg {
  width: 100%;
  height: 100%;
}

/* « ANNULER » EN TOUTES LETTRES, ET NON UNE CROIX. C'est la seule sortie de cet
   écran, et une croix de plus à côté de celle qui vide le champ demandait de
   deviner laquelle fait quoi. Le mot ne se confond avec rien. */
.geosearch-cancel {
  flex-shrink: 0;
  padding: 0;
  background: none;
  border: none;
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  color: var(--color-header-dark);
  cursor: pointer;
}

/* Ce que l'écran dit tant qu'il n'a rien d'autre à dire. Voir showHint,
   geosearch.js : masquée dès qu'un résultat s'affiche. */
.geosearch-hint {
  flex-shrink: 0;
  margin: 18px 2px 0;
  font-size: 14px;
  line-height: 1.45;
  color: var(--color-muted);
}

.geosearch-status {
  flex-shrink: 0;
  margin: 18px 0 0;
  font-size: 14px;
  color: var(--color-muted);
}

.geosearch-results {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;

  /* La liste défile derrière le champ, qui reste fixe : on ne perd pas sa
     saisie en parcourant les résultats. */
  -webkit-overflow-scrolling: touch;
}

/* RANGÉE CLIQUABLE, ET NON LIGNE DE TEXTE. Une hauteur de cible confortable,
   un repère devant, et une surface qui s'allume à l'appui : on parcourt une
   liste de lieux du regard, on ne la lit pas ligne à ligne. */
.geosearch-result {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 10px 10px;
  background: none;
  border: none;
  border-radius: 12px;
  text-align: left;
  cursor: pointer;
  color: var(--color-text-dark);
}

.geosearch-result-pin {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--color-cream);
  color: var(--color-header-dark);
}

.geosearch-result-pin svg {
  width: 20px;
  height: 20px;
}

/* `min-width: 0` : sans lui, un contexte administratif long refuse de se
   tronquer et pousse la rangée hors de l'écran. */
.geosearch-result-text {
  min-width: 0;
}

/* Le nom du lieu porte l'information ; le contexte administratif ne sert qu'à
   départager deux homonymes, d'où la hiérarchie de taille et de couleur. */
.geosearch-result strong {
  display: block;
  font-size: 16px;
  font-weight: 600;
}

.geosearch-result span.geosearch-result-text > span,
.geosearch-result-text > span {
  display: block;
  margin-top: 2px;
  font-size: 14px;
  color: var(--color-muted);

  /* Une seule ligne : deux résultats se départagent par leur début, et un
     contexte qui passe à la ligne fait varier la hauteur des rangées. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.geosearch-result:active {
  background: var(--color-cream);
}

/* =========================================================
   Toast — message éphémère
   ========================================================= */

/* fixed et non absolute : comme tous les overlays du projet, et sans dépendre
   du fait que #map-container se trouve être positionné.

   z-index AU-DESSUS de 1000 : deux des cas d'usage se produisent modale
   ouverte (échec d'envoi d'un POI derrière #poi-modal, qui est à 1000). En
   dessous, le message serait invisible précisément là où il compte. */
.toastContainer {
  position: fixed;

  top: 16px;
  left: 16px;

  /* ANCRAGES SYMÉTRIQUES, ET LE CHEVAUCHEMENT ASSUMÉ.
     Une gouttière avait été réservée à droite pour la colonne de contrôles, que
     les placements bas venaient recouvrir. Elle réglait ce cas-là et en abîmait
     un bien plus fréquent : décentré de trente pixels, un message posé en haut
     de l'écran se lit de travers, à chaque fois, alors que le recouvrement ne
     concerne qu'un message et qu'il dure cinq secondes. Un toast est éphémère,
     les boutons sont toujours là — c'est au premier de céder. */
  right: 16px;
  z-index: 1100;

  /* 400px déborde d'un écran de 360 à 390px : on plafonne, et margin-inline
     recentre dès que l'écran est plus large. */
  max-width: 400px;
  margin-inline: auto;

  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  padding: 12px;

  border: 2px solid;
  border-radius: 6px;
  box-shadow: var(--shadow-card);

  /* Le masquage passe par .hidden, comme partout ailleurs dans le projet :
     display est donc déclaré ici, pas retiré en JS. */
  transition: top 0.25s ease;
}

/* Navigation en cours : le bandeau porte l'instruction de conduite, on ne la
   recouvre pas. Le toast passe donc JUSTE dessous.

   LA HAUTEUR EST MESURÉE, PLUS DÉDUITE. `--nav-banner-h` est une constante de
   108px, tenue à la main en accord avec le min-height du bandeau — et le
   bandeau ne s'y tient pas : il grandit d'une ligne à l'arrivée, et d'autant
   plus en sortie d'itinéraire, où le message passe à la ligne. Le toast venait
   alors se poser DANS le bandeau. showToast pose donc `--nav-banner-actual-h`
   avec la hauteur réellement occupée ; la constante ne sert plus que de repli
   si la mesure manque. */
.toastContainer.below-nav {
  /* 16px et non 8 : à 8, le message se lisait comme un prolongement du bandeau
     plutôt que comme une surface posée dessous — d'autant que les deux sont
     larges et à angles arrondis. L'écart doit se voir pour que les deux se
     distinguent. */
  top: calc(var(--nav-banner-actual-h, var(--nav-banner-h)) + 16px);
}

/* Message posé au MILIEU de l'écran.
   Réservé à ce qu'il ne faut pas manquer : en haut, le message attend en
   périphérie du regard, qui reste sur la carte ; au centre, il le coupe.

   `top: 50%` puis translation de la moitié de sa propre hauteur — c'est le seul
   centrage vertical qui ne demande pas de connaître cette hauteur, laquelle
   dépend du texte affiché. */
.toastContainer.center {
  top: 50%;
  transform: translateY(-50%);
}

/* Message posé au SIXIÈME DE LA HAUTEUR.
   Placement du message de démarrage et de celui de reprise, qui décrivent tous
   deux ce qu'il y a SUR la carte : au centre, ils recouvraient la partie qu'ils
   demandent de regarder, et il fallait attendre leur disparition pour vérifier
   ce qu'on venait de lire.

   PAS DE TRANSLATION, contrairement au centrage : `top` désigne ici le bord
   supérieur, et non le milieu de la boîte. C'est ce qui rend le placement
   indépendant de la hauteur du message — la légende en fait varier le nombre de
   lignes, et un message qui grandit doit descendre vers le bas de l'écran,
   jamais remonter vers la barre d'état. */
/* CLASSE RENOMMÉE avec sa valeur : `quarter` annonçait un quart, et à 15 % le
   nom aurait menti à la première relecture. Elle ne la nomme plus depuis que
   celle-ci est passée à 9 % : le message de démarrage porte deux lignes de
   légende, il est le plus haut de tous, et à 15 % son bas descendait au tiers
   de l'écran — sur la partie de carte qu'il demande justement de regarder. */
.toastContainer.upper {
  top: 9%;
}

/* `.top` n'a besoin d'aucune règle : c'est le placement par défaut, déjà porté
   par le `top: 16px` de .toastContainer. La classe est tout de même posée par
   showToast, pour que les trois placements se retirent de la même façon. */

/* --- Légende ---
   Deux lignes sous le texte, chacune reliant une FORME de tracé à ce qu'elle
   veut dire. Elle n'apparaît que là où elle est demandée (option `legend`), et
   ne coûte rien aux autres messages. */
.toast-legend {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;

  display: flex;
  flex-direction: column;
  gap: 7px;
}

.toast-legend li {
  display: flex;
  /* flex-start et non center : quand la seconde ligne passe sur deux lignes de
     texte, la pastille doit rester en regard de la PREMIÈRE, pas se recentrer
     au milieu du bloc. */
  align-items: flex-start;
  gap: 9px;
}

.toast-legend-text {
  font-size: 13px;
  line-height: 18px;
  color: #535862;
  /* Autorise le bloc à rétrécir sous sa largeur de contenu, sans quoi une ligne
     longue déborderait du toast au lieu de passer à la ligne. */
  min-width: 0;
}

.toast-legend-text strong {
  font-weight: 700;
  color: #121212;
}

/* --- Pastilles ---
   Elles reproduisent le TRACÉ, pas une couleur : c'est un trait qu'on cherche
   sur la carte, et un rond n'aurait rien à voir avec lui. L'épaisseur et le
   contour portent la distinction exactement comme ils la portent à l'écran.

   LES COULEURS SONT RECOPIÉES DE map_explorer/tracks-layer.js — TRACK_TODO,
   CASING_COLOR et TRACK_DONE. Doublon assumé : une feuille de style ne peut pas
   importer un module, et faire dépendre un message d'interface de la couche de
   carte pour trois valeurs coûterait plus que de les tenir synchronisées. Même
   convention que MANUAL_SELECT_MAX_DISTANCE_METERS, déclarée deux fois.

   Décalées de 4px vers le bas pour tomber sur la ligne de base du texte, dont
   la hauteur de ligne est plus grande qu'elles. */
.toast-swatch {
  flex: 0 0 auto;
  width: 26px;
  margin-top: 4px;
  border-radius: 2px;
  box-sizing: border-box;
}

/* À évaluer : trait blanc épais dans un contour sombre. Les proportions suivent
   celles de la carte au zoom 14 — 5,5px de blanc dans 12,5px de contour. */
.toast-swatch.is-todo {
  height: 12px;
  background: #FFFFFF;
  border: 3px solid #1A1A1A;
}

/* Décrit par la communauté : vert, plus fin, et cerclé comme un chemin à
   évaluer. Proportions de la carte au zoom 14 — 3,3px de vert dans 10,3px de
   contour. C'est l'ÉCART D'EMPRISE qui hiérarchise les deux familles, le
   contour étant désormais commun. */
.toast-swatch.is-db {
  height: 10px;
  margin-top: 5px;
  background: #64D058;
  border: 3.5px solid #1A1A1A;
}

.toastContent {
  flex: 1;
  min-width: 0;
}

/* Titre et texte se suivent dans le flux : le titre porte l'essentiel, le
   texte la précision. */
/* Le titre porte l'essentiel : plus gros, plus gras et plus sombre que la
   précision qui le suit. 700 plutôt que 600 — à cette taille, sur un guidon,
   la nuance entre demi-gras et gras est ce qui fait qu'on lit le titre en
   premier au lieu de lire la phrase entière. */
.titleToast {
  font-size: 17px;
  font-weight: 700;
  color: #121212;
}

.textToast {
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;

  /* #535862 sur les fonds très clairs ci-dessous donne un contraste supérieur
     à 7:1. C'était 3,4:1 sur le vert plein #76C476 d'origine, sous le seuil
     AA — illisible sur un guidon en plein soleil. */
  color: #535862;
}

.closeBtnToast {
  all: unset;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #535862;
}

.closeBtnToast svg {
  width: 100%;
  height: 100%;
}

/* --- Tons ---
   Fonds très clairs et bordure saturée : le message reste lisible par-dessus
   la carte, et la couleur se lit d'un coup d'œil sans écraser le texte. */

.toastContainer.tone-success {
  background-color: #F6FEF9;
  border-color: #12B76A;
}

.toastContainer.tone-error {
  background-color: #FFFBFA;
  border-color: #F04438;
}

.toastContainer.tone-info {
  background-color: #F5FAFF;
  border-color: #2E90FA;
}

/* Opération en cours : persistant, remplacé par son issue. */
.toastContainer.tone-pending {
  background-color: #FFFCF5;
  border-color: #F79009;
}

/* --- Rappel du bouton de géolocalisation ---

   QUINZE SECONDES APRÈS LE DÉPART, SI RIEN N'A ÉTÉ FAIT. Posée par
   remindGeolocation (map-controls.js), retirée au premier appui.

   À NE PAS CONFONDRE AVEC `.denied`, juste en dessous. Celle-là clignote en
   ROUGE et dit qu'une action vient d'échouer faute de position : c'est un
   constat. Celle-ci APPELLE, en vert, et dit qu'il reste un geste à faire :
   c'est une invitation. Deux signaux de même couleur sur le même bouton
   auraient rendu les deux illisibles.

   L'ANNEAU S'ÉLOIGNE AU LIEU DE CLIGNOTER. Un clignotement se lit comme une
   alarme — quelque chose ne va pas — quand il n'y a rien d'anormal : la
   position n'est simplement pas encore activée. L'onde qui s'écarte du bouton
   désigne le bouton sans rien dramatiser, et c'est le vocabulaire déjà admis du
   « appuyez ici ».

   LE FOND NE BOUGE PAS, ET C'EST UNE CORRECTION.
   Il passait à la couleur de la charte, « comme ce qu'il va devenir ». Or
   c'est aussi, à peu de chose près, la couleur de l'état ACTIF (voir
   #btnGeolocation.active) : le bouton annonçait donc un suivi en cours au
   moment précis où il réclame qu'on le démarre. Le rappel disait le contraire
   de ce qu'il voulait dire, et il le disait dans le vocabulaire de couleur le
   plus établi de cette interface.

   L'ONDE SUFFIT, ET ELLE SEULE PARLE DE CE QUI MANQUE. Elle est la partie du
   rappel qui n'est ambiguë avec aucun état : rien d'autre, sur cette carte,
   n'émet d'anneau. Le fond, lui, reste celui du suivi à l'arrêt — qui est la
   vérité de l'instant.

   La règle SUBSISTE pour `position: relative`, dont dépend le positionnement
   de l'onde ci-dessous. */
#btnGeolocation.needs-gps:not(.active):not(.is-locating) {
  position: relative;
}

#btnGeolocation.needs-gps:not(.active):not(.is-locating)::after {
  content: '';
  position: absolute;
  /* Part du bord EXTÉRIEUR du liseré blanc de 3px : l'onde semble émise par le
     bouton entier, et non par le disque coloré qu'il contient. */
  inset: -3px;
  border-radius: 50%;
  border: 3px solid var(--color-header-dark);

  /* INDISPENSABLE : l'anneau grandit jusqu'à déborder largement du bouton, et
     intercepterait les appuis destinés à ses voisins — y compris à celui qu'il
     est censé faire toucher, une fois l'onde au plus large. */
  pointer-events: none;

  animation: geoloc-call 1.9s ease-out infinite;
}

@keyframes geoloc-call {
  0%   { transform: scale(1);    opacity: 0.6; }
  /* L'onde s'éteint AVANT la fin du cycle : le reste est un silence. Sans lui,
     une nouvelle onde repart à l'instant où la précédente disparaît, ce qui
     donne un battement continu — de nouveau une alarme. */
  70%  { transform: scale(1.6);  opacity: 0; }
  100% { transform: scale(1.6);  opacity: 0; }
}

/* Mouvement réduit : l'onde devient un anneau fixe. Le rappel doit rester —
   c'est la seule chose qui désigne encore le bouton une fois le message de
   départ effacé — mais il cesse de bouger. */
@media (prefers-reduced-motion: reduce) {
  #btnGeolocation.needs-gps:not(.active):not(.is-locating)::after {
    animation: none;
    transform: scale(1.3);
    opacity: 0.45;
  }
}

/* --- Alerte du bouton de géolocalisation ---
   Une action a échoué faute de position : le bouton clignote en rouge, il est
   la solution. Posée par flashGeolocationDenied (map-controls.js). */
#btnGeolocation.denied {
  animation: geoloc-denied 0.5s ease-in-out 3;
}

@keyframes geoloc-denied {
  /* Phase de repos = la couleur RÉELLE du bouton dans cette situation, c'est-à-
     dire le gris du suivi à l'arrêt : si la géolocalisation est refusée, le
     suivi n'a par définition pas démarré. */
  0%, 100% {
    background: #D6DAD8;
    color: var(--color-header-dark);
  }
  50% {
    background: #E53935;
    color: #fff;
  }
}

/* Le clignotement ne doit pas s'imposer à qui a demandé moins d'animation :
   la couleur reste, seul le battement disparaît. */
@media (prefers-reduced-motion: reduce) {
  #btnGeolocation.denied {
    animation: none;
    background: #E53935;
    color: #fff;
  }
}
/* --- Chevron de modification ---
   Remplace la mention "Modifiable" qui débordait sous chaque pastille : dans la
   feuille de tracé, le mot est porté une seule fois par le badge d'état, et le
   chevron suffit à dire que la pastille s'ouvre.

   Même règle d'apparition qu'avant : data-editable est posé par
   refreshPillBadges (evaluation-ui.js) quand la pastille porte une valeur ET
   reste cliquable. Une pastille vide dit déjà "Choisir", et la pente d'un
   chemin disparu est verrouillée. */
/* HORS DU FLUX. En visibility:hidden il gardait sa largeur et poussait le texte
   vers la gauche ; en display:none sa largeur disparaissait et le texte sautait
   d'un état à l'autre. En absolu il ne fait ni l'un ni l'autre : place toujours
   identique, et le texte se centre sur la pastille entière. */
.pill-chevron {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  opacity: 0.45;
  display: none;
}

.pill-choice[data-editable="true"] .pill-chevron {
  display: block;
}

/* État "non évaluée" : le chemin porte une évaluation, ce champ-là est vide.
   Rendu distinct du "Choisir" d'un chemin vierge, sans aller jusqu'au rendu
   coloré d'une option réellement choisie. */
.pill-choice[data-state="unevaluated"] .pill-sub {
  color: var(--color-muted);
  font-style: italic;
}

/* =========================================================
   Modale de validation — actions et options
   ========================================================= */

/* Boutons en bas de la modale, après la liste d'options. Collants pour rester
   atteignables : .modal.card défile, et la liste des options peut dépasser la
   hauteur disponible sur un petit écran. */
.modal-actions-sticky {
  position: sticky;
  bottom: 0;
  z-index: 1;

  /* Fond opaque et léger dégradé au-dessus : sans eux, les options défilantes
     transparaîtraient derrière les boutons. */
  background: #fff;
  padding-top: 14px;
  /* Porte la marge du bas ET la safe-area, que la carte ne pose plus (voir
     .overlay-backdrop.fullscreen .modal.card). L'ancien `margin-bottom: -20px`
     annulait le padding de la carte dans le calcul de hauteur, mais pas dans
     celui de `sticky` — d'où les 20px résiduels sous les boutons. */
  padding-bottom: max(20px, env(safe-area-inset-bottom));
  box-shadow: 0 -8px 12px -8px rgba(0, 0, 0, 0.18);
}

/* Ligne d'option cochée : fond teinté, bordure gauche et libellé renforcé.
   Les cases sont petites et rondes, leur seul état coché se remarque mal en
   roulant — c'est toute la ligne qui doit changer.

   :has() permet de styler le <label> depuis l'état de son <input>, ce que le
   CSS ne savait pas faire ; sur un navigateur qui ne le gère pas, la case
   reste cochée normalement, seule la mise en valeur manque. */
/* FOND PLEIN, ET NON PLUS UNE TEINTE PÂLE.
   Le #F1F7F5 d'origine se distinguait à peine du blanc de la modale : en plein
   soleil, sur un téléphone à luminosité automatique, une option cochée et une
   option vide se ressemblaient. La rangée bascule donc entièrement dans le vert
   de la charte, et son texte passe en blanc — c'est le contraste le plus fort
   dont on dispose sans introduire une couleur qui ne serait nulle part ailleurs.

   La barre verticale en inset disparaît : elle signalait l'état quand le fond
   ne le faisait pas. Le fond le fait maintenant, et sur du vert plein elle ne
   se verrait plus de toute façon. */
.option-row:has(input[type="checkbox"]:checked) {
  background: var(--color-header-dark);

  /* Le filet de séparation est beige : il trancherait sur le vert. Il n'a rien
     à séparer non plus, la rangée se délimitant désormais toute seule. */
  border-bottom-color: transparent;
}

.option-row:has(input[type="checkbox"]:checked) strong {
  color: #fff;
}

/* La description reste en retrait, mais en blanc atténué plutôt qu'en gris :
   le gris de --color-muted est illisible sur ce fond. */
.option-row:has(input[type="checkbox"]:checked) p {
  color: rgba(255, 255, 255, 0.82);
}

/* Sur fond vert, une case verte disparaîtrait. Elle se creuse : bordure et
   coche blanches sur un intérieur laissé transparent. */
.option-row:has(input[type="checkbox"]:checked) input[type="checkbox"]:checked {
  background-color: transparent;
  border-color: #fff;
}

/* Invitation à contribuer une photo sur un POI de la base qui n'en a pas.
   Affichée par refreshPhotoButton (poi-form.js), et masquée dès qu'une photo
   est choisie : le message aurait alors cessé d'être vrai. */
.poi-photo-invite {
  margin: 10px;
  padding: 10px 12px;
  background: #F5FAFF;
  border: 1px solid #2E90FA;
  border-radius: 8px;
  font-size: 14px;
  line-height: 1.4;
  color: #1c2b25;
}

/* Variante compacte : une confirmation d'un mot n'a pas besoin de la pleine
   largeur ni du fer à gauche. Posée par showToast({ compact: true }). */
.toastContainer.compact {
  max-width: 260px;
  padding: 8px 10px;
}

.toastContainer.compact .toastContent {
  text-align: center;
}

.toastContainer.compact .titleToast {
  font-size: 14px;
}

/* --- Pictogramme de manœuvre ---
   Encadré et détaché du texte : c'est la première chose que l'œil accroche
   quand on relève la tête. Le tracé suit currentColor. */
.nav-maneuver-icon {
  flex-shrink: 0;
  /* LE CADRE SUIT LE TRACÉ, qui fait 50px (voir la règle svg plus bas). Il lui
     laisse 5px de part et d'autre : collé aux bords, le fond translucide
     paraîtrait taillé dans le dessin plutôt que posé derrière lui. C'est ce qui
     a fait passer le bandeau de 80 à 84px. */
  width: 60px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;

  border-radius: 10px;
  /* currentColor à faible opacité : le cadre reste visible sur fond sombre
     comme sur fond clair, sans règle par état. */
  background: color-mix(in srgb, currentColor 14%, transparent);
  color: inherit;
}

.nav-maneuver-icon svg {
  /* La flèche est la première chose que l'œil accroche quand on relève la tête
     du guidon : elle occupe désormais la pleine hauteur utile du bandeau. */
  width: 50px;
  height: 50px;
}

/* Vide (hors itinéraire, pas d'instruction) : le cadre disparaît plutôt que de
   laisser un carré translucide sans contenu. */
.nav-maneuver-icon:empty {
  display: none;
}

/* --- Texte de manœuvre, agrandi ---
   Lu d'un coup d'œil en roulant : la distance prime, l'instruction suit. */
.nav-maneuver {
  min-width: 0;
  display: flex;
  flex-direction: column;
  /* stretch EXPLICITE : c'est lui qui donne aux deux lignes la largeur du
     conteneur, et donc à l'instruction une largeur à tronquer. Une ancienne
     règle homonyme posait `baseline` ici, ce qui les laissait s'étendre à la
     largeur de leur texte — le nowrap débordait alors sous le bouton. */
  align-items: stretch;
  /* 5px et non 2 : dans un bandeau plus haut, deux lignes collées laissaient
     l'espace gagné en haut et en bas au lieu de le mettre entre elles. */
  gap: 5px;
}

/* `inherit` et non `#fff` : le bandeau passe au BLANC en sortie prolongée
   (.off-route-abandoned), où un texte blanc disparaît purement et simplement.
   La couleur suit désormais celle du bandeau, quel que soit son état. */
.nav-maneuver #nav-maneuver-distance {
  font-size: 28px;
  font-weight: 800;
  line-height: 1.1;
  color: inherit;
}

.nav-maneuver #nav-instruction {
  font-size: 16px;
  font-weight: 500;
  line-height: 1.3;
  /* Même raison : couleur héritée, atténuée par l'opacité plutôt que par un
     blanc translucide qui ne vaudrait que sur fond sombre. */
  color: inherit;
  opacity: 0.88;

  /* UNE seule ligne, tronquée : personne ne lit une instruction complète en
     roulant, et sur deux lignes elle repoussait « ARRETER » hors du bandeau. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* SORTIE D'ITINÉRAIRE : LA LIGNE DE MANŒUVRE DISPARAÎT.
   « dans 120 m » ne désigne plus rien quand on est à deux cents mètres du
   tracé, et la distance totale est de toute façon gelée tant qu'on n'a pas
   raccroché (voir updatePosition dans navigation.js). Le message prend toute la
   hauteur du bandeau. */
.nav-banner.off-route .nav-maneuver-line {
  display: none;
}

/* Et il PEUT PASSER À LA LIGNE, contrairement à une instruction de conduite.
   Une consigne se lit d'un coup d'œil et se tronque sans dommage ; ce
   message-ci est une phrase entière, dont la fin — où regarder — est la partie
   utile. La tronquer donnerait « Vous avez quitté l'itinér… ». */
.nav-banner.off-route #nav-instruction {
  white-space: normal;
  overflow: visible;
  font-size: 16px;
  line-height: 1.35;
  opacity: 1;

  /* CENTRÉ, PARCE QUE CE N'EST PAS UNE CONSIGNE.
     Le bandeau range ses instructions à gauche, sous la ligne de distance :
     c'est une colonne qu'on parcourt du même point de départ à chaque fois.
     Ce message-ci n'appartient pas à cette colonne — la ligne de distance est
     masquée juste au-dessus (voir la règle précédente) et rien ne suit — si
     bien qu'une phrase courte calée à gauche laissait un grand vide à sa
     droite, et se lisait comme une consigne restée inachevée.
     `display: block` EST INDISPENSABLE : #nav-instruction est un <span>, et
     `text-align` sur un élément en ligne aligne son contenu sans lui donner de
     largeur à occuper — la règle n'aurait rien fait du tout. */
  display: block;
  text-align: center;
}

/* ARRIVÉE : MÊME LIBERTÉ, POUR LA MÊME RAISON.

   Le message de fin est une phrase entière, et la troncature tombait sur sa
   moitié utile : on lisait « Vous êtes au départ du chemin. Bonne ex… ».
   Une consigne de conduite se tronque sans dommage parce qu'on la lit en
   roulant ; celle-ci se lit une fois arrivé, à l'arrêt, et rien ne presse.

   Le bandeau grandit d'autant : .nav-banner porte un min-height et non une
   hauteur figée, et c'est déjà ce qui se passe en sortie d'itinéraire. */
.nav-banner.arrived #nav-instruction {
  white-space: normal;
  overflow: visible;
}

/* Première ligne : distance de manœuvre, puis distance totale entre
   parenthèses. La seconde est un repère de contexte, d'où sa taille réduite. */
.nav-maneuver-line {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}

.nav-total {
  font-size: 14px;
  font-weight: 600;
  color: inherit;
  opacity: 0.7;
  white-space: nowrap;
}
/* Infobulle du badge : le titre complet, déplié au clic. Posée au-dessus de la
   ligne d'en-tête, dont elle déborde volontairement — elle est éphémère. */
.track-badge-tip {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 2;

  padding: 8px 12px;
  border-radius: 10px;
  background: var(--color-text-dark);
  color: #fff;

  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}

/* Feuille de tracé ouverte : les contrôles remontent au-dessus d'elle.
   `:has()` évite d'avoir à poser une classe depuis le JS — c'est l'état du
   panneau lui-même qui pilote la position, donc rien à synchroniser. */
body:has(#track-panel:not(.hidden)) .map-controls {
  bottom: calc(var(--bottom-bar-h) + 140px);
}

/* Panneau de destination ouvert : même principe, hauteur différente.
   Une RÈGLE SÉPARÉE et non une valeur commune : les deux panneaux n'ont pas la
   même taille, et le décalage doit laisser le même vide sous les contrôles dans
   les deux cas.

   CORRIGÉE DE 92 À 112. Le commentaire d'origine comptait « 80px de panneau
   plus 12px d'écart » — mais .destination-panel en fait 100 (voir sa règle).
   Les contrôles empiétaient donc de 8px sur le panneau depuis le début, ce que
   la nouvelle position de repos, plus haute, aurait rendu franchement visible :
   ouvrir le panneau les aurait fait DESCENDRE. */
body:has(#destination-panel:not(.hidden)) .map-controls {
  bottom: calc(var(--bottom-bar-h) + 112px);
}

/* ===================================================================
   MODE GUIDAGE
   ===================================================================

   UNE SEULE APPLICATION, DEUX MODES. Ce qui est difficile — fusion des caps,
   raccrochage au réseau, seuils variables selon la précision, projection de
   l'avancement — est COMMUN aux deux et n'a aucune raison d'exister en double.
   Ce qui les sépare est de l'affichage, et se règle donc ici.

   LA CLASSE EST SUR <html>, posée par session-mode.js. Un masquage fait en
   JavaScript élément par élément aurait suffi à laisser un bouton d'évaluation
   au milieu d'un guidage le jour où l'on en oublie un.

   MAIS ELLE N'EST PAS LÀ AU PREMIER RENDU, et c'est ce que les deux règles
   ci-dessous prennent en compte. session-mode.js est un module : le navigateur
   le télécharge, avec tout ce dont il dépend, APRÈS avoir analysé le document
   et affiché ce qu'il contient. Entre les deux, <html> ne porte ni
   `mode-exploration` ni `mode-navigation` — et le carré rouge « arrêter le
   guidage » s'affichait donc au démarrage de /explorer, en tête de la colonne
   de contrôles, avant de disparaître.

   LE MASQUAGE PAR DÉFAUT NE VAUT QUE POUR LE GUIDAGE, écrit en négation pour
   couvrir aussi le moment où la classe manque. Ce qui est propre au guidage est
   caché tant qu'on ne sait pas, et n'apparaît que si l'on est bien en guidage :
   dans le doute, rien ne s'affiche.

   L'AUTRE SENS RESTE ÉCRIT EN POSITIF, et ce n'est pas une inattention. Le
   symétrique — cacher aussi l'exploration tant que le mode est inconnu —
   supprimerait la moitié de l'interface pendant tout le temps du chargement des
   modules, pour la faire apparaître d'un bloc ensuite. On échangerait un bouton
   de trop contre un écran vide, ce qui est le plus mauvais des deux. */

.mode-navigation .only-exploration {
  display: none;
}

html:not(.mode-navigation) .only-navigation {
  display: none;
}

/* --- Ce que le guidage retire de la carte ---
   Les COUCHES, elles, sont éteintes à la source : le chargeur par emprise ne
   démarre pas (voir tracks-controller.js), donc chemins, zones et points
   d'intérêt n'ont jamais de données à peindre. Restent les commandes. */

/* RECHERCHE DE LIEU ET AJOUT DE POINT : deux gestes d'exploration, faits à
   l'arrêt. Pendant un guidage, l'un déplacerait la vue loin de la route suivie
   et l'autre ouvrirait un formulaire par-dessus elle. Le troisième bouton de la
   capsule — les trois points — reste : c'est là que vivent le son et la voix. */
.mode-navigation #btn-geosearch,
.mode-navigation #btn-add-poi {
  display: none;
}

/* Le menu ne garde que ce qui règle le comportement pendant qu'on roule. Le
   résumé de session, les segments explorés et l'aide décrivent tous les trois
   une activité d'évaluation qui n'a pas lieu ici. */
.mode-navigation #btn-session-detail,
.mode-navigation #btn-session-explored,
.mode-navigation #btn-session-help,
.mode-navigation .session-menu-stat {
  display: none;
}

/* Pastille de comptage des chemins parcourus non évalués : elle compte quelque
   chose qui ne se produit pas en guidage, et resterait figée sur le total de la
   dernière exploration. */
.mode-navigation #session-summary-badge {
  display: none;
}

/* --- Bandeau de navigation, agrandi ---

   IL A LA PLACE, MAINTENANT QU'IL EST SEUL. En exploration il partageait le
   haut de l'écran avec ce qui pouvait s'y ouvrir ; ici, rien d'autre ne monte
   jusque-là. On rend donc cet espace à la seule chose qu'on lit en roulant :
   la manœuvre qui vient.

   LES TROIS GRANDEURS BOUGENT ENSEMBLE — cadre du pictogramme, tracé, texte.
   N'en grossir qu'une déséquilibrerait un dessin dont les proportions ont été
   réglées les unes par rapport aux autres (voir .nav-maneuver-icon). */

.mode-navigation .nav-banner {
  min-height: 108px;
  padding: 14px 18px;
  gap: 16px;
}

.mode-navigation .nav-maneuver-icon {
  width: 78px;
  height: 78px;
  border-radius: 12px;
}

.mode-navigation .nav-maneuver-icon svg {
  width: 66px;
  height: 66px;
}

/* La distance prime : c'est elle qu'on lit d'un coup d'œil, l'instruction ne se
   lit que si l'on a le temps. Le rapport entre les deux est conservé. */
.mode-navigation .nav-maneuver #nav-maneuver-distance {
  font-size: 36px;
}

.mode-navigation .nav-maneuver #nav-instruction {
  font-size: 19px;
}

/* Le bandeau étant plus haut, ses deux lignes doivent s'écarter d'autant :
   collées, elles laisseraient l'espace gagné au-dessus et au-dessous. */
.mode-navigation .nav-maneuver {
  gap: 7px;
}

/* La croix d'arrêt suit, sans quoi elle paraîtrait tomber dans un coin devenu
   trop grand pour elle. */
.mode-navigation .nav-stop {
  top: 8px;
}

/* --- Aucun parcours en cours ---
   Posé PAR-DESSUS la carte sans la masquer entièrement : on reste dans
   l'application, et le fond dit assez qu'il ne s'est rien passé de grave. */
.nav-empty {
  position: fixed;
  inset: 0;
  z-index: 940;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(18, 34, 51, 0.45);
}

.nav-empty-card {
  background: #fff;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 28px 24px;
  max-width: 360px;
  text-align: center;
  color: var(--color-header-dark);
}

/* Même flèche que le bouton de position, en grand et sans remplissage : elle
   dit « guidage » dans toute l'application, et un pictogramme inédit aurait
   demandé d'être appris pour un écran qu'on ne voit presque jamais. */
.nav-empty-card svg {
  width: 44px;
  height: 44px;
  color: var(--color-primary, #006C57);
  margin-bottom: 12px;
}

.nav-empty-card h2 {
  font-size: 19px;
  margin: 0 0 8px;
}

.nav-empty-card p {
  font-size: 15px;
  line-height: 1.45;
  margin: 0;
  opacity: 0.75;
}

/* --- Panneau du parcours ---
   Posé en bas, au-dessus de la barre système. Il répond à des questions qu'on
   se pose à l'arrêt ou au sommet d'une côte, pas en pleine manœuvre : le haut
   de l'écran reste au bandeau. */
/* PANNEAU DE PARCOURS DÉPLIÉ : LA COLONNE NE BOUGE PLUS, ET C'EST UN RETRAIT.

   Une règle remontait ici `.map-controls` de `--nav-panel-h` au dépliage, du
   temps où la colonne était calée sur le bas de l'écran et où le panneau,
   ouvert, lui passait devant.

   ELLE ÉCRASAIT LES BOUTONS. En guidage, `.mode-navigation .map-controls`
   centre la colonne — `top: 50%`, `bottom: auto`, translation de moitié — et le
   panneau ne la touche plus. Mais cette règle-ci l'emportait en spécificité sur
   ce `bottom: auto` : l'élément se retrouvait avec un `top` ET un `bottom`, donc
   une hauteur imposée, et ses enfants — `flex-shrink: 1` par défaut — se
   comprimaient pour y tenir. Le carré d'arrêt, en tête de colonne, était le
   premier à s'aplatir.

   Le panneau n'ayant plus rien à recouvrir, la correction est de ne pas
   corriger. `--nav-panel-h` reste mesurée et publiée par nav-panel.js : c'est
   `bottomObstructionPx` (map-controls.js) qui en a l'usage.

   ⚠ Plus personne ne lit `body.nav-panel-expanded`. La classe reste posée —
   elle décrit un état vrai, et c'est le genre de prise dont on est content le
   jour où l'on en a besoin. */

/* AUCUN ENFANT DE LA COLONNE NE SE COMPRIME. Garde-fou et non correctif : la
   règle ci-dessus était la seule à imposer une hauteur, et il n'en faut pas
   d'autre pour que les boutons s'aplatissent à nouveau sans qu'on sache
   pourquoi. Sans contrainte de hauteur — le cas ordinaire — cette ligne ne fait
   rien du tout. */
.map-controls > * {
  flex-shrink: 0;
}

.nav-panel {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: max(12px, env(safe-area-inset-bottom));
  z-index: 925;
  max-width: 560px;
  margin: 0 auto;
  background: #fff;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  color: var(--color-header-dark);
}

/* La barre ENTIÈRE est le bouton : viser une petite cible en roulant ne se
   demande pas. `position: relative` pour porter la jauge d'avancement. */
.nav-panel-bar {
  position: relative;
  width: 100%;
  min-height: var(--nav-panel-bar-h);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
}

/* Avancement en FOND DE BARRE. Il occupe une surface déjà là, et se lit sans
   qu'on ait à le chercher — une jauge séparée aurait pris une ligne pour dire
   la même chose. Très pâle : c'est un repère, pas une information à lire. */
.nav-panel-progress {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  background: color-mix(in srgb, #00372c 10%, transparent);
  transition: width .4s ease-out;
  pointer-events: none;
}

.nav-panel-figure {
  position: relative;
  display: flex;
  flex-direction: column;
  line-height: 1.1;
  min-width: 0;
}

.nav-panel-figure em {
  font-style: normal;
  font-size: 20px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* TABULAIRE, ET C'EST LA RAISON D'ÊTRE DE LA LIGNE CI-DESSUS. Ces chiffres
   changent chaque seconde : sans chasse fixe, « 11,2 » et « 18,4 » n'ont pas la
   même largeur et toute la ligne tremble. */

.nav-panel-figure small {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  opacity: .55;
}

.nav-panel-chevron {
  position: relative;
  width: 20px;
  height: 20px;
  margin-left: auto;
  flex-shrink: 0;
  opacity: .5;
  transition: transform .25s ease;
}

.nav-panel.expanded .nav-panel-chevron {
  transform: rotate(180deg);
}

.nav-panel-body {
  padding: 4px 14px 14px;
  border-top: 1px solid var(--color-border);
}

/* PLEINE LARGEUR, PAR MARGES NÉGATIVES. Le corps du panneau réserve 14 px de
   chaque côté pour ses lignes de chiffres ; le profil, lui, est un dessin, et
   quatorze pixels de blanc de part et d'autre lui coûtaient de la hauteur utile
   sans rien apporter à la lecture. Les repères posés en pourcentage suivent
   l'élargissement sans retouche. */
.nav-profile-wrap {
  position: relative;
  margin: 10px -14px 12px;
}

/* 110 px et non 64 : c'est un graphe qu'on lit à l'arrêt, au sommet d'une côte,
   pour savoir ce qui reste. À 64 px, cent mètres de dénivelé tenaient dans un
   centimètre et deux bosses différentes se ressemblaient.
   ⚠ La hauteur du viewBox suit dans nav-panel.js (PROFILE_HEIGHT) : les deux
   doivent rester d'accord, sinon le tracé s'étire verticalement. */
.nav-profile-wrap svg {
  display: block;
  width: 100%;
  height: 110px;
}

/* Plus clair : l'aplat sous la courbe sert de fond aux tronçons colorés, et à
   12 % il les assombrissait tous d'un même voile. */
.nav-profile-area {
  fill: color-mix(in srgb, #00372c 7%, transparent);
}

/* Tronçons du profil. La couleur vient de la classe de pente, posée en plus de
   celle-ci — `currentColor` fait le lien, et une seule liste de teintes sert au
   profil comme au chiffre de la barre. */
.nav-profile-band {
  fill: none;
  stroke: currentColor;
  /* vector-effect : le SVG est étiré horizontalement (preserveAspectRatio
     none), et sans cela le trait s'étirerait avec lui — épais en haut d'une
     montée, fin ailleurs. */
  stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* Altitude posée SUR le profil, coin haut GAUCHE.
   Elle était à droite, où les bornes d'altitude du parcours se sont installées
   depuis : deux chiffres en mètres superposés au même endroit, dont un seul
   bouge, se seraient lus l'un pour l'autre. À gauche, elle occupe le coin que
   le profil laisse vide au départ. */
.nav-profile-altitude {
  position: absolute;
  top: 2px;
  left: 4px;
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: rgba(255, 255, 255, .82);
  border-radius: 5px;
  padding: 1px 5px;
}

/* CENTRÉE, ET NON CALÉE À GAUCHE. Deux chiffres seulement — parcouru, dénivelé
   — sous un profil qui prend toute la largeur : accolés au bord gauche, ils
   laissaient une moitié de panneau vide et paraissaient posés là en attendant
   une troisième colonne. Au centre, ils forment le pied du dessin qu'ils
   commentent.
   `text-align` SUIT : centrer la paire sans centrer chaque légende sous son
   chiffre ne déplace le défaut que d'un cran — « dénivelé + » est plus large
   que la valeur qu'il nomme. */
.nav-panel-grid {
  display: flex;
  justify-content: center;
  text-align: center;
  gap: 22px;
}

.nav-panel-grid em {
  display: block;
  font-style: normal;
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.nav-panel-grid small {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  opacity: .55;
}

/* --- Corrections du mode guidage --- */

/* RECALCULER : MASQUÉ EN NAVIGATION.
   Ce bouton refait l'itinéraire depuis la position courante jusqu'à l'arrivée.
   C'est ce qu'il faut en exploration, où le trajet n'est qu'un moyen d'atteindre
   un chemin à décrire. En navigation, l'itinéraire EST le sujet : l'utilisateur
   l'a construit étape par étape sur le planificateur, et le remplacer par un
   calcul automatique lui ferait perdre ce qu'il est venu suivre — sans compter
   que le réseau manque précisément là où l'on s'écarte.
   Le badge de direction, lui, reste : il montre où est le tracé sans y toucher. */
.mode-navigation #btn-nav-rejoin {
  display: none;
}

/* Le pied de page de /navigation occupe le bas de l'écran. Le panneau se posait
   dessous et s'y cachait entièrement.
   REPRIS DE --bottom-bar-h, ET NON D'UN 70px ÉCRIT ICI : c'est la même hauteur
   que la barre du bas de l'explorateur, déjà déclarée une fois. Deux nombres
   égaux dans deux fichiers finissent toujours par diverger. */
.mode-navigation .nav-panel {
  bottom: calc(var(--bottom-bar-h) + max(10px, env(safe-area-inset-bottom)));
}

/* Contenu centré : trois chiffres de largeurs inégales, alignés à gauche,
   laissaient un vide croissant vers le chevron. Répartis, ils occupent la barre
   et chaque libellé reste sous son nombre. */
.nav-panel-bar {
  justify-content: space-around;
  gap: 8px;
}

.nav-panel-figure {
  align-items: center;
  text-align: center;
}

/* Le chevron sort du flux : centré avec `space-around`, il compterait comme une
   quatrième colonne et décalerait les trois chiffres vers la gauche. */
.nav-panel-chevron {
  position: absolute;
  right: 12px;
  top: 50%;
  margin-left: 0;
  transform: translateY(-50%);
}

.nav-panel.expanded .nav-panel-chevron {
  transform: translateY(-50%) rotate(180deg);
}

/* --- Couleurs de pente ---

   UNE ÉCHELLE, DEUX ENDROITS. La même couleur porte la pente dans le profil et
   dans le chiffre de la barre : en voyant l'un on apprend l'autre, sans légende.

   LES SEUILS SONT CEUX DU VÉLO, pas d'une échelle abstraite. 4 % est la limite
   du faux plat, 8 % celle où l'on change de plateau, 12 % celle où l'on se met
   en danseuse. En descente les mêmes valeurs marquent le moment où l'on cesse de
   pédaler, puis celui où l'on freine. */
/* Palette reprise du planificateur (GRADES, config.js) : le même relief doit se
   lire de la même façon avant de partir et en roulant. Les seuils suivent dans
   nav-stats.js — reprendre les teintes sans les bornes aurait produit un
   désaccord invisible. */
.slope-down { color: #5FA8DC; }        /* descente,   < -2 %  */
.slope-flat { color: #8CC63F; }        /* plat,   -2 à 3 %    */
.slope-up { color: #EFC02B; }          /* 3 à 6 %             */
.slope-up-steep { color: #EE8B2C; }    /* 6 à 9 %             */
.slope-up-severe { color: #DE5330; }   /* 9 à 12 %            */
.slope-up-extreme { color: #A62E28; }  /* > 12 %              */

/* Marqueur de position SUR la ligne du profil.
   Une barre verticale traversait tout le graphe et cachait le relief là où on
   la regardait le plus. Le disque se pose sur le tracé : il dit la même chose —
   où j'en suis — en montrant en plus l'altitude et la pente du moment. */
/* Marqueur de position : élément HTML, pas un cercle SVG.
   Le SVG est étiré en largeur — un cercle y deviendrait une ellipse, et posé à
   l'abscisse zéro au départ il se voyait tronqué par le cadre. En absolu et en
   pourcentage, il reste rond et déborde proprement. */
/* AGRANDI : c'est le seul élément mobile du graphe, et on le cherche du regard
   en une fraction de seconde, une main sur le guidon. Onze pixels sur un profil
   de 110 px de haut se perdaient dans le tracé. */
.nav-profile-marker {
  position: absolute;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid #C1121F;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  box-sizing: border-box;
  pointer-events: none;
  transition: left .4s linear, top .4s linear;
}

/* Aplat sous la courbe, par classe de pente. C'est lui qui porte la lecture :
   une colonne colorée se voit de loin, un trait de deux pixels demande de
   comparer des épaisseurs. Assez pâle pour que le trait reste net dessus. */
/* Aplat des tronçons. ALLÉGÉ DE .30 À .18 : ces colonnes se superposent à
   l'aplat général, et les deux opacités s'additionnaient en un profil
   uniformément sombre où la couleur de pente ne se distinguait plus. */
.nav-profile-fill {
  fill: currentColor;
  fill-opacity: .18;
  stroke: none;
}

/* Grille : des repères, pas une mesure. Assez pâle pour ne pas concurrencer le
   tracé qu'elle aide à lire. */
.nav-profile-gridline {
  stroke: var(--color-border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 2 3;
}

.nav-profile-ticks {
  position: relative;
  height: 14px;
  margin-top: 2px;
}

/* `translateX(-50%)` centre l'étiquette sur sa graduation ; celle de zéro
   déborderait à gauche, d'où le calage à gauche du premier enfant. */
.nav-profile-tick {
  position: absolute;
  transform: translateX(-50%);
  font-size: 10px;
  opacity: .5;
  white-space: nowrap;
}

.nav-profile-tick:first-child {
  transform: none;
}

/* Bornes d'altitude, aux deux coins droits : le profil n'a pas d'échelle
   absolue (voir buildProfilePath), et ces deux chiffres sont ce qui lui en
   donne une. */
.nav-profile-bound {
  position: absolute;
  right: 2px;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  opacity: .55;
  background: rgba(255, 255, 255, .8);
  padding: 0 3px;
  border-radius: 3px;
}

.nav-profile-bound-max { top: 0; }
.nav-profile-bound-min { bottom: 16px; }

/* Avancement figé hors itinéraire : la jauge reculerait ou bondirait au gré de
   la projection pendant qu'on cherche à revenir. */
.nav-panel-progress.waiting {
  opacity: .35;
}

/* Contrôles de carte recentrés en guidage.
   Leur position de repos se compte depuis le bas de l'explorateur, où la barre
   d'évaluation et la feuille de tracé occupent le bas de l'écran. En navigation
   ce bas appartient au panneau de parcours et au pied de page : la colonne s'y
   retrouvait coincée entre les deux, tout en bas du champ.
   CENTRÉE VERTICALEMENT : c'est la seule zone que rien n'occupe dans ce mode —
   le bandeau tient le haut, le panneau le bas — et le pouce y arrive sans que
   la paume couvre la carte. */
.mode-navigation .map-controls {
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
}

/* --- Terminer la navigation ---

   EN GUIDAGE, LA CROIX DU BANDEAU DISPARAÎT. Elle occupe le coin d'une zone
   qu'on regarde en roulant, pour une commande qu'on ne prend qu'à l'arrêt : en
   guidage, le geste vit dans la colonne de contrôles (#btn-nav-stop) et dans la
   rangée « Terminer » du menu de session.

   EN EXPLORATION, ELLE EST LÀ — voir .nav-stop plus haut et le balisage du
   bandeau. Le bouton porte déjà `.only-exploration`, qui suffirait ; cette
   règle-ci reste parce qu'elle vise la croix par ce qu'elle EST et non par le
   mode où on la montre. Si une seconde croix apparaît un jour dans ce bandeau,
   elle sera couverte sans qu'on y pense. */
.mode-navigation .nav-stop {
  display: none;
}

/* LE PANNEAU OCCUPE TOUTE LA LARGEUR, de nouveau. Il se rétrécissait ici de la
   largeur du bouton d'arrêt du bas plus son écart ; ce bouton est retiré (voir
   le balisage de #nav-panel), et la place lui revient. C'est le profil
   altimétrique qui la reçoit, lui qui en manquait le plus.
   ⚠ `--nav-panel-bar-h` sert encore, à la hauteur de la barre et au calage de
   `.bottom-stack` plus bas : la variable reste. */

/* --- « Recentrer » au-dessus du panneau ---

   Sa pile se cale sur `--bottom-bar-h`, c'est-à-dire sur le bas de
   l'explorateur. En guidage, cette hauteur est occupée par le panneau de
   parcours, qui passait devant : le bouton n'apparaissait jamais, alors qu'il
   est le seul moyen de reprendre le suivi après avoir déplacé la carte à la
   main.
   REMONTÉE DE LA HAUTEUR DE LA BARRE REPLIÉE : le panneau déplié couvre
   davantage, mais on ne le laisse pas ouvert en roulant — et un bouton calé sur
   la version dépliée flotterait au milieu de la carte le reste du temps.
   L'ÉCART SE COMPTE MAINTENANT DEPUIS LA HAUTEUR DÉCLARÉE, plus quatre pixels.
   Il valait 64 px pour une barre qui en mesure 56, soit une dizaine de pixels
   de vide entre les deux : assez pour qu'ils se lisent comme deux objets sans
   rapport, alors qu'ils forment la commande du bas de l'écran. Resserrés, ils
   font une pile. */
.mode-navigation .bottom-stack {
  bottom: calc(var(--bottom-bar-h) + var(--nav-panel-bar-h) + 4px + max(12px, env(safe-area-inset-bottom)));
}
