/* =======================================================================
   Bulletin — direction « Lichen »
   Deux polices seulement : Space Grotesk pour les titres, les chiffres et
   tout ce qui se compare d'un coup d'œil ; la police système pour le texte
   courant, qui se lit et ne se scanne pas.
   Une seule couleur d'accent (--accent). Toutes les autres couleurs de
   l'application sont sémantiques : elles disent une surface, une pluie, un
   vent, un niveau de praticabilité, et ne sont donc pas ici — elles vivent
   dans app.js et practicability.js, où elles ont un sens.
   ======================================================================= */

:root {
  --ink:        #1e221e;   /* texte principal */
  --ink-2:      #3a3f3a;   /* libellés de tableau */
  --muted:      #6c736c;   /* texte secondaire */
  --muted-2:    #8d948d;   /* notes, unités */
  --faint:      #9aa09a;   /* graduations, mentions */

  --hair:       #eceee9;   /* filets internes */
  --line:       #e5e7e2;   /* bordures de cartes */
  --line-2:     #dfe2dc;   /* bordures de conteneurs */

  --paper:      #fbfbf8;   /* fonds de panneau */
  --canvas:     #f5f6f2;   /* fond de la page Analyse */
  --sunk:       #f4f5f1;   /* champs et pastilles enfoncées */

  --accent:     #5f7a63;
  --accent-ink: #526a56;

  /* Bandeau : le seul aplat sombre de l'application. */
  --band:       linear-gradient(105deg, #101a13 0%, #243024 44%, #41563f 100%);
  --band-fg:    #eef0ec;
  --band-dim:   #c9cfc7;
  --band-mark:  #aebfae;

  --r-sm: 9px;
  --r-md: 12px;
  --r-lg: 16px;

  --display: "Space Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --ui: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;

  --shadow-float: 0 2px 12px rgba(30, 34, 30, 0.16);
  --shadow-menu:  0 10px 30px rgba(30, 34, 30, 0.2);

  /* Une seule courbe et deux durées pour toute l'application. Court : rien
     ici ne mérite d'être attendu, le mouvement accompagne l'action, il ne
     la met pas en scène. */
  --ease:   cubic-bezier(0.2, 0.6, 0.3, 1);
  --t-fast: 110ms;
  --t-base: 170ms;
}

* { box-sizing: border-box; }

/* Un seul sens pour « masqué », partout dans l'application : les états
   (panneau replié, catégorie sans données, mode inactif) sont pilotés par
   cette classe, et rien ne doit pouvoir la contredire. */
.hidden,
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
  font-family: var(--ui);
  color: var(--ink);
  background: var(--paper);
}

/* Colonne pleine hauteur : l'en-tête prend ce qu'il lui faut, le reste va à
   `main`. Une hauteur d'en-tête codée en dur ne tient pas — il passe à deux ou
   trois lignes dès que la fenêtre se resserre, et `main` débordait alors par
   le bas en emportant le profil d'altitude hors de l'écran. */
body {
  display: flex;
  flex-direction: column;
}

button { font-family: inherit; }

/* Barres de défilement discrètes : le panneau et la page Analyse défilent
   beaucoup, et une barre système large mange 17 px de panneau. */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #d3d7d0; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #bfc5bd; }

/* =======================================================================
   0. Mouvement et focus
   Les transitions sont regroupées ici plutôt qu'éparpillées sur vingt
   règles : une seule liste dit exactement ce qui bouge dans l'application,
   et rien ne peut y entrer par inadvertance.
   Deux principes :
   — on n'anime que ce qui répond. Une carte du tableau de bord qui réagit
     au survol se donne l'air cliquable alors qu'elle ne l'est pas.
   — on n'anime que des propriétés composées (couleur, ombre, opacité,
     transform). Animer une largeur ou une hauteur relance le calcul de mise
     en page à chaque image ; sur une page qui porte une carte Leaflet et
     quatre graphiques SVG, ça se voit.
   Rien de tout cela ne touche le curseur partagé (trait des graphiques et
   des rubans) : il doit coller au pointeur au pixel près, une transition
   dessus le ferait traîner derrière la souris.
   ======================================================================= */

.file-btn,
.analyze-btn,
.mode-toggle button,
.practice-toggle button,
.rs-edit,
.view-picker-btn,
.view-picker-chevron,
.view-menu button,
.supply-type,
.supply-radius button,
.supply-export button,
.supply-item,
.panel-nav button,
.panel-ticks button,
.leaflet-control-zoom a {
  transition: background-color var(--t-base) var(--ease),
              border-color var(--t-base) var(--ease),
              color var(--t-base) var(--ease),
              box-shadow var(--t-base) var(--ease),
              transform var(--t-fast) var(--ease);
}

/* Retour d'appui : l'élément s'enfonce légèrement sous le doigt ou le clic.
   Seulement sur les vraies commandes — pas sur les lignes de liste, où le
   fond qui s'allume suffit. */
.file-btn:active,
.analyze-btn:active,
.mode-toggle button:active,
.practice-toggle button:active,
.supply-radius button:active:not(:disabled),
.supply-export button:active:not(:disabled),
.panel-nav button:active { transform: scale(0.97); }

/* Le chevron pivote au lieu de changer de glyphe : un caractère qu'on
   remplace ne peut pas s'animer, et tous les jeux de caractères ne portent
   pas le triangle pointant vers le haut. */
.view-picker-btn[aria-expanded="true"] .view-picker-chevron { transform: rotate(180deg); }

@keyframes pop-in { from { opacity: 0; transform: translateY(-6px); } }
@keyframes fade-in { from { opacity: 0; } }
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, -8px); } }

/* Ces trois-là s'animent à l'apparition. Passer de `display: none` à visible
   relance l'animation : c'est ce qui la déclenche, sans une ligne de JS. */
.view-menu { animation: pop-in var(--t-base) var(--ease); }
main,
.dashboard { animation: fade-in var(--t-base) var(--ease); }

/* Focus visible au clavier. `:focus-visible` et non `:focus` : le contour ne
   doit apparaître que pour qui navigue au clavier, jamais après un clic. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Sur le bandeau vert foncé, le vert d'accent ne se détache pas. */
.bandeau :focus-visible,
body.settings-open .settings :focus-visible { outline-color: var(--band-mark); }
/* Le champ de fichier est transparent et couvre tout son libellé : c'est le
   libellé qui doit porter le contour, sinon il se dessine sur du vide. */
.file-btn:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
body.settings-open .file-btn:has(input:focus-visible) { outline-color: var(--band-mark); }

/* Réglage système « réduire les animations » : on le respecte. Des durées
   quasi nulles plutôt que `none`, pour que les événements de fin
   d'animation continuent d'être émis. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* =======================================================================
   1. En-tête : bandeau (parcours chargé) et accueil (parcours absent)
   ======================================================================= */

/* `body >` : sans lui la règle habillait aussi les <header> des cartes du
   tableau de bord, qui se retrouvaient en vert foncé sur fond blanc. */
body > header {
  position: relative;   /* repère du bandeau d'état, qui déborde par le bas */
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: var(--band);
  color: var(--band-fg);
}

.bandeau {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 20px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font: 600 15.5px var(--display);
  letter-spacing: -0.01em;
  white-space: nowrap;
}
/* 28 px : en dessous, la trame du dessin se referme et le cycliste n'est
   plus qu'une tache grise. C'est le plancher de lisibilité de cette gravure,
   mesuré, pas choisi. */
.brand-art {
  width: 41px;
  height: 28px;
  flex: none;
  background-color: var(--band-mark);
  -webkit-mask: url("logo.png") no-repeat center / contain;
  mask: url("logo.png") no-repeat center / contain;
}

/* Résumé du parcours : ce qui a été chargé et sous quelles hypothèses. Les
   réglages qui l'ont produit sont à un clic, repliés — on les consulte
   rarement, on les relit souvent. */
.route-summary {
  display: flex;
  align-items: center;
  gap: 11px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.rs-km { font: 500 13px var(--display); }
.rs-dim { font-size: 13px; color: var(--band-dim); }
.rs-rule { width: 1px; height: 13px; background: rgba(238, 240, 236, 0.2); }
.rs-edit {
  background: none;
  border: none;
  color: var(--band-mark);
  font: 500 12.5px var(--display);
  cursor: pointer;
  padding: 4px 0;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.rs-edit:hover { color: var(--band-fg); }

/* Bascule Carte / Analyse : changement de page, pas changement de calque —
   d'où la forme pleine, distincte du sélecteur de vue posé sur la carte. */
.mode-toggle {
  display: flex;
  background: rgba(238, 240, 236, 0.08);
  border-radius: 10px;
  padding: 3px;
  white-space: nowrap;
}
.mode-toggle button {
  border: none;
  cursor: pointer;
  padding: 6px 15px;
  border-radius: 8px;
  font: 500 12.5px var(--display);
  background: transparent;
  color: var(--band-dim);
}
.mode-toggle button:hover { color: var(--band-fg); }
.mode-toggle button.active { background: var(--band-fg); color: #232a24; }

/* ---- Accueil ----------------------------------------------------------
   Tant qu'aucun parcours n'est chargé, l'en-tête *est* la page. Le bandeau
   se replie, l'accueil se déplie, et les champs de réglage — les mêmes
   nœuds, jamais dupliqués — passent du tiroir à la carte blanche. */
.landing-hero,
.landing-foot { display: none; }

body.no-route > header {
  flex: 1;
  justify-content: center;
  align-items: center;
  padding: 40px 26px 44px;
  background: var(--paper);
  color: var(--ink);
  text-align: center;
}
body.no-route .bandeau { display: none; }
body.no-route .landing-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 340px;
}
body.no-route .landing-foot {
  display: block;
  max-width: 340px;
  margin: 20px 0 0;
  font: 400 12px/1.5 var(--ui);
  color: var(--faint);
}

/* Bouton secondaire, volontairement plus discret que « Charger un GPX » :
   c'est l'issue de secours de qui n'a pas de fichier, pas le chemin
   principal. Accueil seulement, comme le pied et les traces récentes. */
.example-btn { display: none; }
body.no-route .example-btn {
  display: block;
  width: 100%;
  background: none;
  border: 1px solid var(--line);
  border-radius: 11px;
  padding: 11px;
  cursor: pointer;
  font: 500 13px var(--display);
  color: var(--ink-2);
}
body.no-route .example-btn:hover { border-color: var(--muted-2); color: var(--ink); }

/* Même traitement : masqué hors accueil, et `.hidden` (posé par le script
   quand aucune URL n'est configurée) reste le plus fort, il est !important. */
.feedback-link { display: none; }
body.no-route .feedback-link {
  display: inline-block;
  margin-top: 10px;
  font: 400 12px var(--ui);
  color: var(--faint);
}
body.no-route .feedback-link:hover { color: var(--ink-2); }

/* ---- Accueil : reprise des traces récentes (recents.js) ----------------
   Bloc ajouté avec l'accord explicite de Pierre (2026-08-08), limité à cette
   carte. Même gabarit que la carte de réglages au-dessus — c'est sa petite
   sœur, elle doit se lire comme telle. Visible sur l'accueil seulement : le
   tiroir « Modifier » n'a pas à montrer la bibliothèque. `.hidden` (peint par
   le script quand le cache est vide) reste le plus fort, il est !important. */
.recents { display: none; }
body.no-route .recents {
  display: block;
  width: 100%;
  max-width: 340px;
  margin-top: 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 14px 18px;
}
.recents h2 {
  margin: 0 0 6px;
  font: 500 13px var(--display);
  color: var(--ink-2);
}
.recent-list { list-style: none; margin: 0; padding: 0; }
.recent-list li { display: flex; align-items: center; gap: 4px; }
.recent-load {
  flex: 1;
  min-width: 0; /* autorise l'ellipse du nom, sinon le li déborde */
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  background: none;
  border: none;
  padding: 7px 8px;
  border-radius: var(--r-sm);
  cursor: pointer;
  font: 14px var(--ui);
  color: var(--ink);
  text-align: left;
}
.recent-load:hover { background: #f6f7f3; }
.recent-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recent-load small { color: var(--faint); font-size: 12px; white-space: nowrap; }
.recent-del {
  background: none;
  border: none;
  color: var(--faint);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 5px 7px;
  border-radius: var(--r-sm);
}
.recent-del:hover { color: var(--ink); background: #f6f7f3; }

.landing-art {
  width: 100%;
  max-width: 260px;
  height: auto;
  margin-bottom: 12px;
}

body.no-route h1 {
  margin: 0 0 8px;
  font: 600 27px/1.15 var(--display);
  letter-spacing: -0.02em;
}
.tagline {
  margin: 0 0 24px;
  font: 400 14.5px/1.5 var(--ui);
  color: var(--muted);
}

/* Les deux zones de contenu sont vides tant qu'aucun GPX n'est lu : sans ça
   elles gardent leur part de la hauteur et l'accueil n'est plus centré. */
body.no-route main,
body.no-route .dashboard { display: none; }

/* ---- Réglages : carte à l'accueil, tiroir dans le bandeau -------------- */
.settings { display: none; }
body.no-route .settings,
body.settings-open .settings { display: flex; }

body.no-route .settings {
  width: 100%;
  max-width: 340px;
  flex-direction: column;
  gap: 14px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 18px;
}
body.settings-open .settings {
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 18px;
  padding: 0 20px 14px;
}

.file-btn {
  position: relative;
  display: block;
  text-align: center;
  background: var(--accent);
  color: #fff;
  border-radius: 11px;
  padding: 14px;
  cursor: pointer;
  font: 500 15px var(--display);
}
.file-btn:hover { background: var(--accent-ink); }
.file-btn input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
body.settings-open .file-btn { padding: 9px 16px; font-size: 13px; border-radius: var(--r-sm); }

/* Bouton de validation : même gabarit que « Charger un GPX » — c'est la paire
   choisir puis valider, les deux doivent peser pareil. Masqué tant qu'aucun
   fichier n'est choisi : valider quoi ? */
.analyze-btn {
  display: block;
  text-align: center;
  background: var(--accent);
  color: #fff;
  border: 0;
  border-radius: 11px;
  padding: 14px;
  cursor: pointer;
  font: 500 15px var(--display);
}
.analyze-btn:hover { background: var(--accent-ink); }
body.settings-open .analyze-btn { padding: 9px 16px; font-size: 13px; border-radius: var(--r-sm); }

.fields { display: flex; flex-wrap: wrap; gap: 9px; }
/* Étiquette sur sa propre ligne : « Départ » suivi de deux champs de saisie
   ne tient pas en largeur dans une carte de 340 px, et une date qui déborde
   de son cadre est pire qu'une ligne de plus. */
/* `text-align: left` : l'accueil centre tout son contenu, mais à l'intérieur
   d'un champ l'étiquette et la valeur doivent s'aligner l'une sous l'autre.
   Centrée, « ALLURE » flottait au-dessus d'un « 23 km/h » resté à gauche. */
.date-field {
  flex: 1 1 150px;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 6px;
  background: var(--sunk);
  border-radius: 10px;
  padding: 8px 12px;
  font: 500 13px var(--display);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: left;
}
.field-name {
  flex-basis: 100%;
  font: 500 10px var(--ui);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-2);
}
/* Encre fixée, et non héritée : le champ garde son fond blanc dans le tiroir
   du bandeau, où le conteneur passe en texte clair. Hérité, le contenu saisi
   devenait blanc sur blanc — date, heure et allure invisibles. */
.date-field input {
  min-width: 0;
  padding: 3px 6px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: #fff;
  font: inherit;
  color: var(--ink);
}
.date-field input[type="number"] { width: 56px; }
.date-field .unit { color: var(--muted-2); font-weight: 400; }

/* Sélecteur de pratique : trois pastilles dans le cadre d'un champ, une seule
   active. Même grammaire visuelle que `.date-field input`, pour qu'il se lise
   comme un réglage de calcul et non comme une navigation. */
.practice-field { flex-basis: 100%; }
.practice-toggle { display: flex; gap: 4px; }
.practice-toggle button {
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  font: inherit;
  color: var(--muted-2);
  cursor: pointer;
}
.practice-toggle button:hover { color: var(--ink); }
.practice-toggle button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
/* Dans le tiroir sombre, les mêmes champs repassent en clair sur le vert et
   reprennent une seule ligne : la place manque en hauteur, pas en largeur. */
body.settings-open .date-field {
  flex: 0 0 auto;
  background: rgba(238, 240, 236, 0.08);
  color: var(--band-fg);
}
body.settings-open .fields { flex: 0 1 auto; }
body.settings-open .field-name { flex-basis: auto; }
body.settings-open .field-name,
body.settings-open .date-field .unit { color: var(--band-dim); }
body.settings-open .practice-field { flex-basis: auto; }
body.settings-open .practice-toggle button {
  background: rgba(238, 240, 236, 0.10);
  border-color: rgba(238, 240, 236, 0.22);
  color: var(--band-dim);
}
body.settings-open .practice-toggle button:hover { color: var(--band-fg); }
body.settings-open .practice-toggle button.active {
  background: var(--band-fg);
  border-color: var(--band-fg);
  color: #232a24;
}

/* Bandeau d'état : messages de chargement et d'erreur. Posé sous l'en-tête
   plutôt qu'à l'intérieur — il apparaît et disparaît sans jamais décaler le
   bandeau ni recalculer la hauteur de la carte. */
.status {
  font: 400 12.5px var(--ui);
  color: var(--muted);
}
.status:empty { display: none; }
body:not(.no-route) .status {
  position: absolute;
  z-index: 1100;   /* au-dessus des commandes Leaflet, qui montent à 1000 */
  top: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%);
  max-width: min(560px, calc(100vw - 32px));
  background: #232a24;
  color: var(--band-fg);
  border-radius: 999px;
  padding: 7px 15px;
  box-shadow: var(--shadow-float);
  text-align: center;
  animation: toast-in var(--t-base) var(--ease);
}
body.no-route .status { margin-top: 16px; }

/* =======================================================================
   2. Mode Carte
   ======================================================================= */

main {
  flex: 1;
  min-height: 0;
  display: flex;
  background: var(--paper);
}

.map-col {
  position: relative;   /* repère du sélecteur de vue */
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
  border-right: 1px solid var(--line);
}

#map {
  flex: 1;
  min-height: 0;
  background: #e8e9e4;
}
.leaflet-container { font-family: var(--ui); }

/* ---- Sélecteur de vue posé sur la carte -------------------------------
   Verre dépoli : le fond de carte reste lisible dessous, et le sélecteur se
   lit comme un objet posé sur la carte plutôt que comme un morceau de
   l'interface qui la recouvre. */
.view-picker {
  position: absolute;
  z-index: 1000;   /* même plan que les commandes Leaflet, qu'il ne recouvre pas */
  top: 14px;
  left: 14px;
  width: 262px;
}
.view-picker-btn {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 9px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: var(--r-md);
  padding: 11px 13px;
  box-shadow: var(--shadow-float);
  font: 500 13.5px var(--display);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.view-picker-btn:hover {
  background: rgba(255, 255, 255, 0.88);
  box-shadow: 0 4px 16px rgba(30, 34, 30, 0.2);
}
.view-picker-chevron {
  margin-left: auto;
  color: var(--muted-2);
  font-size: 11px;
}
.view-picker-chevron::before { content: "\25BE"; display: block; line-height: 1; }

.view-dot {
  width: 9px;
  height: 9px;
  border-radius: 3px;
  flex: none;
  display: inline-block;
}

.view-menu {
  margin-top: 6px;
  background: rgba(255, 255, 255, 0.88);
  backdrop-filter: blur(20px) saturate(1.5);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 14px;
  box-shadow: var(--shadow-menu);
  padding: 8px 6px;
  max-height: min(430px, calc(100vh - 190px));
  overflow-y: auto;
}
.view-group {
  margin: 0;
  font: 500 10px var(--ui);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 11px 9px 6px;
}
.view-group:first-child { padding-top: 4px; }
.view-menu button {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 9px;
  border: none;
  cursor: pointer;
  padding: 8px 9px;
  border-radius: var(--r-sm);
  text-align: left;
  font: 400 13px var(--ui);
  color: var(--muted);
  background: transparent;
}
.view-menu button:hover { background: #f4f5f1; }
.view-menu button.active {
  background: #f0f2ed;
  color: var(--ink);
  font-weight: 500;
}
.view-menu button .view-dot { border-radius: 2px; width: 8px; height: 8px; }

/* Catégorie listée mais sans données : elle dit qu'elle existe et pourquoi
   elle est vide. Éteinte plutôt que retirée — une entrée qui disparaît d'un
   fichier à l'autre se lit comme une panne, pas comme une absence de données. */
.view-menu button:disabled {
  cursor: default;
  color: var(--faint);
}
.view-menu button:disabled .view-dot { background: var(--line) !important; }
.view-menu button:disabled:hover { background: none; }

/* ---- Commandes Leaflet, accordées au sélecteur ------------------------ */
.leaflet-touch .leaflet-bar,
.leaflet-bar {
  border: none;
  box-shadow: none;
}
.leaflet-control-zoom a,
.leaflet-touch .leaflet-control-zoom a {
  width: 30px;
  height: 30px;
  line-height: 30px;
  margin-bottom: 5px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  border: none;
  border-radius: var(--r-sm);
  color: #565c56;
  font-size: 17px;
  font-weight: 400;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.13);
}
/* Leaflet traite sa barre comme un bloc soudé : il arrondit à 2 px les coins
   *extérieurs* de la pile via `a:first-child` / `a:last-child`, et laisse les
   coins intérieurs carrés pour que les boutons se collent. Nos boutons sont
   séparés par 5 px de marge, donc chacun doit être une pastille entière — mais
   `.leaflet-touch .leaflet-bar a:first-child` pèse (0,3,1) et l'emportait sur
   notre rayon. Résultat exactement inverse de l'effet voulu : le « + » avait le
   haut plat et le bas arrondi, le « − » l'inverse, les angles vifs tournés vers
   l'extérieur. On repasse donc à la même spécificité, et plus bas dans la
   cascade. Mesuré avant : haut 2 px / bas 9 px sur le « + ». */
.leaflet-touch .leaflet-control-zoom a:first-child,
.leaflet-touch .leaflet-control-zoom a:last-child,
.leaflet-control-zoom a:first-child,
.leaflet-control-zoom a:last-child {
  border-radius: var(--r-sm);
}

.leaflet-control-zoom a:hover { background: rgba(255, 255, 255, 0.9); color: var(--ink); }

/* Bouton « ma position ». Même habillage que le zoom : il vit dans la même
   colonne et doit se lire comme une commande de carte, pas comme un réglage.
   Sélecteur doublé comme plus haut pour `.leaflet-bar` : Leaflet arrondit à
   2 px les coins extérieurs d'une barre via `a:first-child` / `a:last-child`,
   et ce bouton, seul enfant de la sienne, est les deux à la fois — il héritait
   donc des quatre coins carrés et jurait avec la pile de zoom juste au-dessus.
   La variante `.leaflet-touch` est nécessaire : elle porte une classe de plus
   et l'emporterait sur un sélecteur simple. */
.leaflet-touch .leaflet-bar.locate-control a,
.leaflet-bar.locate-control a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  border-radius: var(--r-sm);
  color: #565c56;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.13);
}
.locate-control a:hover { background: rgba(255, 255, 255, 0.9); color: var(--ink); }
.locate-control svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
}
/* Le disque central se remplit quand le suivi est actif : l'état se lit sans
   couleur, ce qui reste vrai pour qui la distingue mal. */
.locate-control a.active { color: #2a78d6; }
.locate-control a.active svg circle:first-child { fill: currentColor; }
/* Attente du premier point : l'anneau extérieur tourne. */
.locate-control a.pending svg .ring {
  transform-origin: 50% 50%;
  stroke-dasharray: 11 34;
  animation: locate-spin 1s linear infinite;
}
@keyframes locate-spin { to { transform: rotate(360deg); } }

/* Bouton « agrandir la carte ». Même habillage que les deux au-dessus : c'est
   une commande de carte, elle appartient à la même colonne. Sélecteur doublé
   pour la même raison que `.locate-control` — Leaflet arrondit les coins d'une
   barre via `a:first-child`, et ce bouton, seul enfant de la sienne, y perdrait
   ses angles. */
.leaflet-touch .leaflet-bar.expand-control a,
.leaflet-bar.expand-control a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  border-radius: var(--r-sm);
  color: #565c56;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.13);
}
.expand-control a:hover { background: rgba(255, 255, 255, 0.9); color: var(--ink); }
.expand-control svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Un seul des deux jeux de chevrons à la fois : vers les coins tant qu'on peut
   agrandir, vers le centre une fois la carte étendue. */
.expand-control svg .in { display: none; }
body.map-expanded .expand-control svg .out { display: none; }
body.map-expanded .expand-control svg .in { display: initial; }

/* Sélecteur de fond de carte. Même habillage que les commandes au-dessus :
   il vit dans la même colonne et doit se lire comme elles. Sélecteurs doublés
   `.leaflet-touch` pour la même raison que le zoom — la variante porte une
   classe de plus et l'emporterait. L'icône d'origine de Leaflet est un PNG
   gris chargé depuis leaflet.css : remplacée par un pictogramme posé en
   masque (même technique que la marque du bandeau), qui suit la couleur du
   texte comme les SVG des boutons voisins. */
.leaflet-touch .leaflet-control-layers,
.leaflet-control-layers {
  border: none;
  background: none;
  box-shadow: none;
}
.leaflet-touch .leaflet-control-layers-toggle,
.leaflet-control-layers-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  border-radius: var(--r-sm);
  color: #565c56;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.13);
  background-image: none;
}
.leaflet-control-layers-toggle:hover {
  background-color: rgba(255, 255, 255, 0.9);
  color: var(--ink);
}
.leaflet-control-layers-toggle::after {
  content: "";
  width: 17px;
  height: 17px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23000' stroke-width='1.7' stroke-linejoin='round'%3E%3Cpath d='M10 2.5 17 6.5 10 10.5 3 6.5Z'/%3E%3Cpath d='M3 10.5 10 14.5 17 10.5'/%3E%3Cpath d='M3 14.5 10 18.5 17 14.5'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23000' stroke-width='1.7' stroke-linejoin='round'%3E%3Cpath d='M10 2.5 17 6.5 10 10.5 3 6.5Z'/%3E%3Cpath d='M3 10.5 10 14.5 17 10.5'/%3E%3Cpath d='M3 14.5 10 18.5 17 14.5'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Panneau déplié : même carte translucide que les pastilles, liste au gabarit
   des menus de l'app, coches à la couleur d'accent (comme le ravitaillement). */
.leaflet-touch .leaflet-control-layers-expanded,
.leaflet-control-layers-expanded {
  background: rgba(255, 255, 255, 0.88);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  border-radius: var(--r-md);
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.13);
  padding: 9px 12px;
  color: var(--ink);
  font: 13px/1.35 var(--ui);
}
.leaflet-control-layers-base label {
  margin: 0;
  padding: 3px 0;
  cursor: pointer;
}
.leaflet-control-layers-base label:hover { color: var(--accent-ink); }
.leaflet-control-layers-selector {
  accent-color: var(--accent);
  margin-right: 7px;
  cursor: pointer;
}

/* Carte étendue : le panneau de droite et le profil d'altitude s'effacent. Le
   sélecteur de catégorie et la légende, eux, sont posés sur la carte et
   restent — sans quoi on ne saurait plus ce que montrent les couleurs.
   Cantonné à la mise en page bureau, et pas seulement parce que le téléphone a
   déjà sa poignée : là-bas le bouton qui rétablirait le panneau est masqué, et
   la barre de navigation vit dans ce panneau. Sans cette borne, rétrécir la
   fenêtre après avoir agrandi la carte laissait sans panneau, sans navigation
   et sans moyen de revenir — une impasse, constatée en test. */
@media (min-width: 769px) {
  body.map-expanded #stats,
  body.map-expanded .elevation-panel { display: none; }
}

.leaflet-control-attribution {
  background: rgba(255, 255, 255, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--muted);
  font-size: 10px;
  padding: 3px 7px;
}
.leaflet-control-attribution a { color: var(--muted); }

.legend {
  background: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 11px;
  padding: 10px 12px;
  box-shadow: 0 1px 6px rgba(30, 34, 30, 0.12);
  display: flex;
  flex-direction: column;
  gap: 6px;
  font: 400 11.5px var(--ui);
  color: #565c56;
}
.legend div { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.legend span.sw {
  width: 15px;
  height: 3px;
  border-radius: 2px;
  flex: none;
  display: inline-block;
}

/* ---- Profil d'altitude ------------------------------------------------ */
.elevation-panel {
  flex: none;
  height: 206px;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-top: 1px solid var(--line);
  padding: 12px 16px 8px;
}
.elevation-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 6px;
}
.elevation-title {
  font: 500 12px var(--display);
  letter-spacing: 0.02em;
}
.elevation-stat {
  font: 400 11.5px var(--ui);
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
}
.elevation-chart { flex: 1; min-height: 0; }

/* =======================================================================
   3. Panneau latéral
   ======================================================================= */

.stats {
  width: 300px;
  flex: none;
  padding: 16px 18px 22px;
  overflow-y: auto;
  background: var(--paper);
}

/* Navigation par sous-pages : sans objet sur grand écran, où le sélecteur
   posé sur la carte fait le travail. Réactivée par la requête média. */
/* Navigation par sous-pages et repli du panneau : propres à la mise en page
   mobile. Sur grand écran le panneau est une colonne à côté de la carte, il
   n'a rien à replier et les catégories se choisissent sur la carte. */
.panel-nav,
.panel-ticks,
.panel-handle { display: none; }

.stats h2 {
  margin: 0 0 14px;
  font: 600 15px var(--display);
  letter-spacing: -0.01em;
}

.stat-row {
  display: flex;
  align-items: center;
  gap: 9px;
  font: 400 12.5px var(--ui);
  color: var(--ink-2);
  margin-bottom: 8px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.stat-swatch {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  flex-shrink: 0;
}
.stat-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.stat-value { margin-left: auto; color: var(--muted); }

.total-row {
  display: flex;
  margin-top: 3px;
  padding-top: 9px;
  border-top: 1px solid var(--hair);
  font: 500 12.5px var(--display);
  font-variant-numeric: tabular-nums;
}
.total-row span { margin-left: auto; }

.note {
  margin: 14px 0 0;
  font: 400 11.5px/1.65 var(--ui);
  color: var(--muted-2);
  text-wrap: pretty;
}

.metric-chart {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--hair);
  height: 164px;
  flex-shrink: 0;
}

/* =======================================================================
   4. Graphiques SVG (charts.js)
   Le SVG est dessiné en pixels réels : ne pas lui imposer de largeur en CSS,
   il se dimensionne sur son conteneur et se re-dessine via ResizeObserver.
   ======================================================================= */

.chart-wrap { display: flex; flex-direction: column; height: 100%; }
.chart-svg-host { flex: 1; min-height: 0; overflow: hidden; }
.chart-caption-row {
  display: flex;
  justify-content: flex-end;
  font: 500 11.5px var(--display);
  color: #565c56;
  min-height: 14px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.chart-legend-row {
  display: flex;
  gap: 14px;
  justify-content: center;
  font: 400 10.5px var(--ui);
  color: var(--muted);
  padding-top: 3px;
}
.chart-legend-item { display: flex; align-items: center; gap: 5px; }
.chart-legend-item i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.chart-cursor line { stroke: #232a24; stroke-width: 1; }
.chart-grid { stroke: var(--hair); stroke-width: 1; }
.chart-ytick { font: 400 11px var(--display); fill: var(--faint); }
.chart-xtick { font: 400 11px var(--display); fill: var(--muted-2); }
.chart-zero { stroke: var(--faint); stroke-width: 1; stroke-dasharray: 4 4; }
.chart-area { opacity: 0.2; }
/* Profil d'altitude : la pente est portée par le contour, l'aire ne sert plus
   qu'à asseoir la silhouette. */
.chart-elevation-area { fill: #eef1ea; }
.chart-elevation-line {
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.chart-empty { font: 400 11px var(--ui); fill: var(--faint); }

/* =======================================================================
   5. Surcouches de la carte : vent, sens, ravitaillement
   ======================================================================= */

/* Les flèches de vent sont dessinées par Leaflet comme des icônes HTML ;
   la rotation est portée par le SVG lui-même. */
.wind-arrow { background: none; border: none; }
.wind-arrow svg {
  display: block;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}
/* Vitesse en clair sous la flèche : la taille donne l'impression d'ensemble,
   le chiffre lève l'ambiguïté. Halo blanc pour rester lisible sur la carte. */
.wind-arrow .wind-label {
  position: absolute;
  left: 50%;
  top: 100%;
  transform: translate(-50%, -1px);
  font: 500 11px/1 var(--display);
  color: #3d423c;
  white-space: nowrap;
  text-shadow: 0 0 3px #fff, 0 0 3px #fff, 0 0 2px #fff, 0 0 2px #fff;
}

/* Flèches de sens de parcours : posées sur le tracé, non interactives, plus
   discrètes que les flèches de vent pour ne pas rivaliser avec elles. */
.direction-arrow { background: none; border: none; pointer-events: none; }
.direction-arrow svg { display: block; opacity: 0.8; filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35)); }

.supply-marker { background: none; border: none; }
/* Pastille blanche cerclée de la couleur de la catégorie : les émojis ont
   leurs propres couleurs, une goutte bleue sur un disque bleu disparaîtrait.
   Le code couleur tient donc dans le cercle, l'icône dit le reste. */
.supply-marker span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2.5px solid;
  background: #fff;
  box-shadow: 0 1px 4px rgba(30, 34, 30, 0.35);
  font-size: 13px;
  line-height: 1;
}

/* =======================================================================
   6. Panneau Ravitaillement
   ======================================================================= */

/* Deux colonnes dès que la place le permet, une seule sinon : à 300 px de
   panneau, « Station-service » et « Réparateur vélo » passeraient à la ligne. */
/* Puces à largeur de contenu plutôt qu'une grille à colonnes fixes : les
   libellés ne sont plus tronqués, et une puce se lit comme un filtre qu'on
   allume, pas comme une ligne de formulaire. */
.supply-types {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}
/* Case à cocher présentée comme une puce : la case native reste dans le
   document — c'est elle qui porte l'état, le focus et le clavier — mais on
   coche en visant toute la puce, pas un carré de 13 px.
   L'état retenu était porté par un fond #f0f2ed contre #fff : deux gris
   voisins, impossible de dire d'un coup d'œil ce qui était sélectionné. Il est
   maintenant plein et contrasté d'un côté, franchement éteint de l'autre. */
.supply-type {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--line);
  background: #fff;
  border-radius: 999px;
  padding: 5px 11px 5px 6px;
  cursor: pointer;
  font: 500 12px var(--display);
  color: var(--muted-2);
  white-space: nowrap;
  user-select: none;
}
.supply-type:hover { border-color: var(--muted-2); color: var(--ink); }
.supply-type:has(input:checked) {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.supply-type:has(input:checked):hover {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
}
/* Type écarté : sa pastille de catégorie s'éteint avec le reste. Restée vive,
   elle continuait de dire « cette catégorie compte » sur une puce éteinte. */
.supply-type:not(:has(input:checked)) .supply-pin { opacity: 0.4; }
.supply-type:has(input:disabled) { opacity: 0.5; cursor: default; }
.supply-type:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.supply-type input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
/* Repli pour les navigateurs sans `:has` : la case reste visible, la puce
   reste utilisable — on perd l'habillage, jamais la fonction. */
@supports not selector(:has(*)) {
  .supply-type input { position: static; opacity: 1; width: auto; height: auto; }
}

/* Le rayon et le bouton vont ensemble : on règle, puis on cherche. */
.supply-radius {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
}
.supply-radius label {
  flex: 1;
  min-width: 120px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 7px 10px;
  font: 500 10px var(--ui);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted-2);
}
.supply-radius .supply-radius-value {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font: 500 13.5px var(--display);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.supply-radius input {
  width: 46px;
  border: none;
  padding: 0;
  background: none;
  font: inherit;
  color: inherit;
}
.supply-radius input:focus { outline: none; }
.supply-radius .unit { color: var(--muted-2); font: 400 12px var(--ui); }
.supply-radius button {
  border: none;
  border-radius: var(--r-sm);
  padding: 11px 14px;
  cursor: pointer;
  font: 500 12.5px var(--display);
  background: var(--accent);
  color: #fff;
  white-space: nowrap;
}
.supply-radius button:hover { background: var(--accent-ink); }
.supply-radius button:disabled { background: var(--line); color: var(--faint); cursor: default; }

.supply-msg {
  margin: 0 0 14px;
  font: 400 12.5px/1.6 var(--ui);
  color: var(--muted);
  text-wrap: pretty;
}

.supply-list { list-style: none; margin: 12px 0 0; padding: 0; }
.supply-item {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 8px;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.supply-item:hover { background: #f2f4f0; }
/* La même pastille sert partout — liste, décompte, filtres, légende — pour
   qu'une catégorie se reconnaisse d'un seul coup d'œil, où qu'elle apparaisse. */
.supply-pin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid;
  background: #fff;
  flex-shrink: 0;
  font-size: 10px;
  line-height: 1;
}
.supply-item .supply-pin { margin-top: 1px; }
.supply-type .supply-pin,
.stat-row .supply-pin,
.legend .supply-pin { width: 18px; height: 18px; font-size: 9px; }
.legend .supply-pin { margin-right: 0; }
.supply-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.supply-text strong { font: 500 12.5px var(--ui); }
.supply-text small { font: 400 11.5px var(--ui); color: var(--muted-2); font-variant-numeric: tabular-nums; }
.supply-text .supply-open { color: var(--accent); font-weight: 500; }

/* Sélection pour l'export GPX. La case est en tête de ligne, avant la pastille
   de catégorie : c'est la colonne qu'on parcourt du regard en cochant. */
.supply-pick {
  flex-shrink: 0;
  margin: 3px 0 0;
  accent-color: var(--accent);
  cursor: pointer;
}
/* Les deux boutons ne tiennent pas côte à côte dans le panneau de 280 px
   (« Tout décocher » + « Télécharger le GPX (24) » demandent 300 px pour 256
   disponibles) : ils s'empilent, chacun sur toute la largeur. */
.supply-export {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
.supply-export button { flex: 1 1 auto; }
.supply-export #supplyExport { flex-basis: 100%; }
.supply-export button {
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px 12px;
  cursor: pointer;
  font: 500 12px var(--display);
  background: #fff;
  color: var(--ink);
  white-space: nowrap;
}
.supply-export button:hover { background: #f2f4f0; }
.supply-export #supplyExport {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.supply-export #supplyExport:hover:not(:disabled) { background: var(--accent-ink); }
.supply-export #supplyExport:disabled {
  background: var(--line);
  border-color: var(--line);
  color: var(--faint);
  cursor: default;
}
/* Le volet carte fait 39 % de la hauteur d'écran en mise en page mobile, soit
   250 px sur un téléphone de 640 px. Une bulle plus haute que son volet fait
   valser la carte : Leaflet la déplace pour la faire tenir, n'y arrive pas, et
   on se retrouve à 500 m de là. Le corps est donc borné et défile — la borne
   suit la fenêtre pour les petits écrans, plafonnée sur les grands où le
   défilement n'apporterait rien. */
.supply-popup-body {
  max-height: min(26vh, 220px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Cocher depuis la bulle de la carte. Détaché du texte par un filet : c'est la
   seule ligne de la bulle qui agit, le reste ne fait que décrire. */
.supply-popup-pick {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  /* Le libellé entier est la cible : sur mobile la ligne ne faisait que 23 px
     de haut, court pour un pouce. Le rembourrage la porte à ~35 px sans
     alourdir la bulle. */
  padding: 9px 0 4px;
  border-top: 1px solid var(--hair);
  font: 500 12px var(--display);
  color: var(--ink);
  cursor: pointer;
}
.supply-popup-pick input { accent-color: var(--accent); cursor: pointer; margin: 0; }

/* Téléphone, site, avis. Des pastilles et non des liens en ligne : la bulle ne
   fait que 275 px, et trois liens soulignés à la suite y forment une bouillie.
   Chacune fait 28 px de haut, la cible minimale au pouce. */
.supply-links {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 7px;
}
.supply-links a {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 0 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font: 500 11.5px var(--display);
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.supply-links a:hover { background: #f2f4f0; border-color: var(--muted-2); }

/* Point retenu pour l'export : anneau sombre *autour* de la pastille, posé en
   ombre portée. La bordure garde la couleur de la catégorie — elle dit toujours
   *quoi*, l'anneau dit *retenu*. Passer la bordure au sombre l'aurait effacée,
   et il aurait fallu un `!important` pour battre le style en ligne. */
.supply-marker.picked span {
  box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--ink), 0 1px 4px rgba(30, 34, 30, 0.35);
}

/* =======================================================================
   6 bis. Photos du parcours (photos.js)
   ======================================================================= */

/* La vignette *est* le marqueur : une épingle générique n'aurait rien dit de
   plus qu'un point, alors que l'aperçu se lit d'un coup d'œil et donne déjà la
   réponse — bitume ou chemin, forêt ou plaine. Carré à coins arrondis pour ne
   pas être confondu avec les pastilles rondes du ravitaillement. */
.photo-marker { background: none; border: none; }
.photo-marker span {
  display: block;
  width: 26px;
  height: 26px;
  border-radius: 6px;
  border: 2px solid #fff;
  background: #d8d5de center / cover no-repeat;
  box-shadow: 0 1px 4px rgba(30, 34, 30, 0.4);
}
.photo-marker:hover span {
  transform: scale(1.15);
  box-shadow: 0 2px 8px rgba(30, 34, 30, 0.45);
}
.photo-marker span { transition: transform 120ms ease, box-shadow 120ms ease; }
@media (prefers-reduced-motion: reduce) {
  .photo-marker span { transition: none; }
}

/* Hauteur fixe et `cover` : les contributeurs versent du 4:3, du 16:9 et des
   panoramiques 2:1, et une bulle qui change de taille à chaque clic donne
   l'impression que la carte saute. */
/* Hauteur volontairement contenue. Mesuré : à 165 px l'image portait la bulle
   à 276 px, pour un volet de carte qui en fait 275 en mise en page étroite —
   autoPan ne pouvait donc jamais la caser et projetait la carte 500 m plus
   loin à chaque ouverture. 130 px laisse une marge et garde une vue lisible. */
.photo-popup img {
  display: block;
  width: 100%;
  height: 130px;
  object-fit: cover;
  border-radius: var(--r-sm);
  background: var(--line);
}
.photo-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 8px;
  font: 400 11.5px/1.5 var(--ui);
  color: var(--muted);
}
.photo-meta strong { font: 600 13px var(--ui); color: var(--ink); }
.photo-credit { color: var(--muted-2); }
.photo-meta a { color: var(--accent-ink); }

.photo-note {
  margin: 12px 0 0;
  font: 400 11.5px/1.55 var(--ui);
  color: var(--muted-2);
  text-wrap: pretty;
}

/* =======================================================================
   7. Page Analyse
   ======================================================================= */

.dashboard {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  background: var(--canvas);
}

.dash-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 30px 30px 44px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.dash-hero,
.dash-card,
.dash-section {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.dash-hero {
  display: flex;
  align-items: center;
  gap: 40px;
  padding: 32px 34px;
  border-radius: 18px;
  flex-wrap: wrap;
}
.hero-ring { flex: none; }
.hero-nodata {
  width: 148px;
  height: 148px;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 400 44px var(--display);
  color: #c9c9c3;
}
.hero-text { flex: 1; min-width: 260px; }
.hero-text h2 {
  margin: 0 0 8px;
  font: 500 11px var(--ui);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted-2);
}
.hero-verdict {
  margin-bottom: 10px;
  font: 600 28px var(--display);
  letter-spacing: -0.01em;
}
.hero-summary {
  margin: 0 0 14px;
  max-width: 520px;
  font: 400 14.5px/1.55 var(--ui);
  color: #565c56;
  text-wrap: pretty;
}
/* Renvoi vers la recherche de créneau, sous la note. Un lien souligné et non un
   bouton plein : c'est une porte de sortie offerte quand la note est basse, pas
   l'action principale de la page — elle ne doit pas concurrencer le verdict
   qu'elle suit de deux lignes. */
.hero-slot-hint {
  display: inline-block;
  margin: 0 0 14px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  font: 500 13px var(--ui);
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.hero-slot-hint:hover { color: var(--accent-ink); }

.dash-badge {
  display: inline-block;
  font: 500 12px var(--ui);
  color: var(--muted);
  background: var(--sunk);
  border-radius: 999px;
  padding: 6px 13px;
}
/* Les quatre chiffres du parcours : lus ensemble, jamais isolément — d'où le
   bloc à part, séparé du verdict par un filet plutôt que par du vide. */
/* `flex: none` empêchait ce bloc de se réduire : à 375 px il sortait de la
   carte blanche de 86 px. `0 1 auto` le laisse rétrécir sans l'autoriser à
   s'étirer — il garde donc sa largeur naturelle dès qu'il y a la place, et ne
   se comprime que lorsqu'il le faut. Et `minmax(0, 1fr)` autorise les colonnes
   à passer sous la largeur de leur contenu : sans ce zéro, une valeur en
   `nowrap` impose sa largeur à sa colonne et le débord revient par la fenêtre. */
.hero-figures {
  flex: 0 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px 30px;
  font-variant-numeric: tabular-nums;
}
/* Le filet ne sépare que si les deux blocs sont réellement côte à côte :
   dès que le bloc passe à la ligne, une bordure gauche ne sépare plus rien. */
@media (min-width: 1080px) {
  .hero-figures { border-left: 1px solid var(--hair); padding-left: 32px; }
}
.hero-figures div { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.hero-figures span {
  font: 500 10.5px var(--ui);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted-2);
}
.hero-figures strong { font: 600 20px var(--display); white-space: nowrap; }
/* Une donnée absente n'est pas un chiffre : elle garde la ligne mais pas le
   corps de 20 px, qui lui donnerait le poids d'une mesure. */
.hero-figures strong .hero-absent {
  font: 400 13px var(--ui);
  color: var(--muted-2);
  /* La capitale et l'interlettrage viennent du style des intitulés, hérités
     par le contenu : ils conviennent à « DÉNIVELÉ + », pas à une phrase. */
  text-transform: none;
  letter-spacing: 0;
}

.ring-value {
  font-family: var(--display);
  font-weight: 600;
  fill: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.ring-sub {
  font-family: var(--ui);
  font-size: 11px;
  font-weight: 500;
  fill: var(--muted-2);
}

.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(285px, 1fr));
  gap: 14px;
}
.dash-card {
  padding: 18px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.dash-card header {
  display: flex;
  align-items: center;
  gap: 12px;
}
.dash-card header > div { min-width: 0; }
.dash-card h3 {
  margin: 0;
  font: 600 14px var(--display);
  letter-spacing: -0.01em;
  display: inline;
}
.card-headline {
  margin: 3px 0 0;
  font: 400 12.5px/1.4 var(--ui);
  color: #565c56;
  text-wrap: pretty;
}
.card-weight {
  margin-left: 7px;
  font: 500 10px var(--ui);
  color: #7d847d;
  background: #f2f4f0;
  border-radius: 999px;
  padding: 2px 7px;
  white-space: nowrap;
}
.card-chart { height: 92px; }
.card-detail {
  margin: 0;
  font: 400 11.5px/1.55 var(--ui);
  color: var(--muted-2);
  text-wrap: pretty;
}
/* État dégradé : ambre, jamais rouge — c'est une donnée manquante, pas un
   danger sur le parcours. */
.dash-card-warn {
  background: #fdf6e8;
  border-color: #e6d3a8;
}
.dash-card-warn h3 { color: #7a5c14; }
.dash-card-warn .card-detail { color: #6f5a24; font-size: 12.5px; }

/* Carte Pneus : seule tuile avec ses propres entrées. */
.tire-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.tire-form label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: #f6f7f3;
  border-radius: var(--r-sm);
  padding: 7px 10px;
  font: 500 10px var(--ui);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-2);
}
.tire-input {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font: 500 13px var(--display);
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted-2);
}
.tire-input input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.tire-input input:focus { outline: none; }
.tire-form select {
  border: none;
  background: none;
  padding: 0;
  font: 500 13px var(--display);
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
}
.tire-pressure { display: flex; gap: 22px; font-variant-numeric: tabular-nums; }
.tire-pressure div { display: flex; flex-direction: column; gap: 2px; }
.tire-pressure span {
  font: 500 10.5px var(--ui);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted-2);
}
.tire-pressure strong { font: 600 26px var(--display); letter-spacing: -0.02em; }
.tire-pressure strong i { font: 400 13px var(--ui); color: var(--muted-2); font-style: normal; }
#tireOutput { display: flex; flex-direction: column; gap: 12px; }

/* Barre empilée : la part de chaque classe en distance. Plus rapide à comparer
   qu'une colonne de pourcentages. */
.dash-bar {
  display: flex;
  height: 16px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--hair);
}
.dash-bar span { display: block; height: 100%; }
.dash-keys { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-top: 10px; }
.dash-key {
  display: flex;
  align-items: center;
  gap: 5px;
  font: 400 11px var(--ui);
  color: var(--muted);
}
.dash-key i { width: 9px; height: 9px; border-radius: 2px; flex-shrink: 0; }
.dash-key b { color: var(--ink); font-variant-numeric: tabular-nums; }

.dash-section { padding: 20px 24px 22px; }
.dash-section-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.dash-section h3 {
  margin: 0;
  font: 600 15px var(--display);
  letter-spacing: -0.01em;
}
.dash-section-sub { font: 400 12px var(--ui); color: var(--muted-2); }
/* Onglets de coloration de la carte figée. Volontairement plus légers que la
   bascule Analyse / Carte : ils changent une lecture, pas de page. Ils défilent
   latéralement plutôt que de passer à la ligne — sept modules sur deux lignes
   repousseraient la carte hors de l'écran sur mobile. */
.dash-map-tabs {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.dash-map-tabs::-webkit-scrollbar { display: none; }
.dash-map-tabs button {
  flex: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 11px;
  cursor: pointer;
  font: 500 11.5px var(--display);
  background: #fff;
  color: var(--muted-2);
  white-space: nowrap;
}
.dash-map-tabs button:hover { color: var(--ink); border-color: var(--muted-2); }
.dash-map-tabs button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
#dashMapKeys:not(:empty) { margin-top: 12px; }

/* `cursor: pointer` : la carte est cliquable — elle ouvre la vue Carte sur la
   même coloration. Posée après leaflet.css, la règle l'emporte à spécificité
   égale sur le curseur par défaut du conteneur Leaflet. */
.dash-map { height: 390px; border-radius: var(--r-md); overflow: hidden; cursor: pointer; }
.dash-elevation { height: 224px; }
.dash-endpoint {
  background: #232a24;
  color: #fff;
  border: 2.5px solid #fff;
  border-radius: 50%;
  font: 600 10px/17px var(--display);
  text-align: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
/* Arrivée : damier au lieu d'une lettre. La pastille est centrée en flex —
   la hauteur de ligne qui cale le « D » ne cale pas un dessin. */
.dash-endpoint.finish {
  display: flex;
  align-items: center;
  justify-content: center;
}
.dash-endpoint.finish svg { width: 13px; height: 13px; overflow: visible; }
/* Hampe et contour du drapeau au trait, cases pleines : à 13 px, un damier
   entièrement rempli se lirait comme une tache. */
.dash-endpoint.finish svg path,
.dash-endpoint.finish svg .flag {
  fill: none;
  stroke: #fff;
  stroke-width: 1.5;
  stroke-linecap: round;
}
.dash-endpoint.finish svg .flag { stroke-width: 1.1; }
.dash-endpoint.finish svg .checks rect { fill: #fff; }

/* ---- Meilleur créneau -------------------------------------------------- */

.slot-msg {
  margin: 0 0 14px;
  font: 400 12.5px/1.6 var(--ui);
  color: var(--muted);
  text-wrap: pretty;
}
.slot-run {
  border: none;
  border-radius: var(--r-sm);
  padding: 11px 16px;
  cursor: pointer;
  font: 500 12.5px var(--display);
  background: var(--accent);
  color: #fff;
}
.slot-run:hover { background: var(--accent-ink); }

.slot-top { list-style: none; margin: 0 0 20px; padding: 0; display: grid; gap: 8px; }
.slot-top-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  cursor: pointer;
}
.slot-top-item:hover { background: #f6f7f3; border-color: var(--accent); }
.slot-top-item .ring { flex: none; }
.slot-top-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.slot-top-text strong { font: 500 13px var(--display); }
.slot-top-text small { font: 400 11.5px var(--ui); color: var(--muted-2); }
.slot-top-text .slot-why { text-wrap: pretty; }
.slot-pick {
  margin-left: auto;
  flex: none;
  align-self: center;
  font: 500 11.5px var(--display);
  color: var(--accent);
  white-space: nowrap;
}

/* Une bande par jour, une case par heure. Le bloc défile latéralement plutôt
   que de comprimer les cases sous le seuil du cliquable sur un écran étroit. */
.slot-bands { overflow-x: auto; }
.slot-row { display: flex; align-items: center; gap: 10px; margin-bottom: 3px; }
.slot-day {
  flex: none;
  width: 58px;
  font: 500 11px var(--ui);
  color: var(--muted-2);
  text-align: right;
  text-transform: capitalize;
}
.slot-cells {
  position: relative;
  display: flex;
  gap: 2px;
  flex: 1;
  min-width: 384px; /* 24 cases lisibles : en deçà, la bande défile */
}
.slot-cell {
  flex: 1;
  height: 20px;
  min-width: 0;
  padding: 0;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  background: var(--sunk);
}
.slot-cell:hover { outline: 2px solid var(--ink); outline-offset: 1px; }
/* Heure sans créneau : le modèle ne va pas jusque-là, ou la sortie déborderait
   de la fenêtre connue. Creux et non coloré — l'absence de donnée n'est pas
   une mauvaise note. */
.slot-cell-off { background: repeating-linear-gradient(45deg, #f4f5f1 0 3px, #eceee9 3px 6px); }
/* Créneau qui se termine après le coucher du soleil : visible mais barré, pour
   qu'on voie qu'il existe et pourquoi il n'est pas retenu. */
.slot-cell-night { opacity: 0.30; }
.slot-axis { margin-top: 4px; }
.slot-axis .slot-cells { height: 14px; }
/* Une case vide par heure, du même gabarit que les bandes : la graduation se
   centre sur sa case sans qu'aucun pourcentage n'ait à retrouver la largeur des
   cases ni les gouttières. Le sélecteur vise `i span` et non `span`, sinon il
   attrape aussi le libellé du jour et le sort du flux. */
.slot-axis i { position: relative; flex: 1; min-width: 0; }
.slot-axis i span {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  font: 400 10px var(--ui);
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
}
/* Première graduation posée contre le bord intérieur : centrée sur sa case,
   « 00h » déborderait du cadre par la gauche. */
.slot-axis i span.slot-tick-first { left: 0; transform: none; }
.slot-foot {
  margin: 14px 0 0;
  font: 400 11.5px/1.65 var(--ui);
  color: var(--muted-2);
  text-wrap: pretty;
}

/* Rubans : quatre lectures du parcours sur le même axe kilométrique. */
/* Le conteneur épouse exactement les quatre rubans — d'où la marge retirée au
   dernier : le curseur s'y cale en `top:0 / bottom:0` et couvre le dernier
   ruban jusqu'en bas. */
.ribbons { position: relative; }
.ribbons .ribbon-row:last-child { margin-bottom: 0; }
/* Un seul trait traverse les quatre rubans : c'est ce qui fait lire d'un coup
   d'œil « ici, revêtu, roulant, sec, mais vent de face ». */
.ribbon-cursor {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: #232a24;
  pointer-events: none;
  display: none;
}
.ribbon-cursor::before {
  content: "";
  position: absolute;
  left: -3.5px;
  top: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #232a24;
}
.ribbon-row { display: flex; align-items: center; gap: 12px; margin-bottom: 9px; }
.ribbon-label {
  width: 96px;
  flex-shrink: 0;
  font: 400 11.5px var(--ui);
  color: var(--muted);
  white-space: nowrap;
}
.ribbon {
  flex: 1;
  display: flex;
  height: 16px;
  border-radius: 3px;
  overflow: hidden;
  background: var(--hair);
}
.ribbon span { display: block; height: 100%; }
.ribbon-axis {
  flex: 1;
  display: flex;
  justify-content: space-between;
  font: 400 10px var(--display);
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.watchouts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.watchouts li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font: 400 13px/1.5 var(--ui);
}
.watchouts i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 5px;
}
.watch-when {
  width: 118px;
  flex-shrink: 0;
  font: 500 12px var(--display);
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.watch-what { color: #565c56; text-wrap: pretty; }
.dash-empty { margin: 0; font: 400 13px var(--ui); color: var(--muted); }

/* =======================================================================
   8. Mise en page mobile (mode Carte)
   Le panneau latéral de 300 px et le sélecteur de vue posé sur la carte ne
   tiennent pas sur un écran de téléphone. On empile : carte pleine largeur
   en haut, à hauteur fixe pour qu'elle reste toujours entièrement visible,
   et dessous un panneau qui défile seul, une catégorie à la fois. Le mode
   Analyse est déjà responsive, il n'est pas concerné.
   ======================================================================= */

@media (max-width: 768px) {
  /* Deux lignes exactement : marque et mode d'un côté, résumé du parcours
     sur toute la largeur en dessous. Laissé au seul `flex-wrap`, le résumé
     s'intercalait entre les deux et en faisait trois. */
  .bandeau { flex-wrap: wrap; gap: 6px 12px; padding: 10px 14px; }
  .brand { order: 0; font-size: 14px; }
  .mode-toggle { order: 1; }
  .route-summary { order: 2; flex-basis: 100%; gap: 8px; }
  .rs-km { font-size: 12.5px; }
  .rs-dim { font-size: 11.5px; }
  .rs-edit { margin-left: auto; }
  body.settings-open .settings { padding: 0 14px 12px; gap: 10px; }
  .fields { flex: 1; flex-wrap: wrap; }

  /* Les catégories se parcourent dans le panneau, plus sur la carte : le
     sélecteur flottant couvrirait le tiers d'un écran de téléphone. */
  .view-picker { display: none; }
  /* La poignée du panneau fait déjà agrandir la carte, et elle le fait mieux :
     elle laisse la barre de navigation, sans laquelle on ne pourrait plus
     changer de catégorie ici. Deux commandes pour le même geste, dont une
     qui enferme, ne valent pas mieux qu'une seule. */
  .expand-control { display: none; }

  main { flex-direction: column; }
  /* Base fixe, ni compressible ni extensible : c'est ce qui garantit que faire
     défiler le panneau ne fait jamais bouger la carte. */
  .map-col { flex: 0 0 45%; min-height: 210px; border-right: none; }

  /* Panneau replié : le panneau se réduit à ce que mesurent la poignée et la
     barre de navigation, et la carte prend tout le reste. C'est le flex qui
     fait la soustraction, pas nous : une hauteur écrite à la main laissait
     25 px de blanc sous la barre, et se serait démentie au premier changement
     de rembourrage ou de taille de police. */
  .panel-handle {
    display: block;
    width: 100%;
    flex-shrink: 0;
    padding: 9px 0 5px;
    border: none;
    background: var(--paper);
    cursor: pointer;
  }
  .panel-handle i {
    display: block;
    width: 38px;
    height: 4px;
    margin: 0 auto;
    border-radius: 2px;
    background: var(--line-2);
  }
  .panel-handle:active i { background: var(--muted-2); }

  body.panel-collapsed .map-col { flex: 1 1 auto; }
  body.panel-collapsed .stats { flex: 0 0 auto; }
  body.panel-collapsed .panel-ticks,
  body.panel-collapsed .stats-scroll { display: none; }
  /* Sans les repères, la barre de navigation perd la bordure qui la séparait
     du contenu : elle la reprend à son compte. */
  body.panel-collapsed .panel-nav {
    padding-bottom: 10px;
    border-bottom: 1px solid var(--line);
  }

  .stats {
    width: 100%;
    flex: 1;
    min-height: 0;
    padding: 0;
    overflow: hidden;      /* le défilement passe à .stats-scroll */
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--line);
  }
  .panel-nav {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    padding: 10px 10px 0;
    background: var(--paper);
  }
  .panel-nav button {
    width: 44px;
    height: 44px;
    flex: none;
    border: none;
    background: #f0f2ed;
    border-radius: 50%;
    cursor: pointer;
    color: #3a453b;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
  }
  .panel-nav button svg { width: 16px; height: 16px; }
  .panel-nav button:active { background: #e4e8e1; }
  .panel-nav-text {
    flex: 1;
    min-width: 0;
    height: 44px;
    display: flex;
    align-items: center;
    gap: 9px;
    background: #fff;
    border: 1px solid #eaece7;
    border-radius: 22px;
    padding: 0 15px;
    font: 500 14.5px var(--display);
    letter-spacing: -0.01em;
  }
  .panel-nav-text > span:first-of-type {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .panel-pos {
    margin-left: auto;
    font: 400 11.5px var(--display);
    color: var(--faint);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  /* Repères de position : huit traits qui disent où l'on est dans la série,
     et qui s'atteignent d'un doigt sans passer par les flèches. */
  .panel-ticks {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
    padding: 9px 12px;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
  }
  .panel-ticks button {
    flex: 1;
    height: 3px;
    padding: 0;
    border: none;
    border-radius: 2px;
    background: var(--line-2);
    cursor: pointer;
  }

  .stats-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 15px 16px 24px;
  }
  .stats h2 { font-size: 14px; }
  .stat-row { font-size: 13px; }

  /* Dans le panneau, le profil n'a plus de bordure haute à porter : il n'est
     plus collé sous la carte. */
  .elevation-panel { height: 224px; padding: 0; border-top: none; }

  /* La légende posée sur la carte mangeait un bon quart de l'écran alors que le
     tableau juste en dessous porte déjà les mêmes pastilles de couleur. */
  .legend { display: none; }

  /* Accueil sur téléphone : mêmes proportions, en plus resserré. */
  body.no-route > header { padding: 32px 22px 36px; }
  body.no-route h1 { font-size: 25px; }
  .landing-art { max-width: 220px; }

  .dash-inner { padding: 18px 14px 36px; }
  /* Sous ~516 px, l'anneau ne tient plus sur la même ligne que le texte et se
     retrouve seul : sans ce centrage il restait collé à gauche, ce qui se
     lisait comme un défaut d'alignement. Cantonné à la requête mobile — plus
     haut, le bloc de texte occupe la ligne et le centrage n'aurait aucun sens. */
  .dash-hero { gap: 22px; padding: 22px; justify-content: center; }
  /* Deux colonnes tiennent encore à 375 px, à condition de resserrer la
     gouttière et de descendre les chiffres d'un cran. Une seule colonne
     ferait une carte deux fois plus haute pour la même information. */
  /* Pleine largeur sur mobile : le bloc y est seul sur sa ligne, et centré il
     flottait sous un texte aligné à gauche. Étalé, ses deux colonnes partent
     du même bord que tout le reste de la carte. */
  .hero-figures { border-left: none; padding-left: 0; gap: 14px 16px; flex: 1 1 100%; }
  .hero-figures strong { font-size: 18px; }
  /* Plus assez de place pour une colonne de repères alignés : le « quand »
     reprend sa largeur naturelle et le texte suit à la ligne. */
  .watch-when { width: auto; }
}

/* En dessous de ~470 px, une demi-largeur ne tient plus « 23:41 → 03:49 (+1 j) »
   — 171 px de texte pour 139 px de colonne. Une seule colonne plutôt que de
   faire déborder la valeur, de la couper au milieu d'une flèche, ou de l'étaler
   sur deux colonnes : ce dernier choix laissait un trou dans la grille, quatre
   chiffres dont un large ne se répartissant pas sur deux colonnes sans reste.
   La carte y gagne deux lignes de hauteur, et ne dépend plus de la longueur
   du texte. */
@media (max-width: 470px) {
  .hero-figures { grid-template-columns: 1fr; }
}
