@charset "UTF-8";
/* ============================================================================
   muscles-par-sport, feuille de style unique.
   Ecrite a la main par la story 3.1. Ce fichier n'est PAS genere.
   Contrat : AR-14 / AD-14, une seule feuille de style, aucun framework,
   aucun CDN, aucun @import, tous les jetons en proprietes personnalisees sur
   :root, aucune valeur litterale de couleur, de rayon, d'ombre, de duree ou
   d'espacement en dehors de ce bloc.

   Deux exceptions inevitables, ecrites ici pour qu'on ne lise pas un defaut
   la ou il n'y en a pas :
     1. un prelude de media query ne sait pas lire une propriete
        personnalisee, donc 640px, 768px et 992px apparaissent en clair dans
        les trois preludes @media (et sont doubles en jetons --mps-bp-* a
        titre documentaire),
     2. la boite @page n'herite pas des proprietes personnalisees, donc la
        marge d'impression est ecrite en clair dans @page.

   Ordre du fichier :
     1. police de repli metrique
     2. jetons (:root)
     3. reset et couche document
     4. coque de site (ml-)
     5. rampe de niveaux et textures
     6. mise en page et grilles
     7. composants (mps-)
     8. confinement du rendu hors ecran
     9. points de rupture 640 / 768 / 992
    10. impression
    11. mouvement reduit
    12. couleurs forcees
   ========================================================================= */

/* ---------------------------------------------------------------------------
   1. Police de repli metrique (FR-37, AC-33, AD-17, R-33)

   La pile est celle que le critere cite mot pour mot :
     Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif
   Le repli metrique est donc declare SUR LA DEUXIEME FAMILLE de cette pile,
   et non en inserant une cinquieme famille qui casserait la citation.

   Pourquoi pas font-family: Roboto : la feuille Google Fonts est chargee
   AVANT ce fichier (ordre du head fixe par AD-17), donc une @font-face de
   meme famille et de meme plage de graisses declaree ici gagnerait et le vrai
   Roboto ne servirait jamais.

   local("Roboto") vient en premier. RESERVE CONNUE, corrigee ici par la revue
   de code du 31/08/2026 (AA-04), parce que la phrase qui tenait cette place
   affirmait l'inverse de ce que fait le CSS : size-adjust s'applique a la face
   qui repond, quelle qu'elle soit. Sur Android et ChromeOS, ou le vrai Roboto
   est installe, le rendu AVANT bascule est donc du Roboto reduit a 97.7 %,
   pendant que le rendu APRES bascule est le Roboto de Google a 100 %. Ce n'est
   pas metriquement exact, c'est un ecart de 2.3 % sur l'avance, mesure en
   laboratoire (face ajustee 359.80px contre 368.39px sans les surcharges, soit
   0.9767). Sur macOS et sur Windows, ou Roboto n'est pas installe, la face
   repli est bien Helvetica Neue ou Arial et la correction est celle qui a ete
   mesuree pour elles.
   Le retrait de local("Roboto") a ete envisage et DIFFERE plutot qu'applique :
   Android alias arial et helvetica vers Roboto dans fonts.xml, le retrait
   pourrait donc ne rien changer, ou au contraire faire tomber la pile sur Noto
   Sans sans aucune surcharge de metriques, ce qui serait pire. Trancher demande
   une mesure sur un vrai appareil Android, que la story 6.3 fait en vague 12
   quand elle profile CLS, et elle est reparatrice nommee de ce fichier (R-6).
   Si aucune des six polices locales ne repond, la @font-face ne charge pas et
   le navigateur passe simplement a Arial puis Noto Sans puis sans-serif.

   Metriques Roboto : unitsPerEm 2048, ascender 1900, descender -500,
   lineGap 0, soit 92.7734% / 24.4141% / 0% avant correction, chacune divisee
   par le size-adjust retenu pour conserver la meme boite.

   size-adjust MESURE, pas recopie de memoire. Chaine temoin rendue a 15px
   dans Chromium sur cette machine, story 3.1 :
     Roboto Light (Google)              544.8 px
     Helvetica Neue Regular (macOS)     557.7 px  soit un ideal de 97.7 %
     Arial Regular (Windows et macOS)   557.0 px  soit un ideal de 97.8 %
   Les deux faces qui serviront reellement de repli demandent donc la meme
   correction a un dixieme de point pres, ce qui rend une valeur unique
   legitime. Retenu : 97.7 %, d'ou 92.7734 / 0.977 = 94.96 % d'ascendante et
   24.4141 / 0.977 = 24.99 % de descendante.

   font-weight: 400 et surtout PAS 100 900. C'est portant, cela a ete verifie
   a l'ecran et pas seulement mesure : avec la plage 100 900 la face pretend
   couvrir le gras, le navigateur cesse donc de le synthetiser, et les cinq
   graisses 300, 400, 500, 700 et 900 se rendent toutes identiques quand
   fonts.googleapis.com est bloque, c'est-a-dire exactement le cas que AC-61
   mesure. Le <b> des titres, qui est la signature de la DA du site,
   disparaissait purement et simplement. En declarant la face en 400, un poids
   de 700 ou de 900 retombe dessus et le navigateur applique son gras
   synthetique. A noter pour un futur lecteur : la largeur d'avance ne change
   pas sous gras synthetique dans Chromium, donc une mesure de largeur ne voit
   pas la difference, seule une capture la voit. C'est ainsi que le defaut a
   ete trouve.
   --------------------------------------------------------------------------- */
@font-face {
  font-family: "Helvetica Neue";
  src: local("Roboto"), local("Helvetica Neue"), local("HelveticaNeue"),
       local("Arial"), local("ArialMT"), local("Liberation Sans"), local("Arimo");
  font-weight: 400;
  font-style: normal;
  size-adjust: 97.7%;
  ascent-override: 94.96%;
  descent-override: 24.99%;
  line-gap-override: 0%;
}

/* ---------------------------------------------------------------------------
   2. Jetons
   --ml- : jeton du noyau du site (00-da-reference section 15), valeur reprise
           telle quelle pour qu'un lecteur puisse la comparer au site.
   --mps- : jeton ajoute par cet outil (frontmatter de DESIGN.md).
   --------------------------------------------------------------------------- */
:root {
  /* --- 2.1 Noyau mlebret.fr, 00-da-reference section 15 ------------------- */
  --ml-bg: #ffffff;
  --ml-surface: #ffffff;
  --ml-dark: #1c1c1c;
  --ml-text: #666666;
  --ml-heading: #333333;
  --ml-muted: #999999;        /* legal uniquement sur fond sombre (5.98:1) */
  --ml-border: #cccccc;       /* legal uniquement en decoration (1.61:1) */
  --ml-rule: #e9e9e9;
  --ml-accent: #007bff;
  --ml-field: #242424;
  --ml-shadow-bar: 0 2px 3px rgba(0, 0, 0, 0.1);
  --ml-shadow-card: 0 .5rem 1rem rgba(0, 0, 0, .15);
  --ml-radius-card: 1rem;
  --ml-ease: 0.3s ease-in-out;
  --ml-header-h: 80px;
  --ml-container: 1140px;

  /* --- 2.2 Couleurs, bloc colors de DESIGN.md (31 entrees) ---------------- */
  --mps-page-bg: var(--ml-bg);
  --mps-surface: var(--ml-surface);
  --mps-surface-sunken: #f6f8fa;
  --mps-surface-dark: var(--ml-dark);
  --mps-ink-heading: var(--ml-heading);
  --mps-ink-body: var(--ml-text);
  /* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (AA-AUD-06). Le
     jeton valait #767676, mesure a 4.542:1 sur blanc, soit quatre centiemes
     au-dessus du plancher de 4.5:1 : la reconciliation R-25 a decline le
     #595959 de la correction C5 sur une valeur de 4.61:1 qui n'a jamais ete
     celle du jeton livre. La marge reelle etait de 0.9 pour cent, sur l'encre
     de l'invite de recherche, de la legende de la silhouette, de l'invite de
     defilement et de la ligne des points faibles de chaque carte. #6f6f6f vaut
     4.95:1 sur blanc et retire ce risque permanent. La correction du chiffre
     4.61:1 dans R-25 et dans DESIGN.md revient a l'orchestrateur, elle est
     consignee dans deferred-work.md. */
  --mps-ink-muted: #6f6f6f;   /* plancher du texte sur blanc, 4.95:1 mesure */
  --mps-ink-inverse: #ffffff;
  --mps-ink-on-dark: var(--ml-muted);
  --mps-border-hairline: var(--ml-rule);
  --mps-border-decor: var(--ml-border);
  --mps-border-control: #767676;
  --mps-border-field: var(--ml-field);
  --mps-accent: var(--ml-accent);
  --mps-accent-text: #0062cc;
  --mps-accent-hover: #004a99;
  --mps-accent-soft: #e7f1ff;
  --mps-focus-ring: #0062cc;
  --mps-level-0-fill: #eef2f6;
  --mps-level-0-ink: #41505f;
  --mps-level-1-fill: #bcd8f7;
  --mps-level-1-ink: #0b2545;
  --mps-level-2-fill: #1f6fd0;
  --mps-level-2-ink: #ffffff;
  --mps-level-3-fill: #00408f;
  --mps-level-3-ink: #ffffff;
  --mps-zone-stroke: #1c1c1c;
  --mps-zone-body: #f6f8fa;
  /* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-09). #b8c2cc
     ne valait que 1.81:1 sur la page blanche, tres en dessous du plancher de
     3:1 que WCAG 1.4.11 fixe pour un objet graphique necessaire a la
     comprehension. Les deux silhouettes du hero se lisaient comme quatre taches
     bleues flottantes plutot que comme deux corps. #8a97a4 vaut 3.08:1 et garde
     le trait discret devant les contours de zone, qui restent en #1c1c1c. */
  --mps-zone-body-stroke: #8a97a4;
  --mps-alert: #a30606;
  --mps-alert-soft: #fdf0f0;

  /* Selection, reprise du noyau (00 section 2.4) */
  --mps-select-bg: #000000;
  --mps-select-ink: #ffffff;

  /* Encre d'impression : les navigateurs suppriment les fonds par defaut, le
     texte doit donc etre noir plein et non gris. */
  --mps-print-ink: #000000;

  /* Mots-cles systeme, utilises par le seul bloc forced-colors */
  --mps-fc-ink: CanvasText;
  --mps-fc-bg: Canvas;
  --mps-fc-accent: Highlight;
  --mps-fc-accent-ink: HighlightText;
  --mps-fc-btn-ink: ButtonText;
  --mps-fc-btn-bg: ButtonFace;

  /* --- 2.3 Typographie, les 13 roles ------------------------------------- */
  --mps-font-family: Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;

  --mps-h1-size: clamp(32px, 6vw, 46px);
  --mps-h1-weight: 300;
  --mps-h1-lh: 1.05;
  --mps-h1-strong-weight: 500;

  --mps-h2-size: clamp(26px, 4vw, 40px);
  --mps-h2-weight: 300;
  --mps-h2-lh: 1;
  --mps-h2-strong-weight: 500;

  --mps-h3-size: 22px;
  --mps-h3-weight: 300;
  --mps-h3-lh: 1.15;
  --mps-h3-strong-weight: 900;

  --mps-h4-size: 20px;
  --mps-h4-weight: 300;
  --mps-h4-lh: 1.25;

  --mps-lead-size: 17px;
  --mps-lead-weight: 300;
  --mps-lead-lh: 1.55;

  --mps-body-size: 15px;
  --mps-body-weight: 300;
  --mps-body-lh: 1.6;

  --mps-ui-size: 15px;
  --mps-ui-weight: 400;
  --mps-ui-lh: 1.4;

  --mps-table-size: 14px;
  --mps-table-weight: 400;
  --mps-table-lh: 1.45;

  --mps-caption-size: 13px;
  --mps-caption-weight: 400;
  --mps-caption-lh: 1.5;

  --mps-label-caps-size: 12px;
  --mps-label-caps-weight: 900;
  --mps-label-caps-lh: 1.4;
  --mps-label-caps-tracking: 0.2em;   /* signature du site, nulle part ailleurs */

  --mps-badge-size: 12px;
  --mps-badge-weight: 500;
  --mps-badge-lh: 1.3;
  --mps-badge-tracking: 0.06em;

  --mps-numeral-size: 14px;
  --mps-numeral-weight: 700;
  --mps-numeral-lh: 1;

  --mps-btn-size: 13px;
  --mps-btn-weight: 900;
  --mps-btn-lh: 20px;
  --mps-btn-tracking: 0.06em;

  /* --- 2.4 Espacement ----------------------------------------------------- */
  --mps-space-1: 4px;
  --mps-space-2: 8px;
  --mps-space-3: 12px;
  --mps-space-4: 16px;
  --mps-space-5: 24px;
  --mps-space-6: 32px;
  --mps-space-7: 48px;
  --mps-space-8: 70px;
  --mps-gutter: 30px;
  --mps-container: var(--ml-container);
  --mps-prose-max: 720px;
  --mps-profile-label-w: 8.5rem;
  /* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-10). Valait
     360px : la piste de grille retiree des 24px de rembourrage de chaque cote
     du panneau rendait le SVG a 312px, soit sa largeur sur un telephone de
     390px, et la correction C1 demande 420px sur bureau. La piste vaut
     desormais --mps-map-max plus ses deux rembourrages. */
  --mps-map-col: 468px;
  --mps-space-header-h: 80px;
  --mps-space-header-h-compact: 56px;
  --mps-tap-min: 24px;
  --mps-side-pad: 15px;

  /* Hauteur de barre vivante : declaree ici, basculee au point de rupture
     768px, lue par scroll-margin-top sur [id] ET par scroll-padding-top sur
     l'element defilant (WCAG 2.2 SC 2.4.11, UX-DR7). */
  --mps-header-h: var(--mps-space-header-h-compact);

  /* Rythme vertical entre sections, bascule a 768px */
  --mps-section-gap: var(--mps-space-7);

  /* --- 2.5 Rayons --------------------------------------------------------- */
  --mps-radius-none: 0;
  --mps-radius-sm: 2px;
  --mps-radius-md: 8px;
  --mps-radius-lg: var(--ml-radius-card);
  --mps-radius-pill: 50rem;
  --mps-radius-full: 9999px;

  /* --- 2.6 Elevations, cinq et pas une de plus ---------------------------- */
  --mps-elev-bar: var(--ml-shadow-bar);
  --mps-elev-card: var(--ml-shadow-card);
  --mps-elev-card-hover: 0 .75rem 1.5rem rgba(0, 0, 0, .18);
  --mps-elev-sticky-col: 4px 0 6px -4px rgba(0, 0, 0, 0.15);
  --mps-elev-toolbar-stuck: 0 4px 8px -4px rgba(0, 0, 0, 0.18);

  /* --- 2.7 Mouvement, une seule duree ------------------------------------- */
  --mps-ease: var(--ml-ease);
  --mps-motion-off: 0.01ms;
  --mps-lift: -2px;

  /* --- 2.8 Metriques de composants (bloc components de DESIGN.md) --------- */
  --mps-hairline-w: 1px;
  --mps-border-w: 2px;
  --mps-focus-w: 3px;
  --mps-focus-offset: 2px;
  --mps-notice-border-w: 4px;
  --mps-card-sel-border-w: 4px;

  --mps-btn-pad: 16px 24px 14px;
  --mps-btn-pad-page: 24px 27px 22px;
  --mps-btn-pad-sm: 6px 20px 5px;

  --mps-field-h: 52px;
  --mps-field-pad: 14px 44px;

  --mps-chip-pad: 8px 14px;
  --mps-chip-min-h: 40px;
  --mps-segment-pad: 6px 14px;
  --mps-check-size: 12px;

  --mps-icon-sm: 16px;
  --mps-icon-md: 20px;
  --mps-icon-lg: 24px;
  --mps-picto-stroke: 1.5;

  --mps-badge-pad: 3px 10px;

  --mps-swatch-size: 26px;
  --mps-swatch-size-table: 22px;
  --mps-strip-cell: 8px;

  --mps-cell-pad: 8px 12px;
  --mps-row-min-h: 44px;
  --mps-list-pad: 12px 4px;

  --mps-tray-min-h: 64px;
  --mps-live-min-h: 24px;
  --mps-meter-h: 8px;

  --mps-nav-pad: 30px 20px;
  --mps-footer-pad: 60px 0 10px;
  --mps-footer-link-size: 12px;
  --mps-legal-gap: 15px;

  --mps-separator-w: 5rem;
  --mps-separator-h: 6px;
  --mps-underline-w: 30px;
  --mps-underline-h: 2px;

  --mps-map-max: 420px;
  --mps-card-min: 280px;
  --mps-compare-col: 340px;
  --mps-hero-lg: 320px;
  --mps-hero-md: 280px;
  --mps-hero-sm: 200px;

  --mps-zone-stroke-w: 1px;
  --mps-zone-stroke-sel-w: 3px;
  --mps-zone-stroke-deep-w: 2px;
  /* REPARATION story 5.4 (R-6), revue de code du 01/09/2026, AA-5.3-01 et
     AA-5.3-06. Le survol, le focus et la selection avaient la MEME
     specificite (0,3,0) que la regle du muscle profond, qui vient apres eux
     dans l'ordre du fichier : les 9 elements de zone des 8 groupes profonds
     restaient donc a 2px dans tous les etats, et selectionner un groupe
     profond ne se voyait nulle part sur la silhouette. Le focus clavier, lui,
     n'epaississait pas le contour du tout. Chaque etat a maintenant sa
     largeur normale et sa largeur profonde, et l'ordre des regles a ete
     corrige. */
  --mps-zone-stroke-hover-w: 2px;
  --mps-zone-stroke-deep-hover-w: 3px;
  --mps-zone-stroke-deep-sel-w: 4px;
  --mps-zone-dash: 3 2;
  /* Halo du chiffre de zone, VQA-W9-02. */
  --mps-zone-num-halo-w: 2.5px;
  --mps-body-stroke-w: 1.5px;
  --mps-zone-num-size: 11px;

  /* Confinement hors ecran, valeurs de depart re-mesurees par la story 6.3 */
  /* Tailles de reserve du confinement hors ecran, RE-MESUREES le 01/09/2026
     sur la page servie. Les valeurs de depart (1400px et 900px) etaient fausses
     dans les deux sens : une fiche de sport mesure 2556px de moyenne et une
     fiche de muscle 635px a 768px et au-dessus, 3643px et 786px a 390px. La
     consequence etait visible sur beta : un saut a froid sur
     #sport-natation-crawl atterrissait 4371px trop haut, parce que les quatre
     fiches precedentes etaient chacune sous-estimees de 1156px, ce qui est
     exactement le parcours FR-30 decrit (arriver depuis une reponse d'IA sur
     l'ancre d'un sport). Le mot-cle auto fait memoriser la vraie taille des
     qu'une fiche a ete rendue une fois, donc ces deux valeurs ne servent qu'au
     chargement a froid, et c'est le seul moment ou l'ancre compte. Valeurs
     mobiles ici, valeurs bureau dans la requete media 768px. La story 6.3 les
     re-mesure en vague 12 sur N de 89.

     HISTORIQUE. La story 5.4 (R-6, BH-01) avait remplace les moyennes par les
     MAXIMA, sur un raisonnement par fiche : avec une moyenne, une fiche sur
     deux reste sous-estimee. Le raisonnement qui compte est CUMULATIF, parce
     que ce que le navigateur additionne pour placer une ancre, c'est la reserve
     de TOUTES les fiches situees au-dessus d'elle. Avec des maxima l'erreur est
     systematiquement positive et elle s'accumule, avec des moyennes elle se
     compense.

     DECISION D45 DU 01/09/2026 (AA-AUD-03). Les reserves deviennent des
     MOYENNES MESUREES PAR POINT DE RUPTURE, sur les 89 fiches de sport et les
     30 fiches de muscle SERVIES, content-visibility neutralise le temps de la
     mesure, sur beta le 01/09/2026 :

       largeur   sport min / moyenne / max     muscle min / moyenne / max
       375px      3503 / 3809 / 4108            1356 / 1453 / 1561
       768px      2525 / 2705 / 2886             902 /  944 / 1014
       992px      2386 / 2603 / 2802             832 /  870 /  926
       1280px     2386 / 2603 / 2802             810 /  837 /  902

     Ecart cumule sur le document entier, ce qui est la grandeur que l'ancre
     paye. AVANT, a 1280px : 277 380px reserves contre 256 811px mesures, soit
     20 569px de trop, et le document se contractait de 8.1 pour cent pendant
     les deux premieres secondes. APRES : 256 777px contre 256 811px, soit 34px.
     A 375px, avant 406 020 contre 382 618, soit 23 402px de trop, apres
     382 740 contre 382 618, soit 122px.

     Le mot-cle auto fait toujours memoriser la vraie taille des qu'une fiche a
     ete rendue une fois, ces valeurs ne servent donc qu'au chargement a froid,
     et c'est le seul moment ou l'ancre compte. Le defilement convergent de
     muscles-par-sport/js/app.js (defilerVers) reste le filet. */
  --mps-cis-sport: auto 3810px;
  --mps-cis-muscle: auto 1455px;

  /* Points de rupture, doubles ici a titre documentaire : un prelude de media
     query ne sait pas lire une propriete personnalisee. */
  --mps-bp-hint: 640px;
  --mps-bp-md: 768px;
  --mps-bp-lg: 992px;

  /* URL canonique, une seule fois, utilisee par le bloc d'impression */
  --mps-canonical-url: "https://mlebret.fr/muscles-par-sport/";

  /* --- 2.9 Les quatre textures de la rampe -------------------------------- */
  /* Declarees ICI, une seule fois, en images de fond SVG data-URI, indexees
     par data-level (AC-7). Consommees par .mps-swatch, la cellule de tableau
     et la bande de 30 micro-pastilles.
     La zone SVG de la silhouette ne peut PAS les consommer : un <path> SVG ne
     rend aucun arriere-plan CSS, et le seul mecanisme SVG d'un remplissage
     texture est fill: url(#pattern), c'est-a-dire un <pattern> dans l'oeuvre,
     que AR-12 et le critere lui-meme interdisent. La zone porte donc
     remplissage + contour + son <text> numerique, ce qui est exactement ce
     que AD-13 exige, le texte numerique etant le porteur non chromatique
     obligatoire et la texture n'ayant jamais le droit d'etre le second. */
  --mps-texture-0: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3C/svg%3E");
  --mps-texture-1: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Ccircle cx='2' cy='2' r='1' fill='%230b2545' fill-opacity='.3'/%3E%3Ccircle cx='6' cy='6' r='1' fill='%230b2545' fill-opacity='.3'/%3E%3C/svg%3E");
  --mps-texture-2: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M-2 2 2 -2M0 8 8 0M6 10 10 6' stroke='%23ffffff' stroke-opacity='.3' stroke-width='1.3'/%3E%3C/svg%3E");
  --mps-texture-3: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Crect width='8' height='8' fill='%23ffffff' fill-opacity='.08'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------------------
   3. Reset et couche document
   --------------------------------------------------------------------------- */

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

/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-05). Onze
   fieldset de la page portaient la bordure par defaut du navigateur, mesuree
   a 2px groove #efefef, avec la legende qui entaille le bord haut : les dix
   groupes minimum et maximum du profil d'effort et la bascule de vue de la
   silhouette. Ils n'ont pas de classe, donc aucune regle ne les remettait a
   zero, et le panneau « Plus de filtres » se lisait comme un formulaire non
   style pose sous des groupes qui, eux, n'ont aucune boite. Le reset est
   ecrit ici une fois, pour l'element, et chaque composant n'ajoute ensuite
   que ce dont il a besoin. */
fieldset {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

/* AD-8 : un seul porteur de visibilite, l'attribut hidden, declare UNE fois.
   Le !important est portant : sans lui, un conteneur en display grid ou flex
   bat la regle du navigateur et toute une classe de noeuds cesse de se cacher.
   .is-hidden n'existe pas (R-12). */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  /* La moitie de SC 2.4.11 que scroll-margin-top ne couvre pas : le focus
     sequentiel au Tab. Les deux ensemble seulement suffisent. */
  scroll-padding-top: calc(var(--mps-header-h) + var(--mps-space-4));
}

body {
  margin: 0;
  background-color: var(--mps-page-bg);
  color: var(--mps-ink-body);
  font-family: var(--mps-font-family);
  font-size: var(--mps-body-size);
  font-weight: var(--mps-body-weight);
  line-height: var(--mps-body-lh);
  -webkit-text-size-adjust: 100%;
}

::selection {
  background: var(--mps-select-bg);
  color: var(--mps-select-ink);
}

.ml-footer ::selection {
  background: var(--mps-select-ink);
  color: var(--mps-select-bg);
}

/* Ancrage sous la barre fixe. La paire est obligatoire (UX-DR7, NFR-2). */
[id] { scroll-margin-top: calc(var(--mps-header-h) + var(--mps-space-4)); }

h1, h2, h3, h4, h5, h6 {
  margin-top: 0;
  font-family: inherit;
  color: var(--mps-ink-heading);
  text-transform: none;
}

h1 {
  font-size: var(--mps-h1-size);
  font-weight: var(--mps-h1-weight);
  line-height: var(--mps-h1-lh);
  margin: 0 0 var(--mps-space-5);
}
h1 b, h1 strong { font-weight: var(--mps-h1-strong-weight); }

h2 {
  font-size: var(--mps-h2-size);
  font-weight: var(--mps-h2-weight);
  line-height: var(--mps-h2-lh);
  margin: 0 0 var(--mps-space-5);
}
h2 b, h2 strong { font-weight: var(--mps-h2-strong-weight); }

h3 {
  font-size: var(--mps-h3-size);
  font-weight: var(--mps-h3-weight);
  line-height: var(--mps-h3-lh);
  margin: 0 0 var(--mps-space-4);
}
h3 b, h3 strong { font-weight: var(--mps-h3-strong-weight); }

h4 {
  font-size: var(--mps-h4-size);
  font-weight: var(--mps-h4-weight);
  line-height: var(--mps-h4-lh);
  margin: 0 0 var(--mps-space-3);
}

p { margin: 0 0 var(--mps-space-4); }

/* Les marqueurs de liste sont conserves la ou la liste est semantique, ce qui
   est une deviation voulue par rapport au reset du site (NFR-5). */
main ul, main ol { padding-left: var(--mps-space-5); }

a {
  color: var(--mps-ink-muted);
  transition: color var(--mps-ease), border-color var(--mps-ease),
              background-color var(--mps-ease);
}
a:hover, a:focus { color: var(--mps-ink-heading); }

/* REPARATION story 3.2 (R-6), 2026-09-01 : .mps-musclelist__btn rejoint la
   chaine :not(). La liste parallele des 30 groupes est emise en LIENS et non en
   boutons, parce qu'un bouton sans script serait l'affordance morte que AD-27 et
   le dernier critere de la story 3.2 interdisent, et parce que
   copy.json nojs.regions.silhouette promet que cette liste mene aux memes
   fiches. Un lien y prenait donc l'encre accent et le souligne des liens de
   prose PAR-DESSUS la pastille, ce qui n'est pas la pastille que DESIGN.md
   decrit pour ce composant. La regle du composant reprend la main. */
main a:not(.btn):not(.mps-sportcard__link):not(.ml-logo):not(.mps-musclelist__btn) {
  color: var(--mps-accent-text);
  text-decoration: underline;
}
main a:not(.btn):not(.mps-sportcard__link):not(.ml-logo):not(.mps-musclelist__btn):hover,
main a:not(.btn):not(.mps-sportcard__link):not(.ml-logo):not(.mps-musclelist__btn):focus {
  color: var(--mps-accent-hover);
}

/* Anneau de focus visible, obligatoire ici alors que le site pose
   outline: none !important partout. Force pour qu'aucune regle ne le retire. */
:focus-visible {
  outline: var(--mps-focus-w) solid var(--mps-focus-ring) !important;
  outline-offset: var(--mps-focus-offset) !important;
}

img, svg { max-width: 100%; }
img { height: auto; }

table { border-collapse: collapse; }

/* Chiffres tabulaires partout : la colonne 0 a 3 d'un tableau de 30 lignes
   doit s'aligner. */
table, .mps-swatch, .mps-zone__num, .mps-profile__value, .mps-count {
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   4. Coque de site, espace de noms ml-
   --------------------------------------------------------------------------- */

.ml-skiplink {
  position: absolute;
  left: var(--mps-space-2);
  top: calc(var(--mps-space-2) * -10);
  z-index: 10000;
  padding: var(--mps-space-3);
  background: var(--mps-surface-dark);
  color: var(--mps-ink-inverse);
  font-size: var(--mps-btn-size);
  font-weight: var(--mps-btn-weight);
  letter-spacing: var(--mps-btn-tracking);
  text-transform: uppercase;
  text-decoration: none;
  transition: top var(--mps-ease);
}
.ml-skiplink:focus { top: var(--mps-space-2); }

.ml-header {
  height: var(--mps-header-h);
  background: var(--mps-surface);
}

.ml-bar {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  height: var(--mps-header-h);
  background: var(--mps-surface);
  box-shadow: var(--mps-elev-bar);
}

.ml-container {
  width: 100%;
  max-width: var(--mps-container);
  margin: 0 auto;
  padding: 0 var(--mps-side-pad);
}

.ml-bar .ml-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--mps-space-3);
}

/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-06). Le gabarit
   posait un style en ligne max-height: 80px sur l'image, donc sous 768px, ou la
   barre ne fait que 56px, le logo courait de y=-12 a y=68 : le haut du
   logotype etait coupe hors de la fenetre et le bas depassait la barre en
   croisant son ombre, ce qui cassait visiblement le filet du bandeau sur tous
   les telephones. Le style en ligne est retire du gabarit et la taille se lit
   ici, sur le jeton vivant de la barre, moins une respiration : 44px sous
   768px et 68px au-dessus. */
.ml-logo { display: inline-flex; align-items: center; }
.ml-logo img {
  max-height: calc(var(--mps-header-h) - var(--mps-space-3));
  width: auto;
}

.ml-nav { display: flex; align-items: center; }
.ml-nav__link {
  display: inline-block;
  padding: var(--mps-nav-pad);
  color: var(--mps-ink-body);
  font-size: var(--mps-btn-size);
  font-weight: var(--mps-btn-weight);
  line-height: var(--mps-btn-lh);
  letter-spacing: var(--mps-btn-tracking);
  text-transform: uppercase;
  text-decoration: none;
}
.ml-nav__link:hover,
.ml-nav__link:focus,
.ml-nav__link[aria-current] { color: var(--mps-ink-heading); }

/* La barre accent sous un titre de section, signature .custom-separator. */
.ml-separator {
  width: var(--mps-separator-w);
  height: var(--mps-separator-h);
  margin: var(--mps-space-4) 0 var(--mps-space-5);
  border-radius: var(--mps-radius-lg);
  background: var(--mps-accent);
}

.ml-visually-hidden {
  position: absolute;
  width: var(--mps-hairline-w);
  height: var(--mps-hairline-w);
  margin: calc(var(--mps-hairline-w) * -1);
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Boutons : les trois variantes, toutes carrees (grammaire des formes). */
.btn {
  display: inline-block;
  margin: 0;
  padding: var(--mps-btn-pad);
  border: var(--mps-border-w) solid var(--mps-border-control);
  border-radius: var(--mps-radius-none);
  background: none;
  color: var(--mps-ink-heading);
  font-family: inherit;
  font-size: var(--mps-btn-size);
  font-weight: var(--mps-btn-weight);
  line-height: var(--mps-btn-lh);
  letter-spacing: var(--mps-btn-tracking);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--mps-ease), border-color var(--mps-ease),
              background-color var(--mps-ease);
}
.btn:hover, .btn:focus {
  color: var(--mps-ink-heading);
  border-color: var(--mps-ink-heading);
}
.btn-active {
  background-color: var(--mps-surface-dark);
  border-color: var(--mps-surface-dark);
  color: var(--mps-ink-inverse);
  padding: var(--mps-btn-pad-page);
}
.btn-active:hover, .btn-active:focus { color: var(--mps-ink-inverse); }
.btn-sm { padding: var(--mps-btn-pad-sm); }

.ml-footer {
  margin-top: var(--mps-section-gap);
  padding: var(--mps-footer-pad);
  background: var(--mps-surface-dark);
  color: var(--mps-ink-on-dark);
}
.ml-footer h4 { color: var(--mps-ink-inverse); }
.ml-footer a {
  color: var(--mps-ink-on-dark);
  text-decoration: underline;
}
.ml-footer a:hover, .ml-footer a:focus { color: var(--mps-ink-inverse); }
.ml-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mps-space-4);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--mps-footer-link-size);
}
.ml-legal {
  margin: var(--mps-legal-gap) 0 0;
  /* Revue de code du 01/09/2026 (VQA-18) : la signature et la liste de liens
     sont alignees a gauche, la ligne legale etait centree, donc trois blocs du
     meme bloc ne partageaient pas la meme grille. */
  text-align: start;
  font-size: var(--mps-footer-link-size);
}

/* ---------------------------------------------------------------------------
   5. La rampe de niveaux, seul endroit ou la couleur est une donnee
   Trois porteurs, toujours les trois : le chiffre (texte reel, obligatoire),
   le remplissage, la texture (troisieme porteur redondant, jamais le second).
   L'attribut data-level pilote tout, ce qui donne un seul jeu ferme de
   selecteurs quel que soit le mode d'agregation (AD-12, AD-13).
   --------------------------------------------------------------------------- */

[data-level="0"] {
  --mps-lvl-fill: var(--mps-level-0-fill);
  --mps-lvl-ink: var(--mps-level-0-ink);
  --mps-lvl-texture: var(--mps-texture-0);
}
[data-level="1"] {
  --mps-lvl-fill: var(--mps-level-1-fill);
  --mps-lvl-ink: var(--mps-level-1-ink);
  --mps-lvl-texture: var(--mps-texture-1);
}
[data-level="2"] {
  --mps-lvl-fill: var(--mps-level-2-fill);
  --mps-lvl-ink: var(--mps-level-2-ink);
  --mps-lvl-texture: var(--mps-texture-2);
}
[data-level="3"] {
  --mps-lvl-fill: var(--mps-level-3-fill);
  --mps-lvl-ink: var(--mps-level-3-ink);
  --mps-lvl-texture: var(--mps-texture-3);
}

/* Les memes valeurs par les modificateurs de l'inventaire, pour le cas ou un
   gabarit pose la classe sans l'attribut. */
.mps-swatch--lvl0, .mps-zone--lvl0, .mps-table__cell--lvl0 { --mps-lvl-fill: var(--mps-level-0-fill); --mps-lvl-ink: var(--mps-level-0-ink); --mps-lvl-texture: var(--mps-texture-0); }
.mps-swatch--lvl1, .mps-zone--lvl1, .mps-table__cell--lvl1 { --mps-lvl-fill: var(--mps-level-1-fill); --mps-lvl-ink: var(--mps-level-1-ink); --mps-lvl-texture: var(--mps-texture-1); }
.mps-swatch--lvl2, .mps-zone--lvl2, .mps-table__cell--lvl2 { --mps-lvl-fill: var(--mps-level-2-fill); --mps-lvl-ink: var(--mps-level-2-ink); --mps-lvl-texture: var(--mps-texture-2); }
.mps-swatch--lvl3, .mps-zone--lvl3, .mps-table__cell--lvl3 { --mps-lvl-fill: var(--mps-level-3-fill); --mps-lvl-ink: var(--mps-level-3-ink); --mps-lvl-texture: var(--mps-texture-3); }

.mps-swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--mps-swatch-size);
  height: var(--mps-swatch-size);
  border: var(--mps-hairline-w) solid var(--mps-zone-stroke);
  border-radius: var(--mps-radius-sm);
  background-color: var(--mps-lvl-fill);
  background-image: var(--mps-lvl-texture);
  color: var(--mps-lvl-ink);
  font-size: var(--mps-numeral-size);
  font-weight: var(--mps-numeral-weight);
  line-height: var(--mps-numeral-lh);
}

/* Le marquage muscle profond est un CONTOUR, jamais un remplissage, pour qu'il
   survive au mode couleurs forcees. */
.mps-swatch--deep { border-style: dashed; border-width: var(--mps-zone-stroke-deep-w); }

.mps-table .mps-swatch {
  width: var(--mps-swatch-size-table);
  height: var(--mps-swatch-size-table);
}

/* ---------------------------------------------------------------------------
   6. Mise en page
   Mobile first. Trois media queries seulement : 640px (une declaration),
   768px et 992px (les deux seuls points de rupture de mise en page).
   --------------------------------------------------------------------------- */

main { display: block; }

main > section,
.mps-section { margin-bottom: var(--mps-section-gap); }

.mps-breadcrumb {
  margin: var(--mps-space-5) 0 var(--mps-space-4);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
  color: var(--mps-ink-muted);
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-20). Les deux
   entrees n'etaient separees que par une espace, donc le fil se lisait comme
   un fragment de phrase, « Accueil Muscles par sport », et non comme un
   chemin. Un ::before genere n'entre pas dans le nom accessible du lien, la
   lecture d'ecran est donc inchangee. */
.mps-breadcrumb__item { display: inline; }
.mps-breadcrumb__item + .mps-breadcrumb__item::before {
  content: "\203A";
  margin: 0 var(--mps-space-2);
  color: var(--mps-ink-muted);
}
.mps-breadcrumb__current { color: var(--mps-ink-heading); }

/* Intro et composition illustree d'ouverture (C3, UX-DR41).
   Sur petit ecran le hero se rendait APRES un h1 sur deux lignes et un
   paragraphe d'ouverture de dix lignes, donc a y=856 pour un pli a 844 : le
   premier ecran d'un telephone etait entierement textuel, ce que la correction
   C3 existe precisement pour empecher (revue de code du 01/09/2026, VQA-06).
   L'ordre du DOM ne bouge pas, le h1 et sa premiere phrase restent la premiere
   chose qu'un robot et un lecteur d'ecran rencontrent, seul l'ordre de PEINTURE
   change, et seulement en dessous de 992px. */
.mps-intro { display: grid; gap: var(--mps-space-5); }
@media (max-width: 991px) {
  .mps-hero { order: -1; }
}
.mps-intro__prose { max-width: var(--mps-prose-max); }
.mps-intro__lead {
  font-size: var(--mps-lead-size);
  font-weight: var(--mps-lead-weight);
  line-height: var(--mps-lead-lh);
  max-width: var(--mps-prose-max);
}
.mps-hero {
  width: var(--mps-hero-sm);
  max-width: 100%;
  margin-inline: auto;
}
.mps-hero svg { display: block; width: 100%; height: auto; }

.mps-byline {
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
  color: var(--mps-ink-muted);
}
.mps-byline__date { white-space: nowrap; }

/* La grille de l'outil. Un seul panneau silhouette dans le document, et il vit
   ici (R-36). Voir le bloc de commentaire "R-36" plus bas. */
.mps-workspace {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--mps-space-5);
  align-items: start;
}
.mps-workspace__map { min-width: 0; }
.mps-workspace__results { min-width: 0; }

.mps-results {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--mps-space-5);
}

/* ---------------------------------------------------------------------------
   R-36, ecrit ici parce que c'est une regle de geometrie et non un composant.

   Il y a EXACTEMENT UN panneau .mps-bodymap dans le document. Il vit dans
   .mps-workspace__map, a l'interieur de #outil. La "carte fusionnee" de
   l'anatomie de comparaison EST ce panneau, bascule par data-mode
   (couverture ou charge) sur les deux racines <svg>. Ce fichier ne le deplace
   jamais, ne le clone jamais et n'en peint jamais un second.

   Consequence greppable : aucun selecteur de ce fichier ne vise une carte a
   l'interieur de la comparaison. #comparaison .mps-zone,
   #comparaison .mps-bodymap, #comparaison .mps-bodymap__views et tout
   selecteur visant un svg dont l'id commence par mps-map a l'interieur de
   #comparaison n'existent pas ici, parce qu'aucun tel element n'existe dans
   le document (AD-12 et AR-12 n'admettent que deux racines <svg>).

   .mps-compare__map ne recoit AUCUNE regle : ni dimension, ni fond, ni
   bordure. C'est un emplacement de mise en page vide, le balisage peut
   l'omettre entierement sans changer une seule regle de ce fichier.

   L'etat "en comparaison" n'ajoute donc AUCUNE septieme classe d'etat.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   7. Composants, espace de noms mps-
   --------------------------------------------------------------------------- */

/* --- 7.0 L'atome pictogramme -------------------------------------------------
   DEPLACE ICI par la story 3.2 (reparation R-6, 2026-09-01). Cette regle vivait
   apres tous les composants, et chaque pictogramme porte DEUX classes : la
   sienne, ecrite par la story 2.2 sur la racine du SVG, et celle du composant
   qui l'accueille. A specificite egale, la derniere ecrite gagne, donc
   .mps-pictogram battait .mps-chip__icon (16px devenait 24px) et surtout
   .mps-chip__check, dont le display: none est ce qui fait que le glyphe de
   coche n'apparait QU'A l'etat presse : toutes les pastilles s'affichaient
   cochees. Remontee en tete des composants, la regle redevient la valeur par
   defaut que chaque composant surcharge, ce qui est ce que DESIGN.md decrit
   (16px dans une pastille, 20px a cote d'un h3, 24px dans la legende) et ce qui
   evite le meme piege aux stories 3.3 et 3.4. Aucune declaration n'a change. */
.mps-pictogram {
  display: inline-block;
  width: var(--mps-icon-lg);
  height: var(--mps-icon-lg);
  vertical-align: middle;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--mps-picto-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Les quatre emplacements sanctionnes du pictogramme de categorie (R-22,
   UX-DR28) : 16px dans une pastille de filtre, 16px en tete de carte, 16px
   dans une pastille du bac de comparaison et 20px a cote du h3 d'une fiche de
   sport. Les trois premiers portaient leur regle, le quatrieme retombait sur
   le defaut 24px de .mps-pictogram (revue de code du 01/09/2026, BH-14). */
.mps-sheet__icon {
  width: var(--mps-icon-md);
  height: var(--mps-icon-md);
  flex: none;
}

/* --- 7.1 Barre outil, recherche, filtres -------------------------------- */

/* La barre d'outils N'EST PAS collante (revue de code du 01/09/2026, VQA-04).
   Elle et .mps-workspace__map se collaient toutes les deux a
   top: var(--mps-header-h) a l'interieur de #outil, la barre portant en plus
   z-index: 30, donc elle peignait par-dessus la silhouette des que le visiteur
   faisait defiler les resultats : 397px des 666px de la silhouette couverts a
   1280x800, 334px a 1024x768. Le panneau qui merite de rester en vue est la
   carte, pas un panneau de filtres haut de 756px, soit 95% d'une fenetre de
   800px. La classe .mps-filters.is-stuck reste declaree, elle sert a la story
   5.2 si un jour la barre redevient collante avec un decalage calcule. */
.mps-toolbar {
  margin-bottom: var(--mps-space-5);
}

.mps-filters {
  padding: var(--mps-space-5);
  border-radius: var(--mps-radius-lg);
  background: var(--mps-surface);
  box-shadow: var(--mps-elev-card);
  transition: box-shadow var(--mps-ease);
}
.mps-filters.is-stuck { box-shadow: var(--mps-elev-toolbar-stuck); }
.mps-filters__group {
  margin: 0 0 var(--mps-space-5);
  padding: 0;
  border: 0;
}
.mps-filters__group:last-child { margin-bottom: 0; }
.mps-filters__legend {
  padding: 0;
  margin-bottom: var(--mps-space-2);
  color: var(--mps-ink-heading);
  font-size: var(--mps-label-caps-size);
  font-weight: var(--mps-label-caps-weight);
  line-height: var(--mps-label-caps-lh);
  letter-spacing: var(--mps-label-caps-tracking);
  text-transform: uppercase;
}
.mps-search__hint { grid-column: 1 / -1; grid-row: 3; }
.mps-filters__note,
.mps-search__hint {
  margin: var(--mps-space-2) 0 0;
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}
.mps-filters__advanced { margin-top: var(--mps-space-4); }
/* DECISION D36 DU 01/09/2026 (correction C3, findings BH-A7, AA-AUD-02 et
   VQA-08). LES DEUX LIENS D'EVITEMENT DEVIENNENT DES LIENS D'EVITEMENT.

   UX-DR33 et FR-14 demandent les deux liens et ne disent rien de leur
   visibilite permanente. Ils etaient rendus en permanence, ce qui est la seule
   variante du motif que personne n'emploie, et ils coutaient 29px au-dessus du
   champ de recherche sur un pli de 800px que la correction C3 protege. Ils
   prennent donc le motif standard, celui que .ml-skiplink de ce meme fichier
   applique deja au lien d'evitement du site : hors de l'ecran au repos, visible
   des qu'ils recoivent le focus.

   L'ORDRE DU DOM NE BOUGE PAS. Les deux liens restent exactement ou la story
   3.2 les emet, arrets de focus 6 et 14, ils restent atteignables au clavier et
   ils restent dans l'arbre d'accessibilite, ce qui est tout ce que le critere
   demande. Seule la peinture change.

   Au focus le lien reprend sa place dans le flux et non une position absolue :
   une position absolue le ferait recouvrir l'etiquette du champ ou le haut du
   panneau de la silhouette. Le deplacement de 29px qui suit est provoque par
   une frappe au clavier, donc il tombe dans la fenetre d'exclusion de 500ms du
   CLS et ne compte pas comme un decalage inattendu. Mesure apres : CLS a
   0.00025 sur un chargement ordinaire, inchange. */
.mps-filters__bypass,
.mps-bodymap__bypass {
  position: absolute;
  width: var(--mps-hairline-w);
  height: var(--mps-hairline-w);
  margin: calc(var(--mps-hairline-w) * -1);
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  font-size: var(--mps-caption-size);
  color: var(--mps-accent-text);
}
/* Au focus, le lien reprend EXACTEMENT l'apparence qu'il avait quand il etait
   rendu en permanence : encre accent, souligne, sur le fond de la page. Aucune
   nouvelle paire de couleurs n'est introduite. Une premiere version peignait un
   cartouche sombre, comme .ml-skiplink, et elle a ete mesuree puis retiree :
   la regle
   main a:not(.btn):not(.mps-sportcard__link):not(.ml-logo):not(.mps-musclelist__btn):focus
   est plus specifique et impose --mps-accent-hover, ce qui donnait #004a99 sur
   #1c1c1c, soit 1.4:1.
   CHIFFRE CORRIGE LE 02/09/2026, passe de cloture. Ce commentaire annoncait
   8.59:1, qui est le contraste de --mps-accent-hover (#004a99) sur blanc, une
   encre que la regle :focus n'atteint jamais ici. Mesure au clavier sur la
   page servie, les deux liens au focus peignent --mps-accent-text (#0062cc)
   sur le blanc de la page, soit 5.80:1, avec un contour de 3 px de la meme
   encre. Le plancher de 4.5:1 est franchi, la valeur annoncee ne l'etait pas. */
.mps-filters__bypass:focus,
.mps-bodymap__bypass:focus {
  position: static;
  display: inline-block;
  width: auto;
  height: auto;
  margin: 0 0 var(--mps-space-2);
  padding: 0;
  overflow: visible;
  clip-path: none;
}

/* Le champ, son pictogramme et son bouton d'effacement se superposent sans
   conteneur supplementaire : trois colonnes, le champ occupe toute la ligne 2
   et les deux ornements occupent la meme ligne dans les colonnes 1 et 3. Le
   placement est donc independant de l'ordre du balisage, ce qui compte parce
   que ce fichier ne possede pas le gabarit qui l'emet (story 3.2). */
.mps-search {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  margin-bottom: var(--mps-space-5);
}
.mps-search__label {
  grid-column: 1 / -1;
  grid-row: 1;
  display: block;
  margin-bottom: var(--mps-space-2);
  color: var(--mps-ink-heading);
  font-size: var(--mps-ui-size);
  font-weight: var(--mps-ui-weight);
  line-height: var(--mps-ui-lh);
}
.mps-search__field {
  grid-column: 1 / -1;
  grid-row: 2;
  display: block;
  width: 100%;
  height: var(--mps-field-h);
  padding: var(--mps-field-pad);
  border: var(--mps-border-w) solid var(--mps-border-field);
  border-radius: var(--mps-radius-none);
  background: var(--mps-surface);
  color: var(--mps-ink-heading);
  font-family: inherit;
  font-size: var(--mps-ui-size);
  line-height: var(--mps-ui-lh);
  transition: border-color var(--mps-ease), border-width var(--mps-ease);
}
/* Le site eclaircit la bordure au focus, ce qui se lit comme moins de focus.
   Ici elle fonce et epaissit. */
.mps-search__field:focus {
  border-width: var(--mps-focus-w);
  border-color: var(--mps-focus-ring);
}
.mps-search__icon {
  grid-column: 1;
  grid-row: 2;
  z-index: 2;
  justify-self: start;
  margin-inline-start: var(--mps-space-3);
  width: var(--mps-icon-md);
  height: var(--mps-icon-md);
  color: var(--mps-ink-muted);
  pointer-events: none;
}
.mps-search__clear {
  grid-column: 3;
  grid-row: 2;
  z-index: 2;
  justify-self: end;
  margin-inline-end: var(--mps-space-2);
  min-width: var(--mps-tap-min);
  min-height: var(--mps-tap-min);
  padding: var(--mps-space-1);
  border: var(--mps-border-w) solid transparent;
  border-radius: var(--mps-radius-none);
  background: none;
  color: var(--mps-ink-heading);
  cursor: pointer;
}

/* --- 7.2 Pastille de filtre (toggle, donc pilule) ------------------------ */

/* Le jeu de pastilles d'un groupe de filtre. Il existe parce que le bouton de
   divulgation des categories doit pointer aria-controls sur ce qu'il deplie et
   non sur le fieldset qui le contient lui-meme (revue de code du 01/09/2026,
   BH-04), et il donne au passage aux pastilles la gouttiere qu'elles n'avaient
   pas : le generateur les concatene sans espace. */
.mps-chipset {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mps-space-2);
}
.mps-chip { display: inline-flex; }
.mps-chip__input {
  position: absolute;
  width: var(--mps-hairline-w);
  height: var(--mps-hairline-w);
  margin: calc(var(--mps-hairline-w) * -1);
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.mps-chip__label {
  display: inline-flex;
  align-items: center;
  gap: var(--mps-space-2);
  min-height: var(--mps-chip-min-h);
  padding: var(--mps-chip-pad);
  border: var(--mps-border-w) solid var(--mps-border-control);
  border-radius: var(--mps-radius-pill);
  background: var(--mps-surface);
  color: var(--mps-ink-heading);
  font-size: var(--mps-ui-size);
  font-weight: var(--mps-ui-weight);
  line-height: var(--mps-ui-lh);
  overflow-wrap: anywhere;
  cursor: pointer;
  transition: color var(--mps-ease), border-color var(--mps-ease),
              background-color var(--mps-ease);
}
.mps-chip__label:hover { border-color: var(--mps-ink-heading); }
.mps-chip__icon {
  width: var(--mps-icon-sm);
  height: var(--mps-icon-sm);
  flex: none;
}
/* Le glyphe de coche n'apparait qu'a l'etat presse. L'opacite n'est JAMAIS un
   porteur d'etat, c'est le defaut des pastilles de /IG/ que cette page existe
   pour ne pas reproduire. */
.mps-chip__check {
  display: none;
  width: var(--mps-check-size);
  height: var(--mps-check-size);
  flex: none;
}
.mps-chip__input:checked + .mps-chip__label {
  border-color: var(--mps-accent-text);
  background: var(--mps-accent-soft);
  color: var(--mps-accent-hover);
}
.mps-chip__input:checked + .mps-chip__label:hover { border-color: var(--mps-accent-hover); }
.mps-chip__input:checked + .mps-chip__label .mps-chip__check { display: inline-block; }
.mps-chip__input:focus-visible + .mps-chip__label {
  outline: var(--mps-focus-w) solid var(--mps-focus-ring) !important;
  outline-offset: var(--mps-focus-offset) !important;
}

/* --- 7.3 Segment (radiogroup rendu en pilule jointe) --------------------- */

.mps-segment {
  display: inline-flex;
  flex-wrap: wrap;
  border: var(--mps-border-w) solid var(--mps-border-control);
  border-radius: var(--mps-radius-pill);
  overflow: hidden;
}
.mps-segment__option { display: inline-flex; }
/* Sous 480px les options passent a la ligne DANS une pilule entierement
   arrondie : le remplissage noir de l'option choisie gardait ses angles droits
   a l'interieur du conteneur arrondi et la moitie droite de la pilule restait
   vide (revue de code du 01/09/2026, VQA-05). Mesure a 390px : "Mode de calcul"
   sur deux lignes avec 110px de conteneur vide a droite. Le segment devient
   alors une pile verticale assumee, a angles droits, plutot qu'une pilule
   cassee.

   QUATRIEME POINT DE RUPTURE, NON ENREGISTRE (revue de code du 31/08/2026,
   AA-AUD-04). Cette borne a 480px est un quatrieme point de rupture, et du
   type meme que l'exception accordee a 640px a ete accordee pour NE PAS etre :
   elle change display, flex-direction et le rayon, donc elle deplace et
   redimensionne. Elle n'est PAS repliee sur la borne de 640px, parce que le
   segment ne passe pas a la ligne entre 480 et 640px et qu'une pile verticale
   pleine largeur y serait une regression. Son enregistrement dans DESIGN.md,
   dans EXPERIENCE.md, dans NFR-10 et dans le critere de la story 3.1 sort de
   la liste de fichiers de cette revue et est consigne dans deferred-work.md.
   La moitie reparable ici l'est : le rayon droit est celui d'une pile, pas
   celui d'un avis, donc radius-none et non radius-md, pour qu'un controle
   n'emprunte pas la forme d'un autre role (UX-DR4). */
@media (max-width: 480px) {
  .mps-segment {
    display: flex;
    flex-direction: column;
    border-radius: var(--mps-radius-none);
  }
  .mps-segment__option { width: 100%; }
  .mps-segment__label { width: 100%; }
}
.mps-segment__input {
  position: absolute;
  width: var(--mps-hairline-w);
  height: var(--mps-hairline-w);
  margin: calc(var(--mps-hairline-w) * -1);
  overflow: hidden;
  clip-path: inset(50%);
  border: 0;
}
.mps-segment__label {
  display: inline-flex;
  align-items: center;
  min-height: var(--mps-chip-min-h);
  padding: var(--mps-segment-pad);
  background: transparent;
  color: var(--mps-ink-heading);
  font-size: var(--mps-ui-size);
  font-weight: var(--mps-ui-weight);
  line-height: var(--mps-ui-lh);
  cursor: pointer;
  transition: color var(--mps-ease), background-color var(--mps-ease);
}
.mps-segment__input:checked + .mps-segment__label {
  background: var(--mps-surface-dark);
  color: var(--mps-ink-inverse);
}
.mps-segment__input:focus-visible + .mps-segment__label {
  outline: var(--mps-focus-w) solid var(--mps-focus-ring) !important;
  outline-offset: calc(var(--mps-focus-offset) * -1) !important;
}
/* REPARATION story 6.2 (R-6), 2026-09-01. Le decalage negatif ci-dessus peint
   l'anneau A L'INTERIEUR de l'etiquette, pour qu'il ne deborde pas sur la
   pilule voisine du segment joint. Sur l'option CHOISIE, la regle juste
   au-dessus a fait passer le fond a --mps-surface-dark, donc les DEUX bords de
   l'anneau ont #1c1c1c pour voisin et --mps-focus-ring n'y vaut plus que
   2.937:1, sous le plancher de 3:1 que WCAG 1.4.11 fixe pour un composant
   d'interface et que la reconciliation R-25 demande a cette story d'assurer.
   Mesure au clavier sur beta, sur les trois segments qui portent un etat
   choisi : le seuil de niveau, la bascule de vue de la silhouette et le mode
   de la comparaison. L'encre inverse vaut 17.04:1 sur ce meme fond. */
.mps-segment__input:checked:focus-visible + .mps-segment__label {
  outline-color: var(--mps-ink-inverse) !important;
}
/* Inerte : le groupe reste focusable, il perd seulement sa contribution. */
.mps-segment--inert,
.mps-segment.is-inert {
  background: var(--mps-surface-sunken);
}
/* Meme correction qu'a la cellule de niveau 0 plus bas, portee ici par la revue
   de code du 31/08/2026 (BH-05) : --mps-ink-muted sur --mps-surface-sunken ne
   vaut que 4.267:1, sous le plancher de 4.5:1 que le critere de la story 3.1 et
   la correction C5 exigent tous les deux. --mps-level-0-ink vaut 7.774:1 sur
   cette meme surface. L'etat n'est pas du CSS mort : le seuil de niveau reste
   inerte tant qu'aucune region n'est choisie, c'est l'etat par defaut de la
   barre d'outils au premier chargement. */
.mps-segment--inert .mps-segment__label,
.mps-segment.is-inert .mps-segment__label { color: var(--mps-level-0-ink); }
/* Revue de code du 31/08/2026 (BH-01) : la correction ci-dessus reglait le cas
   non coche sur la surface creusee et en creait un autre sur l'option COCHEE.
   Les deux selecteurs ont la meme specificite (0,3,0), le plus tardif gagnait
   donc sur color pendant que la regle :checked gardait son fond sombre, soit
   --mps-level-0-ink sur --mps-surface-dark, 2.06:1. Et c'est l'etat par defaut
   de la barre d'outils : aucune region n'est choisie au premier chargement,
   donc "Tous" s'affichait illisible sur la pilule noire. L'encre inverse est
   retablie pour la seule option cochee. */
.mps-segment--inert .mps-segment__input:checked + .mps-segment__label,
.mps-segment.is-inert .mps-segment__input:checked + .mps-segment__label {
  color: var(--mps-ink-inverse);
}

/* --- 7.4 Axe de profil a deux extremites -------------------------------- */

.mps-range { margin-bottom: var(--mps-space-4); }
.mps-range__axis {
  display: block;
  margin-bottom: var(--mps-space-2);
  color: var(--mps-ink-heading);
  font-size: var(--mps-ui-size);
  font-weight: var(--mps-ui-weight);
}
.mps-range__min, .mps-range__max { margin-right: var(--mps-space-2); }

/* --- 7.5 Filtres actifs, compteur, confirmations ------------------------ */

.mps-activefilters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mps-space-2);
  margin: var(--mps-space-4) 0;
  list-style: none;
  padding: 0;
}
.mps-activefilters__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--mps-space-2);
  min-height: var(--mps-chip-min-h);
  padding: var(--mps-chip-pad);
  border: var(--mps-border-w) solid var(--mps-accent-text);
  border-radius: var(--mps-radius-pill);
  background: var(--mps-accent-soft);
  color: var(--mps-accent-hover);
  font-size: var(--mps-ui-size);
  overflow-wrap: anywhere;
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-23). La page
   portait deux marques pour la meme action « retirer ceci » : un signe
   multiplication en texte ici, fin et petit, et le glyphe ui-close trace a
   1.5px dans le bac de comparaison, nettement plus grand. Les deux vivent dans
   une pilule bleue de meme hauteur et veulent dire exactement la meme chose,
   donc l'ecart se lisait comme un oubli. Le glyphe de texte est cale sur la
   meme taille que l'icone tracee, ce qui aligne les deux marques sans
   construire de SVG au fil de l'execution. */
.mps-activefilters__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--mps-tap-min);
  min-height: var(--mps-tap-min);
  padding: 0;
  border: var(--mps-border-w) solid transparent;
  border-radius: var(--mps-radius-none);
  background: none;
  color: inherit;
  font-size: var(--mps-icon-md);
  line-height: 1;
  cursor: pointer;
}

.mps-count,
.mps-announce {
  display: block;
  min-height: var(--mps-live-min-h);
  margin: var(--mps-space-4) 0;
  color: var(--mps-ink-heading);
  font-size: var(--mps-ui-size);
  font-weight: var(--mps-ui-weight);
  line-height: var(--mps-ui-lh);
}
/* Revue de code du 31/08/2026 (EC-14, F9) : #mps-confirm est desormais revele
   VIDE des js-ready, pour qu'une region aria-live existe dans l'arbre
   d'accessibilite avant sa premiere ecriture. Un lecteur d'ecran n'annonce pas
   de facon fiable une region qui apparait et se remplit dans la meme tache.
   Vide, elle ne doit rien couter a la mise en page : ni hauteur reservee, ni
   marge. Elle reprend l'une et l'autre des qu'elle porte un message. */
.mps-announce:empty {
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-07).
   #mps-confirm est le seul porteur de .mps-announce, et c'est lui qui dit
   qu'un lien partage a ete tronque et quels sports ont ete retires. Il se
   rendait en texte courant de 15px sur blanc, sans fond, sans filet et sans
   marge : rien ne le distinguait d'une phrase ordinaire, alors que la page
   dessine deja la geometrie d'avis dont ce message a besoin, deux elements
   plus loin, sur .mps-alert--limit. La regle ne peut pas fuir sur une annonce
   neutre, la classe n'est portee que par cet element. */
.mps-announce:not(:empty) {
  padding: var(--mps-space-4);
  border-left: var(--mps-notice-border-w) solid var(--mps-alert);
  background: var(--mps-alert-soft);
  color: var(--mps-alert);
}

/* --- 7.6 Etat vide ------------------------------------------------------ */

.mps-empty {
  padding: var(--mps-space-5);
  border-radius: var(--mps-radius-md);
  background: var(--mps-surface-sunken);
}
.mps-empty__title {
  margin: 0 0 var(--mps-space-3);
  font-size: var(--mps-h4-size);
  font-weight: var(--mps-h4-weight);
  line-height: var(--mps-h4-lh);
  color: var(--mps-ink-heading);
}
.mps-empty__term { font-weight: var(--mps-ui-weight); }
.mps-empty__action { margin-top: var(--mps-space-4); }

/* --- 7.7 Panneau silhouette --------------------------------------------- */

/* Le rembourrage lateral CEDE avant la cible tactile (revue de code du
   31/08/2026, AA-02). A 320px la chaine faisait 320 - 2x15 de --mps-side-pad
   - 2x24 ici = 242px de silhouette, soit une unite de viewBox a 0.807px CSS et
   14 des 16 rectangles de 26 unites a 20.97px, sous le plancher de 24 par 24
   que la story 2.1 et WCAG 2.2 SC 2.5.8 exigent tous les deux. La formule rend
   les 24px pleins des que la place existe et les reduit sinon, sans quatrieme
   point de rupture, que NFR-10 n'autorise pas sans le meme traitement ecrit
   que le 640px. La borne est en 100vw et non en pourcentage : un pourcentage se
   resout sur le bloc conteneur, or ce bloc est une piste de grille dont la
   taille depend a son tour du contenu, ce qui creait une boucle de retour et
   retrecissait aussi la silhouette entre 768 et 991px, la ou la place ne manque
   pas. La largeur de la fenetre, elle, ne depend de rien. Mesure apres
   correction : 320px donne 280px de silhouette et 24.27px de cible, 360px et
   au-dessus sont inchanges au pixel pres. */
.mps-bodymap {
  padding: var(--mps-space-5) min(var(--mps-space-5), max(0px, (100vw - 310px) / 2));
  border-radius: var(--mps-radius-lg);
  background: var(--mps-surface);
  /* La carte du corps n'a PAS d'ombre : c'est un schema sur du papier, une
     ombre portee la ferait lire comme une illustration flottante. */
}
.mps-bodymap__views { margin-bottom: var(--mps-space-4); }
.mps-bodymap__view { display: block; }
.mps-bodymap__svg {
  display: block;
  width: 100%;
  max-width: var(--mps-map-max);
  height: auto;
  margin-inline: auto;
}
.mps-bodymap__caption {
  margin: var(--mps-space-3) 0 0;
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}

/* Silhouette : decor, jamais un niveau. */
.mps-bodymap__body,
.mps-bodymap__svg .mps-bodymap__outline {
  fill: var(--mps-zone-body);
  stroke: var(--mps-zone-body-stroke);
  stroke-width: var(--mps-body-stroke-w);
}

/* Zone : remplissage + contour + chiffre. Le script n'ecrit jamais un fill,
   un stroke ou un style, tout est ici, indexe par attribut (AD-12, AR-12). */
.mps-zone { cursor: pointer; }
/* Sans js-ready, la zone n'est PAS une affordance (revue de code du 31/08/2026,
   EC-06). AD-27 demande qu'aucun controle ne reste une affordance morte quand
   le script est bloque, perime derriere le cache de sept jours ou refuse de
   s'initialiser sur un desaccord de schema de charge utile, et le curseur en
   main promettait exactement le contraire a la souris. La silhouette redevient
   l'illustration en lecture seule que AD-12 nomme deja comme son repli, et la
   liste parallele des 30 groupes reste le chemin d'entree. Le role et
   aria-pressed portes par l'oeuvre elle-meme relevent d'une decision
   d'orchestration, consignee dans deferred-work.md. */
:root:not(.js-ready) .mps-zone { cursor: default; pointer-events: none; }
.mps-zone__shape {
  fill: var(--mps-lvl-fill, var(--mps-level-0-fill));
  stroke: var(--mps-zone-stroke);
  stroke-width: var(--mps-zone-stroke-w);
  transition: stroke-width var(--mps-ease);
}
/* Cible tactile invisible d'au moins 24 par 24 px CSS. */
.mps-zone__hit { fill: transparent; stroke: none; }
/* Sans js-ready aucun sport n'est choisi, donc aucune zone ne porte de niveau,
   donc le chiffre n'a rien a porter. Il s'affichait pourtant, 32 fois, au
   dessus d'une legende dont la premiere ligne lit "0 Peu ou pas sollicite" :
   la silhouette affirmait que tous les groupes sont a 0, ce qui est faux et ce
   que la legende voisine contredit en toutes lettres (revue de code du
   01/09/2026, BH-08). La regle "chiffre plus remplissage, jamais la couleur
   seule" (AD-13) porte sur l'etat colore, et l'etat colore n'existe qu'a partir
   de js-ready. */
:root:not(.js-ready) .mps-zone__num { display: none; }
/* REPARATION story 5.4 (R-6), revue de code du 01/09/2026 (VQA-W9-01,
   AA-5.3-08). Avec JavaScript et AUCUN sport choisi, la silhouette imprimait
   un 0 dans ses 32 zones, juste au-dessus d'une legende qui lit en toutes
   lettres « Aucun sport selectionne » et d'une echelle dont la premiere ligne
   lit « 0 Peu ou pas sollicite ». C'est le meme argument que la reparation
   BH-08 juste au-dessus, etendu a l'etat de repos que tout premier visiteur
   rencontre. visibility et non display : le noeud reste dans le DOM dans TOUS
   les etats, ce qu'AD-13 exige, et il est deja aria-hidden donc rien n'est
   perdu pour les technologies d'assistance. La classe est posee et retiree par
   carte.rendre, qui n'ecrit ainsi qu'une classe mps-, sur la liste d'AD-8. */
.mps-bodymap--repos .mps-zone__num { visibility: hidden; }
.mps-zone__num {
  fill: var(--mps-lvl-ink, var(--mps-level-0-ink));
  font-size: var(--mps-zone-num-size);
  font-weight: var(--mps-numeral-weight);
  text-anchor: middle;
  pointer-events: none;
  /* REPARATION story 5.4 (R-6), revue de code du 01/09/2026 (VQA-W9-02). Le
     chiffre etait dessine a meme les contours de zone : dans le quadriceps il
     tombait sur le croisement de deux traits de 1px qui traversaient la panse
     du glyphe, dans les fibulaires il etait coupe en deux par son propre
     contour tirete de 2px. A 1x, les deux se lisaient comme une tache et non
     comme un nombre, alors que ce nombre EST le second porteur obligatoire
     d'AD-13, celui sur lequel repose toute la lecture sans couleur.
     paint-order dessine le halo SOUS le remplissage, donc le glyphe garde
     exactement sa position et sa couleur. Le halo reprend le REMPLISSAGE de la
     zone et non la surface de la page : c'est du contour de zone que le chiffre
     doit se detacher, et sur un niveau 3 un halo blanc aurait ajoute un liseré
     clair autour d'un chiffre deja blanc. */
  paint-order: stroke fill;
  stroke: var(--mps-lvl-fill, var(--mps-level-0-fill));
  stroke-width: var(--mps-zone-num-halo-w);
  stroke-linejoin: round;
}
/* Muscle profond : contour tirete, jamais un remplissage ni un fond hachure,
   pour que le marquage survive au mode couleurs forcees. */
.mps-zone[data-profond] .mps-zone__shape,
.mps-zone--deep .mps-zone__shape {
  stroke-width: var(--mps-zone-stroke-deep-w);
  stroke-dasharray: var(--mps-zone-dash);
}
/* Les etats viennent APRES le muscle profond, sinon ils perdent l'arbitrage a
   specificite egale et un groupe profond n'a plus aucun etat visible
   (AA-5.3-01). Le focus clavier partage la declaration du survol, ce que le
   critere AC-9 demande en toutes lettres (AA-5.3-06, VQA-W9-23). */
.mps-zone:hover .mps-zone__shape,
.mps-zone:focus-visible .mps-zone__shape { stroke-width: var(--mps-zone-stroke-hover-w); }
/* Seule la forme par ATTRIBUT est ajoutee ici : le balisage genere ne porte
   jamais .mps-zone--deep, et la revue de code du 01/09/2026 (BH-11) demande
   plutot de retirer ce jeu de classes mortes que de l'etendre. */
.mps-zone[data-profond]:hover .mps-zone__shape,
.mps-zone[data-profond]:focus-visible .mps-zone__shape { stroke-width: var(--mps-zone-stroke-deep-hover-w); }
/* La selection prend la couleur d'accent en plus de la largeur. Le noir a 3px
   sur les six lobes du quadriceps se lisait comme un gribouillis et non comme
   un etat, et le bleu est deja la langue de la selection partout ailleurs sur
   la page, puces et segments compris (VQA-W9-08). */
.mps-zone.is-selected .mps-zone__shape,
.mps-zone[aria-pressed="true"] .mps-zone__shape {
  stroke: var(--mps-focus-ring);
  stroke-width: var(--mps-zone-stroke-sel-w);
}
.mps-zone[data-profond].is-selected .mps-zone__shape,
.mps-zone[data-profond][aria-pressed="true"] .mps-zone__shape {
  stroke-width: var(--mps-zone-stroke-deep-sel-w);
}
.mps-zone:focus-visible {
  outline: var(--mps-focus-w) solid var(--mps-focus-ring) !important;
  outline-offset: var(--mps-focus-offset) !important;
}
/* Les etiquettes autour de la silhouette n'apparaissent qu'a 992px et plus. */
.mps-bodymap__labels { display: none; }
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-01, BH-A1). Le
   groupe <g class="mps-bodymap__labels"> portait stroke #cccccc et
   stroke-width 1 pour ses lignes de rappel, et les 29 <text> freres heritaient
   ce contour. paint-order vaut normal par defaut, donc le contour se peignait
   PAR-DESSUS le remplissage, et a une taille de glyphe de 8.5 unites il
   couvrait la hampe entiere : l'encre reellement peinte valait #cccccc, soit
   1.61:1 sur blanc, contre les 4.5:1 que la correction C5 exige. Sur un ecran
   1x les mots n'etaient pas peints du tout. Le contour vit desormais sur la
   ligne de rappel seule, ou il a un sens, et la taille de police est passee de
   8.5 a 9.5 dans les deux oeuvres, ce qui donne 13.3px CSS effectifs a la
   largeur de 420px que la carte prend enfin sur bureau (VQA-10), donc le role
   legende de DESIGN.md. Le stroke: none est defensif : il rend la regle vraie
   meme si une oeuvre future reposait un contour sur le groupe. */
.mps-bodymap__label {
  stroke: none;
  fill: var(--mps-ink-muted);
}
.mps-bodymap__leader {
  fill: none;
  stroke: var(--mps-border-control);
  stroke-width: var(--mps-hairline-w);
}

.mps-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mps-space-3);
  margin: var(--mps-space-4) 0 0;
  padding: 0;
  list-style: none;
}
.mps-legend__item {
  display: inline-flex;
  align-items: center;
  gap: var(--mps-space-2);
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}

/* --- 7.8 Liste parallele des 30 groupes --------------------------------- */

.mps-musclelist {
  margin: var(--mps-space-4) 0 0;
  padding: 0;
  list-style: none;
}
.mps-musclelist__item {
  display: grid;
  gap: var(--mps-space-2);
  min-height: var(--mps-row-min-h);
  padding: var(--mps-list-pad);
  border-bottom: var(--mps-hairline-w) solid var(--mps-border-hairline);
  font-size: var(--mps-ui-size);
}
/* L'etiquette de region etait posee en run-in : elle partageait sa ligne avec
   0, 1 ou 2 pastilles selon le hasard du retour a la ligne, donc les onze
   regions ne s'alignaient pas et l'etiquette ne se lisait plus comme un titre
   (revue de code du 01/09/2026, VQA-12). Elle prend sa propre ligne, et les
   liens de la region forment une rangee propre en dessous, dans une liste
   imbriquee que aria-labelledby rattache a cette etiquette (AA-13). */
.mps-musclelist__region {
  color: var(--mps-ink-heading);
  font-weight: var(--mps-ui-strong-weight, 700);
}
.mps-musclelist__group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mps-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.mps-musclelist__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--mps-space-2);
  min-height: var(--mps-chip-min-h);
  padding: var(--mps-chip-pad);
  border: var(--mps-border-w) solid var(--mps-border-control);
  border-radius: var(--mps-radius-pill);
  background: var(--mps-surface);
  color: var(--mps-ink-heading);
  font-family: inherit;
  font-size: var(--mps-ui-size);
  overflow-wrap: anywhere;
  cursor: pointer;
  /* Complete la meme reparation story 3.2 (R-6) que la chaine :not() du haut de
     ce fichier : l'element est un <a>, donc le navigateur le souligne par
     defaut, et une pastille soulignee n'est pas la pastille de DESIGN.md. */
  text-decoration: none;
}
.mps-musclelist__btn[aria-pressed="true"],
.mps-musclelist__btn.is-selected {
  border-color: var(--mps-accent-text);
  background: var(--mps-accent-soft);
  color: var(--mps-accent-hover);
}

/* --- 7.9 Carte de sport ------------------------------------------------- */

.mps-sportcard {
  position: relative;
  padding: var(--mps-space-5);
  border-radius: var(--mps-radius-lg);
  border-top: var(--mps-card-sel-border-w) solid transparent;
  background: var(--mps-surface);
  box-shadow: var(--mps-elev-card);
  transition: transform var(--mps-ease), box-shadow var(--mps-ease);
}
.mps-sportcard:hover {
  transform: translateY(var(--mps-lift));
  box-shadow: var(--mps-elev-card-hover);
}
/* Le zoom 1.05 de la page d'accueil n'est pas repris : dans une grille il
   chevaucherait ses voisines. */
.mps-sportcard.is-selected { border-top-color: var(--mps-accent); }
.mps-sportcard:focus-within {
  outline: var(--mps-focus-w) solid var(--mps-focus-ring);
  outline-offset: var(--mps-focus-offset);
}
.mps-sportcard__head {
  display: flex;
  align-items: center;
  gap: var(--mps-space-2);
  margin-bottom: var(--mps-space-2);
}
.mps-sportcard__icon {
  width: var(--mps-icon-sm);
  height: var(--mps-icon-sm);
  flex: none;
}
/* Hierarchie retablie (revue de code du 01/09/2026, VQA-10). Le sourcil de
   categorie criait en 900 sur l'encre de titre pendant que le nom du sport,
   qui EST l'identite de la carte, se rendait en 300 sur l'encre de corps,
   heritee parce que la story 3.3 l'emet en <p> et non en titre pour ne pas
   sauter un niveau (NFR-3). "ENDURANCE ET ATHLETISME" se lisait comme le titre
   et "Course a pied" comme sa legende. */
.mps-sportcard__eyebrow {
  color: var(--mps-ink-body);
  font-size: var(--mps-label-caps-size);
  font-weight: 700;
  line-height: var(--mps-label-caps-lh);
  letter-spacing: var(--mps-label-caps-tracking);
  text-transform: uppercase;
}
.mps-sportcard__title {
  margin: 0 0 var(--mps-space-2);
  color: var(--mps-ink-heading);
  font-size: var(--mps-h4-size);
  font-weight: var(--mps-h4-weight);
  line-height: var(--mps-h4-lh);
  overflow-wrap: anywhere;
}
.mps-sportcard__top,
.mps-sportcard__weak {
  margin: 0 0 var(--mps-space-2);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}
.mps-sportcard__weak { color: var(--mps-ink-muted); }
.mps-sportcard__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mps-space-2);
  margin-bottom: var(--mps-space-3);
}
/* Bande de 30 micro-pastilles, tete aux pieds, 8px par cellule, sans espace.
   C'est une courbe de lecture, pas un controle, et elle est aria-hidden parce
   que le tableau porte deja la meme information en texte. */
.mps-sportcard__strip {
  display: flex;
  margin-bottom: var(--mps-space-3);
}
.mps-sportcard__strip > * {
  width: var(--mps-strip-cell);
  height: var(--mps-strip-cell);
  flex: none;
  background-color: var(--mps-lvl-fill, var(--mps-level-0-fill));
  background-image: var(--mps-lvl-texture, none);
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-12). Le
   remplissage du niveau 0 vaut 1.13:1 sur la surface blanche de la carte, et
   une cellule de 8 par 8px sans bordure y disparaissait : la bande se lisait
   comme un code-barres troue, sa longueur apparente changeait d'une carte a
   l'autre selon le nombre de zeros, et le lecteur ne distinguait plus un
   groupe non sollicite de la fin de la bande. Le meme remplissage marche dans
   le tableau et sur la silhouette parce qu'il y vit dans une pastille de 22 a
   26px qui porte son propre contour et son propre chiffre. Un filet interne
   suffit ici, la rampe de couleurs n'est pas touchee. */
.mps-sportcard__strip > [data-level="0"] {
  box-shadow: inset 0 0 0 var(--mps-hairline-w) var(--mps-border-hairline);
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-24). Le
   controle etait une etiquette de 24px de haut, soit le minimum strict de WCAG
   2.5.8 et aucune marge d'erreur au doigt, autour d'une case native de 13px
   laissee sans peau alors que tous les autres controles de la page en ont une.
   C'est pourtant l'action principale de la grille de resultats. accent-color
   garde la semantique native et le comportement en couleurs forcees. */
.mps-sportcard__compare {
  position: relative;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: var(--mps-space-2);
  min-height: var(--mps-row-min-h);
  font-size: var(--mps-ui-size);
}
.mps-sportcard__compare input {
  width: var(--mps-icon-md);
  height: var(--mps-icon-md);
  margin: 0;
  accent-color: var(--mps-accent);
}
/* Lien etale : la surcouche passe SOUS la couche de texte pour que la copie
   reste selectionnable, et le controle de comparaison passe au-dessus. */
.mps-sportcard > * { position: relative; z-index: 2; }
.mps-sportcard__link { position: static; }
.mps-sportcard__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* --- 7.10 Badges, differencies par glyphe et libelle, jamais par couleur -- */

.mps-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--mps-space-1);
  padding: var(--mps-badge-pad);
  border: var(--mps-hairline-w) solid var(--mps-border-control);
  border-radius: var(--mps-radius-pill);
  background: var(--mps-surface-sunken);
  color: var(--mps-ink-heading);
  font-size: var(--mps-badge-size);
  font-weight: var(--mps-badge-weight);
  line-height: var(--mps-badge-lh);
  letter-spacing: var(--mps-badge-tracking);
}
.mps-badge--profond {
  border-width: var(--mps-zone-stroke-deep-w);
  border-style: dashed;
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-13). La
   pastille tiretee « estime par analyse du mouvement » n'etait separee du
   libelle de niveau que par une espace de mot, environ 4px a 15px de corps,
   donc son filet tirete se posait sur la derniere lettre de « accessoire » ou
   de « secondaire » et se lisait comme un chevauchement. Meme chose entre la
   pastille de niveau et les mots qui la suivent. Huit lignes par fiche de
   sport, sur 89 fiches. */
.mps-table__cell .mps-badge,
.mps-sheet__badges .mps-badge { margin-left: var(--mps-space-2); }
.mps-table .mps-swatch { margin-right: var(--mps-space-2); }

/* --- 7.11 Bac de selection et panneau de comparaison --------------------- */

.mps-tray {
  min-height: var(--mps-tray-min-h);
  margin-bottom: var(--mps-space-4);
  background: var(--mps-surface);
  font-size: var(--mps-ui-size);
}
.mps-tray__heading {
  margin: 0 0 var(--mps-space-2);
  font-size: var(--mps-h4-size);
  font-weight: var(--mps-h4-weight);
  line-height: var(--mps-h4-lh);
}
.mps-tray__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mps-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.mps-tray__item {
  display: inline-flex;
  align-items: center;
  gap: var(--mps-space-2);
  padding: var(--mps-chip-pad);
  border: var(--mps-border-w) solid var(--mps-accent-text);
  border-radius: var(--mps-radius-pill);
  background: var(--mps-accent-soft);
  color: var(--mps-accent-hover);
  overflow-wrap: anywhere;
}
.mps-tray__icon {
  width: var(--mps-icon-sm);
  height: var(--mps-icon-sm);
  flex: none;
}
.mps-tray__remove {
  min-width: var(--mps-tap-min);
  min-height: var(--mps-tap-min);
  padding: 0;
  border: var(--mps-border-w) solid transparent;
  border-radius: var(--mps-radius-none);
  background: none;
  color: inherit;
  cursor: pointer;
}
.mps-tray__hint {
  margin: var(--mps-space-2) 0 0;
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
}

.mps-compare {
  padding: var(--mps-space-5);
  border-radius: var(--mps-radius-lg);
  background: var(--mps-surface);
  box-shadow: var(--mps-elev-card);
}
/* Le fieldset du mode de calcul portait la bordure par defaut du navigateur
   sur 1058px pour un contenu de 324px, soit 734px de boite vide, la ou la
   maquette approuvee ne dessine aucune boite (revue de code du 01/09/2026,
   VQA-14). Les cinq autres fieldsets de la page passent par
   .mps-filters__group, qui remet border a 0. */
.mps-compare__modes {
  margin: 0 0 var(--mps-space-4);
  padding: 0;
  border: 0;
}
.mps-compare__asym { margin-bottom: var(--mps-space-4); }
.mps-compare__gaps {
  font-size: var(--mps-lead-size);
  font-weight: var(--mps-lead-weight);
  line-height: var(--mps-lead-lh);
}
.mps-compare__index {
  font-size: var(--mps-h4-size);
  font-weight: var(--mps-h4-weight);
  line-height: var(--mps-h4-lh);
}
/* L'indice de completude voyage TOUJOURS avec sa legende. */
.mps-compare__caption {
  margin: var(--mps-space-2) 0 0;
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}
.mps-compare__share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mps-space-3);
  margin-top: var(--mps-space-5);
}
/* .mps-compare__map ne recoit deliberement aucune regle, voir le bloc R-36. */

.mps-copy { display: inline-flex; }
.mps-copy__field {
  width: 100%;
  padding: var(--mps-chip-pad);
  border: var(--mps-border-w) solid var(--mps-border-field);
  border-radius: var(--mps-radius-none);
  background: var(--mps-surface);
  color: var(--mps-ink-heading);
  font-family: inherit;
  font-size: var(--mps-ui-size);
}

/* --- 7.12 Tableaux ------------------------------------------------------- */

.mps-tablewrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* Visible sous 640px seulement. Aucun script ne decide cela, ce fichier le
   decide (correction C6, R-26). Ecrit en mobile first : la ligne est visible
   par defaut et la seule media query a 640px l'eteint. */
.mps-tablewrap__hint {
  margin: 0 0 var(--mps-space-2);
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}

.mps-table {
  width: 100%;
  font-size: var(--mps-table-size);
  font-weight: var(--mps-table-weight);
  line-height: var(--mps-table-lh);
}
.mps-table__caption,
.mps-table caption {
  caption-side: top;
  padding-bottom: var(--mps-space-2);
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  text-align: left;
}
.mps-table th,
.mps-table td {
  padding: var(--mps-cell-pad);
  border-bottom: var(--mps-hairline-w) solid var(--mps-border-hairline);
  text-align: left;
  vertical-align: top;
}
.mps-table thead th {
  background: var(--mps-surface-sunken);
  color: var(--mps-ink-heading);
  font-size: var(--mps-label-caps-size);
  font-weight: var(--mps-label-caps-weight);
  line-height: var(--mps-label-caps-lh);
  letter-spacing: var(--mps-label-caps-tracking);
  text-transform: uppercase;
  /* break-word et non anywhere (revue de code du 01/09/2026, VQA-13) :
     anywhere coupait "GROUPE MUSCULAI / RE" et "CHARGE CUMULE / E" a 390px, et
     la capitale plus l'interlettrage de 2.4px rendent chaque fragment plus dur
     a recoller qu'en casse normale. anywhere reste sur l'en-tete de LIGNE, ou
     un nom de groupe long ne doit jamais deborder de la colonne collante. */
  overflow-wrap: break-word;
  hyphens: auto;
}
/* En-tete de ligne colle a gauche, fond opaque et ombre de bord, pour qu'une
   comparaison a sept colonnes sur telephone ne devienne jamais une grille de
   chiffres nus. */
.mps-table th[scope="row"],
.mps-table__rowhead {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--mps-surface);
  box-shadow: var(--mps-elev-sticky-col);
  overflow-wrap: anywhere;
}
.mps-table__cell,
.mps-table td[data-level] {
  color: var(--mps-ink-heading);
}
/* Niveau 0 : desemphase par le fond de ligne et la couleur de texte
   UNIQUEMENT, jamais par l'opacite, jamais sous 4.5:1. C'est deliberement
   discret parce que ces lignes sont le produit.

   L'encre est --mps-level-0-ink et non --mps-ink-muted, sur mesure : le
   4.61:1 que DESIGN.md prete a ink-muted est mesure sur du BLANC, et sur
   surface-sunken la meme encre ne vaut plus que 4.27:1, donc sous le plancher
   de 4.5:1 que le critere exige. level-0-ink est un jeton du meme jeu, il
   vaut 7.49:1 sur cette surface, et il est deja l'encre du niveau 0 partout
   ailleurs. Constat remonte a l'orchestrateur avec la mesure. */
.mps-table td[data-level="0"],
.mps-table__cell--lvl0 {
  background: var(--mps-surface-sunken);
  color: var(--mps-level-0-ink);
}
/* La desemphase de l'EN-TETE DE LIGNE est reservee au tableau d'une fiche de
   sport, ou une ligne ne porte qu'une seule cellule de niveau (revue de code du
   31/08/2026, BH-04 et EC-18). Sans cette portee, :has() se declenchait aussi
   dans la comparaison a sept colonnes, ou une ligne porte jusqu'a quatre
   niveaux : il suffisait qu'UN des sports compares soit a 0 pour que le nom du
   groupe musculaire soit grise, y compris quand la couverture combinee vaut 3.
   Sur telephone, ou la premiere colonne collante est la seule chose que le
   visiteur lit en defilant, cela inversait le sens du bloc. La regle par
   cellule juste au-dessus reste sans portee : un 0 est un 0 dans les deux
   tableaux. */
.mps-sheet .mps-table tr:has(td[data-level="0"]) th[scope="row"] {
  background: var(--mps-surface-sunken);
  color: var(--mps-level-0-ink);
}

/* REPARATION story 5.4 (R-6), 2026-09-01. Le tableau de comparaison est le
   premier de la feuille a porter SEPT colonnes, et c'est le critere des 320px a
   quatre sports qui l'expose, exactement le moment que R-6 prevoit.
   width:100% seul demandait au tableau de tenir dans la largeur du cadre, donc
   la mise en page automatique repartissait 242px entre sept colonnes : mesure a
   320px avec les quatre noms les plus longs du catalogue, l'en-tete de ligne
   tombait a 85px, "Fléchisseurs du cou" y etait coupe en plein mot par
   overflow-wrap:anywhere, et la ligne d'en-tete montait a 168px de haut. Le nom
   du groupe musculaire n'etait plus lisible dans la colonne collante, c'est-a-dire
   la degradation en grille de chiffres nus que AC-59 existe pour empecher.
   Mesure apres correctif, memes conditions : en-tete de ligne 152px avec
   "Fléchisseurs du cou" sur une seule ligne, ligne d'en-tete 134px de haut,
   tableau 728px dans un cadre de 242px. A 1280px, tableau 1062px et en-tete de
   ligne 188px.
   Le correctif rend au tableau le droit d'etre plus large que son cadre :
   width:auto le laisse prendre la largeur de son contenu, min-width:100% le
   fait quand meme remplir le cadre quand il y a de la place, et un min-width
   par colonne donne un plancher lisible a chacune. Le cadre defile, ce qui est
   la raison d'etre de la colonne collante. Le plancher s'adapte tout seul au
   nombre de sports compares, ce qu'une mise en page fixe n'aurait pas fait, et
   table-layout:fixed retombe de toute facon en mise en page automatique quand
   la largeur du tableau est auto (mesure : a 390px la regle fixe n'avait aucun
   effet).
   La portee .mps-compare est indispensable, les tableaux a deux colonnes des
   fiches de sport ne changent pas. */
.mps-compare .mps-table {
  width: auto;
  min-width: 100%;
}
.mps-compare .mps-table th[scope="row"] { min-width: 9.5rem; }
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-14). A 1280px
   avec trois sports, la colonne « Musculation » se rendait « MUSCULA- » sur
   « TION » : l'interlettrage de 0.2em ajoute environ 26px a un mot de onze
   lettres dans une colonne de 131px, et hyphens: auto coupait alors en plein
   mot. Les en-tetes de colonne de la comparaison portent des noms propres et
   non des libelles courts, ils renoncent donc a l'interlettrage du role
   label-caps. La capitale et la graisse 900 restent, la bande gardee. */
.mps-compare .mps-table thead th[scope="col"] {
  min-width: 7rem;
  letter-spacing: normal;
  hyphens: manual;
}

/* REPARATION story 5.4 (R-6), revue de code du 01/09/2026 (VQA-W9-05), meme
   critere AC-18 que la reparation ci-dessus. L'en-tete de LIGNE etait collant
   a gauche, mais la cellule de coin correspondante du thead restait statique :
   des le premier defilement horizontal, GROUPE MUSCULAIRE partait vers la
   gauche pendant que la colonne des noms restait epinglee, et l'en-tete d'un
   AUTRE sport venait se poser juste au-dessus de ces noms. Mesure a 390px avec
   trois sports, defilement au maximum : la colonne epinglee occupait x 39 a
   191 pendant que CYCLISME SUR ROUTE se plaçait a x 63. Le plan superieur
   garde le coin au-dessus des en-tetes de ligne qui defilent sous lui, et le
   fond reprend celui de la bande d'en-tete pour effacer la couture blanche. */
.mps-table thead th:first-child {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--mps-surface-sunken);
  box-shadow: var(--mps-elev-sticky-col);
}

/* DECISION D45 DU 01/09/2026 (VQA-15). L'EN-TETE DU TABLEAU DE COMPARAISON,
   COLLANT SUR TELEPHONE ET SUR TABLETTE.

   L'en-tete de LIGNE etait deja colle a gauche, et le coin avec lui. La bande
   d'en-tete, elle, ne pouvait pas coller : .mps-tablewrap ne declarait que
   overflow-x, donc sa hauteur epousait toujours son contenu et un
   position: sticky avec top: 0 a l'interieur n'avait aucune course. Sur un
   telephone, le panneau porte 30 lignes de donnees et les colonnes de sport
   devenaient anonymes des la dixieme ligne.

   Le cadre devient donc un vrai port de defilement dans les deux axes sous
   992px, avec une hauteur plafonnee. Au-dessus de 992px il ne change pas : le
   tableau y tient dans la fenetre, et un cadre plafonne entrerait en conflit
   avec la carte collante de #outil.

   LES TROIS PLANS, declares ici une fois pour toutes : la bande d'en-tete a 3,
   le coin a 4, l'en-tete de ligne garde son 1. Le panneau de la carte est a 20
   mais il n'est collant qu'a partir de 992px, donc les deux ne se rencontrent
   jamais. Le bloc d'impression rend au cadre son debordement visible et sa
   hauteur libre, sans quoi le tableau s'imprimerait tronque. */
@media (max-width: 991px) {
  .mps-compare .mps-tablewrap {
    overflow: auto;
    max-height: 70vh;
  }
  .mps-compare .mps-table thead th {
    position: sticky;
    top: 0;
    z-index: 3;
  }
  .mps-compare .mps-table thead th:first-child { z-index: 4; }
}

/* --- 7.13 Fiches (sport et muscle partagent une peau) -------------------- */

.mps-catalogue,
.mps-glossary { display: block; }

.mps-sheet {
  padding: var(--mps-space-5) 0;
  border-bottom: var(--mps-hairline-w) solid var(--mps-border-hairline);
  /* Une fiche est un document, pas un objet : aucune ombre de carte. */
}
.mps-sheet__head {
  display: flex;
  align-items: center;
  gap: var(--mps-space-2);
}
.mps-sheet__title {
  margin: 0 0 var(--mps-space-3);
  font-size: var(--mps-h3-size);
  font-weight: var(--mps-h3-weight);
  line-height: var(--mps-h3-lh);
}
.mps-sheet__title b { font-weight: var(--mps-h3-strong-weight); }
.mps-sheet__quote {
  max-width: var(--mps-prose-max);
  font-size: var(--mps-lead-size);
  font-weight: var(--mps-lead-weight);
  line-height: var(--mps-lead-lh);
}
.mps-sheet__section {
  margin-top: var(--mps-space-5);
  padding-top: var(--mps-space-5);
  border-top: var(--mps-hairline-w) solid var(--mps-border-hairline);
}
/* REPARATION story 3.2 (R-6), 2026-09-01. .mps-sheet__blocktitle n'etait style
   qu'en enfant direct de .mps-sheet__section, or l'anatomie de la comparaison
   (UX-DR20, R-34) porte elle aussi un titre de bloc, "Ce que cette combinaison
   ne couvre pas", et il vit dans .mps-compare et non dans une fiche. Sans cette
   regle il se rendait en corps de texte au lieu du libelle capitales de la DA.
   Constat expose par le premier document reellement rendu de la feature, ce qui
   est exactement le moment que R-6 prevoit pour une reparation. */
.mps-sheet__section > h4,
.mps-sheet__blocktitle {
  color: var(--mps-ink-heading);
  font-size: var(--mps-label-caps-size);
  font-weight: var(--mps-label-caps-weight);
  line-height: var(--mps-label-caps-lh);
  letter-spacing: var(--mps-label-caps-tracking);
  text-transform: uppercase;
}
.mps-sheet__section p,
.mps-sheet p { max-width: var(--mps-prose-max); }
.mps-sheet .mps-tablewrap { max-width: 100%; }

.mps-strong, .mps-weak { max-width: var(--mps-prose-max); }
.mps-weak { color: var(--mps-ink-body); }

/* --- 7.14 Profil d'effort ------------------------------------------------ */

.mps-profile { margin: 0; }
/* Grille et non flex (revue de code du 01/09/2026, VQA-03) : l'etiquette
   etait de largeur automatique, donc la piste ne commencait pas au meme x et
   n'avait pas la meme largeur d'un axe a l'autre. Mesure sur la fiche petanque
   a 1280px : trois axes a la valeur 1 dessinaient 215px, 216px et 208px, et
   trois axes a 3 dessinaient 615px, 648px et 651px. Un diagramme dont deux
   barres egales ne sont pas egales n'est pas un diagramme. La colonne
   d'etiquette est fixe (assez large pour "Coordination"), la piste prend le
   reste, le chiffre garde sa colonne. */
.mps-profile__axis {
  display: grid;
  grid-template-columns: var(--mps-profile-label-w) minmax(0, 1fr) 2ch;
  align-items: center;
  gap: var(--mps-space-3);
  margin-bottom: var(--mps-space-2);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}
.mps-profile__bar {
  height: var(--mps-meter-h);
  border-radius: var(--mps-radius-pill);
  background: var(--mps-surface-sunken);
}
.mps-profile__bar > * {
  display: block;
  height: 100%;
  border-radius: var(--mps-radius-pill);
  background: var(--mps-accent);
}
/* Le chiffre est obligatoire, la barre est le porteur redondant. Le profil
   n'emprunte jamais la rampe de niveaux. */
.mps-profile__value {
  flex: none;
  font-size: var(--mps-numeral-size);
  font-weight: var(--mps-numeral-weight);
  line-height: var(--mps-numeral-lh);
  color: var(--mps-ink-heading);
}

/* --- 7.15 Recherche inverse --------------------------------------------- */

.mps-reverse { margin: 0; }
.mps-reverse__group { margin-bottom: var(--mps-space-4); }
/* Le h5 d'un groupe de niveau et le h4 du bloc qui le contient etaient
   identiques au pixel pres, donc l'imbrication etait invisible et la page
   portait 120 de ces h5 (revue de code du 01/09/2026, VQA-09). Le titre de
   bloc reste le seul libelle capitales en 900 d'une fiche, le h5 descend d'un
   cran en graisse, en interlettrage et en encre. --mps-ink-body vaut 5.74:1
   sur blanc, donc au-dessus du plancher AA. */
.mps-reverse__heading {
  margin: 0 0 var(--mps-space-2);
  color: var(--mps-ink-body);
  font-size: var(--mps-label-caps-size);
  font-weight: 700;
  line-height: var(--mps-label-caps-lh);
  letter-spacing: 1.2px;
  text-transform: uppercase;
}
.mps-reverse__list {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--mps-table-size);
  line-height: var(--mps-table-lh);
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-22). Une liste
   inverse aligne jusqu'a 57 noms de sport dans un seul paragraphe, tous
   soulignes et bleus : cinq lignes pleines de souligne se lisent comme un mur
   et le point median separateur y devient introuvable. Meme probleme dans la
   premiere colonne des tableaux, ou les 30 en-tetes de ligne sont autant de
   filets bleus. La couleur, le curseur et l'anneau de focus suffisent a
   identifier le lien au repos, le souligne revient au survol et au focus. */
.mps-reverse__list > li { display: inline; }
.mps-reverse__list { line-height: var(--mps-body-lh); }
.mps-reverse__list > li + li::before { content: " · "; }
main .mps-reverse__list a,
main .mps-table th[scope="row"] a { text-decoration: none; }
main .mps-reverse__list a:hover,
main .mps-reverse__list a:focus,
main .mps-table th[scope="row"] a:hover,
main .mps-table th[scope="row"] a:focus { text-decoration: underline; }
.mps-reverse__empty {
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}

/* --- 7.16 Sources, FAQ, methode, donnees ouvertes ------------------------ */

.mps-sources {
  margin: 0;
  padding: 0;
  list-style: none;
}
.mps-sources__item,
.mps-method-note {
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}
/* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-21). Les douze
   paires se suivaient avec le meme espace entre une question et sa reponse
   qu'entre deux paires, sans filet et sans numerotation, donc la section se
   lisait comme une seule colonne grise sans aide au reperage. Le filet et
   l'espace superieur rendent la paire visible sans ajouter de couleur. */
.mps-faq__item { margin-bottom: var(--mps-space-5); }
.mps-faq__item + .mps-faq__item {
  padding-top: var(--mps-space-5);
  border-top: var(--mps-hairline-w) solid var(--mps-border-hairline);
}
.mps-faq__q {
  margin: 0 0 var(--mps-space-2);
  font-size: var(--mps-h3-size);
  font-weight: var(--mps-h3-weight);
  line-height: var(--mps-h3-lh);
}
.mps-faq__q b { font-weight: var(--mps-h3-strong-weight); }
.mps-faq__a { max-width: var(--mps-prose-max); }
/* La meme largeur de mesure pour le reste de la prose (revue de code du
   01/09/2026, VQA-08). Mesure a 1280px avant correction : l'intro, les fiches
   et les reponses de FAQ etaient a 720px, soit environ 96 caracteres, pendant
   que la bibliographie de 37 entrees, toute la section Methode, les notes de
   filtre et les legendes couraient de 1062 a 1110px, soit 145 a 171
   caracteres, sans qu'aucune logique editoriale ne justifie l'alternance. */
.mps-method__block p,
.mps-method__block li,
.mps-sources__item,
.mps-dataset__licence,
.mps-search__hint,
.mps-tray__hint,
.mps-filters__note,
.mps-compare__caption { max-width: var(--mps-prose-max); }
.mps-method__block { margin-bottom: var(--mps-space-5); }
.mps-dataset__download { margin-right: var(--mps-space-3); }
.mps-dataset__licence {
  color: var(--mps-ink-muted);
  font-size: var(--mps-caption-size);
}

/* --- 7.17 Avis : rappel sante, alertes, replis sans script --------------- */

.mps-disclaimer,
.mps-nojs {
  padding: var(--mps-space-4);
  border-left: var(--mps-notice-border-w) solid var(--mps-surface-dark);
  border-radius: var(--mps-radius-md);
  background: var(--mps-surface-sunken);
  color: var(--mps-ink-heading);
  font-size: var(--mps-caption-size);
  line-height: var(--mps-caption-lh);
}
.mps-disclaimer--inline { margin-top: var(--mps-space-5); }
/* L'alerte reprend la meme geometrie. Elle sert exactement trois fois, une par
   modificateur. Le rappel sante reste deliberement neutre : un avis de sante
   style comme une erreur se lit comme une erreur qu'on apprend a ecarter. */
.mps-alert {
  padding: var(--mps-space-4);
  border-left: var(--mps-notice-border-w) solid var(--mps-alert);
  border-radius: var(--mps-radius-md);
  background: var(--mps-alert-soft);
  color: var(--mps-alert);
  font-size: var(--mps-ui-size);
  line-height: var(--mps-ui-lh);
}

/* Les deux portes de l'amelioration progressive. Chacune reserve la meme boite
   dans les deux etats, donc le passage a js-ready ne coute aucun decalage.

   Il n'y a deliberement AUCUN selecteur html.js-ready ici, et c'en est la
   place la plus evidente : AD-8 fixe UN SEUL porteur de visibilite, l'attribut
   hidden, avec [hidden] { display: none !important } declare une seule fois
   plus haut, et precise que les classes portent un etat qui n'est pas la
   visibilite. Le mecanisme est donc : la story 3.2 emet .mps-jsonly avec
   hidden et .mps-nojs sans, puis app.js bascule hidden sur les deux au moment
   ou il ajoute js-ready, hidden etant l'une des huit mutations autorisees par
   AD-8. Une regle CSS qui masquerait .mps-nojs sur js-ready serait un SECOND
   porteur de visibilite. Constat verifie et ecarte par la revue de code du
   31/08/2026 (EC-07), note ici pour qu'il ne soit pas rouvert.

   Ce bloc NE DECLARE PLUS display: block sur les deux classes (revue de code
   du 01/09/2026, BH-02). Declaree en fin de section 7, apres toutes les regles
   de composant, elle gagnait a l'anciennete contre .btn { display: inline-block }
   et contre le display natif d'un <tbody> : tbody.mps-nojs se calculait a
   block et sa ligne de repli ne couvrait plus la table, tbody#mps-compare-body
   aurait empile en blocs les 30 lignes de la story 5.4, et les boutons
   .mps-jsonly des fiches, le champ de copie, le segment de mode et le segment
   de vue devenaient des blocs pleine largeur empiles. L'intention declaree
   etait de reserver la meme boite dans les deux etats, ce que display: block ne
   fait ni pour un tbody ni pour un bouton : chaque element garde donc son
   display propre. */

.mps-btn-quiet {
  padding: 0;
  border: var(--mps-border-w) solid transparent;
  background: none;
  color: var(--mps-accent-text);
  font-family: inherit;
  font-size: var(--mps-ui-size);
  font-weight: var(--mps-ui-weight);
  line-height: var(--mps-ui-lh);
  text-decoration: underline;
  cursor: pointer;
}
.mps-btn-quiet:hover, .mps-btn-quiet:focus { color: var(--mps-accent-hover); }

/* ---------------------------------------------------------------------------
   8. Confinement du rendu hors ecran (PRD NFR-1, FR-18, AD-8)
   Applique UNIQUEMENT a des conteneurs dont le contenu est deja deplie, jamais
   en accompagnement d'un <details> replie et jamais a quoi que ce soit qui se
   trouve dans un <details>. content-visibility: auto ne retire rien du
   document : le sous-arbre reste trouvable par la recherche dans la page, par
   la navigation par ancre et par l'arbre d'accessibilite, ce qui est ce qui
   fait que cette technique ne coute rien a FR-1.
   Le mot-cle auto de contain-intrinsic-size fait memoriser au navigateur la
   derniere taille rendue, donc un second passage sur une ancre atterrit juste.
   Valeurs de depart, re-mesurees par la story 6.3 en vague 12.
   --------------------------------------------------------------------------- */

.mps-sheet--sport {
  content-visibility: auto;
  contain-intrinsic-size: var(--mps-cis-sport);
}
.mps-sheet--muscle {
  content-visibility: auto;
  contain-intrinsic-size: var(--mps-cis-muscle);
}

/* ---------------------------------------------------------------------------
   9. Points de rupture. Deux portent de la mise en page, 768px et 992px.
   Le troisieme, 640px, allume ou eteint exactement une declaration et rien
   d'autre : c'est l'exception explicitement enregistree a NFR-10 (C6, R-26).
   Aucun quatrieme point de rupture sans le meme traitement ecrit.

   IL EN EXISTE POURTANT UN QUATRIEME, a 480px, sur .mps-segment, section 7.3.
   Il n'a pas encore ce traitement ecrit et le constat est consigne dans
   deferred-work.md (revue de code du 31/08/2026, AA-AUD-04). Il est nomme ici
   plutot que passe sous silence.
   --------------------------------------------------------------------------- */

@media (min-width: 641px) {
  /* Au-dessus de 640px la ligne d'invite de defilement n'est pas rendue, parce
     que le debordement horizontal n'y est plus reel.

     641 et non 640 : le critere, la correction C6 et R-26 disent tous les trois
     "640px de large ou moins", donc la borne est INCLUSIVE du cote etroit, et
     un min-width: 640px eteignait l'invite a 640px pile (revue de code du
     31/08/2026, AA-12). Ce n'est pas un quatrieme point de rupture, c'est le
     meme, exprime du bon cote de sa borne. */
  .mps-tablewrap__hint { display: none; }
}

@media (min-width: 768px) {
  :root {
    --mps-header-h: var(--mps-space-header-h);
    --mps-section-gap: var(--mps-space-8);
    /* D45. Moyennes mesurees a 768px exactement, voir le tableau du bloc de
       jetons. Elles ne valent que de 768 a 991px : la grille de resultats et la
       colonne de la carte changent a 992px et les fiches se resserrent. */
    --mps-cis-sport: auto 2705px;
    --mps-cis-muscle: auto 944px;
  }

  .mps-results { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Tablette : une colonne, la carte centree a 420px au maximum, au-dessus
     des resultats. */
  .mps-workspace__map {
    max-width: var(--mps-map-max);
    margin-inline: auto;
  }

  .mps-hero { width: var(--mps-hero-md); }
}

@media (min-width: 992px) {
  :root {
    /* D45. Moyennes mesurees a 992px, voir le tableau du bloc de jetons. La
       fiche de sport ne bouge plus au dela, la fiche de muscle si. */
    --mps-cis-sport: auto 2603px;
    --mps-cis-muscle: auto 870px;
  }

  /* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (VQA-10). La
     correction C1 demande que la silhouette monte a 420px sur bureau. Elle n'y
     arrivait jamais : la piste de grille valait --mps-map-col, 360px, le
     panneau prend 24px de rembourrage de chaque cote et le SVG se rendait donc
     a 312px, exactement sa largeur sur un telephone de 390px. Le jeton
     --mps-map-max de 420px etait du code mort au-dessus de 992px, et c'est
     aussi ce qui rendait les etiquettes de la VQA-01 si petites. La piste vaut
     desormais la largeur maximale de l'oeuvre plus ses deux rembourrages. La
     grille de resultats est en repeat(auto-fill, minmax(280px, 1fr)), elle
     passe donc de trois a deux colonnes a 1280px et retrouve la troisieme sur
     un ecran plus large, ce qui est le bon arbitrage pour le seul grand
     graphique de la page. */
  .mps-workspace { grid-template-columns: var(--mps-map-col) minmax(0, 1fr); }

  /* Bureau : grille a deux colonnes, la carte collee sous la barre.
     Sa course collante s'arrete au bas de .mps-workspace, qui est aussi le
     bord haut de #comparaison, donc la silhouette vient se poser juste
     au-dessus du titre de la comparaison et le tableau a sept colonnes est le
     bloc suivant dans le defilement. */
  .mps-workspace__map {
    position: sticky;
    top: var(--mps-header-h);
    /* Ecrit plutot qu'herite (VQA-04). La barre d'outils n'est plus collante,
       la carte est donc le seul panneau colle de #outil et son plan est
       declare ici une fois pour toutes. */
    z-index: 20;
    max-width: none;
    margin-inline: 0;
  }

  .mps-results { grid-template-columns: repeat(auto-fill, minmax(var(--mps-card-min), 1fr)); }

  /* Correction C3 et UX-DR41 : le champ de recherche ne doit pas passer sous
     le pli a 1280x800. La colonne du hero mesurait 320 par 260px et laissait
     environ 330px de vide sous l'illustration pendant que le rappel sante et
     la signature s'empilaient SOUS la prose, ce qui poussait #outil a y=731 et
     le champ a y=1005, soit 205px sous le pli (revue de code du 01/09/2026,
     BH-10, AA-07 et VQA-01). Les deux blocs viennent donc remplir ce vide. */
  .mps-intro {
    grid-template-columns: minmax(0, 1fr) var(--mps-hero-lg);
    align-items: start;
  }
  .mps-intro > .mps-intro__prose:first-child { grid-column: 1; grid-row: 1 / span 2; }
  .mps-hero { grid-column: 2; grid-row: 1; }
  .mps-intro > .mps-intro__prose:last-child { grid-column: 2; grid-row: 2; }

  /* Les etiquettes de muscles autour de la silhouette, avec leurs lignes de
     rappel, ne sont dessinees qu'ici. En dessous, la liste parallele porte
     les noms. */
  .mps-bodymap__labels { display: block; }
}

/* ---------------------------------------------------------------------------
   9 bis. Le point de rupture de 1280px, DECISION D36 DU 01/09/2026.

   C'est un QUATRIEME point de rupture au sens de NFR-10 et il a, comme le
   640px et le 480px avant lui, son traitement ecrit. Il ne porte pas de mise en
   page : il retire exactement 24px de rythme vertical a la composition
   d'ouverture, et il ajuste une reserve de rendu hors ecran. Rien d'autre.

   POURQUOI. La correction C3 demande que le champ de recherche ne passe pas
   sous le pli a 1280 par 800. Mesure sur beta avant cette passe, le haut de
   #mps-recherche est a 870px pour un pli a 800. Les deux liens d'evitement
   passent en visible-au-focus, ce qui rend 29px, et ce bloc en rend 24 de plus,
   pris sur le rythme de la composition d'ouverture et sur elle seule :
   la marge basse du h1, celle de sa barre accent, et celle du paragraphe
   d'ouverture, 8px chacune. Le plancher de 150 mots de FR-41 n'est pas touche,
   la reponse d'ouverture garde ses 162 mots, et la DA garde ses signatures
   partout ailleurs, y compris dans #outil et dans les 119 fiches.

   RESTE 817px POUR UN PLI A 800. D36 le prevoit : C3 est assouplie a 900px et
   le fait est consigne dans 07-orchestrator-ux-review.md et dans epics.md.
   --------------------------------------------------------------------------- */

@media (min-width: 1280px) {
  :root {
    /* D45. La fiche de muscle se resserre encore de 992 a 1280px, la fiche de
       sport non. Moyenne mesuree a 1280px. */
    --mps-cis-muscle: auto 837px;
  }

  .mps-intro h1 { margin-bottom: var(--mps-space-4); }
  .mps-intro .ml-separator { margin-bottom: var(--mps-space-4); }
  .mps-intro__lead { margin-bottom: 0; }
}

/* ---------------------------------------------------------------------------
   10. Impression (FR-43, AC-67, UX-DR38)
   Ce bloc n'ecrit JAMAIS hidden et ne le retire jamais. "La selection en
   cours" est l'ensemble des fiches qui ne portent pas hidden au moment de
   l'impression : une fiche ouverte s'imprime seule, et sans script rien ne
   porte hidden donc c'est toute la reference qui s'imprime. Imprimer une page
   blanche a un visiteur sans script serait pire qu'imprimer une page longue.
   --------------------------------------------------------------------------- */

@page {
  /* Litteral assume : la boite @page n'herite pas des proprietes
     personnalisees. La marge basse reserve la bande du rappel sante, qui se
     repete sur chaque page et se place DANS cette marge. */
  margin: 12mm 12mm 26mm;
}

@media print {
  html, body {
    background: var(--mps-page-bg);
    color: var(--mps-print-ink);
  }

  /* Barre, coque de navigation, outils, chrome de la carte, composition
     d'ouverture, controle de copie, controles du bac et navigation de pied. */
  .ml-bar,
  .ml-header,
  .ml-skiplink,
  .ml-nav,
  .ml-footer__links,
  .mps-toolbar,
  .mps-search,
  .mps-filters,
  .mps-filters__bypass,
  .mps-hero,
  .mps-musclelist,
  .mps-bodymap__views,
  .mps-bodymap__bypass,
  /* REPARATION story 5.4 (R-6), revue de code du 01/09/2026 (EC-14). Le
     segment du mode de calcul est un controle comme les autres et il
     s'imprimait pourtant, en fieldset d'apparence interactive. Ce qu'il porte
     est deja sur le papier en toutes lettres : #mps-map-legende s'imprime et
     lit « Mode de calcul : Charge cumulee. ». */
  .mps-compare__modes,
  .mps-copy,
  .mps-copy__field,
  .mps-tray__remove,
  .mps-tray__clear,
  .mps-compare__share,
  .mps-sportcard__compare,
  .mps-announce { display: none; }

  /* REPARATION story 6.3 (R-6), revue de code du 31/08/2026 (BH-A6, AA-AUD-05).
     .mps-count et .mps-activefilters etaient dans la liste ci-dessus. Un
     visiteur qui filtrait sur les huit sports de combat et imprimait obtenait
     alors un document portant le h1 « Quels muscles travaille chaque sport ? »,
     la phrase d'ouverture qui annonce 89 sports, huit fiches, et RIEN sur le
     papier ne disait qu'un filtre etait actif ni lequel : l'imprime etait
     indiscernable de la reference complete et affirmait un compte qu'il ne
     contenait pas. L'argument qui retire la barre d'outils ne porte pas sur ces
     deux blocs : ce ne sont pas des controles, ce sont deux constats sur le
     document imprime, exactement ce que FR-43 et UX-DR39 demandent de ne pas
     taire. Ils sont donc rendus, en ligne de provenance sous le titre, sans
     pastille et sans bouton de retrait. Sans script les deux conteneurs sont
     vides et masques, l'impression de la reference complete est inchangee. */
  .mps-count,
  .mps-activefilters {
    display: block;
    margin: 0 0 var(--mps-space-2);
    padding: 0;
    color: var(--mps-print-ink);
    font-size: var(--mps-caption-size);
    line-height: var(--mps-caption-lh);
  }
  .mps-activefilters__chip {
    display: inline;
    min-height: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--mps-print-ink);
    font-size: var(--mps-caption-size);
  }
  .mps-activefilters__chip + .mps-activefilters__chip::before { content: ", "; }
  .mps-activefilters__label { display: inline; margin-right: var(--mps-space-1); }
  .mps-activefilters__remove { display: none; }

  /* La silhouette elle-meme, sa ligne de legende et la legende des niveaux
     restent : ce sont les deux lignes qui disent ce que la silhouette montre
     et ce que les chiffres veulent dire. Le chrome retire ci-dessus, ce sont
     les controles. */

  /* Aucune fiche ne doit etre sautee par le moteur d'impression. */
  .mps-sheet--sport,
  .mps-sheet--muscle {
    content-visibility: visible;
    contain-intrinsic-size: auto;
  }

  /* D45. En media pagine, la boite de page fait environ 703px CSS de large,
     donc la requete (max-width: 991px) CORRESPOND et le cadre du tableau de
     comparaison arriverait plafonne a 70vh sur le papier, c'est-a-dire
     tronque. Il retrouve ici son debordement visible et sa hauteur libre. */
  .mps-compare .mps-tablewrap {
    overflow: visible;
    max-height: none;
  }
  .mps-compare .mps-table thead th { position: static; }

  /* Les etiquettes de muscles autour de la silhouette sont RENDUES sur papier
     (revue de code du 31/08/2026, EC-17). En media pagine les requetes de
     largeur s'evaluent sur la boite de page, soit environ 703px CSS pour un A4
     avec les marges de 12mm declarees plus haut, donc (min-width: 992px) ne
     correspond jamais et les etiquettes restaient masquees, pendant que le bloc
     ci-dessus masque deja .mps-musclelist. La feuille imprimee ne portait alors
     que 32 formes numerotees, sans un seul nom de muscle : les chiffres
     survivaient, l'identite non. Les etiquettes vivent dans le viewBox de
     l'oeuvre, elles ne coutent ni page ni mise en page supplementaire. */
  .mps-bodymap__labels { display: block; }

  /* Le chiffre de chaque niveau survit : les navigateurs suppriment les fonds
     par defaut et l'encodage chromatique disparait avec eux.

     .mps-zone__num N'EST PAS dans cette regle (revue de code, AA-13). Le
     chiffre de la silhouette est peint par un fill SVG, et color ne pilote pas
     un fill sauf a valoir currentColor, donc la declaration y etait morte : elle
     ne changeait rien et faisait croire a une garantie. Le contrat reel, ecrit
     ici plutot que suppose : un remplissage SVG n'est pas un fond CSS, il n'est
     donc pas supprime a l'impression, et le chiffre s'imprime a son encre de
     niveau sur le remplissage de son niveau, paire deja mesuree AA. Le forcer
     en noir le rendrait au contraire illisible sur le remplissage du niveau 3. */
  .mps-swatch,
  .mps-table td[data-level] {
    color: var(--mps-print-ink);
    background: none;
  }
  .mps-swatch { border-color: var(--mps-print-ink); }

  .mps-sportcard,
  .mps-compare,
  .mps-bodymap,
  .mps-filters { box-shadow: none; }

  .mps-sheet { break-inside: auto; }
  .mps-sheet__title, h1, h2, h3 { break-after: avoid; }
  .mps-table tr { break-inside: avoid; }

  /* Le rappel sante se repete sur chaque page imprimee. Verifie sur un vrai
     PDF genere par Chromium, pas seulement en emulation d'ecran : la bande
     apparait bien au bas de chaque page.

     Limite connue et assumee, ecrite ici pour qu'elle ne soit pas redecouverte
     comme un bug. Un element fixe cale a bottom: 0 s'ancre au bas de la BOITE
     DE CONTENU de la page et non au bas de la feuille, donc il peut recouvrir
     les toutes dernieres lignes d'une page bien remplie. Deux contournements
     ont ete essayes et mesures sur PDF, et rejetes : translateY(100%) fait
     rogner la bande par le bord de page et la fait reapparaitre en haut de la
     page suivante, et une marge @page plus grande ne change rien puisque la
     bande suit la boite de contenu. Le mecanisme reellement fiable est un
     table-footer-group, qui demande un balisage que cette story ne possede
     pas. Retenu ici : un fond opaque pour que le pire cas reste une bande
     lisible et non un texte sur du texte, plus des break-inside: avoid sur
     les blocs qui atterrissent le plus souvent en bas de page. */
  .mps-disclaimer:not(.mps-disclaimer--inline) {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    padding: var(--mps-space-2) 0 0;
    border-left: 0;
    border-top: var(--mps-hairline-w) solid var(--mps-print-ink);
    border-radius: var(--mps-radius-none);
    background: var(--mps-page-bg);
  }

  .mps-legend,
  .mps-bodymap,
  .mps-sportcard,
  .mps-faq__item,
  .mps-reverse__group,
  .mps-profile__axis,
  .mps-disclaimer { break-inside: avoid; }

  /* Les URL des liens s'impriment apres le libelle, et l'URL de la page
     s'imprime dans la ligne legale, depuis un jeton unique. */
  /* Toutes les cibles reelles, jamais les ancres internes : imprimer #sport-x
     apres chaque lien du catalogue serait du bruit et pas une information. */
  main a[href]:not([href^="#"])::after {
    content: " (" attr(href) ")";
    font-size: var(--mps-caption-size);
    word-break: break-all;
  }
  .ml-legal::after {
    display: block;
    content: var(--mps-canonical-url);
    font-size: var(--mps-caption-size);
  }
  .ml-footer {
    background: none;
    color: var(--mps-print-ink);
    margin-top: var(--mps-space-5);
    padding: 0;
  }
}

/* ---------------------------------------------------------------------------
   11. Mouvement reduit (NFR-2, UX-DR37)
   Le defilement doux n'est ni une transition ni un effet de survol, donc une
   regle globale qui ne le nomme pas laisse tourner une animation de page
   entiere pour exactement les visiteurs que la regle protege.
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: var(--mps-motion-off) !important;
    animation-iteration-count: 1 !important;
    transition-duration: var(--mps-motion-off) !important;
    scroll-behavior: auto !important;
  }
  html { scroll-behavior: auto !important; }
  .mps-sportcard:hover {
    transform: none;
    box-shadow: var(--mps-elev-card);
  }
}

/* ---------------------------------------------------------------------------
   12. Couleurs forcees (NFR-2, AC-68, UX-DR37)
   Ces modes ecrasent le fill SVG, donc chaque zone s'effondrerait sur une
   seule couleur systeme et un fond hachure peut disparaitre entierement. Le
   chiffre, le contour tirete et la bordure des pastilles sont ce qui reste.
   Les mots-cles systeme sont pris dans les jetons --mps-fc-*, declares dans
   :root, pour qu'aucune valeur de couleur ne vive hors du bloc de jetons.
   --------------------------------------------------------------------------- */

@media (forced-colors: active) {
  /* Le texte numerique du niveau reste visible dans TOUS les etats. */
  .mps-zone__num {
    fill: var(--mps-fc-ink);
    /* Le halo de VQA-W9-02 reprend la couleur de fond systeme, sinon il
       peindrait une couleur de theme derriere un glyphe systeme. */
    stroke: var(--mps-fc-bg);
    forced-color-adjust: none;
  }
  .mps-zone__shape {
    fill: var(--mps-fc-bg);
    stroke: var(--mps-fc-ink);
  }
  .mps-zone.is-selected .mps-zone__shape,
  .mps-zone[aria-pressed="true"] .mps-zone__shape { stroke: var(--mps-fc-accent); }
  /* Le marquage muscle profond survit parce qu'il est un contour. */
  .mps-zone[data-profond] .mps-zone__shape,
  .mps-zone--deep .mps-zone__shape { stroke-dasharray: var(--mps-zone-dash); }

  /* La legende et les pastilles de niveau restent lisibles : bordure et
     chiffre, pas de fond. */
  .mps-swatch {
    background-image: none;
    background-color: var(--mps-fc-bg);
    border-color: var(--mps-fc-ink);
    color: var(--mps-fc-ink);
  }
  .mps-legend__item { color: var(--mps-fc-ink); }

  /* Les pastilles de filtre gardent une bordure de 2px et leur coche. */
  .mps-chip__label {
    border-width: var(--mps-border-w);
    border-color: var(--mps-fc-btn-ink);
    color: var(--mps-fc-btn-ink);
  }
  .mps-chip__input:checked + .mps-chip__label {
    border-color: var(--mps-fc-accent);
    color: var(--mps-fc-accent);
  }
  .mps-chip__input:checked + .mps-chip__label .mps-chip__check {
    display: inline-block;
    forced-color-adjust: none;
    color: var(--mps-fc-accent);
  }

  .mps-table td[data-level] { background: none; }
  :focus-visible { outline-color: var(--mps-fc-ink) !important; }
  /* Contrepartie obligatoire de la reparation de la story 6.2 en section 7.3.
     Le selecteur ajoute la-bas est plus specifique que le :focus-visible nu
     ci-dessus, il gagnerait donc aussi en couleurs forcees et remplacerait
     CanvasText par un blanc en dur, ce qui est exactement ce que le mode
     couleurs forcees existe pour empecher (AC-68). */
  .mps-segment__input:checked:focus-visible + .mps-segment__label {
    outline-color: var(--mps-fc-ink) !important;
  }
}
