/* Jetons — source unique de la palette. Toute retouche visuelle se fait ICI,
   pas dans les gabarits.

   Direction « Plan d'eau, route, piste », retenue par Jo le 2026-10-08
   (§interface) ; elle remplace « Verre ». Un sol clair gris-vert, l'eau calme
   au petit matin ; **les seules couleurs saturées sont celles des sports** —
   nage sarcelle, vélo bleu route, course orange piste, muscu violet. Pas
   d'accent séparé : l'accent est l'encre, et un bouton de séance prend la
   couleur de son sport. Clair par défaut, sombre selon le système, et le
   choix manuel à trois états reste (script en ligne de `base.html`).

   Ce qui ne bouge pas, et pourquoi :
   1. Le flou reste ÉTEINT (`--flou:none`), mesuré le 2026-10-07 : sur /plan,
      77 images sur 189 au-delà de 20 ms avec lui, zéro sans. Bloc
      « Finition ».
   2. `--surface-creux` existe parce que `--surface-2` avance dans un thème et
      recule dans l'autre : ce qui doit RECULER se code à part.
   3. `--sur-accent` est le texte posé SUR l'accent : en sombre, l'accent est
      clair, et un texte blanc y disparaîtrait. */
:root{
  color-scheme: light;
  --page:#f2f4f3; --fond:none;
  --surface:#ffffff; --surface-2:#e8edeb; --surface-creux:#eaeeec;
  --ink:#16202a; --ink-2:#3d4955; --muted:#5f6b75;
  --line:rgba(22,32,42,.12); --line-strong:rgba(22,32,42,.24);
  --accent:#16202a; --accent-soft:rgba(22,32,42,.07); --sur-accent:#ffffff;
  --s1:#2f55d4;  /* vélo */ --s2:#e0552f;  /* course */
  --s3:#0f8b8d;  /* nage */ --s4:#7a5ac2;  /* muscu */
  /* Les distances (cartes du questionnaire, 2026-10-22) : ni un sport, ni une zone. */
  --d-sprint:#c4306a; --d-olympique:#a87b0f; --d-703:#73308f;
  --good:#1f7a4f; --warn:#a8741a; --crit:#c2412f;
  --grid:rgba(22,32,42,.08);
  --shadow:0 1px 0 rgba(22,32,42,.04);
  --lueur:0 0 0 transparent;
  --ring:rgba(47,85,212,.35);
  --flou:none;
  --rayon:10px; --rayon-2:6px;
  /* Les chiffres : Barlow Semi Condensed à chiffres FIXES (`tnum`) — le
     vocabulaire des dossards et de la signalisation routière. Les colonnes
     s'alignent sans police à chasse fixe. Le texte : Public Sans. */
  --chiffres:"Barlow Semi Condensed", system-ui, sans-serif;
  --texte:"Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    color-scheme: dark;
    --page:#0f1518; --fond:none;
    --surface:#172126; --surface-2:#1f2b31; --surface-creux:#121a1e;
    --ink:#e6ecea; --ink-2:#b5c1c3; --muted:#8c999d;
    --line:rgba(230,236,234,.10); --line-strong:rgba(230,236,234,.20);
    --accent:#e6ecea; --accent-soft:rgba(230,236,234,.08); --sur-accent:#0f1518;
    --s1:#7f9cf2; --s2:#f2896a; --s3:#3fc1c2; --s4:#b39be8;
    --d-sprint:#f06a98; --d-olympique:#e6b84e; --d-703:#bf86e0;
    --good:#6fcf9f; --warn:#e6b45a; --crit:#f08a7a;
    --grid:rgba(230,236,234,.07);
    --shadow:0 1px 0 rgba(0,0,0,.30);
    --lueur:0 0 0 transparent;
    --ring:rgba(127,156,242,.45);
    --flou:none;
  }
}
:root[data-theme="dark"]{
  color-scheme: dark;
  --page:#0f1518; --fond:none;
  --surface:#172126; --surface-2:#1f2b31; --surface-creux:#121a1e;
  --ink:#e6ecea; --ink-2:#b5c1c3; --muted:#8c999d;
  --line:rgba(230,236,234,.10); --line-strong:rgba(230,236,234,.20);
  --accent:#e6ecea; --accent-soft:rgba(230,236,234,.08); --sur-accent:#0f1518;
  --s1:#7f9cf2; --s2:#f2896a; --s3:#3fc1c2; --s4:#b39be8;
  --d-sprint:#f06a98; --d-olympique:#e6b84e; --d-703:#bf86e0;
  --good:#6fcf9f; --warn:#e6b45a; --crit:#f08a7a;
  --grid:rgba(230,236,234,.07);
  --shadow:0 1px 0 rgba(0,0,0,.30);
  --lueur:0 0 0 transparent;
  --ring:rgba(127,156,242,.45);
  --flou:none;
}

*{box-sizing:border-box}
body{
  margin:0; color:var(--ink);
  background:var(--page);
  background-image:var(--fond);
  background-attachment:fixed;
  background-repeat:no-repeat;
  font-family:var(--texte);
  font-size:15px; line-height:1.5;
}

/* 1480 : la barre du tableau de bord tient sur une ligne. Mesuré le 2026-09-22
   dans Chrome, chaque élément à sa largeur naturelle : marque 92, date la plus
   longue (« au mercredi 30 septembre 2026 ») 211, thème 48, dix onglets 915,
   « Sortir » 49, plus quatre espaces de 16 = 1 379 px — à loger dans la
   barre, qui a 16 px de rembourrage de chaque côté. À 1440, il manquait 13 px ;
   à 1180, elle s'enroulait sur deux rangées. REMESURÉ le 2026-09-28 en Chrome
   sans tête, avec huit onglets, « Admin » et le bouton « Se déconnecter »
   (127) : une ligne à 1480, deux rangées propres à 1280. */
.enveloppe{max-width:1480px; margin:0 auto; padding:24px 20px 64px}

header.barre{
  display:flex; align-items:center; gap:8px 16px; flex-wrap:wrap;
  padding-bottom:10px; border-bottom:1px solid var(--line); margin-bottom:24px;
}
.marque{
  display:inline-flex; align-items:center; text-decoration:none; color:var(--ink);
}
/* La signature : 184 × 60 dans son repère, le mot y fait 40 — donc une hauteur
   de 30 px donne un mot de 20 px, la taille qu'avait le texte seul. */
.marque .signature{height:30px; width:auto; aspect-ratio:184/60; flex:none; overflow:visible}
.marque .signature text{fill:currentColor; font-family:"Barlow Semi Condensed", system-ui, sans-serif;
  font-weight:700; font-size:46.7px; letter-spacing:0}
.marque .signature .elan{fill:var(--accent)}
header.barre .sous{color:var(--muted); font-size:13px; align-self:center}

/* Les onglets portent une marque de position : dix liens identiques ne
   disent pas où on est. Le trait vit sous le texte et non autour, pour que la
   barre reste une barre et pas une rangée de boutons. */
header.barre nav{margin-left:auto; display:flex; gap:0; flex-wrap:wrap}
header.barre nav a{
  color:var(--ink-2); text-decoration:none; font-size:14px;
  padding:8px 8px 9px; border-radius:7px 7px 0 0;
  border-bottom:2px solid transparent; margin-bottom:-11px; white-space:nowrap;
}
header.barre nav a:hover{color:var(--ink); background:var(--surface-2)}
header.barre nav a.actif{
  color:var(--accent); font-weight:600; border-bottom-color:var(--accent);
}
@media (max-width:900px){
  header.barre{gap:6px 12px}
  header.barre nav{margin-left:0; width:100%; gap:2px}
  /* La barre s'enroule : le trait sous l'onglet actif irait se poser sur la
     rangée du dessous. Sur plusieurs rangées, c'est une pastille qui marque la
     position, pas un soulignement. */
  header.barre nav a{
    padding:6px 9px; font-size:13.5px; margin-bottom:0;
    border-bottom:none; border-radius:7px;
  }
  header.barre nav a.actif{background:var(--accent-soft)}
  header.barre .sous{order:3; width:100%}
}

/* 150 px ne tiennent pas une valeur en mono de 28 px : « 41,89 km » et
   « 1045 kcal » se coupaient en deux au milieu de leur carte. */
.chiffres{display:grid; grid-template-columns:repeat(auto-fit,minmax(178px,1fr)); gap:12px; margin-bottom:24px}
/* La fiche d'une activité en porte jusqu'à huit : au-delà de quatre colonnes,
   la dernière rangée n'en garde qu'une, seule au bout d'une ligne vide. */
.chiffres.large{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.carte{
  background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:14px 16px; box-shadow:var(--shadow);
}
.carte .etiquette{font-size:11px; color:var(--muted)}
.carte .valeur{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:28px; font-weight:600; line-height:1.15; margin-top:4px;
  font-variant-numeric:tabular-nums;
}
.carte .note{font-size:12px; color:var(--muted); margin-top:2px}
.positif{color:var(--good)} .negatif{color:var(--crit)}

table.activites{width:100%; border-collapse:collapse; font-size:14px}
table.activites th{
  text-align:left; font-size:11px;
  color:var(--muted); font-weight:600; padding:8px 10px; border-bottom:1px solid var(--line-strong);
}
table.activites td{padding:9px 10px; border-bottom:1px solid var(--line)}
table.activites tbody tr:hover{background:var(--surface-2)}
table.activites .num{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  text-align:right; font-variant-numeric:tabular-nums;
}
.sport{display:inline-flex; align-items:center; gap:6px}
.sport::before{content:""; width:8px; height:8px; border-radius:2px; background:var(--muted)}
.sport.velo::before{background:var(--s1)}
.sport.course::before{background:var(--s2)}
.sport.nage::before{background:var(--s3)}
.sport.muscu::before{background:var(--s4)}

/* Une activité écartée par la déduplication : visible, mais clairement hors charge. */
tr.hors-charge{opacity:.5}
tr.hors-charge td:first-child::after{
  content:"doublon"; margin-left:8px; font-size:10px; color:var(--warn); border:1px solid var(--warn);
  border-radius:3px; padding:1px 4px;
}

.section{margin-top:32px}
.section h2{font-size:13px; color:var(--muted); margin:0 0 12px}

.vide{
  background:var(--surface); border:1px dashed var(--line-strong); border-radius:10px;
  padding:32px; text-align:center; color:var(--muted);
}
.vide code{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:13px;
  background:var(--surface-2); padding:2px 6px; border-radius:4px; color:var(--ink-2);
}
.avertissement{
  font-size:12px; color:var(--muted); margin-top:8px; padding-left:10px;
  border-left:2px solid var(--line-strong);
}

/* ═══════════════════ Musculation ═══════════════════
   L'écran de séance se manipule d'une main, en sueur. Cibles tactiles
   généreuses, chiffres en mono, rien de superflu entre deux séries. */

.carte-prochaine{
  display:grid; gap:4px; background:var(--surface); border:1px solid var(--line);
  border-left:3px solid var(--accent); border-radius:10px; padding:18px 20px;
  box-shadow:var(--shadow); text-decoration:none; color:inherit; margin-bottom:28px;
}
.carte-prochaine:hover{border-color:var(--accent)}
.carte-prochaine .etiquette{font-size:11px; color:var(--muted); font-weight:600}
.carte-prochaine .titre{font-size:22px; font-weight:600; letter-spacing:-.01em}
.carte-prochaine .action{font-size:13px; color:var(--accent); font-weight:500}

.grille-charges{display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:10px}
.charge{background:var(--surface); border:1px solid var(--line); border-radius:8px;
  padding:11px 13px; display:grid; gap:2px}
.charge .nom{font-size:12.5px; color:var(--ink-2); line-height:1.3}
.charge .poids{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:21px;
  font-weight:600; font-variant-numeric:tabular-nums}
.charge .poids small{font-size:11px; color:var(--muted); margin-left:2px; font-weight:400}
.charge .source{font-size:10px; color:var(--muted)}

.entete-seance{display:flex; align-items:flex-start; gap:16px; flex-wrap:wrap; margin-bottom:18px}
.titre-seance{font-size:24px; font-weight:700; letter-spacing:-.02em; margin:0}
.meta-seance{margin:3px 0 0; color:var(--muted); font-size:13px; display:flex;
  gap:9px; align-items:center; flex-wrap:wrap}
.etiquette-bloc{font-size:10px;
  font-weight:600; padding:2px 8px; border-radius:999px;
  border:1px solid var(--line-strong); color:var(--ink-2)}
.etiquette-bloc.en-cours{color:var(--accent); border-color:var(--accent)}
.retour{margin-left:auto; color:var(--ink-2); text-decoration:none; font-size:14px}
.retour:hover{color:var(--accent)}

.consigne-medicale{
  background:var(--surface-2); border-left:3px solid var(--warn);
  border-radius:0 8px 8px 0; padding:11px 15px; font-size:13px;
  color:var(--ink-2); margin:0 0 20px;
}

.exos{display:flex; flex-direction:column; gap:12px}
.exo{background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:15px 17px; display:flex; flex-direction:column; gap:11px;
  /* Bloc conteneur de la fête du record : le calque se pose dessus sans
     toucher au flux, donc la liste ne saute pas sous le doigt. */
  position:relative}
.exo.active{opacity:.62}
.exo.activation{border-left:3px solid var(--warn)}
.exo header{display:flex; align-items:baseline; gap:12px}
.exo-titre{display:flex; flex-direction:column; gap:1px}
.exo h3{font-size:16px; font-weight:600; margin:0; letter-spacing:-.01em}
.marque-activation{font-size:10.5px;
  color:var(--warn); font-weight:600}
/* Le nom anglais d'un exercice, sous son nom (migration 071) : celui qu'on
   tape pour chercher une vidéo. Secondaire, sur sa propre ligne. */
.nom-anglais{display:block; margin-top:1px; font-family:var(--texte);
  font-size:12px; font-weight:400; line-height:1.3; color:var(--muted)}
.exo .cible{margin-left:auto; font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:13px; color:var(--muted); white-space:nowrap}

.suggestion{margin:0; display:flex; gap:10px; align-items:baseline; flex-wrap:wrap;
  font-size:12.5px; color:var(--ink-2)}
.suggestion b{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:17px;
  color:var(--ink); font-weight:600}
.suggestion span{flex:1; min-width:200px}

.repos-regle{display:flex; gap:6px; margin-left:auto}
.repos-regle button{padding:3px 9px; font-size:12px; line-height:1.5;
  border:1px solid var(--bord); background:transparent; color:inherit;
  border-radius:999px; cursor:pointer; opacity:.75}
.repos-regle button:hover{opacity:1; background:var(--surface-2)}

.exo header .entete-droite{display:flex; align-items:center; gap:10px; flex:none}
.ordre{display:flex; gap:4px}
.ordre button{width:30px; height:30px; padding:0; line-height:1;
  font-size:15px; border:1px solid var(--bord); background:var(--surface-2);
  color:var(--ink-2); border-radius:6px; cursor:pointer}
.ordre button:hover{background:var(--surface); color:var(--ink)}
.ordre button:active{transform:translateY(1px)}

.derniere-fois{margin:0; display:flex; gap:10px; align-items:baseline;
  flex-wrap:wrap; font-size:12.5px; color:var(--ink-2);
  padding:7px 10px; background:var(--surface-2); border-radius:6px}
.derniere-fois .quand{flex:none; color:var(--muted)}
.derniere-fois .quand em{font-style:normal; color:var(--ink-2)}
.derniere-fois b{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:14px;
  color:var(--ink); font-weight:600}
.derniere-fois .rpe-ref{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:12px; color:var(--muted)}

.series-faites{list-style:none; margin:0; padding:0; display:flex;
  flex-direction:column; gap:4px}
.series-faites li{display:flex; align-items:center; gap:10px; font-size:13px;
  padding:5px 9px; background:var(--surface-2); border-radius:6px}
.series-faites .rang{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11px;
  color:var(--muted); width:16px; text-align:center; flex:none}
.series-faites .detail{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-variant-numeric:tabular-nums; color:var(--ink)}

.saisie{display:flex; gap:9px; align-items:flex-end; flex-wrap:wrap}
.saisie label{display:flex; flex-direction:column; gap:3px; font-size:11px; color:var(--muted); font-weight:600}
.saisie label small{text-transform:none; letter-spacing:0; font-weight:400}
.saisie input{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:17px;
  width:86px; padding:9px 10px; border:1px solid var(--line-strong);
  border-radius:7px; background:var(--surface-2); color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.saisie input:focus, .form-fin input:focus, .form-couper input:focus{
  outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent)}
.saisie button{
  margin-left:auto; font-family:inherit; font-size:14px; font-weight:600;
  padding:11px 20px; min-height:44px; border:none; border-radius:8px;
  background:var(--accent); color:var(--sur-accent); cursor:pointer;
}
.saisie button:hover{filter:brightness(1.08)}
.saisie button .sur{opacity:.65; font-weight:400}

.exo-fini{margin:0; display:flex; align-items:center; gap:12px; font-size:13px;
  color:var(--good); font-weight:500}
.lien-discret{background:none; border:none; color:var(--muted); font-size:12px;
  text-decoration:underline; cursor:pointer; font-family:inherit; padding:4px}
.lien-discret:hover{color:var(--crit)}
/* « + / − série » : pas une destruction, donc pas le rouge au survol. */
.series-plus-moins{margin:6px 0 0; display:flex; justify-content:flex-end; gap:6px}
.series-plus-moins .lien-discret:hover{color:var(--ink)}
.serie-de-plus{font-family:inherit; font-size:13px; font-weight:600; cursor:pointer;
  padding:8px 14px; min-height:40px; border-radius:8px;
  border:1px solid var(--accent); background:none; color:var(--accent)}
.serie-de-plus:hover{background:var(--accent); color:var(--sur-accent)}

.fin-seance{margin-top:26px; padding-top:20px; border-top:1px solid var(--line);
  display:flex; flex-direction:column; gap:12px}
.form-fin{display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap}
.form-fin label{display:flex; flex-direction:column; gap:3px; font-size:11px; color:var(--muted); font-weight:600}
.form-fin label small{text-transform:none; letter-spacing:0; font-weight:400}
.form-fin input{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:17px;
  width:86px; padding:9px 10px; border:1px solid var(--line-strong);
  border-radius:7px; background:var(--surface-2); color:var(--ink)}
button.principal{font-family:inherit; font-size:14px; font-weight:600;
  padding:11px 22px; min-height:44px; border:none; border-radius:8px;
  background:var(--good); color:var(--sur-accent); cursor:pointer}
.form-couper{display:flex; gap:10px; align-items:center; flex-wrap:wrap}
.form-couper input{flex:1; min-width:200px; font-family:inherit; font-size:14px;
  padding:10px 12px; border:1px solid var(--line); border-radius:7px;
  background:var(--surface); color:var(--ink)}
button.danger{font-family:inherit; font-size:13px; font-weight:600;
  padding:10px 16px; min-height:44px; border:1px solid var(--crit);
  border-radius:8px; background:transparent; color:var(--crit); cursor:pointer}
button.danger:hover{background:var(--crit); color:var(--sur-accent)}
/* Ni vert ni rouge : annuler une séance n'est ni un accomplissement ni une
   alerte, c'est une décision ordinaire. Le bouton a le poids des deux autres —
   44 px, on le vise avec un pouce — sans leur couleur. */
button.secondaire{font-family:inherit; font-size:14px; font-weight:500;
  padding:11px 20px; min-height:44px; border:1px solid var(--line-strong);
  border-radius:8px; background:var(--surface); color:var(--ink-2); cursor:pointer}
button.secondaire:hover{border-color:var(--accent); color:var(--accent)}

.note-couper{margin:0; font-size:12px; color:var(--muted); max-width:64ch}

.statut{font-size:11px; font-weight:600}
.statut.faite{color:var(--good)}
.statut.en_cours{color:var(--accent)}
.statut.coupee_douleur{color:var(--warn)}
.statut.prevue, .statut.sautee{color:var(--muted)}
/* « A eu lieu, pas encore saisie » : ni verte comme une séance complète, ni
   éteinte comme une séance qui n'a pas eu lieu. Elle attend quelque chose de
   Jo, et c'est exactement ce que dit l'avertissement. */
.statut.a_saisir{color:var(--warn)}

@media (max-width:560px){
  .saisie{gap:7px}
  .saisie input, .form-fin input{width:72px; font-size:16px}
  .saisie button{margin-left:0; width:100%}
  .exo .cible{margin-left:0}
  .exo header{flex-wrap:wrap}
}

/* RPE en boutons : un tap, pas un clavier. Laissé en champ libre, il ne se
   remplit pas — et sans RPE l'autorégulation ne monte jamais la charge. */
fieldset.rpe{border:none; margin:0; padding:0; display:flex; flex-direction:column; gap:4px}
fieldset.rpe legend{font-size:11px;
  color:var(--muted); font-weight:600; padding:0}
fieldset.rpe legend small{text-transform:none; letter-spacing:0; font-weight:400}
.boutons-rpe{display:flex; gap:4px}
.boutons-rpe label{position:relative; cursor:pointer}
.boutons-rpe input{position:absolute; opacity:0; width:0; height:0}
.boutons-rpe span{
  display:grid; place-items:center; min-width:42px; min-height:44px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:16px; font-weight:500;
  border:1px solid var(--line-strong); border-radius:7px;
  background:var(--surface-2); color:var(--ink-2);
}
.boutons-rpe label.cible span{border-style:dashed}
.boutons-rpe input:checked + span{
  background:var(--accent); border-color:var(--accent); color:var(--sur-accent); font-weight:600}
.boutons-rpe input:focus-visible + span{outline:2px solid var(--accent); outline-offset:2px}

.marque-estimee{
  font-size:10px; font-weight:600;
  color:var(--muted); border:1px solid var(--line-strong); border-radius:3px;
  padding:1px 5px; margin-left:7px; white-space:nowrap;
}

/* ── Fiche d'exercice ──
   Une seule série tracée : le titre la nomme, pas de légende de couleur.
   L'écart estimé / saisi passe par la FORME de la marque autant que par sa
   couleur — il tient en niveaux de gris et pour un daltonien. */
.bandeau-exo{display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:12px; margin-bottom:26px}
.bandeau-exo .stat{background:var(--surface); border:1px solid var(--line);
  border-radius:10px; padding:14px 16px; display:flex; flex-direction:column; gap:3px}
.bandeau-exo .etiquette{font-size:10.5px; color:var(--muted); font-weight:600}
.bandeau-exo .valeur{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:27px;
  font-weight:600; line-height:1.15; font-variant-numeric:tabular-nums}
.bandeau-exo .valeur small{font-size:12px; color:var(--muted); margin-left:3px; font-weight:400}
.bandeau-exo .note{font-size:12px; color:var(--ink-2); line-height:1.45}

.avertissement-fort{background:var(--surface-2); border-left:3px solid var(--warn);
  border-radius:0 8px 8px 0; padding:11px 15px; font-size:12.5px;
  color:var(--ink-2); margin:0 0 14px; max-width:76ch}

.cadre-courbe{background:var(--surface); border:1px solid var(--line);
  border-radius:10px; padding:14px 12px 6px; overflow-x:auto}
.cadre-courbe svg{display:block; width:100%; min-width:460px; height:auto}
.grille-exo{stroke:var(--grid); stroke-width:1}
.axe-exo{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:10px; fill:var(--muted)}
.axe-exo.fin{text-anchor:end}
.axe-exo.milieu{text-anchor:middle}
.ligne-exo{fill:none; stroke:var(--accent); stroke-width:2;
  stroke-linejoin:round; stroke-linecap:round}
.pt-exo{stroke-width:2}
.pt-exo.saisi{fill:var(--accent); stroke:var(--surface)}
.pt-exo.estime{fill:var(--surface); stroke:var(--muted); stroke-dasharray:3 2}
.marque-sautee{stroke:var(--muted); stroke-width:2; stroke-linecap:round; opacity:.55}
.etiq-exo{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11.5px;
  font-weight:600; fill:var(--accent)}

.legende-courbe{display:flex; gap:16px; flex-wrap:wrap; margin:9px 0 0;
  font-size:12px; color:var(--ink-2)}
.cle-exo{display:inline-flex; align-items:center; gap:6px}
.cle-exo i{width:11px; height:11px; border-radius:50%; display:block; flex:none}
.cle-exo i.saisi{background:var(--accent)}
.cle-exo i.estime{background:var(--surface); border:2px dashed var(--muted)}
.cle-exo i.sautee{width:2px; height:11px; border-radius:0; background:var(--muted); opacity:.6}
tr.a-venir{opacity:.45}
a.charge{text-decoration:none; color:inherit}
a.charge:hover{border-color:var(--accent)}
.lien-exo{color:inherit; text-decoration:none; border-bottom:1px solid transparent}
.lien-exo:hover{border-bottom-color:var(--accent); color:var(--accent)}

/* Minuteur de repos : lisible à bout de bras, effaçable d'un doigt. */
.repos{display:flex; align-items:center; gap:12px; padding:9px 14px;
  background:var(--surface-2); border:1px solid var(--line-strong);
  border-radius:8px; cursor:pointer; user-select:none}
.repos .etiquette-repos{font-size:11px; color:var(--muted); font-weight:600}
.repos .compte{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:22px;
  font-weight:600; font-variant-numeric:tabular-nums; color:var(--ink);
  margin-left:auto}
.repos.bientot{border-color:var(--warn)}
.repos.bientot .compte{color:var(--warn)}
.repos.fini{border-color:var(--good); background:transparent}
.repos.fini .compte{color:var(--good); font-size:15px}
@media (prefers-reduced-motion:reduce){.repos{transition:none}}
.tableau-large{overflow-x:auto}
.tableau-large table{min-width:620px}
td .zero{color:var(--muted); opacity:.5}
.lien-section{float:right; font-size:12px; font-weight:500; color:var(--accent);
  text-decoration:none; text-transform:none; letter-spacing:0}
.lien-section:hover{text-decoration:underline}

/* Catalogue et édition : formulaires denses, lisibles au poste comme au téléphone. */
.form-exo{display:flex; gap:9px; flex-wrap:wrap; align-items:flex-end;
  background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:14px 16px}
.form-exo.ligne{border-radius:10px 10px 0 0; border-bottom:none}
.form-exo label{display:flex; flex-direction:column; gap:3px; font-size:10.5px; color:var(--muted); font-weight:600}
.form-exo label small{text-transform:none; letter-spacing:0; font-weight:400}
.form-exo label.large{flex:1; min-width:170px}
.form-exo input, .form-exo select{
  font-family:inherit; font-size:14px; padding:8px 9px; min-height:38px;
  border:1px solid var(--line-strong); border-radius:6px;
  background:var(--surface-2); color:var(--ink)}
.form-exo input[type=number]{width:78px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-variant-numeric:tabular-nums}
.form-exo input:focus, .form-exo select:focus{outline:2px solid var(--accent);
  outline-offset:1px; border-color:var(--accent)}
.form-exo button{font-family:inherit; font-size:13px; font-weight:600;
  padding:9px 16px; min-height:38px; border:1px solid var(--line-strong);
  border-radius:6px; background:var(--surface-2); color:var(--ink); cursor:pointer}
.form-exo button:hover{border-color:var(--accent); color:var(--accent)}
.form-exo button.principal{background:var(--accent); border-color:var(--accent); color:var(--sur-accent)}
.actions-exo{display:flex; align-items:center; gap:12px; margin-left:auto}

.liste-exos{display:flex; flex-direction:column; gap:16px}
.form-suppr{margin:-16px 0 0; padding:7px 16px; background:var(--surface);
  border:1px solid var(--line); border-top:none; border-radius:0 0 10px 10px}
.usage{margin:-16px 0 0; padding:8px 16px; font-size:11.5px; color:var(--muted);
  background:var(--surface); border:1px solid var(--line); border-top:none;
  border-radius:0 0 10px 10px}

details.ajuster{margin:0}
details.ajuster summary{font-size:11px;
  color:var(--muted); cursor:pointer; list-style:none; padding:2px 0}
details.ajuster summary::-webkit-details-marker{display:none}
details.ajuster summary:hover{color:var(--accent)}
.ajuster-corps{display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap;
  padding:9px 0 2px}
.ajuster-corps label{display:flex; flex-direction:column; gap:3px; font-size:10.5px; color:var(--muted); font-weight:600}
.ajuster-corps input{width:68px; padding:7px 8px; min-height:38px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:14px;
  border:1px solid var(--line-strong); border-radius:6px;
  background:var(--surface-2); color:var(--ink)}
.ajuster-corps button{font-family:inherit; font-size:12.5px; padding:8px 14px;
  min-height:38px; border:1px solid var(--line-strong); border-radius:6px;
  background:var(--surface-2); color:var(--ink); cursor:pointer}
.ajuster-corps form{display:flex; gap:10px; align-items:flex-end}

details.ajout-exo{margin-top:22px; background:var(--surface);
  border:1px solid var(--line); border-radius:10px; padding:13px 16px}
details.ajout-exo summary{font-size:13px; color:var(--ink-2); cursor:pointer}
details.ajout-exo summary:hover{color:var(--accent)}
details.ajout-exo form{display:flex; gap:9px; align-items:center;
  flex-wrap:wrap; padding-top:11px}
details.ajout-exo select{flex:1; min-width:220px; font-family:inherit; font-size:14px;
  padding:9px 10px; min-height:40px; border:1px solid var(--line-strong);
  border-radius:6px; background:var(--surface-2); color:var(--ink)}
details.ajout-exo button{font-family:inherit; font-size:13px; font-weight:600;
  padding:10px 18px; min-height:40px; border:none; border-radius:6px;
  background:var(--accent); color:var(--sur-accent); cursor:pointer}

/* Graphiques partagés : couleurs prises aux jetons, jamais écrites en dur,
   pour que le même tracé tienne en clair comme en sombre. */
.cadre-graphe{background:var(--surface); border:1px solid var(--line);
  border-radius:10px; padding:14px 12px 6px; overflow-x:auto}
.cadre-graphe svg{display:block; width:100%; min-width:560px; height:auto}
.grille-g{stroke:var(--grid); stroke-width:1; fill:none}
.grille-g.zero{stroke:var(--line-strong); stroke-width:1.5}
.axe-g{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:10.5px; fill:var(--muted)}
.axe-g.fin{text-anchor:end}
.axe-g.milieu{text-anchor:middle}
.ligne-g{fill:none; stroke-width:2; stroke-linejoin:round; stroke-linecap:round}
.ligne-g.ctl{stroke:var(--s1)}
.ligne-g.atl{stroke:var(--s2)}
.ligne-g.tsb{stroke:var(--s3); stroke-width:1.5; opacity:.75}
.pt-g{stroke:var(--surface); stroke-width:2}
.pt-g.ctl{fill:var(--s1)} .pt-g.atl{fill:var(--s2)} .pt-g.tsb{fill:var(--s3)}
.etiq-g{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11.5px; font-weight:600}
.etiq-g.ctl{fill:var(--s1)} .etiq-g.atl{fill:var(--s2)} .etiq-g.tsb{fill:var(--s3)}
.ligne-g.actuelle{stroke:var(--s1)}
.ligne-g.precedente{stroke:var(--muted); stroke-width:1.5; stroke-dasharray:5 4}
.pt-g.actuelle{fill:var(--s1)}
nav.periodes{margin-left:auto; display:flex; gap:4px}
nav.periodes a{font-size:12.5px; padding:6px 12px; min-height:34px;
  display:inline-flex; align-items:center; border:1px solid var(--line);
  border-radius:7px; color:var(--ink-2); text-decoration:none; background:var(--surface)}
nav.periodes a:hover{border-color:var(--accent); color:var(--accent)}
nav.periodes a.actif{background:var(--accent); border-color:var(--accent); color:var(--sur-accent); font-weight:600}

/* Les sous-onglets d'un onglet de la barre — Mes données, Zones, Puissance.
   Le trait sous le texte, comme la barre elle-même : ce sont des onglets, pas
   le sélecteur de période en pastilles qui vit à droite de certains titres. */
nav.sous-onglets{display:flex; flex-wrap:wrap; gap:2px 4px; margin:0 0 18px;
  border-bottom:1px solid var(--line)}
nav.sous-onglets a{font-size:14px; padding:7px 10px 8px; color:var(--ink-2);
  text-decoration:none; border-bottom:2px solid transparent; margin-bottom:-1px;
  white-space:nowrap}
nav.sous-onglets a:hover{color:var(--ink)}
nav.sous-onglets a.actif{color:var(--accent); font-weight:600;
  border-bottom-color:var(--accent)}

/* L'aperçu d'un programme : les exercices que le matériel permet. */
dl.exos-retenus{display:grid; grid-template-columns:max-content 1fr; gap:6px 16px;
  margin:6px 0 18px; font-size:14px}
dl.exos-retenus dt{color:var(--muted)}
dl.exos-retenus dd{margin:0}
@media (max-width:600px){dl.exos-retenus{grid-template-columns:1fr}
  dl.exos-retenus dd{margin-bottom:6px}}

/* Plan */
tr.semaine-courante{background:var(--accent-soft)}
tr.semaine-courante td{border-color:var(--accent)}
.blocs{display:flex; flex-direction:column; gap:6px}
.bloc-cible{display:flex; gap:12px; align-items:baseline; flex-wrap:wrap;
  padding:8px 11px; background:var(--surface-2); border-radius:7px; font-size:13px}
.bloc-cible .structure{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-weight:600; color:var(--ink)}
.bloc-cible .pct{color:var(--muted); font-size:12px}
.bloc-cible .watts{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  color:var(--accent); font-weight:600; margin-left:auto}
.bloc-cible .watts.muet{color:var(--muted); font-weight:400; font-size:12px}
.bloc-cible .recup{font-size:11.5px; color:var(--muted)}
.consignes{margin:0; font-size:12.5px; color:var(--ink-2); line-height:1.55}
.realise{margin:0; font-size:12.5px; color:var(--good); font-weight:500}
.note-simulation{font-size:12.5px; color:var(--ink-2); margin-left:auto; align-self:center}
.note-seuil{font-size:12px; color:var(--muted); max-width:36ch}
/* L'attribution que Garmin exige (`marque.py`) : du texte, lisible — jamais
   en bulle ni en repli —, sans couleur de sport : une source n'en est pas un. */
.attribution{margin:2px 0 0; font-size:13px; color:var(--ink-2)}
.attribution-derive{margin-top:28px}
.score{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-weight:600;
  padding:2px 8px; border-radius:999px; border:1px solid; font-size:12px}
.score.bon{color:var(--good); border-color:var(--good)}
.score.moyen{color:var(--warn); border-color:var(--warn)}
.score.faible{color:var(--crit); border-color:var(--crit)}
.realise-bloc{display:flex; flex-direction:column; gap:7px}
.ecarts{display:flex; gap:12px; align-items:center; flex-wrap:wrap}
.ecart-detail{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:12px;
  color:var(--ink-2)}
.ecarts form{margin-left:auto}
.entete-seance form button{font-family:inherit; font-size:13px; padding:9px 16px;
  min-height:38px; border:1px solid var(--line-strong); border-radius:7px;
  background:var(--surface); color:var(--ink-2); cursor:pointer}
.entete-seance form button:hover{border-color:var(--accent); color:var(--accent)}
.entete-seance form{margin-left:auto}
/* Matériel : la jauge doit se lire d'un coup d'œil, sans lire les chiffres. */
.jauge-usure{display:flex; flex-direction:column; gap:5px}
.jauge-usure .barre{height:9px; border-radius:5px; background:var(--surface-2);
  border:1px solid var(--line); overflow:hidden}
.jauge-usure .barre span{display:block; height:100%; border-radius:4px}
.jauge-usure .barre span.neuf{background:var(--good)}
.jauge-usure .barre span.surveille{background:var(--warn)}
.jauge-usure .barre span.bientot{background:var(--s2)}
.jauge-usure .barre span.depasse{background:var(--crit)}
/* D'où vient un seuil d'usure. Le kilomètre de gauche est MESURÉ — il sort des
   sorties — et le seuil de droite ne l'est pas forcément : sans marque, la même
   barre rouge servait aux deux.

   Encodage par la FORME autant que par la couleur, comme l'italique pâle d'une
   trace écartée : un repère supposé porte un contour tireté, qui se lit comme
   « pas ferme » même en noir et blanc.

   Les trois modificateurs sont COMPOSÉS (`.origine-seuil.suppose`) et non nus.
   Un `.suppose{}` seul serait un identifiant global, et c'est exactement ce qui
   avait fait hériter le jeton de bloc de la carte de musculation. */
.origine-seuil{
  display:inline-block; white-space:nowrap; margin-left:8px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:10px; padding:1px 6px;
  border-radius:999px; border:1px solid var(--line-strong); color:var(--muted);
  vertical-align:middle; font-style:normal; width:auto; min-height:0;
}
.origine-seuil.suppose{border-style:dashed; font-style:italic; text-transform:none;
  letter-spacing:0}
.origine-seuil.fabricant{color:var(--ink-2); border-color:var(--ink-2)}
.origine-seuil.perso{color:var(--accent); border-color:var(--accent)}
/* L'accueil (`/accueil`) : un paramètre mesuré porte un trait plein, un
   paramètre estimé le trait tireté de `.suppose` — la même forme que le
   matériel, pour la même raison. « Pas encore fait » reste le jeton nu et
   pâle : ce n'est pas une faute, il ne doit pas se lire comme une alerte. */
.origine-seuil.mesure{color:var(--ink-2); border-color:var(--ink-2)}

/* La réserve d'un repère supposé. Elle reste malgré la règle « ne pas décrire
   ce que l'écran montre » : elle empêche de mal lire un chiffre, ce que cette
   règle-là préserve explicitement. */
.reserve-seuil{
  margin:2px 0 0; font-size:12.5px; color:var(--muted); line-height:1.5;
  max-width:70ch;
}

.chiffres-usure{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:12px;
  color:var(--ink-2); font-variant-numeric:tabular-nums}

/* ── Semaine d'entraînement (tableau de bord) ──────────────────────────────
   Sept colonnes qui deviennent sept lignes sous 720 px : sur un téléphone,
   une colonne de 50 px ne peut porter ni un sport ni une durée. */

/* Le jeton de bloc — « charge », « allègement », « test ».
   Il vivait sous `.tete-semaine`, une classe qui a disparu du tableau de bord
   en septembre : le jeton n'était donc plus stylé nulle part, et sa valeur
   « charge » tombait sur `.charge`, la carte de charge de musculation. D'où la
   grosse boîte grise sur quatre écrans. Un jeton se style par LUI-MÊME. */
.bloc-sem{
  display:inline-block; white-space:nowrap;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11px; padding:2px 8px; border-radius:999px;
  background:var(--surface-2); color:var(--ink-2); border:1px solid var(--line-strong);
  /* Il partage son nom avec `.charge` et `.test` : ces deux déclarations
     annulent tout ce qu'une autre section pourrait lui prêter. */
  width:auto; min-height:0; text-align:left;
}
/* L'affûtage partage la teinte de l'allègement : deux semaines à volume
   réduit, que le TEXTE distingue. Une teinte à lui aurait pris celle d'un
   sport, et le tireté dit déjà « supposé » ailleurs (§interface). */
.bloc-sem.recup, .bloc-sem.allegement, .bloc-sem.affutage{color:var(--s3); border-color:var(--s3)}
.bloc-sem.test{color:var(--s4); border-color:var(--s4)}
/* Le même jeton nomme aussi le statut d'un bloc de plan. Les valeurs portent
   l'underscore de la base (`en_cours`, `a_venir`) : une table de correspondance
   dans le gabarit serait une deuxième façon d'écrire la même chose. */
.bloc-sem.en_cours{color:var(--accent); border-color:var(--accent)}
.bloc-sem.a_venir{color:var(--ink-2)}
.bloc-sem.terminee{color:var(--muted)}

.semaine{
  display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:1px;
  background:var(--line); border:1px solid var(--line); border-radius:10px;
  overflow:hidden;
}
.jour{
  background:var(--surface); padding:9px 10px 11px; min-height:104px;
  display:flex; flex-direction:column; gap:6px;
}
.jour.passe{background:var(--surface-2)}
.jour .date-jour{
  display:flex; align-items:baseline; gap:5px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11px; color:var(--muted);
}
.jour .date-jour b{font-size:14px; color:var(--ink-2); font-weight:600; letter-spacing:0}

/* La journée courante : c'est la seule chose que l'écran doit crier. */
.jour.aujourdhui{
  background:var(--accent-soft);
  box-shadow:inset 0 0 0 2px var(--accent);
}
.jour.aujourdhui .date-jour{color:var(--accent)}
.jour.aujourdhui .date-jour b{color:var(--accent)}
.marque-auj::before{content:"·\00a0"; margin-left:1px}

.item-jour{
  display:block; font-size:12.5px; line-height:1.3; text-decoration:none;
  color:var(--ink); border-left:2px solid var(--line-strong); padding:2px 0 2px 7px;
}
a.item-jour:hover{color:var(--accent); border-left-color:var(--accent)}
.item-jour.velo{border-left-color:var(--s1)}
.item-jour.course{border-left-color:var(--s2)}
.item-jour.nage{border-left-color:var(--s3)}
.item-jour.muscu{border-left-color:var(--s4)}
.item-jour .d{
  display:block; font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:11px; color:var(--muted);
}
.item-jour.prevu{opacity:.72}
/* Plus de rature sur une séance sautée depuis le 2026-09-13 : le ✗ rouge le dit
   maintenant, et la rature voulait dire DEUX choses selon la ligne — « sautée »
   ici, « a eu lieu » sur une séance faite. Un même trait pour deux sens
   contraires, c'est le pire des encodages.

   L'estompe reste : elle dit qu'il n'y a rien à lire dessous. Et ça libère au
   passage le ✗ de la rature, qu'aucune règle descendante ne peut annuler — en
   CSS, `text-decoration` d'un ancêtre ne se retire pas. */
.item-jour.prevu.sautee{opacity:.45}
/* Une séance PRÉVUE qui a eu lieu se lit comme ce qu'elle est : faite. Sans
   cette règle elle gardait l'estompe du prévu — « écrit en double comme si je
   ne l'avais pas fait », le 2026-09-13. Les déclarations sont partagées avec
   `.fait` pour que les deux ne puissent pas diverger. */
.item-jour.fait, .item-jour.prevu.faite,
.item-jour.prevu.a_saisir{font-weight:500; opacity:1}
.item-jour.fait .d, .item-jour.prevu.faite .d{color:var(--good)}
/* A eu lieu mais pas encore saisie : le nom se barre — l'entraînement a bien
   eu lieu — et le détail passe en avertissement, parce qu'il attend quelque
   chose. Ni le vert d'une séance complète, ni l'estompe d'une séance qui n'a
   pas eu lieu. */
.item-jour.prevu.a_saisir .d{color:var(--warn)}

.rien-jour{font-size:12px; color:var(--muted); font-style:italic}

@media (max-width:720px){
  .semaine{grid-template-columns:1fr}
  .jour{min-height:0; flex-direction:row; align-items:flex-start; gap:12px; padding:10px 12px}
  /* Sur une colonne de 62 px, « aujourd'hui » passe à la ligne : `.date-jour`
     est un conteneur flex, donc c'est `flex-basis` qui l'y envoie, pas
     `display:block`. Le point médian disparaît avec la mise en ligne. */
  .jour .date-jour{flex:none; width:76px; padding-top:2px; flex-wrap:wrap}
  .jour .date-jour .marque-auj{flex:0 0 100%; margin-top:1px}
  /* (Historique, du temps de Sora et des capitales espacées, retirées le
     2026-10-08.) En Sora, les capitales espacées étaient plus larges qu'en chasse fixe :
     « AUJOURD'HUI » touchait le trait de la séance (2026-10-07). */
  .jour .date-jour .marque-auj{font-size:10px; letter-spacing:.02em}
  .jour .date-jour .marque-auj::before{content:none}
  .jour .contenu-jour{flex:1; display:flex; flex-direction:column; gap:5px; min-width:0}
}

/* ── Calendrier mensuel ────────────────────────────────────────────────────
   Sept colonnes fixes : un mois ne se lit que si les colonnes s'alignent d'une
   semaine à l'autre. Sous 720 px la grille défile horizontalement plutôt que
   d'écraser les cases — une case de 40 px ne porte aucun libellé. */
.cal-entete, .calendrier{
  display:grid; grid-template-columns:repeat(7,minmax(104px,1fr));
}
.cal-entete{
  gap:1px; margin-bottom:1px; padding:0 1px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11px; color:var(--muted);
}
.cal-entete span{padding:0 8px 6px}
.calendrier{
  gap:1px; background:var(--line); border:1px solid var(--line);
  border-radius:10px; overflow:hidden;
}
.cal-jour{
  background:var(--surface); min-height:96px; padding:7px 8px 9px;
  display:flex; flex-direction:column; gap:4px;
}
.cal-jour.hors-mois{background:var(--surface-2)}
.cal-jour.hors-mois .cal-no{opacity:.45}
.cal-jour.aujourdhui{background:var(--accent-soft); box-shadow:inset 0 0 0 2px var(--accent)}
.cal-no{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:12px;
  color:var(--ink-2); font-variant-numeric:tabular-nums;
  display:flex; align-items:baseline; gap:6px;
}
.cal-jour.aujourdhui .cal-no{color:var(--accent); font-weight:600}
.cal-marque{font-size:9.5px;}

.cal-item{
  display:block; text-decoration:none; color:var(--ink);
  border-left:2px solid var(--line-strong); padding:1px 0 1px 6px;
  font-size:12px; line-height:1.25; min-width:0;
}
a.cal-item:hover .cal-nom{color:var(--accent)}
.cal-item.velo{border-left-color:var(--s1)}
.cal-item.course{border-left-color:var(--s2)}
.cal-item.nage{border-left-color:var(--s3)}
.cal-item.muscu{border-left-color:var(--s4)}
/* Le nom et sa marque sur UNE ligne. `.cal-nom` est un bloc — il faut la
   troncature à l'ellipse dans une case de calendrier — donc le ✓ tombait sur
   la ligne suivante et allongeait toutes les cases. La rangée flex garde la
   troncature au nom et pose la marque à côté, qui ne se rétrécit jamais. */
.cal-tete{display:flex; align-items:baseline; gap:2px; min-width:0}
.cal-nom{
  display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  min-width:0; flex:1;
}
.cal-tete .marque-exec{flex:none; margin-left:0}
.cal-detail{
  display:block; font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:10.5px; color:var(--muted);
}
.cal-item.fait .cal-nom, .cal-item.prevu.faite .cal-nom,
.cal-item.prevu.a_saisir .cal-nom{font-weight:500}
.cal-item.fait .cal-detail, .cal-item.prevu.faite .cal-detail{color:var(--good)}
.cal-item.prevu.a_saisir .cal-detail{color:var(--warn)}
.cal-item.prevu{opacity:.6}
.cal-item.prevu.faite, .cal-item.prevu.a_saisir{opacity:1}
.cal-item.prevu.sautee{opacity:.38}


@media (max-width:720px){
  .cal-entete, .calendrier{min-width:660px}
  .cal-cadre{overflow-x:auto}
}

/* ── Import de programme ───────────────────────────────────────────────────
   L'erreur doit être plus visible que le formulaire : un fichier refusé sans
   qu'on voie pourquoi se retente à l'identique. */
.regle-erreur{
  margin:18px 0; border:1px solid color-mix(in srgb, var(--crit) 34%, var(--line));
  border-left:3px solid var(--crit); border-radius:8px;
  background:color-mix(in srgb, var(--crit) 5%, var(--surface));
  padding:15px 18px;
}
.regle-erreur h3{margin:0 0 8px; font-size:14.5px}
.regle-erreur ul{margin:0; padding-left:20px; display:flex; flex-direction:column; gap:4px}
.regle-erreur li{font-size:13px; color:var(--ink-2); line-height:1.45}
.note-erreur{margin:10px 0 0; font-size:12.5px; color:var(--muted); max-width:70ch}

/* Les écarts du plan aux règles d'Essor (2026-10-09) : la géométrie de la
   boîte d'erreur, en --warn — à surveiller, pas bloquant. */
.regle-ecarts{
  /* La largeur du descriptif : au bureau, le compte ne part pas à l'autre
     bout de l'écran, loin de sa règle. */
  max-width:72ch;
  margin:18px 0; border:1px solid color-mix(in srgb, var(--warn) 34%, var(--line));
  border-left:3px solid var(--warn); border-radius:var(--rayon-2);
  background:color-mix(in srgb, var(--warn) 6%, var(--surface));
  padding:15px 18px;
}
.regle-ecarts h3{margin:0 0 6px; font-size:14.5px}
/* Une ligne par règle ; elle se déplie sur ses semaines. Le `summary` garde
   son affichage natif : en `flex`, Chrome retire le triangle, et plus rien ne
   dit que la ligne s'ouvre. La mise en ligne vit dans le span intérieur. */
.regle-ecarts details{border-top:1px solid var(--line)}
.regle-ecarts summary{cursor:pointer; padding:9px 0; font-size:13.5px; color:var(--ink)}
.regle-ecarts summary:focus-visible{outline:2px solid var(--ring); outline-offset:2px}
.regle-ecarts .ecart-ligne{display:inline-flex; align-items:baseline; justify-content:space-between;
  gap:12px; width:calc(100% - 1.4em); vertical-align:top}
/* Le compte à l'encre : en --warn sur ce fond, il passait sous 4,5:1. */
.regle-ecarts .ecart-compte{font-family:var(--chiffres); font-size:14px; color:var(--ink);
  white-space:nowrap}
.regle-ecarts ul{margin:0 0 10px; padding-left:20px; display:flex; flex-direction:column; gap:4px}
.regle-ecarts li{font-size:13px; color:var(--ink-2); line-height:1.45; max-width:70ch;
  overflow-wrap:anywhere}
/* Ce que l'IA dit de ses propres écarts : cité, pas listé — une affirmation
   à côté de la mesure, qui ne se lit pas comme elle (§interface). */
.regle-ecarts .ecart-raison{margin:0 0 10px; padding-left:10px; max-width:70ch;
  border-left:2px solid var(--line-strong); font-size:13px; color:var(--ink);
  line-height:1.45; overflow-wrap:anywhere}

.form-import{display:flex; flex-direction:column; gap:8px; align-items:flex-start; max-width:100%}
.form-import label{font-size:12px; color:var(--muted); margin-top:6px}
.form-import textarea{
  width:100%; font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:12.5px;
  background:var(--surface-2); color:var(--ink); border:1px solid var(--line-strong);
  border-radius:8px; padding:10px 12px; line-height:1.5; resize:vertical;
}
.form-import textarea:focus, .form-import input:focus{outline:2px solid var(--accent); outline-offset:1px}

/* ── Programmes livrés ─────────────────────────────────────────────────── */
.programmes-livres{display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:14px}
.carte-programme{
  background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:15px 17px 16px; display:flex; flex-direction:column; gap:7px; margin:0;
}
.carte-programme h3{margin:0; font-size:15.5px; letter-spacing:-.01em}
.chiffres-programme{
  margin:0; font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:12px; color:var(--ink-2); font-variant-numeric:tabular-nums;
}
.notes-programme{margin:0; font-size:13px; color:var(--ink-2); line-height:1.45}
.carte-programme button{margin-top:auto; align-self:flex-start}

/* ── Survol d'un graphique temporel ────────────────────────────────────────
   La lecture vit AU-DESSUS du graphique, pas dans une bulle qui suit le
   curseur : elle ne sort jamais du cadre, elle ne masque jamais la courbe, et
   elle reste au même endroit d'un jour à l'autre — donc lisible au balayage. */
.survol-lecture{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 18px;
  margin:0 0 10px; min-height:22px; font-size:13px; color:var(--ink-2);
}
.survol-lecture .survol-jour{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-weight:600;
  color:var(--ink); min-width:6.5em;
}
.survol-lecture b{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-weight:600;
  color:var(--ink); font-variant-numeric:tabular-nums; margin-left:2px;
}
/* pan-y et non none : sur un téléphone, le doigt doit encore pouvoir faire
   défiler la page verticalement au-dessus du graphique. */
[data-survol]{touch-action:pan-y}
[data-survol] svg{cursor:crosshair}
/* Sans ce rectangle, le pointeur traverse un graphique fait uniquement de
   traits : rien n'y capte l'événement. Il doit rester DERRIÈRE les tracés. */
.survol-capture{fill:transparent; pointer-events:all}
.survol{pointer-events:none}
.survol-trait{stroke:var(--line-strong); stroke-width:1; stroke-dasharray:3 3; opacity:0}
.survol-pt{stroke:var(--surface); stroke-width:2; opacity:0}
.survol-pt.ctl{fill:var(--s1)} .survol-pt.atl{fill:var(--s2)} .survol-pt.tsb{fill:var(--s3)}
.survole .survol-trait{opacity:1}
.survole .survol-pt{opacity:1}
/* Les séries des autres graphiques ont leurs propres noms de classe. */
.survol-pt.valeur, .survol-pt.actuelle, .survol-pt.serie{fill:var(--s1)}
.survol-pt.precedente{fill:var(--muted)}
.survol-pt.fc_repos{fill:var(--s2)} .survol-pt.sommeil_h{fill:var(--s1)}
.survol-pt.hrv{fill:var(--s3)} .survol-pt.poids_kg{fill:var(--s4)}

/* Plus de rature depuis le 2026-10-08 (direction « Plan d'eau », §interface,
   qui remplace la décision du 2026-09-10) : une ligne barrée se lit
   « annulée ». Fait = pastille pleine, prévu = pastille creuse, sauté =
   estompé — bloc « Plan d'eau », en fin de fichier. */

/* ── Tableau de bord ───────────────────────────────────────────────────────
   Deux blocs, un pied de page. Le rythme vertical est généreux et RÉGULIER :
   c'est lui qui sépare « ma semaine » de « condition et forme », pas un trait.
   Une page qui respire se lit sans qu'on ait à chercher où commence quoi. */
.bord{display:flex; flex-direction:column; gap:44px}
/* Le `gap` espace les enfants : leurs marges s'y ajoutaient — en flex, elles ne
   fusionnent pas : 94 px sous l'en-tête du point de contrôle (vu à 390 px,
   2026-10-28). */
.bord > .section{margin-top:0}
.bord > .entete-seance{margin-bottom:0}
.bord .bloc{display:flex; flex-direction:column; gap:14px}

.bloc-tete{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 12px;
  padding-bottom:10px; border-bottom:1px solid var(--line);
}
.bloc-tete h2{
  margin:0; font-size:19px; font-weight:600; letter-spacing:-.015em;
  text-transform:none; color:var(--ink);
}
.bloc-sous{margin:0; font-size:13px; color:var(--muted)}
.lien-bloc{
  margin-left:auto; font-size:12.5px; font-weight:500; color:var(--accent);
  text-decoration:none; white-space:nowrap;
}
.lien-bloc:hover{text-decoration:underline}

/* « NP » et « moy » collés au nombre. Discrets mais jamais absents : deux
   puissances différentes sous un même « 148 W » seraient illisibles, et trente
   watts les séparent sur une sortie à intervalles. */
.sigle-w{font-style:normal; font-size:.85em; opacity:.6; letter-spacing:.02em}

/* ✓ et ✗ — « un crochet vert pour les activités complètes et un x rouge à côté
   des activités passées qui n'ont pas été faites », Jo le 2026-09-13.

   Le glyphe se lit SANS la couleur : ✓ et ✗ ne se confondent pas en noir et
   blanc, et la teinte ne fait que renforcer. C'est la même règle que l'italique
   d'une trace écartée et le tireté d'un repère supposé. */
.marque-exec{
  display:inline-block; margin-left:6px; font-size:12px; font-weight:700;
  line-height:1; vertical-align:baseline; font-style:normal;
}
.marque-exec.fait{color:var(--good)}
.marque-exec.manque{color:var(--crit)}

/* La ligne de synchro. Discrète par construction : elle répond à « est-ce que
   c'est à jour », une question qu'on ne se pose qu'au retour d'une sortie. */
.ligne-synchro{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:0; font-size:12.5px; color:var(--muted);
}
.ligne-synchro button{
  font-family:inherit; font-size:12px; padding:4px 11px; min-height:28px;
  background:var(--surface); border:1px solid var(--line-strong); border-radius:7px;
  color:var(--ink-2); cursor:pointer;
}
.ligne-synchro button:hover{border-color:var(--accent); color:var(--accent)}
.synchro-muette{color:var(--warn)}

/* Jauge de volume : une barre en dit plus qu'un pourcentage isolé, parce
   qu'elle montre le reste à faire en même temps que le fait. */
.jauge-semaine{display:flex; flex-direction:column; gap:6px}
.jauge-piste{
  height:6px; border-radius:3px; background:var(--surface-2);
  border:1px solid var(--line); overflow:hidden;
}
.jauge-piste span{display:block; height:100%; background:var(--accent); border-radius:3px}
.jauge-texte{
  margin:0; font-size:13px; color:var(--ink-2);
  display:flex; align-items:baseline; gap:8px;
}
.jauge-texte b{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:15px;
  color:var(--ink); font-variant-numeric:tabular-nums;
}
.jauge-pct{
  margin-left:auto; font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:12px; color:var(--muted);
}

/* L'écart sur sept jours accompagne le nombre sans lui disputer la place. */
.carte .sigle{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; opacity:.55;
  letter-spacing:.04em;
}
/* NEUTRE, délibérément. Colorer une hausse en « bon » et une baisse en
   « mauvais » serait une interprétation : une fatigue qui monte peut être
   voulue en semaine de charge. Le signe suffit à dire le sens. */
.ecart-7j{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:12px;
  font-weight:500; margin-left:7px; vertical-align:middle; color:var(--muted);
}

/* Pied de page : quatre pistes, pas un paragraphe de liens noyés dans du texte. */
.pistes{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:12px;
}
.pistes a{
  display:flex; flex-direction:column; gap:3px; text-decoration:none;
  background:var(--surface); border:1px solid var(--line); border-radius:10px;
  padding:13px 15px; color:var(--ink);
}
.pistes a:hover{border-color:var(--accent); background:var(--accent-soft)}
.pistes b{font-size:14px; font-weight:600}
.pistes span{font-size:12px; color:var(--muted); line-height:1.35}

/* La semaine a besoin d'air autour d'elle plus que de bordures. */
.bord .semaine{margin-top:2px}
/* `.chiffres` porte sa propre marge basse pour les autres écrans ; ici c'est
   le `gap` du bloc qui commande, sinon l'espace double. */
.bord .chiffres{margin-bottom:0}
.bord .legende-courbe{margin-bottom:0}

/* Le calendrier suit « ma semaine » : plus de rature (2026-10-08), la
   pastille pleine ou creuse dit fait ou prévu — bloc « Plan d'eau ». */
/* Une trace écartée comme doublon était DÉJÀ barrée pour dire « écartée ».
   Maintenant que TOUTE activité faite l'est, la rature ne distingue plus rien :
   l'écartée passe en italique pâle. Encodage par la forme autant que par la
   couleur, comme les points estimés des courbes de musculation. */
.cal-item.ecartee{opacity:.55}
.cal-item.ecartee .cal-nom{font-style:italic}
/* La ligne de détail s'allonge avec les watts : elle doit pouvoir se replier
   dans une case étroite plutôt que déborder. */
.cal-detail{overflow-wrap:anywhere}

/* ── Mes données et mes zones ─────────────────────────────────────────────── */
.valeur-param{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; color:var(--ink);
  font-weight:600; font-variant-numeric:tabular-nums;
}
.aide-param{margin:0; font-size:12.5px; color:var(--muted); max-width:70ch}
/* Un éducatif de nage expliqué (`educatifs.py`) : le nom, comment le faire,
   ce qu'on y cherche. Lu au bord du bassin : des lignes courtes. */
.educatif{margin:0 0 16px; max-width:62ch}
.educatif h3{font-size:15px; margin:0 0 4px}
.educatif p{margin:0 0 4px}
.educatif .educatif-but{color:var(--ink-2); font-style:italic}
.jeton-zone{
  display:inline-block; min-width:2.6em; text-align:center;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11.5px;
  font-weight:600; padding:2px 7px; border-radius:4px;
  background:var(--surface-2); border:1px solid var(--line-strong); color:var(--ink-2);
}
/* Le jeton prend la teinte de son RANG, comme les pastilles de la répartition.
   Il la prenait de l'AXE : les cinq zones cardiaques sortaient toutes en orange
   dans le tableau des bornes et en cinq couleurs dans la répartition, juste
   dessous. Deux encodages des mêmes zones sur la même page. */
.jeton-zone.z0, .jeton-zone.z1, .jeton-zone.z2, .jeton-zone.z3,
.jeton-zone.z4, .jeton-zone.z5, .jeton-zone.z6, .jeton-zone.z7{
  color:var(--teinte);
  border-color:color-mix(in srgb, var(--teinte) 45%, transparent);
  background:color-mix(in srgb, var(--teinte) 12%, transparent);
}
/* Le « ✕ » d'une ligne d'historique. Il portait `.form-suppr`, qui est le
   bandeau collé SOUS une carte d'exercice — fond, bordure et marge négative
   de 16 px : dans une cellule de tableau, ça faisait un gros bloc gris posé
   de travers sur la ligne. */
.form-suppr button, .suppr-ligne button{
  background:none; border:none; color:var(--muted); cursor:pointer;
  font-size:13px; padding:0 4px; line-height:1;
}
.form-suppr button:hover, .suppr-ligne button:hover{color:var(--crit)}
.suppr-ligne{margin:0; display:inline-block}

/* ── Saisie de zones personnelles ─────────────────────────────────────────── */
.form-zones{display:flex; flex-direction:column; gap:6px}
.ligne-zone{display:flex; gap:8px; align-items:center; flex-wrap:wrap}
.ligne-zone input[type=text]{flex:0 1 auto; min-width:60px}
.ligne-zone input[name=nom]{flex:1 1 160px}
.ligne-zone input[type=number]{width:92px; font-family:var(--chiffres); font-variant-numeric:tabular-nums}
.unite-zone{font-size:12px; color:var(--muted); min-width:3em}
.boutons-zones{margin-top:6px}

/* ── Temps en zone ──────────────────────────────────────────────────────────
   Une rampe de sept teintes, du calme à l'intense, plus un gris pour ce qui tombe
   sous la zone 1. Le rang de la zone fait la couleur, pas son nom : les jeux
   de bornes n'ont pas tous le même nombre de zones — sept chez Coggan, cinq
   dans les zones de laboratoire de Jo.
   L'ordre des teintes est monotone : il doit se lire comme une intensité, pas
   comme un code arbitraire à mémoriser.

   Fixée par Jo le 2026-10-08 : **Z1 gris, Z2 bleu, Z3 vert, Z4 jaune, Z5
   rouge** — puis, pour les modèles à sept zones, Z6 rouge foncé et Z7 violet.
   Avant, Z1 et Z2 étaient deux bleus presque pareils (`#8fa4cc` / `#7f9fe8`
   en sombre) : le profil d'une endurance ne montrait aucun changement de
   zone. Le jaune clair est assombri (`#c7a200`) : il sert aussi de texte dans
   les jetons, sur fond blanc. */
:root{
  --z0:#b4bbc0; --z1:#8a96a0; --z2:#2f6fe0; --z3:#2e9e4f;
  --z4:#c7a200; --z5:#d93b30; --z6:#9e1f2b; --z7:#7d4fc4;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --z0:#5f6a72; --z1:#9aa5ae; --z2:#6f95f2; --z3:#5ccf7a;
    --z4:#f2cf45; --z5:#f0605a; --z6:#c23a44; --z7:#b48cf0;
  }
}
:root[data-theme="dark"]{
  --z0:#5f6a72; --z1:#9aa5ae; --z2:#6f95f2; --z3:#5ccf7a;
  --z4:#f2cf45; --z5:#f0605a; --z6:#c23a44; --z7:#b48cf0;
}

/* Le rang pose une TEINTE ; chaque consommateur décide s'il la met en fond ou
   en texte. Poser `background` ici peindrait aussi ce qui ne veut que la
   couleur du trait. */
.z0{--teinte:var(--z0)} .z1{--teinte:var(--z1)} .z2{--teinte:var(--z2)}
.z3{--teinte:var(--z3)} .z4{--teinte:var(--z4)} .z5{--teinte:var(--z5)}
.z6{--teinte:var(--z6)} .z7{--teinte:var(--z7)}
.pastille-z{display:inline-block; width:9px; height:9px; border-radius:2px;
  vertical-align:baseline; background:var(--teinte, var(--muted))}

.barre-zones{
  display:flex; width:100%; height:10px; border-radius:5px; overflow:hidden;
  background:var(--surface-2); border:1px solid var(--line);
}
.barre-zones span{display:block; min-width:0; background:var(--teinte, var(--muted))}
.barre-zones.mince{height:6px; border-radius:3px}

.legende-zones{
  display:flex; flex-wrap:wrap; gap:6px 14px; margin-top:8px;
  font-size:12px; color:var(--ink-2);
}
.legende-zones b{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-weight:600;
  color:var(--ink); font-variant-numeric:tabular-nums;
}
.legende-zones .cle{color:var(--muted); font-size:11px}

.tz-tete{
  display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin-bottom:8px;
}
.tz-tete h3{margin:0; font-size:14px; font-weight:600}
.tz-tete .tz-total{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:12px;
  color:var(--muted); margin-left:auto; font-variant-numeric:tabular-nums;
}
.tz-axe{margin-bottom:22px}
.tz-axe:last-child{margin-bottom:0}
/* Deux colonnes au plus, jamais trois. Une ligne de zone porte 300 px de
   colonnes fixes — nom, bornes, durée : à trois colonnes sur 1180 px, il ne
   restait que 40 px pour la barre, et une barre de 40 px ne compare rien.
   Le seuil est donc posé pour que la troisième colonne ne tienne pas. */
/* `min(…, 100%)` : sous 400 px, une colonne de 400 px élargissait la page au
   téléphone (relevé le 2026-10-08 sur /zones). */
.tz-duo{display:grid; grid-template-columns:repeat(auto-fit,minmax(min(400px,100%),1fr)); gap:22px 30px}
.tz-rang{display:flex; align-items:center; gap:10px; margin:5px 0}
.tz-rang .tz-nom{min-width:120px; font-size:13px; color:var(--ink-2)}
.tz-rang .tz-piste{
  flex:1; min-width:60px; height:12px; background:var(--surface-2); border-radius:6px;
  border:1px solid var(--line); overflow:hidden;
}
.tz-rang .tz-piste span{display:block; height:100%; background:var(--teinte, var(--muted))}
.tz-rang .tz-val{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:12px;
  min-width:96px; text-align:right; font-variant-numeric:tabular-nums;
}
.tz-rang .tz-val i{color:var(--muted); font-style:normal}
.tz-bornes{font-size:11px; color:var(--muted); min-width:86px; text-align:right}
.tz-etiq{
  font-size:10px; color:var(--muted);
}
@media (max-width:700px){
  .tz-rang .tz-nom{min-width:74px; font-size:12px}
  .tz-bornes{display:none}
}

/* Le sélecteur de fenêtre de /zones : il vit dans un bloc, pas dans un en-tête
   de page, donc il ne se pousse pas à droite et il s'enroule sur un téléphone. */
.bloc nav.periodes{
  margin:14px 0 0; flex-wrap:wrap; align-items:center; gap:6px 10px;
}
.bloc nav.periodes .meta-seance{margin-left:6px}

/* ── Programmes ─────────────────────────────────────────────────────────────
   Le bloc du programme en cours prend un liseré d'accent : sur une page qui
   empile passé, présent et futur, c'est le présent qu'on vient voir. */
.bloc-vedette{border-left:3px solid var(--accent)}
/* L'entretien et la transition : provisoires, réécrits toutes les quatre
   semaines. Le tireté le dit par la forme, comme un repère supposé. */
.bloc.bloc-provisoire{border-style:dashed}
.objectif-phase{
  margin:0 0 16px; padding:12px 14px; border-radius:8px;
  background:var(--accent-soft); color:var(--ink-2);
  font-size:14px; line-height:1.55;
}
.objectif-phase p{margin:0}
.objectif-phase p + p{margin-top:6px}
details.ajuster > summary b{color:var(--ink)}

/* Le guide Zwift : une liste de consignes, pas un tableau. */
.liste-reglages{margin:0; padding-left:20px; color:var(--ink-2); font-size:14px}
.liste-reglages li{margin:7px 0; line-height:1.55}
.zwift-ligne{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 12px;
  margin:8px 0 2px; font-size:13px; color:var(--ink-2);
}
.zwift-ligne b{color:var(--ink); font-weight:600}
.zwift-ligne .erg{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11px;
  color:var(--muted); border:1px solid var(--line-strong); border-radius:4px;
  padding:1px 6px;
}

/* Séparateur de phase dans un tableau qui en enchaîne plusieurs. Il doit se
   voir sans peser : c'est un repère, pas une ligne de données. */
table.activites tr.tete-phase th{
  padding-top:22px; font-size:12px; text-transform:none; letter-spacing:0;
  color:var(--ink-2); font-weight:600; border-bottom:1px solid var(--line-strong);
}
table.activites tr.tete-phase:first-child th{padding-top:8px}
table.activites tr.tete-phase:hover{background:none}

/* Une cellule numérique ne se coupe jamais au milieu de son unité : le guide
   Zwift affichait « 88–93 » sur une ligne et « % » sur la suivante. */
table.activites td.num, table.activites th.num{white-space:nowrap}

/* La bascule de thème : discrète, à côté du sous-titre. Elle ne mérite pas
   une place dans la barre d'onglets — on y touche deux fois par an. */
.theme{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:10.5px; cursor:pointer;
  background:none; border:1px solid var(--line); border-radius:999px;
  color:var(--muted); padding:3px 9px; align-self:center; line-height:1.5;
}
.theme:hover{border-color:var(--accent); color:var(--accent)}


/* ── Plus lourd que la dernière fois ─────────────────────────────────────────
   Demandée par Jo le 2026-09-13, pour l'écran de saisie en séance. Trois règles
   viennent de CET écran-là et pas d'un goût pour l'animation :

   1. `pointer-events:none` PARTOUT. Rien ne doit intercepter un doigt qui vise
      déjà la série suivante — la fête n'a aucun bouton et ne se ferme pas, elle
      s'en va.
   2. Tout en position absolue, dans un calque. Une carte qui grandirait
      pousserait le reste de la liste pendant qu'on tape.
   3. Le chiffre est gros et le mouvement bref. On regarde de biais, téléphone
      posé de travers sur un banc. */
.fete-record{
  position:absolute; inset:0; z-index:5; pointer-events:none;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:2px; overflow:visible;
}
.fete-gain{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:40px; font-weight:600; letter-spacing:-.02em; color:var(--good);
  text-shadow:0 2px 18px color-mix(in srgb, var(--good) 55%, transparent);
  animation:fete-monte 1.9s cubic-bezier(.16,.84,.36,1) forwards;
}
.fete-sous{
  font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:11.5px;
  color:var(--good); opacity:0;
  animation:fete-sous 1.9s ease-out forwards;
}
/* Un anneau qui s'ouvre et s'éteint. `box-shadow` plutôt qu'une bordure : il ne
   prend aucune place et ne peut donc pas décaler la carte. */
.exo.fete-carte{animation:fete-anneau 1.1s ease-out}

.fete-eclat{
  position:absolute; top:50%; left:50%; width:7px; height:7px; border-radius:2px;
  opacity:0; animation:fete-eclat 1.15s cubic-bezier(.2,.7,.3,1) var(--retard) forwards;
}

@keyframes fete-monte{
  0%   {transform:translateY(14px) scale(.6); opacity:0}
  18%  {transform:translateY(0) scale(1.12); opacity:1}
  30%  {transform:translateY(0) scale(1)}
  70%  {opacity:1}
  100% {transform:translateY(-30px) scale(.94); opacity:0}
}
@keyframes fete-sous{
  0%, 12% {opacity:0; transform:translateY(6px)}
  26%     {opacity:.95; transform:translateY(0)}
  72%     {opacity:.95}
  100%    {opacity:0; transform:translateY(-12px)}
}
@keyframes fete-anneau{
  0%   {box-shadow:0 0 0 0 color-mix(in srgb, var(--good) 55%, transparent)}
  100% {box-shadow:0 0 0 16px transparent}
}
@keyframes fete-eclat{
  0%   {opacity:0; transform:translate(-50%,-50%) scale(.4) rotate(0)}
  12%  {opacity:1}
  100% {opacity:0;
        transform:translate(calc(-50% + var(--dx)), calc(-50% + var(--dy)))
                  scale(.5) rotate(var(--tour))}
}

/* Le chiffre reste, le mouvement part. C'est l'information qui compte ; les
   particules sont l'emballage. */
@media (prefers-reduced-motion: reduce){
  .fete-gain{animation:fete-fondu 1.6s ease-out forwards; text-shadow:none}
  .fete-sous{animation:fete-fondu 1.6s ease-out forwards}
  .exo.fete-carte{animation:none}
  @keyframes fete-fondu{0%{opacity:0} 15%,70%{opacity:1} 100%{opacity:0}}
}


/* ── La semaine du plan — une rangée par jour, 2026-10-05 ───────────────────
   Jo : « plus beau et simple visuellement ». Cinq séances tenaient sur
   2 222 px, dont un tableau de sept zones par séance — six à « 0 s » pour une
   sortie que son bloc disait déjà en Z2. Le temps en zone passe en barre
   mince (`tz.resume`), les actions dans un menu « ⋯ », et les jours de repos
   apparaissent : un jour sans séance est une décision du plan.

   La couleur du sport vit dans le FILET de gauche de la séance ; le titre ne
   reprend pas la pastille — un encodage, pas deux. */
.sem-resume{
  display:flex; flex-wrap:wrap; align-items:center; gap:10px 28px;
  padding:14px 18px; margin-bottom:14px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--rayon);
  -webkit-backdrop-filter:var(--flou); backdrop-filter:var(--flou);
  box-shadow:var(--shadow), var(--lueur);
}
.sem-volume{margin:0; display:flex; align-items:baseline; gap:8px; color:var(--muted); font-size:13px}
.sem-volume b{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:22px;
  font-weight:600; color:var(--ink)}
.sem-sports{margin:0; display:flex; flex-wrap:wrap; gap:6px 18px; font-size:13px; color:var(--ink-2)}
.sem-sports b{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-weight:500;
  color:var(--ink); margin-left:3px}
.sem-zones{display:flex; flex-direction:column; gap:5px}
.sem-resume .sem-zones{flex:1 1 300px; min-width:240px}
.sem-zone{display:grid; grid-template-columns:40px minmax(0,1fr); align-items:center; gap:8px}
.sem-zone .legende-zones{margin-top:3px}
.sem-etiq{font-size:10px; color:var(--muted)}
.sem-zones-titre{margin:0 0 2px}

.sem-jours{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:8px}
.sem-jour{
  display:grid; grid-template-columns:76px minmax(0,1fr); gap:14px; align-items:start;
  padding:12px 16px; background:var(--surface); border:1px solid var(--line);
  border-radius:var(--rayon-2); box-shadow:var(--lueur);
}
.sem-jour.passe, .sem-jour.repos{background:var(--surface-creux)}
.sem-jour.repos{padding-top:8px; padding-bottom:8px; align-items:baseline}
.sem-jour.aujourdhui{box-shadow:inset 0 0 0 2px var(--accent), var(--lueur)}
.sem-jour .date-jour{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:5px; padding-top:2px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:11px; color:var(--muted);
}
.sem-jour .date-jour b{font-size:15px; color:var(--ink-2); font-weight:600; letter-spacing:0}
.sem-jour.aujourdhui .date-jour, .sem-jour.aujourdhui .date-jour b{color:var(--accent)}
.sem-jour .marque-auj{flex:0 0 100%; font-size:10px}
.sem-jour .marque-auj::before{content:none}

.sem-seances{display:flex; flex-direction:column; gap:14px}
.sem-seance{position:relative; padding:1px 0 1px 12px; border-left:3px solid var(--line-strong)}
.sem-seance.velo{border-left-color:var(--s1)}
.sem-seance.course{border-left-color:var(--s2)}
.sem-seance.nage{border-left-color:var(--s3)}
.sem-seance.muscu{border-left-color:var(--s4)}
.sem-seance.sautee{opacity:.5}
.sem-seance header{display:flex; align-items:baseline; gap:8px; padding-right:34px}
.sem-titre{font-size:15px; font-weight:600; color:var(--ink); text-decoration:none; letter-spacing:-.01em}
a.sem-titre:hover{color:var(--accent)}
.sem-duree{margin-left:auto; font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:13px; color:var(--muted); white-space:nowrap}

/* Les blocs d'une séance partagent leurs colonnes : la structure, la cible
   relative, la valeur absolue et la récup s'alignent d'une ligne à l'autre. */
.sem-blocs{display:grid; grid-template-columns:repeat(4, max-content);
  gap:4px 18px; align-items:baseline; margin-top:7px; font-size:13px}
.sem-bloc{display:contents}
.sem-bloc .structure{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-weight:600; color:var(--ink)}
.sem-bloc .pct{font-size:12px; color:var(--muted)}
.sem-bloc .abs{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-weight:600;
  color:var(--accent); white-space:nowrap}
.sem-bloc .abs .muet{font-family:inherit; font-weight:400; font-size:12px; color:var(--muted)}
.sem-bloc .recup{font-size:11.5px; color:var(--muted); white-space:nowrap}
.sem-seance .sem-zones{margin-top:9px; max-width:560px}
.sem-consignes{margin:7px 0 0; font-size:12.5px; line-height:1.45; color:var(--ink-2); max-width:76ch}
.sem-zwift{margin:5px 0 0; font-size:12px; color:var(--muted)}
.sem-zwift b{color:var(--ink-2); font-weight:600; margin-right:3px}
.sem-zwift .erg{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:10px;
  border:1px solid var(--line-strong); border-radius:4px; padding:0 4px; margin-left:4px}
.sem-realise{margin:7px 0 0; display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 10px;
  font-size:12.5px; color:var(--ink-2)}

.sem-menu > summary{
  position:absolute; top:-2px; right:0; list-style:none; cursor:pointer;
  padding:1px 8px 5px; border-radius:7px; font-size:18px; line-height:1; color:var(--muted);
}
.sem-menu > summary::-webkit-details-marker{display:none}
.sem-menu > summary:hover, .sem-menu[open] > summary{background:var(--surface-2); color:var(--ink)}
.sem-menu-corps.ajuster-corps{
  flex-direction:column; align-items:stretch; gap:12px; margin-top:10px; padding:12px 14px;
  background:var(--surface-creux); border:1px solid var(--line); border-radius:var(--rayon-2);
}
.sem-menu-corps form{flex-wrap:wrap}
.sem-menu-corps input[type=date]{width:auto}
.sem-menu-corps select{min-height:38px}

@media (max-width:720px){
  .sem-resume{padding:12px 14px; gap:10px 18px}
  .sem-jour{grid-template-columns:1fr; gap:6px; padding:10px 12px}
  .sem-jour.repos{grid-template-columns:76px 1fr}
  .sem-jour .marque-auj{flex:none}
  .sem-blocs{display:flex; flex-direction:column; gap:5px}
  .sem-bloc{display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 10px}
  .sem-bloc .abs{margin-left:auto}
}

/* ═══════════════════ Matière — ex-direction « Verre » ═══════════════════
   « Verre » (2026-09-13) est REMPLACÉE par « Plan d'eau, route, piste »
   (2026-10-08, bloc en fin de fichier) : les jetons sont devenus opaques et
   la lueur transparente, donc ce bloc ne pose plus de verre — il ne garde que
   ses rayons, ses rangées de cartes et ses creux de saisie. Ses commentaires
   « verre » racontent pourquoi ces règles sont nées ; le texte de la
   direction est archivé (`docs/archives/interface.md`).

   CE BLOC VIT EN FIN DE FICHIER, ET C'EST DÉLIBÉRÉ. Il redéclare des rayons
   et des ombres déjà posés plus haut, à spécificité égale : placé en tête, il
   perdrait sur chacun d'eux. Une couleur, elle, se change dans les jetons du
   haut — jamais ici.

   Le flou n'est posé que sur les conteneurs de PREMIER niveau. Une cellule de
   calendrier floutée à l'intérieur d'un calendrier flouté, c'est deux passes
   de GPU pour un effet que personne ne voit — et sur un téléphone, ça se
   sent. */
.carte, .vide, .carte-prochaine, .charge, .exo, .bandeau-exo .stat,
.cadre-courbe, .cadre-graphe, .repos, .form-exo, details.ajout-exo,
.carte-programme, .pistes a, .semaine, .calendrier, .form-import textarea{
  border-radius:var(--rayon);
  -webkit-backdrop-filter:var(--flou); backdrop-filter:var(--flou);
  box-shadow:var(--shadow), var(--lueur);
}

/* Deuxième rang : la matière sans le flou. Ces blocs sont posés DANS une carte
   déjà floutée — les flouter une seconde fois ne montre rien de plus. */
.bloc-cible, .derniere-fois, .usage, .avertissement-fort, .consigne-medicale,
.series-faites li, .suggestion, .note-seuil, .reserve-seuil{
  border-radius:var(--rayon-2);
}

/* Ce qui doit RECULER ne se code pas en « surface-2 » : ce jeton éclaircit en
   sombre et assombrit en clair, donc il avance dans un thème et recule dans
   l'autre. `--surface-creux` recule dans les deux. */
.jour.passe, .cal-jour.hors-mois{background:var(--surface-creux)}

/* La semaine et le calendrier cessent d'être une grille à filets pour devenir
   une rangée de CARTES : le fond passe entre elles, chacune porte sa tranche
   de lumière. Le filet d'un pixel ne survit pas à la translucidité — la
   cellule et le filet se mélangeaient en un gris unique, mesuré à l'écran le
   2026-09-13. */
.semaine, .calendrier{
  background:transparent; border:none; border-radius:0; overflow:visible;
  box-shadow:none; -webkit-backdrop-filter:none; backdrop-filter:none;
  gap:10px;
}
.calendrier{gap:6px}
.jour, .cal-jour{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--rayon-2); box-shadow:var(--lueur);
}
.jour.aujourdhui, .cal-jour.aujourdhui{
  box-shadow:inset 0 0 0 2px var(--accent), var(--lueur);
}

/* (Historique : Sora, plus large qu'Archivo ; Barlow, plus étroite, ne
   déborde plus, et la règle reste sans dommage.) « DIM 13 · AUJOURD'HUI » ne tenait plus
   dans un septième de 1180 px et se coupait au bord. La ligne s'enroule. */
.jour .date-jour{flex-wrap:wrap}

/* La barre d'onglets devient une barre de verre posée sur le fond, et non un
   filet tiré sous la page. Conséquence assumée : l'onglet actif se marque par
   une PASTILLE à toutes les largeurs, là où le soulignement dépendait du filet
   du bas — un marqueur au lieu de deux, et le comportement que la version
   téléphone avait déjà. */
header.barre{
  padding:10px 16px; margin-bottom:22px; border-bottom:none;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--rayon);
  -webkit-backdrop-filter:var(--flou); backdrop-filter:var(--flou);
  box-shadow:var(--shadow), var(--lueur);
}
header.barre nav a{
  padding:7px 11px; margin-bottom:0;
  border-bottom:none; border-radius:11px;
}
header.barre nav a.actif{
  color:var(--accent); font-weight:600;
  background:var(--accent-soft); border-bottom-color:transparent;
}
@media (max-width:900px){
  header.barre{padding:10px 12px}
  header.barre nav a{padding:6px 9px; font-size:13.5px}
}

/* Un champ de saisie sur du verre : creusé, pas posé. L'ombre intérieure fait
   le creux que la bordure seule ne fait plus sur un fond translucide. */
.saisie input, .form-exo input, .form-exo select, .form-fin input,
.form-couper input, .ajuster-corps input, .form-import textarea{
  box-shadow:inset 0 1px 2px rgba(0,0,0,.10);
}

/* Le tableau garde ses filets, mais pose ses lignes sur le verre plutôt que
   sur le fond nu : sans cela, une liste de 40 activités est le seul écran de
   l'app qui flotte sans support. */
.tableau-large{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--rayon); padding:4px 14px 6px;
  -webkit-backdrop-filter:var(--flou); backdrop-filter:var(--flou);
  box-shadow:var(--shadow), var(--lueur);
}

/* (Historique : mesuré en Sora, plus large qu'Archivo et que Barlow.) « 13 sept 2026 » passait sur deux lignes et
   chaque rangée y gagnait 20 px. La date ne s'enroule pas. */
table.activites td:first-child{white-space:nowrap}

/* ── L'écran de connexion ────────────────────────────────────────────────────
   Le seul écran de l'app qu'on voit sans être connecté, donc le seul qui n'a
   ni barre d'onglets ni enveloppe. La carte est CENTRÉE dans la fenêtre et non
   posée en haut : il n'y a rien d'autre sur la page, et un formulaire de deux
   champs collé au plafond laisse l'écran vide en dessous.

   Elle est étroite — 380 px — parce qu'un champ de saisie de 1100 px de large
   pour une adresse courriel n'aide personne à viser. */
.page-connexion{display:flex; min-height:100dvh; align-items:center; justify-content:center;
  padding:24px 20px}
.portail{width:100%; max-width:380px; display:flex; flex-direction:column; gap:18px}
.portail .marque{justify-content:center}
/* Centrer la BOÎTE ne centre pas le mot : dans le repère de 184, « Essor »
   occupe 8 → 114,2 (centre 61,1) et l'élan file jusqu'à 176. La boîte centrée,
   le mot tombait à 27 px à gauche de l'axe du formulaire — c'est lui que l'œil
   aligne. On décale de (92 − 61,1) / 184 = 16,8 % de la largeur du SVG, et
   l'élan déborde à droite. */
.portail .marque .signature{height:52px; transform:translateX(16.8%)}

.portail-erreur{
  margin:0; padding:10px 13px; border-radius:var(--rayon-2); font-size:13.5px;
  color:var(--crit); background:var(--surface); border:1px solid var(--line);
  border-left:3px solid var(--crit);
}

.form-portail{
  display:flex; flex-direction:column; gap:14px;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--rayon); padding:22px 20px;
  -webkit-backdrop-filter:var(--flou); backdrop-filter:var(--flou);
  box-shadow:var(--shadow), var(--lueur);
}
.form-portail label{
  display:flex; flex-direction:column; gap:5px; font-size:11px; color:var(--muted);
}
.form-portail input{
  font-family:inherit; font-size:16px;  /* 16 px : en dessous, iOS zoome sur le
    champ au foyer et la page ne revient jamais à sa taille d'origine. */
  padding:10px 12px; border-radius:var(--rayon-2);
  border:1px solid var(--line-strong); background:var(--surface-creux);
  color:var(--ink); box-shadow:inset 0 1px 2px rgba(0,0,0,.10);
}
.form-portail input:focus{outline:2px solid var(--accent); outline-offset:1px}
.form-portail button{
  font-family:inherit; font-size:15px; font-weight:600; cursor:pointer;
  padding:11px 14px; margin-top:4px; border-radius:var(--rayon-2);
  border:1px solid var(--accent); background:var(--accent); color:var(--sur-accent);
}
.form-portail button:hover{filter:brightness(1.06)}
.form-portail button:focus-visible{outline:2px solid var(--ring); outline-offset:2px}

/* Le bouton de déconnexion dans la barre. Il était un « Sortir » en petit
   texte gris souligné, rejeté seul sous les onglets sur téléphone — Jo l'a
   cherché sans le voir (2026-09-28). Un vrai bouton, à bord, et sur la
   PREMIÈRE rangée d'un écran étroit, à côté de la marque. */
.deconnexion{display:inline-flex; margin:0}
.deconnexion button{
  font-family:inherit; font-size:13px; font-weight:500; color:var(--ink-2);
  cursor:pointer; background:none; white-space:nowrap;
  border:1px solid var(--line-strong); border-radius:7px; padding:6px 10px;
}
.deconnexion button:hover{color:var(--ink); border-color:var(--ink-2)}
.deconnexion button:focus-visible{outline:2px solid var(--ring); outline-offset:2px}
@media (max-width:900px){
  /* marque · thème · déconnexion sur la première rangée, puis les onglets,
     puis le sous-titre (déjà en `order:3`). */
  .deconnexion{order:1; margin-left:auto}
  header.barre nav{order:2}
}

/* Le pied du portail — le lien vers l'autre porte. Discret : il ne doit pas
   concurrencer le bouton, seulement être là pour qui s'est trompé d'écran. */
.portail-pied{margin:0; text-align:center; font-size:13px; color:var(--muted)}
/* La politique de confidentialité : un document à lire, pas un formulaire. */
.portail.document{max-width:760px; padding:32px 0}
.document h1{margin:0; font-size:24px; font-weight:600; letter-spacing:-.01em}
.document h2{margin:18px 0 0; font-size:17px; font-weight:600}
.document p, .document li{margin:0; font-size:14px; line-height:1.6; color:var(--ink-2)}
.document ul{margin:0; padding-left:20px; display:grid; gap:4px}
.document .meta{font-size:12.5px; color:var(--muted)}
.document table{width:100%; border-collapse:collapse; font-size:13px}
.document th, .document td{text-align:left; vertical-align:top; padding:7px 8px;
  border-bottom:1px solid var(--line)}
.document th{font-weight:600; color:var(--ink)}
.document .encadre{border-left:3px solid var(--accent); padding:4px 0 4px 14px}
.pied-page{margin:40px 0 12px; text-align:center; font-size:12.5px}
.pied-page a{color:var(--muted); text-decoration:none}
.pied-page a:hover{text-decoration:underline}
/* `.form-portail label` (classe + élément) battait `.consentement` (classe
   seule) : les cases de l'inscription sortaient en colonne, en petites
   capitales de 11 px — une phrase de consentement illisible. */
.consentement, .form-portail label.consentement{display:flex; flex-direction:row;
  gap:10px; align-items:flex-start; font-size:12.5px; line-height:1.5;
  color:var(--ink-2); text-transform:none; letter-spacing:normal}
.consentement input{margin-top:3px; flex:none}
.form-portail .consentement input{padding:0; box-shadow:none}
.consentement-sorties{display:grid; gap:10px}
.consentement-sorties details{font-size:13px; color:var(--ink-2)}
.consentement-sorties summary{cursor:pointer; color:var(--accent)}
.consentement-sorties form{margin-top:10px}
.consentement-sorties button.lien{background:none; border:none; padding:0;
  color:var(--accent); cursor:pointer; font:inherit}
.consentement-sorties button.lien:hover{text-decoration:underline}
.consentement a{color:var(--accent)}
.portail-pied a{color:var(--accent); text-decoration:none}
.portail-pied a:hover{text-decoration:underline}

/* La règle du mot de passe, dite AVANT l'échec. */
.portail-aide{margin:-4px 0 0; font-size:12px; color:var(--muted); line-height:1.45}

/* La page d'arrivée (`essai.html`, 2026-10-23) : où mènent les annonces. Le
   portail, élargi : une colonne au téléphone, alignée à gauche ; au bureau,
   l'accroche et la capture côte à côte. La seule chose hardie est la VRAIE
   capture d'un aperçu — rien d'autre ne crie : pas de cartes de
   « fonctionnalités », pas d'animation. L'accroche en Barlow Semi Condensed,
   grande et serrée : le vocabulaire des dossards (`--chiffres`). Les étapes
   sont numérotées parce que c'en sont. */
.page-connexion:has(.portail.arrivee){align-items:flex-start}
.portail.arrivee{max-width:1040px; gap:56px; padding:8px 0 24px}
.portail.arrivee .marque{justify-content:flex-start}
.portail.arrivee .marque .signature{height:40px; transform:none}
.arrivee-tete{display:grid; gap:28px; align-items:center}
@media (min-width:900px){
  .arrivee-tete{grid-template-columns:minmax(0, 1fr) 360px; gap:56px}
}
.arrivee-accroche{display:grid; gap:16px; max-width:34rem}
.arrivee-accroche h1{margin:0; font-family:var(--chiffres); font-weight:700;
  font-size:clamp(40px, 11vw, 68px); line-height:.98; letter-spacing:-.01em; color:var(--ink)}
.arrivee-accroche p{margin:0}
.arrivee-chapo{font-size:17px; line-height:1.55; color:var(--ink-2)}
.arrivee-accroche a.bouton-lien{min-height:52px; padding:0 26px; font-size:16px; font-weight:600}
.arrivee-connexion{font-size:14px; color:var(--muted)}
.arrivee-connexion a, .arrivee-qui a{color:var(--accent)}
/* La capture : l'écran tel qu'il est, dans un cadre fin à la couleur de la
   distance de l'exemple — la seule note saturée de la page. */
.arrivee-capture{margin:0; display:grid; gap:10px; justify-self:center; width:min(100%, 360px)}
.arrivee-capture img{display:block; width:100%; height:auto; border-radius:14px;
  border:1px solid var(--line-strong); box-shadow:0 0 0 5px color-mix(in srgb, var(--d-sprint) 22%, transparent)}
.arrivee-capture figcaption{font-size:13px; color:var(--muted)}
.portail.arrivee h2{margin:0 0 16px; font-family:var(--chiffres); font-size:28px; font-weight:600}
/* Les étapes, et la première semaine du même aperçu à côté au bureau. */
.arrivee-etapes-corps{display:grid; gap:28px; align-items:start}
@media (min-width:900px){
  .arrivee-etapes-corps{grid-template-columns:minmax(0, 1fr) 360px; gap:56px}
}
.arrivee-etapes ol{margin:0; padding:0; list-style:none; counter-reset:etape;
  display:grid; gap:18px; max-width:40rem}
.arrivee-etapes li{counter-increment:etape; display:grid; grid-template-columns:44px 1fr;
  column-gap:14px; row-gap:2px}
.arrivee-etapes li::before{content:counter(etape); grid-row:span 2; font-family:var(--chiffres);
  font-variant-numeric:tabular-nums; font-size:34px; font-weight:700; line-height:1; color:var(--ink)}
.arrivee-etapes b{font-size:16px}
.arrivee-etapes span{color:var(--ink-2)}
/* Le partiel des formules compte sur l'espacement de sa page (`.bloc` de
   `/prix`) : ici, la section le lui donne — sans lui, « Comment ton plan est
   écrit » collait aux cartes (vu en capture, 2026-10-23). Des marges, pas une
   grille : en grille, ses enfants prenaient leur largeur minimale, et la
   page débordait de 390 px (vu en capture, le même jour). */
.arrivee-formules > * + *{margin-top:24px}
.portail.arrivee .arrivee-formules h2{margin:0}
.arrivee-questions{display:grid; gap:10px; max-width:40rem}
.arrivee-questions h2{margin-bottom:6px}
.arrivee-questions details{border-bottom:1px solid var(--line); padding:4px 0 12px}
.arrivee-questions summary{cursor:pointer; font-weight:600; padding:6px 0}
.arrivee-questions summary:focus-visible{outline:2px solid var(--ring); outline-offset:2px}
.arrivee-questions p{margin:4px 0 0; color:var(--ink-2)}
.arrivee-qui{margin:0; font-size:14px; color:var(--muted)}

/* ── Écran de réglages ───────────────────────────────────────────────────────
   Des sections empilées plutôt qu'une grille : chaque réglage est une décision
   qu'on prend seule, et les mettre côte à côte inviterait à les survoler. */
.carte-reglage{
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--rayon); padding:16px 18px; margin-bottom:22px;
  -webkit-backdrop-filter:var(--flou); backdrop-filter:var(--flou);
  box-shadow:var(--shadow), var(--lueur);
}
.carte-reglage .note{font-size:12.5px; color:var(--muted); line-height:1.5;
  margin:10px 0 0; max-width:62ch}
.carte-reglage .form-exo{background:none; border:none; padding:0; margin-top:12px}
.carte-reglage table.activites{margin-bottom:10px}
.carte-reglage td.agent{font-size:12px; color:var(--muted);
  max-width:42ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

.reglage-etat{display:flex; align-items:baseline; gap:10px}
.reglage-etat .etiquette{font-size:11px; color:var(--muted)}

/* PRÉSENT et ABSENT se distinguent par la FORME autant que par la couleur —
   même règle que les repères supposés du §interface. Un point vert seul ne dit
   rien à qui ne le voit pas. */
.marque-etat{font-size:14px; font-weight:600; padding:1px 9px 2px;
  border-radius:999px; border:1px solid}
.marque-etat.posee{color:var(--good); border-color:var(--good);
  background:color-mix(in srgb, var(--good) 12%, transparent)}
.marque-etat.absente{color:var(--warn); border-color:var(--warn);
  border-style:dashed; font-style:italic; background:none}

/* Le message d'issue après une action. Il disparaît au rechargement suivant :
   c'est un accusé de réception, pas un état. */
.issue{margin:0 0 20px; padding:10px 14px; border-radius:var(--rayon-2);
  font-size:13.5px; background:var(--surface); border:1px solid var(--line);
  border-left:3px solid var(--muted)}
.issue.bon{border-left-color:var(--good)}
.issue.mal{border-left-color:var(--crit); color:var(--crit)}

/* ── Écran des appareils (`/sources`, 2026-10-23) ───────────────────────────
   UNE liste à filets, pas cinq cartes : cinq réponses à la même question,
   « d'où viennent mes séances ? ». Aucune couleur de sport — une source n'en
   est pas un. Le nom du fournisseur et les chiffres en Barlow, comme un
   dossard ; l'état par `.marque-etat`, qui le dit par la forme. */
.sources-derniere{display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 12px;
  margin:0 0 18px}
.sources-derniere .etiquette{font-size:11px; color:var(--muted); width:100%}
.sources-derniere-valeur{font-family:var(--chiffres); font-size:22px; font-weight:600;
  color:var(--ink); text-decoration:none; line-height:1.15}
.sources-derniere-valeur:hover{text-decoration:underline}
.sources-origine{font-size:13px; color:var(--muted)}
.sources-lecture{margin:0 0 16px}
.sources{background:var(--surface); border:1px solid var(--line);
  border-radius:var(--rayon); box-shadow:var(--shadow), var(--lueur); margin-bottom:28px}
.source{padding:16px 18px}
.source + .source{border-top:1px solid var(--line)}
.source-tete{display:flex; align-items:baseline; justify-content:space-between; gap:12px}
.source-nom{margin:0; font-family:var(--chiffres); font-size:19px; font-weight:600;
  letter-spacing:0}
.source .note{font-size:12.5px; color:var(--muted); line-height:1.5; margin:8px 0 0; max-width:62ch}
.source-bilan{margin:6px 0 0; font-size:13px; color:var(--ink-2)}
.source-chiffre{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:16px; font-weight:600; color:var(--ink)}
.source-erreur{margin:8px 0 0; font-size:12.5px; color:var(--crit); max-width:62ch}
.source-gestes{display:flex; flex-wrap:wrap; align-items:flex-end; gap:10px; margin-top:12px}
.source-gestes form{margin:0}
.source-cle label, .source-fichiers{display:flex; flex-direction:column; gap:4px;
  font-size:11px; font-weight:600; color:var(--muted)}
.source-cle input{font-family:var(--texte); font-size:15px; width:min(320px, 70vw);
  padding:10px 12px; min-height:44px; border:1px solid var(--line-strong);
  border-radius:var(--rayon-2); background:var(--surface-2); color:var(--ink)}
.source-cle input:focus{outline:2px solid var(--accent); outline-offset:1px}
details.source-delier > summary{cursor:pointer; font-size:13px; color:var(--muted);
  min-height:44px; display:flex; align-items:center}
details.source-delier > summary:hover{color:var(--ink)}
details.source-delier form{display:flex; flex-direction:column; gap:10px; align-items:flex-start;
  padding:4px 0 2px}
details.source-delier label.case{display:flex; gap:8px; align-items:flex-start;
  font-size:13px; color:var(--ink-2)}
/* Ce qui ne se relie pas aujourd'hui recule : une ligne, pas la taille d'une
   option réelle. */
.source-absente{padding:12px 18px}
.source-absente .source-nom{font-size:16px; color:var(--ink-2)}
.source-absente .note{margin-top:4px}
.source-depot{display:flex; flex-wrap:wrap; align-items:flex-end; gap:12px}
.source-fichiers input{font-family:var(--texte); font-size:13px; color:var(--ink-2)}
/* Le bouton natif du choix de fichier porte la forme de `button.secondaire` :
   sans ça, il arrive dans la langue et le style du navigateur, seul à jurer
   sur la page. */
.source-fichiers input::file-selector-button{font-family:inherit; font-size:14px;
  font-weight:500; margin-right:10px; padding:10px 16px; min-height:44px;
  border:1px solid var(--line-strong); border-radius:8px; background:var(--surface);
  color:var(--ink-2); cursor:pointer}
.source-fichiers input::file-selector-button:hover{border-color:var(--accent); color:var(--accent)}
@media (max-width:560px){
  .source, .source-absente{padding:14px}
  .source-gestes, .source-depot{flex-direction:column; align-items:stretch}
  .source-gestes form button, .source-gestes > button, .source-depot > button,
  .source-cle input{width:100%}
}

/* Le texte de machine de l'écran de réglages — adresse, agent. Discret : on le
   lit pour reconnaître un appareil, pas pour l'admirer. */
.carte-reglage code{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:12px; color:var(--ink-2); background:none; padding:0}

/* Les écrans de message du portail — confirmation, lien périmé. */
.portail-titre{margin:0 0 4px; font-size:19px; font-weight:600; letter-spacing:-.01em}
.portail-texte{margin:0; font-size:14px; line-height:1.55; color:var(--ink-2)}
.bouton-portail{
  display:block; text-align:center; text-decoration:none; margin-top:6px;
  font-size:15px; font-weight:600; padding:11px 14px; border-radius:var(--rayon-2);
  border:1px solid var(--accent); background:var(--accent); color:var(--sur-accent);
}
.bouton-portail:hover{filter:brightness(1.06)}

/* Le bandeau d'adresse non confirmée. Il vit en haut de CHAQUE écran, et il
   ne se ferme pas : ce n'est pas une notification, c'est un état — tant qu'il
   dure, « mot de passe oublié » ne peut pas fonctionner. */
.bandeau-verif{
  display:flex; flex-wrap:wrap; align-items:center; gap:10px 14px;
  margin:0 0 20px; padding:10px 14px; font-size:13.5px;
  border-radius:var(--rayon-2); background:var(--surface);
  border:1px solid var(--line); border-left:3px solid var(--warn);
}
.bandeau-verif form{display:inline; margin:0}
.bandeau-verif button{
  font:inherit; font-weight:600; color:var(--accent); cursor:pointer;
  background:none; border:none; padding:0; text-decoration:underline;
  text-underline-offset:3px;
}

/* La carte de suppression. Elle ne crie pas — pas de fond rouge : un écran
   qui hurle se lit moins bien, et cette action se prend calmement. Un liseré
   suffit à dire qu'on n'est plus dans les réglages ordinaires. */
.carte-reglage.danger{border-left:3px solid var(--crit)}
.form-exo button.destructif{
  background:none; border-color:var(--crit); color:var(--crit); font-weight:600;
}
.form-exo button.destructif:hover{background:var(--crit); color:var(--sur-accent)}

/* ── Accueil : calibrer un compte neuf ───────────────────────────────────── */
.calib-etape{padding:14px 0; border-top:1px solid var(--line)}
.calib-etape:first-of-type{border-top:none}
.calib-etape header{display:flex; flex-wrap:wrap; align-items:center; gap:4px 0}
.calib-etape h3{margin:0; font-size:15px}
.calib-depend{margin:4px 0 0; font-size:12.5px; color:var(--muted); max-width:70ch}
.calib-valeurs{list-style:none; margin:8px 0 0; padding:0; font-size:13.5px}
.calib-valeurs li{margin:2px 0}
.calib-libelle{color:var(--muted); margin-right:6px}
.calib-date{margin-left:6px; font-size:12px; color:var(--muted)}
.calib-protocole{margin:8px 0; padding-left:20px; font-size:13.5px; max-width:70ch}
.calib-erreur{margin:8px 0 0; font-size:13.5px; color:var(--warn)}
.calib-ok{margin:8px 0 0; font-size:13.5px; color:var(--good); font-weight:600}
.calib-connue{margin-top:8px; font-size:13.5px}
.calib-connue summary{cursor:pointer; color:var(--accent)}
.calib-parq{display:flex; gap:8px; align-items:flex-start; font-size:13px; max-width:60ch}
.calib-parq input{width:auto; min-height:0; margin-top:3px}
.calib-muscu{margin:14px 0 0; font-size:13px; color:var(--muted)}
/* ── Premiers pas : le parcours en quatre étapes ────────────────────────────
   Des pastilles numérotées, comme le questionnaire du plan : la même forme
   pour la même idée — « une étape à la fois ». Faite : ✓ à la place du
   numéro. Montrée : le liseré d'accent. */
.parcours{list-style:none; margin:0 0 18px; padding:0; display:flex; flex-wrap:wrap; gap:6px 8px}
.parcours a{display:flex; align-items:center; gap:8px; padding:6px 12px 6px 6px;
  border-radius:999px; border:1px solid var(--line); background:var(--surface);
  font-size:13px; color:var(--muted); text-decoration:none}
.parcours a:hover{border-color:var(--accent); color:var(--accent)}
.parcours-no{display:inline-grid; place-items:center; width:22px; height:22px;
  border-radius:50%; background:var(--surface-2); border:1px solid var(--line);
  font-weight:600; font-size:12px}
.parcours li.faite a{color:var(--ink-2)}
.parcours li.faite .parcours-no{background:var(--good); border-color:var(--good); color:var(--sur-accent)}
.parcours li.montree a{border-color:var(--accent); color:var(--accent);
  background:var(--accent-soft); font-weight:600}
.parcours li.montree .parcours-no{border-color:var(--accent)}
.parcours-tdb{margin:0 0 20px; padding:12px 14px; border-radius:var(--rayon-2);
  background:var(--surface); border:1px solid var(--line); border-left:3px solid var(--accent)}
.parcours-tdb p{margin:0 0 10px; font-size:14px}
.parcours-tdb .parcours{margin-bottom:12px}
a.bouton-lien{display:inline-flex; align-items:center; min-height:44px; padding:0 20px;
  border-radius:8px; background:var(--good); color:var(--sur-accent); font-weight:600;
  font-size:14px; text-decoration:none}
a.bouton-lien:hover{filter:brightness(1.06)}
.parcours-suite{display:flex; justify-content:space-between; align-items:center;
  flex-wrap:wrap; gap:10px; margin-top:22px; padding-top:14px; border-top:1px solid var(--line)}
.parcours-fait{font-size:14px}
.parcours-echelles{display:grid; grid-template-columns:max-content 1fr; gap:6px 16px;
  margin:4px 0 12px; font-size:14px}
.parcours-echelles dt{color:var(--muted)}
.parcours-echelles dd{margin:0}
.parcours-fonctions{margin:0; padding-left:22px; display:grid; gap:14px; max-width:70ch}
.parcours-fonctions h3{display:inline; margin:0 8px 0 0; font-size:15px}
.parcours-fonctions p{margin:4px 0; font-size:13.5px; color:var(--ink-2)}
.parcours-fonctions a{font-size:13.5px}
/* Les tests physiques : une carte repliable par sport, une seule ouverte. */
.test-parq{margin:0 0 14px; font-size:13.5px; max-width:70ch; color:var(--ink-2)}
details.test{border:1px solid var(--line); border-radius:var(--rayon-2);
  background:var(--surface); margin:0 0 10px}
details.test[open]{border-color:var(--accent)}
details.test > summary{display:flex; align-items:center; gap:12px; padding:12px 14px;
  cursor:pointer; list-style:none}
details.test > summary::-webkit-details-marker{display:none}
.test-no{display:inline-grid; place-items:center; flex:none; width:26px; height:26px;
  border-radius:50%; border:1px solid var(--line-strong); font-weight:600; font-size:13px}
details.test[open] .test-no{border-color:var(--accent); color:var(--accent)}
.test-titre{flex:1; display:flex; flex-direction:column; gap:2px; font-size:15px}
.test-meta{font-size:12.5px; color:var(--muted)}
.test-corps{padding:0 14px 14px 52px}
.test-corps h4{margin:14px 0 4px; font-size:12px; color:var(--muted)}
.test-corps .calib-valeurs{margin-top:0}
@media (max-width:600px){ .test-corps{padding-left:14px} }
.calib-jours{border:none; padding:0; margin:0; display:flex; flex-wrap:wrap; gap:6px 14px}
.calib-jours legend{font-size:13px; color:var(--muted); margin-bottom:4px}
.calib-jours label{display:flex; gap:6px; align-items:center; font-size:13.5px}
.calib-jours input{width:auto; min-height:0}

/* Le questionnaire de création : une question à la fois. Les réponses déjà
   données restent en haut, chacune modifiable ; la question courante se
   détache, les suivantes s'effacent. */
.questionnaire-etapes{list-style:none; margin:0 0 18px; padding:0; display:flex;
  flex-wrap:wrap; gap:6px 8px; counter-reset:q}
.questionnaire-etapes li{counter-increment:q; display:flex; align-items:baseline; gap:6px;
  padding:6px 12px; border-radius:999px; border:1px solid var(--line);
  background:var(--surface); font-size:13px; color:var(--muted)}
.questionnaire-etapes li::before{content:counter(q); font-weight:600}
/* La pastille des étapes à venir n'est pas une étape : sans numéro. */
.questionnaire-etapes li.reste::before{content:none}
.questionnaire-etapes li.faite{color:var(--ink-2)}
.questionnaire-etapes li.courante{border-color:var(--accent); color:var(--accent);
  background:var(--accent-soft); font-weight:600}
.questionnaire-etapes .q-valeur{color:var(--ink); font-weight:600}
.questionnaire-etapes .q-modifier{font-size:12px}
.questionnaire{flex-direction:column; align-items:stretch; gap:14px}
.questionnaire > button{align-self:flex-start}
.questionnaire .aide-param{margin:0}
.questionnaire .aide-parties{padding-left:18px; display:grid; gap:4px; line-height:1.45}
.choix{border:none; padding:0; margin:0; display:grid; gap:10px; width:100%;
  grid-template-columns:repeat(auto-fit, minmax(210px, 1fr))}
.choix legend{font-weight:600; margin-bottom:10px; font-size:15px; color:var(--ink)}
/* Ce qui a été fait, sport par sport (point de contrôle, 2026-10-21) : une
   rangée par sport, quatre pastilles — une question qui se répond d'un pouce,
   au téléphone, sans rien avoir noté. */
.fait-sports{border:none; padding:0; margin:0; display:flex; flex-direction:column; gap:10px}
.fait-sports legend{font-weight:600; margin-bottom:6px; font-size:15px; color:var(--ink)}
.fait-sport{display:grid; grid-template-columns:7.5em repeat(4, minmax(0, 1fr)); gap:6px;
  align-items:stretch}
.fait-nom{font-family:var(--chiffres); font-size:17px; font-weight:600; align-self:center}
/* La forme des cartes de choix voisines (`label.choix-carte`), en plus
   serré. Le `display` explicite : une règle du questionnaire remet les
   libellés en bloc, et le bouton restait en haut (mesuré dans Chrome). */
.form-exo label.fait-choix{display:flex; flex-direction:row; align-items:center; gap:8px; min-height:44px;
  padding:8px 10px; border:1px solid var(--line); border-radius:var(--rayon-2);
  background:var(--surface-2); cursor:pointer; text-transform:none; letter-spacing:0;
  font-size:13.5px; font-weight:400; color:var(--ink); line-height:1.25}
.form-exo label.fait-choix:has(input:checked){border-color:var(--accent);
  background:var(--accent-soft); font-weight:600}
.form-exo label.fait-choix input{min-height:0; margin:0; flex:none}
@media (max-width:720px){
  .fait-sport{grid-template-columns:repeat(2, minmax(0, 1fr))}
  .fait-nom{grid-column:1 / -1}
}
/* Refaire un test : une proposition, pas une alerte — ni rouge ni vert. */
.test-propose{display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:10px 18px; padding:14px 16px; border:1px solid var(--line); border-left:4px solid var(--ink);
  border-radius:var(--rayon); background:var(--surface); margin:0 0 16px}
.test-propose p{margin:0; max-width:62ch}
/* Le texte libre des précisions : un libellé qui se lit comme la question des
   cartes, et un champ creusé comme les autres saisies. */
.form-exo label.q-libelle{font-weight:600; font-size:15px; color:var(--ink);
  text-transform:none; letter-spacing:0}
.questionnaire textarea{width:100%; font:inherit; font-size:14.5px; line-height:1.5;
  background:var(--surface-2); color:var(--ink); border:1px solid var(--line-strong);
  border-radius:8px; padding:10px 12px; resize:vertical;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.10)}
.questionnaire textarea:focus{outline:2px solid var(--accent); outline-offset:1px}
/* Un menu prend la largeur de sa plus longue option : celui des courses
   (« Challenge CAP Québec — demi (1,9 / 90 / 21,1 km) — dimanche… ») sortait
   de la carte au téléphone. Tronqué fermé ; la liste native montre tout. */
.questionnaire select{width:100%; max-width:100%; min-width:0; text-overflow:ellipsis}

/* L'accueil d'un plan se lit au téléphone, une question à la fois (Jo,
   2026-10-09 : « les polices sont souvent petites »). Les libellés compacts
   de `.form-exo` — 10,5 px, faits pour saisir une série entre deux charges —
   y passent à la taille du texte, « facultatif » sur la même ligne ; chaque
   champ à 16 px : en dessous, Safari sur iPhone zoome la page au toucher. */
.questionnaire{gap:18px}
/* À l'ordinateur, la carte s'étalait sur toute la page : deux cartes oui/non
   de 600 px, des points de douleur de 100 px, des questions perdues (Jo,
   2026-10-09 : « pas juste au téléphone »). Une largeur de lecture ; l'aperçu,
   hors du formulaire, garde la sienne. Collée à gauche, elle laissait la
   moitié de l'écran vide (Jo, 2026-10-18 : « sur PC les questions ne sont
   pas centered ») : toute la colonne — en-tête, étape, carte — se centre ; le
   texte reste aligné à gauche dedans. */
.questionnaire, .q-ou{max-width:680px}
.q-colonne{max-width:680px; margin-inline:auto}
.questionnaire label{display:block; font-size:15px; font-weight:500; color:var(--ink-2);
  line-height:1.4}
.questionnaire label small{font-size:13px; font-weight:400; color:var(--muted)}
.questionnaire label > input:not([type=checkbox]):not([type=radio]),
.questionnaire label > select{display:block; margin-top:6px}
.questionnaire input, .questionnaire select{font-size:16px; min-height:44px}
.questionnaire input[type=text], .questionnaire input[type=date],
.questionnaire input[type=month]{width:100%; max-width:24em}
.questionnaire input[type=number]{width:8em; font-size:17px}
.questionnaire .aide-param{font-size:14px; line-height:1.5}
.questionnaire .choix legend, .questionnaire .pratique-sport legend{font-size:17px;
  font-weight:600; color:var(--ink); line-height:1.35}
.questionnaire .pratique-sport{gap:14px; padding:12px 16px 16px; align-items:end}
.questionnaire .calib-jours{gap:12px 20px}
.questionnaire .calib-jours legend{font-size:17px; font-weight:600; color:var(--ink);
  margin-bottom:8px}
/* `.form-exo label` empile en colonne : une case passait au-dessus de son nom. */
.questionnaire .calib-jours label, .questionnaire label.case{display:flex;
  flex-direction:row; align-items:center; gap:10px; font-size:15px}
/* Les douleurs du point de contrôle : un sport par ligne, le champ dans sa
   colonne — chaque nom poussait le sien (vu à 390 px, 2026-10-28). */
.questionnaire .calib-jours.douleurs{display:grid; gap:10px}
.questionnaire .calib-jours.douleurs label{display:grid; grid-template-columns:8.5em 6em;
  align-items:center; gap:12px}
.questionnaire .calib-jours.douleurs input{width:100%}
.questionnaire .pratique-temps select{flex:1; min-width:0; width:auto}
.questionnaire .pratique-temps input{width:6.5em; flex:none}
.questionnaire fieldset.douleur legend small{display:block; margin-top:2px; font-size:14px;
  font-weight:400; color:var(--muted)}
.questionnaire button{font-size:16px; min-height:46px; padding:10px 20px}
/* Une case à cocher : la case, puis sa phrase — `.form-exo` en faisait une
   grande boîte vide centrée au-dessus de son texte (vu sur la capture). */
.questionnaire label.case{display:flex; gap:10px; align-items:flex-start;
  color:var(--ink); font-weight:400}
.questionnaire input[type=checkbox], .questionnaire input[type=radio]{width:20px;
  height:20px; min-height:0; padding:0; margin:0; flex:none; accent-color:var(--ink)}
.questionnaire label.case input{margin-top:1px}
/* Une question longue (le dépistage) : en texte courant, pas en titre gras. */
.questionnaire fieldset.question legend{font-size:16px; font-weight:500}
.q-signes{grid-column:1 / -1; margin:0 0 4px; padding-left:20px; display:grid; gap:3px;
  font-size:15px; color:var(--ink-2); line-height:1.4}
/* L'accord d'un médecin ne compte qu'après un « oui » au dépistage : il ne
   paraît qu'alors (2026-10-14, PR #5). Les deux questions par leur NOM — un
   « oui » à « horaires irréguliers », sur la même étape, ne l'ouvre pas.
   Sans `:has`, la règle entière tombe et l'accord reste visible : on ne le
   cache jamais à tort. */
.sante-accord{display:grid; gap:8px}
.questionnaire:not(:has(input[name=maladie][value=oui]:checked,
  input[name=symptomes][value=oui]:checked)) .sante-accord{display:none}

/* Où on en est dans l'accueil : l'étape en clair, les réponses repliées. */
.q-ou{margin:0 0 18px}
.q-etape{margin:0; font-family:var(--chiffres); font-size:22px; font-weight:600;
  color:var(--ink); line-height:1.2}
.q-numero{display:block; font-size:15px; font-weight:500; color:var(--muted)}
details.q-reponses{margin-top:10px}
details.q-reponses > summary{cursor:pointer; font-size:15px; color:var(--ink-2); padding:4px 0}
details.q-reponses .questionnaire-etapes{margin:10px 0 8px}
details.q-reponses .questionnaire-etapes li{font-size:14px}

/* La douleur, sport par sport : une échelle à toucher. Deux rangées au
   téléphone (des points de 44 px), une seule au-delà. Le point choisi à 5
   et plus passe au rouge — le seuil où le code gèle le sport. */
.questionnaire fieldset.douleur{border:none; padding:0; margin:0; display:grid; gap:14px}
.questionnaire fieldset.douleur legend{font-size:17px; font-weight:600; color:var(--ink);
  margin-bottom:6px}
.echelle{display:grid; gap:6px}
.echelle-sport{font-size:15px; font-weight:500; color:var(--ink-2)}
.echelle-points{display:grid; grid-template-columns:repeat(6, minmax(0, 1fr)); gap:6px}
.questionnaire .echelle-points label{position:relative; display:block; margin:0}
.questionnaire .echelle-points input{position:absolute; opacity:0; inset:0; width:100%;
  height:100%; margin:0; cursor:pointer}
.echelle-points span{display:flex; align-items:center; justify-content:center; height:44px;
  border:1px solid var(--line-strong); border-radius:var(--rayon-2);
  background:var(--surface-2); font-family:var(--chiffres); font-size:18px; color:var(--ink)}
.echelle-points input:focus-visible + span{outline:2px solid var(--ring); outline-offset:2px}
.echelle-points input:checked + span{background:var(--ink); border-color:var(--ink);
  color:var(--surface); font-weight:600}
.echelle-points label:nth-child(n+6) input:checked + span{background:var(--crit);
  border-color:var(--crit)}
@media (min-width:560px){
  .echelle-points{grid-template-columns:repeat(11, minmax(0, 1fr))}
}
/* La pratique déclarée : un cadre par sport, ses champs côte à côte quand la
   largeur le permet, empilés sur téléphone. */
.pratique-sport{border:1px solid var(--line); border-radius:var(--rayon-2);
  padding:10px 14px 14px; margin:0; width:100%; display:grid; gap:10px;
  grid-template-columns:repeat(auto-fit, minmax(170px, 1fr))}
.pratique-sport legend{font-weight:600; font-size:14.5px; color:var(--ink); padding:0 4px}
.pratique-temps{display:flex; gap:6px}
.pratique-temps select{flex:1}
.pratique-temps input{width:6.5em}
/* Les curseurs (Jo, 2026-10-23 : « une slide bar pour les questions
   applicables » ; `essor.js`, « Les curseurs du questionnaire »). La valeur en
   grand au-dessus de la piste, en chiffres ; la piste prend la couleur du
   sport quand il y en a un. Un curseur jamais touché n'a pas de réponse : sa
   valeur se tait et sa piste s'estompe — la position de départ ne se lit pas
   comme une réponse (OBSERVÉ et SUPPOSÉ, §interface). Le champ d'origine,
   qui part au serveur, sort de la vue ; sans script, il reste le seul. */
.curseur{--sport:var(--ink); display:grid; gap:4px; min-width:0}
.curseur[data-sport=velo]{--sport:var(--s1)} .curseur[data-sport=course]{--sport:var(--s2)}
.curseur[data-sport=nage]{--sport:var(--s3)} .curseur[data-sport=muscu]{--sport:var(--s4)}
.curseur[data-arme] .curseur-source{position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; opacity:0}
/* Ce qui n'aide qu'à taper (« minutes », « m:ss ») se tait : le curseur dit
   sa valeur en mots. */
.curseur[data-arme] .curseur-saisie{display:none}
.pratique-sport > .curseur{grid-column:1 / -1}
.curseur-tete{display:flex; align-items:baseline; justify-content:space-between; gap:12px}
.curseur-valeur{font-family:var(--chiffres); font-size:24px; font-weight:600; line-height:1.1;
  color:var(--sport); font-variant-numeric:tabular-nums}
.curseur.curseur-vide .curseur-valeur{font-family:var(--texte); font-size:15px; font-weight:400;
  color:var(--muted)}
.curseur.curseur-a-regler .curseur-valeur{color:var(--crit); font-weight:600}
/* `.form-exo input` donne un cadre et un fond à tout champ : pas à la piste. */
.form-exo .curseur input[type=range]{width:100%; min-height:36px; margin:0; padding:0;
  border:none; background:none; box-shadow:none; accent-color:var(--sport); cursor:pointer}
.curseur.curseur-vide input[type=range]{accent-color:var(--muted); opacity:.55}
.curseur input[type=range]:focus-visible{outline:2px solid var(--ring); outline-offset:2px}
.curseur-bornes{display:flex; justify-content:space-between; font-family:var(--chiffres);
  font-size:13px; color:var(--muted); font-variant-numeric:tabular-nums}
.questionnaire .curseur-effacer{min-height:0; padding:2px 0; font-size:13px}
/* « Je ne sais pas » coché : la plus longue séance ne compte pas. */
.pratique-sport:has(input[name$="_pas_sur"]:checked) :is(.curseur-longue, .q-sans-arret){opacity:.4}
/* Des cases plutôt qu'un texte (2026-10-23) : des pastilles, la case visible
   dedans — la couleur ne dit pas seule le choix ; « Autre » dessous. */
.q-cases{border:none; padding:0; margin:0; display:grid; gap:10px; min-width:0}
.questionnaire .q-cases legend{font-size:17px; font-weight:600; color:var(--ink);
  line-height:1.35; margin-bottom:8px; padding:0}
.q-pastilles{display:flex; flex-wrap:wrap; gap:8px}
.form-exo label.q-pastille{display:inline-flex; flex-direction:row; align-items:center; gap:8px;
  min-height:44px; padding:8px 14px; border:1.5px solid var(--line-strong); border-radius:var(--rayon-2);
  background:var(--surface-2); cursor:pointer; text-transform:none; letter-spacing:0;
  font-size:15px; font-weight:400; color:var(--ink)}
.form-exo label.q-pastille:has(input:checked){border-color:var(--accent); background:var(--accent-soft);
  font-weight:600}
.form-exo label.q-pastille:has(input:focus-visible){outline:2px solid var(--ring); outline-offset:2px}
/* `.form-exo label` met ses libellés en petites capitales, en colonne : une
   carte de choix n'est pas un champ, elle se lit comme une phrase. */
.form-exo label.choix-carte{display:grid; grid-template-columns:auto 1fr; gap:2px 10px;
  align-items:center; padding:14px 16px; border:1px solid var(--line);
  border-radius:var(--rayon-2); background:var(--surface-2); cursor:pointer;
  text-transform:none; letter-spacing:0; font-size:15px; font-weight:400; color:var(--ink)}
.form-exo label.choix-carte input{min-height:0; margin:0}
.form-exo label.choix-carte b{font-weight:600}
.form-exo label.choix-carte:has(input:checked){border-color:var(--accent); background:var(--accent-soft)}
.form-exo label.choix-carte span{grid-column:2; font-size:12.5px; color:var(--muted)}
.form-exo label.choix-carte.indisponible{opacity:.5; cursor:not-allowed}
/* Les cartes de distance (Jo, 2026-10-22, d'après l'app Stamina : « que ça
   prenne de la place, des couleurs pour les différents types »). Un dossard :
   le nom en grand dans la couleur de la distance, quatre trous d'épingle aux
   coins, les épreuves avec leur pictogramme. La couleur n'est pas seule à dire
   le choix : le bouton radio reste visible, teinté de la distance. */
.questionnaire .choix-distance{grid-template-columns:1fr; gap:14px}
.d-sprint{--dist:var(--d-sprint)} .d-olympique{--dist:var(--d-olympique)} .d-703{--dist:var(--d-703)}
.form-exo label.carte-distance{position:relative; display:grid; grid-template-columns:1fr auto;
  align-content:space-between; gap:16px 12px; min-height:clamp(176px, 25vh, 212px);
  padding:20px 20px 18px; border:1.5px solid color-mix(in srgb, var(--dist) 45%, transparent);
  border-radius:var(--rayon); cursor:pointer; text-transform:none; letter-spacing:0;
  font-size:15px; font-weight:400; color:var(--ink);
  background:linear-gradient(155deg, color-mix(in srgb, var(--dist) 13%, var(--surface)) 0%,
    var(--surface) 72%)}
.form-exo label.carte-distance::after{content:""; position:absolute; inset:9px; pointer-events:none;
  --trou:radial-gradient(circle, var(--page) 0 2.5px,
    color-mix(in srgb, var(--dist) 50%, transparent) 2.5px 3.5px, transparent 4px);
  background:var(--trou) top left / 8px 8px no-repeat, var(--trou) top right / 8px 8px no-repeat,
    var(--trou) bottom left / 8px 8px no-repeat, var(--trou) bottom right / 8px 8px no-repeat}
.form-exo label.carte-distance:has(input:checked){border:2px solid var(--dist);
  padding:19.5px 19.5px 17.5px;
  background:linear-gradient(155deg, color-mix(in srgb, var(--dist) 24%, var(--surface)) 0%,
    color-mix(in srgb, var(--dist) 6%, var(--surface)) 80%)}
.form-exo label.carte-distance:has(input:focus-visible){outline:2px solid var(--ring); outline-offset:3px}
.form-exo label.carte-distance input{grid-column:2; grid-row:1; align-self:start; margin:4px 0 0;
  width:22px; height:22px; min-height:0; accent-color:var(--dist)}
.carte-distance .nom-distance{grid-column:1; grid-row:1; font-family:var(--chiffres);
  font-size:clamp(42px, 12vw, 58px); font-weight:700; line-height:.95; color:var(--dist);
  font-variant-numeric:tabular-nums}
.carte-distance .epreuves{grid-column:1 / -1; display:flex; flex-wrap:wrap; gap:8px clamp(10px, 3vw, 16px);
  font-family:var(--chiffres); font-size:clamp(17px, 5vw, 19px); font-weight:600; font-variant-numeric:tabular-nums}
.carte-distance .epreuves > span{display:inline-flex; align-items:center; gap:5px; white-space:nowrap}
.carte-distance .icone-sport{width:22px; height:22px; flex:none; color:var(--sport)}
.carte-distance .preparation{grid-column:1 / -1; font-size:14px; line-height:1.4; color:var(--muted)}
/* Un oui ou non (l'accueil du plan, 2026-10-09) : deux cartes côte à côte,
   la question au-dessus — une douzaine de questions fermées ne doivent pas
   faire défiler deux pleines cartes chacune au téléphone. */
.form-exo fieldset.oui-non{display:grid; grid-template-columns:1fr 1fr; gap:8px}
.form-exo fieldset.oui-non legend{grid-column:1 / -1}
.form-exo fieldset.oui-non label.choix-carte{padding:10px 14px}
/* Les détails d'un « oui » (blessures, courses faites) se taisent après un
   « non ». Sans `:has`, ils restent visibles : rien ne se perd. */
.form-exo fieldset.oui-non:has(input[value="non"]:checked) + .si-oui{display:none}
.form-exo .si-oui{display:grid; gap:12px}
/* Le résumé de la demande (la confirmation de l'accueil) : ce que l'IA a
   compris — une affirmation à valider, citée et attribuée comme la raison
   d'un écart, pas un encadré d'alerte (§interface). */
.resume-demande{margin:0 0 6px; max-width:68ch}
.resume-demande figcaption{font-size:13px; color:var(--muted); margin-bottom:6px}
.resume-demande blockquote{margin:0; padding:2px 0 2px 14px;
  border-left:3px solid var(--line-strong)}
.resume-demande blockquote p{margin:0 0 6px; font-size:15px; line-height:1.5; color:var(--ink)}
/* Ce que la personne a déclaré, écrit par le code (2026-10-20) : hors de la
   citation de l'IA, sans son filet — un fait, pas une affirmation à valider. */
.resume-declare{margin:4px 0 14px; max-width:68ch; font-size:14px; color:var(--ink-2)}

/* Les variantes du catalogue pour un autre matériel — repliées. */
details.variantes{margin-top:18px}
details.variantes > summary{cursor:pointer; color:var(--ink-2); font-size:14px;
  padding:6px 0}
details.variantes[open] > summary{margin-bottom:10px}

/* ── Le coach ────────────────────────────────────────────────────────────────
   Une proposition de l'IA est du SUPPOSÉ (§1) : la carte a une bordure en
   POINTILLÉS, la même forme que `.origine-seuil.suppose` — lisible en niveaux
   de gris, pas seulement par la couleur (§interface). */
.proposition-ia{border:1px dashed var(--line-strong); margin-bottom:12px}
.proposition-ia h3{margin:0 0 6px; font-size:15px}
.proposition-ia ul{margin:6px 0; padding-left:20px}

/* Le plan écrit par l'IA, semaine par semaine (`plan_generation.html`). */
.seances-generees{list-style:none; margin:0; padding:0}
.seances-generees li{padding:6px 0; border-top:1px solid var(--line); font-size:14px}
.seances-generees li:first-child{border-top:0}
/* Les demandes de modification d'un plan prêt, et ce qui en est sorti. */
.modifications-plan{list-style:none; margin:0 0 12px; padding:0}
.modifications-plan li{padding:6px 0; border-top:1px solid var(--line); font-size:14px; line-height:1.45}
.modifications-plan li:first-child{border-top:0}
.seances-generees .meta-seance{display:inline-block; min-width:150px}

/* L'avancement d'une écriture par l'IA (`plan_generation.html`) */
.avancement-ia{margin:10px 0; padding-left:22px; color:var(--muted); font-size:14px}
.avancement-ia li{margin:4px 0}
.avancement-ia li.fait{color:var(--ink-2)}
.avancement-ia li.fait::marker{content:"✓  "; color:var(--good)}
.avancement-ia li.en-cours{color:var(--ink); font-weight:600}

/* La barre d'écriture du plan (Jo, 2026-10-09). La PISTE porte les trois
   sports dans l'ordre d'une course — nage, vélo, course ; ce qui reste à
   écrire la recouvre depuis la droite, si bien que la part écrite DÉCOUVRE
   la course au lieu de s'étirer. Un sillage défile sur la part écrite tant
   que la page se relit ; immobile pour qui réduit les animations. */
.parcours-ia{margin:20px 0 10px; max-width:640px}
.parcours-piste{position:relative; height:22px; border-radius:var(--rayon-2);
  overflow:hidden; border:1px solid var(--line);
  /* Sous la bordure, rien : sinon la couleur de la course fuyait en fins
     traits le long de ce qui reste à écrire (vu sur la capture). */
  background:linear-gradient(90deg, var(--s3) 0 16%, var(--s1) 24% 58%, var(--s2) 66% 100%)
    padding-box}
.parcours-reste{position:absolute; top:0; bottom:0; right:0; left:var(--fait);
  background:var(--surface-2)}
.parcours-sillage{position:absolute; top:0; bottom:0; left:0; width:var(--fait);
  background-image:linear-gradient(115deg, transparent 0 38%,
    color-mix(in srgb, var(--surface) 34%, transparent) 38% 62%, transparent 62%);
  background-size:18px 100%; animation:sillage 1.1s linear infinite}
/* La tête de la part écrite : là où l'IA en est. */
.parcours-sillage::after{content:""; position:absolute; right:5px; top:50%; width:10px;
  height:10px; margin-top:-5px; border-radius:50%; background:var(--surface);
  box-shadow:0 0 0 2px var(--ink); animation:tete 1.6s ease-in-out infinite}
.parcours-jonction{position:absolute; top:0; bottom:0; left:var(--jonction); width:2px;
  margin-left:-1px; background:var(--ink); opacity:.55}
@keyframes sillage{to{background-position:18px 0}}
@keyframes tete{50%{box-shadow:0 0 0 5px color-mix(in srgb, var(--ink) 25%, transparent)}}
@media (prefers-reduced-motion: reduce){
  .parcours-sillage, .parcours-sillage::after{animation:none}
}
.parcours-compte{margin:12px 0 2px; font-family:var(--chiffres); font-size:20px;
  color:var(--ink-2); font-variant-numeric:tabular-nums}
.parcours-compte b{font-size:30px; color:var(--ink); font-weight:600}
.parcours-etape{margin:0; font-size:15px; color:var(--ink-2)}

/* Les façons de se calibrer, une par sport ouverte à la fois (`accueil.html`) */
details.calib-voie{border:1px solid var(--line); border-radius:var(--rayon-2);
  margin:8px 0; background:var(--surface-2)}
details.calib-voie > summary{cursor:pointer; padding:10px 12px; display:flex;
  flex-wrap:wrap; align-items:baseline; gap:4px 10px}
details.calib-voie[open] > summary{border-bottom:1px solid var(--line)}
details.calib-voie[open] > summary b{color:var(--accent)}
details.calib-voie > :not(summary){margin-left:12px; margin-right:12px}
details.calib-voie > form{margin-bottom:12px}
fieldset.calib-seuil{border:1px solid var(--line); border-radius:var(--rayon-2);
  padding:6px 10px 10px; margin:0}
fieldset.calib-seuil legend{font-size:12.5px; color:var(--ink-2); padding:0 4px}
fieldset.calib-zones{display:grid; gap:6px; width:100%}
.calib-zone{display:grid; grid-template-columns:minmax(120px,1fr) auto auto; gap:8px;
  align-items:center; font-size:13.5px}
.form-exo .calib-zone label{flex-direction:row; align-items:center; gap:6px}
.calib-zone input{width:64px}
@media (max-width:480px){ .calib-zone{grid-template-columns:1fr 1fr} .calib-zone > span{grid-column:1/-1} }

/* ── Administration ───────────────────────────────────────────────────── */
/* La table des comptes défile seule sur téléphone : sept colonnes n'entrent
   pas dans 360 px, et la page, elle, ne doit jamais défiler de côté. */
.carte-reglage:has(> table.admin-comptes){overflow-x:auto}
dl.admin-fiche{display:grid; grid-template-columns:max-content 1fr; gap:6px 16px; margin:0}
dl.admin-fiche dt{color:var(--muted)}
dl.admin-fiche dd{margin:0}
@media (max-width:600px){dl.admin-fiche{grid-template-columns:1fr}
  dl.admin-fiche dd{margin-bottom:6px}}

/* Le descriptif d'un programme, écrit par l'IA pour la personne : ce qu'elle
   reçoit. Un paragraphe de lecture, pas une aide en petits caractères. */
.descriptif-phase{margin:0 0 16px; max-width:72ch; font-size:15px; line-height:1.6;
  color:var(--ink-2)}
/* Les chiffres du programme sous le descriptif, écrits par le CODE (tranche
   3) : des faits fermes, au trait plein — le tireté dit le provisoire. */
.faits-plan{list-style:none; margin:0 0 16px; padding:1px 0 1px 12px; max-width:72ch;
  border-left:2px solid var(--ink); display:grid; gap:4px; font-size:14px;
  line-height:1.5; color:var(--ink); font-variant-numeric:tabular-nums}

/* ── Glisser une séance (plan_semaine) ─────────────────────────────────────
   `touch-action: none` sur la poignée SEULEMENT : le doigt qui la prend
   glisse la séance, partout ailleurs il fait défiler la page. */
.sem-glisser-aide{margin:0 0 8px; font-size:12.5px; color:var(--muted)}
.sem-poignee{touch-action:none; cursor:grab; background:none; border:0;
  color:var(--muted); font-size:16px; line-height:1; padding:4px 6px; margin-right:2px}
.sem-poignee:hover,.sem-poignee:focus-visible{color:var(--ink)}
.sem-seance.en-glisser{position:relative; z-index:20; pointer-events:none;
  opacity:.85; box-shadow:0 8px 24px rgba(0,0,0,.25); cursor:grabbing}
.sem-jour.cible-glisser{outline:2px dashed var(--accent); outline-offset:-2px}

/* Le lien d'abonnement de l'agenda (`agenda_lien.html`) : sélectionnable en
   entier d'un geste, et assez large pour se lire. */
.lien-agenda{width:100%; max-width:62ch; font-family:ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size:13px; padding:8px; border:1px solid var(--muted); border-radius:6px;
  background:transparent; color:var(--ink)}

/* L'abonnement mis de l'avant (`_agenda_carte.html`) : après l'adoption d'un
   plan (vedette, cadre plein d'accent) et en haut du calendrier (discret).
   Déjà abonné : une ligne, pas une carte. */
.carte-agenda{display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:12px 20px; border:1px solid var(--line-strong); border-radius:10px;
  padding:14px 16px; margin:0 0 16px}
.carte-agenda.vedette{border:2px solid var(--accent); background:var(--accent-soft)}
.carte-agenda .etiquette{font-size:11px; color:var(--muted)}
.carte-agenda .titre{font-size:17px; font-weight:600; margin:2px 0}
.carte-agenda .note{margin:0; font-size:13px; color:var(--ink-2); max-width:56ch}
.carte-agenda form{margin:0}
.agenda-actif{margin:0 0 12px; font-size:13px; color:var(--muted)}
.etapes-agenda{margin:6px 0 8px; padding-left:20px; font-size:14px; color:var(--ink); max-width:56ch}
.etapes-agenda li{margin:2px 0}
.annonce-agenda{margin:12px 0 0; font-size:14px; color:var(--ink-2); max-width:64ch}

/* Le guide de l'agenda (`plan_agenda.html`, 2026-10-19) : deux étapes reliées
   par un tracé — le chiffre cerclé des tests de l'accueil (`.test-no`), en
   grand —, puis une carte repliable par application, une seule ouverte. */
.guide-agenda{max-width:680px}
.guide-agenda h3{font-family:var(--chiffres); font-size:18px; font-weight:600; margin:18px 0 6px}
.parcours-agenda{list-style:none; margin:16px 0 8px; padding:0; counter-reset:etape}
.parcours-agenda > li{position:relative; padding:0 0 18px 48px; counter-increment:etape}
.parcours-agenda > li::before{content:counter(etape); position:absolute; left:0; top:0;
  display:grid; place-items:center; width:32px; height:32px; border-radius:50%;
  border:2px solid var(--accent); background:var(--page); color:var(--accent);
  font-family:var(--chiffres); font-size:18px; font-weight:600}
.parcours-agenda > li:last-child{padding-bottom:0}
.parcours-agenda > li:not(:last-child)::after{content:""; position:absolute; left:15px;
  top:34px; bottom:0; width:2px; background:var(--line-strong)}
.parcours-agenda > li > h3{margin:4px 0 6px}
.parcours-agenda p{margin:0 0 10px; font-size:14px; max-width:64ch}
.agenda-apps details{border:1px solid var(--line); border-radius:var(--rayon-2);
  background:var(--surface); margin:0 0 8px}
.agenda-apps details[open]{border-color:var(--accent)}
.agenda-apps summary{padding:12px 14px; min-height:44px; box-sizing:border-box;
  cursor:pointer; font-size:15px; font-weight:500}
.agenda-apps .calib-protocole{margin:0 14px 12px}
.guide-agenda > p{margin:0 0 12px}
.guide-agenda > p:not([class]){font-size:14px; max-width:64ch}
.guide-agenda > h3:not(:first-child){margin-top:28px}
.guide-agenda > details > summary{cursor:pointer; font-size:14px; color:var(--ink-2); padding:8px 0}
/* Le second rang : télécharger le fichier, copier le lien — un contour, pas
   le plein de l'action principale. */
.bouton-contour{display:inline-flex; align-items:center; box-sizing:border-box;
  font-family:inherit; font-size:14px; font-weight:600; padding:0 20px;
  min-height:44px; border:1px solid var(--accent); border-radius:8px;
  background:var(--surface); color:var(--accent); text-decoration:none; cursor:pointer}
a.bouton-contour:visited{color:var(--accent)}
.lien-copier{display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin:12px 0}
.lien-copier .lien-agenda{flex:1 1 260px; min-height:44px; box-sizing:border-box}

/* Les journées de musculation (`journees.py`, 2026-10-06). */
ul.journees{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:10px}
.journee{display:flex; justify-content:space-between; align-items:center; gap:12px;
  flex-wrap:wrap; background:var(--surface); border:1px solid var(--line);
  border-radius:10px; padding:12px 16px}
.journee > div{display:flex; flex-direction:column; gap:3px}
.journee .muet{font-size:12px; color:var(--muted)}
.journee form button{font-family:inherit; font-size:13px; font-weight:600; padding:8px 14px;
  min-height:38px; border:1px solid var(--accent); border-radius:6px;
  background:var(--surface-2); color:var(--accent); cursor:pointer}
ol.lignes-journee{list-style:none; margin:12px 0 0; padding:0; display:flex;
  flex-direction:column; gap:10px}
.nom-ligne{flex:1; min-width:170px; align-self:center; font-size:14px}
.lignes-journee .actions-exo button{padding:9px 12px}
.confirmation-journee{margin:0 0 14px; padding:9px 14px; border-radius:8px;
  background:var(--surface-2); border:1px solid var(--line); font-size:13px}
.lien-journees{margin:14px 0 0; font-size:13px; color:var(--muted)}
.lien-journees a{color:var(--accent); font-weight:600}
.sem-ajout{display:block; margin-top:4px; font-size:11px; font-weight:600;
  color:var(--muted); text-decoration:none}
.sem-ajout:hover{color:var(--accent)}

/* ══ Finition — 2026-10-07 ═══════════════════════════════════════════════════
   Jo : « que l'app ait l'air moins vibe-codée, plus pro — des effets, des
   animations ; le serveur performant, l'app sans lag ». En FIN de fichier,
   après « Matière », pour la même raison qu'elle : à spécificité égale, le
   dernier déclaré gagne.

   Trois règles tiennent tout le bloc :
   1. **Rien ne s'anime hors de `transform` et `opacity`** — les deux seules
      propriétés que le compositeur anime sans repeindre ni remettre en page.
   2. **Les défauts passent par `:where()`** : spécificité nulle, donc chaque
      règle d'écran existante gagne encore. Ils ne rattrapent que ce que
      personne n'avait stylé — le bouton natif, le lien bleu souligné.
   3. **`prefers-reduced-motion` éteint tout**, en bas du bloc. */

/* Le dégradé sur son propre calque fixe, au lieu de `background-attachment:
   fixed` : celui-ci repeint toute la fenêtre à chaque pas de défilement.
   Mesuré sur /plan : 178 → 70 ms de travail du fil principal pour trois
   défilements complets. Le fond ne glisse toujours pas (règle 2 du haut). */
body{background:var(--page)}
body::before{
  content:""; position:fixed; inset:0; z-index:-1; pointer-events:none;
  background:var(--page); background-image:var(--fond);
}

/* ── Texte ── */
body{-webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility}
h1, h2, h3{text-wrap:balance}
p{text-wrap:pretty}
::selection{background:var(--accent-soft); color:var(--ink)}
html{scrollbar-color:var(--line-strong) transparent}

/* ── Les défauts : liens et boutons que personne n'avait stylés ── */
:where(a){color:var(--accent); text-decoration:none;
  text-underline-offset:3px; text-decoration-thickness:1px}
:where(a):visited{color:var(--accent)}
:where(a):hover{text-decoration:underline}
:where(button){
  font:inherit; font-size:13px; font-weight:600; line-height:1.2;
  padding:8px 14px; border-radius:10px; cursor:pointer;
  color:var(--ink); background:var(--surface-2);
  border:1px solid var(--line-strong);
}
:where(button):hover{border-color:var(--accent); color:var(--accent)}
:where(button):disabled{opacity:.55; cursor:not-allowed}
:where(input, select, textarea){font:inherit}

/* Le focus au clavier : un anneau net, jamais au clic de souris. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible{
  outline:2px solid var(--accent); outline-offset:2px; border-radius:6px;
}

/* ── Le mouvement des contrôles ── */
:where(a, button, summary, .pistes a, .carte-programme, header.barre nav a){
  transition:color .16s ease, background-color .16s ease, border-color .16s ease,
             box-shadow .2s ease, transform .2s cubic-bezier(.2,.7,.3,1);
}
/* L'appui : le bouton cède d'un pixel, le temps du clic. */
:where(button):active:not(:disabled){transform:translateY(1px) scale(.985)}
/* Une carte qu'on peut ouvrir se soulève au survol — et seulement celles-là. */
@media (hover:hover){
  .pistes a:hover, a.carte-programme:hover{
    transform:translateY(-2px);
    box-shadow:0 14px 30px -22px rgba(26,34,48,.55), var(--lueur);
  }
}

/* Un envoi en cours (`essor.js`) : le bouton le dit, et un second clic ne
   repart pas. */
button[aria-busy="true"]{position:relative; color:transparent !important; pointer-events:none}
button[aria-busy="true"]::after{
  content:""; position:absolute; inset:0; margin:auto; width:14px; height:14px;
  border-radius:50%; border:2px solid var(--accent); border-right-color:transparent;
  animation:essor-tourne .7s linear infinite;
}
button.principal[aria-busy="true"]::after{border-color:var(--sur-accent); border-right-color:transparent}
@keyframes essor-tourne{to{transform:rotate(360deg)}}

/* Une requête htmx qui tarde : un liseré qui avance en haut de la fenêtre.
   Le délai d'apparition le cache pour les réponses rapides — 52 à 72 ms en
   production, mesuré (§3 du CLAUDE.md) : il ne clignote pas pour rien. */
.essor-charge{
  position:fixed; inset:0 0 auto 0; height:2px; z-index:100; pointer-events:none;
  background:linear-gradient(90deg, transparent, var(--accent), transparent);
  transform:scaleX(0); transform-origin:left; opacity:0;
}
html.htmx-en-cours .essor-charge{
  opacity:1; transform:scaleX(.85);
  transition:opacity .15s ease .18s, transform 2.4s cubic-bezier(.1,.6,.2,1) .18s;
}

/* ── Les transitions de page ──
   Une page qui en remplace une autre se fond en 180 ms au lieu de clignoter,
   et la barre reste en place (`view-transition-name`). Chrome, Edge, Safari
   18.2 ; ailleurs, la navigation reste instantanée. Rien à charger. */
@view-transition{navigation:auto}
header.barre{view-transition-name:barre}
::view-transition-old(root), ::view-transition-new(root){
  animation-duration:.18s; animation-timing-function:ease;
}

/* ── Les données entrent en scène, une fois ──
   Les jauges partent de zéro, les courbes se tracent. `scaleX` et non
   `width` : la largeur est la donnée, posée par le gabarit, et l'animer
   remettrait la page en forme à chaque image. */
.jauge-piste span, .tz-piste span{
  transform-origin:left; animation:essor-pousse .7s cubic-bezier(.2,.7,.2,1) both;
}
@keyframes essor-pousse{from{transform:scaleX(0)}}
/* `pathLength="1"` sur les courbes pleines (`.trace`) : le pointillé de la
   courbe de comparaison (`.precedente`) est une donnée, on n'y touche pas. */
.ligne-g.trace{stroke-dasharray:1; stroke-dashoffset:1;
  animation:essor-trace 1s cubic-bezier(.3,.7,.2,1) .1s forwards}
@keyframes essor-trace{to{stroke-dashoffset:0}}
.pt-g{animation:essor-apparait .3s ease .9s both}
@keyframes essor-apparait{from{opacity:0}}

/* ── La barre : un sous-titre long ne fait plus passer « Se déconnecter » à
   la ligne (« Phase 2 — Build FTP (Magog 70.3) », constaté à 1 440 px). ── */
header.barre .sous{flex:0 1 auto; min-width:0; max-width:34ch;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

/* Le liseré du programme en cours (`.bloc-vedette`) était collé au texte :
   `.bloc` n'a pas de marge intérieure, et « Phase 2 — … » commençait sur le
   trait même. */
.bloc-vedette{padding-left:18px}

/* Les onglets un peu plus serrés : la barre tient sur une ligne à 1 440 px,
   la largeur la plus courante d'un portable, même avec un sous-titre de phase
   (« Phase 2 — Build FTP (Magog 70.3) » la faisait passer à deux rangées). */
@media (min-width:901px){
  header.barre nav a{padding:7px 9px}
  header.barre{gap:8px 12px}
}
/* Sous 1 440 px, la barre ne tient plus sur une ligne : au lieu de laisser
   « Se déconnecter » seul sur la seconde rangée (constaté à 1 280), la
   marque et la sortie restent en haut, les onglets prennent la rangée
   entière dessous. */
@media (max-width:1439px){
  header.barre form.deconnexion{order:1; margin-left:auto}
  header.barre nav{order:2; width:100%; margin-left:0}
}

@media (prefers-reduced-motion:reduce){
  *, *::before, *::after{animation-duration:.01ms !important;
    animation-iteration-count:1 !important; transition-duration:.01ms !important}
  @view-transition{navigation:none}
  .ligne-g.trace{stroke-dasharray:none}
  button[aria-busy="true"]::after{animation:none}
}

/* ══ Plan d'eau, route, piste — 2026-10-08 ═══════════════════════════════════
   La direction retenue par Jo (§interface). En FIN de fichier, après
   « Matière » et « Finition », pour la même raison qu'eux : à spécificité
   égale, le dernier déclaré gagne. Une couleur se change dans les jetons du
   haut, jamais ici.

   L'audace est à UN endroit : la séance du jour dessinée par son profil
   d'effort. Le reste se tait — pas de carton vitré, pas de lueur, des titres
   en Barlow et de l'espace pour séparer. */

/* ── La couleur d'un sport, une variable que chaque élément consomme ── */
.velo{--sport:var(--s1)} .course{--sport:var(--s2)}
.nage{--sport:var(--s3)} .muscu{--sport:var(--s4)}

/* ── Les titres : Barlow, en casse de phrase ── */
h1, h2, h3, .titre-j{font-family:var(--chiffres); font-weight:600;
  letter-spacing:.005em; color:var(--ink)}
h1{font-size:30px; line-height:1.15}
h2{font-size:22px; line-height:1.2}
h3{font-size:18px; line-height:1.25}
.section h2{font-size:20px; color:var(--ink); margin:0 0 12px}
.carte .etiquette, table.activites th{font-size:13px; color:var(--muted)}
.carte .valeur{font-weight:600}

/* ── La barre : posée sur le sol, plus de plaque de verre ── */
header.barre{
  background:transparent; border:none; border-bottom:1px solid var(--line);
  border-radius:0; box-shadow:none; padding:6px 0 12px; margin-bottom:26px;
}
header.barre .sous{font-size:14px}
header.barre nav a{
  font-size:15px; font-weight:500; color:var(--ink-2);
  padding:8px 12px; border-radius:0; background:none;
}
header.barre nav a:hover{color:var(--ink); background:none}
header.barre nav a.actif{
  color:var(--ink); font-weight:600; background:none;
  box-shadow:inset 0 -2px 0 var(--ink);
}
/* Le bouton de thème garde ses trois états, sans pastille de capitales. */
header.barre .theme{font-size:13px; font-weight:500; padding:5px 10px;
  border-radius:var(--rayon-2); background:none}
@media (max-width:1439px){
  /* Quatre entrées tiennent sur une ligne à toute largeur d'ordinateur :
     les onglets restent à droite, plus de seconde rangée. */
  header.barre nav{order:0; width:auto; margin-left:auto}
  header.barre form.deconnexion{order:0; margin-left:0}
}

/* ── Au téléphone : la même barre, collée en bas ──
   UNE seule navigation (`base.html`) : deux barres seraient deux encodages
   de la même chose. Le pouce l'atteint ; le contenu garde sa place au-dessus. */
@media (max-width:720px){
  .enveloppe{padding:16px 16px calc(84px + env(safe-area-inset-bottom))}
  /* La barre (54 px mesurés) cache le bas de l'écran : ce qui défile vers un
     élément — une ancre, un champ qui prend le focus, la question recentrée
     d'`essor.js` — vise la part visible, pas l'écran entier. */
  html{scroll-padding-bottom:calc(54px + env(safe-area-inset-bottom))}
  header.barre{gap:6px 10px}
  header.barre nav{
    position:fixed; left:0; right:0; bottom:0; z-index:30; margin:0; width:auto;
    display:grid; grid-template-columns:repeat(4, 1fr); gap:0;
    background:var(--surface); border-top:1px solid var(--line);
    padding:4px 6px calc(6px + env(safe-area-inset-bottom));
  }
  header.barre nav a{
    text-align:center; font-size:14px; padding:12px 4px 10px;
    box-shadow:none;
  }
  header.barre nav a.actif{box-shadow:inset 0 3px 0 var(--ink)}
  header.barre .sous{order:3; width:100%; max-width:none}
}

/* ── Aujourd'hui : la séance, son profil d'effort ── */
.jour-j{display:flex; flex-direction:column; gap:12px}
.titre-j{font-size:30px; margin:0}
.seance-j{
  --sport:var(--ink);
  display:flex; flex-direction:column; gap:14px;
  background:var(--surface); border:1px solid var(--line);
  border-left:4px solid var(--sport); border-radius:var(--rayon);
  padding:16px 18px 18px;
}
.seance-j.velo{--sport:var(--s1)} .seance-j.course{--sport:var(--s2)}
.seance-j.nage{--sport:var(--s3)} .seance-j.muscu{--sport:var(--s4)}
.seance-j-tete{display:flex; align-items:baseline; justify-content:space-between; gap:12px}
.seance-j h3{margin:0; font-size:24px}
.seance-j h3 a{color:var(--ink)}
.seance-j-duree{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:24px; font-weight:600; color:var(--sport); white-space:nowrap}
.seance-j-pied{display:flex; align-items:center; gap:12px}
.seance-j.saute{opacity:.55}
.etat-j{font-weight:600; color:var(--ink-2)}
.repos-j{font-size:17px; color:var(--ink-2); margin:0}

/* Un bouton de séance prend la couleur de son sport (jetons, « pas d'accent
   séparé »). `--sur-accent` : blanc en clair, foncé en sombre, où les
   couleurs de sport s'éclaircissent. */
.bouton-sport{
  display:inline-block; font-weight:600; font-size:15px; text-decoration:none;
  padding:11px 18px; border-radius:var(--rayon-2);
  background:var(--sport); color:var(--sur-accent);
}
.bouton-sport:hover{text-decoration:none; filter:brightness(1.08)}
.bouton-sport:visited{color:var(--sur-accent)}

.profil-effort{display:block; width:100%; height:88px; color:var(--sport); overflow:visible}
.profil-effort .sol{stroke:var(--line-strong); stroke-width:1; vector-effect:non-scaling-stroke}
.profil-effort .seg{fill:currentColor}
.profil-effort .seg.echauffement, .profil-effort .seg.retour_au_calme{opacity:.55}
.profil-effort .seg.recup{opacity:.28}
/* SUPPOSÉ : une hauteur sans cible n'est pas une mesure — contour pointillé,
   pas de plein. La forme le dit autant que la couleur (§interface). */
.profil-effort .seg.suppose{fill:none; stroke:currentColor; stroke-width:1.5;
  stroke-dasharray:4 3; vector-effect:non-scaling-stroke}
/* Le seul mouvement non provoqué de l'app : le profil monte, une fois.
   `prefers-reduced-motion` le coupe (bloc « Finition »). */
.profil-effort .seg{transform-box:fill-box; transform-origin:bottom;
  animation:essor-monte .5s cubic-bezier(.2,.7,.2,1) both;
  animation-delay:calc(var(--i, 0) * 22ms)}
@keyframes essor-monte{from{transform:scaleY(0)}}

/* ── Fait = plein, prévu = creux, sauté = estompé ──
   Une pastille devant le nom, à la couleur du sport. Plus de rature : elle se
   lisait « annulée » (2026-10-08). L'opacité du prévu tombe : le creux suffit,
   et un texte pâle se lit mal en plein soleil. */
.item-jour{border-left:none; padding:3px 0}
.item-jour .nom-item, .cal-item .cal-nom{display:inline-flex; align-items:center; gap:7px}
.item-jour .nom-item::before, .cal-item .cal-nom::before{
  content:""; flex:none; width:9px; height:9px; border-radius:50%;
  background:var(--sport, var(--muted)); border:1.5px solid var(--sport, var(--muted));
}
.item-jour.prevu .nom-item::before, .cal-item.prevu .cal-nom::before{background:transparent}
.item-jour.prevu{opacity:1}
.item-jour.fait, .item-jour.prevu.faite{font-weight:500}
.item-jour.saute, .item-jour.prevu.sautee, .cal-item.prevu.sautee{opacity:.45}
.item-jour .d{padding-left:16px}
.item-jour.fait .d, .item-jour.prevu.faite .d{color:var(--muted)}

/* Le jour courant : un trait d'encre, pas un fond teinté. */
.jour.aujourdhui, .cal-jour.aujourdhui{
  background:var(--surface); box-shadow:inset 0 0 0 2px var(--ink);
}
.jour.aujourdhui .date-jour, .jour.aujourdhui .date-jour b{color:var(--ink)}
.jour .date-jour{font-size:13px}
.jour .date-jour b{font-size:16px}
.jauge-piste span{background:var(--ink)}

/* ── La lecture du profil d'effort (survol, Jo 2026-10-08) ──
   Fixe, au-dessus du dessin : elle ne le masque jamais (§interface). Une
   hauteur réservée — sinon la carte sauterait d'une ligne à l'autre au
   survol quand une cible est absente. */
.profil-cadre{display:flex; flex-direction:column; gap:8px}
.lecture-profil{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 14px;
  margin:0; min-height:1.5em; font-size:14px; color:var(--ink-2);
}
.lecture-profil b{font-family:var(--chiffres); font-size:17px; font-weight:600; color:var(--sport)}
.lecture-profil b:empty, .lecture-profil span:empty{display:none}
.lecture-profil [data-lecture="duree"]{font-family:var(--chiffres); font-variant-numeric:tabular-nums}
.profil-effort{cursor:crosshair; touch-action:pan-y}
.profil-effort .seg{transition:opacity .12s ease}
.profil-effort:hover .seg:not(.survole){opacity:.4}
.profil-effort:hover .seg.survole{opacity:1}

/* ── Le profil en couleurs de zone (Jo, 2026-10-08) ──
   La teinte vient du RANG de la zone (`.z1`…`.z7`, rampe du temps en zone) :
   une zone a la même couleur ici que dans les répartitions. Le rôle
   (échauffement, retour au calme) ne pâlit plus une barre zonée — la couleur
   dit déjà l'intensité visée. La récupération reste estompée ; une barre
   SUPPOSÉE garde son contour pointillé. */
.profil-effort .seg.zonee{fill:var(--teinte)}
.profil-effort .seg.zonee.echauffement, .profil-effort .seg.zonee.retour_au_calme{opacity:1}
.profil-effort .seg.zonee.suppose{fill:none; stroke:var(--teinte)}
.lecture-profil b[class*="z"]{color:var(--teinte)}
.cle-zones{display:flex; flex-wrap:wrap; gap:4px 10px; margin:0; font-size:13px; color:var(--ink-2)}
.cle-zones .jeton-zone{margin-right:4px}
/* Mesuré à la capture (1 400 et 390 px, 2026-10-08) : la durée de la lecture
   sortait plus petite que ses voisines, et 88 px laissaient une endurance
   flotter sous un grand vide. */
.lecture-profil [data-lecture="duree"]{font-size:15px}
.profil-effort{height:72px}
/* Au survol, TOUTE barre non survolée s'estompe — la règle « une barre zonée
   ne pâlit pas » (plus haut) la battait à spécificité égale et l'échauffement
   restait vif (vu dans Chrome, 2026-10-08). */
.profil-effort:hover .seg.zonee:not(.survole){opacity:.4}

/* Le calendrier : la pastille dit le sport ET l'état ; le trait de gauche le
   redisait — deux encodages du même sport sur la même case (§interface).
   L'opacité du prévu tombe aussi : le creux de la pastille suffit. */
.cal-item{border-left:none; padding-left:0}
.cal-item.prevu{opacity:1}
/* Le bouton principal est à l'ENCRE (jetons : « pas d'accent séparé ») ; le
   vert reste à « fait » — une étape cochée, une série réussie. Un bouton vert
   disait « réussi » avant même d'être cliqué. */
a.bouton-lien{background:var(--accent); color:var(--sur-accent); border-radius:var(--rayon-2)}
a.bouton-lien:visited{color:var(--sur-accent)}
button.principal{background:var(--accent); color:var(--sur-accent); border-radius:var(--rayon-2)}

/* « À saisir » garde sa couleur d'attente (revue de la PR #3) : l'état
   `a_saisir` se range en « fait » — l'entraînement a eu lieu —, et la règle
   `.item-jour.prevu.a_saisir` d'avant ne le voyait plus. */
.item-jour.a_saisir .d, .etat-j.a-saisir{color:var(--warn)}

/* Ce qui a été fait aujourd'hui sans être au plan : la liste, pas « repos ». */
.faites-j{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:6px}
.faites-j li{display:flex; align-items:center; gap:10px; font-size:17px}
.faites-j li::before{content:""; width:10px; height:10px; border-radius:50%; background:var(--sport, var(--muted))}
.faites-j span{font-family:var(--chiffres); font-variant-numeric:tabular-nums; color:var(--ink-2)}

/* ── /zones, un onglet par sport (2026-10-08) ───────────────────────────────
   Les sports se lisent en Barlow, chacun avec sa pastille de couleur ; le
   trait de l'onglet actif prend la couleur du sport, pas l'encre : c'est la
   seule chose voyante de la page, le reste suit le système. Sur téléphone,
   la rangée défile dans sa propre boîte plutôt que d'élargir la page. */
nav.sports-zones{display:flex; gap:4px 24px; margin:2px 0 14px; overflow-x:auto;
  scrollbar-width:none; border-bottom:1px solid var(--line)}
nav.sports-zones a{display:inline-flex; align-items:center; gap:8px; min-height:44px;
  font-family:var(--chiffres); font-size:22px; font-weight:600; color:var(--ink-2);
  text-decoration:none; white-space:nowrap; border-bottom:3px solid transparent;
  margin-bottom:-1px}
nav.sports-zones a::before{content:""; width:9px; height:9px; border-radius:50%;
  background:var(--sport)}
nav.sports-zones a:hover{color:var(--ink)}
nav.sports-zones a.actif{color:var(--ink); border-bottom-color:var(--sport)}
nav.sports-zones a:focus-visible{outline:2px solid var(--ring); outline-offset:2px}
.fenetre-zones{display:flex; flex-wrap:wrap; align-items:center; gap:6px 12px}
.fenetre-zones nav.periodes{margin-left:0; flex-wrap:wrap}
.fenetre-zones .meta-seance{margin:0}
@media (max-width:720px){
  nav.sports-zones{gap:4px 16px}
  nav.sports-zones a{font-size:19px}
}
.vide-zones{display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px}
.vide-zones p{margin:0}

/* ── L'aperçu d'un plan (tranche 4 ; refait le 2026-10-21, Jo : « trop
   compliqué », « des trous » dans le graphique) ────────────────────────────
   La frise de la préparation : une case par semaine, sans trou. Une semaine
   détaillée est pleine ; une semaine esquissée est rayée — « pas ferme » par
   la forme autant que par la couleur (§interface) — dans la teinte de sa
   phase, de plus en plus foncée vers la course ; plus basse pour une semaine
   plus légère — fondations comprises : leur bande en pointillé (2026-10-21)
   est retirée le 2026-10-23, elle laissait croire que seules les fondations
   attendaient le plan complet. Un espace marque chaque changement de phase,
   y compris entre des cases pleines. Les heures sont
   dans la liste des phases, qui reprend la même teinte. La teinte est celle
   de la distance depuis le 2026-10-23 (`--teinte`, ci-dessous). */
.apercu-titre{margin:0; font-family:var(--chiffres); font-size:28px; font-weight:600;
  line-height:1.15; letter-spacing:-.01em; max-width:34ch; text-wrap:balance}
/* L'aperçu qui vend (Jo, 2026-10-23 : « étrangement fait », « c'est cette
   page qui va vendre »). La teinte est celle de la distance choisie — la
   frise fonce vers la course dans SA couleur ; l'encre sans distance. Au
   bureau, l'appel au plan complet reste à droite pendant la lecture ; au
   téléphone, il ferme la page, et le bouton du dossard mène aux formules. */
.apercu-pret{--teinte:var(--dist, var(--ink))}
.apercu-pret > .bloc + .bloc{margin-top:36px}
@media (min-width:1100px){
  .apercu-pret{display:grid; grid-template-columns:minmax(0, 1fr) 340px; gap:0 48px; align-items:start}
  .apercu-pret > .bloc{grid-column:1}
  .apercu-pret > .appel-complet{grid-column:2; grid-row:1 / span 4; position:sticky; top:24px;
    margin-top:0}
  .apercu-pret .appel-tete{display:none}
}
/* Le dossard : celui de la carte de distance du questionnaire — le nom en
   grand dans la couleur de la distance, quatre trous d'épingle, les épreuves
   avec leur pictogramme. */
.dossard{position:relative; display:grid; gap:14px; max-width:880px; padding:22px 22px 20px;
  border:1.5px solid color-mix(in srgb, var(--teinte) 45%, transparent); border-radius:var(--rayon);
  background:linear-gradient(155deg, color-mix(in srgb, var(--teinte) 18%, var(--surface)) 0%,
    color-mix(in srgb, var(--teinte) 4%, var(--surface)) 78%)}
.dossard::after{content:""; position:absolute; inset:9px; pointer-events:none;
  --trou:radial-gradient(circle, var(--page) 0 2.5px,
    color-mix(in srgb, var(--teinte) 50%, transparent) 2.5px 3.5px, transparent 4px);
  background:var(--trou) top left / 8px 8px no-repeat, var(--trou) top right / 8px 8px no-repeat,
    var(--trou) bottom left / 8px 8px no-repeat, var(--trou) bottom right / 8px 8px no-repeat}
.dossard h2{display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 14px; margin:0;
  font-weight:400; border:0; padding:0}
.dossard .nom-distance{font-family:var(--chiffres); font-size:clamp(48px, 14vw, 72px); font-weight:700;
  line-height:.9; color:var(--teinte)}
.dossard .date-course{font-family:var(--chiffres); font-size:clamp(19px, 5vw, 24px); font-weight:600;
  color:var(--ink)}
.dossard .epreuves{display:flex; flex-wrap:wrap; gap:8px clamp(12px, 4vw, 22px); margin:0;
  font-family:var(--chiffres); font-size:clamp(17px, 5vw, 20px); font-weight:600;
  font-variant-numeric:tabular-nums}
.dossard .epreuves > span{display:inline-flex; align-items:center; gap:6px; white-space:nowrap}
.dossard .icone-sport{width:24px; height:24px; flex:none; color:var(--sport)}
.dossard .preparation{margin:0; font-size:15px; color:var(--ink-2)}
.apercu-tete .appel-tete{margin:16px 0 0}
/* L'appel au plan complet : plein à l'encre, comme l'offre principale des
   formules (§interface, « Les cartes des formules »). */
.appel-complet{padding:22px 22px 20px; border-radius:var(--rayon); background:var(--accent);
  color:var(--sur-accent); max-width:680px; margin-top:32px}
.appel-complet h3{margin:0 0 12px; font-family:var(--chiffres); font-size:26px; font-weight:700;
  color:inherit}
.appel-complet ul{list-style:none; margin:0 0 14px; padding:0; display:grid; gap:10px}
.appel-complet li{position:relative; padding-left:22px; line-height:1.4}
.appel-complet li::before{content:""; position:absolute; left:0; top:.45em; width:10px; height:6px;
  border-left:2px solid currentColor; border-bottom:2px solid currentColor; transform:rotate(-45deg)}
.appel-complet p{margin:0 0 14px; max-width:60ch; opacity:.85}
.appel-complet p:last-child, .appel-complet form{margin-bottom:0}
.appel-complet p:has(> .bouton-lien){opacity:1}
.appel-complet a.bouton-lien, .appel-complet a.bouton-lien:visited,
.appel-complet .form-exo button.principal{background:var(--sur-accent); border-color:var(--sur-accent);
  color:var(--accent)}
.appel-complet .form-exo{background:none; border:none; padding:0; align-items:center}
.appel-complet .meta-seance{color:inherit; opacity:.8}
.frise .fondations, .phases-apercu .fondations{--ton:color-mix(in srgb, var(--teinte) 38%, transparent)}
.frise .construction, .phases-apercu .construction{--ton:color-mix(in srgb, var(--teinte) 68%, transparent)}
.frise .affutage, .phases-apercu .affutage{--ton:var(--teinte)}
.frise{margin:8px 0 0; max-width:880px}
.frise-cases{display:flex; align-items:flex-end; gap:2px; height:48px}
.frise .case{flex:1 1 0; min-width:2px; height:100%; border-radius:2px;
  background:repeating-linear-gradient(135deg, var(--ton) 0 1.5px, transparent 1.5px 5px),
             color-mix(in srgb, var(--ton) 20%, transparent)}
.frise .case.legere{height:60%}
/* Une semaine de fondations que l'IA n'a pas esquissée : « à venir », en
   contour pointillé sans rayures — rayée, elle passerait pour une esquisse
   (OBSERVÉ et SUPPOSÉ ; revue de la PR #49). */
.frise .case.a-venir, .frise-cles i.a-venir, .phases-apercu li.a-venir > i{
  border:1.5px dashed var(--ton); background:color-mix(in srgb, var(--ton) 10%, transparent)}
.frise .case.bascule{margin-left:5px}
.frise .case.ferme{background:var(--teinte)}
/* La ligne d'arrivée : un damier à la fin de la frise, la course (2026-10-23). */
.frise .arrivee{flex:none; width:10px; height:100%; margin-left:4px; border-radius:2px;
  background:conic-gradient(var(--ink) 25%, var(--page) 0 50%, var(--ink) 0 75%, var(--page) 0)
    0 0 / 5px 5px; box-shadow:0 0 0 1px var(--ink)}
.frise figcaption{display:flex; justify-content:space-between; gap:8px; margin-top:6px;
  font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:14px; color:var(--muted)}
.frise-cles{display:flex; flex-wrap:wrap; gap:4px 16px; margin:0; font-size:13px; color:var(--ink-2)}
.frise-cles i{display:inline-block; width:10px; height:12px; margin-right:6px; vertical-align:-1px;
  border-radius:2px; --ton:color-mix(in srgb, var(--teinte) 68%, transparent);
  background:repeating-linear-gradient(135deg, var(--ton) 0 1.5px, transparent 1.5px 4px)}
.frise-cles i.ferme{background:var(--teinte)}
.frise-cles i.legere{height:7px; vertical-align:0}
/* Ce que « esquissée » veut dire, pour les deux phases (2026-10-23). */
.frise-note{margin:8px 0 0; font-size:14px; color:var(--ink-2); max-width:62ch}
.phases-apercu{list-style:none; margin:4px 0 0; padding:0; max-width:880px}
.phases-apercu li{display:grid; grid-template-columns:14px 1fr; gap:2px 12px; padding:10px 0;
  border-top:1px solid var(--line)}
.phases-apercu li:first-child{border-top:0}
.phases-apercu li > i{grid-row:span 2; width:14px; height:14px; margin-top:5px; border-radius:3px;
  background:repeating-linear-gradient(135deg, var(--ton) 0 1.5px, transparent 1.5px 4px),
             color-mix(in srgb, var(--ton) 20%, transparent)}
.phases-apercu b{font-family:var(--chiffres); font-size:19px; font-weight:600; margin-right:8px}
.phases-apercu .duree-phase{font-size:13.5px; color:var(--muted); white-space:nowrap}
.phases-apercu p{margin:0; font-size:14px; color:var(--ink-2); max-width:62ch}

/* Les deux semaines : le résumé par sport, puis un jour par rangée. Chaque
   séance sur un fond à peine teinté de son sport, son trait à gauche et son
   pictogramme — le nom et la durée d'abord, la consigne en dessous (Jo,
   2026-10-23, d'après Stamina : « plus attirant », des couleurs). */
.semaine-apercu + .semaine-apercu{margin-top:28px}
.semaine-apercu h3{display:flex; align-items:baseline; gap:10px; margin:8px 0 10px; font-size:22px}
.semaine-apercu h3 span{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:18px; font-weight:500; color:var(--muted)}
.sports-semaine{list-style:none; margin:0 0 6px; padding:0; max-width:880px;
  display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:8px}
.sports-semaine li{--sport:var(--ink); display:grid; grid-template-columns:auto 1fr; gap:0 8px;
  align-items:center; padding:10px 12px; border-radius:var(--rayon-2);
  background:color-mix(in srgb, var(--sport) 10%, var(--surface))}
.sports-semaine .velo{--sport:var(--s1)} .sports-semaine .course{--sport:var(--s2)}
.sports-semaine .nage{--sport:var(--s3)} .sports-semaine .muscu{--sport:var(--s4)}
.sports-semaine .icone-sport{grid-row:span 3; width:26px; height:26px; color:var(--sport)}
.sports-semaine b{font-size:14px; font-weight:600; color:var(--sport); overflow:hidden; text-overflow:ellipsis}
.sports-semaine span{font-size:13px; color:var(--ink-2)}
.sports-semaine .temps{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:17px; font-weight:600; color:var(--ink)}
.jours-apercu{list-style:none; margin:0; padding:0; max-width:880px}
.jours-apercu > li{display:grid; grid-template-columns:10.5em 1fr; gap:8px 18px; padding:14px 0;
  border-top:1px solid var(--line)}
.jours-apercu > li:first-child{border-top:0}
.jours-apercu .jour-nom{display:block; font-size:14px; font-weight:600; color:var(--ink-2); padding-top:10px}
.jours-apercu .jour-nom::first-letter{text-transform:uppercase}
.jours-apercu ul{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:8px}
.seance-apercu{--sport:var(--ink); display:grid; grid-template-columns:22px 1fr auto; gap:3px 10px;
  align-items:center; padding:10px 14px 11px 12px; border-left:3px solid var(--sport);
  border-radius:0 var(--rayon-2) var(--rayon-2) 0;
  background:color-mix(in srgb, var(--sport) 7%, var(--surface))}
.seance-apercu.velo{--sport:var(--s1)} .seance-apercu.course{--sport:var(--s2)}
.seance-apercu.nage{--sport:var(--s3)} .seance-apercu.muscu{--sport:var(--s4)}
.seance-apercu .icone-sport{width:22px; height:22px; color:var(--sport)}
.seance-apercu .titre{font-weight:600}
.seance-apercu .duree{font-family:var(--chiffres); font-variant-numeric:tabular-nums;
  font-size:17px; font-weight:600; color:var(--sport); white-space:nowrap}
.seance-apercu p{grid-column:2 / -1; margin:0; font-size:13.5px; color:var(--ink-2); max-width:62ch}
@media (max-width:720px){
  .jours-apercu > li{grid-template-columns:1fr; gap:6px}
  .jours-apercu .jour-nom{padding-top:0}
  .sports-semaine{grid-template-columns:repeat(2, minmax(0, 1fr))}
}

/* La suite semaine par semaine, repliée : la frise la résume déjà. */
.suite-apercu{max-width:880px; border-top:1px solid var(--line); border-bottom:1px solid var(--line)}
.suite-apercu summary{cursor:pointer; padding:12px 0; font-weight:600}
.suite-apercu summary:focus-visible{outline:2px solid var(--accent); outline-offset:2px}
.suite-apercu .aide-param{margin-bottom:6px}
.appel-plan{max-width:64ch}
.esquisse-apercu{list-style:none; margin:0 0 12px; padding:0}
.esquisse-apercu li{display:grid; grid-template-columns:4.5em 7em 1fr; gap:2px 12px; align-items:baseline;
  padding:7px 0; border-top:1px solid var(--line); font-size:14px}
.esquisse-apercu li:first-child{border-top:0}
.esquisse-apercu .no, .esquisse-apercu .lundi{font-family:var(--chiffres);
  font-variant-numeric:tabular-nums; color:var(--muted)}
.esquisse-apercu b{font-weight:600}
.esquisse-apercu .cles{grid-column:3; color:var(--ink-2)}
@media (max-width:720px){
  .esquisse-apercu li{grid-template-columns:5.5em 1fr}
  .esquisse-apercu .lundi{display:none}
  .esquisse-apercu .cles{grid-column:2}
}

/* L'appel au tableau de bord : un trait d'encre à gauche, comme une séance
   porte celui de son sport. */
.appel-tdb{display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:12px 20px; max-width:none; margin-bottom:-12px; padding:18px 20px; border-radius:var(--rayon);
  background:var(--surface); border:1px solid var(--line); border-left:4px solid var(--ink)}
.appel-tdb p{margin:0; max-width:60ch}

/* ── Les formules (`_formules.html` : `/prix` et la fenêtre ; refaites le
   2026-10-21, Jo : « mieux présentées et plus belles ») ─────────────────────
   Deux cartes : l'annuel, l'offre principale, à l'encre pleine ; le mensuel
   en contour. Le prix en grand, en chiffres de dossard ; ce qui distingue les
   deux formules sur leur carte, ce qu'elles partagent une fois, dessous.
   Aucune couleur de sport. */
.offres{display:grid; grid-template-columns:repeat(auto-fit, minmax(15rem, 1fr)); gap:14px; max-width:680px}
.offre{display:flex; flex-direction:column; padding:20px 20px 18px; border-radius:var(--rayon);
  border:1.5px solid var(--line-strong); background:var(--surface); color:var(--ink)}
.offre.principale{background:var(--accent); border-color:var(--accent); color:var(--sur-accent)}
.offre-tete{display:flex; align-items:center; justify-content:space-between; gap:8px; min-height:26px}
.offre h3{margin:0; font-family:var(--chiffres); font-size:21px; font-weight:600; color:inherit}
.offre-gain{font-size:12.5px; font-weight:600; padding:2px 10px; border-radius:999px;
  border:1px solid currentColor; white-space:nowrap}
.offre-prix{margin:14px 0 0; font-size:15px}
.offre-prix .montant{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:48px;
  font-weight:700; line-height:1; letter-spacing:-.02em; margin-right:4px}
.offre-equiv{margin:4px 0 0; font-size:14px; color:var(--ink-2)}
.offre.principale .offre-equiv{color:color-mix(in srgb, var(--sur-accent) 78%, transparent)}
.offre-plan{margin:14px 0 0; padding-top:12px; border-top:1px solid var(--line); font-weight:600}
.offre.principale .offre-plan{border-top-color:color-mix(in srgb, var(--sur-accent) 28%, transparent)}
.offre form{margin:auto 0 0; padding-top:18px}
.offre button{width:100%; min-height:46px; font-family:inherit; font-size:15px; font-weight:600;
  border-radius:var(--rayon-2); border:1.5px solid var(--accent); background:transparent;
  color:var(--ink); cursor:pointer}
.offre button:hover{background:var(--accent-soft)}
.offre.principale button{background:var(--sur-accent); border-color:var(--sur-accent); color:var(--accent)}
.offre.principale button:hover{filter:brightness(.94)}
.offre button:focus-visible{outline:2px solid var(--ring); outline-offset:3px}
/* Comment ton plan est écrit : quatre raisons, deux par rangée au bureau.
   Un trait d'encre au-dessus de chacune, comme la tête d'une colonne. */
.offres-raisons{max-width:680px; margin-bottom:10px}
.offres-raisons h3{margin:4px 0 10px; font-size:19px}
.offres-raisons ul{list-style:none; margin:0; padding:0; display:grid;
  grid-template-columns:repeat(auto-fit, minmax(15rem, 1fr)); gap:16px 20px}
.offres-raisons li{display:flex; flex-direction:column; gap:4px; padding-top:10px;
  border-top:2px solid var(--ink)}
.offres-raisons b{font-family:var(--chiffres); font-size:17px; font-weight:600}
.offres-raisons span{font-size:14px; color:var(--ink-2); line-height:1.5}
.offres-communes{max-width:680px}
.offres-communes h3{margin:0 0 4px; font-size:15px; color:var(--ink-2)}
.offres-communes ul{list-style:none; margin:0; padding:0}
.offres-communes li{position:relative; padding:5px 0 5px 24px; font-size:14.5px}
.offres-communes li::before{content:""; position:absolute; left:3px; top:.68em; width:10px; height:5px;
  border-left:2px solid var(--ink); border-bottom:2px solid var(--ink); transform:rotate(-45deg)}
.bloc > p.note, #formules-contenu > p.note{margin:0; font-size:13px; color:var(--muted)}
.prix-fondateur{margin:0; padding:10px 14px; border-left:3px solid var(--good);
  background:var(--surface); max-width:64ch}
.visuellement-cache{position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap}
/* Le champ piège de l'inscription : hors de la vue ET des lecteurs d'écran
   (`aria-hidden`), mais présent pour qui remplit tout sans regarder. */
.piege{position:absolute; left:-10000px; width:1px; height:1px; overflow:hidden}

/* La fenêtre des formules (`base.html`, 2026-10-21) : elle couvre l'écran —
   plein écran au téléphone, une feuille centrée au bureau, sur un fond
   assombri. Son en-tête reste en haut quand on fait défiler les offres. */
.fenetre-formules{width:min(720px, calc(100vw - 24px)); max-width:none; max-height:calc(100dvh - 24px);
  padding:0; border:0; border-radius:14px; background:var(--page); color:var(--ink);
  box-shadow:0 24px 60px rgba(0,0,0,.35); overflow:auto}
.fenetre-formules[open]{display:flex; flex-direction:column; animation:fenetre-ouvre .18s ease-out}
.fenetre-formules::backdrop{background:rgba(8,14,18,.62)}
.fenetre-tete{position:sticky; top:0; z-index:1; display:flex; align-items:center;
  justify-content:space-between; gap:12px; padding:16px 20px 12px; background:var(--page);
  border-bottom:1px solid var(--line)}
.fenetre-tete h2{margin:0; font-size:22px}
.fenetre-tete form{margin:0}
.fenetre-tete .fermer{font-family:inherit; font-size:14px; font-weight:600; min-height:44px;
  padding:0 16px; border:1px solid var(--line-strong); border-radius:var(--rayon-2);
  background:var(--surface); color:var(--ink); cursor:pointer}
.fenetre-tete .fermer:focus-visible{outline:2px solid var(--ring); outline-offset:2px}
#formules-contenu{flex:1; display:flex; flex-direction:column; gap:14px; padding:18px 20px 24px}
body:has(.fenetre-formules[open]){overflow:hidden}
@keyframes fenetre-ouvre{from{opacity:0; transform:translateY(10px)}}
@media (prefers-reduced-motion:reduce){.fenetre-formules[open]{animation:none}}
@media (max-width:560px){
  .fenetre-formules{width:100vw; height:100dvh; max-height:100dvh; margin:0; border-radius:0}
}

/* Le portail (phase 3) : « Gérer mon abonnement », sous les cartes. Les deux
   boutons d'achat vivent dans les cartes (`.offre`) depuis le 2026-10-21. */
.payer{display:flex; flex-wrap:wrap; gap:10px; margin:18px 0 8px; max-width:640px}
.payer form{flex:1 1 15em; margin:0}
.payer button{width:100%; font-family:inherit; font-size:15px; font-weight:600;
  padding:11px 18px; min-height:44px; border:1px solid var(--accent); border-radius:6px;
  background:var(--surface-2); color:var(--ink); cursor:pointer}
.payer button:focus-visible{outline:2px solid var(--accent); outline-offset:2px}

/* Le code d'accès bêta (phase 2, `_code_acces.html`) : une question, un champ,
   un bouton — sous les formules, sans les concurrencer. La saisie est celle
   de `.form-exo` ; le libellé, une phrase lisible, pas une petite étiquette. */
.code-acces{margin-top:22px; padding-top:16px; border-top:1px solid var(--line); max-width:480px}
.code-acces label{display:block; font-size:14px; color:var(--ink-2); margin-bottom:6px}
.code-acces-ligne{display:flex; gap:8px; flex-wrap:wrap}
.code-acces input{flex:1 1 12em; min-width:0; font-family:inherit; font-size:15px;
  letter-spacing:.04em; padding:8px 10px; min-height:40px; border:1px solid var(--line-strong);
  border-radius:6px; background:var(--surface-2); color:var(--ink);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.10)}
.code-acces input:focus{outline:2px solid var(--accent); outline-offset:1px; border-color:var(--accent)}
.code-acces button{font-family:inherit; font-size:14px; font-weight:600; padding:9px 18px;
  min-height:40px; border:1px solid var(--accent); border-radius:6px;
  background:var(--accent); color:var(--sur-accent); cursor:pointer}
.code-acces .issue{margin:10px 0 0}
/* Un bouton dans une ligne de tableau (« Retirer l'accès ») : le style de
   `.form-exo`, sans l'écart qu'il prend sous une carte de réglage. */
.carte-reglage td .form-exo{margin:0}
/* La page admin du code : le seul chiffre en avant, les places prises. */
.code-places{margin:12px 0 4px; font-size:14px; color:var(--ink-2)}
.code-places-n{font-family:var(--chiffres); font-variant-numeric:tabular-nums; font-size:28px;
  font-weight:600; color:var(--ink); margin-right:4px}
