/* ==========================================================================
   FITNESS — ce qui n'appartient qu'à ce portail.

   À charger APRÈS workbench.css, qui porte le reste : reset, typographie,
   variables, champs de saisie, boutons, cartes, bandeau, menu de compte.
   Ne rien redéfinir ici de ce que le socle tient déjà — c'est comme ça qu'on
   se retrouve avec un champ collé à son bouton parce que la règle du socle
   n'a jamais été chargée.

   Prendre une nouvelle version du socle :
       python3 lib/workbench/scripts/install_css.py app/static
       python3 lib/workbench/scripts/install_css.py --check app/static
   ========================================================================== */

/* --- Accent : vert callisthenics ----------------------------------------- */
/* Workbench ne porte aucune teinte. Celle-ci est une PAIRE, vérifiée dans les
   deux thèmes — ne pas la retoucher « juste un peu » sans relancer :

     python3 lib/workbench/css/check_contrasts.py "#2f6f4f" "#ffffff" "#6fbf95" "#0d0d0e"

     clair   blanc sur vert  5,99:1   vert sur fond  5,59:1
     sombre  encre sur vert  8,85:1   vert sur fond  8,85:1
*/
:root {
  --accent: #2f6f4f;
  --accent-tx: #ffffff;
  --accent-soft: #e6f0ea;
  --accent-line: #c3ddcd;
  --ring: rgba(47, 111, 79, .30);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --accent: #6fbf95;
    --accent-tx: #0d0d0e;
    --accent-soft: #23302a;
    --accent-line: #2f4a3b;
    --ring: rgba(111, 191, 149, .32);
  }
}
:root[data-theme="dark"] {
  --accent: #6fbf95;
  --accent-tx: #0d0d0e;
  --accent-soft: #23302a;
  --accent-line: #2f4a3b;
  --ring: rgba(111, 191, 149, .32);
}

/* --- Couleurs propres au métier ------------------------------------------ */
/* Un type de séance, un état de calendrier. Rien de tout ça n'a sa place au
   socle : c'est du vocabulaire de programme d'entraînement. */
:root {
  --a: #2f6f4f;        /* séance A */
  --b: #7a4fa0;        /* séance B */
  --c: #b0642f;        /* circuit  */
  --done: #2f6f4f;
  --missed: #c0392b;
  --up: #b07d2f;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --a: #6fbf95; --b: #b492d8; --c: #e0965c;
    --done: #6fbf95; --missed: #e57368; --up: #d7a44e;
  }
}
:root[data-theme="dark"] {
  --a: #6fbf95; --b: #b492d8; --c: #e0965c;
  --done: #6fbf95; --missed: #e57368; --up: #d7a44e;
}

/* --- Ponts vers le vocabulaire du socle ---------------------------------- */
/* Les gabarits de fitness parlent en --card / --ink / --muted, et en .card.
   Plutôt que de réécrire cent trente sélecteurs d'un coup — un renommage muet
   qu'aucun test ne rattrape — on fait pointer les anciens noms sur ceux du
   socle. La conversion se fera écran par écran. */
:root {
  --card: var(--surface);
  --ink: var(--tx);
  --muted: var(--tx-2);
  --radius: var(--r-l);
  --maxw: 720px;
  --nav-h: 58px;
  --safe-b: env(safe-area-inset-bottom, 0px);
}

.wrap { max-width: var(--maxw); margin: 0 auto; padding: var(--e-4); }

/* `.card` est LE conteneur de zone : une seule définition, ici, et pas une
   seconde deux cents lignes plus bas. Il y en avait deux — la première posait
   `padding: var(--e-4)`, la seconde l'écrasait par `14px 16px`. Un padding
   hors échelle qu'aucune règle ne réclamait, et deux blocs voisins qui ne
   respiraient pas pareil selon celui qui gagnait. */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-l); box-shadow: var(--shadow-1);
  padding: var(--e-4); margin-bottom: var(--e-3);
}

/* ---------- Barre de nav basse (mobile) ---------- */
nav.bottom-nav { display: none; }
@media (max-width: 599px) {
  nav.bottom-nav {
    display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
    background: var(--card); border-top: 1px solid var(--line);
    padding-bottom: var(--safe-b);
  }
  nav.bottom-nav a {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; height: var(--nav-h); color: var(--muted);
    font-size: .68rem; font-weight: 600; letter-spacing: .01em;
  }
  nav.bottom-nav a .ico { font-size: 1.15rem; line-height: 1; }
  nav.bottom-nav a.active { color: var(--accent); }
  main.wrap { padding-bottom: calc(var(--nav-h) + var(--safe-b) + 20px); }
}

/* ---------- Typo ---------- */
h1 { font-size: clamp(1.25rem, 4.5vw, 1.5rem); margin: .2rem 0 .1rem; line-height: 1.25; }
.sub { color: var(--muted); margin: 0 0 var(--e-4); font-size: .92rem; }
/* Le rythme vertical d'une page : un titre de section respire d'un cran de
   plus au-dessus qu'en dessous, pour appartenir visiblement à ce qui le suit.
   Sur l'échelle du socle plutôt qu'en rem — `1.4rem 0 .6rem` donnait 22,4 et
   9,6 px, deux valeurs qui ne tombaient sur rien et qu'aucun autre bloc ne
   pouvait reprendre. */
.section-title {
  font-size: .78rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin: var(--e-5) 0 var(--e-2);
}
.muted { color: var(--muted); }
.empty { color: var(--muted); text-align: center; padding: var(--e-6) 0; }
.badge {
  display: inline-block; font-size: .72rem; font-weight: 700; padding: 2px 8px;
  border-radius: 999px; vertical-align: middle; white-space: nowrap;
}
.badge.A { background: var(--accent-soft); color: var(--a); }
.badge.B { background: rgba(122,79,160,.14); color: var(--b); }
.badge.C { background: rgba(176,125,47,.16); color: var(--c); }

/* ---------- Jours du calendrier ---------- */
/* Une ligne de jour est une carte plus dense : même horizontale que `.card`
   pour que les bords s'alignent d'une section à l'autre, mais moins d'air en
   vertical — il y en a sept d'affilée. */
.day {
  display: flex; align-items: center; gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
  margin-bottom: var(--e-2); min-height: 62px;
}
.day .date {
  font-variant-numeric: tabular-nums; min-width: 46px; text-align: center; flex-shrink: 0;
}
.day .date .d { font-size: 1.25rem; font-weight: 700; display: block; line-height: 1.1; }
.day .date .m { font-size: .68rem; color: var(--muted); text-transform: uppercase; }
.day .meta { flex: 1; min-width: 0; }
.day .meta .wd { text-transform: capitalize; font-size: .95rem; }
.day .meta .st { font-size: .8rem; color: var(--muted); }
.dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; margin-right: 5px; }
.dot.done { background: var(--done); }
.dot.missed { background: var(--missed); }
.dot.upcoming { background: var(--up); }
.day.today { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* ---------- Schéma + fiche technique ---------- */
.howto { display: flex; gap: 14px; align-items: flex-start; margin-top: 8px; }
.schema {
  /* 132px réduisaient le schéma d'un tiers : le repère de pouce y devenait un
     point de 1,5px, juste et invisible. 176px le rendent lisible sans écraser
     la fiche technique à côté. */
  margin: 0; flex: 0 0 176px; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line); border-radius: 10px; padding: 6px;
}
.schema svg { display: block; width: 100%; height: auto; }
.fiche { margin: 0; flex: 1; min-width: 0; font-size: .85rem; }
.fiche dt {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); font-weight: 700; margin-top: 6px;
}
.fiche dt:first-child { margin-top: 0; }
.fiche dd { margin: 1px 0 0; color: var(--ink); }
.fiche .warn { color: var(--missed); }
@media (max-width: 599px) {
  .howto { flex-direction: column; gap: 10px; }
  .schema { flex: none; width: 100%; max-width: 260px; align-self: center; }
}

/* ---------- Coach (chat) ---------- */
/* `.chatfab` a laissé la place à `.pastille-assistant` du socle : même rond de
   52px au même endroit, mais l'ouverture, la fermeture et l'effacement de la
   pastille quand le panneau est là viennent avec — c'était du JavaScript
   maison. Ne reste ici que le décalage au-dessus de la navigation basse, que
   le socle ne peut pas connaître : elle appartient à cette app. */
.chatpanel {
  position: fixed; z-index: 41; right: 16px; bottom: calc(78px + var(--safe-b));
  width: min(420px, calc(100vw - 32px)); max-height: min(70vh, 560px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0,0,0,.25);
}
.chatpanel[hidden] { display: none; }
/* L'entretien d'accueil prend tout l'écran : à ce moment-là la conversation
   n'est pas un à-côté de la page, elle EST la page. Dix questions dans une
   vignette de 420 px, sur un téléphone où le clavier mange déjà la moitié de
   la hauteur, se lisent trois lignes à la fois. */
.chatpanel.plein-ecran {
  inset: 0; right: 0; bottom: 0;
  width: 100%; max-height: none; height: 100%;
  border: none; border-radius: 0; box-shadow: none;
  padding-bottom: var(--safe-b);
}
/* Les messages peuvent s'élargir : la contrainte des 88 % existe pour qu'une
   bulle ne touche pas le bord opposé dans une vignette étroite, ce qui ne se
   pose plus ici. */
.chatpanel.plein-ecran .chatlog { padding: 16px; }
.chatpanel.plein-ecran .chatlog .msg { max-width: min(92%, 46rem); }
.chatpanel.plein-ecran .chatlog,
.chatpanel.plein-ecran form { margin: 0 auto; width: 100%; max-width: 46rem; }
.chatpanel header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
}
.chatpanel header button {
  background: none; border: none; color: var(--muted); font-size: 1rem; cursor: pointer;
  padding: 4px 6px;
}
.chatlog { flex: 1; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
.chatlog .msg {
  max-width: 88%; padding: 8px 11px; border-radius: 12px; font-size: .92rem;
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.chatlog .msg.me { align-self: flex-end; background: var(--accent-soft); color: var(--ink); }
.chatlog .msg.bot { align-self: flex-start; background: var(--bg); border: 1px solid var(--line); }
.chatpanel form {
  display: flex; align-items: center; gap: 8px; padding: 10px 12px;
  border-top: 1px solid var(--line);
}
.chatpanel form input[type="text"] { flex: 1; min-width: 0; }
.chatpanel form button[type="submit"] {
  width: 38px; height: 38px; flex-shrink: 0; padding: 0; border-radius: 50%;
  font-size: 1.05rem; line-height: 1;
}
.chatclip { cursor: pointer; font-size: 1.15rem; opacity: .55; flex-shrink: 0; }
.chatclip.on { opacity: 1; }
@media (max-width: 599px) {
  /* Sans ça, la pastille se pose SUR la barre de navigation basse : le socle
     l'ancre au bas de l'écran, il ignore que cette app y met déjà une barre. */
  .pastille-assistant { bottom: calc(var(--nav-h) + var(--safe-b) + 12px); }
  .chatpanel {
    right: 8px; left: 8px; width: auto;
    bottom: calc(var(--nav-h) + var(--safe-b) + 12px); max-height: 72vh;
  }
}

/* ---------- Circuit (séance hors programme) ---------- */
.circuit .rounds { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.circuit .round {
  font-size: .72rem; font-weight: 700; padding: 3px 9px; border-radius: 999px;
  background: rgba(176,100,47,.14); color: var(--c);
}
.circuit .note { font-size: .88rem; color: var(--muted); }
.circuit .rest { font-size: .88rem; margin-top: 6px; font-weight: 600; }
.circuit .prog { font-size: .8rem; color: var(--accent); margin-top: 6px; }
.free-cta { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--ink); }
.switch { display: flex; align-items: center; gap: 6px; margin-top: -.6rem; }

/* ---------- Stats ---------- */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.stat {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; min-width: 0;
}
.stat .n { font-size: clamp(1.35rem, 6vw, 1.6rem); font-weight: 700; font-variant-numeric: tabular-nums; }
.stat .l { font-size: .76rem; color: var(--muted); font-weight: 400; }
.progress { height: 8px; background: var(--line); border-radius: 999px; overflow: hidden; margin-top: 8px; }
.progress > span { display: block; height: 100%; background: var(--accent); }

/* ---------- Tableaux : scroll horizontal, jamais la page ---------- */
/* Le débord annule le padding de la carte pour que le tableau touche ses
   bords, puis le remet en padding interne. Les deux valeurs doivent donc être
   celle de `.card` — en dur, elles divergeaient à la première retouche. */
.table-scroll {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  margin: 0 calc(-1 * var(--e-4)); padding: 0 var(--e-4);
}
table { width: 100%; border-collapse: collapse; font-size: .9rem; min-width: 420px; }
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--line); white-space: nowrap; }
th { font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Exercices ---------- */
/* Un exercice est une carte : c'est l'unité qu'on lit, qu'on note et qu'on
   coche. En filets à l'intérieur d'une carte unique, la fiche technique de
   l'un touchait le titre du suivant, et un exercice sans schéma cassait
   l'alignement de toute la liste. `.card` porte le fond, la bordure et le
   padding ; il ne reste ici que ce qui est propre à l'exercice. */
.exos { margin-bottom: var(--e-3); }
.exo.card { margin-bottom: var(--e-3); }
.exo.card:last-child { margin-bottom: 0; }

/* UN EXO DANS UNE CARTE se sépare du suivant. Sur `/log` et le bilan, les six
   blocs sont rangés dans une seule carte et ne portent donc PAS `.card` : ils
   n'avaient ni marge ni filet, et « Pompes inclinées · cible 4×9 · [S1][S2] »
   enchaînait directement sur « Rowing élastique ». Six blocs collés se lisent
   comme un seul pavé, et sur un écran de saisie on ne sait plus quelle case
   appartient à quel exercice. */
.card > .exo + .exo {
  margin-top: var(--e-4); padding-top: var(--e-4);
  border-top: 1px solid var(--line);
}
.exo .h { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.exo .name { font-weight: 600; }
.exo .cat { font-size: .68rem; color: var(--muted); text-transform: uppercase; }
.exo .target { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; margin-left: auto; }
.exo .note { font-size: .85rem; color: var(--muted); margin-top: 3px; }
.exo .prog { font-size: .8rem; color: var(--accent); margin-top: 3px; }
.exo .prog.done-range { font-weight: 600; }
.exo .level {
  font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 1px 6px; border-radius: 999px; vertical-align: middle;
  background: var(--accent-soft); color: var(--accent);
}
.exo .logged { font-size: .85rem; margin-top: 6px; }
/* Sans schéma, la fiche occupe toute la largeur de la carte et ses lignes
   deviennent deux fois plus longues que celles des exercices qui en ont un.
   Le même œil parcourt les deux : la mesure doit rester la même. */
.exo .howto > .fiche:only-child { max-width: 62ch; }
.chip {
  display: inline-block; background: var(--accent-soft); color: var(--a);
  border-radius: 7px; padding: 3px 9px; margin: 2px 4px 2px 0;
  font-variant-numeric: tabular-nums; font-weight: 600;
}

.form-row { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.form-row > div { flex: 1 1 130px; min-width: 0; }
/* ---------- Courbe ---------- */
svg .grid { stroke: var(--line); }
svg .goal { stroke: var(--accent); stroke-dasharray: 4 4; }
svg .line { stroke: var(--accent); fill: none; stroke-width: 2.5; }
svg .dot-pt { fill: var(--card); stroke: var(--accent); stroke-width: 2; }
svg text { fill: var(--muted); font-size: 12px; }
/* Graduations : très pâles, elles situent sans rien montrer. Ce sont elles qui
   disent que l'échelle continue sous la ligne d'objectif — sans quoi cette
   dernière, seule horizontale marquée, se lisait comme l'axe des abscisses et
   la masse maigre paraissait sortir du graphique. */
svg .grad { stroke: var(--line); opacity: .45; }
svg text.grad-tx { fill: var(--muted); font-size: 10px; opacity: .8; }
svg .base { stroke: var(--line); }
/* Séparateurs de mois : plus discrets que les graduations de poids, qui sont
   déjà pâles. Ils bornent le temps, ils ne se lisent pas. */
svg .grad-mois { stroke: var(--line); opacity: .3; }
svg text.mois-tx { fill: var(--muted); font-size: 10px; opacity: .85; }
/* L'écart entre poids et masse maigre EST la masse grasse : le remplir la rend
   lisible sans rien calculer. Très transparent — il passe sous les deux
   courbes, qui restent l'information principale. */
svg .aire-gras { fill: var(--accent); opacity: .1; stroke: none; }
svg .line-maigre { stroke: var(--muted); fill: none; stroke-width: 2;
                   stroke-dasharray: 5 3; }
.legende-poids { font-size: .8rem; display: flex; gap: 6px; align-items: center;
                 flex-wrap: wrap; margin-top: 6px; }
.legende-poids .cle { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }
.legende-poids .cle.poids { background: var(--accent); }
.legende-poids .cle.maigre { background: var(--muted); }
.legende-poids .cle.gras { background: var(--accent); opacity: .25; height: 10px; }
.legende-poids .cle:not(:first-child) { margin-left: 8px; }

/* ---------- Retouches mobile ---------- */
@media (max-width: 599px) {
  .exo .h { flex-direction: column; align-items: flex-start; gap: 2px; }
  .exo .target { margin-left: 0; }
  .stat .l { line-height: 1.35; }
}

/* ---------- Calendrier en grille ---------- */
.calhead {
  display: flex; align-items: center; gap: 10px; margin: 1.4rem 0 .6rem;
}
.calhead strong { font-size: 1rem; }
.calnav {
  width: 30px; height: 30px; display: inline-flex; align-items: center;
  justify-content: center; border: 1px solid var(--line); border-radius: 8px;
  color: var(--ink); font-size: 1.1rem; line-height: 1;
}
.today-link { margin-left: auto; font-size: .82rem; }
.calgrid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px;
}
.caldow {
  text-align: center; font-size: .68rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; padding-bottom: 2px;
}
.calday {
  background: var(--card); border: 1px solid var(--line); border-radius: 9px;
  min-height: 58px; padding: 3px 4px 4px; display: flex; flex-direction: column;
}
.calday.out { opacity: .35; }
.calday.today { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.calday .n { font-size: .7rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.calday .marks {
  display: flex; flex-wrap: wrap; gap: 2px; align-items: center;
  justify-content: center; flex: 1;
}
.mark {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; border-radius: 6px; font-size: .72rem;
  font-weight: 700; line-height: 1; padding: 0 3px;
}
/* Le statut porte la couleur, la lettre dit quelle séance : les deux
   informations sont lisibles d'un coup d'œil sans passer par la légende. */
.mark.seance.done { background: var(--done); color: #fff; }
.mark.seance.upcoming { background: transparent; color: var(--up); border: 1.5px dashed var(--up); }
.mark.seance.missed { background: var(--missed); color: #fff; opacity: .85; }
.mark.act { font-size: .95rem; background: var(--bg); border: 1px solid var(--line); }
.callegend {
  display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 8px;
  font-size: .74rem; color: var(--muted);
}
.callegend span { display: inline-flex; align-items: center; gap: 4px; }
.callegend .dot { margin: 0; }
@media (max-width: 599px) {
  .calday { min-height: 50px; }
  .mark { min-width: 18px; height: 18px; font-size: .68rem; }
}
h1.goal { font-size: 1.05rem; font-weight: 600; color: var(--muted); margin-bottom: .8rem; }
.ok-msg { color: var(--done); font-size: .9rem; margin: 10px 0 0; }


/* --- Variantes de bouton propres à fitness ------------------------------- */
/* Le socle porte `.bouton` et ses variantes ; celle-ci ne concerne que les
   actions destructrices de ce portail (révoquer un jeton, déconnecter un
   service). */
.bouton.danger {
  color: var(--err-tx); border-color: var(--err-line); background: var(--err-bg);
}
.bouton.danger:hover { background: var(--err-bg); filter: brightness(.96); }

/* --- Pages sans bandeau : connexion, mot de passe ------------------------ */
/* Elles n'ont pas de session, donc pas de menu de compte — d'où une carte
   centrée plutôt que le gabarit de base. */
body.centered {
  display: flex; align-items: center; justify-content: center;
  /* 100dvh, jamais 100vh : sur mobile, vh compte la barre d'adresse même
     rétractée, et la carte se retrouve poussée sous le pli. */
  min-height: 100dvh; padding: var(--e-4);
}
.card.auth { width: min(380px, 100%); }
.card.auth h1 { margin-top: 0; }
/* L'espacement entre champs vient du socle via `.champ` ; il ne reste ici que
   le rythme vertical du formulaire lui-même. */
.card.auth form { display: flex; flex-direction: column; gap: var(--e-3); }
.auth-error {
  color: var(--err-tx); background: var(--err-bg);
  border: 1px solid var(--err-line); border-radius: var(--r-m);
  padding: var(--e-2) var(--e-3); font-size: .9rem; margin: 0 0 var(--e-3);
}

/* --- Jetons d'écriture (réglages) ---------------------------------------- */
.jetons { list-style: none; padding: 0; margin: var(--e-3) 0 0; }
.jetons li {
  display: flex; align-items: center; gap: var(--e-3); flex-wrap: wrap;
  padding: var(--e-3) 0; border-top: 1px solid var(--line);
}
.jetons li .muted { font-size: .85rem; margin-right: auto; }
.jeton-nu {
  display: block; word-break: break-all; margin-top: var(--e-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Un formulaire empilé : libellé, saisie, action. Le `gap` est ce qui manquait
   quand app.css ne chargeait pas — le bouton se retrouvait collé au champ. */
.form-empile {
  display: flex; flex-direction: column; gap: var(--e-3);
  margin-top: var(--e-4); align-items: flex-start;
}
.form-empile > .champ { width: 100%; }

/* --- Saisie des séries ---------------------------------------------------- */
/* Une case par série, en grille : empilées en pleine largeur, quatre séries
   remplissent l'écran d'un téléphone et la séance devient illisible. */
.setgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(68px, 1fr));
  gap: var(--e-2); margin-top: var(--e-2);
}
.setgrid input { width: 100%; text-align: center; }

/* Une séance annulée reste lisible mais sort visiblement du planning : barrée
   et en retrait. La faire disparaître laisserait un trou inexplicable. */
.mark.seance.cancelled {
  color: var(--tx-3); border-style: dashed; opacity: .55;
  text-decoration: line-through;
}

/* Annuler est un geste rare et volontaire : replié par défaut, il ne doit pas
   se retrouver à portée de pouce à côté de « Saisir cette séance ». */
/* Ne reste que la marge : `.repli` du socle porte le reste — curseur, cible de
   44px, chevron dessiné, et surtout le masquage du marqueur ▶ natif que ces
   deux lignes ne faisaient pas. C'est de là que venait le « ▶ Je ne ferai pas
   cette séance ». Le `<form>` de réactivation garde la classe pour sa marge :
   il n'est pas un repli. */
.form-annulation { margin-top: var(--e-4); }

/* --- Étirements ---------------------------------------------------------- */
.liste-etirements { list-style: none; padding: 0; margin: 0; }
.liste-etirements li { padding: var(--e-3) 0; border-top: 1px solid var(--line); }
.liste-etirements li:first-child { border-top: none; padding-top: 0; }
.liste-etirements .muted { font-size: .85rem; }

/* --- Échauffement -------------------------------------------------------- */
/* Même découpe que les étirements : une ligne par geste, la consigne dessous.
   Le bouton ▶ reste sur la ligne du nom — on le cherche à côté de la durée. */

/* Le format d'exécution, juste sous le titre de la liste d'exercices : c'est
   la première question qu'on se pose devant une séance qu'on n'a pas encore
   faite. */
.format-seance { margin: calc(var(--e-2) * -1) 0 var(--e-3); }

/* --- Séance en direct ----------------------------------------------------- */

.etat-exo { font-size: .85rem; color: var(--done); font-variant-numeric: tabular-nums; }

.en-cours { color: var(--up); font-weight: 600; }

/* Le minuteur est fixé en bas : pendant un repos on ne cherche pas un
   compteur dans la page, on veut le voir sans rien faire.

   z-index AU-DESSUS de tout ce qui vit aussi en bas d'écran — la barre de nav
   (30) et la pastille du coach. À 20, il était dessiné *derrière* la nav,
   qui fait toute la largeur et 58 px de haut : sur téléphone, c'est-à-dire
   pendant une séance, le minuteur était intégralement recouvert. Il tournait,
   il était simplement invisible. Pendant un repos, il est ce qui compte : il
   masque la nav le temps du décompte, et pas l'inverse.

   145 ET NON 45 depuis que la pastille vient du socle : elle y est posée à
   140, quand le bouton maison qu'elle remplace était à 40. Laissé à 45, le
   minuteur repassait sous le coach — le même bug, par le même chemin. */
.minuteur {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 145;
  display: flex; align-items: center; gap: var(--e-3);
  padding: var(--e-3) var(--e-4);
  padding-bottom: calc(var(--e-3) + env(safe-area-inset-bottom));
  background: var(--accent-soft); border-top: 1px solid var(--accent-line);
}
.minuteur-titre { font-weight: 600; }
/* MÊME TAILLE QUE `.guide-valeur` (4rem), et ce n'est pas une symétrie
   décorative : pendant une séance, le temps de repos qui reste et le nombre de
   reps sont la même information — ce qu'il faut lire d'un mètre, téléphone au
   sol, sans se pencher. À 1.6rem le minuteur se lisait comme une légende de bas
   de page alors qu'il commande le départ de la série suivante. */
.minuteur-temps {
  margin-left: auto; font-size: 4rem; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.minuteur.fini { background: var(--accent); color: var(--accent-tx); }
.minuteur.fini .bouton { color: inherit; border-color: currentColor; }

/* L'action de la page, collée en bas. Au-dessus de la navigation mobile, qui
   est elle-même fixe : sans ce décalage, le bouton passe dessous et devient
   incliquable sur les trois derniers centimètres de l'écran.

   z-index 20, sous `.minuteur` (145) : c'est délibéré. Un décompte en cours
   recouvre la barre plutôt que l'inverse, parce que pendant un échauffement
   compté on ne cherche pas à démarrer la séance — on regarde le temps qui
   reste. Ne pas remonter ce z-index pour « réparer » le chevauchement. */
/* `flex` et non un simple bloc : depuis qu'un créneau échu propose « Noter »
   ET « La faire maintenant », la barre peut porter deux boutons. Empilés sans
   gouttière, deux cibles de 44px qui se touchent, c'est la seconde qu'on
   déclenche en visant la première — et ici la seconde ouvre un guide de
   quinze écrans. `gap` plutôt qu'une marge sur l'enfant : la marge ne
   s'appliquerait pas au cas à un seul bouton, qui reste le plus fréquent. */
.barre-action {
  position: sticky; bottom: 0; z-index: 20;
  margin: var(--e-4) calc(-1 * var(--e-4)) 0;
  padding: var(--e-3) var(--e-4) calc(var(--e-3) + var(--safe-b));
  background: linear-gradient(to bottom, transparent, var(--bg) 35%);
  display: flex; flex-direction: column; gap: var(--e-2);
}
@media (max-width: 599px) {
  .barre-action { bottom: calc(var(--nav-h) + var(--safe-b)); }
}

/* Les cinq dernières secondes. Le bip porte le signal ; ceci le double pour
   qui coupe le son, et sert de repère au coin de l'œil sans avoir à lire le
   chiffre. La pulsation est relancée à chaque seconde par le script. */
.minuteur.bientot { background: var(--accent-line); }
/* Au-dessus des 4rem de base, jamais en dessous. Cette règle valait 2.2rem
   quand la base en faisait 1.6, puis 3.6 quand elle en faisait 3 : laissée en
   arrière à chaque fois, le chiffre RÉTRÉCIRAIT dans les cinq dernières
   secondes, c'est-à-dire au seul moment où il compte. Elle se remonte donc
   avec la base, à chaque fois. */
.minuteur.bientot .minuteur-temps { font-size: 4.6rem; }

/* Sous 360px (iPhone SE de première génération, iPhone 5), la barre déborde de
   29px quand le chiffre grossit sur la fin du décompte. On resserre plutôt que
   de rapetisser le temps : c'est lui qu'on vient lire, les marges non. */
@media (max-width: 360px) {
  .minuteur { gap: var(--e-2); padding-left: var(--e-3); padding-right: var(--e-3); }
  .minuteur.bientot .minuteur-temps { font-size: 4rem; }
}
/* Origine à droite : le chiffre est collé au bord (margin-left:auto), un
   grossissement centré le ferait sortir de l'écran. */
.minuteur-temps.pulse { animation: minuteur-pulse 0.6s ease-out; transform-origin: right center; }
@keyframes minuteur-pulse {
  from { transform: scale(1.25); opacity: 0.55; }
  to   { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .minuteur-temps.pulse { animation: none; }
}

/* Les deux dates côte à côte : « du » et « au » sont une seule information,
   les empiler les fait lire comme deux questions. */

/* --- Cibles tactiles : 44px partout, y compris sur le discret ------------- */
/* Le socle pose `min-height: 36px` sur `.bouton.discret`, et il a raison chez
   lui : un bouton secondaire de bureau se vise à la souris. Ici on appuie en
   pleine séance, téléphone au sol, mains moites — et la mesure le disait :
   « La faire maintenant » 143×36, « Voir le détail » 98×36, « Corriger cette
   séance » 156×36, le ▶ de l'échauffement à 29×36. Un bouton qu'on rate est
   un bouton absent.

   Surchargé ici et pas corrigé dans le socle : 36px n'est pas un défaut de
   Workbench, c'est un défaut DANS CE CONTEXTE. Le remonter là-haut imposerait
   la contrainte d'une salle de sport à des applications qui n'en ont pas. */
.bouton.discret { min-height: 44px; min-width: 44px; padding: 0 var(--e-3); }

/* --- La pastille du coach ne mord plus sur la barre basse ----------------- */
/* Le socle la pose à `--e-4` du bas du cadre, ce qui la met DANS la bande de
   la navigation mobile — elle en recouvrait les items, et sur les pages
   courtes elle tombait sur le bouton principal (28px de « Noter cette
   séance », 40px de « Voir le bilan »).

   Recouvrir du texte qui défile est le lot de tout bouton flottant ; recouvrir
   l'action principale d'une page ne l'est pas. Elle passe au-dessus de la
   barre, et la page réserve de quoi la laisser passer. */
@media (max-width: 599px) {
  .pastille-assistant {
    bottom: calc(var(--nav-h) + var(--safe-b) + var(--e-2));
  }
  /* La réserve couvre la barre ET la pastille : 58 + 8 + 52 + une gouttière.
     Sans quoi le dernier bloc de la page finit sous l'un ou sous l'autre. */
  main.wrap { padding-bottom: calc(var(--nav-h) + var(--safe-b) + 84px); }
}

/* --- Les séances, en tête de la bibliothèque ------------------------------ */
/* Une ligne par séance : la lettre, le nom, ce qu'elle contient. La lettre
   d'abord parce que c'est l'identifiant — celui du calendrier, de la rotation
   et des URL — et le nom ensuite parce que c'est ce qui se lit. */
.liste-seances { padding: 0; }
.ligne-seance {
  display: flex; align-items: center; gap: var(--e-3);
  padding: var(--e-3) var(--e-4); min-height: 56px;
  text-decoration: none; color: inherit;
  border-top: 1px solid var(--line);
}
.ligne-seance:first-child { border-top: 0; }
.ligne-seance:active { background: var(--surface-2); }
.ligne-seance .nom { font-weight: 600; flex: 1 1 auto; min-width: 0; }
.ligne-seance .detail { color: var(--muted); font-size: .85rem; white-space: nowrap; }

/* Le nom d'une séance quand il accompagne son badge : du texte, donc sécable.
   « Séance A » tenait dans un badge de 78px insécables ; un vrai nom ne le
   pourrait pas, et c'est pour ça que la lettre est sortie du badge. */
.nom-seance { font-weight: 600; }

/* --- Écran d'accueil : ce qu'il y a à faire aujourd'hui -------------------- */
/* Une page d'action, pas une page de lecture. La hiérarchie y est brutale et
   volontaire : la date situe, le titre nomme, UN bouton agit, le reste est en
   texte. Trois boutons pleins ne présentent pas un choix, ils présentent une
   dette — c'est `aujourdhui.etat_du_jour` qui garantit qu'il n'y en a qu'un,
   et cette feuille qui le rend visible. */

.jour-courant { margin-bottom: var(--e-1); text-transform: capitalize; }
.etat-titre { font-size: clamp(1.5rem, 6vw, 2rem); margin: 0 0 var(--e-2); }

/* L'aperçu : des lignes, pas des cartes. Six cartes repousseraient le bouton
   sous la ligne de flottaison, et c'est lui qu'on vient chercher. */
.accueil-apercu { list-style: none; margin: 0; padding: 0; }
.accueil-apercu li {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--e-3); padding: var(--e-2) 0;
  border-bottom: 1px solid var(--line);
}
.accueil-apercu li:last-child { border-bottom: 0; }
.accueil-apercu .nom { min-width: 0; }
.accueil-apercu .cible {
  color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Le réalisé se distingue de la cible par la forme, pas seulement par le
   chiffre : sans ça, « 8 » visé et « 8 » fait se lisent pareil. */
.accueil-apercu .cible.fait { display: inline-flex; gap: var(--e-1); }
.accueil-apercu .cible.fait b {
  background: var(--accent-soft); color: var(--ink); font-weight: 600;
  border-radius: var(--r-s); padding: 1px 7px; min-width: 26px; text-align: center;
}

/* Aussi haut que « Fait » dans le guide (68px) : c'est le même geste, pris
   dans les mêmes conditions — une main, un pouce, pas de précision. */
.accueil-primaire {
  min-height: 68px; font-size: 1.15rem; margin-top: var(--e-5);
  display: flex; align-items: center; justify-content: center;
}

/* Les alternatives en ligne et en discret. Empilées et pleine largeur, elles
   se seraient lues comme trois primaires de plus. */
.accueil-secondaires {
  display: flex; flex-wrap: wrap; gap: var(--e-2); margin-top: var(--e-3);
}

/* LE PLAN DU JOUR — une carte, et toute la carte est la cible.

   La liste flottait au fil du texte : rien ne disait qu'elle formait un tout
   ni qu'elle s'ouvrait. Une carte le dit des deux façons. Le chevron n'est
   qu'un signe — viser huit pixels avec un pouce moite, c'est les rater. */
.plan-jour {
  display: block; text-decoration: none; color: inherit;
  margin-top: var(--e-4); padding: var(--e-3) var(--e-4);
}
.plan-jour:active { background: var(--surface-2); }
.plan-tete {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--e-3); margin-bottom: var(--e-2);
}
.plan-quoi {
  font-size: .78rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted);
}
.plan-aller { font-size: .85rem; color: var(--accent); font-weight: 600; white-space: nowrap; }

/* LA DERNIÈRE MESURE — du suivi, pas une action, d'où le pied de page.
   `flex` explicite : `.card` est un bloc, et le chevron se posait sur le
   chiffre. */
.mesure-poids {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e-3); margin-top: var(--e-6); text-decoration: none; color: inherit;
}
.mesure-corps { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.mesure-quoi {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--tx-3);
}
.mesure-valeur {
  font-size: 1.7rem; font-weight: 700; font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.mesure-valeur .unite { font-size: .9rem; font-weight: 500; color: var(--muted); }
.mesure-quand { font-size: .85rem; color: var(--muted); }

/* Le pied de la carte de suivi : la valeur d'un côté, l'invite de l'autre. */
.suivi-pied {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e-3); margin-top: var(--e-3); font-size: .9rem;
}

/* --- Séance guidée --------------------------------------------------------- */
/* Un exercice à l'écran, lisible à un mètre : le téléphone est au sol. */
.guide-etapes { list-style: none; margin: 0; padding: 0; }
/* L'ÉCHAUFFEMENT — le prologue du guide.

   Aligné à gauche là où les étapes sont centrées, et c'est délibéré : une
   étape se regarde (un nom, un chiffre, un bouton), un échauffement se LIT
   (cinq lignes, avec leurs consignes). Centrer une liste de cinq entrées de
   longueurs inégales rend chaque ligne plus lente à retrouver.

   Le bouton de fin est collant en bas : la liste dépasse l'écran sur un
   téléphone, et un bouton qui la suit sort du cadre au moment précis où l'on
   vient de finir de s'échauffer. */
.guide-prologue { text-align: left; padding-bottom: var(--e-6); }
.guide-prologue-titre {
  font-size: 1.15rem; font-weight: 700; margin: 0 0 var(--e-4);
}
.liste-echauffement { list-style: none; margin: 0 0 var(--e-5); padding: 0; }
.liste-echauffement > li {
  padding: var(--e-3) 0; border-bottom: 1px solid var(--line);
}
.liste-echauffement > li:last-child { border-bottom: 0; }
.liste-echauffement .sub { margin: 2px 0 0; font-size: .85rem; }
/* PARTAGÉE par le prologue du guide et l'aperçu de séance : c'est la même
   liste, elle n'a pas à être stylée deux fois. L'aperçu la portait dans un
   `style=` inline sans repli, donc avec le débordement mesuré ci-dessous —
   corrigé d'un côté seulement, il serait resté de l'autre.

   `flex-wrap`, et le nom qui a le droit de rétrécir. Mesuré, pas supposé :
   dans 326px de contenu utile (un iPhone de 390 moins les marges), deux des
   cinq lignes débordaient — 423px pour « Cercles de bras + rotations
   d'épaules · 10 dans chaque sens », 361px pour « Chat-vache (dos rond / dos
   creux) ». Soit près de cent pixels hors cadre, invisibles au test, qui ne
   lit que la présence du texte et jamais sa position.

   Le détail garde son `nowrap` — on ne coupe pas « 45 s » en deux — et son
   `margin-left: auto` le tient à droite, y compris quand le repli l'envoie
   seul sur la ligne suivante. */
.liste-echauffement .ligne {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--e-2) var(--e-3); flex-wrap: wrap;
}
.liste-echauffement .nom { font-weight: 600; flex: 1 1 auto; min-width: 0; }
.liste-echauffement .detail {
  color: var(--muted); display: inline-flex; align-items: center; gap: var(--e-2);
  white-space: nowrap; margin-left: auto;
}
.guide-prologue [data-prologue-fini] { position: sticky; bottom: var(--e-3); }

.guide-etape { text-align: center; }
.guide-tete { display: flex; justify-content: space-between; align-items: center;
              gap: var(--e-3); margin-bottom: var(--e-3); }
.guide-nom { font-size: 1.15rem; font-weight: 700; text-align: left; }
.guide-compte { color: var(--muted); font-size: .85rem; }

/* Le ⓘ. 44px, la cible tactile minimale : il porte la technique, qu'on ouvre
   entre deux séries avec un pouce moite. Rond et en trait fin — il situe, il
   ne réclame pas. Le seul objet en accent de l'écran reste « Fait ». */
.guide-info {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%;
  border: 1.5px solid var(--line-controle); background: var(--surface);
  color: var(--muted); font-family: var(--serif); font-size: 1.1rem;
  font-style: italic; font-weight: 700; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}

/* Le bandeau de séries : `[10][10][9][ ]` à la place de « série 3 / 4 ».
   Un compteur abstrait dit où l'on en est ; le bandeau dit EN PLUS d'où sort
   la valeur proposée — ce qui rend le report compréhensible sans l'expliquer. */
.guide-outils { display: flex; gap: var(--e-2); flex: 0 0 auto; align-items: center; }
/* Le libellé reste écrit, il ne devient pas une icône nue : ce bouton existe
   parce qu'on ne DÉCOUVRAIT pas qu'un minuteur était là — il fallait valider
   une série pour le voir démarrer. Une horloge seule reposerait le problème. */
.guide-repos { white-space: nowrap; font-size: .82rem; min-height: 44px; }
/* Le nom cède la place avant les outils, et se replie plutôt que de les
   pousser hors du cadre. */
.guide-tete .guide-nom { min-width: 0; }

.guide-bandeau { display: flex; gap: var(--e-2); justify-content: center;
                 margin-bottom: var(--e-3); }
.guide-bandeau span {
  min-width: 42px; height: 36px; border-radius: var(--r-m);
  display: flex; align-items: center; justify-content: center;
  font-size: 1rem; font-weight: 700; font-variant-numeric: tabular-nums;
  border: 1.5px solid var(--line); background: var(--surface-2); color: var(--muted);
}
.guide-bandeau span.faite {
  background: var(--accent-soft); border-color: var(--accent-line); color: var(--ink);
}
.guide-bandeau span.courante {
  border-color: var(--accent); border-width: 2px; background: var(--surface);
}

.guide-cible { color: var(--muted); margin: var(--e-2) 0;
               display: flex; align-items: center; justify-content: center;
               gap: var(--e-2); flex-wrap: wrap; }
.guide-cible b { color: var(--ink); }
.guide-ecart {
  font-size: .8rem; padding: 1px 8px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); font-weight: 600;
}
/* N'apparaît que sur écart. Le report propage le réel : sans ce retour, un 9
   raté se reproduit jusqu'à la fin de l'exercice et bloque la montée. */
.guide-revenir {
  font: inherit; font-size: .8rem; font-weight: 600;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent-line); border-radius: 999px;
  padding: 4px 12px; min-height: 32px; cursor: pointer;
}

.guide-compteur { display: flex; align-items: center; justify-content: center;
                  gap: var(--e-4); margin: var(--e-4) 0; }
.guide-valeur { font-size: 4rem; font-weight: 700; font-variant-numeric: tabular-nums;
                min-width: 3ch; line-height: 1; }

/* 72px, et VISIBLES en permanence. Avec le report, corriger n'est pas un cas
   rare : sur un relevé 10-10-9-8 c'est deux corrections pour quatre séries,
   soit un geste sur trois, et l'écart vaut presque toujours −1. Un pas caché
   derrière un appui sur le chiffre coûterait un geste de plus au moment précis
   où l'on est le plus cuit. */
.guide-pas {
  flex: 0 0 auto; width: 72px; height: 72px; border-radius: 50%;
  border: 1.5px solid var(--line-controle); background: var(--surface);
  color: var(--ink); font: inherit; font-size: 2rem; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.guide-pas:active { background: var(--surface-2); }

/* LE FILET. Sans lui, douze pixels séparent le compteur du bouton vert, et
   valider en croyant décrémenter écrit une valeur fausse — or la cible ne
   monte que si TOUTES les séries l'ont tenue, donc une erreur ne salit pas
   l'historique : elle déclenche ou bloque une progression, et rien ne
   rétrograde. Ne pas resserrer pour gagner de la hauteur. */
.guide-fait { width: 100%; font-size: 1.15rem; min-height: 68px;
              margin-top: var(--e-6); }

/* La feuille de technique, par-dessus l'étape. Elle RESTE sur l'écran
   d'exécution — la repousser sur la transition défait une correction
   documentée, et en circuit elle s'y rejouerait dix-huit fois — mais elle ne
   coûte plus une ligne de hauteur. */
.guide-feuille {
  position: fixed; inset: 0; z-index: 150; overflow-y: auto;
  background: var(--bg); padding: var(--e-4);
  padding-bottom: calc(var(--e-6) + env(safe-area-inset-bottom));
  text-align: left;
}
.guide-feuille h2 { margin-top: 0; }
.guide-feuille .schema { flex: none; width: 100%; max-width: 420px; margin: 0 auto var(--e-4); }
.guide-feuille .fiche { font-size: 1rem; }
.guide-feuille [data-feuille-fermer] { margin-top: var(--e-5); min-height: 52px; }
.guide-fin { text-align: center; }

/* L'écran entre deux exercices. Plein cadre et centré : c'est un temps mort
   assumé, pas une étape de plus dans la liste — on souffle, et on lit ce qui
   arrive. */
.guide-transition {
  display: flex; flex-direction: column; align-items: center; gap: var(--e-3);
  text-align: center; padding: var(--e-5) var(--e-4);
}
.guide-transition-titre {
  margin: 0; font-size: .78rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted);
}
.guide-transition-nom { margin: 0; font-size: 1.25rem; font-weight: 600; }
/* BORNÉ EN HAUTEUR, pas en largeur. La transition montre la pose SEULE, dont
   le cadrage est `100 × 120` — un portrait, là où la paire fait `200 × 120`.
   Contraint à 260px de large, ce portrait montait à plus de 300px de haut et
   poussait l'objectif et le bouton hors de l'écran. À 200px de haut, le
   bonhomme occupe environ 167px de large : au-dessus de ce que donnait la
   paire, où chaque pose tombait à 130. */
.guide-transition-schema {
  width: 100%; max-width: 260px; display: flex; justify-content: center;
}
.guide-transition-schema svg {
  display: block; width: auto; height: auto;
  max-height: 200px; max-width: 100%;
}
.guide-transition-repos {
  margin: 0; font-variant-numeric: tabular-nums; color: var(--muted);
}

/* --- Bilan de fin de séance ----------------------------------------------- */
.bilan-exo .muscles-ligne { margin-top: var(--e-2); }
.ligne-suite { padding: var(--e-2) 0; border-top: 1px solid var(--line); }
.ligne-suite:first-child { border-top: none; }
.ligne-histo {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--e-3); padding: var(--e-2) 0; border-top: 1px solid var(--line);
}
.ligne-histo:first-child { border-top: none; }

/* --- Fiche exercice ------------------------------------------------------- */
.schema-large { flex: none; width: 100%; max-width: 420px; margin: 0 auto; }
.fiche-large { font-size: .95rem; }

/* La silhouette : deux vues, groupes remplis. Le trait suit `currentColor`
   comme les schémas d'exécution, donc elle suit le thème. */
.silhouette { display: block; width: 100%; max-width: 340px; margin: 0 auto; }
.silhouette .corps {
  fill: none; stroke: currentColor; stroke-width: 1.4; opacity: .45;
}
/* `path` ET `ellipse` : muscles.py dessine les deux, et un sélecteur qui n'en
   vise qu'un laisse l'autre au remplissage par défaut — noir opaque. Un test
   vérifie que ce sélecteur couvre toutes les primitives utilisées. */
.silhouette .prim path, .silhouette .prim ellipse { fill: var(--accent); opacity: .95; stroke: none; }
.silhouette .sec path, .silhouette .sec ellipse { fill: var(--accent); opacity: .38; stroke: none; }
.silhouette .vue {
  font-size: 9px; text-anchor: middle; fill: currentColor; opacity: .6;
  font-family: inherit;
}

.legende-muscles { margin-top: var(--e-3); font-size: .9rem; }
.legende-muscles p { margin: var(--e-2) 0; }
.pastille {
  display: inline-block; width: 12px; height: 12px; border-radius: 3px;
  vertical-align: -1px; background: var(--accent);
}
.pastille.sec { opacity: .38; }

/* L'échelle de niveaux : les crans franchis, celui du jour, ceux qui restent.
   C'est la seule vue qui montre la progression comme une progression. */
.echelle { list-style: none; padding: 0; margin: 0; }
.echelle li {
  display: flex; align-items: baseline; gap: var(--e-3);
  padding: var(--e-2) 0 var(--e-2) var(--e-4);
  border-left: 3px solid var(--line);
}
.echelle li .reps { font-variant-numeric: tabular-nums; }
.echelle li.atteint { border-left-color: var(--done); color: var(--tx-2); }
.echelle li.atteint .reps { text-decoration: line-through; opacity: .7; }
.echelle li.courant {
  border-left-color: var(--accent); font-weight: 700;
  background: var(--accent-soft);
}

/* Les groupes travaillés, au bilan : des étiquettes séparées, pas une liste
   de mots. Sans fond ni écart, « Pectoraux » et « Triceps » se lisent comme
   un seul mot. */
.muscles-ligne { display: flex; flex-wrap: wrap; gap: var(--e-2); }
.tag-muscle {
  font-size: .78rem; padding: 2px 8px; border-radius: 999px;
  background: var(--accent-soft); color: var(--tx-1);
  border: 1px solid var(--accent-line);
}

/* --- Marque de séance : l'haltère et la lettre ---------------------------- */
/* La lettre seule supposait qu'on sache que A/B/C = musculation. Évident tant
   qu'il n'y avait que ça au calendrier ; plus du tout depuis le cardio. */
.mark.seance { gap: 1px; }
.mark.seance .ico { font-size: .72em; line-height: 1; }
.mark.seance .lettre { font-weight: 700; }

/* Séance déplacée : un coin marqué sur la case du jour, pas un symbole dans la
   marque — à 22px un troisième glyphe ne se lit plus. Le coin dit qu'il s'est
   passé quelque chose ; le survol et la page de séance disent quoi. */
.calday.reporte { position: relative; }
.calday.reporte::after {
  content: ""; position: absolute; top: 0; right: 0;
  border-width: 0 8px 8px 0; border-style: solid;
  border-color: transparent var(--up) transparent transparent;
}

/* Le cardio : un cœur tant qu'il est proposé, l'icône du sport une fois fait.
   C'est ce qui distingue une intention d'une sortie. */
.mark.cardio {
  background: transparent; border: 1.5px dashed var(--missed);
  font-size: .8em;
}
.mark.cardio.done { border-style: solid; border-color: var(--done); }

.badge.cardio { background: rgba(192,57,43,.13); color: var(--missed); }

/* Deux champs côte à côte tant que l'écran le permet — durée et distance vont
   ensemble, les empiler double la hauteur du formulaire pour rien. */
.deux-champs { display: flex; gap: var(--e-3); }
.deux-champs .champ { flex: 1; min-width: 0; }

/* La consigne technique dans le guidage : repliée, parce que ce qu'on regarde
   pendant la série est la cible et le bouton. Mais atteignable sans quitter
   l'étape — y retourner par la vue complète faisait perdre où on en était. */
/* `text-align: left` est le seul réglage propre à cet écran : la fiche vit dans
   un `.guide-etape` centré, et une consigne technique ne se lit pas centrée.
   Le reste vient de `.repli` — y compris le masquage du ▶ natif, que la mise en
   forme d'origine ne faisait pas. */
.guide-technique { margin-top: var(--e-3); text-align: left; }
.guide-technique .fiche { margin-top: 0; }
/* La durée suit la date sans lui disputer la place : c'est un repère, pas le
   sujet de la ligne. */
.duree-seance { font-weight: 400; font-size: .82rem; margin-left: var(--e-2); }

/* Séparateur « ou » entre le mot de passe et la connexion externe.
   Les deux filets sont des pseudo-éléments qui prennent la place restante :
   un <hr> avec un mot posé dessus ne se recentre pas quand le libellé change.
   Repris de wishlist à l'identique — au troisième usage, ça monte au socle. */
.separateur {
  display: flex; align-items: center; gap: var(--e-3);
  color: var(--tx-3); font-size: var(--t-petit);
}
.separateur::before, .separateur::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}

/* Le créneau qu'une séance déplacée a soldé : barré, en retrait, à côté du
   jour réel. Assez présent pour dire « c'était prévu là », assez discret pour
   que l'œil aille sur la vraie date. */
.creneau-barre {
  color: var(--muted); font-weight: 400; text-decoration-thickness: 1px;
  margin-right: var(--e-1);
}

/* --- Bibliothèque d'exercices -------------------------------------------- */
/* Un repli par exercice, aligné sur `.repli` du socle (chevron, cible de 44px,
   ▶ natif masqué). Ne restent ici que l'espacement de la liste et la mise en
   ligne du nom avec son badge de séance.
   
   Préfixe `biblio-` et non `exo-` : `.exo` est le nom d'un exercice de séance,
   et ces règles-ci lui écrasaient ses bordures et ses marges. */
/* Une liste continue, pas quarante-cinq cartes détachées : à ce nombre, la
   marge entre cartes fait plus de hauteur que les cartes elles-mêmes. Les
   replis se collent et ne gardent leurs coins qu'aux extrémités. */
.biblio { margin-top: var(--e-2); }
.biblio-item { border-radius: 0; }
.biblio-item + .biblio-item { border-top: none; }
.biblio-item:first-child { border-radius: var(--r-m) var(--r-m) 0 0; }
.biblio-item:last-child { border-radius: 0 0 var(--r-m) var(--r-m); }
.biblio-item:only-child { border-radius: var(--r-m); }
.biblio-item > summary { display: flex; align-items: center; gap: var(--e-2); }
/* Pousse le badge à droite sans le décoller du nom quand celui-ci passe sur
   deux lignes — un `justify-content: space-between` le laisserait flotter au
   milieu d'une ligne courte. */
.biblio-nom { flex: 1; min-width: 0; }
.biblio-meta { margin: var(--e-3) 0 0; font-size: .85rem; }
/* Secondaire : la même pastille en retrait, comme la silhouette distingue les
   deux teintes. Le matériel n'est pas un muscle — il sort de la couleur
   d'accent pour ne pas se lire comme un groupe de plus. */
.chip.faible { opacity: .55; font-weight: 500; }
.chip.materiel {
  background: transparent; border: 1px solid var(--line);
  color: var(--muted); font-weight: 500;
}

/* La planche-contact des schémas : les trois tailles côte à côte, parce qu'un
   schéma lisible à 420px peut être une tache à 176. */
.planche { display: flex; gap: var(--e-4); align-items: flex-start; flex-wrap: wrap; }
.planche figcaption { font-size: .75rem; text-align: center; margin-top: 4px; }

/* Formulaire de fin de guide : champ de note et bouton empilés. */
.guide-fin-form {
  display: flex; flex-direction: column; gap: var(--e-3);
  max-width: 420px; margin: 0 auto;
}

/* COMMENT ÇA MARCHE — une page qui se lit de haut en bas sur un téléphone.
   Les titres de moment restent au corps du texte à peine grossi : ce sont des
   repères, pas des affiches, et quatre h2 de 1.5rem pousseraient les règles
   hors du premier écran. */
.moment h2 { font-size: 1.05rem; margin: 0 0 var(--e-2); }
.moment p { margin: 0 0 var(--e-2); }
.moment p:last-child { margin-bottom: 0; }
/* La vraie prochaine séance, en miniature : un composant réel ne vieillit pas
   comme une capture d'écran. */
.plan-jour-mini {
  display: flex; flex-direction: column; gap: var(--e-1, 4px);
  margin-top: var(--e-3); padding: var(--e-3);
  border: 1px solid var(--line); border-radius: 10px;
  text-decoration: none; color: inherit;
}
.regles { padding-left: 1.4em; }
.regles li + li { margin-top: var(--e-3); }
.carte-sections { list-style: none; padding: 0; }
.carte-sections li { display: flex; gap: var(--e-3); align-items: baseline; }
.carte-sections li + li { margin-top: var(--e-3); }
.carte-sections .ico { flex: none; width: 1.5em; text-align: center; }
/* Le bouton de fin d'entretien, posé dans le fil du chat : il doit se
   détacher du dernier message sans se coller à la saisie. */
.chatlog .suite-entretien { display: block; margin: var(--e-3) 0; text-align: center; }
.avance { margin-top: var(--e-5); }
