/*
 * Sillage : une seule feuille, écrite à la main. L'interface est en noir, blanc et gris : la couleur est réservée
 * aux coureurs (--athlete-1 à 8), pour qu'on les repère du premier coup sur la carte et les courbes, à la pente
 * sur le profil d'un segment (--grade-*), et aux deux maillots d'une sortie à plusieurs (--jersey-*).
 * Classes du système : .btn, .card, .field, .input, .chip, .dot, .table, .stat, .seg, .chart, .runner.
 */

:root {
  --bg: #f4f4f1;
  --surface: #fcfcfb;
  --surface-2: #eeede8;
  --line: #e1e0d9;
  --line-strong: #c3c2b7;
  --text: #0b0b0b;
  --muted: #52514e;
  --faint: #898781;
  --primary: #0f1114;
  --primary-text: #ffffff;
  --danger: #b3261e;
  --danger-soft: #fdecea;
  --ok: #006300;
  --ok-soft: #e6f4e6;
  --shadow: 0 1px 2px rgb(11 11 11 / 5%), 0 6px 20px rgb(11 11 11 / 4%);
  --radius: 12px;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;

  /* Les huit couleurs de coureur, dans un ordre fixe, et l'encre lisible posée dessus. */
  --athlete-1: #2a78d6; --athlete-1-ink: #ffffff;
  --athlete-2: #eb6834; --athlete-2-ink: #0b0b0b;
  --athlete-3: #1baf7a; --athlete-3-ink: #0b0b0b;
  --athlete-4: #eda100; --athlete-4-ink: #0b0b0b;
  --athlete-5: #e87ba4; --athlete-5-ink: #0b0b0b;
  --athlete-6: #008300; --athlete-6-ink: #ffffff;
  --athlete-7: #4a3aa7; --athlete-7-ink: #ffffff;
  --athlete-8: #e34948; --athlete-8-ink: #ffffff;
  /* Les mêmes dans la maquette, qui a son jour et sa nuit à elle, quel que soit le thème de l'écran
     (lib/theme.js, `athleteColor`) : celles du thème clair le jour, celles du thème sombre la nuit. */
  --athlete-1-day: #2a78d6; --athlete-1-night: #3987e5;
  --athlete-2-day: #eb6834; --athlete-2-night: #d95926;
  --athlete-3-day: #1baf7a; --athlete-3-night: #199e70;
  --athlete-4-day: #eda100; --athlete-4-night: #c98500;
  --athlete-5-day: #e87ba4; --athlete-5-night: #d55181;
  --athlete-6-day: #008300; --athlete-6-night: #008300;
  --athlete-7-day: #4a3aa7; --athlete-7-night: #9085e9;
  --athlete-8-day: #e34948; --athlete-8-night: #e66767;

  /* La pente, sur le profil d'un segment (lib/grade.js). Les montées ont les couleurs des sites de profils de
     cols (l'échelle de Climbfinder, relevée le 2026-10-12) : du jaune pâle au rouge sombre puis au presque noir,
     un palier à 1, 2, 4, 6, 8, 10, 12, 15 et 20 %. Le plat est gris ; les descentes en bleu, un palier à 2, 5, 8
     et 11 %. Les mêmes en thème clair et en thème sombre : c'est un code, il ne change pas avec l'écran. Ne pas
     les redéfinir dans le bloc du thème sombre. */
  --grade-flat: #e2e1dc;
  --grade-up-1: #fed976; --grade-up-2: #feb24c; --grade-up-3: #fd8d3c; --grade-up-4: #fc4e2a; --grade-up-5: #e31a1c;
  --grade-up-6: #bd0026; --grade-up-7: #98001f; --grade-up-8: #4f0017; --grade-up-9: #24000a;
  --grade-down-1: #86b6ef; --grade-down-2: #5598e7; --grade-down-3: #256abf; --grade-down-4: #104281;

  /* Les deux maillots d'une sortie à plusieurs (`jersey`, dans OutingsHelper) : le jaune, et le blanc à pois
     rouges. Comme la pente, c'est un code : les mêmes en thème clair et en thème sombre, à ne pas redéfinir
     dans le bloc du thème sombre. */
  --jersey-yellow: #ffd91a; --jersey-white: #ffffff; --jersey-dots: #e0261c;
}

/* Le thème sombre. Il s'applique quand le système le demande et que l'on n'a pas choisi le clair sur cet appareil,
   ou quand on a choisi le sombre (`data-theme`, posé sur `body` par le gabarit d'après `ThemesController`). Les
   couleurs sont donc sur `body`, pas sur `:root`, et c'est là que `lib/theme.js` les lit. Les deux blocs disent
   la même chose, mot pour mot : toucher à l'un, c'est toucher à l'autre (un test le vérifie). */
@media (prefers-color-scheme: dark) {
  body:not([data-theme="light"]) {
    --bg: #0d0d0d;
    --surface: #1a1a19;
    --surface-2: #262624;
    --line: #2c2c2a;
    --line-strong: #383835;
    --text: #ffffff;
    --muted: #c3c2b7;
    --faint: #898781;
    --primary: #f1efea;
    --primary-text: #131311;
    --danger: #f2877f;
    --danger-soft: #3a1b18;
    --ok: #0ca30c;
    --ok-soft: #132a13;
    --shadow: 0 1px 2px rgb(0 0 0 / 40%);

    --athlete-1: #3987e5; --athlete-1-ink: #ffffff;
    --athlete-2: #d95926; --athlete-2-ink: #ffffff;
    --athlete-3: #199e70; --athlete-3-ink: #0b0b0b;
    --athlete-4: #c98500; --athlete-4-ink: #0b0b0b;
    --athlete-5: #d55181; --athlete-5-ink: #ffffff;
    --athlete-6: #008300; --athlete-6-ink: #ffffff;
    --athlete-7: #9085e9; --athlete-7-ink: #0b0b0b;
    --athlete-8: #e66767; --athlete-8-ink: #0b0b0b;
  }
}
body[data-theme="dark"] {
  --bg: #0d0d0d;
  --surface: #1a1a19;
  --surface-2: #262624;
  --line: #2c2c2a;
  --line-strong: #383835;
  --text: #ffffff;
  --muted: #c3c2b7;
  --faint: #898781;
  --primary: #f1efea;
  --primary-text: #131311;
  --danger: #f2877f;
  --danger-soft: #3a1b18;
  --ok: #0ca30c;
  --ok-soft: #132a13;
  --shadow: 0 1px 2px rgb(0 0 0 / 40%);

  --athlete-1: #3987e5; --athlete-1-ink: #ffffff;
  --athlete-2: #d95926; --athlete-2-ink: #ffffff;
  --athlete-3: #199e70; --athlete-3-ink: #0b0b0b;
  --athlete-4: #c98500; --athlete-4-ink: #0b0b0b;
  --athlete-5: #d55181; --athlete-5-ink: #ffffff;
  --athlete-6: #008300; --athlete-6-ink: #ffffff;
  --athlete-7: #9085e9; --athlete-7-ink: #0b0b0b;
  --athlete-8: #e66767; --athlete-8-ink: #0b0b0b;
}
/* Les champs, les listes et les barres de défilement du navigateur suivent le thème choisi. */
body[data-theme="dark"] { color-scheme: dark; }
body[data-theme="light"] { color-scheme: light; }

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 var(--sans);
}

h1, h2, h3, p, figure { margin: 0; }
h1 { font-size: 1.65rem; line-height: 1.2; letter-spacing: -0.02em; }
h2 { font-size: 1.08rem; line-height: 1.3; letter-spacing: -0.01em; }
h3 { font-size: 0.95rem; line-height: 1.3; }
a { color: inherit; }
[hidden] { display: none !important; }

.muted { color: var(--muted); }
.faint { color: var(--faint); }
.num { font-variant-numeric: tabular-nums; }
.icon { flex: none; }
.nowrap { white-space: nowrap; }

:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; border-radius: 6px; }

.skip { position: absolute; left: -999px; }
.skip:focus { left: 12px; top: 12px; z-index: 2000; background: var(--surface); padding: 8px 12px; }

/* --- En-tête ------------------------------------------------------------ */

.topbar { background: var(--surface); border-bottom: 1px solid var(--line); }
.topbar__inner {
  max-width: 1160px;
  margin: 0 auto;
  padding: 10px 20px;
  display: flex;
  align-items: center;
  gap: 8px 20px;
  flex-wrap: wrap;
}
.topbar__actions { margin-left: auto; display: flex; gap: 8px; }
.topbar form { margin: 0; }

.brand { display: inline-flex; align-items: center; gap: 9px; font-weight: 700; text-decoration: none; letter-spacing: -0.02em; font-size: 1.05rem; }
.brand__mark {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--primary);
  color: var(--primary-text);
}

.nav { display: flex; gap: 2px; margin-right: auto; flex-wrap: wrap; }

/* Sur un téléphone, le site se parcourt comme les applications : les cinq mêmes onglets au pied de l'écran, à portée
   de pouce (`tab_link`). Le bandeau, qui passait sur trois lignes, n'y garde que le nom du site ; « Compte » mène
   aux autres rubriques et à la déconnexion. `--foot` dit à ce qui se colle en bas la place que la barre occupe. */
:root { --foot: 0px; }
.tabbar { display: none; }
@media (max-width: 640px) {
  .topbar .nav, .topbar__inner > form { display: none; }
  body:has(> .tabbar) { --foot: calc(59px + env(safe-area-inset-bottom)); }
  body:has(> .tabbar) .container { padding-bottom: calc(var(--foot) + 28px); }
  .tabbar { position: fixed; inset: auto 0 0; z-index: 900; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); padding: 4px 6px calc(4px + env(safe-area-inset-bottom)); border-top: 1px solid var(--line); background: var(--surface); }
  .tabbar__link { display: grid; justify-items: center; align-content: center; gap: 3px; min-height: 50px; border-radius: 10px; color: var(--muted); font-size: 0.7rem; font-weight: 550; text-decoration: none; -webkit-tap-highlight-color: transparent; }
  .tabbar__link:active { background: var(--surface-2); }
  .tabbar__link.is-current { color: var(--text); font-weight: 700; }
  .tabbar__link.is-current .icon { stroke-width: 2.5; }
  .is-riding .tabbar { display: none; } /* une séance en plein écran prend toute la fenêtre */
}
.nav__link { padding: 6px 11px; border-radius: 7px; text-decoration: none; color: var(--muted); font-weight: 500; }
.nav__link:hover, .nav__link.is-current { color: var(--text); background: var(--surface-2); }

/* Dans l'application (dossier `ios/`) : la barre de navigation porte le titre, la page commence plus haut. */
.is-native .container { padding-top: 16px; }
.is-native h1.is-titled, .is-native .crumb { display: none; } /* le retour est le bouton de la barre */
/* Un bouton que la barre de navigation de l'application a repris (`bar`) ne se montre plus dans la page. */
.is-native [data-bar], .is-native form:has(> [data-bar]) { display: none; }

/* La page « Compte » y donne en liste les rubriques que le bandeau propose sur le site. */
.menu { display: grid; overflow: hidden; }
.menu__item { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; text-decoration: none; font-weight: 500; }
.menu__item + .menu__item { border-top: 1px solid var(--line); }
.menu__item:hover { background: var(--surface-2); }
.menu__item::after { content: "›"; color: var(--faint); font-size: 1.2rem; line-height: 1; }
/* Une rubrique : son pictogramme, ce qu'elle est, ce qu'elle vaut (un nombre, un choix), et le chevron. */
.menu__icon { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px; background: var(--surface-2); flex: none; }
.menu__item:hover .menu__icon { background: var(--surface); }
.menu__text { flex: 1; display: grid; gap: 2px; min-width: 0; }
.menu__hint { color: var(--muted); font-size: 0.88rem; font-weight: 400; }
.menu__value { color: var(--muted); font-size: 0.92rem; font-weight: 400; white-space: nowrap; }
/* Qui est connecté : la pastille du coureur, son nom. */
.menu__item--me { padding-top: 16px; padding-bottom: 16px; gap: 14px; }
.menu__item--me .profile__face { width: 48px; height: 48px; font-size: 1.1rem; }
.menu__item--me strong { font-size: 1.1rem; letter-spacing: -0.01em; overflow-wrap: anywhere; }
@media (max-width: 640px) { .signout { width: 100%; } }
/* Un réglage sur une ligne : ce qu'il vaut, et de quoi le changer, replié dessous. */
.setting + .setting { border-top: 1px solid var(--line); }
/* Une liste de réglages dont certains sont dans un cadre Turbo (le profil) : le filet se met entre ses lignes, cadre ou non. */
.settings > turbo-frame { display: block; }
.settings > * + * { border-top: 1px solid var(--line); }
/* Une ligne sans rien à déplier (un bouton suffit) garde son bouton en face du texte, comme les autres. */
.settings .setting__row { flex-wrap: nowrap; }
.settings .setting__row .setting__what { flex: 1; }
.settings .setting__row > .btn, .settings .setting__row > form { flex: none; }
.setting__value .chip { color: var(--text); }
.setting > summary, .setting__row { display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; padding: 14px 20px; }
.setting > summary { list-style: none; cursor: pointer; }
.setting > summary::-webkit-details-marker { display: none; }
.setting__row { flex-wrap: wrap; }
.setting__row form { margin: 0; }
.setting__what { display: grid; gap: 2px; min-width: 0; flex: 1 1 260px; }
.setting > summary .setting__what { flex: 1; }
.setting__value { color: var(--muted); font-size: 0.92rem; overflow-wrap: anywhere; }
.setting[open] .setting__edit, .setting:not([open]) .setting__close { display: none; }
.setting__body { padding: 4px 20px 18px; }
/* Sous un formulaire replié, ce qui en tient lieu quand on ne peut pas le remplir (le mot de passe oublié). */
.setting__aside { display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; flex-wrap: wrap; padding-top: 14px; border-top: 1px solid var(--line); }
.setting__aside form { margin: 0; }
@media (max-width: 640px) { .setting .input { font-size: 16px; } } /* en deçà, iOS grossit la page au toucher */

/* --- Page ---------------------------------------------------------------- */

.container { width: 100%; max-width: 1160px; margin: 0 auto; padding: 28px 20px 56px; flex: 1; }
.container.narrow { max-width: 460px; }
.container.entry { max-width: 940px; } /* la porte d'entrée : connexion, inscription (`.auth`) */
.container.medium { max-width: 720px; }

.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 14px 20px; flex-wrap: wrap; margin-bottom: 22px; }
.page-head p { margin-top: 4px; }
.page-head__actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.page-head__actions form { margin: 0; }

.crumb { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); text-decoration: none; font-size: 0.9rem; margin-bottom: 10px; }
.crumb:hover { color: var(--text); }
/* Le retour à l'écran « Compte » : sur un téléphone seulement, où le bandeau ne montre plus les rubriques. */
.crumb--narrow { display: none; }
@media (max-width: 640px) { .crumb--narrow { display: inline-flex; } }

.stack { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
.stack--lg { gap: 24px; }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.section-title { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }

.flash { display: flex; gap: 8px; align-items: flex-start; padding: 10px 14px; border-radius: var(--radius); margin-bottom: 18px; }
.flash .icon { margin-top: 3px; }
.flash--notice { background: var(--ok-soft); color: var(--text); }
.flash--notice .icon { color: var(--ok); }
.flash--alert { background: var(--danger-soft); color: var(--text); border-left: 3px solid var(--danger); }
/* Dans une carte qui vient de se recharger seule : le message de l'action, au-dessus de ce qu'elle a changé. */
.card__body > .flash { margin-bottom: 14px; }
.stack > .flash, .card__note > .flash { margin-bottom: 0; } /* `.card__note` : un corps de carte fait de ce seul message */
/* Le temps qu'une carte se recharge (une carte dans son cadre ; pas un cadre qui se charge à l'ouverture). */
turbo-frame[busy] > .card { opacity: 0.6; }

/* --- Boutons ------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-weight: 550;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { background: var(--surface-2); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn--primary { background: var(--primary); border-color: var(--primary); color: var(--primary-text); }
.btn--primary:hover { background: var(--primary); opacity: 0.88; }
.btn--quiet { border-color: transparent; background: transparent; color: var(--muted); }
.btn--quiet:hover { color: var(--text); }
.btn--danger { color: var(--danger); }
.btn--small { padding: 5px 10px; font-size: 0.9rem; }
.btn--wide { width: 100%; }
.btn--icon { padding: 8px; }

/* --- Cartes, formulaires ------------------------------------------------- */

.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); }
.card--pad { padding: 18px 20px; }
.card__head { padding: 14px 20px; border-bottom: 1px solid var(--line); display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.card__body { padding: 16px 20px; }
/* Le pied d'une carte : ce qui suit sa liste, sous un filet (une remarque, de quoi y ajouter). */
.card__foot { margin: 0; padding: 12px 20px; border-top: 1px solid var(--line); }
.card__foot .feed__row { padding: 7px 0; align-items: center; }
.card__foot .feed__row form { margin: 0; flex: none; }
.card__foot > p { margin-top: 8px; }

.field { display: grid; gap: 5px; }
fieldset.field { border: 0; padding: 0; margin: 0; min-width: 0; }
fieldset.field legend { padding: 0; margin-bottom: 6px; }
.field__label { font-weight: 550; font-size: 0.92rem; }
.field__hint { color: var(--muted); font-size: 0.88rem; }
.field__error { color: var(--danger); font-size: 0.88rem; }
.fields { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.input, .select {
  width: 100%;
  padding: 8px 11px;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
.select { appearance: none; padding-right: 30px; background-image: linear-gradient(45deg, transparent 50%, var(--faint) 50%), linear-gradient(135deg, var(--faint) 50%, transparent 50%); background-position: calc(100% - 15px) 50%, calc(100% - 10px) 50%; background-size: 5px 5px; background-repeat: no-repeat; }
.input:focus-visible, .select:focus-visible { outline-offset: 0; }
/* Un choix parmi des cases à cocher (la qualité des vues, dans les réglages d'affichage) : à leur taille. */
.select--small { width: auto; padding: 3px 28px 3px 9px; font-size: 0.9rem; }

.copy { display: flex; gap: 8px; }
.copy .input { font-variant-numeric: tabular-nums; min-width: 0; }
/* Un lien à copier ou à partager : sur un téléphone, le lien sur une ligne, ses deux boutons dessous. */
@media (max-width: 640px) {
  .copy--share { flex-wrap: wrap; }
  .copy--share .input { flex: 1 1 100%; }
  .copy--share .btn { flex: 1; }
}
/* Un coureur face à ses propres temps : une ligne par coureur, ses comparaisons toutes prêtes. */
.versus { border-top: 1px solid var(--line); display: grid; gap: 4px; }
.versus__list { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 8px; }
.versus__row { display: flex; align-items: center; justify-content: space-between; gap: 8px 16px; flex-wrap: wrap; }
.versus__actions { display: inline-flex; gap: 6px; flex-wrap: wrap; }
/* Le choix de l'espace (un groupe, ou les segments personnels) où vont les segments créés d'un clic. */
.space-switch { display: flex; align-items: center; gap: 8px 10px; flex-wrap: wrap; margin: 0 0 14px; color: var(--muted); font-size: 0.92rem; }
.space-switch .select { width: auto; padding-top: 5px; padding-bottom: 5px; }
.card__head .space-switch { margin: 8px 0 0; }
.space-tabs { margin-bottom: 16px; }
.chips { display: flex; gap: 4px 12px; flex-wrap: wrap; }
.share { display: grid; gap: 10px; }
.share__bar { display: flex; align-items: center; gap: 10px 14px; flex-wrap: wrap; }
.share__bar .copy { flex: 1; min-width: min(100%, 280px); }
.share__bar form { margin: 0; }
.share__title { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.share__cell { min-width: 260px; }

/* Connexion et inscription : la porte d'entrée du site. Le formulaire, et ce que Sillage fait (`.auth__pitch`) :
   dessous sur un téléphone, où qui revient se connecte d'abord ; à gauche du formulaire sur un écran large. */
.auth { display: grid; gap: 32px 64px; align-items: start; }
.auth__form { width: 100%; max-width: 420px; margin: 0 auto; }
.auth__brand { display: flex; justify-content: center; margin: 4px 0 8px; font-size: 1.2rem; }
.auth__pitch { display: grid; gap: 10px; justify-items: center; text-align: center; width: 100%; max-width: 420px; margin: 0 auto; padding-top: 28px; border-top: 1px solid var(--line); }
.auth__tagline { font-size: 1.3rem; letter-spacing: -0.02em; text-wrap: balance; }
.auth__points { list-style: none; margin: 8px 0 4px; padding: 0; display: grid; gap: 10px; text-align: left; }
.auth__points li { display: flex; align-items: center; gap: 12px; }
.auth__note { font-size: 0.9rem; }
@media (min-width: 900px) {
  .auth { grid-template-columns: minmax(0, 1fr) minmax(0, 420px); padding-top: 32px; }
  .auth__form { margin: 0; }
  .auth__pitch { order: -1; justify-items: start; text-align: left; max-width: 440px; margin: 0; padding-top: 14px; border-top: 0; }
  .auth__pitch .welcome__art { max-width: 380px; margin-bottom: 10px; }
  .auth__tagline { font-size: 1.6rem; }
}
/* Un mot de passe que l'on peut montrer : le bouton est posé dans le champ, à droite. */
.peek { position: relative; }
.peek .input { padding-right: 88px; }
.peek__button { position: absolute; top: 50%; right: 5px; transform: translateY(-50%); padding: 5px 9px; border: 0; border-radius: 7px; background: transparent; color: var(--muted); font: inherit; font-size: 0.88rem; font-weight: 550; cursor: pointer; }
.peek__button:hover { color: var(--text); background: var(--surface-2); }
@media (max-width: 640px) { .auth .input { font-size: 16px; } } /* en deçà, iOS grossit la page au toucher */
.auth-head { text-align: center; margin: 12px 0 22px; }
.auth-head p { color: var(--muted); margin-top: 6px; }
.auth-links { text-align: center; margin-top: 16px; color: var(--muted); }
/* Le choix de la langue, sous un formulaire d'entrée ou au pied d'une course partagée : deux mots, celui en vigueur
   en gras. */
.languages { display: flex; align-items: center; justify-content: center; gap: 4px; margin-top: 16px; color: var(--muted); font-size: 0.88rem; }
.languages__choice { padding: 6px 8px; border: 0; border-radius: 6px; background: none; color: inherit; font: inherit; cursor: pointer; }
.languages__choice:hover { color: var(--text); background: var(--surface-2); }
.languages__choice[aria-pressed="true"] { color: var(--text); font-weight: 600; }
.fine-print__links { margin-top: 4px; text-align: center; color: var(--muted); font-size: 0.82rem; }
.fine-print__links a { color: inherit; }
/* Les pages légales : des listes à puces aérées, et les coordonnées en regard de leur intitulé. */
.legal__points { display: grid; gap: 8px; padding-left: 1.2em; }
.legal__facts { display: grid; gap: 10px; margin: 0; }
.legal__facts > div { display: grid; grid-template-columns: minmax(9rem, 14rem) 1fr; gap: 12px; }
.legal__facts dt { color: var(--muted); }
.legal__facts dd { margin: 0; }
@media (max-width: 640px) { .legal__facts > div { grid-template-columns: 1fr; gap: 2px; } }
/* « ou », entre le bouton Google et le formulaire : un mot entre deux filets. */
.auth-or { display: flex; align-items: center; gap: 12px; margin: 14px 0; color: var(--muted); font-size: 0.88rem; }
.auth-or::before, .auth-or::after { content: ""; flex: 1; border-top: 1px solid var(--line); }

/* --- Pastilles, vignettes -------------------------------------------------- */

.dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex: none; background: var(--faint); }
.chip { display: inline-flex; align-items: center; gap: 7px; font-weight: 550; }
.badge { display: inline-block; padding: 1px 8px; border-radius: 999px; background: var(--surface-2); color: var(--muted); font-size: 0.8rem; font-weight: 550; white-space: nowrap; }

.dot--1, .lane--1 { background: var(--athlete-1); color: var(--athlete-1-ink); }
.dot--2, .lane--2 { background: var(--athlete-2); color: var(--athlete-2-ink); }
.dot--3, .lane--3 { background: var(--athlete-3); color: var(--athlete-3-ink); }
.dot--4, .lane--4 { background: var(--athlete-4); color: var(--athlete-4-ink); }
.dot--5, .lane--5 { background: var(--athlete-5); color: var(--athlete-5-ink); }
.dot--6, .lane--6 { background: var(--athlete-6); color: var(--athlete-6-ink); }
.dot--7, .lane--7 { background: var(--athlete-7); color: var(--athlete-7-ink); }
.dot--8, .lane--8 { background: var(--athlete-8); color: var(--athlete-8-ink); }

.ink--1 { color: var(--athlete-1); } .ink--2 { color: var(--athlete-2); } .ink--3 { color: var(--athlete-3); }
.ink--4 { color: var(--athlete-4); } .ink--5 { color: var(--athlete-5); } .ink--6 { color: var(--athlete-6); }
.ink--7 { color: var(--athlete-7); } .ink--8 { color: var(--athlete-8); }

/* Numéro de couloir : la couleur ne porte jamais seule l'identité d'un coureur. */
.lane { display: inline-grid; place-items: center; min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px; font-size: 0.76rem; font-weight: 700; flex: none; font-variant-numeric: tabular-nums; }

.shape { display: block; flex: none; }
.shape__line { fill: none; stroke: var(--text); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.shape__start { fill: var(--surface); stroke: var(--text); stroke-width: 2; }

/* --- Listes -------------------------------------------------------------- */

.grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }

.grid--wide { grid-template-columns: repeat(auto-fill, minmax(min(100%, 380px), 1fr)); }

/* Un segment proposé : sa carte, ce qu'il mesure, qui l'a parcouru, et de quoi le créer. */
.proposal { overflow: hidden; display: flex; flex-direction: column; }
.proposal .map { border: 0; border-bottom: 1px solid var(--line); border-radius: 0; }
.proposal__body { padding: 14px 16px 14px; display: grid; gap: 8px; align-content: start; flex: 1; }
.proposal__name { font-size: 1.05rem; letter-spacing: -0.01em; }
.proposal .facts { margin: 0; }
.proposal__who { font-size: 0.9rem; }
.proposal__who .sport { display: inline-flex; vertical-align: middle; }
/* Le nom proposé convient le plus souvent : de quoi en choisir un autre est replié. */
.proposal__form { display: grid; gap: 5px; width: 100%; }
.proposal__form details.more { margin-top: 0; }
.proposal__form details.more[open] { display: grid; gap: 5px; }
.proposal__actions { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; margin-top: auto; padding-top: 2px; }
.proposal__actions form { margin: 0; }
/* À la place d'une proposition créée ou écartée : ce qui vient de se passer, en deux lignes. */
.proposal--done { flex-direction: row; align-items: center; justify-content: space-between; gap: 10px 14px; flex-wrap: wrap; padding: 14px 16px; background: var(--surface-2); box-shadow: none; }
.proposal__done { display: flex; align-items: flex-start; gap: 8px; min-width: 0; flex: 1 1 220px; }
.proposal__done .icon { margin-top: 3px; color: var(--ok); flex: none; }
.proposal--done .proposal__actions { margin-top: 0; padding-top: 0; }
/* Chaque proposition est dans son cadre : c'est lui la case de la grille, et la carte en prend la hauteur. */
.grid > turbo-frame { display: grid; min-width: 0; }
/* La recherche en cours : la page est là, la liste arrive. */
.searching progress { width: min(240px, 100%); height: 6px; accent-color: var(--primary); }
turbo-frame[busy] > .card.searching { opacity: 1; }
@media (max-width: 640px) {
  /* Sur un téléphone, neuf cartes se suivent : la carte de chacune est plus basse, et sans boutons de zoom
     (deux doigts suffisent). */
  .proposal .map--small { height: 160px; }
  .proposal .leaflet-control-zoom { display: none; }
}
.count { display: inline-grid; place-items: center; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px; background: var(--primary); color: var(--primary-text); font-size: 0.78rem; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Une carte de liste (un segment, un segment à rouler) : la forme du parcours sur sa vignette, le nom, ses chiffres
   clés sous leur intitulé (`facts`), puis qui est passé. La carte entière se touche ; le chevron le dit. */
.seg { display: flex; gap: 14px; align-items: center; padding: 14px 16px; text-decoration: none; -webkit-tap-highlight-color: transparent; }
.seg:hover { border-color: var(--line-strong); }
.seg:active { background: var(--surface-2); }
.seg__shape { flex: none; align-self: flex-start; display: grid; place-items: center; width: 72px; height: 72px; border-radius: 10px; background: var(--surface-2); }
.seg__body { flex: 1; min-width: 0; display: grid; gap: 3px; }
.seg__name { font-weight: 650; line-height: 1.3; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.seg__meta { color: var(--muted); font-size: 0.88rem; }
.seg__foot { display: flex; align-items: center; gap: 6px 10px; flex-wrap: wrap; color: var(--muted); font-size: 0.86rem; }
.seg__foot .bubbles { margin-top: 0; }
.seg__go { color: var(--faint); }
/* Où l'on en est soi-même sur le segment. */
.seg__you { color: var(--muted); font-size: 0.86rem; }
.seg__you strong { color: var(--text); font-weight: 600; }
/* Les derniers passages, à côté d'une liste : qui, où, en combien, quand. */
.feed__who { display: grid; gap: 1px; min-width: 0; }
.feed__who .chip { font-weight: 600; }
.feed__who .badge { margin-left: 4px; }
.feed__where { color: var(--muted); font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed__row .feed__where a { font-weight: 400; color: inherit; }
/* Le profil d'un segment en silhouette (« Rouler ») : la ligne de crête sur son aplat. */
.relief { display: block; flex: none; }
.relief__area { fill: var(--line-strong); opacity: 0.55; }
.relief__line { fill: none; stroke: var(--text); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
/* Le segment de la dernière séance, en tête de « Rouler » : toute la largeur, son profil en grand, et le bouton
   que l'on vient chercher. */
.seg--lead { grid-column: 1 / -1; border-color: var(--line-strong); }
.seg--lead .seg__shape { width: 216px; height: 116px; }
.seg--lead .seg__shape .relief { width: 100%; height: 100%; }
.seg--lead .seg__body { gap: 5px; }
.seg--lead .seg__name { font-size: 1.15rem; }
.seg--lead .facts { max-width: 420px; }
.seg--lead .seg__cta { padding: 8px 18px; font-size: 0.95rem; }
/* Une liste et ce qui l'accompagne (« Rouler » : les segments et les dernières séances ; « Segments » : la liste
   et les derniers passages), puis des explications : côte à côte sur un grand écran, à la suite sur un téléphone. */
.duo { display: grid; gap: 24px; }
.duo > * { min-width: 0; }
.duo__notes, .duo__notes .more { margin-top: 0; }
@media (min-width: 960px) {
  .duo--split { grid-template-columns: minmax(0, 1fr) 340px; grid-template-areas: "main side" "notes side"; align-items: start; }
  .duo--split .duo__main { grid-area: main; }
  .duo--split .duo__side { grid-area: side; }
  .duo--split .duo__notes { grid-area: notes; }
  /* La page d'un groupe : ce qu'il partage, et à côté les derniers passages puis les membres. Dans la page, les
     derniers passages viennent d'abord : c'est l'ordre d'un téléphone. */
  .duo--news { grid-template-columns: minmax(0, 1fr) 340px; grid-template-areas: "main news" "main side"; grid-template-rows: auto 1fr; align-items: start; }
  .duo--news > .duo__main { grid-area: main; }
  .duo--news > .duo__news { grid-area: news; }
  .duo--news > .duo__side { grid-area: side; }
}
/* Face à qui l'on roulera : leurs pastilles dans la phrase. */
.seg__rivals { display: flex; align-items: center; gap: 3px 7px; flex-wrap: wrap; }
.seg__rivals .bubbles { margin-top: 0; }
.seg__kicker { color: var(--muted); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
@media (max-width: 640px) {
  /* Sur un téléphone : le profil sur toute la largeur de la carte, puis le reste dessous. */
  .seg--lead { flex-direction: column; align-items: stretch; }
  .seg--lead .seg__shape { width: 100%; height: 76px; }
  .seg--lead .seg__cta { flex: 1 1 100%; justify-content: center; margin-left: 0; padding: 10px 18px; }
}
.seg__cta { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; padding: 5px 12px; border-radius: 999px; background: var(--primary); color: var(--primary-text); font-size: 0.88rem; font-weight: 600; }
.facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2px 12px; margin: 4px 0 3px; }
.fact { display: grid; min-width: 0; }
.fact dt { color: var(--muted); font-size: 0.74rem; }
.fact dd { margin: 0; font-weight: 600; white-space: nowrap; }
/* La liste des segments : liste ou carte, et dans quel ordre. Sur un téléphone, sur une même ligne. */
.list-tools { display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; flex-wrap: wrap; margin-bottom: 14px; }
.list-tools .tabs { margin: 0; }
.list-tools__sort { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 0.9rem; }
.list-tools__sort .select { width: auto; }
/* La carte des segments : un tracé par segment ; celui que l'on touche passe devant, sa bulle mène à sa page. */
.segments-map { margin-bottom: 18px; }
.segments-map__card { display: grid; gap: 3px; min-width: 160px; font: inherit; }
.segments-map__card a { margin-top: 4px; color: var(--text); font-weight: 600; }
/* La bulle prend les couleurs du thème : Leaflet la peint en blanc, où le texte du thème sombre ne se lirait pas. */
.segments-map .leaflet-popup-content-wrapper, .segments-map .leaflet-popup-tip { background: var(--surface); color: var(--text); }
/* Chercher dans la liste (contrôleur `search`). */
.search { display: flex; align-items: center; gap: 8px; max-width: 420px; margin-bottom: 14px; padding: 0 12px; border: 1px solid var(--line-strong); border-radius: 10px; background: var(--surface); color: var(--muted); }
.search:focus-within { outline: 2px solid var(--text); outline-offset: 1px; }
.search__input { flex: 1; min-width: 0; min-height: 40px; border: 0; background: transparent; color: var(--text); font: inherit; outline: none; }
@media (max-width: 640px) { .search { max-width: none; } .search__input { font-size: 16px; } } /* en deçà, iOS grossit la page au toucher */
/* Les bulles : qui est passé sur le segment. Une pastille par coureur, à sa couleur, avec ses initiales ; elles se
   chevauchent un peu, cernées de la couleur de la carte pour rester lisibles. */
.bubbles { display: flex; margin-top: 4px; padding-left: 4px; }
.bubble { display: inline-grid; place-items: center; width: 26px; height: 26px; margin-left: -4px; border-radius: 50%; box-shadow: 0 0 0 2px var(--surface); font-size: 0.68rem; font-weight: 700; letter-spacing: -0.01em; flex: none; }
.bubble--more { background: var(--surface-2); color: var(--muted); font-variant-numeric: tabular-nums; }

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.stat { background: var(--surface); padding: 12px 16px; display: grid; gap: 2px; align-content: start; } /* une tuile sans note ne s'étire pas à la hauteur de sa voisine */
.stat__label { color: var(--muted); font-size: 0.82rem; }
.stat__value { font-size: 1.25rem; font-weight: 650; letter-spacing: -0.01em; }
.stat__note { color: var(--muted); font-size: 0.82rem; }
/* Quatre chiffres : en ligne sur un écran large, deux par deux sur un écran étroit, jamais trois et un orphelin. */
.stats--quad { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 760px) { .stats--quad { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.stat__note a { color: inherit; }
/* Une tuile qui mène, dans la page, à ce qu'elle compte. */
a.stat { color: inherit; text-decoration: none; }
a.stat:hover .stat__label, a.stat:focus-visible .stat__label { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
/* Sur un téléphone, deux par ligne ; une dernière tuile seule prend toute la largeur, plutôt qu'une case vide. */
@media (max-width: 640px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stats > .stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
  /* Trois chiffres courts restent sur une ligne. */
  .stats--trio { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .stats--trio > .stat:last-child:nth-child(odd) { grid-column: auto; }
  .stats--trio .stat { padding: 10px 12px; }
}

/* Le tableau de bord. */
.dash-tabs { margin-bottom: 20px; }
/* Administration : les activités arrivées jour après jour. */
.daybars { display: block; width: 100%; }
.daybars__bar { fill: var(--text); }
.daybars__bar--none { fill: var(--line-strong); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.feed { list-style: none; margin: 0; padding: 0; }
.feed__row { display: flex; justify-content: space-between; gap: 14px; padding: 10px 20px; }
.feed__row + .feed__row { border-top: 1px solid var(--line); }
.feed__row a { text-decoration: none; font-weight: 550; }
.feed__row a:hover { text-decoration: underline; }
/* Une ligne qui dit quoi (`feed__what` : un nom, puis ce qu'il faut en savoir) et porte son geste à droite. */
.feed__what { display: grid; gap: 2px; min-width: 0; }
.feed__row:has(> .feed__what) { align-items: center; }
.feed__value { text-align: right; white-space: nowrap; }
.spark { display: block; }
.spark__line { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.spark__end { fill: currentColor; stroke: var(--surface); stroke-width: 2; }
/* Le calendrier de la régularité : une case par jour, d'autant plus pleine que la journée a été longue. */
.heat { display: block; width: 100%; max-width: 440px; height: auto; }
.heat__day { fill: currentColor; }
.heat__day--0 { fill: var(--surface-2); }
.heat__day--1 { opacity: 0.3; }
.heat__day--2 { opacity: 0.55; }
.heat__day--3 { opacity: 0.8; }
.heat__month { fill: var(--muted); font-size: 9px; }
.heat__key { font-size: 0.86rem; }
.stats--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.gauge { display: block; }
.gauge__track { fill: var(--surface-2); }
.gauge__fill { fill: currentColor; }
/* La même jauge sur toute la largeur de son bloc (`meter`) : ce qui est fait d'une quantité prévue. */
svg.meter { display: block; width: 100%; height: 8px; border-radius: 4px; overflow: hidden; }
/* Une évolution précédée de sa flèche (`trend`). */
.trend { display: inline-flex; align-items: center; gap: 3px; }
/* « Cette semaine », en tête du résumé : ce qui est fait du volume prévu, puis les séances de la semaine. */
.thisweek__progress { display: grid; gap: 6px; }
.thisweek__done { font-size: 1.25rem; letter-spacing: -0.01em; }
.thisweek .sessions--compact .session:first-child { padding-top: 0; }
.thisweek .sessions--compact .session:last-child { padding-bottom: 0; }
/* La semaine jour par jour : sept colonnes, le jour, une pastille à la couleur du coureur quand il est sorti, la
   durée écrite dessous. Aujourd'hui en gras ; les jours à venir, en pointillé. */
.week-days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; max-width: 520px; margin: 0; padding: 0; list-style: none; }
.week-days__day { display: grid; justify-items: center; gap: 5px; padding: 6px 2px; border-radius: 8px; text-align: center; }
.week-days__day.is-today { background: var(--surface-2); }
.week-days__day.is-today .week-days__name { color: var(--text); font-weight: 700; }
.week-days__name { color: var(--muted); font-size: 0.78rem; text-transform: capitalize; }
.week-days__day .dot { width: 14px; height: 14px; }
.week-days__none { background: none; box-shadow: inset 0 0 0 1.5px var(--line-strong); }
.week-days__day.is-ahead .week-days__none { box-shadow: none; border: 1.5px dashed var(--line); }
.week-days__time { min-height: 1.2em; font-size: 0.8rem; font-weight: 600; white-space: nowrap; }
/* Les derniers records, de toutes sortes : ce qu'ils mesurent en petit au-dessus. */
.feats__kind { font-size: 0.74rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.key--target { background: var(--text); }
.callout { padding: 12px 16px; border-radius: var(--radius); background: var(--surface-2); border-left: 3px solid var(--line-strong); }
/* Ce qui demande un coup d'œil (l'administration) : le filet le dit, les mots aussi. */
.callout--warn { border-left-color: var(--danger); }
/* Les altitudes d'un segment, au pied de son profil : le doute, puis leur correction d'après le relief des cartes. */
.heights__doubt { margin-top: 14px; }
.heights__doubt p { margin: 0; }
.card--pad > .heights { margin: 16px -20px -18px; border-top: 1px solid var(--line); }
.callout .icon { vertical-align: -2px; margin-right: 4px; }
.outing__you { font-weight: 550; }
/* Les réglages de l'installation : le nom en petit, ce qu'il vaut, ce que cela change. */
.settings-list { margin: 0; }
.settings-list__item { padding: 12px 20px; display: grid; gap: 2px; }
.settings-list__item + .settings-list__item { border-top: 1px solid var(--line); }
.settings-list dt { color: var(--muted); font-size: 0.82rem; overflow-wrap: anywhere; }
.settings-list dd { margin: 0; }
.settings-list dd strong { font-weight: 600; }
/* Le champ de recherche d'une liste rangée dans une carte : sans marge sous lui, le tableau suit. */
.card__search { padding-bottom: 4px; }
.card__search .search { margin-bottom: 0; }
.sessions { list-style: none; margin: 0; padding: 0; counter-reset: session; }
.session { counter-increment: session; display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 2px 12px; padding: 12px 20px; }
.session + .session { border-top: 1px solid var(--line); }
.session::before { content: counter(session); grid-row: span 2; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--surface-2); font-weight: 700; font-size: 0.82rem; }
.session--quality::before, .session--race::before { background: var(--primary); color: var(--primary-text); }
.session__title { font-weight: 650; }
.session__detail { grid-column: 2; color: var(--muted); font-size: 0.92rem; }
.sessions--compact .session { padding: 8px 0; }
.session__ride { grid-column: 2; justify-self: start; font-size: 0.9rem; }
/* Les défis de la semaine des groupes, en tête de la liste des segments : une tuile par groupe. */
.challenges { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; margin-bottom: 18px; }
.challenge-tile { display: grid; gap: 3px; padding: 12px 16px; color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }
.challenge h3 { margin-top: 6px; }
/* Le défi sur la page du groupe : les trois premiers, et soi-même plus loin ; le reste est sur la page du défi. */
.challenge__top { margin: 0; }
.challenge__top .feed__row { align-items: center; padding-block: 8px; }
.challenge__top .is-me { background: var(--surface-2); }
.challenge__rank { min-width: 1.4em; color: var(--muted); font-weight: 600; }
.challenge .card__foot { display: flex; justify-content: space-between; align-items: baseline; gap: 4px 14px; flex-wrap: wrap; }
.challenge .card__foot a { font-weight: 550; }
/* La page du défi : où l'on en est, les défis d'avant et qui les a gagnés. */
.challenge__you { font-weight: 550; }
.challenge__wins { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin-top: 4px; }
.challenge__missing { margin: 0 2px; font-size: 0.92rem; }
#defis .feed__row.is-current { background: var(--surface-2); }
#defis .feed__where a[aria-current] { text-decoration: none; }
/* La salle d'attente d'un rendez-vous en direct : qui vient, et s'il est prêt. */
.crew { list-style: none; margin: 10px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px 18px; }
.crew li { display: flex; align-items: center; gap: 8px; }
.crew__state { font-size: 0.88rem; }
/* Une séance guidée dans une liste (« Séances guidées ») : son dessin, son nom, ce qu'elle demande. */
.workout { display: flex; flex-direction: column; gap: 3px; padding: 14px 16px; color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }
.workout__art { display: block; height: 56px; margin-bottom: 8px; }
.workout-art { display: block; width: 100%; height: 100%; }
.workout-art__blocks { fill: var(--line-strong); }
.workout-art__ftp { stroke: var(--text); stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.workout__name { font-weight: 650; }
.workout__summary { font-size: 0.92rem; }
/* Le seuil de l'entraînement, en une ligne : sa valeur, d'où elle vient, et le test qui la mesure. */
.threshold { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; }
.threshold__line { margin: 0; }
details.more.threshold__more { flex-basis: 100%; margin-top: 0; }
.workout .seg__cta { align-self: flex-start; margin: 8px 0 0; }
.week > summary { cursor: pointer; }
.week[open] > summary { margin-bottom: 4px; font-weight: 550; }
.plan tr.is-current td { background: var(--surface-2); }
/* Sur un téléphone, une semaine du plan tient en trois lignes : son numéro et sa phase, le prévu et le fait, puis
   ses séances à déplier. */
@media (max-width: 700px) {
  /* (`.table--stack.plan` : ces règles passent avant celles, plus bas, de tous les tableaux qui s'empilent.) */
  .table--stack.plan tr { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 3px 14px; }
  .table--stack.plan tr.is-current { background: var(--surface-2); }
  .table--stack.plan td.plan__week { grid-column: 1; }
  .table--stack.plan td.plan__phase { grid-column: 2; display: block; justify-self: end; color: var(--muted); font-weight: 400; }
  .table--stack.plan td.plan__phase::before { content: none; }
  .table--stack.plan td.plan__amount { display: block; font-weight: 400; }
  .table--stack.plan td.plan__amount::before { content: attr(data-label) " "; font-size: inherit; }
  .table--stack.plan td.plan__amount + td.plan__amount { justify-self: end; }
  .table--stack.plan td.plan__sessions { grid-column: 1 / -1; }
}
/* Sur un téléphone, un record tient en peu de lignes : ce qu'il mesure et sa valeur face à face, sa jauge dessous,
   puis quand et pendant quelle sortie, en petit. */
@media (max-width: 700px) {
  .table--stack.records tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 3px 12px; }
  .table--stack.records td:first-child { grid-column: 1; }
  .table--stack.records td:nth-child(2) { grid-column: 2; grid-row: 1; display: block; font-size: 1.05rem; text-align: right; }
  .table--stack.records td:nth-child(2)::before { content: none; }
  .table--stack.records td:nth-child(n + 3) { grid-column: 1 / -1; }
  .table--stack.records td[data-label]:nth-child(n + 3) { display: block; color: var(--muted); font-size: 0.88rem; font-weight: 400; white-space: normal; }
  .table--stack.records td[data-label]:nth-child(n + 3)::before { content: attr(data-label) " "; font-size: inherit; }
}
/* Les membres d'un groupe : une ligne chacun (`.setting`), son nom, son rôle en étiquette, et en petit ce qu'il a
   fait sur les segments du groupe. Ce qu'un administrateur peut faire de lui est replié sous « Gérer ». */
.member > summary, .member .setting__row { padding: 11px 20px; }
.member .setting__what { flex: 1 1 160px; }
.member__name { display: flex; align-items: center; gap: 4px 6px; flex-wrap: wrap; }
.member .setting__value { font-size: 0.88rem; overflow-wrap: normal; }
.member .setting__body .muted { font-size: 0.88rem; }
.leave > p { font-size: 0.88rem; }
.leave form { margin: 10px 0 2px; }
/* Les comptes de l'administration, sur un téléphone : le compte, ses deux dates face à face, ses quatre chiffres
   sur une ligne, puis son lien avec intervals.icu. Toutes les fiches ont la même forme. */
@media (max-width: 700px) {
  .table--stack.accounts tr { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px 12px; }
  .table--stack.accounts td:nth-child(1), .table--stack.accounts td:nth-child(8) { grid-column: 1 / -1; }
  .table--stack.accounts td:nth-child(2) { grid-column: 1 / 3; grid-row: 2; }
  .table--stack.accounts td:nth-child(6) { grid-column: 3 / 5; grid-row: 2; }
  .table--stack.accounts td:nth-child(3), .table--stack.accounts td:nth-child(4),
  .table--stack.accounts td:nth-child(5), .table--stack.accounts td:nth-child(7) { grid-row: 3; }
  .table--stack.accounts td:nth-child(8) { grid-row: 4; }
}
/* Un tableau de trois colonnes courtes reste un tableau sur un téléphone : on y resserre les marges, et son
   intitulé peut passer à la ligne. */
@media (max-width: 640px) {
  .table--fit th, .table--fit td { padding: 9px 8px; }
  .table--fit th { white-space: normal; }
  .table--fit th:first-child, .table--fit td:first-child { padding-left: 16px; }
  .table--fit th:last-child, .table--fit td:last-child { padding-right: 16px; }
}

.table-wrap { overflow-x: auto; }
.table-wrap--ruled { border-top: 1px solid var(--line); }
.table { width: 100%; border-collapse: collapse; font-size: 0.93rem; }
.table th { text-align: left; font-weight: 550; color: var(--muted); font-size: 0.82rem; padding: 9px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.table td { padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.table th[scope="row"] { white-space: normal; } /* un intitulé de ligne peut passer à la ligne sur un écran étroit */
.table tr:last-child td, .table tbody tr:last-child th { border-bottom: 0; }
.table .right { text-align: right; }
.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.table a { text-decoration: none; font-weight: 550; }
.table a:hover { text-decoration: underline; }
.table__actions { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }
.table__actions form { margin: 0; }
/* Un tableau qui se lit en travers (un tronçon par ligne, une mesure par colonne) ne s'empile pas : quand il défile
   de côté, sa première colonne reste en place. */
.table--pinned th:first-child, .table--pinned td:first-child { position: sticky; left: 0; z-index: 1; background: var(--surface); }
@media (max-width: 640px) {
  .table--pinned th, .table--pinned td { padding: 8px; }
  .table--pinned th:first-child, .table--pinned td:first-child { padding-left: 12px; }
}
/* Un intertitre dans un tableau (le mois, dans la liste des activités), et ce que le mois pèse, en face. */
.table__group th { padding: 8px 12px; background: var(--surface-2); color: var(--muted); font-size: 0.82rem; font-weight: 600; }
.table__month { display: flex; justify-content: space-between; align-items: baseline; gap: 2px 14px; flex-wrap: wrap; }
.table__month > span:first-child { color: var(--text); }
.table__sum { font-weight: 400; }
/* La liste des activités : l'heure à côté du jour, en retrait. */
.acts__hour { margin-left: 4px; color: var(--muted); }
.acts__name .badge { margin-left: 2px; }
.badge--line { background: none; box-shadow: inset 0 0 0 1px var(--line-strong); }
/* Le meilleur d'une sortie : ce qu'elle vaut, et le record du coureur en face. */
#records .card__head > div { flex: 1 1 280px; min-width: 0; } /* le texte se replie, le bouton reste à sa droite */
.feats td:nth-child(2) .muted { margin-left: 6px; font-weight: 400; font-size: 0.88rem; }
.feats td:last-child { color: var(--muted); }
.feats td:last-child a { color: inherit; font-weight: inherit; }
/* Les activités voisines, au pied d'une activité : la plus récente à gauche, la plus ancienne à droite. */
.neighbours { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.neighbour { display: grid; gap: 1px; padding: 12px 16px; text-decoration: none; min-width: 0; }
.neighbour:hover { border-color: var(--line-strong); }
.neighbour--next { text-align: right; }
.neighbour__way { color: var(--muted); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.neighbour__way::before { content: "← "; }
.neighbour--next .neighbour__way::before { content: none; }
.neighbour--next .neighbour__way::after { content: " →"; }
.neighbour__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.neighbour .muted { font-size: 0.86rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 640px) {
  /* Sur un téléphone, l'une sous l'autre : leurs noms se lisent en entier. */
  .neighbours { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .neighbours > span:empty { display: none; }
  .neighbour--next { text-align: left; }
}
/* Le classement d'un segment : la ligne du coureur du compte se repère, et tous les passages sont repliés dessous. */
.ranks tr.is-me { background: var(--surface-2); }
.ranks__who .badge { margin-left: 4px; }
.passes { border-top: 1px solid var(--line); }
.passes > summary { padding: 12px 20px; cursor: pointer; color: var(--muted); font-weight: 550; }
.passes > summary:hover { color: var(--text); }
.passes[open] > summary { border-bottom: 1px solid var(--line); }
.stat__note .chip { font-size: inherit; }
/* Avant une séance : face à qui l'on roule, et de quoi en décider autrement. */
.rivals > div { display: grid; gap: 4px; min-width: 0; }
.rivals__line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; }
.rivals__label { font-weight: 600; }
.rivals__one { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.rivals__more, .rivals__best { font-size: 0.9rem; }
.rivals__best strong { color: var(--text); }
/* Les touches qui commandent la lecture, dites sous le lecteur. */
.transport__keys { font-size: 0.82rem; }
.transport__keys kbd { padding: 1px 5px; border: 1px solid var(--line-strong); border-radius: 5px; background: var(--surface-2); font: inherit; font-size: 0.78rem; white-space: nowrap; }
/* Le verdict d'une comparaison, sous son titre : qui devance qui, et de combien. */
.verdict { margin-top: 8px; line-height: 1.9; }
.verdict .chip { vertical-align: baseline; }
.verdict strong { font-weight: 650; }
/* Chercher dans ses activités : un nom, une année. */
.finder { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 0 0 12px; }
.finder .search { flex: 1 1 180px; min-width: 0; max-width: 420px; margin-bottom: 0; }
.finder .select { flex: none; width: 7.4rem; min-height: 42px; } /* « Année », « 2026 » : pas la plus longue de ses lignes */
/* Ce que pèse la liste que l'on regarde, et de quoi revenir à tout. */
.tally { display: flex; justify-content: space-between; align-items: baseline; gap: 4px 14px; flex-wrap: wrap; margin: 0 2px 10px; color: var(--muted); font-size: 0.92rem; }
.tally strong { color: var(--text); }
/* Sur un téléphone, un tableau large se lisait par une lucarne, ses colonnes coupées : le temps d'un classement
   sortait de l'écran. `.table--stack` fait de chaque ligne une fiche : ce qui la nomme en tête, puis chaque
   chiffre sous son intitulé (`data-label`). Les en-têtes restent pour un lecteur d'écran, et les rôles écrits
   dans la vue gardent au tableau son sens quand sa mise en page change. */
@media (max-width: 700px) {
  .table--stack, .table--stack tbody { display: block; }
  .table--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  /* Les chiffres se suivent et passent à la ligne, chacun à sa largeur : une date ne déborde pas sur sa voisine. */
  .table--stack tr { position: relative; display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
  .table--stack tbody tr:last-child { border-bottom: 0; }
  .table--stack td { display: block; min-width: 0; padding: 0; border: 0; }
  .table--stack td.num { text-align: left; }
  .table--stack td:not([data-label]) { flex: 1 1 100%; grid-column: 1 / -1; }
  .table--stack td[data-label] { flex: 1 0 28%; min-width: auto; display: grid; align-content: start; font-weight: 600; white-space: nowrap; }
  .acts td[data-label], .ranks td[data-label] { min-width: 0; }
  .table--stack td[data-label]::before { content: attr(data-label); color: var(--muted); font-size: 0.74rem; font-weight: 400; }
  .table--stack td[data-label] strong { font-weight: inherit; }
  .table--stack td[data-label] br { display: none; }
  .table--stack td[data-label] > .badge { justify-self: start; }
  .table--stack td[data-label] .muted { font-weight: 400; font-size: 0.86rem; }
  /* Une cellule de texte (le nom d'une activité, une phrase) : sous son intitulé, sur toute la largeur. */
  .table--stack td[data-wide] { flex: 1 1 100%; font-weight: 400; white-space: normal; }
  .table--stack .table__group { display: block; padding: 0; }
  .table--stack .table__group th { display: block; padding: 8px 16px; border: 0; }
  .table--stack .stretch::after { content: ""; position: absolute; inset: 0; } /* toute la fiche ouvre l'activité */
  .table--stack .table__actions { justify-content: flex-start; }
  .table--stack .share__cell { min-width: 0; }
  .table--stack .share__cell .copy:not(.copy--share) .input { flex: 1; }

  /* Une activité : son nom, puis la date, puis ses chiffres, alignés d'une fiche à l'autre. */
  .acts tr { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr) minmax(0, 1.25fr) minmax(0, 0.85fr); gap: 3px 8px; }
  .acts td[data-label] { font-size: 0.95rem; }
  .table--stack td.acts__name { order: -2; font-size: 1rem; }
  .table--stack td.acts__date { order: -1; grid-column: 1 / -1; color: var(--muted); font-size: 0.86rem; }

  /* Un classement : le rang, le coureur et son temps sur la première ligne, la date dessous, puis les mesures. */
  .ranks tr { display: grid; grid-template-columns: 22px repeat(6, minmax(0, 1fr)); gap: 4px 12px; }
  .ranks td[data-label] { grid-column: span 2; }
  .table--stack td.ranks__rank { grid-column: 1; grid-row: 1 / span 4; color: var(--muted); font-weight: 600; }
  .table--stack td.ranks__who { grid-column: 2 / span 3; grid-row: 1; }
  .table--stack td.ranks__time { grid-column: 5 / span 3; grid-row: 1; display: grid; justify-items: end; gap: 2px; font-size: 1.05rem; text-align: right; }
  .ranks__time strong { order: -1; }
  .ranks__time br { display: none; }
  .ranks__time .muted, .ranks__time .badge { font-size: 0.76rem; }
  .table--stack td.ranks__date { grid-column: 2 / -1; grid-row: 2; color: var(--muted); font-size: 0.86rem; }
  .table--stack td.ranks__actions { grid-column: 2 / -1; margin-top: 4px; }
}

/* Les segments parcourus pendant une activité : une ligne par passage, qui s'ouvre sur ce que les capteurs y ont
   mesuré. Les chiffres de la ligne (temps, vitesse, fréquence cardiaque, puissance) tombent en colonnes. */
.passage + .passage { border-top: 1px solid var(--line); }
.passage > summary { display: grid; grid-template-columns: 12px minmax(0, 1fr) 64px 100px 76px 64px; align-items: center; gap: 4px 14px; padding: 11px 20px; cursor: pointer; list-style: none; }
.passage > summary::-webkit-details-marker { display: none; }
.passage > summary::before { content: ""; width: 7px; height: 7px; border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted); transform: rotate(-45deg); }
.passage[open] > summary::before { transform: translateY(-2px) rotate(45deg); }
.passage > summary:hover { background: var(--surface-2); }
.passage > summary:focus-visible { outline-offset: -2px; }
.passage:last-child:not([open]) > summary { border-radius: 0 0 var(--radius) var(--radius); }
.passage[open] > summary { border-bottom: 1px solid var(--line); }
.passage__name { display: grid; gap: 1px; min-width: 0; font-size: 0.93rem; }
.passage__name a { font-weight: 550; text-decoration: none; }
.passage__name a:hover { text-decoration: underline; }
.passage__name .muted { font-size: 0.85rem; }
.passage__fact { text-align: right; white-space: nowrap; font-size: 0.93rem; font-variant-numeric: tabular-nums; }
.passage__wait { padding: 16px 20px; }
.passage__body { padding: 16px 20px 20px; display: grid; gap: 20px; grid-template-columns: minmax(0, 1fr); }
.passage__body h3 { margin-bottom: 4px; }
/* Jusqu'à neuf chiffres, sur plusieurs lignes : chaque case trace elle-même ses filets, pour que la fin de la
   dernière ligne reste vide plutôt que grise. */
.passage__body .stats { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0; background: var(--surface); }
.passage__body .stat { align-content: start; box-shadow: 1px 0 0 var(--line), 0 1px 0 var(--line); }
.passage__body .table-wrap { margin: 0 -12px; }
.passage__standing { display: flex; align-items: center; justify-content: space-between; gap: 10px 20px; flex-wrap: wrap; }
.passage__standing p { flex: 1; min-width: min(100%, 320px); }
@media (max-width: 700px) {
  .passage > summary { grid-template-columns: 12px repeat(4, auto) minmax(0, 1fr); }
  .passage__name { grid-column: 2 / -1; }
  .passage__name + .passage__fact { grid-column: 2; }
  .passage__fact { text-align: left; }
}
/* Les courbes d'un passage, empilées : même échelle de distance, un titre et son unité au-dessus de chacune. */
.telemetry { display: grid; gap: 14px; grid-template-columns: minmax(0, 1fr); }
.chart__unit { color: var(--muted); font-weight: 400; }
.chart__tabs { margin-bottom: 8px; }

.empty { text-align: center; padding: 40px 24px; display: grid; gap: 10px; justify-items: center; }
.empty p { color: var(--muted); max-width: 52ch; }
.steps { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); text-align: left; margin: 10px 0; width: 100%; padding: 0; list-style: none; counter-reset: step; }
.steps li { counter-increment: step; padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius); display: grid; gap: 4px; }
.steps li::before { content: counter(step); display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: var(--primary); color: var(--primary-text); font-weight: 700; font-size: 0.8rem; }
.steps span { color: var(--muted); font-size: 0.9rem; }

/* Premiers pas : les trois étapes d'un nouveau compte, l'une sous l'autre. Celle en cours porte ses actions et un
   repère plein ; une étape faite, une coche. */
.start { list-style: none; margin: 0; padding: 0; }
.start__step { position: relative; display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 14px; padding: 14px 20px; }
/* Le fil qui relie une étape à la suivante : plus marqué une fois l'étape faite. */
.start__step:not(:last-child)::after { content: ""; position: absolute; left: 33px; top: 46px; bottom: -10px; width: 2px; border-radius: 1px; background: var(--line); }
.start__step.is-done:not(:last-child)::after { background: var(--line-strong); }
.start__mark { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--line-strong); color: var(--muted); font-weight: 700; font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.start__step.is-current .start__mark { background: var(--primary); border-color: var(--primary); color: var(--primary-text); }
.start__step.is-done .start__mark { background: var(--surface-2); border-color: transparent; color: var(--text); }
.start__body { display: grid; gap: 4px; padding-top: 3px; justify-items: start; }
.start__title { font-size: 1rem; }
.start__step:not(.is-current) .start__title { color: var(--muted); font-weight: 550; }
.start__text { color: var(--muted); max-width: 70ch; }
.start__hint { color: var(--muted); font-size: 0.9rem; }
.start__actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 8px 0 4px; }
/* Sous l'action d'une étape : les autres chemins, en une phrase. */
.start__alts { flex: 1 1 100%; color: var(--muted); font-size: 0.9rem; max-width: 70ch; }
/* Le lien d'une étape faite, pour y revenir. */
.start__again { white-space: nowrap; }
/* L'avancement : un trait par étape, plein quand elle est faite. */
.steps-bar { display: flex; gap: 4px; width: 100%; max-width: 200px; margin-top: 8px; }
.steps-bar span { flex: 1; height: 4px; border-radius: 2px; background: var(--line-strong); }
.steps-bar .is-done { background: var(--text); }
@media (max-width: 640px) {
  .start__actions .btn { min-width: 0; white-space: normal; text-align: center; } /* un libellé long passe à la ligne, il ne sort pas de la carte */
  .start__actions .btn--primary { flex: 1 1 100%; } /* l'action de l'étape, sous le pouce */
}
.start-card { margin-bottom: 22px; }
.start-card .card__head form { margin: 0; }
/* Le rappel de l'étape en cours, sur les pages qui y mènent. */
.nextstep { display: flex; align-items: baseline; gap: 6px 12px; flex-wrap: wrap; padding: 10px 14px; margin-bottom: 18px; border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--surface); }
.nextstep__tag { text-decoration: none; background: var(--primary); color: var(--primary-text); }
/* Ce que Sillage permet d'autre : des liens en pavés. `div`, pour ne pas ranger aussi en colonnes une explication
   repliée (`details.more`, plus bas), dont le texte partait à droite de sa question. */
div.more { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.more__item { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); text-decoration: none; }
.more__item:hover { border-color: var(--line-strong); }
.more__text { display: grid; gap: 4px; min-width: 0; }
.more__text span { color: var(--muted); font-size: 0.9rem; }
/* La page qui accueille un nouveau compte : trois coureurs sur un même tronçon, à leur couleur et à leur numéro. */
.welcome { display: grid; gap: 8px; justify-items: center; text-align: center; margin: 4px 0 24px; }
.welcome p { max-width: 58ch; }
.welcome__art { display: block; width: 100%; max-width: 340px; height: auto; margin-bottom: 4px; }
.welcome__road { fill: none; stroke: var(--line-strong); stroke-width: 3; stroke-linecap: round; }
.welcome__end { fill: var(--bg); stroke: var(--text); stroke-width: 2; }
.welcome__flag { fill: var(--text); stroke: var(--text); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.welcome__runner circle { fill: currentColor; stroke: var(--bg); stroke-width: 2; }
.welcome__runner text { font-size: 11px; font-weight: 700; }
.welcome__runner.ink--1 text { fill: var(--athlete-1-ink); }
.welcome__runner.ink--2 text { fill: var(--athlete-2-ink); }
.welcome__runner.ink--3 text { fill: var(--athlete-3-ink); }

/* Les étapes d'un guide, numérotées, l'une sous l'autre. */
.guide { margin: 0; padding: 0; list-style: none; counter-reset: step; display: grid; gap: 10px; }
.guide li { counter-increment: step; display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 12px; align-items: start; }
.guide li::before { content: counter(step); display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--surface-2); font-weight: 700; font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.guide li > span { padding-top: 2px; }
a.tab { text-decoration: none; }
section[id], turbo-frame[id], details[id] { scroll-margin-top: 16px; }

.hint { margin-top: 14px; }
.pager { display: flex; justify-content: space-between; align-items: center; margin-top: 14px; color: var(--muted); }

.filters { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.filter { display: inline-flex; align-items: center; gap: 7px; padding: 5px 12px; border: 1px solid var(--line-strong); border-radius: 999px; text-decoration: none; font-size: 0.9rem; background: var(--surface); }
.filter.is-current { background: var(--primary); border-color: var(--primary); color: var(--primary-text); }
.filter__count { color: var(--muted); font-size: 0.82rem; }
.filter.is-current .filter__count { color: inherit; opacity: 0.75; }
/* Sur un écran étroit, une rangée de filtres ou d'onglets ne passe plus à la ligne : elle défile de côté, d'un
   doigt, jusqu'au bord de l'écran, et l'élément en cours est amené en vue (contrôleur `rail`). */
@media (max-width: 640px) {
  .filters, .page-head__actions { flex-wrap: nowrap; overflow-x: auto; margin-inline: -20px; padding-inline: 20px; scrollbar-width: none; }
  .page-head__actions { width: calc(100% + 40px); padding-block: 3px; } /* la place du contour de mise au point */
  .page-head__actions::-webkit-scrollbar { display: none; }
  .page-head__actions > * { flex: none; }
  .page-head__actions > .btn--primary { order: -1; } /* l'action principale d'abord, sans attendre que l'on fasse défiler */
  /* Des boutons en ligne ailleurs dans la page (`row--rail`) : la même rangée qui défile, dans la marge de sa carte. */
  .row--rail { flex: 1 1 100%; min-width: 0; flex-wrap: nowrap; overflow-x: auto; margin-inline: -20px; padding: 3px 20px; scrollbar-width: none; }
  .row--rail::-webkit-scrollbar { display: none; }
  .row--rail > * { flex: none; }
  .filters::-webkit-scrollbar { display: none; }
  .filter { flex: none; min-height: 36px; white-space: nowrap; }
  .btn--small { min-height: 36px; }
}
/* Le sport : son pictogramme et son nom, d'un seul tenant, dans la couleur du texte pour ressortir d'une ligne grisée. */
.sport { display: inline-flex; align-items: center; gap: 5px; color: var(--text); font-weight: 550; white-space: nowrap; vertical-align: bottom; }
.sport-icon { flex: none; }
.badge--sport { display: inline-flex; align-items: center; gap: 4px; vertical-align: middle; }
/* Avec qui l'on est sorti, dans la liste des activités : des noms, qui passent à la ligne s'il le faut. */
.acts__with { display: inline-flex; align-items: center; gap: 4px; vertical-align: middle; white-space: normal; }
.acts__with svg { flex: none; }

/* --- Import -------------------------------------------------------------- */

/* La page d'import : d'où repartir, ce qui vient d'arriver, et ce qui dispense d'importer. */
.import__since { margin: 0; font-size: 0.95rem; }
.import__auto-head { display: flex; align-items: flex-start; gap: 14px; }
.import__auto-head h2 { margin-bottom: 4px; }
.import__auto-head .menu__icon { flex: none; }
.arrivals .feed__who { display: grid; gap: 2px; min-width: 0; }
.arrivals__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arrivals .feed__value { flex: none; text-align: right; font-size: 0.86rem; }
.drop { display: grid; gap: 8px; justify-items: center; text-align: center; padding: 30px 20px; border: 1.5px dashed var(--line-strong); border-radius: var(--radius); background: var(--surface-2); cursor: pointer; }
.drop.is-over { border-color: var(--text); background: var(--surface); }
/* Des fichiers sont choisis : la zone n'invite plus, elle dit ce qu'elle tient. */
.drop.is-chosen { border-style: solid; padding-top: 20px; padding-bottom: 20px; }
.drop.is-chosen .drop__files { color: var(--text); font-weight: 650; font-size: 1rem; overflow-wrap: anywhere; }
.drop__change { display: none; color: var(--muted); font-size: 0.88rem; text-decoration: underline; text-underline-offset: 3px; }
.drop.is-chosen .drop__invite { display: none; }
.drop.is-chosen .drop__change { display: inline; }
/* Ce que l'on a choisi, nommé : un fichier par ligne, son poids, ou pourquoi il sera écarté. */
.drop__list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--radius); font-size: 0.92rem; }
.drop__list[hidden] { display: none; }
.drop__list li { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 8px 12px; }
.drop__list li + li { border-top: 1px solid var(--line); }
.drop__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drop__note { color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.drop__list .is-ignored .drop__name { color: var(--muted); text-decoration: line-through; }
.drop__list .is-ignored .drop__note { color: var(--danger); }
.drop__rest { color: var(--muted); }
/* Ce que dit la zone selon ce avec quoi on la touche : une souris dépose et clique, un doigt touche. */
.touch-only { display: none; }
@media (hover: none) and (pointer: coarse) {
  .touch-only { display: inline; }
  .mouse-only { display: none; }
}
@media (max-width: 640px) { .drop__go .btn--primary { flex: 1 1 100%; } } /* l'action, sous le pouce */
.drop input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.drop:focus-within { outline: 2px solid var(--text); outline-offset: 2px; }
.drop__files { color: var(--muted); font-size: 0.9rem; max-width: 60ch; }
/* L'avancement d'un import par lots, puis son compte rendu. */
.upload-progress { display: grid; gap: 6px; }
.upload-progress progress { width: 100%; height: 8px; accent-color: var(--primary); }
.report { display: grid; gap: 4px; margin-bottom: 0; }
.report[hidden] { display: none; }
.report a { font-weight: 550; }

/* Le choix d'une couleur : huit pastilles sur une ligne, chacune au milieu d'une case de 44 px, la taille d'un
   doigt. Celle que l'on a choisie est cernée et cochée : la coche se lit sans distinguer les couleurs. */
.swatches { display: grid; grid-template-columns: repeat(8, minmax(0, 44px)); }
.swatch { position: relative; display: grid; place-items: center; height: 44px; }
.swatch input { position: absolute; opacity: 0; inset: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.swatch span { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--line-strong); }
.swatch input:checked + span { box-shadow: 0 0 0 2px var(--text); }
.swatch input:checked + span::after { content: ""; width: 6px; height: 11px; margin-top: -2px; border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.swatch input:focus-visible + span { outline: 2px solid var(--text); outline-offset: 3px; }

/* Le profil : le coureur tel que les autres le voient. Sa pastille en grand, à sa couleur et avec ses initiales. */
.profile__head { justify-content: flex-start; align-items: center; flex-wrap: nowrap; gap: 16px; }
.profile__head > div { min-width: 0; }
.profile__head h1 { overflow-wrap: anywhere; }
.profile__face { display: grid; place-items: center; width: 64px; height: 64px; border-radius: 50%; font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; flex: none; }
/* Elle prend la couleur que l'on coche, avant même d'enregistrer : on voit ce que l'on choisit. La pastille de la
   ligne « Nom et couleur », juste au-dessus des couleurs, fait de même. */
.profile:has(.swatch input[value="1"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-1); color: var(--athlete-1-ink); }
.profile:has(.swatch input[value="2"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-2); color: var(--athlete-2-ink); }
.profile:has(.swatch input[value="3"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-3); color: var(--athlete-3-ink); }
.profile:has(.swatch input[value="4"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-4); color: var(--athlete-4-ink); }
.profile:has(.swatch input[value="5"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-5); color: var(--athlete-5-ink); }
.profile:has(.swatch input[value="6"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-6); color: var(--athlete-6-ink); }
.profile:has(.swatch input[value="7"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-7); color: var(--athlete-7-ink); }
.profile:has(.swatch input[value="8"]:checked) :is(.profile__face, .profile__dot) { background: var(--athlete-8); color: var(--athlete-8-ink); }
/* Deux champs courts restent côte à côte, même sur un téléphone, et ne s'étirent pas sur un écran large. */
.fields--pair { grid-template-columns: repeat(2, minmax(0, 200px)); }
@media (max-width: 640px) {
  .profile__face { width: 56px; height: 56px; font-size: 1.3rem; }
  .profile .input { font-size: 16px; } /* en deçà, iOS grossit la page au toucher */
}

/* --- Cartes géographiques ------------------------------------------------- */

.map { height: 380px; border-radius: var(--radius); border: 1px solid var(--line); overflow: hidden; background: var(--surface-2); z-index: 0; }
.map--tall { height: 460px; }
@media (max-width: 640px) { .map--tall { height: clamp(260px, 46vh, 440px); } } /* la carte et ce qui la suit tiennent à l'écran */
.map--small { height: 220px; }
.leaflet-container { font: inherit; background: var(--surface-2); }
.leaflet-control-attribution { font-size: 11px; }

/* Sélecteur de fond et logo Google : même allure que les boutons de zoom de la carte. */
.map-layers { appearance: none; padding: 6px 28px 6px 10px; border: 2px solid rgb(0 0 0 / 20%); border-radius: 4px; background-color: #ffffff; background-clip: padding-box; background-image: linear-gradient(45deg, transparent 50%, #52514e 50%), linear-gradient(135deg, #52514e 50%, transparent 50%); background-position: calc(100% - 14px) 50%, calc(100% - 9px) 50%; background-size: 5px 5px; background-repeat: no-repeat; color: #17191d; font: 550 13px/1.2 var(--sans); cursor: pointer; }
.map-layers:focus-visible { outline: 2px solid #17191d; outline-offset: 1px; }
.map-google { display: block; height: 18px; width: auto; }

.route-casing { stroke: #ffffff; stroke-width: 7; stroke-opacity: 0.9; fill: none; }
.route-line { stroke: #17191d; stroke-width: 3; stroke-opacity: 0.85; fill: none; }
.route-line--muted { stroke: #6b6a66; stroke-opacity: 0.7; }
.route-line--selected { stroke: #17191d; stroke-width: 5; stroke-opacity: 1; }

.pin { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: #17191d; color: #ffffff; border: 2px solid #ffffff; font-size: 11px; font-weight: 700; box-shadow: 0 1px 4px rgb(0 0 0 / 35%); }
.pin--finish { background: #ffffff; color: #17191d; border-color: #17191d; }
.pin--loop { width: 34px; border-radius: 12px; }
.pin--drag { cursor: grab; width: 28px; height: 28px; touch-action: none; } /* le doigt posé dessus le déplace, sans faire défiler la page */
/* Un doigt sur la carte fait défiler la page, la carte se déplace à deux (`spare`, dans `lib/map.js`) : on rend
   le geste au navigateur, que Leaflet lui retire (trois classes, d'où les quatre), et le rappel. */
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom.is-yielding { touch-action: pan-x pan-y; }
.map-reminder { position: absolute; z-index: 900; inset: 50% auto auto 50%; transform: translate(-50%, -50%); width: max-content; max-width: calc(100% - 32px); margin: 0; padding: 9px 16px; border-radius: 999px; background: rgb(15 17 20 / 88%); color: #ffffff; font-size: 0.95rem; font-weight: 600; text-align: center; pointer-events: none; }
.map-reminder[hidden] { display: none; }

.runner { position: relative; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; border: 2px solid #ffffff; font-size: 12px; font-weight: 700; box-shadow: 0 1px 5px rgb(0 0 0 / 40%); font-variant-numeric: tabular-nums; }
/* Silhouettes (cycliste, coureur) : le repère garde sa boîte de 26 px centrée sur le point de la route ; le dessin
   la déborde vers le haut, pieds ou roues posés sur ce point, et le numéro passe dans une étiquette. */
.runner--figure { background: none; border-color: transparent; box-shadow: none; }
.runner__figure { position: absolute; left: 50%; bottom: 50%; transform: translateX(-50%); line-height: 0; filter: drop-shadow(0 1px 2px rgb(0 0 0 / 35%)); }
.runner__figure.is-flipped { transform: translateX(-50%) scaleX(-1); }
.runner__figure svg { display: block; }
.runner__tag { position: absolute; left: calc(50% + 6px); bottom: calc(50% + 25px); display: grid; place-items: center; min-width: 15px; height: 15px; padding: 0 3px; border: 1.5px solid #17191d; border-radius: 8px; background: #ffffff; color: #17191d; font-size: 10px; font-weight: 700; line-height: 1; }
.runner--figure.is-beating::after { inset: 4px; background: currentColor; }

/* Le halo d'un coureur bat à sa fréquence cardiaque : --beat est la durée d'un battement. */
.runner.is-beating::after { content: ""; position: absolute; inset: -2px; border-radius: 50%; background: inherit; z-index: -1; animation: halo var(--beat, 1s) ease-out infinite; }
@keyframes halo {
  0% { transform: scale(1); opacity: 0.55; }
  70%, 100% { transform: scale(2.3); opacity: 0; }
}

/* --- Sélection d'un tronçon ----------------------------------------------- */

.range2 { position: relative; height: 34px; }
.range2__track { position: absolute; left: 0; right: 0; top: 15px; height: 4px; border-radius: 2px; background: var(--line-strong); }
.range2__fill { position: absolute; top: 15px; height: 4px; border-radius: 2px; background: var(--text); }
.range2 input { position: absolute; inset: 0; width: 100%; margin: 0; background: none; pointer-events: none; appearance: none; -webkit-appearance: none; height: 34px; }
.range2 input::-webkit-slider-runnable-track { background: none; height: 4px; }
.range2 input::-moz-range-track { background: none; height: 4px; }
.range2 input::-webkit-slider-thumb { -webkit-appearance: none; pointer-events: auto; width: 20px; height: 20px; margin-top: -8px; border-radius: 50%; background: var(--surface); border: 2px solid var(--text); cursor: grab; }
.range2 input::-moz-range-thumb { pointer-events: auto; width: 16px; height: 16px; border-radius: 50%; background: var(--surface); border: 2px solid var(--text); cursor: grab; }
.range2 input:focus-visible { outline: none; }
.range2 input:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--text); outline-offset: 2px; }
.range2 input:focus-visible::-moz-range-thumb { outline: 2px solid var(--text); outline-offset: 2px; }

.nudge { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.nudge__group { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 0.9rem; }
/* L'outil de tracé d'un segment : un volet au pied du profil d'une activité, sur toute la largeur de sa carte.
   Replié, il tient en un titre et une phrase ; déplié, il montre les curseurs, les chiffres du tronçon et de quoi
   créer le segment. */
.tracer { margin: 0 -20px -16px; border-top: 1px solid var(--line); }
.tracer > summary { position: relative; display: grid; gap: 2px; padding: 12px 48px 12px 20px; cursor: pointer; list-style: none; }
.tracer > summary::-webkit-details-marker { display: none; }
.tracer > summary::after { content: "›"; position: absolute; top: 10px; right: 20px; color: var(--muted); font-size: 1.4rem; line-height: 1; transition: transform 0.15s; }
.tracer[open] > summary::after { transform: rotate(90deg); }
.tracer > summary:hover { background: var(--surface-2); }
.tracer > summary:focus-visible { outline-offset: -2px; }
.tracer:not([open]) > summary { border-radius: 0 0 var(--radius) var(--radius); }
.tracer > summary .muted { font-size: 0.9rem; }
.tracer__title { display: inline-flex; align-items: center; gap: 7px; font-weight: 650; }
.tracer > .stack { padding: 6px 20px 18px; }
/* Des tuiles plus serrées, pour les chiffres d'un tronçon : trois par ligne sur un téléphone. */
/* Leur nombre varie avec les capteurs : chaque tuile porte son filet, et une ligne incomplète reste blanche. */
.stats--compact { gap: 0; background: var(--surface); }
.stats--compact .stat { padding: 9px 12px; box-shadow: 1px 0 0 var(--line), 0 1px 0 var(--line); }
.stats--compact .stat__value { font-size: 1.05rem; }
@media (max-width: 640px) {
  .stats--compact { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .stats--compact > .stat:last-child:nth-child(odd) { grid-column: auto; }
  .stats--compact .stat { padding: 8px 10px; align-content: start; }
  .stats--compact .stat__label { font-size: 0.74rem; }
  .stats--compact .stat__value { font-size: 0.98rem; white-space: nowrap; }
  /* Le départ et l'arrivée côte à côte, chacun sous son nom. */
  .nudge { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 14px; }
  .nudge__group { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 4px 6px; }
  .nudge__group > span { grid-column: 1 / -1; }
  .nudge__group strong { text-align: center; white-space: nowrap; }
}

/* --- Graphiques ---------------------------------------------------------- */

.chart { position: relative; }
.chart__title { font-size: 0.92rem; font-weight: 650; }
.chart__sub { color: var(--muted); font-size: 0.85rem; margin-bottom: 6px; }
.chart canvas { display: block; width: 100%; touch-action: pan-y; border-radius: 6px; }
.chart canvas:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.chart__tip { position: absolute; top: 0; left: 0; z-index: 5; pointer-events: none; min-width: 150px; padding: 8px 10px; border-radius: 9px; background: var(--surface); border: 1px solid var(--line-strong); box-shadow: var(--shadow); font-size: 0.85rem; display: grid; gap: 3px; }
.chart__tip-head { color: var(--muted); font-size: 0.8rem; margin-bottom: 2px; }
.chart__tip-row { display: flex; align-items: center; gap: 7px; white-space: nowrap; }
.chart__tip-row strong { font-variant-numeric: tabular-nums; }
.chart__tip-row span:last-child { color: var(--muted); }
.key { display: inline-block; width: 12px; height: 3px; border-radius: 2px; flex: none; background: var(--faint); }
.key--1 { background: var(--athlete-1); } .key--2 { background: var(--athlete-2); } .key--3 { background: var(--athlete-3); }
.key--4 { background: var(--athlete-4); } .key--5 { background: var(--athlete-5); } .key--6 { background: var(--athlete-6); }
.key--7 { background: var(--athlete-7); } .key--8 { background: var(--athlete-8); }

.legend { display: flex; gap: 6px 18px; flex-wrap: wrap; margin-bottom: 8px; font-size: 0.88rem; color: var(--muted); }
.legend__item { display: inline-flex; align-items: center; gap: 7px; }
/* Les deux formes de point d'une courbe de progression : plein (record battu), creux (autre passage). */
.mark { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: none; background: var(--muted); }
.mark--hollow { background: transparent; box-shadow: inset 0 0 0 2px var(--muted); }
.chart__tip-note { color: var(--muted); font-size: 0.8rem; white-space: nowrap; }

/* La pente (lib/grade.js) : une pastille carrée à la couleur de sa classe, et l'échelle posée sous un profil, les
   paliers écrits entre les cases. Un filet cerne chaque case : les couleurs sont les mêmes dans les deux thèmes,
   et la plus pâle se perdrait sur un fond clair, la plus sombre sur un fond sombre. */
.grade { display: inline-block; width: 10px; height: 10px; border-radius: 2px; flex: none; box-shadow: inset 0 0 0 1px var(--line-strong); }
.grade--flat { background: var(--grade-flat); }
.grade--up-1 { background: var(--grade-up-1); } .grade--up-2 { background: var(--grade-up-2); }
.grade--up-3 { background: var(--grade-up-3); } .grade--up-4 { background: var(--grade-up-4); }
.grade--up-5 { background: var(--grade-up-5); } .grade--up-6 { background: var(--grade-up-6); }
.grade--up-7 { background: var(--grade-up-7); } .grade--up-8 { background: var(--grade-up-8); }
.grade--up-9 { background: var(--grade-up-9); }
.grade--down-1 { background: var(--grade-down-1); } .grade--down-2 { background: var(--grade-down-2); }
.grade--down-3 { background: var(--grade-down-3); } .grade--down-4 { background: var(--grade-down-4); }
.grade-scale { display: flex; align-items: flex-start; gap: 10px; margin-top: 8px; color: var(--muted); font-size: 0.78rem; line-height: 12px; }
.grade-scale__steps { display: inline-flex; gap: 2px; padding-bottom: 15px; }
.grade-scale__step { position: relative; width: 24px; height: 12px; border-radius: 2px; box-shadow: inset 0 0 0 1px var(--line-strong); }
@media (max-width: 520px) { .grade-scale__step { width: 16px; } .grade-scale__mark { font-size: 0.68rem; } }
.grade-scale__mark { position: absolute; top: 15px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.grade-scale__mark--before { left: -1px; transform: translateX(-50%); }
.grade-scale__mark--after { right: -1px; transform: translateX(50%); }

.tabs { display: inline-flex; padding: 3px; border-radius: 10px; background: var(--surface-2); gap: 2px; flex-wrap: wrap; }
.tab { padding: 5px 12px; border: 0; border-radius: 8px; background: transparent; color: var(--muted); font: inherit; font-weight: 550; font-size: 0.9rem; cursor: pointer; }
.tab:hover { color: var(--text); }
.tab.is-current, .tab[aria-pressed="true"] { background: var(--surface); color: var(--text); box-shadow: 0 1px 2px rgb(0 0 0 / 12%); }
/* Sur un écran étroit, des onglets ne passent pas à la ligne : ils défilent de côté dans leur rangée. */
@media (max-width: 640px) {
  .tabs { display: flex; flex-wrap: nowrap; overflow-x: auto; width: fit-content; max-width: 100%; scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { flex: none; display: inline-flex; align-items: center; justify-content: center; min-height: 36px; white-space: nowrap; }
}
/* Un libellé qui se raccourcit quand la place manque : la version longue, ou la courte. */
.narrow-only { display: none; }
@media (max-width: 640px) { .narrow-only { display: inline; } }

/* --- Comparaison --------------------------------------------------------- */

.compare__row { display: grid; grid-template-columns: auto minmax(120px, 1fr) minmax(220px, 2fr) auto; gap: 10px 12px; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--line); }
.compare__row:last-of-type { border-bottom: 0; }
.compare__row.is-off .chip, .compare__row.is-off .select { opacity: 0.45; }
/* Sur un téléphone, la liste des coureurs est plus haute que l'écran : les boutons qui lancent la comparaison
   restent collés au pied de la fenêtre tant que l'on choisit les passages. `--foot` les tient au-dessus de ce qui
   couvre le bas de la fenêtre : les onglets du site, ou ceux de l'application iOS, qui en dit la hauteur à la
   page (contrôleur `native`). */
@media (max-width: 640px) {
  .compare__go { position: sticky; bottom: var(--foot); z-index: 5; margin: 6px -20px -16px; padding: 10px 20px; border-top: 1px solid var(--line); border-radius: 0 0 var(--radius) var(--radius); background: var(--surface); }
  .compare__go .btn { flex: 1; }
  .is-overlaid .compare__go { position: static; } /* une application iOS qui ne dit pas cette hauteur : ils passeraient sous ses onglets */
  .wide-only { display: none; }
}
/* Une explication de détail, repliée : elle reste sur la page, à un geste. */
.more { margin-top: 6px; }
.more > summary { width: fit-content; cursor: pointer; color: var(--muted); font-size: 0.9rem; font-weight: 550; text-decoration: underline; text-underline-offset: 3px; list-style: none; }
.more > summary::-webkit-details-marker { display: none; }
.more[open] > summary { margin-bottom: 4px; }
.check { width: 18px; height: 18px; accent-color: var(--primary); }
@media (max-width: 640px) {
  /* Deux lignes par coureur : sa case, son nom et « Autre date » ; puis la date choisie, sur toute la largeur. */
  .compare__row { grid-template-columns: auto 1fr auto; }
  .compare__row .select { grid-column: 1 / -1; }
  .compare__row > :last-child { grid-row: 1; grid-column: 3; }
}

/* Le lecteur d'une course : la carte et, juste dessous, ce qui la commande (`.transport`) ; le classement en direct
   à côté, sur toute leur hauteur, ou dessous sur un écran étroit. */
.race { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 14px; align-items: stretch; }
.race__main { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-content: start; }
.race__map { height: 520px; }
.race__stage { min-width: 0; }

/* La scène en perspective (MapLibre) : mêmes pastilles que la carte, zoom et mentions par-dessus. */
.scene { position: relative; background: #eef3f5; } /* la couleur de l'horizon : rien ne tranche au-dessus de la ligne de crête */
.scene--maquette { background: #f4f4f1; } /* le monde maquette : son horizon est blanc cassé... */
.scene--night { background: #1b1c1e; }    /* ...ou sombre, la nuit */
/* Un coureur dans la scène : son repère, et au-dessus de lui ce que la séance en dit (son écart). */
.scene__body { position: relative; display: block; }
/* La brume et le voile de la pluie (lib/scene.js, `mist`) : rien au pied de l'image, de plus en plus dense vers
   l'horizon, à la couleur du ciel (`--haze`). Il ne retient ni le doigt ni la souris. */
.scene__haze { position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to top, transparent 0%, color-mix(in srgb, var(--haze) 30%, transparent) 30%, color-mix(in srgb, var(--haze) 78%, transparent) 55%, var(--haze) 72%); }
.scene__note { position: absolute; left: 50%; bottom: calc(50% + 20px); transform: translateX(-50%); padding: 0 6px; border: 1.5px solid #17191d; border-radius: 9px; background: #ffffff; color: #17191d; font-size: 11px; font-weight: 700; line-height: 1.45; white-space: nowrap; font-variant-numeric: tabular-nums; }
.scene__body:has(.runner--figure) .scene__note { bottom: calc(50% + 46px); }
/* Un cycliste en volume : son numéro et ce qui s'écrit de lui se tiennent au-dessus du casque (--lift, que règle la scène). */
.runner--volume .runner__tag { left: 50%; bottom: calc(50% + var(--lift, 36px) + 5px); transform: translateX(-50%); }
.scene__body:has(.runner--volume) .scene__note { bottom: calc(50% + var(--lift, 36px) + 26px); }
.runner--volume.is-bibbed .runner__tag { visibility: hidden; } /* le dossard se lit dans son dos : pas de doublon */
.scene__body:has(.is-bibbed) .scene__note { bottom: calc(50% + var(--lift, 36px) + 6px); }
.scene__loading { position: absolute; left: 50%; bottom: 34px; z-index: 2; transform: translateX(-50%); margin: 0; padding: 5px 14px; border-radius: 999px; background: rgb(255 255 255 / 88%); color: #17191d; font-size: 0.88rem; font-weight: 550; box-shadow: 0 1px 4px rgb(0 0 0 / 25%); white-space: nowrap; }
.scene__marker { position: absolute; top: 0; left: 0; will-change: transform; }
.scene__marker.is-out { visibility: hidden; } /* sous la caméra, ou derrière elle */
/* Les jalons de la route (arches, bornes, panneaux) : le pied du dessin est posé sur le point de la carte, et le
   dessin grandit ou rapetisse autour de ce pied selon son éloignement. */
.sign { position: absolute; left: 0; bottom: 0; transform-origin: 0 100%; line-height: 0; filter: drop-shadow(0 1px 2px rgb(0 0 0 / 35%)); }
.sign svg { display: block; transform: translateX(-50%); font-family: var(--sans); }
.scene__zoom { position: absolute; top: 10px; left: 10px; z-index: 2; display: grid; border: 2px solid rgb(0 0 0 / 20%); border-radius: 4px; background-clip: padding-box; overflow: hidden; }
.scene__zoom button { width: 30px; height: 30px; border: 0; background: #ffffff; color: #17191d; font: 700 18px/1 var(--sans); cursor: pointer; }
.scene__zoom button + button { border-top: 1px solid #cfcdc6; }
.scene__zoom button:hover { background: #f4f4f1; }
.scene__credit { position: absolute; right: 0; bottom: 0; z-index: 2; padding: 1px 6px; background: rgb(255 255 255 / 80%); color: #333333; font-size: 11px; }
.scene__credit a { color: #0078a8; text-decoration: none; }
.scene__logo { position: absolute; left: 10px; bottom: 10px; z-index: 2; }
.race__side { display: flex; flex-direction: column; min-height: 0; }
.board { list-style: none; margin: 0; padding: 6px 0; overflow-y: auto; flex: 1; }
.board__row { display: grid; grid-template-columns: 18px auto minmax(0, 1fr) auto; gap: 4px 10px; align-items: center; padding: 9px 16px; }
.board__row + .board__row { border-top: 1px solid var(--line); }
.board__rank { color: var(--faint); font-weight: 650; font-variant-numeric: tabular-nums; }
.board__name { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board__pick { max-width: 100%; padding: 0; border: 0; background: none; color: inherit; font: inherit; font-weight: 650; text-align: left; cursor: pointer; }
.board__pick:hover { text-decoration: underline; }
.board__row.is-focus .board__pick::after { content: " · caméra"; color: var(--muted); font-weight: 500; font-size: 0.82rem; }
/* Un mot écrit par la feuille de styles se traduit par la langue de la page (`<html lang>`). */
:lang(en) .board__row.is-focus .board__pick::after { content: " · camera"; }
.board__hint { padding: 8px 16px 0; font-size: 0.85rem; }
.board__date { color: var(--muted); font-size: 0.82rem; }
/* Un rendez-vous en direct : les maillots du moment à côté du nom de leur porteur, et de quoi l'encourager. */
.board__jerseys { display: inline-flex; gap: 2px; margin-left: 4px; vertical-align: -3px; }
.board__jerseys:empty { display: none; }
.board__cheer { display: block; margin-top: 4px; padding: 2px 8px; font-size: 0.78rem; min-height: 0; }
/* Un maillot et qui le porte, sur une ligne. */
.worn { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.watch__news { display: grid; gap: 4px; }
.watch__news p { margin: 0; }
.board__speed { text-align: right; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
.board__gap { text-align: right; color: var(--muted); font-size: 0.82rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.pulse { grid-column: 3 / -1; display: flex; align-items: center; gap: 5px; font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.pulse__heart { display: inline-grid; }
.pulse__heart.is-beating { animation: beat var(--beat, 1s) ease-in-out infinite; }
.pulse__value { font-size: 0.95rem; min-width: 3ch; text-align: right; }
.pulse__unit { color: var(--muted); white-space: nowrap; }
.pulse__meter { flex: 1; height: 6px; margin-left: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
.pulse__fill { display: block; width: 0; height: 100%; border-radius: 3px; transition: width 0.25s linear; }
@keyframes beat {
  0%, 42%, 100% { transform: scale(1); }
  12% { transform: scale(1.3); }
  24% { transform: scale(1.02); }
  33% { transform: scale(1.15); }
}
@media (prefers-reduced-motion: reduce) {
  .pulse__heart.is-beating { animation: none; }
  .runner.is-beating::after { content: none; }
}

.board__extra { grid-column: 3 / -1; color: var(--muted); font-size: 0.82rem; font-variant-numeric: tabular-nums; display: flex; gap: 12px; flex-wrap: wrap; }

.transport { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 14px 18px; }
.transport__bar { display: flex; align-items: center; gap: 12px 16px; flex-wrap: wrap; min-width: 0; }
.transport__clock { font-size: 1.5rem; font-weight: 650; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; min-width: 5.2ch; }
.transport__total { color: var(--muted); font-variant-numeric: tabular-nums; }
.transport__time { display: flex; align-items: baseline; gap: 6px; }
.transport__rates { margin-left: auto; }
/* Les réglages d'affichage, repliés sous les commandes : des cases que le navigateur retient. */
.options > summary { display: inline-flex; align-items: center; gap: 7px; width: fit-content; cursor: pointer; color: var(--muted); font-size: 0.9rem; font-weight: 550; list-style: none; }
.options > summary::-webkit-details-marker { display: none; }
.options > summary::after { content: "›"; font-size: 1.1rem; line-height: 1; transition: transform 0.15s; }
.options[open] > summary { color: var(--text); margin-bottom: 10px; }
.options[open] > summary::after { transform: rotate(90deg); }
.options__list { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; }
/* Les temps de passage : un coureur par colonne, son nom en tête et la date dessous ; la colonne des repères
   reste en place quand le tableau défile de côté. */
.splits th.num { vertical-align: bottom; }
.splits__date { display: block; color: var(--muted); font-size: 0.78rem; font-weight: 400; }
.splits th:first-child, .splits td:first-child { position: sticky; left: 0; z-index: 1; background: var(--surface); }
.play { width: 44px; height: 44px; border-radius: 50%; border: 0; background: var(--primary); color: var(--primary-text); display: grid; place-items: center; cursor: pointer; flex: none; }
.play:hover { opacity: 0.88; }
.play:disabled { opacity: 0.4; cursor: progress; }
.scrub { width: 100%; accent-color: var(--primary); margin: 0; }
.toggle { display: inline-flex; align-items: center; gap: 7px; font-size: 0.9rem; color: var(--muted); cursor: pointer; }

@media (max-width: 900px) {
  .race { grid-template-columns: 1fr; }
  .race__map { height: 56vh; min-height: 320px; }
  .board { overflow-y: visible; } /* le classement s'allonge avec la page : pas de défilement dans le défilement */
}
/* Sur un téléphone, la carte et ses commandes tiennent ensemble à l'écran : lecture et chrono, puis le curseur,
   les vitesses sur toute la largeur, les vues, et les réglages repliés. */
@media (max-width: 640px) {
  .race__map { height: clamp(260px, 46vh, 440px); min-height: 0; }
  .transport { padding: 12px 14px; }
  .transport__restart { margin-left: auto; }
  .transport__rates { order: 3; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); width: 100%; max-width: none; margin-left: 0; }
  .transport__rates .tab { padding-inline: 0; }
  .transport__views .tabs { flex: 1; min-width: 0; width: auto; }
  .options__list { display: grid; gap: 4px; }
  .options .toggle { min-height: 36px; }
  /* Quatre coureurs tiennent dans la largeur : le nom sous la pastille, le retard sous le temps. */
  .splits th, .splits td { padding: 8px 6px; }
  .splits th:first-child, .splits td:first-child { padding-left: 14px; }
  .splits th:last-child, .splits td:last-child { padding-right: 14px; }
  .splits td .faint { display: block; font-size: 0.78rem; }
  .splits__name { display: block; max-width: 68px; margin-left: auto; overflow: hidden; text-overflow: ellipsis; }
  .splits__date { display: none; }
}

details.more { margin-top: 16px; }
details.more > summary { cursor: pointer; color: var(--muted); font-weight: 550; padding: 4px 0; }
details.more[open] > summary { margin-bottom: 10px; }
details.more > p + p { margin-top: 6px; }
.stack > details.more.card { margin-top: 0; } /* une carte parmi d'autres : l'écart est celui de la pile */

/* --- Séance sur home trainer ------------------------------------------------ */

.ride__devices { display: grid; gap: 10px; }
.ride__device { display: grid; grid-template-columns: minmax(230px, auto) minmax(0, 1fr) auto; gap: 8px 14px; align-items: center; }
.ride__device .btn { justify-content: center; }
/* Les cases des appareils d'une séance (`.slots`) : une marque dit l'état de chaque mesure, et le texte dessous le dit
   aussi (la marque ne porte pas seule l'information). Pleine : reçue ; creuse : attendue ; en tirets : perdue ; un
   trait : rien pour la donner. */
.slot__mark { display: inline-block; width: 8px; height: 8px; margin-right: 6px; border: 1.5px solid var(--muted); border-radius: 50%; vertical-align: 1px; }
.slot[data-state="live"] .slot__mark { background: var(--text); border-color: var(--text); }
.slot[data-state="lost"] .slot__mark { border-style: dashed; }
.slot[data-state="none"] .slot__mark, .slot[data-state="off"] .slot__mark { height: 0; border-width: 1.5px 0 0; border-radius: 0; vertical-align: 0.3em; }
.slot[data-state="none"] .stat__value, .slot[data-state="off"] .stat__value { color: var(--muted); }
.slots__check { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.slots__check[hidden], .slots__ask[hidden] { display: none; }
.slots__ask { display: inline-flex; flex-wrap: wrap; gap: 8px; }
.diag__box { margin-top: 12px; }
.diag { font: 0.78rem/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre; overflow-x: auto; resize: vertical; }
@media (max-width: 640px) { .ride__device { grid-template-columns: 1fr; } }
/* Sur la ligne (`.lineup`), l'écran d'avant le départ : une ligne par choix, chacune repliée sur ce qu'elle vaut
   (`details.setting`), en blocs séparés d'un filet (les appareils, face à qui et les conditions, le reste). Sur un
   téléphone, le départ reste collé au pied de la fenêtre pendant que l'on prépare (`prep__go`). */
.lineup > .settings + .settings, .lineup > .settings + .card__body { border-top: 1px solid var(--line); }
.lineup > [data-ride-target="pending"] { border-bottom: 1px solid var(--line); }
.lineup .setting__value.rivals__line { gap: 4px 10px; }
.lineup .setting > summary { padding-top: 11px; padding-bottom: 11px; }
.setting__value--clip { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; overflow-wrap: normal; }
/* Ce que dit la ligne « Appareils » repliée : chacun avec sa marque, pleine quand il envoie, en tirets quand la
   liaison a sauté (la même que celle des cases). */
.gear { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 12px; }
.gear__item { display: inline-flex; align-items: center; gap: 6px; color: var(--text); }
.gear__mark { width: 8px; height: 8px; border: 1.5px solid var(--text); border-radius: 50%; background: var(--text); flex: none; }
.gear__item[data-state="lost"] .gear__mark { background: none; border-style: dashed; }
.gear__item[data-state="none"] { color: var(--muted); }
.gear__item[data-state="none"] .gear__mark { background: none; border-color: var(--faint); border-style: dashed; }
/* Face à qui : une case par passage, la pastille du coureur, son nom, son temps et sa date. */
.picks { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.pick { display: flex; align-items: center; gap: 9px; min-height: 40px; cursor: pointer; flex-wrap: wrap; }
.pick__who { font-weight: 600; }
.pick__what { font-size: 0.9rem; }
.prep__go { padding: 12px 20px 16px; border-top: 1px solid var(--line); }
/* L'accueil de Rouler : la barre des appareils (`.gearbar`, ou l'invitation à commencer par eux), ce qu'il y a à
   rouler maintenant (`.next`), puis les modes, en tuiles (`.mode`). */
.gearbar__bar { display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; padding-top: 12px; padding-bottom: 12px; }
.gearbar__bar .gear { min-width: 0; }
.gearbar__first { display: flex; align-items: center; justify-content: space-between; gap: 14px 24px; flex-wrap: wrap; }
.gearbar__first h2 { font-size: 1.05rem; }
.gearbar__first .stack { flex: 1 1 320px; gap: 4px; }
.next__body { display: grid; gap: 6px; justify-items: start; }
.next__kicker { font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.next__title { font-size: 1.15rem; text-wrap: balance; }
.next__go { margin-top: 6px; }
.modes { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.mode { display: grid; gap: 4px; align-content: start; padding: 16px 18px; text-decoration: none; color: var(--text); }
.mode:hover { border-color: var(--line-strong); }
/* Événements prend toute la largeur quand un rendez-vous roule ou part bientôt. */
.mode--wide { grid-column: 1 / -1; }
.mode__hint .bubbles { display: inline-flex; vertical-align: middle; margin: 0 0 0 4px; }
.mode__icon { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 10px; background: var(--surface-2); margin-bottom: 6px; }
.mode__name { font-weight: 650; font-size: 1.02rem; }
.mode__hint { color: var(--muted); font-size: 0.9rem; }
.pending { display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; flex-wrap: wrap; }
.devices__go { justify-content: flex-start; }
/* La piste (Velodrome) : ses épreuves, puis la séance, le vélodrome vu de dessus. */
.velo-event { display: grid; gap: 4px; align-content: start; padding: 16px 18px; }
.velo-event__art { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 10px; background: var(--surface-2); margin-bottom: 6px; }
.velo-event__name { margin: 0; font-size: 1.05rem; }
.velo-event__name a { color: inherit; text-decoration: none; }
.velo-event__name a:hover { text-decoration: underline; }
.velo-event__facts, .velo-event__summary { margin: 0; }
.velo-event__summary { font-size: 0.92rem; }
.velo-event__marks { display: grid; gap: 4px; margin: 8px 0 0; padding: 10px 0 0; border-top: 1px solid var(--line); font-size: 0.9rem; }
.velo-event__marks div { display: flex; justify-content: space-between; gap: 12px; }
.velo-event__marks dt { color: var(--muted); }
.velo-event__marks dd { margin: 0; text-align: right; font-weight: 600; }
.velo-event__go { margin-top: 10px; }
.velo__stage { overflow: hidden; }
.velo__stage:fullscreen { display: flex; flex-direction: column; overflow-y: auto; border-radius: 0; }
.velo__cockpit { border-bottom: 1px solid var(--line); }
/* Des compteurs qui remplissent chaque rangée, quel que soit leur nombre (l'écart s'efface quand on roule seul). */
.velo__cockpit .cockpit__grid { display: flex; flex-wrap: wrap; }
.velo__cockpit .cockpit__tile { flex: 1 1 140px; }
.velo__track { position: relative; background: var(--surface-2); }
.velo__track canvas { display: block; width: 100%; }
.velo__track .velo__arena { aspect-ratio: 16 / 9; max-height: 68vh; }
.velo__stage:fullscreen .velo__arena { aspect-ratio: auto; height: 62vh; max-height: none; }
.velo__views { position: absolute; right: 10px; bottom: 10px; box-shadow: 0 1px 3px rgb(0 0 0 / 0.25); }
@media (max-width: 640px) { .velo__track .velo__arena { aspect-ratio: 4 / 3; } }
/* Ce que la séance annonce se pose au milieu de la piste, sur la pelouse, là où rien ne roule. */
.velo__note { position: absolute; top: 50%; left: 18%; right: 18%; transform: translateY(-50%); display: flex; justify-content: center; pointer-events: none; }
.velo__note .moment { padding: 7px 16px; font-size: 0.95rem; }
@media (max-width: 640px) { .velo__note { left: 12%; right: 12%; } .velo__note .moment { padding: 5px 12px; font-size: 0.82rem; } }
.velo__board { list-style: none; margin: 0; padding: 4px 0; columns: 2 260px; column-gap: 0; }
.velo__board:empty { display: none; }
.velo__board .board__row { grid-template-columns: auto minmax(0, 1fr) auto; break-inside: avoid; padding: 7px 16px; }
.velo__board .board__row + .board__row { border-top: 0; }
.velo__board .board__row.is-me { background: var(--surface-2); }
.velo__board .board__value { white-space: nowrap; }
.velo__controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; padding: 12px 16px; border-top: 1px solid var(--line); }

/* Les jeux (Game) : la liste des jeux, une tuile chacun, sa vignette en couleur ; puis la partie, sa scène, ce qui s'y
   lit d'un coup d'œil (l'objet, le bord de l'écran, l'annonce) et ses commandes à la taille d'un pouce. */
.games { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 14px; }
@media (max-width: 640px) { .games { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } }
.game-tile { display: grid; grid-template-rows: auto 1fr; overflow: hidden; color: var(--text); text-decoration: none; }
.game-tile:hover { border-color: var(--line-strong); }
.game-tile:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.game-tile__art { position: relative; display: block; }
/* Un jeu qui se joue aussi en direct le dit sur le coin de sa vignette. */
.game-tile__live { position: absolute; top: 8px; left: 8px; padding: 1px 8px; border-radius: 999px; background: var(--surface); color: var(--text);
                   font-size: 0.75rem; font-weight: 650; box-shadow: 0 1px 3px rgb(0 0 0 / 0.25); }
.game-tile__body { display: grid; align-content: start; gap: 3px; padding: 10px 12px 12px; }
.game-tile__name { margin: 0; font-size: 1.05rem; }
.game-tile__line { margin: 0; color: var(--muted); font-size: 0.86rem; line-height: 1.35; }
@media (max-width: 640px) {
  .game-tile__body { padding: 8px 10px 10px; }
  .game-tile__name { font-size: 0.95rem; }
  .game-tile__line { font-size: 0.8rem; }
}

/* Les vignettes des jeux ont leurs couleurs (2026-10-22, demandé tel quel : « Ajouter de la couleur ») : de petites
   scènes, comme le monde maquette, chacune avec son fond. Les mêmes en thème clair et en thème sombre ; le reste de la
   page reste en noir, blanc et gris. */
.game-art {
  display: block; width: 100%; height: auto; aspect-ratio: 2 / 1;
  --grass: #8cc66f; --bush: #5e9e4b; --field: #c7df8c; --field-2: #e9d27a; --meadow: #bfdc7e; --sky: #cfe8f6;
  --hill: #a7d18a; --dusk: #2f5b52; --tree: #234a42; --road: #4d535c; --paint: #fbfaf6; --ink: #24262b;
  --red: #e5484d; --blue: #3b82f6; --yellow: #f6c344; --orange: #f08a24; --teal: #129e9e; --cyan: #3fd0e0;
  --green: #22a06b; --purple: #8b5cf6; --pack: #a3abb5; --mud: #7a5a3a; --mud-2: #5f4229; --rope: #c8a46a;
  --wood: #d6a46a; --grain: #c39157; --track: #f5ecd6; --cereal: #e39a3a; --sun: #ffd166;
}
.game-art .grass { fill: var(--grass); }
.game-art .bush { fill: var(--bush); }
.game-art .field { fill: var(--field); }
.game-art .field-2 { fill: var(--field-2); }
.game-art .meadow { fill: var(--meadow); }
.game-art .sky { fill: var(--sky); }
.game-art .hill { fill: var(--hill); }
.game-art .dusk { fill: var(--dusk); }
.game-art .tree { fill: var(--tree); }
.game-art .sun { fill: var(--sun); }
.game-art .road, .game-art .kerb, .game-art .kerb-base, .game-art .lane { fill: none; stroke-linecap: round; }
.game-art .kerb-base { stroke: var(--paint); stroke-width: 24; }
.game-art .kerb { stroke: var(--red); stroke-width: 24; stroke-dasharray: 7 7; stroke-linecap: butt; }
.game-art .road { stroke: var(--road); stroke-width: 18; }
.game-art .lane { stroke: var(--paint); stroke-width: 1.5; stroke-dasharray: 6 6; opacity: 0.8; }
.game-art .flag { fill: var(--ink); }
.game-art .line-white { fill: var(--paint); }
.game-art .item { fill: var(--yellow); stroke: var(--ink); stroke-width: 2; }
.game-art .item-mark { fill: var(--ink); font: 800 16px system-ui, sans-serif; }
.game-art .peel { fill: var(--yellow); stroke: #8a6a12; stroke-width: 1.5; }
.game-art .rider { stroke: var(--paint); stroke-width: 2; }
.game-art .red { fill: var(--red); }
.game-art .blue { fill: var(--blue); }
.game-art .yellow { fill: var(--yellow); }
.game-art .orange { fill: var(--orange); }
.game-art .teal { fill: var(--teal); }
.game-art .cyan { fill: var(--cyan); }
.game-art .green { fill: var(--green); }
.game-art .purple { fill: var(--purple); }
.game-art .pack { fill: var(--pack); }
.game-art .slate { fill: var(--ink); stroke: var(--paint); stroke-width: 1.5; }
.game-art .slate-text { fill: var(--paint); font: 700 13px system-ui, sans-serif; }
.game-art .cone { fill: var(--orange); }
.game-art .cone-band { stroke: var(--paint); stroke-width: 2; }
.game-art .van { fill: var(--paint); stroke: var(--ink); stroke-width: 2; }
.game-art .van-glass { fill: #9fc6e0; }
.game-art .broom-stick { stroke: #8a5a2b; stroke-width: 2.5; stroke-linecap: round; }
.game-art .broom-head { fill: var(--yellow); stroke: #8a6a12; stroke-width: 1; }
.game-art .chase { fill: none; stroke: var(--paint); stroke-width: 2; stroke-dasharray: 3 5; stroke-linecap: round; opacity: 0.85; }
.game-art .radar { fill: none; stroke: var(--cyan); stroke-width: 1.5; opacity: 0.55; }
.game-art .mud { fill: var(--mud); }
.game-art .mud-2 { fill: var(--mud-2); }
.game-art .post { stroke: var(--paint); stroke-width: 3; stroke-linecap: round; }
.game-art .rope { fill: none; stroke: var(--rope); stroke-width: 3; stroke-linecap: round; }
.game-art .ribbon { fill: var(--red); }
.game-art .bike { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: round; }
.game-art .body, .game-art .arm { fill: none; stroke-linecap: round; }
.game-art .body { stroke-width: 7; }
.game-art .arm { stroke-width: 3; }
.game-art .body.red, .game-art .arm.red { stroke: var(--red); }
.game-art .body.blue, .game-art .arm.blue { stroke: var(--blue); }
.game-art .head { fill: #f0c9a4; stroke: var(--ink); stroke-width: 1; }
.game-art .wood { fill: var(--wood); }
.game-art .grain { stroke: var(--grain); stroke-width: 1.5; }
.game-art .track { fill: none; stroke: var(--track); stroke-width: 14; }
.game-art .cereal { fill: none; stroke: var(--cereal); stroke-width: 20; stroke-dasharray: 2 4; stroke-linecap: round; opacity: 0.85; }
.game-art .bowl { fill: var(--paint); stroke: var(--blue); stroke-width: 3; }
.game-art .milk { fill: #fffdf7; }
.game-art .mug { fill: var(--red); stroke: var(--ink); stroke-width: 1.5; }
.game-art .mug-handle { fill: none; stroke: var(--red); stroke-width: 3; }
.game-art .coffee { fill: #6b3f22; }
.game-art .jam { fill: #b3243a; }
.game-art .stage { fill: #262a45; }
.game-art .staff { stroke: rgb(255 255 255 / 0.14); stroke-width: 1; }
.game-art .beat-easy { fill: #3fbf7f; }
.game-art .beat-steady { fill: #f2b33d; }
.game-art .beat-hard { fill: #ef5a5a; }
.game-art .now { stroke: var(--paint); stroke-width: 2; opacity: 0.8; }
.game-art .trail { fill: none; stroke: var(--yellow); stroke-width: 4; stroke-linecap: round; }
.game-art .note { fill: none; stroke: var(--paint); stroke-width: 2.5; stroke-linejoin: round; }
.game-art .note-head { fill: var(--paint); }
.game-art .butter { fill: #f7e27a; stroke: #c9a83a; stroke-width: 1; }

.arcade__stage { overflow: hidden; }
.arcade__stage:fullscreen { display: flex; flex-direction: column; overflow-y: auto; border-radius: 0; }
.arcade__cockpit { border-bottom: 1px solid var(--line); }
.arcade__cockpit .cockpit__grid { display: flex; flex-wrap: wrap; }
.arcade__cockpit .cockpit__tile { flex: 1 1 120px; }
.arcade__cockpit .arcade__score { flex: 1.6 1 170px; } /* Rythme : un score de cinq chiffres et sa série */
.arcade__screen { position: relative; background: var(--surface-2); }
/* La scène et ce qui s'y pose ; les vues se tiennent dans son coin, ou dessous sur un téléphone. */
.arcade__frame { position: relative; }
.arcade__screen canvas { display: block; width: 100%; }
.arcade__world { aspect-ratio: 16 / 9; max-height: 70vh; }
.arcade__map { aspect-ratio: 16 / 10; max-height: 70vh; }
/* Au kart, en volume : le plan du circuit dans un coin. */
.arcade__screen .arcade__map--mini { position: absolute; top: 10px; right: 10px; width: 28%; max-width: 230px; aspect-ratio: 4 / 3; border: 1px solid var(--line-strong);
                     border-radius: 10px; box-shadow: 0 1px 4px rgb(0 0 0 / 0.25); pointer-events: none; }
.arcade__stage:fullscreen .arcade__world { aspect-ratio: auto; height: 68vh; max-height: none; }
.arcade__stage:fullscreen .arcade__map:not(.arcade__map--mini) { aspect-ratio: auto; height: 68vh; max-height: none; }
.arcade__views { position: absolute; right: 10px; bottom: 10px; box-shadow: 0 1px 3px rgb(0 0 0 / 0.25); }
.arcade__hud { position: absolute; inset: 0; pointer-events: none; }
.arcade__moment { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); max-width: 70%; margin: 0; padding: 7px 16px; font-size: 0.95rem; text-align: center; }
.arcade__item { position: absolute; left: 10px; bottom: 10px; display: flex; align-items: center; gap: 10px; padding: 8px 12px 8px 8px;
                border: 1px solid var(--line-strong); border-radius: 12px; background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / 0.25); }
.arcade__item-art svg { display: block; width: 38px; height: 38px; fill: none; stroke: var(--muted); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.arcade__item.is-held .arcade__item-art svg { stroke: var(--text); stroke-width: 2.2; }
.arcade__item-name { font-weight: 650; font-size: 0.95rem; }
.arcade__item:not(.is-held) .arcade__item-name { color: var(--muted); font-weight: 500; }
/* La table : où l'on en est de l'écran. La jauge se vide à mesure qu'on s'en laisse distancer. */
.arcade__edge { position: absolute; left: 10px; bottom: 10px; display: grid; gap: 4px; min-width: min(60%, 300px); padding: 8px 12px;
                border: 1px solid var(--line-strong); border-radius: 12px; background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / 0.25); font-size: 0.88rem; }
.arcade__edge-bar { display: block; height: 8px; border-radius: 4px; background: var(--surface-2); overflow: hidden; border: 1px solid var(--line); }
.arcade__edge-bar span { display: block; height: 100%; width: 100%; background: var(--text); transition: width 0.2s linear; }
.arcade__edge.is-near .arcade__edge-bar span { background: var(--muted); }
.arcade__edge.is-near { border-style: dashed; }
.arcade__pad { display: grid; grid-template-columns: 1fr auto 1fr; gap: 10px; padding: 12px 16px; border-top: 1px solid var(--line); }
.arcade__pad .btn { min-height: 56px; justify-content: center; font-size: 1.02rem; }
.arcade__pad:not(:has(.arcade__use)) { grid-template-columns: 1fr 1fr; }
.arcade__use { min-width: 130px; }
.arcade__board { border-top: 1px solid var(--line); }
.arcade__keys { margin: 0; padding: 0 16px 12px; font-size: 0.85rem; }
@media (max-width: 640px) {
  .arcade__world { aspect-ratio: 4 / 3; }
  .arcade__map:not(.arcade__map--mini) { aspect-ratio: 1 / 1; }
  .arcade__screen .arcade__map--mini { width: 34%; }
  /* Sur un téléphone, la scène d'abord : des compteurs serrés, trois par rangée, et les vues sous la scène. */
  .arcade__cockpit .cockpit__tile { flex: 1 1 30%; padding: 8px 10px; }
  .arcade__cockpit .cockpit__tile .cockpit__value { font-size: 1.45rem; }
  .arcade__cockpit .cockpit__tile--main .cockpit__value { font-size: 1.9rem; }
  .arcade__cockpit .cockpit__label { font-size: 0.72rem; }
  .arcade__views { position: static; display: flex; width: max-content; margin: 8px 10px 8px auto; box-shadow: none; }
  /* L'annonce se range à gauche du plan du circuit, au-dessus de l'horizon : au milieu, elle cachait le coureur. */
  .arcade__moment { top: 10px; left: 10px; transform: none; max-width: calc(66% - 30px); width: max-content; padding: 5px 14px; justify-content: start; text-align: left; border-radius: 16px; }
  .arcade__hud .arcade__moment strong { font-size: 1.02rem; }
  .arcade__hud .arcade__moment span { font-size: 0.86rem; }
  .arcade__item { padding: 6px 10px 6px 6px; }
  .arcade__item-art svg { width: 30px; height: 30px; }
  .arcade__use { min-width: 0; }
}
@media (prefers-reduced-motion: reduce) { .arcade__edge-bar span { transition: none; } }
@media (max-width: 640px) {
  .modes { grid-template-columns: 1fr 1fr; gap: 10px; }
  .mode { padding: 14px; }
  .mode:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .next__go { width: 100%; justify-content: center; }
  .gearbar__first .btn--primary { flex: 1 1 100%; justify-content: center; }
  .devices__go .btn--primary { flex: 1 1 100%; justify-content: center; }
}
@media (max-width: 640px) {
  .prep__go { position: sticky; bottom: var(--foot); z-index: 5; padding: 10px 20px; border-radius: 0 0 var(--radius) var(--radius); background: var(--surface); }
  .prep__go .btn--primary { flex: 1 1 100%; }
  .prep__go .muted { font-size: 0.86rem; line-height: 1.35; }
  /* Prêt à partir, le bouton suffit : la consigne s'efface, la puissance reçue tient sur la ligne de l'essai. */
  .prep__go { gap: 6px 12px; padding-top: 8px; padding-bottom: 8px; }
  .prep__go:has([data-ride-target="start"]:not([disabled])) .prep__note { display: none; }
  .prep__go [data-ride-target="reading"] { font-size: 0.9rem; margin-left: auto; }
  .is-overlaid .prep__go { position: static; } /* une application iOS qui ne dit pas la hauteur de ses onglets */
}

/* Les compteurs : de gros chiffres, lisibles depuis le vélo, à un ou deux mètres de l'écran. */
.cockpit { overflow: hidden; }
.cockpit__banner { margin: 0; padding: 12px 20px; background: var(--primary); color: var(--primary-text); font-weight: 600; font-size: 1.1rem; }
/* Huit compteurs : quatre par ligne sur un écran large, deux sur un téléphone, jamais de case vide. */
.cockpit__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; background: var(--line); }
.cockpit__tile { background: var(--surface); padding: 12px 18px 14px; display: grid; grid-template-columns: auto 1fr; align-content: start; align-items: baseline; gap: 0 6px; min-width: 0; }
.cockpit__label { grid-column: 1 / -1; color: var(--muted); font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cockpit__value { font-size: 2.6rem; line-height: 1.1; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cockpit__tile--main .cockpit__value { font-size: 3.4rem; }
.cockpit__unit { color: var(--muted); font-size: 0.95rem; white-space: nowrap; }
@media (max-width: 760px) {
  .cockpit__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cockpit__value { font-size: 2rem; }
  .cockpit__tile--main .cockpit__value { font-size: 2.6rem; }
}

/* Les compteurs et la route vue de côté prennent place dans le bloc de la course, au-dessus et au-dessous de la scène. */
.race__hud, .race__strip { grid-column: 1 / -1; min-width: 0; }
/* Un moment de la séance (une côte qui commence, l'écart à une borne) : annoncé quelques secondes. */
.moment { grid-column: 1 / -1; justify-self: center; display: flex; align-items: baseline; justify-content: center; gap: 2px 14px; flex-wrap: wrap; margin: 0; padding: 9px 24px; border-radius: 999px; background: var(--primary); color: var(--primary-text); text-align: center; animation: moment 0.35s ease-out; }
.moment[hidden] { display: none; }
.moment strong { font-size: 1.35rem; font-weight: 700; }
.moment span { font-size: 1.05rem; }
.moment span:empty { display: none; }
@keyframes moment { from { opacity: 0; transform: translateY(-8px); } }
@media (prefers-reduced-motion: reduce) { .moment { animation: none; } }
/* Les vitesses virtuelles (lib/gears.js) : le braquet du moment, et de quoi en changer, au doigt. Sous les compteurs
   dans la page ; en plein écran, au bord de la scène (voir plus bas). Le plus dur est en haut, ou à droite. */
.gearing { display: grid; gap: 8px; }
.gearing .fields { grid-template-columns: repeat(2, minmax(0, 9rem)); }
.gearbox { grid-column: 1 / -1; display: flex; flex-direction: row-reverse; justify-content: center; align-items: center; gap: 14px; }
.gearbox[hidden] { display: none; }
.gearbox__shift { display: grid; place-items: center; width: 56px; height: 56px; padding: 0; border: 1px solid var(--line-strong); border-radius: 50%; background: var(--surface); color: var(--text); font: 700 1.9rem/1 var(--sans); cursor: pointer; touch-action: manipulation; }
.gearbox__shift:active:not(:disabled) { background: var(--primary); color: var(--primary-text); }
.gearbox__shift:disabled { opacity: 0.35; cursor: default; }
.gearbox__gear { display: grid; justify-items: center; min-width: 76px; margin: 0; }
.gearbox__label, .gearbox__words { color: var(--muted); font-size: 0.8rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.gearbox__number { font-size: 2.2rem; line-height: 1.05; font-variant-numeric: tabular-nums; }
/* La résistance d'une sortie libre : la boîte du braquet, et ses trois modes (la route, une pente, une puissance).
   Le mode en cours se lit plein, et son nom est écrit : la couleur n'y est pas. */
.resist__modes { display: flex; gap: 4px; }
.resist__mode { padding: 7px 11px; border: 1px solid var(--line-strong); border-radius: 999px; background: var(--surface); color: var(--text); font: 600 0.85rem/1 var(--sans); cursor: pointer; touch-action: manipulation; }
.resist__mode.is-current { background: var(--primary); border-color: var(--primary); color: var(--primary-text); }
.resist__mode:disabled { opacity: 0.35; cursor: default; }
/* L'arrivée d'une séance, posée sur la scène restée en plein écran : le temps en grand, ce qu'il vaut, la suite.
   Elle entre en scène après la caméra, qui passe d'abord devant le coureur. Dans la page, le résultat a sa carte. */
.finale { display: none; }
.race.is-immersive .finale:not([hidden]) { grid-area: 1 / 1 / 3 / 2; z-index: 4; align-self: end; justify-self: center; display: grid; justify-items: center; gap: 3px; width: min(540px, calc(100% - 28px)); margin: 0 0 16px; padding: 16px 24px 18px; border: 1px solid var(--line); border-radius: 16px; background: color-mix(in srgb, var(--surface) 90%, transparent); backdrop-filter: blur(10px); box-shadow: 0 6px 24px rgb(0 0 0 / 25%); text-align: center; animation: finale 0.6s 1.6s ease-out both; }
.finale p, .finale dl, .finale dd { margin: 0; }
.finale__title { color: var(--muted); font-size: 0.9rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; }
.finale__time { font-size: clamp(2.6rem, 7vw, 4.4rem); line-height: 1.05; font-weight: 750; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.finale__verdict { font-size: 1.15rem; font-weight: 650; }
.finale__best { color: var(--muted); }
/* Les segments franchis au fil d'un parcours, à l'arrivée d'une séance : un par ligne, son temps en regard. */
.crossed { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.finale__best:empty, .finale__status:empty { display: none; }
.finale__facts { display: flex; justify-content: center; gap: 6px 26px; flex-wrap: wrap; margin-top: 8px; }
.finale__facts dt { color: var(--muted); font-size: 0.8rem; }
.finale__facts dd { font-size: 1.15rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.finale__status { margin-top: 6px; color: var(--muted); font-size: 0.88rem; }
.finale__actions { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
@keyframes finale { from { opacity: 0; transform: translateY(14px); } }
/* La photo d'arrivée : sa vignette, et de quoi la partager ou l'enregistrer. */
.photo { display: grid; gap: 8px; justify-items: center; margin-top: 10px; }
.photo[hidden] { display: none; }
.photo__image { width: min(100%, 360px); aspect-ratio: 16 / 9; border-radius: 10px; border: 1px solid var(--line); object-fit: cover; background: var(--surface-2, var(--line)); }
.photo__image--tall { width: auto; height: min(300px, 34vh); aspect-ratio: 4 / 5; } /* prise sur un téléphone tenu droit */
.finale .photo__image--tall { height: min(170px, 20vh); } /* le tableau d'arrivée doit tenir sous le coureur */
.photo__actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.card .photo { justify-items: start; }
.card .photo__actions { justify-content: start; }
@media (prefers-reduced-motion: reduce) { .race.is-immersive .finale:not([hidden]) { animation: none; } }
/* Dans une fenêtre large, ou basse (un téléphone couché), le tableau se range sur le côté gauche, le classement
   restant à droite : le coureur garde le milieu de la scène. Ailleurs il s'étale au pied de la scène, et la
   caméra cadre le coureur au-dessus de lui (voir `shade`, dans le contrôleur `ride`). */
@media (min-width: 900px), (max-height: 520px) {
  .race.is-immersive .finale:not([hidden]) { align-self: center; justify-self: start; width: min(340px, 44%); margin: 0 0 0 max(16px, env(safe-area-inset-left)); }
}
@media (max-height: 520px) {
  .race.is-immersive .finale:not([hidden]) { padding: 10px 14px 12px; }
  .finale__time { font-size: 2.2rem; }
  .finale__verdict { font-size: 1rem; }
  .finale__facts { display: none; }
  .finale .photo__image { display: none; }
}
.ridebar { overflow: hidden; }
.ridebar__head { display: flex; align-items: baseline; justify-content: space-between; gap: 2px 16px; flex-wrap: wrap; padding: 9px 16px 0; font-size: 0.88rem; }
.ridebar__controls { display: flex; align-items: center; justify-content: flex-end; gap: 8px 14px; flex-wrap: wrap; padding: 9px 16px; border-top: 1px solid var(--line); }
.ridebar__tryout { display: grid; gap: 4px; flex: 1; min-width: 220px; margin-right: auto; font-size: 0.9rem; color: var(--muted); }
.race:not(.is-immersive) .ridebar__controls .tabs { display: none; } /* dans la page, les vues se choisissent juste dessous */

/* La route vue de côté (lib/sideview.js) : le relief sur un canevas, les coureurs posés dessus. Le point de la
   route est l'origine de chaque coureur ; son écart s'écrit dessous. */
.sideview { position: relative; height: 150px; overflow: hidden; }
.sideview canvas { display: block; width: 100%; height: 100%; }
.sideview__rider { position: absolute; top: 0; left: 0; width: 0; height: 0; will-change: transform; }
.sideview__rider .runner { position: absolute; top: -13px; left: -13px; }
.sideview__rider.is-away { opacity: 0.7; }
.sideview__note { position: absolute; top: 7px; left: 0; transform: translateX(-50%); padding: 0 6px; border: 1px solid var(--line-strong); border-radius: 9px; background: var(--surface); font-size: 0.78rem; font-weight: 650; line-height: 1.5; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sideview__note--left { left: -12px; transform: none; }
.sideview__note--right { left: 12px; transform: translateX(-100%); }

/* Pendant qu'on roule, la course occupe toute la fenêtre : la scène, les compteurs posés sur son ciel, le classement
   sur son côté, la route vue de côté à son pied. Les commandes de la carte se rangent sous les compteurs, dont
   la séance donne la hauteur (--hud). */
html.is-riding { overflow: hidden; }
.race.is-immersive { position: fixed; inset: 0; z-index: 1000; gap: 0; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; background: var(--bg); }
.race.is-immersive .race__stage { grid-area: 1 / 1 / 3 / 2; display: grid; min-height: 0; }
.race.is-immersive .race__map { height: auto; min-height: 0; border: 0; border-radius: 0; }
.race.is-immersive .race__hud { grid-area: 1 / 1; z-index: 2; border: 0; border-radius: 0; box-shadow: none; background: color-mix(in srgb, var(--surface) 78%, transparent); backdrop-filter: blur(8px); }
.race.is-immersive .race__side { grid-area: 2 / 1; z-index: 2; justify-self: end; align-self: start; width: 290px; max-height: calc(100% - 46px); margin: 10px; background: color-mix(in srgb, var(--surface) 86%, transparent); backdrop-filter: blur(8px); }
.race.is-immersive .board__hint { display: none; }
/* Dans l'application, la séance prend tout l'écran : la scène file sous l'encoche et les bords arrondis, les
   compteurs, le classement et les commandes restent en deçà. */
.is-native .race.is-immersive .race__hud { padding: env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left); }
.is-native .race.is-immersive .race__strip { padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
.is-native .race.is-immersive .race__side { margin-right: calc(10px + env(safe-area-inset-right)); }
.race.is-immersive .race__main:has(.leaflet-container:not([hidden])) ~ .race__side { margin-top: 54px; } /* sous le sélecteur de fond */
/* En plein écran, la carte redevient une case de la grille, et les commandes de lecture s'effacent : la séance a les siennes. */
.race.is-immersive .race__main { display: contents; }
.race.is-immersive .transport { display: none; }
.race.is-immersive .moment { grid-area: 2 / 1; z-index: 3; align-self: start; margin-top: 14px; max-width: calc(100% - 28px); box-shadow: 0 2px 10px rgb(0 0 0 / 30%); }
/* Les vitesses virtuelles, à portée de pouce : dans le coin en bas à gauche de la scène, le seul où rien ne se pose
   (les annonces sont en haut, les commandes des cartes en haut à gauche, leurs mentions en bas à droite, le
   classement à droite). Au milieu du bord, une annonce cachait le bouton + sur un téléphone tenu droit. */
.race.is-immersive .gearbox { grid-area: 2 / 1; z-index: 3; justify-self: start; align-self: end; flex-direction: column; gap: 8px; margin: 0 0 12px 12px; padding: 10px 8px; border-radius: 999px; background: color-mix(in srgb, var(--surface) 82%, transparent); backdrop-filter: blur(8px); box-shadow: 0 2px 10px rgb(0 0 0 / 25%); }
.is-native .race.is-immersive .gearbox { margin-left: calc(12px + env(safe-area-inset-left)); }
.race.is-immersive .resist { border-radius: 22px; }
.race.is-immersive .resist__modes { flex-direction: column; }
.race.is-immersive .race__strip { grid-area: 3 / 1; border: 0; border-top: 1px solid var(--line); border-radius: 0; box-shadow: none; }
.race.is-immersive .leaflet-top { top: var(--hud, 0); }
.race.is-immersive .scene__zoom { top: calc(var(--hud, 0px) + 10px); }
.race.is-immersive .sideview { height: clamp(112px, 19vh, 180px); }
/* Une séance guidée, au pied de la scène à la place de la route vue de côté : un bloc par palier, le trait du
   seuil, ce qui est fait en plus sombre jusqu'au curseur (lib/workout.js). En noir et gris. */
.planstrip { height: 96px; padding: 8px 16px 10px; }
.planstrip__svg { display: block; width: 100%; height: 100%; }
.planstrip__todo { fill: var(--line-strong); }
.planstrip__done { fill: var(--muted); }
.planstrip__ftp { stroke: var(--text); stroke-width: 1; stroke-dasharray: 4 4; vector-effect: non-scaling-stroke; }
.planstrip__cursor { stroke: var(--text); stroke-width: 2; vector-effect: non-scaling-stroke; }
.race.is-immersive .planstrip { height: clamp(80px, 14vh, 130px); }
.race.is-immersive .cockpit__banner { font-size: 1.35rem; text-align: center; }
/* Huit compteurs sur une ligne ; le dernier, plus large, loge le nom de celui à qui l'on se mesure. */
.race.is-immersive .cockpit__grid { grid-template-columns: repeat(7, minmax(0, 1fr)) minmax(0, 1.5fr); gap: 0; background: none; }
@media (min-width: 901px) {
  .race.is-immersive .cockpit__grid:has([data-ride-target="shelterTile"]:not([hidden])) { grid-template-columns: repeat(8, minmax(0, 1fr)) minmax(0, 1.5fr); } /* un compteur de plus : l'abri */
  .race.is-immersive .cockpit__grid:has([data-ride-target="windTile"]) { grid-template-columns: repeat(8, minmax(0, 1fr)) minmax(0, 1.5fr); } /* ou le vent */
  .race.is-immersive .cockpit__grid:has([data-ride-target="windTile"]):has([data-ride-target="shelterTile"]:not([hidden])) { grid-template-columns: repeat(9, minmax(0, 1fr)) minmax(0, 1.5fr); } /* ou les deux */
}
.race.is-immersive .cockpit__tile { padding: 8px 12px 10px; background: none; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.race.is-immersive .cockpit__label { font-size: 0.8rem; }
.race.is-immersive .cockpit__value { font-size: clamp(1.5rem, 2.9vw, 2.9rem); }
.race.is-immersive .cockpit__tile--main .cockpit__value { font-size: clamp(1.8rem, 3.6vw, 3.6rem); }
@media (max-width: 900px) {
  .race.is-immersive .race__side { display: none; }
  .race.is-immersive .cockpit__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .race.is-immersive .cockpit__tile { padding: 6px 10px 8px; }
  .race.is-immersive .cockpit__value { font-size: clamp(1.25rem, 5.6vw, 2.2rem); }
  .race.is-immersive .cockpit__tile--main .cockpit__value { font-size: clamp(1.4rem, 6.4vw, 2.6rem); }
  .race.is-immersive .cockpit__unit { font-size: 0.8rem; }
}
/* Sur un téléphone, la légende de la route vue de côté laisse sa place à la scène : on la lit avant de partir. */
@media (max-width: 900px) {
  .race.is-immersive .ridebar__head [data-ride-target="caption"] { display: none; }
}
/* Un téléphone couché : peu de hauteur. Les compteurs tiennent sur une ligne, la route vue de côté se tasse et les
   commandes tiennent sur une rangée, pour laisser la scène se voir. Les boutons « Réduire » et « Arrêter » restent
   à l'écran : ce sont les vues qui défilent. Avant, les compteurs recouvraient toute la scène, et ces boutons
   sortaient de l'écran. */
@media (max-height: 520px) and (orientation: landscape) {
  .race.is-immersive .cockpit__grid { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  .race.is-immersive .cockpit__tile { padding: 4px 8px 5px; }
  .race.is-immersive .cockpit__label { font-size: 0.68rem; }
  .race.is-immersive .cockpit__value, .race.is-immersive .cockpit__tile--main .cockpit__value { font-size: clamp(1.05rem, 3vw, 1.7rem); }
  .race.is-immersive .cockpit__unit { font-size: 0.68rem; }
  .race.is-immersive .cockpit__banner { padding: 4px 12px; font-size: 1rem; }
  .race.is-immersive .moment { margin-top: 6px; padding: 5px 16px; }
  .race.is-immersive .moment strong { font-size: 1.05rem; }
  .race.is-immersive .moment span { font-size: 0.9rem; }
  .race.is-immersive .sideview { height: 68px; }
  .race.is-immersive .gearbox { flex-direction: row-reverse; gap: 6px; margin-bottom: 8px; padding: 5px; } /* couchée : sous le zoom des cartes */
  .race.is-immersive .resist__modes { flex-direction: row; }
  .race.is-immersive .gearbox__shift { width: 46px; height: 46px; font-size: 1.6rem; }
  .race.is-immersive .gearbox__number { font-size: 1.5rem; }
  .race.is-immersive .gearbox__label { display: none; }
  .race.is-immersive .ridebar__head { padding: 3px 12px 0; font-size: 0.8rem; }
  .race.is-immersive .ridebar__controls { flex-wrap: nowrap; justify-content: flex-start; padding: 5px 12px; }
  .race.is-immersive .ridebar__controls > * { flex: none; }
  .race.is-immersive .ridebar__controls .tabs { flex: 0 1 auto; flex-wrap: nowrap; min-width: 110px; margin-left: auto; overflow-x: auto; scrollbar-width: none; }
  .race.is-immersive .ridebar__controls .tabs::-webkit-scrollbar { display: none; }
  .race.is-immersive .ridebar__controls .tab { flex: none; white-space: nowrap; }
  .race.is-immersive .ridebar__tryout { flex: 0 1 190px; min-width: 120px; margin-right: 0; }
}

/* La rencontre : se comparer à ceux qui sont là, en rapprochant les téléphones. Les ondes disent que le téléphone
   se signale autour de lui ; la liste, qui est là ; le code QR reste noir sur blanc quel que soit le thème, sans
   quoi un appareil photo ne le lit pas. */
.meet { display: grid; gap: 8px; justify-items: center; text-align: center; padding-top: 24px; }
.meet__waves { position: relative; display: grid; place-items: center; width: 60px; height: 60px; margin-bottom: 4px; }
.meet__waves::after { content: ""; width: 12px; height: 12px; border-radius: 50%; background: var(--text); }
.meet__waves span { position: absolute; inset: 0; border: 2px solid var(--text); border-radius: 50%; opacity: 0; animation: ripple 2.4s ease-out infinite; }
.meet__waves span:nth-child(2) { animation-delay: 0.8s; }
.meet__waves span:nth-child(3) { animation-delay: 1.6s; }
@keyframes ripple {
  0% { transform: scale(0.2); opacity: 0.7; }
  100% { transform: scale(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .meet__waves span { animation: none; opacity: 0.22; }
  .meet__waves span:nth-child(2) { inset: 10px; }
  .meet__waves span:nth-child(3) { inset: 19px; }
}
.meet__list { list-style: none; margin: 0; padding: 0; display: grid; }
.meet__list + .card__body { border-top: 1px solid var(--line); }
.meet__who { display: flex; align-items: center; gap: 6px 10px; flex-wrap: wrap; min-height: 50px; padding: 8px 20px; }
.meet__who + .meet__who { border-top: 1px solid var(--line); }
.meet__act { margin: 0 0 0 auto; }
.meet__list--near { width: 100%; margin-top: 8px; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; text-align: left; }
.meet__list--near .meet__who { padding: 8px 12px; }
.meet__who.is-close { background: var(--surface-2); }
/* Le code à montrer : le QR et les six caractères côte à côte, ce qu'il faut en savoir dessous. */
.meet__pass { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px 22px; align-items: center; }
.meet__id { display: grid; gap: 6px; }
.meet__ask { font-size: 1rem; font-weight: 650; line-height: 1.35; }
.meet__about { grid-column: 1 / -1; display: grid; gap: 10px; }
.qr { flex: none; border-radius: 8px; }
.meet__code { font-size: 2rem; font-weight: 700; letter-spacing: 0.12em; line-height: 1.1; }
/* Seul sur l'écran : on attend qu'un autre arrive. Le point bat doucement ; rien ne bouge si le système demande
   moins d'animations. */
.meet__waiting { display: flex; align-items: baseline; gap: 8px; padding-top: 10px; border-top: 1px solid var(--line); font-size: 0.92rem; text-align: left; }
.meet__pulse { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--text); animation: waiting 1.6s ease-in-out infinite; }
@keyframes waiting { 50% { opacity: 0.25; } }
/* Le téléphone en relais (lib/relay.js) : sur la page de séance, ce qu'il en est et comment le mettre en service ;
   sur le téléphone, sa page (à qui il sert, ce qu'il fait passer, la demande d'un appareil à choisir). */
.relay { display: grid; gap: 8px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius); }
.relay[hidden] { display: none; }
.relay.is-on { border-color: var(--text); }
.relay__status { display: flex; align-items: center; gap: 8px; margin: 0; }
.relay .row:not(:has(.btn:not([hidden]))) { display: none; }
.relay__how { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; padding-top: 8px; }
.relay__how > div { flex: 1 1 16rem; }
.relay__state { display: flex; align-items: center; gap: 10px; margin: 0 0 6px; font-size: 1.15rem; }
.relay__mark { flex: none; width: 12px; height: 12px; border: 2px solid var(--muted); border-radius: 50%; }
.relay__mark[data-state="ready"] { border-color: var(--text); }
.relay__mark[data-state="on"] { border-color: var(--text); background: var(--text); animation: waiting 1.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .relay__mark[data-state="on"] { animation: none; } }
.relay__ask { display: grid; gap: 10px; margin-top: 14px; padding: 14px; border-radius: var(--radius); background: var(--surface-2); }
.relay__ask[hidden] { display: none; }
.relay__ask p { margin: 0; }
.relay__devices { display: grid; margin: 14px 0 0; padding: 0; list-style: none; }
.relay__devices[hidden] { display: none; }
.relay__device { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 12px; padding: 10px 0; border-top: 1px solid var(--line); }
.relay__steps { display: grid; gap: 8px; margin: 0 0 12px; padding-left: 20px; }
@media (prefers-reduced-motion: reduce) { .meet__pulse { animation: none; } }
.meet__entry { text-transform: uppercase; letter-spacing: 0.1em; font-weight: 600; }
.meet__entry::placeholder { font-weight: 400; }
.meet__times { list-style: none; margin: 4px 0 2px; padding: 0; display: grid; gap: 3px; max-width: 300px; font-size: 0.92rem; }
.meet__times li { display: flex; align-items: center; gap: 8px; }
.meet__times strong { margin-left: auto; }
/* L'écart au premier, en petit, dans une colonne de même largeur pour que les temps restent alignés. */
.meet__gap { min-width: 4.6em; color: var(--muted); font-size: 0.82rem; text-align: right; }
/* Le bilan d'une rencontre : une ligne par présent, le nombre de segments gagnés en face. */
.tally-board { margin: 0; }
.tally-board .feed__row { align-items: center; }
progress.meter { display: block; width: 100%; height: 6px; accent-color: var(--primary); }
@media (max-width: 640px) {
  .meet__pass { gap: 12px 16px; }
  .meet__pass .qr { width: 148px; height: 148px; }
  .meet__pass .meet__code { font-size: 1.6rem; letter-spacing: 0.08em; }
  .meet__entry { font-size: 16px; } /* en deçà, iOS grossit la page au toucher */
}

/* --- Sorties à plusieurs --------------------------------------------------- */

/* Les maillots : un dessin cerné de la couleur du texte, pour se détacher d'une carte claire comme d'une carte
   sombre. Ils se distinguent sans leur couleur (uni, à pois) et leur nom est toujours écrit à côté. */
.jersey { display: block; flex: none; }
.jersey__cloth { fill: var(--jersey-white); stroke: var(--text); stroke-width: 1.2; stroke-linejoin: round; }
.jersey--yellow .jersey__cloth { fill: var(--jersey-yellow); }
.jersey__dot { fill: var(--jersey-dots); }
.badge--jersey { display: inline-flex; align-items: center; gap: 4px; vertical-align: middle; }
.jersey-head { display: flex; align-items: center; gap: 12px; }

/* Les deux maillots de la sortie, et qui les porte. */
.jerseys { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.jerseys__card { display: flex; align-items: center; gap: 14px; padding: 16px 18px; text-decoration: none; -webkit-tap-highlight-color: transparent; }
.jerseys__card:hover { border-color: var(--line-strong); }
.jerseys__text { display: grid; gap: 1px; min-width: 0; }
.jerseys__name { font-size: 0.82rem; font-weight: 650; text-transform: uppercase; letter-spacing: 0.04em; }
.jerseys__text > .muted { font-size: 0.88rem; }
.jerseys__who { display: flex; flex-wrap: wrap; gap: 2px 12px; margin-top: 5px; font-size: 1.1rem; }
@media (max-width: 760px) {
  .jerseys { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .jerseys__card { padding: 12px 16px; }
}

/* Une sortie dans une liste : quand, qui, et qui porte chaque maillot. Toute la carte se touche. */
.outing { display: flex; align-items: center; gap: 12px 18px; padding: 14px 16px; text-decoration: none; -webkit-tap-highlight-color: transparent; }
.outing:hover { border-color: var(--line-strong); }
.outing:active { background: var(--surface-2); }
.outing__body { flex: 1 1 240px; min-width: 0; display: grid; gap: 3px; }
.outing__title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-weight: 650; line-height: 1.3; }
.outing__mine { font-size: 0.92rem; font-weight: 550; }
.outing__jerseys { flex: 0 1 190px; display: grid; gap: 4px; min-width: 0; margin: 0; padding: 0; list-style: none; font-size: 0.92rem; font-weight: 550; }
.outing__jerseys li { display: flex; align-items: center; gap: 8px; min-width: 0; }
.outing__jerseys li > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* La liste des sorties d'un groupe : un intertitre par mois, avec le nombre de sorties du mois. */
.list__month { display: flex; justify-content: space-between; align-items: baseline; gap: 2px 14px; flex-wrap: wrap; margin: 0 2px -4px; color: var(--muted); font-size: 0.86rem; font-weight: 600; letter-spacing: 0; }
.list__month > span:first-child { color: var(--text); }
.list__month:not(:first-child) { margin-top: 10px; }
/* Le palmarès : une ligne par coureur, ses maillots et ses sorties en colonnes étroites, qui tiennent dans la
   largeur d'un téléphone. Le tableau replié dessous garde les mêmes colonnes. */
.honours__table { table-layout: fixed; }
.honours__col { width: 3.4em; }
.honours__col--rank { width: 2.8em; }
.honours__col--wide { width: 4.8em; }
.honours__table th, .honours__table td { padding-inline: 8px; }
.honours__table th:first-child, .honours__table td:first-child { padding-left: 20px; }
.honours__table th:last-child, .honours__table td:last-child { padding-right: 20px; }
.honours__table th .jersey { vertical-align: middle; }
.honours__table tr.is-me { background: var(--surface-2); }
.honours__rank { color: var(--muted); font-weight: 600; }
.honours__who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.honours__who .chip { display: inline-flex; max-width: 100%; vertical-align: middle; }
.honours__who .chip > span { overflow: hidden; text-overflow: ellipsis; }
.honours .card__foot { font-size: 0.86rem; }
@media (max-width: 640px) {
  .honours__table th:first-child, .honours__table td:first-child { padding-left: 16px; }
  .honours__table th:last-child, .honours__table td:last-child { padding-right: 16px; }
}
.feed .outing { border: 0; border-radius: 0; box-shadow: none; background: none; padding: 12px 20px; }
.feed li + li .outing { border-top: 1px solid var(--line); }
@media (max-width: 640px) {
  .outing { flex-wrap: wrap; position: relative; padding-right: 36px; }
  .outing__jerseys { flex: 1 1 100%; grid-auto-flow: column; grid-auto-columns: minmax(0, max-content); gap: 4px 14px; justify-content: start; }
  .outing > .seg__go { position: absolute; top: 16px; right: 12px; }
  .feed .outing { padding-right: 40px; }
}

/* Un segment de la sortie : ce qu'il est, puis le temps de chacun, le plus rapide d'abord, avec ce qu'il y gagne. */
.leg { padding: 14px 20px 12px; }
.leg + .leg { border-top: 1px solid var(--line); }
.leg__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px 16px; }
.leg__head h3 a { text-decoration: none; }
.leg__head h3 a:hover { text-decoration: underline; }
.leg__head p { margin-top: 2px; font-size: 0.9rem; }
.leg__rows { list-style: none; margin: 10px 0 0; padding: 0; display: grid; }
.leg__row { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto 64px 84px; align-items: center; gap: 4px 12px; padding: 6px 0; }
.leg__row + .leg__row { border-top: 1px solid var(--line); }
.leg__rank { color: var(--muted); font-weight: 600; }
.leg__who { display: flex; align-items: center; gap: 8px; min-width: 0; }
.leg__prizes { grid-column: 3; display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.leg__time, .leg__gap { text-align: right; white-space: nowrap; }
.leg__time { grid-column: 4; }
.leg__gap { grid-column: 5; font-size: 0.88rem; }
/* Sur un téléphone, deux lignes par coureur : son rang, son nom et son temps ; puis ce qu'il gagne, et son écart. */
@media (max-width: 640px) {
  .leg { padding: 12px 16px 10px; }
  .leg__row { grid-template-columns: 22px minmax(0, 1fr) auto; }
  .leg__time { grid-column: 3; grid-row: 1; }
  .leg__prizes { grid-column: 2; grid-row: 2; justify-content: flex-start; }
  .leg__gap { grid-column: 3; grid-row: 2; }
}

/* La photo finish d'un segment pris à plusieurs : la route vue de côté à l'instant où le premier passe la ligne, un
   couloir par coureur. Les silhouettes prennent la couleur du coureur (`ink--N`) ; ses initiales et son nom le disent
   aussi. La bande grisée : une seconde de course devant la ligne, où les montres ne départagent pas. */
.photo { margin: 14px 0 4px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); display: grid; gap: 8px; }
.photo__caption { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; font-size: 0.92rem; }
.photo__strip { width: 100%; height: auto; display: block; }
.photo__strip--wide { max-width: 760px; }
.photo__strip--narrow { display: none; }
@media (max-width: 640px) { .photo__strip--wide { display: none; } .photo__strip--narrow { display: block; } }
.photo__blur { fill: var(--surface-2); }
.photo__lane { stroke: var(--line); stroke-width: 1; }
.photo__line { stroke: var(--text); stroke-width: 3; }
.photo__tick { stroke: var(--muted); stroke-width: 1; }
.photo__label { fill: var(--muted); font-size: 11px; }
.photo__rider path { fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.photo__wheel { fill: none; stroke: currentColor; stroke-width: 2; }
.photo__head { fill: currentColor; }
.photo__tag circle { fill: currentColor; }
.photo__tag text { font-size: 10px; font-weight: 700; }
.photo__tag.ink--1 text { fill: var(--athlete-1-ink); }
.photo__tag.ink--2 text { fill: var(--athlete-2-ink); }
.photo__tag.ink--3 text { fill: var(--athlete-3-ink); }
.photo__tag.ink--4 text { fill: var(--athlete-4-ink); }
.photo__tag.ink--5 text { fill: var(--athlete-5-ink); }
.photo__tag.ink--6 text { fill: var(--athlete-6-ink); }
.photo__tag.ink--7 text { fill: var(--athlete-7-ink); }
.photo__tag.ink--8 text { fill: var(--athlete-8-ink); }
.photo__order { list-style: none; margin: 0; padding: 0; display: grid; }
.photo__place { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; align-items: center; gap: 4px 12px; padding: 4px 0; }
.photo__place + .photo__place { border-top: 1px solid var(--line); }
.photo__gap { text-align: right; white-space: nowrap; font-size: 0.88rem; }
.photo__note { font-size: 0.82rem; }
@media (max-width: 640px) { .photo { padding: 10px; } }

/* Ce que récompense chaque maillot. */
.rules { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.rules li { display: flex; align-items: center; gap: 12px; }

/* Les badges (`Badges`, `medal`) : des médaillons en noir et gris, la couleur restant aux coureurs et aux maillots.
 * Gagné : plein et cerné ; à gagner : en pointillé. Le niveau se lit au nombre de liserés, et s'écrit à côté. */
.medal { --ring: 2px; display: inline-flex; align-items: center; justify-content: center; gap: 1px; width: 48px; height: 48px; margin: 7px; border: var(--ring) solid var(--text); border-radius: 50%; background: var(--surface-2); color: var(--text); font-size: 1.05rem; font-weight: 750; letter-spacing: -0.03em; flex: none; }
.medal--l2 { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--text); }
.medal--l3 { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--text), 0 0 0 5px var(--surface), 0 0 0 6.5px var(--text); }
.medal--l4 { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--text), 0 0 0 5px var(--surface), 0 0 0 6.5px var(--text), 0 0 0 8px var(--surface), 0 0 0 9.5px var(--text); margin: 10px; }
.medal.is-locked { border-style: dashed; border-color: var(--line-strong); background: transparent; color: var(--muted); }
.medal.is-locked .jersey { opacity: 0.4; filter: grayscale(1); }
.medal--small { --ring: 1.5px; width: 28px; height: 28px; margin: 5px; font-size: 0.68rem; }
.medal--small.medal--l2 { box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.5px var(--text); }
.medal--small.medal--l3 { box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.5px var(--text), 0 0 0 4px var(--surface), 0 0 0 5px var(--text); }
.medal--small.medal--l4 { box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.5px var(--text), 0 0 0 4px var(--surface), 0 0 0 5px var(--text), 0 0 0 6.5px var(--surface), 0 0 0 7.5px var(--text); margin: 8px; }
.awards { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 16px 24px; margin: 0; padding: 16px 14px; list-style: none; }
.award { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
.award__text { display: grid; gap: 2px; min-width: 0; flex: 1; padding-top: 6px; }
.award__name { font-weight: 650; line-height: 1.3; }
.award__state, .award__left { color: var(--muted); font-size: 0.86rem; }
.award__deed { font-size: 0.92rem; }
.award.is-locked .award__name { font-weight: 550; }
.medal-meter { display: block; width: 100%; height: 6px; margin-top: 6px; border-radius: 3px; color: var(--text); }
/* La page des badges : la part gagnée, sous le titre ; les badges pas encore commencés, repliés en fin de carte,
   une ligne chacun, leur médaillon en petit. */
.medal-meter--head { max-width: 320px; margin-top: 10px; }
.card__count { margin-left: 6px; color: var(--muted); font-size: 0.86rem; font-weight: 500; letter-spacing: 0; }
.card__head + .card__foot { border-top: 0; padding-top: 8px; } /* rien entre l'en-tête et le pied : un seul filet */
.awards__later > summary { margin-left: 0; }
.awards--compact { gap: 6px 24px; padding: 8px 0 4px; }
.awards--compact .award { align-items: center; }
.awards--compact .award__text { padding-top: 0; gap: 0; }
.awards--compact .award__deed { color: var(--muted); font-size: 0.86rem; }
.medals-brief { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px 28px; padding-left: 14px; padding-right: 14px; }
.medals-brief__title { margin: 0 0 4px 7px; color: var(--muted); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.medal-list { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.medal-list li { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
.medal-list--wide { grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 12px 24px; padding-left: 14px; padding-right: 14px; }
.medal-row { align-items: center; }
.medal-row > span:first-child { flex: none; }
.medal-strip { display: flex; flex-wrap: wrap; justify-content: flex-end; margin: 0; padding: 0; list-style: none; }
turbo-frame:empty { display: none; } /* un cadre qui n'a rien à montrer ne prend pas de place dans une pile */
/* L'annonce d'un badge juste gagné (`badge_announcement`), en tête de page. */
.news { display: grid; gap: 6px; margin-bottom: 18px; padding: 12px 14px 12px 10px; border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow); }
.news__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-left: 7px; }
.news__title { font-size: 0.78rem; font-weight: 650; letter-spacing: 0.04em; text-transform: uppercase; }
.news__list { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 4px 20px; }
.news__foot { margin: 0; padding-left: 7px; font-size: 0.9rem; }
.news__losses { display: grid; gap: 4px; margin: 0; padding: 2px 0 2px 7px; list-style: none; font-size: 0.95rem; }
.news__losses .chip { vertical-align: baseline; font-weight: 600; }
