/* Statsmaker v2 — coquille de navigation (design §B.1, architecture §D).
 *
 * Porte du CSS de `sm-nav.js` (31 Ko de composants web à Shadow DOM) vers de
 * simples classes : les partiels `v2/partials/_header.html`, `_tabbar.html` et
 * `_footer.html` sont du HTML rendu par le serveur, pas du JavaScript. Le
 * Shadow DOM isolait le CSS — un défaut ici, puisque seuls les tokens le
 * traversaient.
 *
 * Aucune valeur de couleur, de rayon ou de taille n'est écrite en dur : tout
 * vient de `sm-theme.css` (palette) et de `sm-tokens.css` (couche sémantique).
 */

/* ── Lien d'évitement (§F.5) : premier élément focalisable de la page ── */
.sm-skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--color-surface); color: var(--color-text);
  border: 1px solid var(--sm-border-field); border-radius: var(--r-sm);
  padding: 10px 16px; font-size: var(--fs-ui); font-weight: 600;
}
.sm-skip:focus { left: 8px; top: 8px; }

/* ── Gabarit de page, identique sur tous les écrans (design §A.4) ── */
.sm-main {
  max-width: var(--sm-page-max); margin: 0 auto;
  padding: 20px 20px 90px;   /* 90px : la barre d'onglets mobile fait 74px */
  /* 🛑 Le contenu PREND la place qui reste, et pousse le pied en bas (voir
   * `body.sm-v2`). `1 0 auto` et non `1` : `flex: 1` autorise le
   * rétrécissement, et sur une page plus haute que l'écran le navigateur
   * écraserait le contenu pour faire tenir le pied. */
  flex: 1 0 auto;
  /* 🔴 **`width: 100%` N'EST PAS UNE REDONDANCE — C'EST LA MOITIÉ DU PIED DE
   * PAGE COLLANT.** Une marge `auto` sur l'axe transversal **annule le
   * `stretch`** d'un item flex : la règle est dans la spécification, et elle
   * est contre-intuitive. En disposition de bloc, `max-width` + `margin: 0
   * auto` donne « aussi large que possible, jusqu'à 1120 px, centré ». Le jour
   * où `<body>` est devenu une colonne flex, la même déclaration a donné
   * « aussi large que le CONTENU, centré » : tous les écrans se sont resserrés
   * autour de leur contenu intrinsèque, et l'application entière avait l'air
   * dessinée pour un téléphone.
   *
   * ⚠️ `box-sizing` est déjà global, mais il est REDIT ici : sans lui, les
   * 40 px de rembourrage s'ajouteraient aux 100 % et la page déborderait de
   * 40 px — soit exactement le défilement latéral que
   * `test_v2_largeur_telephone` existe pour interdire. */
  width: 100%;
  box-sizing: border-box;
}
@media (max-width: 640px) { .sm-main { padding-left: 14px; padding-right: 14px; } }

/* -- Zone de messages de la coquille (`v2/partials/_messages.html`) -------
   Une seule zone pour les 21 ecrans. Elle n'existe dans le DOM que lorsqu'il y
   a quelque chose a dire : sans message, aucune de ces regles ne s'applique et
   aucun ecran deja valide ne bouge d'un pixel.

   Aucune regle generique : tout est porte par la famille `.sm-msg*`. Les tons
   reprennent les MEMES couples que `.sm-auth-alerte` / `.sm-auth-motif`, deja
   mesures par `ContrasteDesJetonsTests` - une zone de coquille n'est pas
   l'endroit ou l'on invente une palette. */
.sm-msgs { margin: 0 0 var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-3); }
.sm-msg {
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--r-sm);
  border-left: 3px solid transparent;
  font-size: var(--fs-body-sm);
  line-height: 1.45;
  text-wrap: pretty;
}
/* Neutre : le ton par defaut, y compris pour un niveau que la table ne connait
   pas - un message inconnu s'affiche, il ne disparait pas. */
.sm-msg[data-sm-ton="info"] {
  background: var(--color-accent-100);
  border-left-color: var(--color-accent-300);
  color: var(--color-accent-800);
}
/* Le vert du produit = << c'est fait >>. */
.sm-msg[data-sm-ton="succes"] {
  background: var(--sm-scope-bg);
  border-left-color: var(--sm-scope-border);
  color: var(--sm-scope-fg);
}
/* `--sm-draw` est un REMPLISSAGE : en texte il tombe a 2,58:1 en mode clair
   (F-1). Le texte prend `--sm-draw-text`. */
.sm-msg[data-sm-ton="attente"] {
  background: color-mix(in srgb, var(--sm-draw) 12%, transparent);
  border-left-color: var(--sm-draw);
  color: var(--sm-draw-text);
}
/* Idem : `--sm-loss` remplit, `--sm-loss-text` ecrit. */
.sm-msg[data-sm-ton="erreur"] {
  background: color-mix(in srgb, var(--sm-loss) 10%, transparent);
  border-left-color: var(--sm-loss);
  color: var(--sm-loss-text);
}
.sm-msg a { color: inherit; }

/* ── En-tête collant ─────────────────────────────────────────────── */
.sm-hd {
  /* `flex: none` : dans la colonne de `body.sm-v2`, le `flex-shrink: 1` par
   * défaut comprimerait l'en-tête sur une page longue — et `--sm-shell-h`,
   * dont dépendent tous les décalages collants, deviendrait faux. */
  flex: none;
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--color-bg) 90%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: var(--sm-hd-filet) solid var(--color-divider);
  font-family: var(--font-body); color: var(--color-text);
}
/* ⚠️ `--sm-hd-pad-y` et `--sm-hd-filet` ne sont pas des jetons de confort :
   `--sm-shell-h` est CALCULÉE à partir d'eux (`sm-tokens.css`), et c'est elle
   que lisent tous les `top:` collants. Écrire `padding: 11px 20px` en dur ici
   ferait diverger la hauteur réelle de la hauteur annoncée — c'est-à-dire le
   défaut d'origine, qui cachait la barre de catégories du glossaire sous
   l'en-tête. `test_v2_css.HauteurDeLEnTeteTests` le garde. */
.sm-hd-bar {
  max-width: var(--sm-page-max); margin: 0 auto;
  padding: var(--sm-hd-pad-y) 20px; display: flex; align-items: center; gap: var(--sp-5);
}
.sm-hd-logo { display: flex; align-items: center; gap: 9px; text-decoration: none; color: inherit; flex: none; }
.sm-hd-mark {
  width: 28px; height: 28px; border-radius: var(--r-xs);
  background: var(--color-accent); color: var(--color-bg);
  display: grid; place-items: center;
  font-family: var(--font-heading); font-size: 16px;
}
.sm-hd-word { font-family: var(--font-heading); font-size: var(--fs-h4); }
.sm-hd-dest { display: flex; gap: 2px; flex: none; }
.sm-hd-dest a {
  padding: 10px 12px; font-size: var(--fs-ui); text-decoration: none;
  border-radius: var(--r-pill); color: var(--color-neutral-600);
  font-weight: 600; white-space: nowrap;
}
.sm-hd-dest a:hover { background: var(--color-neutral-200); color: var(--color-text); }
.sm-hd-dest a[aria-current] { color: var(--color-accent-800); background: var(--color-accent-100); }

.sm-hd-search { position: relative; flex: 1 1 220px; min-width: 0; max-width: 360px; }
.sm-hd-search input {
  width: 100%; box-sizing: border-box; font-family: inherit; font-size: var(--fs-body-sm);
  padding: 11px 14px 11px 34px; border: 1px solid var(--sm-border-field);
  background: var(--color-surface); border-radius: var(--r-pill); color: inherit;
  direction: ltr; text-align: left; unicode-bidi: plaintext;
}
.sm-hd-mag { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--color-neutral-500); }
.sm-hd-mag svg { display: block; }

.sm-hd-acts { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; justify-content: flex-end; margin-left: auto; }
.sm-hd-wrap { position: relative; display: flex; align-items: center; gap: var(--sp-1); flex: none; }

/* ⚠️ `.sm-ft-icon` (bascule de thème du PIED) partage cette règle plutôt que
   d'en recopier une : c'est le MÊME carré de 44 px, et le jour où la cible
   tactile change, les deux suivent. Le sélecteur est groupé, pas dupliqué —
   `CiblesTactilesTests` lit chaque membre du groupe séparément. */
.sm-hd-icon,
.sm-ft-icon {
  position: relative; width: var(--sm-tap-min); height: var(--sm-tap-min); flex: none;
  border: none; background: none; cursor: pointer; border-radius: var(--r-pill);
  display: grid; place-items: center; color: inherit; text-decoration: none;
}
.sm-hd-icon:hover,
.sm-ft-icon:hover { background: var(--color-neutral-200); }
.sm-hd-dot {
  position: absolute; top: 6px; right: 6px; min-width: 16px; height: 16px;
  border-radius: var(--r-pill); background: var(--color-accent); color: var(--color-bg);
  font-size: 10px; font-weight: 700; display: grid; place-items: center; padding: 0 3px;
}
.sm-hd-me {
  width: var(--sm-tap-min); height: var(--sm-tap-min); flex: none; border-radius: var(--r-round);
  background: var(--color-neutral-200); color: var(--color-neutral-700);
  display: grid; place-items: center; font-family: var(--font-heading);
  font-size: var(--fs-meta); text-decoration: none; border: none; cursor: pointer;
}
.sm-hd-me:hover { background: var(--color-neutral-300); }
.sm-hd-signin {
  flex: none; border: none; background: var(--color-accent); color: var(--color-bg);
  border-radius: var(--r-pill); padding: 12px 16px; font-family: inherit;
  font-size: var(--fs-ui); font-weight: 600; cursor: pointer; white-space: nowrap; text-decoration: none;
}
/* ⚠️ `min-height: var(--sm-tap-min)` sur les TROIS contrôles ci-dessous
   (« Créer un compte », le sélecteur de langue, son bouton « OK ») : la règle
   maison des 44 px vaut pour tout contrôle, et l'en-tête RÉDUIT de la famille
   « authentification » ne laisse que ceux-là. Mesurés à 38, 36 et 34 px, ils
   étaient les seuls sous le seuil sur la page la plus traversée du produit.
   Aucun coût de mise en page : `.sm-hd-icon` fait déjà 44 px — c'est LUI qui
   fixe la hauteur de la barre, et `--sm-shell-h` la DÉRIVE (67 px, 63 px sous
   640 px). */
.sm-hd-signup {
  flex: none; border: 1px solid var(--sm-border-field); background: none; color: inherit;
  border-radius: var(--r-pill); padding: 11px 15px; font-family: inherit;
  font-size: var(--fs-ui); font-weight: 600; cursor: pointer; white-space: nowrap; text-decoration: none;
  min-height: var(--sm-tap-min); display: inline-flex; align-items: center;
}

.sm-hd-menu {
  position: absolute; top: calc(100% + 7px); right: 0; width: min(260px, 84vw);
  background: var(--color-surface); border: 1px solid var(--color-divider);
  border-radius: var(--r-card); box-shadow: var(--shadow-lg); z-index: 80;
  overflow: hidden; padding: var(--sp-2);
}
.sm-hd-menu[hidden] { display: none; }
.sm-hd-menu a, .sm-hd-menu button {
  display: block; width: 100%; box-sizing: border-box; text-align: left;
  padding: 11px 12px; border-radius: 11px; text-decoration: none; color: inherit;
  font-family: inherit; font-size: var(--fs-body-sm); border: none; background: none; cursor: pointer;
}
.sm-hd-menu a:hover, .sm-hd-menu button:hover { background: var(--color-neutral-100); }
.sm-hd-mhead { padding: 10px 12px 8px; border-bottom: 1px solid var(--color-divider); margin-bottom: var(--sp-2); }
.sm-hd-nm { font-size: var(--fs-body-sm); font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; }
.sm-hd-sb { font-size: var(--fs-micro); color: var(--color-neutral-700); display: block; }

/* Sélecteur de langue — un vrai formulaire POST vers /i18n/setlang/ :
   il fonctionne sans JavaScript, l'îlot ne fait qu'y ajouter la soumission
   automatique au changement et masquer le bouton. */
.sm-hd-lang,
.sm-ft-lang { display: flex; align-items: center; gap: var(--sp-2); }
.sm-hd-lang select,
.sm-ft-lang select {
  font-family: inherit; font-size: var(--fs-ui); color: inherit;
  background: var(--color-surface); border: 1px solid var(--sm-border-field);
  border-radius: var(--r-pill); padding: 10px 10px; cursor: pointer;
  min-height: var(--sm-tap-min);
}
.sm-hd-lang button,
.sm-ft-lang button {
  font-family: inherit; font-size: var(--fs-ui); cursor: pointer;
  border: 1px solid var(--sm-border-field); background: none; color: inherit;
  border-radius: var(--r-pill); padding: 9px 12px;
  min-height: var(--sm-tap-min);
}

/* 🛑 REPÈRE DE FOCUS VISIBLE (WCAG 2.4.7) sur les commandes de préférence.
   Il est POSÉ, et non laissé à la feuille du navigateur : les deux commandes
   descendent dans un pied à fond `--color-surface`, où l'anneau par défaut de
   certains moteurs disparaît, et c'est exactement le motif du défaut trouvé au
   lot précédent (le champ de filtre du glossaire, dont l'`outline: none`
   vivait dans un `style=`). L'en-tête est inclus dans le même geste : deux
   commandes identiques n'ont pas à avoir deux comportements au clavier. */
.sm-hd-icon:focus-visible,
.sm-ft-icon:focus-visible,
.sm-hd-lang select:focus-visible,
.sm-ft-lang select:focus-visible,
.sm-hd-lang button:focus-visible,
.sm-ft-lang button:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 2px;
}

/* Seconde ligne facultative : un unique lien de retour.
 * ⚠️ **Enfant DIRECT de `<body>`, donc item de la colonne flex** — même piège
 * que `.sm-main` : sans `width: 100%`, ses marges `auto` annulent le `stretch`
 * et le lien de retour se recentre au milieu de l'écran au lieu de s'aligner
 * sur la gouttière de la page. */
.sm-hd-sub {
  flex: none;
  width: 100%; box-sizing: border-box;
  max-width: var(--sm-page-max); margin: 0 auto; padding: 0 20px 9px;
  display: flex; align-items: center; gap: var(--sp-6); flex-wrap: wrap;
  font-size: var(--fs-cap); color: var(--color-neutral-700);
}
.sm-hd-back { display: inline-flex; align-items: center; gap: var(--sp-2); text-decoration: none; color: var(--color-accent-800); font-weight: 600; font-size: var(--fs-meta); }
.sm-hd-back:hover { text-decoration: underline; }

@media (max-width: 1180px) { .sm-hd-word { display: none; } }
@media (max-width: 820px)  { .sm-hd-dest { display: none; } }
/* ⚠️ Le padding vertical vient du jeton, redéfini pour cette largeur dans
   `sm-tokens.css` : le recopier ici (`9px`) remettrait deux sources pour une
   grandeur, et `--sm-shell-h` mentirait de 4 px sous 640 px. */
@media (max-width: 640px)  { .sm-hd-bar { padding: var(--sm-hd-pad-y) 14px; gap: 9px; } .sm-hd-sub { padding: 0 14px 8px; } }

/* 🛑 LE CHAMP DE RECHERCHE À 0 PX — la fin d'un contrôle fantôme.
 *
 * Mesuré au navigateur sur `/fr/glossaire/` en téléphone : le `<form>` fait
 * 0 px de large et son `<input>` en fait 50 — dont 48 de padding et 2 de
 * bordure. La zone de SAISIE mesure donc zéro : un champ focalisable,
 * annoncé par son `placeholder`, et dans lequel on ne peut rien lire.
 *
 * La cause est arithmétique, pas cosmétique. Sous 640 px la rangée offre
 * 292 px à 320 et 332 px à 360. Le logo (28), les gouttières et le groupe de
 * droite (« Se connecter » : 111 en français) sont incompressibles ; quand un
 * écran occupe EN PLUS la fente d'actions (« Télécharger en PDF » : 131), il
 * ne reste rien — et à 320 px il manquait même 5 px. Le champ ne peut donc
 * pas coexister avec une fente occupée sur un téléphone.
 *
 * `display: none` et non une largeur nulle : un champ invisible mais
 * focalisable est un piège au clavier (un arrêt de tabulation qui ne montre
 * rien). Retiré, il ne coûte plus non plus sa gouttière — ce sont ces 9 px
 * qui font tenir la rangée à 320 px.
 *
 * ⚠️ La règle est DÉRIVÉE de l'attribut `data-sm-acts`, posé par
 * `_header.html` quand `statsmaker.coquille` lui donne un partiel d'actions.
 * Aucun écran n'est nommé ici : un écran qui gagnerait une fente d'actions
 * serait traité sans qu'on touche à cette feuille. Sur les écrans SANS fente,
 * le champ reste — et il est enfin utilisable, 126 px à 320 et 166 px à 360,
 * là où il en faisait 0. */
@media (max-width: 640px) { .sm-hd-bar[data-sm-acts] .sm-hd-search { display: none; } }

/* ── Barre d'onglets (téléphone seulement) ───────────────────────── */
.sm-tb {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 70; display: none;
  background: var(--color-surface); border-top: 1px solid var(--color-divider);
  box-shadow: var(--shadow-lg); padding: 4px 2px calc(4px + env(safe-area-inset-bottom));
}
.sm-tb a {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 2px; text-decoration: none; color: var(--color-neutral-600);
  font-size: 11px; font-weight: 600; min-height: 52px; justify-content: center;
  font-family: var(--font-body);
}
.sm-tb a[aria-current] { color: var(--color-accent-800); }
.sm-tb a[aria-current] .sm-tb-ic { background: var(--color-accent-100); }
.sm-tb-ic { width: 34px; height: 24px; border-radius: var(--r-pill); display: grid; place-items: center; }
@media (max-width: 820px) { .sm-tb { display: flex; } }

/* ── Pied compact (présent sur tous les écrans) ──────────────────── */
.sm-ft { flex: none; border-top: 1px solid var(--color-divider); margin-top: var(--sp-3); background: var(--color-surface); }
.sm-ft-in {
  max-width: var(--sm-page-max); margin: 0 auto; padding: 20px 20px 16px;
  display: flex; gap: var(--sp-9); flex-wrap: wrap; align-items: flex-start;
}
/* ⚠️ `margin-inline-end: auto` : sur l'accueil, la rangée à plat des six
   destinations n'est PAS rendue (la bande riche porte les liens groupés). C'est
   elle qui poussait, par son `flex: 1`, l'espace compte et les préférences vers
   la droite ; sans elle, tout se tassait à gauche et le pied de l'accueil ne
   ressemblait plus à celui des vingt autres écrans. La marge automatique tient
   la même mise en page dans les deux cas, sans condition dans le gabarit. */
.sm-ft-logo { display: flex; align-items: center; gap: 9px; text-decoration: none; color: inherit; margin-inline-end: auto; }
.sm-ft-mark {
  width: 30px; height: 30px; border-radius: 9px; background: var(--color-accent);
  color: var(--color-bg); display: grid; place-items: center;
  font-family: var(--font-heading); font-size: 15px;
}
.sm-ft-word { font-family: var(--font-heading); font-size: var(--fs-h4); }
.sm-ft-nav { display: flex; gap: var(--sp-3) 20px; flex-wrap: wrap; flex: 1; min-width: 200px; }
.sm-ft-nav a { font-size: var(--fs-ui); padding: 6px 0; }
/* ⚠️ `margin-inline-start: auto` : sur l'accueil, le logo du pied compact n'est
   pas rendu (la bande riche porte la marque). Sans cette marge, l'espace compte
   et les préférences se tassaient à gauche. Avec elle, ils restent à droite dans
   les deux cas — et sur les vingt autres écrans la marge vaut zéro, la rangée de
   destinations (`flex: 1`) ayant déjà absorbé l'espace libre. */
.sm-ft-acts { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-inline-start: auto; }
.sm-ft-acts a { padding: 10px 14px; font-size: var(--fs-ui); }
/* Préférences (thème, langue) — descendues de l'en-tête (D-A). Le pied est
   libre de se replier : il n'a pas de hauteur annoncée, contrairement à
   l'en-tête dont `--sm-shell-h` est une constante dérivée. `flex-wrap` y est
   donc autorisé, et c'est précisément pour ça que les deux commandes ont ce
   domicile-ci. */
.sm-ft-prefs { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: center; }
.sm-ft-copy {
  border-top: 1px solid var(--color-divider); padding: 12px 20px; text-align: center;
  font-size: var(--fs-cap); color: var(--color-neutral-700);
}
@media (max-width: 820px) { .sm-ft { margin-bottom: var(--sm-tabbar-h); } }

/* ── Base de page v2 (aucun actif commun avec le front v1) ───────── */
/* 🔴 **LE PIED DE PAGE FLOTTAIT AU MILIEU DE L'ÉCRAN.** Il suivait simplement
 * `<main>` dans le flux : sur une page courte — un fil vide, une fiche sans
 * rencontre, un formulaire de connexion, une erreur — il s'arrêtait là où le
 * contenu s'arrêtait, avec un aplat de fond en dessous. Le défaut dépendait de
 * la LONGUEUR DU CONTENU ET DE LA HAUTEUR DE L'ÉCRAN : invisible sur un
 * portable bien rempli, flagrant sur un grand écran de bureau. Il touchait les
 * 21 écrans, puisqu'il est dans la coquille.
 *
 * 🛑 **UNE COLONNE FLEX, PAS UN `position: fixed`.** Un pied fixé se
 * superposerait au contenu et volerait sa hauteur en permanence ; celui-ci
 * reste DANS le flux — il descend quand la page est courte, et se laisse
 * pousser quand elle est longue. C'est ce qui permet à un écran de grandir
 * dynamiquement (un îlot qui insère des résultats, un `<details>` qu'on ouvre,
 * un salon de chat qui reçoit un message) sans que le pied ne gêne quoi que ce
 * soit au-dessus de lui.
 *
 * ⚠️ **`100dvh` APRÈS `100vh`, et pas à sa place.** Sur un mobile, `100vh`
 * compte la barre d'adresse du navigateur comme si elle n'existait pas : la
 * page dépasse d'une soixantaine de pixels et gagne un défilement pour rien.
 * `dvh` suit la hauteur réelle. La déclaration `vh` reste **en premier** comme
 * repli : un moteur qui ignore `dvh` garde un pied correct.
 *
 * ⚠️ L'en-tête reste `position: sticky` : le collant fonctionne dans une
 * colonne flex tant qu'aucun ancêtre ne porte d'`overflow` — et aucun n'en
 * porte ici. La barre d'onglets mobile est `position: fixed`, donc hors flux :
 * elle n'entre pas dans la colonne. */
body.sm-v2 {
  margin: 0; background: var(--color-bg); color: var(--color-text);
  font-family: var(--font-body); font-size: var(--fs-body);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* ⚠️ La portée `.sm-v2` est enveloppée dans `:where()`, et ce n'est pas un
   détail d'écriture : `:where()` a une spécificité NULLE.

   Écrite `body.sm-v2 a`, la règle générique pèse (0,1,2) et bat donc TOUTE
   classe de composant posée sur un `<a>` : `.sm-hd-signin` (0,1,0) se
   retrouvait peint en bleu sur bleu — contraste 1:1, sur le bouton le plus
   important d'un produit fermé. Le mécanisme d'isolation, juste dans son
   intention, gonflait la spécificité de tout ce qu'il préfixait.

   Avec `:where(body.sm-v2) a`, la règle pèse (0,0,1) — sa spécificité réelle.
   L'isolation est conservée (aucun écran v1 n'est touché) et c'est l'ORDRE des
   feuilles qui la fait primer sur `ds-tokens.css`, pas un artifice.

   Le `:hover` suit le même principe : `a:hover` pèse (0,1,1) et rebattrait
   `.sm-hd-signin` au survol ; `a:where(:hover)` pèse (0,0,1), comme la règle
   de base, et s'applique par l'ordre d'écriture.

   ➤ Toute règle générique ajoutée ici doit suivre ce motif —
     `football/tests/test_v2_css.py` échoue sinon. */
:where(body.sm-v2) a { color: var(--color-accent); }
:where(body.sm-v2) a:where(:hover) { color: var(--color-accent-700); }
:where(body.sm-v2) button { font-family: inherit; }
:where(body.sm-v2) ::placeholder { color: var(--color-neutral-600); }   /* F-5 */
@media print { .no-print { display: none !important; } body.sm-v2 { background: #fff; } }

/* Bascule de thème : les deux icônes sont dans le HTML, le CSS choisit —
   aucun calcul au chargement, donc aucun scintillement. */
.sm-ic-lune { display: none; }
html[data-sm-theme="dark"] .sm-ic-soleil { display: none; }
html[data-sm-theme="dark"] .sm-ic-lune { display: block; }

/* Panneau de suggestions de recherche (créé par l'îlot `nav-recherche`). */
/* `.sm-hd-note` partage la géométrie du panneau — même ancrage, même boîte —
   mais PAS son `role="listbox"` : c'est un message d'état (quota atteint), et
   un texte placé dans une liste d'options serait annoncé comme une option. Les
   deux ne sont jamais visibles en même temps. */
.sm-hd-panel, .sm-hd-note {
  position: absolute; top: calc(100% + 7px); left: 0; right: 0; min-width: 300px;
  background: var(--color-surface); border: 1px solid var(--color-divider);
  border-radius: var(--r-card); box-shadow: var(--shadow-lg);
  padding: var(--sp-2); z-index: 80; max-height: 66vh; overflow: auto;
}
.sm-hd-panel[hidden], .sm-hd-note[hidden] { display: none; }
.sm-hd-note {
  margin: 0; padding: var(--sp-3);
  font-size: var(--fs-meta); color: var(--color-neutral-700); text-wrap: pretty;
}
.sm-hd-row {
  display: block; padding: 8px 10px; border-radius: var(--r-sm);
  text-decoration: none; color: inherit; min-height: var(--sm-tap-min); box-sizing: border-box;
}
.sm-hd-row:hover, .sm-hd-row:focus-visible { background: var(--color-neutral-100); }
/* Option courante du combobox de recherche (motif APG « Editable Combobox With
   List Autocomplete ») : le focus DOM reste sur le champ, la position est
   portée par `aria-activedescendant`. Sans repère VISIBLE, un utilisateur au
   clavier voyant naviguerait à l'aveugle — l'attribut ne parle qu'aux
   technologies d'assistance. Spécificité (0,2,0) : au-dessus de `:hover`
   (0,2,0 également) mais déclarée après, donc elle gagne. */
.sm-hd-row[aria-selected="true"] {
  background: var(--color-neutral-100);
  outline: 2px solid var(--color-accent); outline-offset: -2px;
}

/* ── Correctifs d'accessibilité appliqués aux classes venues de `ds-tokens.css`
   (design §F.3 F-4 et §F.6). Portés ICI, sous `:where(body.sm-v2)`, pour deux
   raisons : `ds-tokens.css` est un fichier importé tel quel de la maquette, et
   la portée `.sm-v2` garantit qu'aucun écran v1 n'est touché. La portée est
   neutralisée par `:where()` (voir plus haut) : ces règles pèsent (0,1,0),
   comme les classes qu'elles corrigent, et priment par l'ordre des feuilles. ── */
:where(body.sm-v2) .input,
:where(body.sm-v2) .seg,
:where(body.sm-v2) .btn-secondary,
:where(body.sm-v2) .radio .dot,
:where(body.sm-v2) select,
:where(body.sm-v2) textarea {
  /* F-4 : `--color-divider` (1,31:1) ne peut pas être la SEULE limite visible
     d'un contrôle — 1.4.11 exige 3:1.
     F-4ter : la liste est celle de TOUS les contrôles bordés de
     `ds-tokens.css` — `.btn-secondary` et `.radio .dot` y manquaient, alors
     que le design nommait « tous les formulaires ». Un contrôle bordé ajouté
     à `ds-tokens.css` doit être ajouté ICI ; `BordureDesControlesTests`
     (`football/tests/test_v2_css.py`) le vérifie sur la couleur RÉELLEMENT
     PEINTE — cascade des cinq feuilles résolue — et non sur le jeton. */
  border-color: var(--sm-border-field);
}
/* ── Affordance : une CASE À COCHER n'est pas un bouton radio (partie 8 §9).
   `ds-tokens.css` peint `.radio .dot` en pastille ronde, et la classe sert des
   deux côtés : les quatre cases du produit (« Rester connecté », les CGU, les
   douze compétitions de la mise en route, le consentement marketing) sont donc
   rendues rondes, c'est-à-dire avec l'affordance d'un choix EXCLUSIF. Sur une
   grille de douze, au premier écran d'un client, le contresens se paie.

   La règle porte sur `input[type="checkbox"]` : elle ne peut pas atteindre un
   vrai bouton radio, et elle n'a rien à savoir de l'écran qui l'emploie — un
   correctif par gabarit aurait été à recopier à chaque nouvelle case.

   ⚠️ La coche remplace le disque intérieur de `ds-tokens.css` (`box-shadow`
   inset) : sans `box-shadow: none`, l'état coché superposerait un anneau et
   une coche. La bordure n'est pas touchée — `--sm-border-field` (F-4) reste
   posé par le bloc ci-dessus. ── */
:where(body.sm-v2) .radio input[type="checkbox"] + .dot {
  position: relative;
  border-radius: var(--sm-case-radius);
}
:where(body.sm-v2) .radio input[type="checkbox"]:checked + .dot {
  box-shadow: none;
}
:where(body.sm-v2) .radio input[type="checkbox"]:checked + .dot::after {
  /* Une coche tracée avec deux bords d'une boîte vide, tournée de 45° : aucune
     image, aucune police d'icônes, donc rien à charger en 3G. */
  content: "";
  position: absolute;
  inset-block-start: 15%;
  inset-inline-start: 32%;
  width: 25%;
  height: 55%;
  border: solid var(--color-bg);
  border-width: 0 var(--sm-case-marque) var(--sm-case-marque) 0;
  transform: rotate(45deg);
}
:where(body.sm-v2) .input:hover {
  /* F-4quater : `ds-tokens.css` pose `.input:hover` à 2,95:1. Même poids
     (0,2,0) que la règle corrigée, donc c'est l'ORDRE des feuilles qui
     départage — comme pour le bloc de repos ci-dessus. Mesuré par
     `BordureDesControlesTests`, qui résout désormais aussi l'état de survol :
     un état qui dégrade la limite du repos est un échec 1.4.11 à part
     entière. */
  border-color: var(--sm-border-field-hover);
}
:where(body.sm-v2) .btn,
:where(body.sm-v2) .input,
:where(body.sm-v2) select {
  /* F.6 : cible tactile de 44 px (36 px dans la maquette).
     ⚠️ `select` a été AJOUTÉ à la liste (partie 8 §9.2) : il en était exclu,
     et il fait donc moins de 44 px partout. Le correctif est additif, et il
     répare DEUX contrôles d'un coup — la liste « Vous êtes » de l'inscription,
     et le sélecteur de langue de l'en-tête, qui est présent sur toutes les
     pages v2 et était déjà signalé. Un contrôle de formulaire ajouté à la
     coquille doit rejoindre cette liste. */
  min-height: var(--sm-tap-min);
}


/* ── Contrôle segmenté partagé (« Tous · Jaunes · Rouges ») ────────────────
 *
 * Il vivait dans `sm-compare.css` tant que Compare en était le seul
 * consommateur. L'écran 13 en porte deux ; il est donc transverse, et il vit
 * ici — la même décision, et pour la même raison, que le bloc de périmètre
 * parti dans `sm-scope.css`.
 *
 * ⚠️ L'état actif se lit sur `[aria-current]`, quelle que soit sa valeur — et
 * les trois écrans écrivent désormais `page`. Les options sont des LIENS (le
 * serveur refiltre), et la valeur juste pour un lien de navigation est `page` ;
 * Compare écrivait `true`, seul de son espèce. Les deux gestes sont faits : la
 * divergence est corrigée à la source, ET le sélecteur reste tolérant, parce
 * qu'un écran qui s'allume et pas l'autre est un défaut purement visuel —
 * donc de ceux qu'aucune suite de tests ne voit.
 *
 * F-4 : c'est un CONTRÔLE, donc `--sm-border-field` (3:1) et non
 * `--color-divider` (1,31:1). */
.sm-seg {
  display: inline-flex; border: 1px solid var(--sm-border-field);
  border-radius: var(--r-pill); overflow: hidden;
}
.sm-seg-opt {
  padding: var(--sp-3) var(--sp-6); min-height: var(--sm-tap-min);
  display: flex; align-items: center;
  font-size: var(--fs-meta); font-weight: 600; text-decoration: none;
  color: var(--color-neutral-700);
}
.sm-seg-opt[aria-current] { background: var(--color-accent); color: var(--color-bg); }


/* ── Pagination des écrans denses ────────────────────────────────────────
 *
 * Balisage partagé : `templates/v2/partials/_pagination.html`.
 *
 * Elle vivait dans `sm-discipline.css` tant que Discipline en était le seul
 * consommateur. Dix écrans denses restent à écrire et pagineront tous ; une
 * classe partagée qui reste dans la feuille d'UN écran oblige les suivants à
 * charger cette feuille — ou, ce qui arrive toujours, à la recopier.
 *
 * ⚠️ `.sm-pg-inactif` habille un `<span>`, jamais un `<a>` désactivé : le
 * partiel explique pourquoi, et la nuance est d'accessibilité, pas de style.
 * L'opacité seule ne suffirait donc pas à la reproduire. */
.sm-pg {
  display: flex; align-items: center; justify-content: center;
  gap: var(--sp-6); flex-wrap: wrap; margin: var(--sp-7) var(--sp-7) 0;
}
/* Les deux flèches faisaient 36 px et portaient `.sm-touch`, dont le
 * pseudo-élément débordait de 4 px de chaque côté. Ce partiel est inclus par
 * TOUS les écrans denses : le défaut était minime et partout à la fois. Les
 * 8 px manquants sont pris dans le bouton (R-T, corollaire 2). */
.sm-pg .btn { min-height: var(--sm-tap-min); }
.sm-pg-etat { font-size: var(--fs-ui); color: var(--color-neutral-700); }
.sm-pg-inactif {
  font-size: var(--fs-ui); color: var(--color-neutral-600); opacity: .55;
}


/* ══════════════════════════════════════════════════════════════════════════
   LES SIX MOTIFS DE PAGE PARTAGÉS (design §3.2, arbitrage A-13)
   ══════════════════════════════════════════════════════════════════════════

   🛑 POURQUOI ILS SONT ICI ET NON DANS `sm-accueil.css`.

   Cinq feuilles d'écran redéfinissent DÉJÀ chacune « une carte »
   (`sm-glossaire.css`, `sm-compare.css`, `sm-info.css` ×2, `sm-recherche.css`).
   Une sixième définition dans la feuille de l'accueil aurait fait six copies
   d'une même chose, qui divergent à la première retouche. Neuf écrans suivent :
   c'est une dette ÷ 9, pas × 9.

   ⚠️ Le rattrapage des cinq feuilles existantes n'est PAS fait dans ce lot :
   aucun pixel n'y change, et rouvrir cinq écrans livrés pour une mise en
   commun cosmétique est exactement ce qu'un lot ne doit pas faire.

   ⚠️ `.sm-grid` était DÉJÀ REPLIÉE par deux requêtes de média de
   `sm-theme.css` — deux règles qui corrigeaient une classe qui n'existait
   nulle part. La maquette posait les colonnes en style en ligne, interdit ici.
   ────────────────────────────────────────────────────────────────────────── */

.sm-grid  {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--sm-aside-w);
  gap: var(--sp-9);
}
.sm-grid2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-8);
}

/* L'élévation par défaut du produit. */
.sm-carte {
  background: var(--color-surface); border-radius: var(--r-card);
  box-shadow: var(--shadow-sm); padding: var(--sp-7);
}
/* La carte porte une LISTE : ce sont les lignes qui portent la marge.
   ⚠️ `overflow:hidden` est RÉSERVÉ aux cartes dont les enfants ne sont pas
   focalisables — sinon l'anneau de focus des lignes de bord est rogné. */
.sm-carte--plat { padding: 0; overflow: hidden; }

/* L'en-tête de section : un titre, et « voir tout → ». */
.sm-sec { margin-top: var(--sp-10); }
.sm-sec-tete {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-5);
}
.sm-sec-h { margin: 0; font-size: var(--fs-h2-sec); font-family: var(--font-heading); }
.sm-sec-voir {
  margin-left: auto; font-size: var(--fs-meta); font-weight: 700;
  color: var(--color-accent-700); text-decoration: none;
  /* ⚠️ 13 px et NON 12 : mesuré au navigateur, la ligne de `--fs-meta` fait
     19,x px et non 20, et la cible tombait à 43 px — un seuil manqué d'un pixel
     est manqué. C'est le même pixel que celui déjà payé sur la pastille
     d'équipe de Discipline. */
  padding-block: 13px;
}
.sm-sec-voir:hover { text-decoration: underline; }

/* La suréchelle typographique : intitulé de rubrique, jamais un titre. */
.sm-rubrique {
  font-size: var(--fs-cap); font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--color-neutral-700);
  margin: 0 0 var(--sp-3);
}

/* L'état vide (§G.9).
   ⚠️ `--sm-border-field` (5,00:1) et NON `--color-divider` (1,31:1) : un
   pointillé à 1,31:1 est invisible. Il ne borde aucun CONTRÔLE, il ne rejoint
   donc pas `CONTROLES_BORDES` — mais il n'échoue pas non plus 1.4.11. */
.sm-vide {
  background: var(--color-surface); border: 1px dashed var(--sm-border-field);
  border-radius: var(--r-card); padding: var(--sp-10) var(--sp-9);
  text-align: center; color: var(--color-neutral-700);
  font-size: var(--fs-body-sm); text-wrap: pretty;
}
.sm-vide-h {
  font-family: var(--font-heading); font-size: var(--fs-h3);
  color: var(--color-text); margin: 0 0 var(--sp-2);
}


/* ── La PASTILLE D'ÉQUIPE — sept écrans, un seul dessin ──────────────────
 *
 * Balisage partagé : `templates/v2/partials/_pastille_equipe.html`.
 *
 * 🛑 R-5, LA DÉCISION DE FORME. `Equipe.initiales` accepte CINQ caractères
 * quand la déduction en rend TROIS. La pastille TOLÈRE les cinq : `min-width`
 * plutôt que `width`, et le rond s'étire en ellipse au-delà de trois lettres.
 * Tronquer à trois aurait affiché « ASE » pour une équipe dont le client vient
 * d'écrire « ASECM » — à l'endroit exact où le champ existe pour lui laisser
 * corriger la déduction. La règle de forme du design tient : c'est un rond
 * étiré, jamais un carré (le carré arrondi dit « une organisation »).
 *
 * `neutral-800` sur `neutral-200` : 10,80:1, rampe inversée en sombre.
 *
 * ⚠️ La variante `--tap` reprend, à l'identique, ce que `sm-discipline.css`
 * portait sous `.sm-disc-equipe` : dans un TABLEAU, la pastille EST la cible
 * tactile, elle garde donc ses 44 px. Le `min-height` explicite n'est pas
 * décoratif : en `--fs-cap` (12 px), la pastille tombait à 43 px à rembourrage
 * égal — et un seuil manqué d'un pixel est manqué. */
/* 🛑 `--sm-pastille-taille` EST LE POINT DE VÉRITÉ DE LA BOÎTE, et il existe
 * pour une raison précise : trois feuilles redimensionnent cette pastille
 * (`sm-competition.css` la passe à 40 px en `.sm-pastille-lg`, à 26 px dans les
 * classements individuels). Chacune écrivait `width`/`height` ; le jour où une
 * quatrième règle a eu besoin de la MÊME mesure — la largeur définie que
 * l'écusson exige, ci-dessous — il n'y avait aucun endroit où la lire. La
 * variable se surcharge par la cascade, sans course à la spécificité. */
.sm-pastille {
  --sm-pastille-taille: var(--sm-pastille);
  display: inline-grid; place-items: center;
  min-width: var(--sm-pastille-taille); height: var(--sm-pastille-taille);
  padding-inline: 4px; border-radius: var(--r-round);
  background: var(--color-neutral-200); color: var(--color-neutral-800);
  font-family: var(--font-heading); font-size: var(--fs-cap);
  text-decoration: none;
}
.sm-pastille--tap {
  min-width: 34px; height: auto; min-height: var(--sm-tap-min);
  padding-inline: var(--sp-3); border-radius: var(--r-pill);
  background: var(--color-neutral-100);
}
.sm-pastille:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 2px;
  border-radius: var(--r-xs);
}

/* ── L'ÉCUSSON DANS LA PASTILLE (lot 3 du peigne fin) ──────────────────────
 * 🔴 La v2 avait remplacé tous les écussons par des initiales, alors que le
 * modèle porte `Equipe.logo` et que la v1 le servait sur vingt-cinq gabarits.
 * Sur un produit de football, un blason se reconnaît d'un coup d'œil là où
 * « AVC » demande une lecture — et deux clubs peuvent rendre le même sigle.
 *
 * ⚠️ L'image ne remplit PAS la pastille : `contain` la contient et laisse le
 * fond neutre autour. Un `cover` recadrerait des blasons dont la forme varie du
 * cercle à l'écu — et un écusson rogné n'est plus reconnaissable, ce qui
 * annulerait tout l'intérêt de le servir.
 *
 * ══════════════════════════════════════════════════════════════════════════
 * 🔴 LE DÉFAUT QUE CE BLOC A INTRODUIT, ET QUI EST CORRIGÉ ICI
 * ══════════════════════════════════════════════════════════════════════════
 *
 * `.sm-pastille` déclarait `min-width` et **pas** `width`. Sa largeur restait
 * donc `auto`, c'est-à-dire dimensionnée sur le contenu — ce qui convenait
 * parfaitement à deux ou cinq lettres, et pas du tout à une image.
 *
 * Une image en `width: 100%` dans une boîte de largeur INDÉFINIE : la
 * proportion ne peut pas se résoudre, elle est traitée comme `auto` pour le
 * calcul des contributions intrinsèques, et la contribution retenue est la
 * largeur NATURELLE DU FICHIER. Mesuré sur la base : `art_icon.png` fait
 * 512 × 512. La pastille prenait donc 512 px de large sur 24 de haut, la piste
 * `auto` de `.sm-mt-eq` se dimensionnait dessus, la colonne du nom
 * (`minmax(0, 1fr)`) s'écrasait à zéro — et comme la règle suivante retire le
 * fond, l'écusson se lisait comme un décor derrière la carte.
 *
 * Cinq écrans étaient touchés (accueil, explore, équipe, joueur, match) ; le
 * seul épargné était Compétition, parce que `sm-competition.css` redéfinit
 * `.sm-pastille` **avec un `width`**. C'est-à-dire que l'écran qui allait bien
 * allait bien par accident.
 *
 * Le correctif est de donner une largeur DÉFINIE dès qu'il y a une image —
 * jamais dans le cas du sigle, dont l'élasticité est voulue (R-5 : cinq
 * caractères tolérés, le rond s'étire en ellipse). Les deux cas restent donc
 * distincts, et c'est le `:has()` qui les sépare.
 *
 * ⚠️ Les attributs `width`/`height` de l'`<img>` (posés par
 * `_pastille_equipe.html`) ne suffisaient pas et ne suffiraient pas : le
 * `width: 100%` du CSS les écrase. Ils servent à réserver la place avant
 * l'arrivée du fichier, ce qui est un second défaut, pas le même. */
.sm-pastille-img {
  width: 100%; height: 100%;
  object-fit: contain;
  border-radius: inherit;
  display: block;
}

/* La pastille qui porte une image n'a plus besoin de son fond ni de son
   rembourrage : ils servaient à asseoir deux lettres. Et elle prend une largeur
   DÉFINIE — voir le pavé ci-dessus. `flex: none` la protège en contexte flex,
   où un conteneur étroit la comprimerait sous sa boîte carrée. */
.sm-pastille:has(.sm-pastille-img) {
  width: var(--sm-pastille-taille);
  min-width: var(--sm-pastille-taille);
  flex: none;
  background: transparent;
  padding-inline: 0;
}

/* La variante tactile est le seul cas où la boîte n'est PAS carrée : 34 px de
   large au minimum pour 44 de haut. Une image y serait étirée par la règle
   ci-dessus ; on lui rend sa boîte carrée de 24 px, centrée dans la cible. */
.sm-pastille--tap:has(.sm-pastille-img) {
  width: auto; min-width: 34px;
  padding-inline: var(--sp-3);
}
.sm-pastille--tap .sm-pastille-img {
  width: var(--sm-pastille-taille);
  height: var(--sm-pastille-taille);
}

/* ── LA FORME DIT LA NATURE DE L'ENTITÉ (lot D) ────────────────────────────
 * Règle de forme du design : rond = une équipe ou une personne, carré arrondi
 * = une ORGANISATION. Une compétition en est une, et la distinction n'est pas
 * cosmétique : sur l'écran Match, le logo de la compétition et les écussons
 * des deux clubs se retrouvent à quelques centimètres l'un de l'autre.
 *
 * ⚠️ Un MODIFICATEUR, et pas une seconde classe de boîte : la taille, la
 * largeur définie sous `:has()`, le fond et la police sont les mêmes — seul
 * l'arrondi change. Une `.sm-blason` complète aurait recopié quatre règles
 * pour en changer une, et les trois autres auraient divergé au premier
 * correctif.
 *
 * ⚠️ `--r-chip` (6 px) et non `--r-sm` (12 px) : sur une boîte de 24 px, un
 * rayon de 12 px EST un cercle. Le jeton qui dit « carré arrondi » dépend donc
 * de la taille de la boîte, et c'est la plus petite qui décide — 6 px se lit
 * comme un carré à 24 px et le reste à 40. */
/* ⚠️ **Sélecteur DOUBLÉ, et ce n'est pas un tic.** Les feuilles d'écran sont
 * chargées APRÈS `sm-shell.css` et plusieurs y écrivent `border-radius: 50%`
 * sur leur propre classe de pastille (`.sm-ent-ini`, `.sm-agt-joueur-ini`…).
 * À spécificité égale, la dernière l'emporte : le carré arrondi redevenait un
 * rond dès qu'une compétition portait une classe d'écran, c'est-à-dire
 * exactement là où la distinction sert. */
.sm-pastille.sm-pastille--org { border-radius: var(--r-chip); }

/* ⚠️ **Un portrait se RECADRE, un écusson non.** `.sm-pastille-img` contient
 * l'image (`contain`) : la forme d'un blason varie du cercle à l'écu, et un
 * blason rogné n'est plus reconnaissable. Un visage, lui, est centré et
 * supporte de perdre ses bords — le contenir laisserait deux bandes de fond
 * dans un rond. Les deux règles sont opposées, et c'est voulu ; c'est aussi la
 * seule divergence que les six copies de portrait des fiches avaient toutes
 * écrite pareil, ce qui la rendait sûre à remonter ici. */
.sm-pastille--visage .sm-pastille-img { object-fit: cover; }

/* ── LE DRAPEAU D'UN PAYS — une seule fois pour tout le produit (lot D) ────
 * 🔴 Cette règle était écrite DEUX fois à l'identique (`sm-competition.css`,
 * `sm-equipe.css`), et six écrans en portaient une variante sous leur propre
 * nom (`sm-jou-drapeau`, `sm-arb-drapeau`, `sm-ent-drapeau`, `sm-agt-drapeau`,
 * `sm-but-drapeau`, `sm-comp-drapeau`). `sm-competition.css` annonçait
 * lui-même la montée : « le jour où une troisième surface en aura besoin, la
 * règle montera dans `sm-shell.css` plutôt que d'être recopiée une fois de
 * plus. » Ce jour est celui-ci — `v2/partials/_drapeau_pays.html` en fait la
 * septième.
 *
 * ⚠️ `contain`, et NON `cover` — c'est le point où les copies divergeaient.
 * Les quatre fiches de personne argumentaient `contain` (« un drapeau rogné ne
 * se reconnaît plus ») ; les deux copies partagées écrivaient `cover` sans
 * l'expliquer. C'est le même argument que l'écusson, et il tranche : une boîte
 * de 4:3 recadre un drapeau de 3:2 en lui coupant un quart de sa largeur, et
 * un tricolore amputé d'une bande n'est plus le même drapeau.
 *
 * Le filet à 1 px détache les drapeaux à bande blanche (Nigeria, Sierra Leone)
 * du fond de la carte. Un drapeau de 16 px ne distingue pas le Tchad de la
 * Roumanie et il est muet pour un lecteur d'écran : le nom du pays reste écrit
 * à côté, partout, et c'est pourquoi le partiel ne rend RIEN quand l'image
 * manque — il n'y a aucun trou à combler. */
.sm-drapeau {
  width: 16px;
  height: 12px;
  object-fit: contain;
  border-radius: 2px;
  border: 1px solid var(--color-divider);
  vertical-align: -1px;
  margin-inline-end: 4px;
}



/* ── LA CARTE DE MATCH — six écrans, un seul dessin ──────────────────────
 *
 * Balisage partagé : `templates/v2/partials/_carte_match.html`.
 *
 * C'est la surface la plus vue du produit (§D.5) et le porteur du verrou de
 * score. Elle vit ici pour la même raison que `.sm-seg` et `.sm-pg` : Explore,
 * Competition, Team, Player et Match la rendront, et une classe partagée
 * laissée dans la feuille d'UN écran oblige les suivants à charger cette
 * feuille — ou, ce qui arrive toujours, à la recopier.
 *
 * 🛑 LE LIEN ÉTIRÉ. La carte est un `<div>` et non un `<a>` : le verrou de
 * score EST un lien (sur mobile c'est le seul chemin) et un `<a>` dans un `<a>`
 * est invalide. Le lien du match couvre donc la carte par son `::after`, et le
 * verrou passe au-dessus en `z-index`. Deux points d'arrêt au clavier, dans
 * l'ordre du document, chacun annonçant ce qu'il ouvre. */
.sm-mt {
  position: relative;
  display: grid; grid-template-columns: 64px minmax(0, 1fr) 18px;
  gap: var(--sp-5); align-items: center;
  background: var(--color-surface); border-radius: var(--r-card);
  box-shadow: var(--shadow-sm); padding: var(--sp-6) var(--sp-7);
  color: inherit;
}
.sm-mt + .sm-mt { margin-top: var(--sp-5); }
.sm-mt:hover { box-shadow: var(--shadow-md); }
.sm-mt-lien { color: inherit; text-decoration: none; }
.sm-mt-lien::after { content: ''; position: absolute; inset: 0; }
.sm-mt-lien:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 2px;
  border-radius: var(--r-xs);
}
/* Le verrou repasse AU-DESSUS du lien étiré : sans cela il serait recouvert et
   deviendrait incliquable — exactement le contraire de ce que §D.5 demande. */
.sm-mt .sm-score--locked { position: relative; z-index: 1; }

/* Colonne fixe : c'est elle qui fait que les listes s'alignent d'un écran à
   l'autre. La MAJUSCULE est en CSS, jamais dans le msgid — sinon l'anglais
   casse et la traduction double. */
.sm-mt-statut {
  display: block; text-align: center;
  font-size: var(--fs-cap); font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--color-neutral-700);
}
.sm-mt-statut--live    { color: var(--sm-loss); }
.sm-mt-statut--mitemps { color: var(--sm-draw-text); }   /* jamais `--sm-draw` */
.sm-mt-statut--termine { color: var(--color-neutral-700); }
.sm-mt-statut--avenir  { color: var(--color-accent-2-700); }
.sm-mt-statut--annule  { color: var(--color-neutral-600); }
.sm-mt-heure {
  display: block; margin-top: 3px; text-transform: none; letter-spacing: 0;
  font-family: var(--font-heading); font-size: 15px; color: var(--color-text);
}

.sm-mt-corps { min-width: 0; }
/* ⚠️ La ligne est passée de `block` à `flex` pour accueillir le badge vidéo à
   sa droite. L'ellipse a suivi sur le LIEN : posée sur le conteneur flex, elle
   n'aurait rien tronqué (un élément flex ne déborde pas de son parent, il le
   pousse) et « Confédération Africaine de Football · J14 » aurait chassé le
   badge hors de la carte. */
.sm-mt-comp {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: var(--fs-cap); color: var(--color-neutral-700);
  margin-bottom: 7px;
}
.sm-mt-comp > .sm-mt-lien {
  min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Le badge vidéo : une existence, jamais une promesse d'accès. Neutre et non
   accentué — il ne doit pas se lire comme un bouton, la carte entière étant
   déjà le lien. `flex: none` : c'est le nom de la compétition qui se tronque,
   jamais lui. */
.sm-mt-video {
  display: inline-flex; align-items: center; flex: none;
  color: var(--color-neutral-700);
}
.sm-mt-video svg { display: block; }
/* L'issue du match (T.A.B., après prolongation). Sous les deux lignes
   d'équipes, alignée sur le score — c'est de lui qu'elle corrige la lecture. */
.sm-mt-issue {
  display: block; margin-top: 5px; text-align: right;
  font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-neutral-700);
}
.sm-mt-eq {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 9px; row-gap: 5px; align-items: center;
}
/* `min-width: 0` est INDISPENSABLE : sans lui, une piste de grille refuse de
   descendre sous la largeur de son contenu, et « Mamelodi Sundowns FC » fait
   déborder la page à 390 px. */
.sm-mt-nom {
  min-width: 0; font-size: var(--fs-body); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sm-mt-score {
  font-family: var(--font-heading); font-size: var(--fs-num-md);
  text-align: right;
}
/* 🛑 LE POINT DE §D.5 : deux lignes d'équipe, UNE seule pastille de verrou. */
.sm-mt-eq > .sm-score--locked {
  grid-column: 3; grid-row: 1 / span 2; align-self: center;
}
.sm-mt-chev { color: var(--color-neutral-500); text-align: center; }

@media (max-width: 420px) {
  .sm-mt { grid-template-columns: 56px minmax(0, 1fr); padding: var(--sp-5) var(--sp-6); }
  /* Le chevron est DÉCORATIF : la carte entière est le lien. */
  .sm-mt-chev { display: none; }
}

/* Une carte qui porte une liste : c'est la liste qui perd ses puces, pas la
   carte qui gagne une règle de plus. */
.sm-liste { list-style: none; margin: 0; padding: 0; }


/* ── LA BANDE RICHE DU PIED — rendue sur le seul accueil, DÉCLARÉE ICI ────
 *
 * Balisage : `templates/v2/partials/_footer.html`, sous `{% if pied_riche %}`.
 *
 * 🛑 ELLE NE PEUT PAS VIVRE DANS `sm-accueil.css`, ET LE GARDE-FOU L'A DIT.
 * `_footer.html` est un partiel PARTAGÉ : `test_aucune_classe_emise_n_est_sans_feuille`
 * a fait rougir `v2/glossaire.html` — un gabarit qui n'ouvre jamais la bande,
 * mais qui inclut le partiel qui la porte, et qui ne charge pas la feuille de
 * l'accueil. Les classes ne peignaient donc rien pour lui. La règle générale
 * est celle-ci : **le style d'un partiel partagé vit dans une feuille
 * partagée**, quel que soit le nombre d'écrans qui l'activent aujourd'hui.
 *
 * Ce n'est PAS un second pied (arbitrage A-8) : c'est une bande AU-DESSUS du
 * pied compact. Écrire un `_footer_riche.html` séparé aurait dupliqué la
 * bascule de thème, le sélecteur de langue et l'espace compte dans un second
 * FICHIER — quatre divergences en attente.
 *
 * 🔴 CINQ COLONNES TITRÉES, ET NON DEUX ZONES. La bande portait « Rester
 * informé » et « Nous suivre » pendant que les six destinations tombaient en
 * rangée à plat dans le pied compact et que les liens légaux se noyaient dans
 * la ligne de copyright : aucune hiérarchie, sur le dernier écran que voit un
 * visiteur. On reprend le découpage de la maquette — marque · Explorer ·
 * Aide & infos · Légal · Rester informé. « Nous suivre » a été retirée : les
 * quatre comptes n'existent pas (motif complet dans `statsmaker/coquille.py`).
 *
 * ⚠️ `repeat(auto-fit, minmax(240px, 1fr))` NE POUVAIT PAS tenir cinq colonnes :
 * 5 × 240 + 4 gouttières dépassent les 1120 px de `--sm-page-max`, la grille
 * retombait donc à quatre pistes et la dernière colonne passait à la ligne,
 * seule. Les proportions sont donc DÉCLARÉES : la marque et l'inscription à la
 * lettre d'information sont plus larges que trois listes de liens courts. */
.sm-ftx { flex: none; border-top: 1px solid var(--color-divider); background: var(--color-surface); }
.sm-ftx-in {
  max-width: var(--sm-page-max); margin: 0 auto;
  padding: var(--sp-10) var(--sp-9);
  display: grid;
  grid-template-columns:
    minmax(0, 1.3fr) repeat(3, minmax(0, 1fr)) minmax(0, 1.7fr);
  gap: var(--sp-9) var(--sp-8);
  align-items: start;
}
.sm-ftx-col { min-width: 0; }
.sm-ftx-logo { display: flex; align-items: center; gap: 9px; }
/* La baseline REPREND le `<h1>` de l'accueil : zéro `msgid` neuf, et les mots
   du produit plutôt que ceux d'une maquette. Bornée à 30 caractères de large :
   au-delà, une ligne de pied de page se lit comme un paragraphe. */
.sm-ftx-base {
  margin: var(--sp-4) 0 0; max-width: 30ch;
  font-size: var(--fs-cap); color: var(--color-neutral-700); text-wrap: pretty;
}
/* Titre de colonne. Il TITRE vraiment un groupe de liens — d'où un `<h2>` — mais
   il a son propre dessin et n'emprunte plus `.sm-rubrique`, qui est une
   suréchelle de contenu et non de navigation. */
.sm-ftx-h {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-cap); font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--color-neutral-700);
}
.sm-ftx-liens {
  display: flex; flex-direction: column;
  margin: 0; padding: 0; list-style: none;
}
/* 44 px de haut : ce sont des liens de pied de page sur un produit consulté au
   pouce, et 2.5.5 ne fait pas d'exception pour le bas de page. Empilés, ils
   forment une colonne facile à viser — c'est l'inverse de la rangée à plat
   qu'ils remplacent, où les cibles se touchaient. */
.sm-ftx-liens a {
  display: inline-flex; align-items: center;
  min-height: var(--sm-tap-min); font-size: var(--fs-ui);
}
.sm-ftx-nl { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.sm-ftx-nl .input { flex: 1 1 180px; min-width: 0; }
/* La cible est le `<label>` ENTIER, pas la seule case. Non pré-cochée : un
   consentement pré-coché n'en est pas un. */
.sm-ftx-consent {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--sm-tap-min); margin: var(--sp-3) 0 0;
  font-size: var(--fs-cap); color: var(--color-neutral-700); text-wrap: pretty;
}
/* ⚠️ `min-width` AUTANT que `min-height` : 2.5.5 demande 44 × 44, et « Privacy »
   ne fait que 39 px de large en anglais — le défaut n'existait pas en français.
   Un seuil vérifié dans une seule langue n'est pas vérifié. */
.sm-ftx-lien {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--sm-tap-min); min-width: var(--sm-tap-min);
  font-size: var(--fs-cap);
}

/* ── Les trois replis de la bande ──────────────────────────────────────────
 *
 * 980 px : les cinq pistes tombent sous 130 px et « Conditions d'utilisation »
 * ne tient plus sur deux lignes. Les trois listes gardent leurs trois colonnes ;
 * la marque et l'inscription, qui portent du texte long, prennent la largeur.
 *
 * 640 px : deux colonnes. Trois listes sur deux pistes laissent un trou en bas
 * à droite, et c'est le bon compromis — trois colonnes de 100 px hacheraient
 * chaque libellé sur trois lignes.
 *
 * ⚠️ Aucune requête ne descend sous 640 : à une colonne, la bande ferait
 * 900 px de haut au-dessus d'un pied compact, sur l'écran où l'on a le moins de
 * place. Deux colonnes de liens courts tiennent parfaitement à 360 px. */
@media (max-width: 980px) {
  .sm-ftx-in { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .sm-ftx-marque,
  .sm-ftx-nlcol { grid-column: 1 / -1; }
}
@media (max-width: 640px) {
  .sm-ftx-in {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    padding: var(--sp-9) var(--sp-7);
    gap: var(--sp-8) var(--sp-6);
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   LES PUCES DE FILTRE — une seule écriture, quatre écrans
   ══════════════════════════════════════════════════════════════════════════
   🔴 Elles n'existaient que sur Joueur (`.sm-jou-puce`). Équipe, Explore et
   Discipline filtraient sans jamais dire QUOI : panneau refermé, l'utilisateur
   lit un tableau borné sans le savoir. Le style monte donc ici avec le partiel
   `_filtres_puces.html`. */
.sm-puces {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  /* 🔴 **AUCUNE MARGE ICI.** Elle valait 12 px, et sur Joueur la pile qui
   * porte les puces a deja `gap: 16px` — mesure au navigateur : 28 px entre les
   * puces et le contenu, la ou les autres blocs en ont 16. Deux espacements
   * pour une meme separation s'additionnent, et le decalage se lit comme un
   * desalignement sans qu'on sache d'ou il vient. C'est la pile qui espace ;
   * un ecran dont la pile n'a pas de `gap` ajoute la sienne, chez lui. */
  margin: 0;
}

.sm-puce {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 3px 3px 12px;
  border-radius: 999px;
  background: var(--color-accent-100);
  color: var(--color-accent-800);
  font-size: 12.5px;
  /* Une puce ne se coupe pas en deux : « Équipe : Espérance de » suivi d'une
     croix seule à la ligne suivante ne se lit plus. */
  max-width: 100%;
}

/* Le NOM du filtre est plus discret que sa VALEUR : « Équipe » est le
   contenant, « Espérance de Tunis » est ce que l'utilisateur a choisi. Sans
   cette distinction, la puce se lit comme un bloc de texte uniforme. */
.sm-puce-lib { opacity: .75; }
.sm-puce-lib::after { content: " :"; }

.sm-puce-val {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 🔴 **LA CROIX FAISAIT 32 × 26 PX.** Le projet impose 44 px de cible tactile
 * (WCAG 2.5.5) à tous ses contrôles, et celui-ci y échappait — alors qu'il est
 * le plus petit de l'écran, et qu'un doigt qui le manque retire *une autre*
 * puce. On garde le dessin compact et on étend la zone CLIQUABLE par un
 * pseudo-élément : la puce ne grossit pas, la cible atteint 44 px. */
.sm-puce-x {
  position: relative;
  display: grid;
  place-items: center;
  /* ⚠️ **`flex: 0 0 auto` OU LA LARGEUR NE TIENT PAS.** `.sm-puce` est un
   * conteneur flex ; par défaut ses enfants ont `flex-shrink: 1`, et la croix
   * se laissait écraser à la largeur de son glyphe — **10,6 px** mesurés au
   * navigateur, pour 26 px déclarés. Le `width` était bien appliqué, il était
   * simplement écrasé par la répartition flex. Le défaut ne se voit pas en
   * relisant la règle : il faut mesurer l'élément. */
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  color: inherit;
  text-decoration: none;
  font-size: 13px;
  line-height: 1;
}

.sm-puce-x::after {
  content: "";
  position: absolute;
  /* (44 − 26) ÷ 2 = 9 px débordés de chaque côté. */
  inset: -9px;
}

.sm-puce-x:hover,
.sm-puce-x:focus-visible { background: rgba(0, 0, 0, .1); }

.sm-puces-vider {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--color-primary);
  /* Souligné : c'est un lien parmi des pastilles colorées, et la couleur seule
     ne le distingue pas (WCAG 1.4.1). */
  text-underline-offset: 2px;
  /* La cible tactile, ici aussi. */
  display: inline-flex;
  align-items: center;
  min-height: var(--sm-tap-min, 44px);
  padding-inline: 4px;
}

/* ══════════════════════════════════════════════════════════════════════════
   LA BARRE DE FILTRES — une seule écriture pour TOUTE l'application
   ══════════════════════════════════════════════════════════════════════════

   🔴 **CINQ ÉCRITURES DIVERGEAIENT.** Explore avait deux apparences pour deux
   commandes de la même barre (le panneau de portée en pilules, celui des
   filtres en rectangles) ; Joueur, Discipline et l'onglet Vidéos en avaient
   chacun une troisième. Le client l'a dit sans détour : « Ce devrait être un
   style réutilisable partout ! TOUS LES FILTRES DE L'APPLICATION ! »

   🛑 **TOUTES LES VALEURS CI-DESSOUS SONT MESURÉES SUR LA MAQUETTE**, servie et
   ouverte au navigateur, pas déduites de son code. Elles ne se « corrigent »
   pas à vue de nez : chacune vient d'un `getComputedStyle` sur l'élément réel.

   ── LA HAUTEUR DES CHAMPS EST UNE DÉCISION DU CLIENT, PAS UN OUBLI ──────────

   ⚠️ 32 px, et non les 44 px que ce fichier impose ailleurs (WCAG 2.5.5).
   J'avais tranché l'inverse aux lots 9 et 10 — « sur un produit consulté au
   pouce, c'est la maquette qui a tort » — et le client a explicitement choisi
   la maquette après que l'écart lui a été présenté chiffré. C'est sa décision,
   elle est consignée ici pour qu'on ne la « répare » pas au lot suivant.

   Ce qui est conservé de la règle : la zone CLIQUABLE des petites cibles
   (croix de puce, bouton de sens) reste étendue par pseudo-élément. Un contrôle
   de 32 px de haut sur toute une ligne se vise ; une croix de 11 px, non. */

/* ── La rangée de commandes : recherche · portée · filtres · tri · total ── */
.sm-fb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 9px;
  margin-bottom: 12px;
}

/* Le total en fin de rangée, comme la maquette. */
.sm-fb-total {
  margin-inline-start: auto;
  font-size: 12.5px;
  color: var(--color-neutral-600);
  white-space: nowrap;
}

/* ── Les déclencheurs : « Filtres (n) », « Trier : … », « Liste / Cartes » ──
   🔴 **FOND TRANSPARENT, PAS BLANC.** Mesuré : la maquette rend
   `rgba(0,0,0,0)` et un filet à 13 % d'opacité ; la v2 rendait `#ffffff` plein
   et une bordure `rgb(103,112,127)`. Le bouton v2 pesait comme un bouton
   secondaire alors que la maquette le veut discret — c'est le « le background
   du bouton filtres n'est pas blanc ! » du client.

   🔴 **POLICE DE TITRE.** La maquette met **Anton** sur ces déclencheurs, la v2
   mettait Archivo. Anton est condensée et grasse : à l'œil, ça se lit comme une
   autre casse — et c'est l'explication du « même la casse n'est pas la même ».
   Une relecture du CSS ne le voyait pas ; la mesure au navigateur, oui. */
.sm-fb-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 31px;
  min-height: 31px;
  padding: 0 14px;
  border: 1px solid var(--sm-border-field);
  border-radius: 999px;
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 13px;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}

.sm-fb-btn::-webkit-details-marker { display: none; }

.sm-fb-btn:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }

/* Ouvert, ou portant une valeur : le même fond à 7 % que le survol de la
   maquette. Un état actif signalé par la seule couleur du texte ne se verrait
   pas (WCAG 1.4.1) — ici c'est le FOND qui change, pas la teinte du mot. */
.sm-fb-btn--actif,
details[open] > .sm-fb-btn {
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
}

/* ── Le panneau : PLEINE LARGEUR, en frère de la rangée ──────────────────
   🔴 **IL SORTAIT EN COLONNE DE 190 PX.** Le `<details>` d'Explore était un
   ITEM FLEX de la rangée de commandes : un item flex se dimensionne sur son
   contenu, donc le panneau prenait la largeur du bouton « Filtres » et sa
   grille interne se repliait en une seule colonne. Mesuré : 190 px de large
   pour 1080 px disponibles — c'est le « la largeur est bête, il fait déborder
   l'élément filtres » du client.
   Le panneau est désormais un FRÈRE de la rangée, comme dans la maquette. */
.sm-fb-panneau {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  width: 100%;
  margin-bottom: 12px;
  padding: 12px 15px;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md, 16px);
  background: var(--color-surface);
}

/* ── Les contrôles ──────────────────────────────────────────────────────
   🛑 **LE LIBELLÉ EST DANS LE CHAMP**, pas au-dessus : la première option d'une
   liste porte le nom du filtre (« Tous postes »), un combo le porte en
   `placeholder`. C'est la maquette, et c'est aussi ce qui tient sur une seule
   ligne. Un `<label>` reste écrit pour les lecteurs d'écran, mais caché —
   `.sm-visuellement-cache`. Le retirer priverait le champ de son nom
   accessible : le `placeholder` n'en est pas un. */
.sm-fb-champ,
.sm-fb-combo {
  /* ⚠️ **`min-height` AUSSI, ET PAS SEULEMENT `height`.** Ce fichier pose plus
   * haut `:where(body.sm-v2) select { min-height: var(--sm-tap-min) }` — soit
   * 44 px. `min-height` l'emporte toujours sur `height`, quelle que soit la
   * specificite : les listes sortaient donc a 44 px (mesure au navigateur)
   * alors que la regle disait 32. Le defaut est invisible en relisant le CSS,
   * puisque les deux declarations sont justes ; c'est leur CASCADE qui
   * tranche. */
  height: 32px;
  min-height: 32px;
  padding: 0 12px;
  border: 1px solid var(--sm-border-field);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: 13px;
  /* `min-width: 0` : sans lui, un `<select>` refuse de se réduire sous la
     largeur de sa plus longue option et fait déborder la rangée. */
  min-width: 0;
  max-width: 100%;
}

.sm-fb-combo { background: var(--color-bg); width: 150px; }

/* Un champ court : une borne d'âge, de note, de minute. */
.sm-fb-nombre {
  height: 32px;
  min-height: 32px;
  width: 62px;
  padding: 0 10px;
  border: 1px solid var(--sm-border-field);
  border-radius: 999px;
  background: var(--color-surface);
  font: inherit;
  font-size: 13px;
}

/* ── La PAIRE : deux champs pour un seul critère ─────────────────────────
   ⚠️ **LA SEULE FORME DONT LE LIBELLÉ SORT DU CHAMP**, et c'est la maquette qui
   l'impose : « Âge [min]–[max] », « Note [min]–[max] », « Dates [ ]–[ ] ». Le
   client l'avait relevé (« les labels sont bien à l'intérieur des champs sauf
   pour les dates ») — ce n'est pas une incohérence, c'est la règle : deux
   champs ne peuvent pas porter chacun la moitié d'un nom. */
/* 🔴 **C'ÉTAIT UN `<fieldset>`, ET C'EST CE QUI DÉCALAIT LE LIBELLÉ.** Un
 * `<legend>` est sorti du flux par le navigateur pour être encastré dans la
 * bordure du `fieldset` : il ignore `display: inline-flex` sur son parent, et
 * aucune valeur de `display` ne le remet dans la rangée de façon portable. Le
 * mot « Âge » se posait donc au-dessus de ses deux champs, seul contrôle du
 * panneau à ne pas tenir sur une ligne — le « les champs de … à … ont un
 * décalage de position » du client. Le groupe est désormais un `<span
 * role="group">`, qui nomme les deux bornes aussi bien et se dispose
 * normalement. */
.sm-fb-paire {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 0;
  padding: 0;
  margin: 0;
}

.sm-fb-paire-lib {
  font-size: 12.5px;
  color: var(--color-neutral-700);
  white-space: nowrap;
}

.sm-fb-date {
  height: 32px;
  min-height: 32px;
  padding: 0 10px;
  border: 1px solid var(--sm-border-field);
  border-radius: 999px;
  background: var(--color-surface);
  font: inherit;
  font-size: 13px;
}

/* ── Le champ de recherche ───────────────────────────────────────────────
   🔴 **LE « CARRÉ » DU CLIENT VENAIT DE `type="search"`.** Avec
   `-webkit-appearance: auto`, Chrome dessine son propre bouton d'effacement dès
   qu'on tape — un carré, dans une pilule. Mesuré : le pseudo-élément
   `::-webkit-search-cancel-button` s'affiche en `inline-block`. La maquette
   emploie un input texte simple. On garde `type="search"` (sémantique, et le
   clavier mobile propose « Rechercher ») et on éteint le dessin natif.

   🔴 **LA BORDURE ÉTAIT TROP LOURDE** : `--sm-border-field` est un gris plein,
   la maquette pose un filet à 13 %. « Pas besoin de bordure », dit le client :
   on garde un filet, parce que sans aucune bordure la pilule blanche
   disparaîtrait sur le fond clair — mais c'est le filet de la maquette. */
.sm-fb-recherche {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 32px;
  min-height: 32px;
  padding: 0 14px;
  border: 1px solid var(--sm-border-field);
  border-radius: 999px;
  background: var(--color-surface);
}

.sm-fb-recherche svg { flex: 0 0 auto; color: var(--color-neutral-500); }

.sm-fb-recherche input {
  width: 150px;
  max-width: 100%;
  border: 0;
  background: none;
  font: inherit;
  font-size: 13.5px;
  color: var(--color-text);
  -webkit-appearance: none;
  appearance: none;
}

.sm-fb-recherche input::-webkit-search-cancel-button,
.sm-fb-recherche input::-webkit-search-decoration { -webkit-appearance: none; display: none; }

/* 🛑 **L'ANNEAU DE FOCUS DÉMÉNAGE, IL NE DISPARAÎT PAS.** La maquette pose
 * `outline: none` sur son champ nu ; le projet l'interdit hors d'un état de
 * focus, et il a raison — un champ sans anneau est un champ qu'on perd au
 * clavier (WCAG 2.4.7). L'anneau se dessine donc sur l'ENVELOPPE, qui est la
 * pilule qu'on voit : le focus reste visible, et il entoure le contrôle entier
 * plutôt qu'un rectangle interne qui déborderait de la pilule. */
.sm-fb-recherche input:focus { outline: none; }

.sm-fb-recherche:focus-within {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ── Le tri : un critère, un sens ────────────────────────────────────────
   🔴 **LA V2 FUSIONNAIT LES DEUX.** « Plus récent » et « Plus ancien » sont UN
   critère et DEUX sens ; les servir comme deux jetons distincts est ce qui
   produisait des libellés bâtards, et interdisait d'offrir plus de trois ou
   quatre critères. La maquette en propose DIX-HUIT, groupés en sept familles —
   ce que seul un `<select>` peut porter. */
.sm-fb-tri { display: inline-flex; align-items: center; gap: 8px; }

.sm-fb-tri-lib { font-size: 12.5px; color: var(--color-neutral-700); }

/* ⚠️ Le bouton de SENS est petit et souvent visé : sa zone cliquable est
   étendue à 44 px sans que son dessin grossisse. C'est ce qui reste de la règle
   2.5.5 une fois la hauteur des champs ramenée à 32 px. */
.sm-fb-sens { position: relative; }
.sm-fb-sens::after { content: ""; position: absolute; inset: -7px; }

/* La note d'un critère inversé — « plus bas = mieux ». Sans elle, un tri sur
   les cartons ou sur le BRT est muet sur son sens, et le lecteur suppose que
   le premier de la liste est le meilleur. */
.sm-fb-note {
  flex: 1 0 100%;
  margin: 0;
  font-size: 12px;
  color: var(--color-neutral-600);
}

/* Sur téléphone la rangée se replie ; le champ de recherche prend la ligne,
   parce que c'est le contrôle qu'on emploie en premier. */
@media (max-width: 560px) {
  .sm-fb-recherche { flex: 1 0 100%; }
  .sm-fb-recherche input { width: 100%; }
  .sm-fb-total { margin-inline-start: 0; }
}

/* L'enveloppe d'un champ : elle ne porte que le label caché et le contrôle.
   `display: contents` la rendrait invisible à la disposition — mais un
   `<datalist>` y vit aussi, et `contents` sur un parent de datalist casse le
   rendu dans certains navigateurs. On la garde en `inline-flex`, sans style. */
.sm-fb-champ-enveloppe { display: inline-flex; align-items: center; min-width: 0; }

/* Le bouton « Appliquer » : masqué par l'îlot `filtre-auto` dès que le script
   tourne, et seule façon d'appliquer un filtre sans script. Il ferme la rangée,
   à droite. */
.sm-fb-appliquer { margin-inline-start: auto; }

/* Un contrôle qui dépend d'un autre non posé : visible mais inerte, avec la
   raison portée par le `title`. Le MASQUER ferait apparaître et disparaître des
   contrôles au fil des choix, et le lecteur ne saurait jamais ce que l'écran
   sait faire. */
.sm-fb-champ:disabled,
.sm-fb-combo:disabled,
.sm-fb-nombre:disabled { opacity: .5; cursor: not-allowed; }

/* ── Le tiroir : POURQUOI LE PANNEAU EST SORTI DU FLUX DE LA RANGÉE ──────
   🔴 **`display: contents` SUR UN `<details>` A CESSÉ DE MARCHER.** C'était la
   mécanique précédente : la boîte du `<details>` disparaissait à l'ouverture,
   son résumé et son panneau devenaient deux items directs de `.sm-fb`, et
   `order` renvoyait le panneau en dernier. Elle tenait — jusqu'à ce que les
   navigateurs enveloppent le contenu d'un `<details>` dans un
   `::details-content` (Chrome 131, puis les autres). Cette boîte-là, elle,
   **reste** : c'est ELLE qui devient l'item flex, à la place qu'occupait le
   `<details>`, et le panneau se rouvre donc au milieu de la rangée en poussant
   les boutons voisins. Le symptôme est exactement celui que le client décrit
   deux fois — « les filtres apparaissent entre les éléments déjà affichés » —
   et aucune valeur d'`order` ne l'atteint, puisque l'ordre porte sur une boîte
   qu'on ne cible plus.

   🛑 **UN PANNEAU QUI POUSSE EST UN PANNEAU QUI DÉPLACE LA CIBLE.** Même bien
   ordonné, un tiroir en flux repousse la liste au moment où le doigt vient de
   viser un bouton. Le panneau est donc **hors flux**, ancré sous la rangée :
   il ne peut plus, par construction, déplacer quoi que ce soit — ni les
   boutons, ni les résultats. C'est la garantie que l'`order` ne donnait pas.

   ⚠️ **ET IL NE PEUT PAS ÊTRE COUPÉ.** Hors flux, un panneau plus haut que
   l'écran ne se laisserait pas atteindre par le défilement de la page : il
   porte donc sa propre borne (`max-height`) et défile en interne.

   🛑 **AUCUN SCRIPT.** Le `<summary>` reste la seule bascule qui fonctionne
   sans une ligne de JavaScript, au clavier comme à la souris. */
.sm-fb { position: relative; }

.sm-fb .sm-fb-details > .sm-fb-panneau {
  position: absolute;
  inset-inline: 0;
  top: calc(100% + 6px);
  z-index: 40;
  width: auto;
  margin-bottom: 0;
  max-height: min(62vh, 460px);
  overflow-y: auto;
  box-shadow: var(--shadow-md);
}

/* Le total ferme la rangée, à droite, comme dans la maquette. */
.sm-fb-total { margin-inline-start: auto; }

/* ── L'APPARITION DU PANNEAU ─────────────────────────────────────────────
 * 🔴 **« LEUR APPARITION EST FLUIDE » (maquette).** Un `<details>` natif passe
 * de `display: none` à `display: flex` d'un seul coup : le panneau surgit.
 * `@starting-style` + `transition-behavior: allow-discrete` donnent la
 * transition SANS une ligne de JavaScript — le navigateur anime l'opacité et le
 * glissement pendant que `display` bascule.
 *
 * ⚠️ Les navigateurs qui ne connaissent pas ces propriétés ignorent le bloc et
 * retrouvent exactement le comportement d'avant : rien ne dépend de la
 * transition, elle ne fait qu'adoucir. */
@media (prefers-reduced-motion: no-preference) {
  .sm-fb-details > .sm-fb-panneau {
    transition: opacity .16s ease, translate .16s ease, display .16s allow-discrete;
  }

  .sm-fb-details[open] > .sm-fb-panneau {
    opacity: 1;
    translate: 0 0;
  }

  @starting-style {
    .sm-fb-details[open] > .sm-fb-panneau {
      opacity: 0;
      translate: 0 -6px;
    }
  }
}

/* 🔴 **UN PANNEAU FERMÉ RESTAIT AFFICHÉ.** Le navigateur cache le contenu d'un
 * `<details>` replié par une règle d'agent utilisateur —
 * `details > :not(summary) { display: none }` — et `.sm-fb-panneau { display:
 * flex }` l'écrasait. Les trois panneaux étaient donc rendus en permanence :
 * mesuré, le panneau de portée sortait à 221 px sous un `<details>` que le DOM
 * disait FERMÉ (`open === false`).
 *
 * ⚠️ Le défaut est invisible en relisant l'une ou l'autre règle : chacune est
 * juste, c'est leur rencontre qui casse. Et il ne se voit pas non plus au
 * premier coup d'œil, puisqu'un panneau ouvert par erreur ressemble à un
 * panneau qu'on vient d'ouvrir. */
.sm-fb-details:not([open]) > .sm-fb-panneau { display: none; }

/* Fermé, le `<details>` reste un item normal qui ne montre que son résumé. */
.sm-fb-details > summary::-webkit-details-marker { display: none; }

/* La pastille de périmètre garde sa teinte : c'est le seul contrôle de la barre
   qui ne FILTRE pas — il change ce qui est calculé, pas ce qui est listé. Une
   couleur distincte est ici une information, pas une décoration. */
.sm-fb-btn--portee {
  background: var(--color-accent-2-100, #e6f6ef);
  border-color: var(--color-accent-2-300, #95d7bb);
}

.sm-fb-btn--portee:hover {
  background: color-mix(in srgb, var(--color-accent-2-100, #e6f6ef) 80%, var(--color-text));
}

/* 🛑 **LA BORDURE DES CONTRÔLES NE SUIT PAS LA MAQUETTE, ET C'EST DÉLIBÉRÉ.**
 *
 * La maquette pose un filet à 13 % d'opacité sur ses champs. Mesuré contre les
 * deux fonds du produit, il **ne tient pas le seuil de 3:1** qu'exige WCAG
 * 1.4.11 pour la bordure d'un contrôle — et `test_v2_css` le vérifie sur les
 * deux thèmes, au repos et au survol.
 *
 * Le client a choisi la maquette sur la HAUTEUR (32 px au lieu de 44), après
 * que l'écart lui a été présenté chiffré. La bordure est un autre sujet : une
 * hauteur réduite gêne le doigt, une bordure invisible efface le contrôle pour
 * qui voit mal. On garde donc `--sm-border-field` sur ce qui est un CONTRÔLE,
 * et le filet de la maquette sur ce qui n'en est pas un — le panneau, qui est
 * une surface.
 *
 * L'écart visuel est faible ; l'écart d'usage ne l'est pas. */
