/* SMMTT — hand-written charter CSS, no framework (D-07). */

:root {
  /* Colors — sampled from logo_smm.png, per 01-UI-SPEC.md */
  --color-bg: #FFFFFF;
  --color-surface: #EEF1F8;
  --color-accent: #2A3A85;
  --color-error: #C0392B;
  --color-success-border: #2E7D32;
  --color-success-bg: #E8F5E9;
  --color-brand-yellow: #FDEB04;
  --color-amber-text: #7B4E0E;
  --color-amber-bg: #FFFBEB;
  --color-amber-border: #B7791F;

  /* Phase 16 (16-UI-SPEC.md §4, sketch 020-C) : densité "salle 1" de la
     timeline du planning public — unique nouveau token de tout le contrat. */
  --color-surface-strong: #DDE0F0; /* ≈16% de --color-accent sur blanc — densité "salle 1"
                                       de la timeline (sketch 020-C), jamais un nouvel accent */

  /* Neutral structure grays — sketch 004 (07-CONTEXT.md D7-01/D7-05) : gris
     utilitaires, jamais des couleurs de marque, mais tokenisés en :root pour
     satisfaire UX-11 (0 littéral hex hors :root). */
  --color-text: #1A1A1A;
  --color-text-muted: #5A5A5A;
  --color-border: #D6DCEC;
  --color-result-loss: #9AA3BF; /* 261025-npg : gris des défaites du site public (site.css --grey-soft) */
  --color-hover: #F4F6FC;

  /* Grille calendaire mensuelle (10-UI-SPEC.md §Color, amende D-09) — 5
     nouveaux tokens de catégorie ; chpt-jeunes/autre réutilisent
     --color-accent/--color-text-muted directement dans les règles .cat-*. */
  --cat-crit-1: #1E8FA6;
  --cat-crit-2: #7B4FA6;
  --cat-feminine: #C0447D;
  --cat-jeudi-vendredi: #D9600F;
  --cat-dimanche: #3F7A55;
  --cat-chpt-paris: #A65E00;
  --cat-vie-club: #B3261E;

  /* Spacing scale — multiples of 4, per 01-UI-SPEC.md Spacing Scale */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --space-2xl: 48px;
  --space-3xl: 64px;

  /* Jeu libre — 11 pastilles de cours/formule (Phase 20, D-32,
     20-UI-SPEC.md §4.1). Un couple fond/encre par groupe-créneau puis par
     formule, JAMAIS réutilisé d'un groupe à l'autre : c'est ce qui donne la
     garantie « deux personnes de la même couleur sont du même cours, sans
     exception ». Ratio de contraste WCAG 2.1 en regard de chaque couple,
     tous AA pour du texte normal (≥4.5:1), la plupart proches d'AAA.
     ⚠ INTERDICTION EXPLICITE : la pastille n° 5 (#FBF0C8 / #6B5406) est
     visuellement proche du jaune de marque mais N'EST PAS
     --color-brand-yellow (#FDEB04) — ne jamais les fusionner, ne jamais
     réutiliser l'un pour l'autre. C'est un token de pastille comme les dix
     autres, soumis aux mêmes règles (fond clair + encre foncée, jamais
     l'inverse). */
  --jl-pill-1-bg: #DCE7FB;   /* mar. 18h    — 8.58:1 */
  --jl-pill-1-ink: #1B3D74;
  --jl-pill-2-bg: #E2DCF6;   /* mar. 18h15  — 8.99:1 */
  --jl-pill-2-ink: #3B2C6B;
  --jl-pill-3-bg: #DCEFDF;   /* mar. 19h30  — 7.45:1 */
  --jl-pill-3-ink: #22532C;
  --jl-pill-4-bg: #FBE4CE;   /* jeu. 18h    — 6.48:1 */
  --jl-pill-4-ink: #7A4310;
  --jl-pill-5-bg: #FBF0C8;   /* ven. 18h    — 6.35:1 (PAS le jaune de marque) */
  --jl-pill-5-ink: #6B5406;
  --jl-pill-6-bg: #D3ECF2;   /* lun. 19h    — 7.31:1 */
  --jl-pill-6-ink: #10505E;
  --jl-pill-7-bg: #EBE2D6;   /* lun. 20h30  — 6.57:1 */
  --jl-pill-7-ink: #5E4A2E;
  --jl-pill-8-bg: #E3EBD2;   /* mer. 20h30  — 6.74:1 */
  --jl-pill-8-ink: #44541F;
  --jl-pill-9-bg: #F6DCE8;   /* formule « jeu libre » — 8.39:1 */
  --jl-pill-9-ink: #6E1F45;
  --jl-pill-10-bg: #FADCD9;  /* formule « week-end »  — 7.23:1 */
  --jl-pill-10-ink: #7E2B22;
  --jl-pill-11-bg: #E6E2DC;  /* formule « midi »      — 7.46:1 */
  --jl-pill-11-ink: #4A443B;

  /* Jeu libre — cœur actif (Phase 20, 20-UI-SPEC.md §4.2). UN SEUL usage :
     le glyphe ♥ de .heart quand aria-pressed="true". C'est la seule couleur
     du produit qui n'a de sens que dans un navigateur donné — la liste
     « Mes amis » vit en localStorage et ne quitte jamais le téléphone
     (D-26/JEUL-20), ce n'est JAMAIS un état serveur. */
  --jl-heart-active: #D6456E;

  /* Jeu libre — « ligne rouge à zéro » du journal admin (Phase 20, D-34,
     20-UI-SPEC.md §4.4). Un créneau sans volontaire n'est ni une erreur de
     formulaire (--color-error, jamais employé comme FOND) ni un
     avertissement à corriger (l'ambre, réservé au bandeau des clés) : c'est
     un CONSTAT définitif sur un créneau déjà passé, d'où un couple
     sémantique distinct des deux. 6.89:1 (AA). */
  --color-stop-bg: #FBE7E3;
  --color-stop-text: #93281B;

  /* Jeu libre — surlignage bref des nouvelles lignes de la mise à jour sur
     place du tableau (260914-ows, D-18 amendé). Une teinte jaune très pâle
     dérivée de la marque, PAS `--color-brand-yellow` (#FDEB04, trop
     saturée pour un fond de texte) ni `--jl-pill-5-bg` (déjà attribuée à un
     cours, JEUL-19). `--color-text` dessus reste très largement AA
     (15.9:1, proche de AAA) — la teinte n'a donc jamais besoin d'encre
     dédiée. Le fondu passe par `.jl-name` (transition sur
     `background-color`), et la règle globale `prefers-reduced-motion` du
     bas de ce fichier le ramène déjà à l'instantané. */
  --jl-name-new-bg: #FFF6C4;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background-color: var(--color-bg);
  color: var(--color-text);
}

/* Typography utility classes — 01-UI-SPEC.md Typography table */
.text-body {
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
}

.text-label {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
}

.text-heading {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.2;
}

.text-display {
  font-size: 28px;
  font-weight: 600;
  line-height: 1.2;
}

/* Bandeau permanent d'instance archive (Phase 15, D-13/D-06) — première
   chose visible sur toute page, rendue via le global Jinja2
   instance_is_archive (app/deps.py, P-13). Réutilise --color-amber-*
   (déjà employé par .warning-banner) : un avertissement permanent, pas une
   erreur (--color-error) ni un succès. */
.instance-banner {
  padding: var(--space-sm) var(--space-lg);
  background-color: var(--color-amber-bg);
  color: var(--color-amber-text);
  border-bottom: 1px solid var(--color-amber-border);
  text-align: center;
  font-size: 14px;
}

/* Header / brand lockup */
.site-header {
  padding: var(--space-lg) var(--space-lg) 0 var(--space-lg);
  text-align: center;
}

.brand-lockup {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  text-decoration: none;
}

.brand-logo {
  height: 40px;
  width: auto;
}

.brand-wordmark {
  color: var(--color-accent);
}

/* Decorative-only brand highlight (never on interactive elements — UI-SPEC) */
.brand-underline {
  height: 3px;
  width: 64px;
  margin: var(--space-sm) auto 0;
  background-color: var(--color-brand-yellow);
}

main {
  padding: var(--space-xl) var(--space-lg);
}

/* En-tête compact du jeu libre (quick 260915-g7p, point 3) : scopé par
   `:has(.jl-page)` aux seules pages publiques du jeu libre, qui portent
   toutes `.jl-page` sur leur `<section>` racine — jamais les écrans
   d'administration (`.jl-admin-page`), /planning, /competitions ou le
   dashboard, qui gardent l'en-tête de base ci-dessus INCHANGÉ. Réduit le
   blanc du haut (retour utilisateur : petits écrans, trop de blanc). */
body:has(.jl-page) .site-header {
  display: flex;
  align-items: center;
  padding: var(--space-sm) var(--space-md);
  text-align: left;
  border-bottom: 3px solid var(--color-brand-yellow);
}

body:has(.jl-page) .brand-underline {
  display: none;
}

body:has(.jl-page) main {
  /* Le padding HORIZONTAL de 24px (`--space-lg`) est conservé : c'est de
     lui que dépend le budget de largeur à 360px
     (tests/test_jeulibre_layout_css.py). */
  padding-top: var(--space-md);
}

.jl-lockup {
  justify-content: flex-start;
}

.jl-lockup .brand-logo {
  height: 28px;
}

.jl-lockup .brand-wordmark {
  font-size: 20px;
}

.jl-lockup-sep {
  color: var(--color-border);
}

.jl-lockup-sub {
  font-size: 16px;
  color: var(--color-text-muted);
}

/* Login card (D-07: sober, centered, minimal) */
.login-card {
  max-width: 360px;
  margin: var(--space-2xl) auto 0 auto;
  padding: var(--space-lg);
  background-color: var(--color-bg);
  border: 1px solid var(--color-surface);
  border-radius: 8px;
}

.login-card .text-display {
  color: var(--color-accent);
  margin: 0 0 var(--space-lg) 0;
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

/* Mobile-first tap-target exception — 01-UI-SPEC.md Spacing Scale exceptions */
input[type="password"],
input[type="text"],
input[type="file"],
button {
  min-height: 44px;
}

input[type="password"],
input[type="text"] {
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-surface);
  border-radius: 4px;
  font-size: 16px;
}

input[type="password"]:focus,
input[type="text"]:focus {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 12px var(--space-md);
  border: none;
  border-radius: 4px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

.btn-primary {
  background-color: var(--color-accent);
  color: var(--color-bg);
}

.btn-secondary {
  background-color: var(--color-bg);
  color: var(--color-accent);
  border: 1px solid var(--color-accent);
}

.error-message {
  color: var(--color-error);
  margin-top: var(--space-md);
}

/* Dashboard */
.dashboard-section {
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}

.upload-section {
  max-width: 480px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}

.empty-state {
  background-color: var(--color-surface);
  padding: var(--space-lg);
  border-radius: 8px;
}

.empty-state a {
  color: var(--color-accent);
}

.success-banner {
  border: 1px solid var(--color-success-border);
  background-color: var(--color-success-bg);
  padding: var(--space-md);
  border-radius: 4px;
}

.warning-banner {
  border: 1px solid var(--color-amber-border);
  background-color: var(--color-amber-bg);
  color: var(--color-amber-text);
  padding: var(--space-md);
  border-radius: 4px;
}

/* Bloc Favoris + Outils fusionné (10-UI-SPEC.md §3, D-14, sketch
   011-favoris-outils-filet LOCKED) — remplace .favorites-bar/.tools-bar (2
   barres horizontales séparées) par 1 seul conteneur grid 2 colonnes égales
   + filet vertical pleine hauteur, en tête de page. */
.fo-block {
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  background-color: var(--color-surface);
}

.fo-col {
  display: flex;
  flex-direction: column;
}

.fo-divider {
  background: var(--color-border);
}

.fo-title {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  font-size: 11px;
  font-weight: 700;
  padding: 12px 12px 4px;
}

.fo-item {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 44px;
  padding: 8px 16px 8px 12px;
  color: var(--color-text);
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
  border: none;
  background: transparent;
  cursor: pointer;
  font-family: inherit;
}

.fo-item:hover,
.fo-item:focus {
  background-color: var(--color-hover);
}

.fo-icon {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  border-radius: 3px;
  display: grid;
  place-items: center;
  font-size: 10px;
  font-weight: 700;
  color: var(--color-bg);
  background-color: var(--color-success-border);
}

.fo-icon--fftt {
  background-color: var(--color-accent);
}

.fo-icon--assoconnect {
  background-color: var(--color-amber-border);
}

/* Déviation du sketch 011 (10-UI-SPEC.md §2) : AssoConnect occupe déjà
   --color-amber-border en production, réutiliser l'ambre créerait une
   collision entre 2 favoris de même couleur ; --color-text-muted n'introduit
   aucun nouveau token et convient à un document de référence statique. */
.fo-icon--pdf {
  background-color: var(--color-text-muted);
}

.fo-icon--contacts {
  background-color: var(--color-accent);
}

.fo-arrow {
  margin-left: 8px;
  flex: 0 0 auto;
  color: var(--color-text-muted);
  font-size: 11px;
}

/* Grille calendaire mensuelle (10-UI-SPEC.md §1, sketch 012 round 2, D-07..
   D-10/D-13b) — remplace .calendar-annex (liste plate repliée, D-18). */
.cal-switch {
  display: flex;
  max-width: 100%;
  overflow-x: auto;
  border-radius: 999px;
  background-color: var(--color-surface);
  padding: 4px;
}

.cal-switch-option {
  flex: 0 0 auto;
  padding: 8px 12px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

/* Phase 15 (D-06/P-02) : "1"/"2" désignent la première et la seconde
   année civile de la saison COURANTE, jamais une année précise — ces
   classes servent n'importe quelle saison sans modification de la feuille
   de style (dérivées par app.season.derive.annee_classe). */
.cal-switch-option.cal-year-1 {
  background-color: var(--color-surface);
}

.cal-switch-option.cal-year-2 {
  background-color: var(--color-hover);
}

.cal-switch-radio:checked + .cal-switch-option {
  background-color: var(--color-bg);
  color: var(--color-accent);
}

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: var(--space-xs);
}

/* debug 260805-candrier-vide-en-aout-ok-mais : l'ancien couple de règles
   .cal-grid et .cal-list, filtrées par attribut data-cal-month égal à "1",
   forçait display: none — ciblait l'index de mois "1" (septembre) en dur,
   EN PLUS du toggle JS
   (panel.style.display = ... ? "" : "none"). "" ne fait qu'effacer le style
   inline — la règle CSS ci-dessus continuait donc de s'appliquer en
   permanence à l'index 1, rendant septembre impossible à afficher, même
   avec JS actif (voir 10-REVIEW.md WR-02, qui n'avait identifié que le cas
   sans JS). `.cal-panel--hidden` remplace ce mécanisme : appliqué
   server-side sur tous les mois sauf le premier (Jinja `loop.first`), puis
   basculé côté JS via classList — plus jamais de conflit inline/CSS, et ça
   fonctionne pour n'importe quel nombre de mois. */
.cal-panel--hidden {
  display: none;
}

.cal-day-header {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  text-align: center;
}

.cal-day {
  min-height: 44px;
  padding: var(--space-xs);
  border-radius: 4px;
  background-color: var(--color-bg);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.cal-day-btn {
  cursor: pointer;
  border: none;
  font-family: inherit;
  width: 100%;
  text-align: left;
}

.cal-day--empty {
  background: transparent;
}

.cal-day.selected {
  border: 2px solid var(--color-accent);
  background-color: var(--color-hover);
}

.cal-day.has-events:hover {
  background-color: var(--color-hover);
}

/* D-13b — SEUL 2e emploi sanctionné de --color-brand-yellow en
   background-color, aux côtés de .date-card--next. */
.cal-day.is-today {
  background-color: var(--color-brand-yellow);
}

.cal-day.is-today .dnum {
  color: var(--color-accent);
  font-weight: 700;
}

.dnum {
  font-size: 14px;
  font-weight: 600;
}

.cal-pill,
.list-dot,
.legend-pill {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}

.legend-pill,
.list-dot {
  width: 9px;
  height: 9px;
}

/* debug 260805-legende-cal-illisible-scroll : .cal-legend n'avait aucune règle CSS —
   les libellés s'affichaient sans saut de ligne ni pastille visible (les .legend-pill
   étaient des <span> restés display:inline, donc width/height sans effet). Chaque
   catégorie est maintenant un item flex discret (.legend-item), avec wrap. */
.cal-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-md);
  margin-top: var(--space-xs);
}

.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.pill-extra {
  font-size: 10px;
  font-weight: 700;
  color: var(--color-text-muted);
}

.cat-chpt-jeunes {
  background-color: var(--color-accent);
}

.cat-chpt-jeunes.is-cloture {
  background: transparent;
  border: 1.5px solid var(--color-accent);
}

.cat-crit-1 {
  background-color: var(--cat-crit-1);
}

.cat-crit-1.is-cloture {
  background: transparent;
  border: 1.5px solid var(--cat-crit-1);
}

.cat-crit-2 {
  background-color: var(--cat-crit-2);
}

.cat-crit-2.is-cloture {
  background: transparent;
  border: 1.5px solid var(--cat-crit-2);
}

.cat-feminine {
  background-color: var(--cat-feminine);
}

.cat-feminine.is-cloture {
  background: transparent;
  border: 1.5px solid var(--cat-feminine);
}

.cat-jeudi-vendredi {
  background-color: var(--cat-jeudi-vendredi);
}

.cat-jeudi-vendredi.is-cloture {
  background: transparent;
  border: 1.5px solid var(--cat-jeudi-vendredi);
}

.cat-dimanche {
  background-color: var(--cat-dimanche);
}

.cat-dimanche.is-cloture {
  background: transparent;
  border: 1.5px solid var(--cat-dimanche);
}

.cat-chpt-paris {
  background-color: var(--cat-chpt-paris);
}

.cat-chpt-paris.is-cloture {
  background: transparent;
  border: 1.5px solid var(--cat-chpt-paris);
}

.cat-autre {
  background-color: var(--color-text-muted);
}

.cat-autre.is-cloture {
  background: transparent;
  border: 1.5px solid var(--color-text-muted);
}

.cat-vie-club {
  background-color: var(--cat-vie-club);
}

.cat-vie-club.is-cloture {
  background: transparent;
  border: 1.5px solid var(--cat-vie-club);
}

.cal-list {
  margin-top: var(--space-sm);
}

/* debug 260805-legende-cal-illisible-scroll : max-height + overflow-y:auto forçait un
   scroll interne dès que la liste dépassait 220px (5+ événements) — la page doit
   scroller, pas un sous-conteneur (cf. symptoms Bug B). */
.list-scroll {
}

.list-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  font-family: inherit;
  cursor: pointer;
}

.list-row.selected {
  background-color: var(--color-surface);
  box-shadow: inset 3px 0 0 var(--color-accent);
}

.list-date {
  flex: 0 0 52px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}

.list-label {
  flex: 1 1 auto;
  font-size: 14px;
}

.dashboard-messages-cta {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* Dashboard cards (quick 260715-iy9) — one card per action, consistent with the
   message-card / journee-selector surface pattern. */
.dashboard-card {
  background-color: var(--color-bg);
  border: 2px solid var(--color-accent);
  border-radius: 8px;
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.dashboard-card .text-heading {
  color: var(--color-accent);
  margin: 0;
}

.dashboard-card .btn {
  align-self: flex-start;
}

/* Sheet links list */
.link-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.link-list li {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.link-list a {
  color: var(--color-accent);
  font-weight: 600;
  text-decoration: none;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

.link-list a:hover,
.link-list a:focus {
  text-decoration: underline;
}

.dashboard-card code {
  background-color: var(--color-surface);
  padding: 2px var(--space-xs);
  border-radius: 4px;
  font-size: 14px;
  word-break: break-all;
}

/* Full-width action buttons on mobile for one-handed tap */
@media (max-width: 480px) {
  .dashboard-card .btn {
    align-self: stretch;
  }
}

.dashboard-footer {
  margin-top: var(--space-md);
}

/* Messages page (Phase 3, D-17) — mobile-optimized: one card per tranche
   d'âge, xl gap between stacked cards (03-UI-SPEC.md Spacing Scale). */
.messages-section {
  max-width: 480px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}

.back-link {
  color: var(--color-accent);
  text-decoration: none;
}

.journee-selector {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  background-color: var(--color-surface);
  padding: var(--space-lg);
  border-radius: 8px;
}

.journee-selector select,
.form-field select {
  min-height: 44px;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-accent);
  border-radius: 4px;
  font-size: 16px;
}

.message-card {
  background-color: var(--color-bg);
  border: 1px solid var(--color-surface);
  border-radius: 8px;
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.message-text {
  white-space: pre-wrap;
  font-size: 16px;
  line-height: 1.5;
}

.copy-button--success {
  color: var(--color-success-border);
}

/* D-17/03-UI-SPEC.md: full-width Copier button on mobile, one-handed tap */
@media (max-width: 480px) {
  .message-card .copy-button {
    width: 100%;
  }
}

/* Public parent présence form (05-UI-SPEC.md §3, PRES-05/D-07) — mobile-first,
   opened from a WhatsApp link on a parent's phone. Same container rules as
   .messages-section (no new spacing/color token, reused as-is). */
.presence-form-section {
  max-width: 480px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}

/* Full-width "Enregistrer ma réponse" submit button on mobile, one-handed tap. */
@media (max-width: 480px) {
  .presence-form-section .btn {
    width: 100%;
  }
}

/* Screen-reader-only utility (06-UI-SPEC.md §1.2) — NEVER display:none, the
   element must stay in the accessibility tree and keyboard-focusable.
   Single declaration reused as-is by plan 06-04 for the dashboard date band. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Cartouche de date — écran parents (06-UI-SPEC.md §2.2, 005-B). Le fond/
   padding vient d'ici ; taille/poids de police vient de la classe utilitaire
   .text-heading appliquée en plus dans le HTML. */
.presence-cartouche {
  background-color: var(--color-surface);
  padding: var(--space-lg);
  border-radius: 8px;
}

/* Reset des styles navigateur par défaut d'un <fieldset> pour hériter du
   même rythme vertical que .form-field existant (dispo/voiture passent de
   <select> à <fieldset>/<legend> de boutons segmentés). */
fieldset.form-field {
  border: none;
  padding: 0;
  margin: 0 0 var(--space-md) 0;
}

/* quick 260923-p2o : `.form-field` est en `display: flex`, qui l'emporte
   sur l'attribut `hidden` — le fieldset des places de covoiturage doit
   pourtant rester masqué tant qu'aucune voiture n'est proposée. */
fieldset.form-field[hidden] {
  display: none;
}

/* Boutons segmentés Oui/Non (06-UI-SPEC.md §2.2) — technique radio sr-only +
   label, réutilisée telle quelle par le plan 06-04 pour la bande de dates. */
.segmented-control {
  display: flex;
  gap: var(--space-sm);
}

.segmented-option {
  flex: 1;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-accent);
  border-radius: 4px;
  font-size: 16px;
  font-weight: 600;
  color: var(--color-accent);
  background-color: var(--color-bg);
  cursor: pointer;
}

.segmented-radio:checked + .segmented-option {
  background-color: var(--color-accent);
  color: var(--color-bg);
}

.segmented-radio:focus-visible + .segmented-option {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Bande de cartes-dates Championnat (06-UI-SPEC.md §1.2, D-15) — scroll
   horizontal, une carte par entrée, radio sr-only + label (même technique
   que .segmented-control ci-dessus). */
.journee-band {
  display: flex;
  gap: var(--space-sm);
  overflow-x: auto;
  padding-bottom: var(--space-xs);
  /* Ancre les radios .sr-only (position: absolute) DANS le scroller : sans
     cela leur bloc conteneur est un ancêtre hors de .journee-band, et le
     navigateur n'a aucune boîte vers laquelle défiler quand un radio hors
     écran prend le focus clavier — la bande ne défilait pas (WR-04). */
  position: relative;
}

.date-card {
  min-height: 44px;
  min-width: 112px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-surface);
  border-radius: 8px;
  cursor: pointer;
  text-align: center;
  flex-shrink: 0;
  --dc-jx-ink: var(--color-accent);
  --dc-day-ink: var(--color-text-muted);
  --dc-date-ink: var(--color-text);
}

/* SEUL usage de --color-brand-yellow en background-color dans tout le
   fichier — le jaune est une surface, jamais une encre (D-13a). */
.date-card--next {
  background-color: var(--color-brand-yellow);
  color: var(--color-accent);
  border-color: var(--color-brand-yellow);
  --dc-day-ink: var(--color-accent);
  --dc-date-ink: var(--color-accent);
}

/* Le jaune IDENTIFIE la carte « prochaine échéance » ; ce n'est pas un état.
   La carte is_next étant cochée par défaut (dashboard.html), toute règle
   :checked (0,3,0) qui repeint `background-color` s'applique au MÊME élément
   que .date-card--next (0,1,0) et annule le jaune exactement dans l'état par
   défaut — le seul que l'utilisateur voit en arrivant (D-13a). La sélection
   se marque donc par un anneau intérieur : box-shadow inset, jamais rogné par
   l'overflow-x de .journee-band, et qui n'entre pas en concurrence avec
   l'outline de :focus-visible ci-dessous. NE PAS réintroduire de
   `background-color` ici — cf. test_next_card_yellow_wins_cascade_in_default_state. */
.journee-radio:checked + .date-card {
  box-shadow: inset 0 0 0 3px var(--color-accent);
}

/* Sélection d'une carte NON-next : aucun jaune à préserver, le remplissage
   accent reste le signal principal. Spécificité (0,4,0) > (0,1,0). */
.journee-radio:checked + .date-card:not(.date-card--next) {
  background-color: var(--color-accent);
  color: var(--color-bg);
  border-color: var(--color-accent);
  --dc-jx-ink: var(--color-bg);
  --dc-day-ink: var(--color-bg);
  --dc-date-ink: var(--color-bg);
}

/* Hiérarchie typographique des cartes-dates (sketch 004 index.html:51-53,
   D7-04) — sélecteurs de classe autonomes, sans lien syntaxique avec
   .date-card, donc hors du périmètre du garde-fou de cascade CR-01. L'encre
   vient des custom properties --dc-*-ink posées ci-dessus (héritées). */
.dc-kind {
  min-height: 1.5em;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--dc-day-ink, var(--color-text-muted));
}

.dc-jx {
  min-height: 1.5em;
  font-size: 20px;
  font-weight: 600;
  color: var(--dc-jx-ink, var(--color-accent));
}

.dc-dates {
  min-height: 3em;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
}

.dc-date {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--dc-date-ink, var(--color-text));
}

/* outline-offset NÉGATIF : un anneau extérieur (+2px) est rogné par
   l'overflow-x: auto de .journee-band sur les cartes de bord (WR-04).
   L'outline coexiste avec le box-shadow inset de :checked — les deux
   propriétés sont distinctes, un état sélectionné+focalisé garde les deux. */
.journee-radio:focus-visible + .date-card {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* Hero à 3 actions (06-UI-SPEC.md §1.2/§1.3, D-15/D-21, sketch 004 D7-01) —
   un seul des N heros server-rendered est visible à la fois, basculé par le
   script inline du dashboard. */
.journee-hero--hidden {
  display: none;
}

/* Pastille « prochaine échéance » (sketch 004 index.html:56, D7-02) — le
   jaune ne marque QUE le hero is_next (D-13a). */
.badge-next {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  align-self: flex-start;
  background-color: var(--color-brand-yellow);
  color: var(--color-accent);
  padding: var(--space-xs) var(--space-sm);
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.5;
}

.hero-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Lignes de largeur uniforme (sketch 004 index.html:60-67, D7-01) — plus de
   bouton plein navy : filet léger, fond blanc, icône/texte/chevron alignés. */
.hero-action {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  min-height: 56px;
  padding: var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background-color: var(--color-bg);
  color: var(--color-text);
  text-decoration: none;
  cursor: pointer;
}

.hero-action:hover,
.hero-action:focus {
  border-color: var(--color-accent);
  background-color: var(--color-hover);
}

.hero-action--disabled {
  cursor: not-allowed;
  background-color: var(--color-surface);
  color: var(--color-text-muted);
}

.criterium-published-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.criterium-published-links a {
  color: var(--color-accent);
  overflow-wrap: anywhere;
}

.hero-action-ico {
  width: 40px;
  height: 40px;
  flex: 0 0 40px;
  border-radius: 4px;
  background-color: var(--color-surface);
  color: var(--color-accent);
  display: grid;
  place-items: center;
}

.hero-action-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.hero-action-title {
  font-weight: 600;
  color: var(--color-accent);
}

.hero-action-subtitle {
  color: var(--color-text-muted);
}

.hero-action-go {
  color: var(--color-text-muted);
  flex: 0 0 auto;
}

/* ===== 261105-gqn (sketch 040) : jeu libre réduit pendant les compétitions ===== */
.jl-compet-titre { margin: var(--space-sm) 0 var(--space-xs); font-weight: 800; color: var(--color-accent); }
.jl-slot-jl { font-size: 14px; color: var(--color-accent); }
.jl-slot-jl--off { color: var(--color-text-muted); }
.jl-ferme span { color: var(--color-text-muted); }
.jl-reglage { margin-bottom: var(--space-md); padding: var(--space-sm) var(--space-md); border: 2px solid var(--color-accent); border-radius: 6px; background: var(--color-surface); display: flex; flex-direction: column; gap: var(--space-xs); }
.jl-reglage label[for="reglage-general"] { margin-top: 0; }
.jl-reglage small { color: var(--color-text-muted); font-size: 14px; }
.jl-ligne-tables { display: flex; align-items: center; justify-content: space-between; gap: var(--space-sm); font-size: 16px; }
.jl-ligne-tables select { min-height: 44px; min-width: 84px; font-size: 16px; padding: 0 var(--space-sm); border: 1px solid var(--color-accent); border-radius: 4px; background: var(--color-bg); }
.jl-reduit { display: none; flex-direction: column; gap: var(--space-xs); margin-top: var(--space-xs); }
.jl-admin-slot:has(.jl-switch-input:checked) .jl-reduit { display: flex; }
.jl-reduit-case { display: flex; align-items: center; gap: var(--space-sm); min-height: 44px; padding: 0 var(--space-sm); border: 1px dashed var(--color-accent); border-radius: 4px; font-size: 16px; color: var(--color-accent); cursor: pointer; }
.jl-reduit-case input { width: 22px; height: 22px; accent-color: var(--color-accent); }
.jl-reduit .jl-ligne-tables { display: none; padding: 0 var(--space-sm); }
.jl-reduit:has(.jl-reduit-input:checked) .jl-ligne-tables { display: flex; }
.jl-reduit:has(.jl-reduit-input:checked) .jl-reduit-case { border-style: solid; background: var(--color-surface-strong); font-weight: 700; }
.jl-admin-slot:has(.jl-reduit-input:checked) { background: var(--color-surface); }
.jl-off-note { display: none; margin: 0; padding: var(--space-xs) var(--space-sm); font-size: 14px; border-radius: 4px; background: var(--color-amber-bg); color: var(--color-amber-text); border: 1px solid var(--color-amber-border); }
.jl-admin-page:has(#reglage-general:not(:checked)) .jl-reduit:has(.jl-reduit-input:checked) .jl-off-note { display: block; }
.jl-admin-page:has(#reglage-general:not(:checked)) .jl-reduit .jl-ligne-tables { opacity: .45; }
.jl-perso { font-size: 14px; color: var(--color-text-muted); }

.criterium-section {
  max-width: 720px;
}

.criterium-section h1,
.criterium-section h2 {
  color: var(--color-accent);
  margin: 0;
}

.criterium-section form {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.criterium-section input[type="file"] {
  min-height: 44px;
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  font-size: 16px;
}

.criterium-section input[type="file"]:focus-visible,
.criterium-section .btn:focus-visible,
.criterium-section a:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.criterium-section .link-list {
  overflow-wrap: anywhere;
}

/* Import contacts Google (Phase 9, 09-UI-SPEC.md) — même forme que
   .dashboard-section, nouvelle classe (720px, page desktop). */
.contacts-import-section {
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}

.contacts-import-section table {
  width: 100%;
  border-collapse: collapse;
}

.contacts-import-section th {
  background-color: var(--color-surface);
  color: var(--color-accent);
  text-align: left;
  padding: var(--space-sm) var(--space-md);
  font-size: 14px;
  font-weight: 600;
}

.contacts-import-section td {
  padding: var(--space-sm) var(--space-md);
  font-size: 14px;
}

input[type="checkbox"] {
  accent-color: var(--color-accent);
}

/* WR-05 (09.1-REVIEW.md) : lockup icône + titre de page (ex. l'en-tête de
   /evaluations) — même layout que .brand-lockup mais avec le gap "md",
   extrait ici plutôt que laissé en `style="..."` inline dans le template
   (discipline du dépôt : tout le layout passe par style.css). */
.page-header-lockup {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

/* Classement mensuel — page écran et prévisualisation du PDF mural. */
.ranking-page {
  max-width: 1120px;
  margin: 0 auto;
}
.ranking-page-logo { width: 52px; height: 52px; object-fit: contain; }
.ranking-intro { max-width: 680px; color: var(--color-text-muted); }
.ranking-controls { display: flex; align-items: end; justify-content: space-between; gap: var(--space-md); margin: var(--space-lg) 0; padding: var(--space-md); background: var(--color-surface); border-radius: 8px; }
.ranking-generate-form, .ranking-actions { display: flex; align-items: end; gap: var(--space-sm); flex-wrap: wrap; }
.ranking-generate-form label { font-size: 14px; font-weight: 600; }
.ranking-generate-form select { min-height: 44px; padding: 0 var(--space-sm); border: 1px solid var(--color-border); border-radius: 4px; background: var(--color-bg); font-size: 16px; }
.ranking-message { padding: var(--space-sm) var(--space-md); margin: var(--space-md) 0; border-radius: 4px; }
.ranking-message--error { color: var(--color-error); border: 1px solid var(--color-error); }
.ranking-message--success { color: var(--color-success-border); background: var(--color-success-bg); border: 1px solid var(--color-success-border); }
.ranking-population-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-md); margin-bottom: var(--space-lg); }
.ranking-population-card { display: flex; flex-direction: column; gap: var(--space-xs); padding: var(--space-md); color: var(--color-text); border: 2px solid var(--color-border); border-radius: 8px; text-decoration: none; }
.ranking-population-card strong { color: var(--color-accent); font-size: 18px; }
.ranking-population-card span:last-child { color: var(--color-text-muted); }
.ranking-population-card--active { border-color: var(--color-accent); background: var(--color-surface); }
.ranking-print-sheet { padding: var(--space-lg); border: 1px solid var(--color-border); }
.ranking-print-header { display: flex; align-items: center; gap: var(--space-md); margin-bottom: var(--space-md); }
.ranking-print-header img { width: 44px; height: 44px; object-fit: contain; }
.ranking-print-header h2 { margin: 0; color: var(--color-accent); }
.ranking-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-lg); }
.ranking-table { width: 100%; border-collapse: collapse; }
.ranking-table th { padding: var(--space-sm); color: var(--color-accent); background: var(--color-surface); text-align: left; font-size: 13px; }
.ranking-table td { padding: 7px var(--space-sm); border-bottom: 1px solid var(--color-border); font-size: 14px; vertical-align: top; }
.ranking-points { color: var(--color-accent); font-weight: 700; text-align: right; }
.ranking-license { display: block; color: var(--color-text-muted); font-size: 11px; }
.ranking-generated { margin: var(--space-md) 0 0; color: var(--color-text-muted); font-size: 12px; }
.ranking-history-title { margin-top: var(--space-xl); color: var(--color-accent); }
.ranking-history-row { display: grid; grid-template-columns: 1fr 100px 100px; gap: var(--space-sm); padding: var(--space-sm) 0; border-bottom: 1px solid var(--color-border); }
.ranking-history-row a { color: var(--color-accent); }
.ranking-not-ready { color: var(--color-text-muted); }

@media (max-width: 600px) {
  .ranking-controls, .ranking-generate-form, .ranking-actions { align-items: stretch; flex-direction: column; }
  .ranking-generate-form select, .ranking-generate-form button, .ranking-actions .btn { width: 100%; }
  .ranking-population-cards, .ranking-columns { grid-template-columns: 1fr; }
  .ranking-history-row { grid-template-columns: 1fr 76px 76px; font-size: 14px; }
}

@media print {
  @page { size: A4 landscape; margin: 10mm; }
  .site-header, .ranking-intro, .ranking-controls, .ranking-population-cards, .ranking-history-title, .ranking-history { display: none !important; }
  main { padding: 0; }
  .ranking-page, .ranking-print-sheet { max-width: none; border: 0; padding: 0; }
  .ranking-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8mm; }
  .ranking-table th, .ranking-table td { padding: 2mm 1.5mm; }
  .ranking-table { break-inside: auto; }
  .ranking-table tr { break-inside: avoid; }
}

/* /evaluations — étapes verticales numérotées (Phase 19, D-14, 19-UI-SPEC
   §7.1). Reprend le conteneur existant .upload-section (max-width 480px,
   inchangé) ; chaque étape est un bloc simple dans ce flux, jamais une
   carte encadrée séparée. */
.eval-steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.eval-step {
  display: flex;
  gap: var(--space-md);
  align-items: flex-start;
}

.eval-step-num {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-surface);
  color: var(--color-accent);
  font-size: 20px;
  font-weight: 600;
  display: grid;
  place-items: center;
}

.eval-step-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  flex: 1;
  min-width: 0;
}

.eval-step-title {
  font-weight: 600;
  color: var(--color-accent);
  margin: 0;
}

.eval-step-desc {
  color: var(--color-text-muted);
  margin: 0;
}

.eval-step-action {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
}

.eval-public-link {
  overflow-wrap: anywhere;
  color: var(--color-accent);
}

/* /competitions — agenda public des compétitions (Phase 19, 19-UI-SPEC.md
   §7-§10, D-09, D-10). Bandeau et pied de page réutilisent .planning-* sans
   règle supplémentaire ; ce bloc ne couvre que l'espace de noms
   .competitions-* et .filter-chip. Déclaré AVANT .planning-page (garde
   tests/test_planning_layout_css.py::_planning_section) : .competitions-day
   est une grille, seule exception hors .planning-entry/.planning-slot pour
   la section suivante. */

.competitions-page {
  max-width: 720px;
  margin: 0 auto;
}

.competitions-page [data-cat="chpt-jeunes"] { --c: var(--color-accent); }
.competitions-page [data-cat="crit-1"] { --c: var(--cat-crit-1); }
.competitions-page [data-cat="crit-2"] { --c: var(--cat-crit-2); }
.competitions-page [data-cat="jeudi-vendredi"] { --c: var(--cat-jeudi-vendredi); }
.competitions-page [data-cat="feminine"] { --c: var(--cat-feminine); }
.competitions-page [data-cat="dimanche"] { --c: var(--cat-dimanche); }
.competitions-page [data-cat="chpt-paris"] { --c: var(--cat-chpt-paris); }
.competitions-page [data-cat="autre"] { --c: var(--color-text-muted); }
.competitions-page [data-cat="vie-club"] { --c: var(--cat-vie-club); }

.competitions-nextcard--hidden, .competitions-dls--hidden, .competitions-view-panel--hidden, .competitions-is-filtered-out {
  display: none;
}

.competitions-xlink {
  text-align: right;
  margin: 0 0 var(--space-sm);
}

.competitions-xlink a, .competitions-page .planning-footer a {
  color: var(--color-accent);
}

/* Carte prochaine échéance (D-09, emploi n°2 du jaune de marque). */
.competitions-nextcard {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
  border-radius: 8px;
  padding: var(--space-md);
  margin: 0 0 var(--space-md);
}

.competitions-nextcard--over {
  /* Saison terminée : ce n'est pas une échéance, donc pas de jaune. */
  background: var(--color-surface);
  color: var(--color-text);
}

.competitions-nextcard-kicker {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
}

.competitions-nextcard-date {
  font-size: 20px;
  font-weight: 600;
  margin: var(--space-xs) 0;
}

.competitions-nextcard-items {
  font-size: 16px;
  margin: 0;
  padding-left: var(--space-md);
}

.competitions-nextcard-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-accent);
}

/* Contrôles — bascule de vue et filtre à pastilles (D-03). */
.competitions-controls {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin: 0 0 var(--space-md);
  min-width: 0;
}

.competitions-filters {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-sm);
  overflow-x: auto;
  padding-bottom: var(--space-xs);
}

.filter-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 44px;
  padding: 0 var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 14px;
  font-weight: 400;
  white-space: nowrap;
  cursor: pointer;
}

.filter-chip.is-active {
  font-weight: 600;
  border-color: var(--color-accent);
  background: var(--color-surface);
  color: var(--color-accent);
}

.filter-chip:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Vue « Par compétition » (19-UI-SPEC.md §7.3). */
.competitions-family-title {
  font-size: 14px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--color-text-muted);
  margin: var(--space-lg) 0 var(--space-sm);
}

.competitions-card {
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--c, var(--color-accent));
  border-radius: 8px;
  margin-bottom: var(--space-sm);
}

.competitions-card-summary {
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-sm);
  min-height: 48px;
  padding: var(--space-sm) var(--space-md);
  cursor: pointer;
  background: var(--color-surface);
  border-radius: 8px;
}

.competitions-card-summary::-webkit-details-marker {
  display: none;
}

.competitions-card-summary::before {
  content: "›";
  color: var(--color-accent);
  transition: transform .1s ease;
}

details[open] > .competitions-card-summary::before {
  transform: rotate(90deg);
}

.competitions-card-summary:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.competitions-card-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--color-accent);
  overflow-wrap: break-word;
}

.competitions-card-name small {
  display: block;
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-muted);
}

.competitions-nx {
  font-size: 14px;
  white-space: nowrap;
  color: var(--color-text-muted);
}

.competitions-nx--hot {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
  border-radius: 4px;
  padding: 2px 6px;
  font-weight: 600;
}

.competitions-card-body {
  display: flex;
  flex-direction: column;
  padding: var(--space-sm) var(--space-md);
}

.competitions-card-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  padding: var(--space-xs) 0;
  border-bottom: 1px solid var(--color-hover);
  font-size: 14px;
}

.competitions-rd {
  flex: 0 0 auto;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.competitions-rdetail {
  min-width: 0;
  overflow-wrap: anywhere;
}

.competitions-card-row--past {
  color: var(--color-text-muted);
  background: var(--color-bg);
}

.competitions-card-row--next {
  /* Pas de jaune ici (D-09) : une ligne de liste n'est pas une des deux
     surfaces autorisées pour --color-brand-yellow. */
  color: var(--color-accent);
  font-weight: 600;
}

.competitions-tag {
  border: 1px dashed var(--c, var(--color-border));
  border-radius: 4px;
  padding: 1px 6px;
  text-transform: uppercase;
  font-size: 14px;
  font-weight: 600;
  font-style: normal;
  color: var(--color-text);
}

/* Phase 24 (CAL-02) : mentions de clôture et encart « Prochaines clôtures ».
   Aucun jaune de marque (UI-SPEC : les clôtures n'utilisent pas l'accent) ;
   tailles sur l'échelle 14/16/20/28 (test_font_sizes_stay_on_the_four_step_scale,
   le 12/13 px du UI-SPEC est ramené à 14). */
.competitions-dl {
  font-size: 14px;
  font-weight: 700;
  color: var(--color-text-muted);
}

.competitions-dl::before {
  content: "\23F1\00A0";
}

.competitions-dl--soon {
  color: var(--color-stop-text);
}

.competitions-dl--past {
  font-weight: 600;
  text-decoration: line-through;
  color: var(--color-text-muted);
}

.competitions-dls-for {
  display: block;
  margin-top: var(--space-xs);
}

.competitions-dls {
  border: 1.5px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-bg);
  padding: 12px 14px;
  margin: var(--space-sm) 0;
}

.competitions-dls-row {
  display: flex;
  gap: var(--space-sm);
  font-size: 14px;
  padding: 2px 0;
}

.competitions-dls-date {
  flex: 0 0 78px;
  font-weight: 700;
  color: var(--color-accent);
}

/* V-3 (revue 24) : délai « J-n » sous la date, cible (« Tour 2 ») en sous-ligne. */
.competitions-dls-date i {
  display: block;
  font-style: normal;
  font-size: 14px;
  color: var(--color-text-muted);
}

.competitions-dls-row small {
  display: block;
  font-size: 14px;
  color: var(--color-text-muted);
}

.competitions-dls summary {
  font-size: 14px;
  font-weight: 800;
  color: var(--color-accent);
  cursor: pointer;
}

/* Tableau équipes (D-02). */
.competitions-phase {
  margin: var(--space-sm) 0;
}

.competitions-phase-title {
  margin: 0 0 var(--space-xs);
  font-size: 14px;
  font-weight: 600;
  color: var(--color-accent);
}

.competitions-phcl, .competitions-phcl--past {
  font-weight: 400;
  color: var(--color-text-muted);
}

.competitions-phcl--next {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
  border-radius: 4px;
  padding: 1px 4px;
  font-weight: 600;
}

.competitions-eq-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  font-size: 14px;
}

.competitions-eq-table th {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .03em;
  text-align: center;
  padding: var(--space-xs) 2px;
  border-bottom: 3px solid var(--c, transparent);
  overflow-wrap: anywhere;
}

/* Écart assumé : 56px au lieu des 48px de l'UI-SPEC §8, parce que
   « Barrage » en 14px dépasse 48px. Chaîne de largeur à 360px :
   360 − 48 (padding de main) − 5 (bordures de carte) − 32 (padding du
   corps) − 56 = 219, divisé en 4 colonnes de 54px, ce qui suffit à
   « 17/09 » avec le padding du badge — vérifié dans le navigateur par la
   tâche 2 du plan 19-04. */
.competitions-eq-table thead th:first-child, .competitions-eq-table th[scope="row"] {
  width: 56px;
}

.competitions-eq-table th[scope="row"] {
  text-align: left;
  text-transform: none;
  letter-spacing: 0;
  border-bottom: 1px solid var(--color-hover);
}

.competitions-eq-table td {
  text-align: center;
  padding: var(--space-sm) 2px;
  border-bottom: 1px solid var(--color-hover);
  font-variant-numeric: tabular-nums;
}

.competitions-cell--next span {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
  border-radius: 4px;
  padding: 1px 4px;
  font-weight: 600;
}

.competitions-cell--past {
  color: var(--color-text-muted);
  background: var(--color-bg);
}

.competitions-cell--na {
  color: var(--color-border);
}

/* Vue « Par date » (19-UI-SPEC.md §7.4). */
.competitions-month {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--color-bg);
  margin: 0;
  padding: var(--space-sm) 0 var(--space-xs);
  font-size: 14px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
}

.competitions-now {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin: var(--space-sm) 0;
  color: var(--color-accent);
  text-transform: uppercase;
  font-size: 14px;
  font-weight: 600;
}

.competitions-now::before, .competitions-now::after {
  content: "";
  flex: 1;
  border-top: 2px solid var(--color-accent);
}

.competitions-day {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr);
  gap: var(--space-sm);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--color-hover);
}

.competitions-day-num {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--space-xs) 0;
  color: var(--color-text-muted);
}

.competitions-day-num small {
  font-size: 14px;
  text-transform: uppercase;
}

.competitions-day-num b {
  font-size: 20px;
  font-weight: 600;
}

.competitions-day-num.is-next {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
  border-radius: 6px;
}

.competitions-day--past .competitions-day-num {
  color: var(--color-text-muted);
  background: var(--color-bg);
}

.competitions-day-items {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  min-width: 0;
}

.competitions-nf {
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--color-accent);
}

.competitions-item {
  border-left: 4px solid var(--c, var(--color-border));
  background: var(--color-surface);
  border-radius: 0 6px 6px 0;
  padding: var(--space-xs) var(--space-sm);
  min-width: 0;
}

.competitions-item b {
  display: block;
  font-size: 16px;
  font-weight: 400;
  overflow-wrap: break-word;
}

.competitions-item-sub {
  display: block;
  font-size: 14px;
  color: var(--color-text-muted);
}

.competitions-kk {
  display: block;
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--color-text);
}

.competitions-item--cloture {
  background: var(--color-bg);
  border: 1px dashed var(--c, var(--color-border));
  border-left: 4px dashed var(--c, var(--color-border));
}

.competitions-item--past {
  background: var(--color-bg);
  color: var(--color-text-muted);
}

.competitions-empty {
  color: var(--color-text-muted);
}

/* Jeu libre (Phase 20, 20-UI-SPEC.md §6.1/§6.2/§9) — chrome commun aux
   écrans publics et aux deux écrans admin.

   Déclaré ICI, avant la section du planning public, délibérément :
   tests/test_planning_layout_css.py::_planning_section lit TOUT ce qui suit
   `.planning-page {` comme appartenant au planning. Même précaution que le
   bloc .competitions-* de la Phase 19.

   Aucun littéral de couleur dans ce bloc (UX-11) et aucune taille de police
   hors {14, 16, 20, 28}. Les cibles tactiles restent écrites en littéral
   (44px / 40px) : `var(--tap-target)` n'existe pas dans ce fichier et est
   proscrit par l'UI-SPEC §0.6. */

.jl-page {
  /* Même largeur que .presence-form-section : mobile d'abord, un seul flux
     vertical, jamais de grille à deux colonnes (UI-SPEC §8). */
  max-width: 480px;
  margin: 0 auto;
}

.jl-admin-page {
  /* Même largeur que le public (D-17, « c'est le même utilisateur ») :
     l'administrateur consulte aussi depuis un téléphone. */
  max-width: 480px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}

/* Navigation persistante des écrans identifiés : 3 onglets Créneau / Semaine
   / [initiale] Prénom (quick 260915-g7p, point 4 — remplace le badge prénom
   en haut ET l'ancienne nav Accueil/Ma fiche), avec une zone tactile
   confortable et le respect des appareils à encoche. Les écrans transitoires
   ne rendent jamais ce composant. */
.jl-bottom-nav {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 10;
  display: flex;
  justify-content: center;
  padding: var(--space-xs) var(--space-sm) env(safe-area-inset-bottom);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
}

.jl-bottom-nav a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  min-height: 44px;
  gap: var(--space-xs);
  color: var(--color-text-muted);
  font-size: 14px;
  font-weight: 400;
  text-decoration: none;
}

.jl-bottom-nav a.is-active {
  color: var(--color-accent);
  font-weight: 600;
}

/* Le trait jaune au-dessus de l'onglet actif (point 4, maquette validée). */
.jl-bottom-nav a.is-active::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 28px;
  height: 3px;
  background: var(--color-brand-yellow);
}

.jl-tab-ico {
  font-size: 20px;
  line-height: 1;
}

/* La pastille ronde de l'onglet « Moi », initiale du prénom en blanc sur
   fond accent. */
.jl-tab-me {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-bg);
  font-size: 14px;
  font-weight: 600;
}

.jl-bottom-nav a:focus-visible,
.jl-password-toggle:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.jl-page:has(+ .jl-bottom-nav) {
  padding-bottom: calc(72px + env(safe-area-inset-bottom));
}

.jl-password-field {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.jl-password-field input {
  min-width: 0;
  flex: 1 1 auto;
}

.jl-password-toggle {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 0 var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-surface);
  color: var(--color-accent);
  font-size: 14px;
  font-weight: 600;
}

/* font-weight 700 au titre de l'AMENDEMENT 20-UI-SPEC.md §3.0, qui reconnaît
   un troisième poids déjà présent en production (.fo-title, .cal-day-header,
   .list-date…). Ses DEUX seuls usages autorisés : les kickers/labels
   uppercase compacts (≤14px) et les valeurs numériques mises en avant à côté
   d'un dénominateur. Jamais du texte courant. */
.jl-kicker {
  margin: 0 0 var(--space-xs);
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-text-muted);
}

/* Porte (quick 260915-g7p, point 6). */
.jl-hint {
  margin: var(--space-sm) 0 var(--space-md);
  color: var(--color-text-muted);
}

.jl-porte-entrer {
  width: 100%;
}

.jl-promesses {
  list-style: none;
  margin: var(--space-lg) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-sm);
}

.jl-promesses li {
  display: flex;
  gap: var(--space-sm);
  align-items: flex-start;
  font-size: 14px;
}

.jl-promesse-ico {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--color-surface);
  display: grid;
  place-items: center;
  font-size: 14px;
}

/* Pastilles de cours/formule (D-32). Toujours accompagnées de leur libellé
   texte, jamais une couleur seule (WCAG 1.4.1). Ces règles ne référencent
   QUE les tokens --jl-pill-N-* de :root. */
.jl-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0 var(--space-sm);
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

.jl-pill--1 { background: var(--jl-pill-1-bg); color: var(--jl-pill-1-ink); }
.jl-pill--2 { background: var(--jl-pill-2-bg); color: var(--jl-pill-2-ink); }
.jl-pill--3 { background: var(--jl-pill-3-bg); color: var(--jl-pill-3-ink); }
.jl-pill--4 { background: var(--jl-pill-4-bg); color: var(--jl-pill-4-ink); }
.jl-pill--5 { background: var(--jl-pill-5-bg); color: var(--jl-pill-5-ink); }
.jl-pill--6 { background: var(--jl-pill-6-bg); color: var(--jl-pill-6-ink); }
.jl-pill--7 { background: var(--jl-pill-7-bg); color: var(--jl-pill-7-ink); }
.jl-pill--8 { background: var(--jl-pill-8-bg); color: var(--jl-pill-8-ink); }
.jl-pill--9 { background: var(--jl-pill-9-bg); color: var(--jl-pill-9-ink); }
.jl-pill--10 { background: var(--jl-pill-10-bg); color: var(--jl-pill-10-ink); }
.jl-pill--11 { background: var(--jl-pill-11-bg); color: var(--jl-pill-11-ink); }

/* Largeurs fixes NON tokenisées (20-UI-SPEC.md §2), déclarées ici avec le
   reste du contrat de largeur de la phase pour que le budget à 360px soit
   vérifiable mécaniquement dès maintenant
   (tests/test_jeulibre_layout_css.py). Les écrans qui les consomment
   arrivent aux plans 20-07 (Ma fiche) et 20-09 (Journal admin). */
.jl-idlist-key {
  width: 96px; /* assez large pour « Ma catégorie » sans retour à la ligne */
  flex: 0 0 96px;
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.jl-log-hour {
  width: 82px;
  flex: 0 0 82px;
  font-size: 14px;
  font-weight: 700;
  /* 82px ne tiennent pas un horaire complet (« 12h00 → 14h00 ») : il
     s'enroule sur deux lignes, mot par mot (UI-SPEC §2). Jamais de `nowrap`
     ici : il le faisait déborder sur la colonne « qui » (quick 261023-osb). */
  /* Chasse tabulaire : sans elle, les horaires du journal ne s'alignent pas
     verticalement d'un jour à l'autre (plan 20-10). */
  font-variant-numeric: tabular-nums;
}

.jl-log-count {
  min-width: 42px;
  text-align: right;
  font-size: 14px;
  /* 700 sur le chiffre, au titre de l'amendement §3.0 (« valeur numérique
     mise en avant à côté d'un dénominateur »), 600 sur le dénominateur. */
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.jl-log-count small {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text-muted);
}

/* --- Écran 2 : identification (plan 20-07, 20-UI-SPEC.md §6.3) ---------- */

/* Champ de recherche : la loupe est décorative (aria-hidden), le bouton
   « Effacer » est en bout de ligne. Colonne unique (§8) : c'est un flex, pas
   une grille. */
.jl-page .search {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0 var(--space-sm);
  border: 1px solid var(--color-surface);
  border-radius: 4px;
  background: var(--color-bg);
}

.jl-page .search:focus-within {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.jl-page .mag {
  flex: 0 0 auto;
  font-size: 20px;
  color: var(--color-text-muted);
}

/* ÉCART TYPOGRAPHIQUE DOCUMENTÉ (20-UI-SPEC.md §3) : le champ reprend la
   taille de `input[type=text]` déjà en production (16px, qui empêche le zoom
   automatique d'iOS au focus), et NON la règle générale de remontée, qui
   donnerait 20px — inutilement grand pour un champ de saisie. */
.jl-page input[type="search"] {
  flex: 1 1 auto;
  min-height: 44px;
  border: 0;
  padding: var(--space-sm) 0;
  background: transparent;
  color: var(--color-text);
  font-size: 16px;
}

.jl-page input[type="search"]:focus {
  /* Le halo est porté par le conteneur (`:focus-within` ci-dessus) : deux
     contours imbriqués se liraient comme une erreur de rendu. */
  outline: none;
}

/* Cible tactile CORRIGÉE de 26×26 (maquette) à 40×40, glyphe inchangé et
   centré — plancher des actions secondaires compactes (§2/§6.3). Le littéral
   est délibéré : `var(--tap-target)` n'existe pas dans ce fichier (§0.6). */
.jl-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: 40px;
  min-height: 40px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 16px;
}

.jl-search-hint {
  margin: var(--space-sm) 0 0;
  color: var(--color-text-muted);
}

/* Masqué par défaut par son attribut `hidden` ; le <noscript> de la page le
   révèle, puisque c'est LUI qui rend la recherche utilisable sans script. */
.jl-search-submit {
  margin-top: var(--space-md);
}

.jl-results {
  list-style: none;
  margin: var(--space-md) 0 0;
  padding: 0;
}

/* Ligne de résultat : action primaire, donc 44px (§2). Elle ne porte QUE le
   prénom et le nom (D-21) — aucune sous-ligne à styler. */
.jl-result {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  width: 100%;
  min-height: 44px;
  padding: var(--space-sm) var(--space-md);
  border: 0;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text);
  font-size: 16px;
  font-weight: 600;
  text-align: left;
}

.jl-result:hover {
  background: var(--color-hover);
}

/* Le surlignage est posé par app/jeulibre/recherche.py, sur le prénom ET sur
   le nom SÉPARÉMENT (D-20) : deux <mark> possibles par ligne. */
.jl-result mark {
  background: var(--color-brand-yellow);
  color: var(--color-text);
}

.jl-results-empty {
  padding: var(--space-md);
}

.jl-results-empty-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}

.jl-results-empty-body {
  margin: var(--space-xs) 0 0;
  color: var(--color-text-muted);
}

/* --- Écran 4 : ma fiche (plan 20-07, 20-UI-SPEC.md §6.4) ---------------- */

/* Bordure épaisse réservée aux cartes d'IDENTITÉ, cohérente avec
   .dashboard-card. `overflow: hidden` pour que la dernière ligne de la liste
   n'échappe pas au rayon de bordure. */
.jl-idcard {
  margin: var(--space-lg) 0;
  border: 2px solid var(--color-accent);
  border-radius: var(--space-md);
  overflow: hidden;
}

.jl-idcard-top {
  padding: var(--space-md);
  background: var(--color-surface);
}

.jl-idlist {
  list-style: none;
  margin: 0;
  padding: 0;
}

.jl-idlist li {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  padding: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

.jl-idlist li:last-child {
  border-bottom: 0;
}

.jl-idlist-value {
  /* Cours MULTIPLES séparés par un espacement vertical (--space-sm), jamais
     par une virgule inline (§6.4). */
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  font-size: 14px;
}

.jl-idlist-entry {
  display: flex;
  flex-direction: column;
}

.jl-idlist-value small {
  color: var(--color-text-muted);
  font-size: 14px;
}

.jl-banner {
  margin: var(--space-lg) 0;
  font-size: 14px;
}

.jl-cta {
  width: 100%;
  min-height: 44px;
}

/* « Non » et « Ce n'est pas moi » : actions de retrait, jamais mises en
   avant — elles ne doivent pas concurrencer visuellement le CTA principal. */
.jl-cta-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-sm);
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 16px;
  font-weight: 600;
}

/* --- Écrans 5, 6 et 7 : tableau, jour J et semaine (plan 20-12,
   20-UI-SPEC.md §3/§4.3/§4.5/§6.6/§6.8) ---------------------------------- */

/* Toutes les tailles de police de ce lot appartiennent à l'échelle du projet
   {14, 16, 20, 28}, et tous les poids 700 relèvent de l'amendement §3.0
   (kickers/labels uppercase compacts, et valeurs numériques mises en avant à
   côté d'un dénominateur). Aucune cible tactile ne descend sous 40px, et
   toutes sont écrites en LITTÉRAL : `var(--tap-target)` n'existe pas dans ce
   fichier et est proscrit (§0.6). */

.jl-tableau,
.jl-semaine,
.jl-bienvenue {
  /* Colonne unique à TOUTES les largeurs (§8), comme /planning et
     /competitions : jamais de grille à deux colonnes sur ces trois écrans. */
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* 261023-k7t : une section par semaine, empilées sur l'écran semaine. */
.jl-semaine-bloc {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  /* 261023-l4d : 0 et non un espace — avec une marge, le bas de la section
     précédente restait visible et son bandeau collant y dépassait, tronqué. */
  scroll-margin-top: 0;
}

.jl-semaine-bloc + .jl-semaine-bloc {
  margin-top: var(--space-md);
}

/* Bandeau de collision compétition du jour J (§5.6). Ce n'est PAS un
   avertissement : la salle reste ouverte, et c'est exactement ce que le
   bandeau dit. D'où la surface neutre — l'ambre est réservé au bandeau des
   clés (§4.3) et le jaune de marque aux deux emplois de D-13d (§4.5). */
.jl-collision {
  margin: 0;
  padding: var(--space-sm) var(--space-md);
  border-radius: 4px;
  background: var(--color-surface);
  color: var(--color-text);
}

/* --- Le sous-titre d'ouverture (point 7, 260915-g7p) -------------------- */

.jl-subtitle {
  margin: 0;
  font-size: 14px;
  color: var(--color-text-muted);
}

.jl-subtitle strong {
  color: var(--color-text);
}

/* Titre des écrans du jeu libre (fast 260915-g7p) : la marge par défaut du
   navigateur sur <h1> (≈0,67em) creusait un blanc de plus de 40px entre le
   kicker, le titre et le sous-titre — précisément le haut de page que la
   refonte mobile voulait resserrer pour les petits téléphones. */
.jl-page h1.text-display {
  margin: 0 0 var(--space-xs);
}

/* --- Le compteur, point focal de D-26 ---------------------------------- */

/* ÉCART TYPOGRAPHIQUE DOCUMENTÉ (§3) : la maquette pose 36px, PLAFONNÉ à 28
   — aucune valeur du projet ne va au-delà de l'échelle. */
.jl-count {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin: var(--space-md) 0 0;
}

.jl-count b {
  font-size: 28px;
  font-weight: 600;
  line-height: 1;
  color: var(--color-accent);
}

/* Point 8 (260915-g7p) : la jauge n/dénominateur a disparu — le compteur
   dit désormais « N joueurs présents · N tables », sans « on tourne ». */
.jl-count-unite {
  font-size: 16px;
  font-weight: 600;
  color: var(--color-text);
}

.jl-count-tables {
  font-size: 14px;
  color: var(--color-text-muted);
}

/* Réponse à un geste REFUSÉ — créneau fermé pendant la lecture, clés prises
   par quelqu'un d'autre, heure hors bornes (vérification du 2026-09-14,
   défaut A). Reprend les jetons ambre déjà en service, jamais une teinte
   inventée ; le texte porte seul l'information (WCAG 1.4.1), la couleur ne
   fait que l'accompagner. */
.jl-notice {
  margin: 0 0 var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-amber-border);
  border-radius: 4px;
  background: var(--color-amber-bg);
  color: var(--color-amber-text);
  font-size: 14px;
  font-weight: 600;
}

/* Le créneau vide POSE UNE QUESTION, il n'affiche pas « 0 » (§5.5). */
.jl-count-empty {
  font-size: 20px;
  font-weight: 600;
  color: var(--color-text);
}

/* --- Les deux emplois du jaune de marque, amendement D-13d (§4.5) ------- */

/* `.jl-cta--empty` (personne n'a encore répondu) et `.jl-cta--confirmed`
   (le bouton dont l'intention vient d'être confirmée — « ✓ Je viens » ou
   « ✓ Pas là », D-27 amendée par le quick 260914-wad) portent la MÊME
   déclaration, et sont MUTUELLEMENT EXCLUSIFS sur un même bouton à un
   instant donné : le gabarit les applique par des conditions Jinja2
   exclusives, jamais par deux conditions cumulables. Encre
   `var(--color-accent)` dans les deux cas (8.35:1, ratio déjà établi D-13a).
   Le TEXTE du bouton change en plus de la couleur (« ✓ Je viens », « ✓ Pas
   là ») : la confirmation ne repose jamais sur la seule teinte
   (WCAG 1.4.1). */
.jl-cta--empty {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
}

.jl-cta--confirmed {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
}

/* Reprend `.btn.btn-primary` : rayon 4px de la charte, jamais les 12px de la
   maquette (§6.6). Redéclaré ici plutôt que laissé à l'héritage, pour que le
   contrat soit lisible à l'endroit où il est décidé. */
.jl-cta {
  border-radius: 4px;
}

.jl-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* D-27 amendée (quick 260914-wad) : « Je viens » et « Pas là » côte à côte
   à 360px, jamais l'un sous l'autre (§8 : jamais grid-template-columns).
   Cibles ≥ 44px héritées de .btn/.jl-cta, jamais redéclarées ici. Le jaune
   de marque reste réservé à .jl-cta--empty/--confirmed ci-dessus. */
.jl-reponse {
  display: flex;
  gap: var(--space-sm);
}

.jl-reponse .jl-cta {
  flex: 1 1 0;
  min-width: 0;
}

/* --- L'heure d'arrivée (§6.6) ------------------------------------------ */

/* `<summary>` d'un `<details>` NATIF : le bloc s'ouvre sans une ligne de
   script. Cible CORRIGÉE — la maquette ne déclare qu'un `padding`. */
.jl-addtime {
  display: flex;
  align-items: center;
  min-height: 40px;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-accent);
  text-decoration: underline;
  cursor: pointer;
  list-style: none;
}

.jl-addtime::-webkit-details-marker {
  display: none;
}

.jl-arrival-details > .jl-addtime:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.jl-arrival-why {
  margin: var(--space-xs) 0;
  color: var(--color-amber-text);
}

.jl-arrival {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.jl-arrival-label {
  font-size: 14px;
  color: var(--color-text-muted);
}

.jl-arrival select {
  min-height: 40px;
  padding: 0 var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 16px;
  font-weight: 400;
}

/* Cible CORRIGÉE : la maquette ne déclare aucune hauteur pour « retirer ». */
.jl-arrival-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  min-height: 40px;
  padding: 0 var(--space-sm);
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 14px;
  font-weight: 400;
  text-decoration: underline;
}

/* --- Le bandeau des clés (§4.3/§5.5) ----------------------------------- */

/* RÉUTILISATION de l'ambre déjà en production, jamais un nouveau token : la
   maquette porte un `--warn-bg` qui lui est propre, visuellement
   interchangeable avec `--color-amber-bg` et AA comme lui. Le projet préfère
   systématiquement réutiliser un token existant plutôt qu'en dupliquer un
   quasi identique (§4.3). La valeur de la maquette n'est PAS recopiée ici :
   tests/test_criterium_ui.py lit ce fichier commentaires compris, et une
   teinte citée en commentaire s'y ferait prendre pour un littéral. */
.jl-keys {
  padding: var(--space-md);
  border-radius: 4px;
  background: var(--color-amber-bg);
  color: var(--color-amber-text);
  border: 1px solid var(--color-amber-border);
}

/* État CALME (quelqu'un y va) : surface neutre, aucun filet — le bandeau
   cesse d'appeler l'œil dès que la seule dépendance collective de la page
   est levée. */
.jl-keys--open {
  background: var(--color-surface);
  color: var(--color-text-muted);
  border: 0;
}

.jl-keys--alert {
  background: var(--color-amber-bg);
  color: var(--color-amber-text);
  border: 1px solid var(--color-amber-border);
}

.jl-keys-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text);
}

.jl-keys-body {
  margin: var(--space-xs) 0 0;
}

.jl-keys-cta {
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-md);
  border-radius: 4px;
  font-size: 16px;
  font-weight: 600;
}

/* Cible CORRIGÉE de 38px (maquette) à 40px. */
.jl-keys-release {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  min-height: 40px;
  margin-top: var(--space-sm);
  padding: 0 var(--space-sm);
  border: 0;
  background: transparent;
  color: var(--color-amber-text);
  font-size: 14px;
  font-weight: 400;
  text-decoration: underline;
}

/* --- La liste des noms (§6.6) ------------------------------------------ */

.jl-names {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.jl-names-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Séparateur de bloc : une ÉTIQUETTE de liste, jamais un <h2> (§7). Poids
   700 au titre de l'amendement §3.0 (label uppercase compact). */
.jl-divider {
  margin: var(--space-md) 0 var(--space-xs);
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-text-muted);
}

/* Le bloc « Mes amis » n'existe que si le navigateur le remplit : le serveur
   ne voit jamais un cœur (D-26/JEUL-20). Sans script, le <noscript> du
   gabarit le masque, comme les cœurs eux-mêmes. */
.jl-divider--amis {
  margin-top: 0;
}

.jl-name {
  display: flex;
  align-items: center;
  /* La ligne ENROULE : à 360px, un patronyme long, une pastille et une heure
     ne tiennent pas sur une rangée, et rien ne doit déborder (§8). */
  flex-wrap: wrap;
  gap: var(--space-sm);
  min-height: 40px;
  padding: 0 var(--space-xs);
  border-bottom: 1px solid var(--color-border);
  font-size: 16px;
  font-weight: 400;
  /* Surlignage bref des nouvelles lignes (260914-ows) : le fondu se joue sur
     cette transition, la classe `.jl-name--nouveau` n'apportant que la
     couleur. `prefers-reduced-motion` (règle globale, plus bas) le ramène
     déjà à l'instantané. */
  transition: background-color 1.2s ease;
}

.jl-name-text {
  flex: 1 1 auto;
}

/* Celui qui regarde l'écran se retrouve d'un coup d'œil dans une liste de
   vingt lignes — reprend `.name.me` de la maquette. */
.jl-name--me {
  background: var(--color-surface);
  border-radius: 8px;
}

/* Surlignage bref d'une ligne qui vient d'apparaître par la mise à jour sur
   place (260914-ows, D-18 amendé) — APRÈS `.jl-name--me` pour l'emporter
   pendant les quelques secondes du fondu, y compris sur MA propre ligne. */
.jl-name--nouveau {
  background: var(--jl-name-new-bg);
}

.jl-name--mine {
  font-weight: 600;
}

.jl-name-time {
  flex: 0 0 auto;
  font-size: 14px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

/* Le point discret des lignes sans heure (D-28) : jamais un tiret, qui se
   lit comme une donnée manquante, jamais un faux horaire déduit du créneau. */
.jl-name-time--none {
  color: var(--color-border);
}

.jl-link-week {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  color: var(--color-accent);
  font-size: 16px;
  font-weight: 600;
}

/* Cible CORRIGÉE de 32×32 (maquette) à 40×40, le glyphe `♥` restant
   visuellement inchangé et centré. Le cœur est une amélioration progressive
   PURE : sans script, le <noscript> du gabarit le masque entièrement plutôt
   que de laisser un bouton mort à l'écran (§6.6). */
.heart {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: 40px;
  min-height: 40px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--color-border);
  font-size: 16px;
  line-height: 1;
}

/* SEUL usage de --jl-heart-active dans tout le fichier (§4.2) : l'état
   pressé d'un cœur, et rien d'autre. Cette couleur n'a de sens que dans un
   navigateur donné — elle n'est jamais partagée, jamais un état serveur. */
.heart[aria-pressed="true"] {
  color: var(--jl-heart-active);
}

/* Filtre « Jeunes / Adultes / Tout le club » (D-24), câblé EN CSS sur les
   `data-pop` posés par le gabarit — jamais par script : un filtre inopérant
   sans JavaScript serait un contrôle mort de plus, exactement ce que le
   <noscript> des cœurs évite. `:has()` est nécessaire parce que les radios
   vivent DANS `.segmented-control` (classe réutilisée verbatim) et ne sont
   donc pas des frères précédents de `.jl-names` ; sur un navigateur qui ne
   le connaîtrait pas, la liste reste complète — l'état par défaut du filtre,
   « Tout le club ».
   Le ciblage se fait par la VALEUR du bouton radio, jamais par son `id` :
   un sélecteur d'identifiant introduirait un croisillon dans le fichier, et
   tests/test_criterium_ui.py, qui lit ce fichier commentaires compris, le
   prendrait pour un littéral de couleur.
   Point 11 (260915-g7p) : le filtre est désormais placé APRÈS le bloc
   d'actions et AVANT les listes (D-26 amendée), mais reste un descendant de
   `.jl-tableau` — ce sélecteur `:has()` n'a donc pas eu besoin de changer. */
.jl-tableau:has(.jl-filter .segmented-radio[value="jeune"]:checked)
  .jl-name[data-pop="adulte"] {
  display: none;
}

.jl-tableau:has(.jl-filter .segmented-radio[value="adulte"]:checked)
  .jl-name[data-pop="jeune"] {
  display: none;
}

/* --- Écran 7 : la semaine (§6.8) --------------------------------------- */

.jl-slots {
  list-style: none;
  margin: 0;
  padding: 0;
}

.jl-slot {
  border-bottom: 1px solid var(--color-border);
}

/* Une ligne de créneau : à gauche une colonne `.jl-slot-main` (jour +
   horaire, puis salle et état des clés), à droite l'effectif, centré sur
   la hauteur des deux rangées (fast 260915-g7p : l'effectif enroulait sous
   la méta, décalé à gauche). Toujours un flex, jamais une grille — la
   colonne unique de §8 vaut aussi ici. */
.jl-slot-link {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 44px;
  padding: var(--space-sm) var(--space-xs);
  color: var(--color-text);
  text-decoration: none;
}

/* Restreint au seul LIEN (260918-fgh) : une ligne passée est un `<div>`,
   pas un `<a>` — elle ne doit pas s'éclairer au survol en promettant un
   clic qui n'existe plus. */
a.jl-slot-link:hover {
  background: var(--color-hover);
}

/* Chevron d'affordance de clic (260918-gcw) : reprend le patron
   `.jl-lien::after` du même écran (même glyphe, même taille, même token)
   plutôt qu'un vocabulaire visuel neuf. Sélecteur de TYPE `a` délibéré —
   une ligne passée est un `<div>` et ne doit rien promettre (test CSS
   complémentaire : `test_le_chevron_de_creneau_est_reserve_aux_lignes_cliquables`
   dans tests/test_jeulibre_layout_css.py). Pas de `margin-left: auto`
   (contrairement à `.jl-lien`) : `.jl-slot-main` est déjà en
   `flex: 1 1 auto` et pousse seul, l'espacement vient du `gap` du
   conteneur. Le chevron est une affordance d'INTERACTION, jamais le
   porteur d'une information (D-34 / WCAG 1.4.1 : l'état passé reste porté
   par le quantième, « étaient là » et la pastille « Terminé »). */
a.jl-slot-link::after {
  content: "›";
  flex: 0 0 auto;
  font-size: 20px;
  color: var(--color-text-muted);
}

.jl-slot-main {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1 1 auto;
  min-width: 0;
}

.jl-slot-day {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-sm);
}

.jl-slot-day b {
  font-size: 14px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-text-muted);
}

/* L'horaire d'une ligne de semaine : poids 700 au titre de l'amendement
   §3.0, et jamais coupé en deux lignes. */
.jl-slot-day span {
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
}

.jl-slot-meta {
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-muted);
}

/* Pastille texte d'un créneau fermé (point 12, 260915-g7p — remplace le
   barré de D-34/WCAG 1.4.1 : « Compétition » ou « Fermé », toujours
   accompagnée du motif en toutes lettres dans `.jl-slot-meta`, jamais
   portée par la seule teinte). */
.jl-slot-tag {
  display: inline-block;
  padding: 0 var(--space-sm);
  border-radius: 999px;
  background: var(--color-brand-yellow);
  color: var(--color-accent);
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

/* Variante discrète de la pastille, pour une ligne passée (260918-gcw) :
   même patron structurel que `.jl-slot-tag`, dont elle hérite par la
   classe partagée, mais DÉLIBÉRÉMENT plus discrète — la pastille de
   fermeture (jaune de marque sur encre accent, graisse 600) ne doit pas
   avoir à lui disputer l'œil. Contraste mesuré 6,10:1, AA sur texte
   normal. Placée APRÈS `.jl-slot-tag` dans la feuille : à spécificité
   égale c'est l'ORDRE DE SOURCE qui fait gagner la variante — ne jamais la
   remonter. */
.jl-slot-tag--termine {
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-weight: 400;
}

/* 261023-l4d : lignes compactes, bandeaux de semaine collants, pastilles
   d'ancre. Placé APRÈS les règles `.jl-slot*` de base (la première règle
   `.jl-slot-link` reste celle lue par les gardes de cible tactile). */
/* En-tête du site non collant, barre du bas en z-index 10 : le bandeau
   (z-index 1) ne passe ni dessous ni dessus. Collant DANS sa section, donc
   poussé par le bandeau de la section suivante. */
.jl-semaine-bandeau {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: var(--space-sm) var(--space-md);
  border-radius: 4px;
  font-size: 16px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
}

/* 261023-l4d (vérification en prod) : réserve de défilement en bas de page.
   Sans elle, la page était trop courte pour amener « Semaine prochaine » en
   haut de l'écran : l'ancre s'arrêtait en butée et le bandeau de la semaine
   courante restait affiché, tronqué, au-dessus. */
.jl-semaine {
  padding-bottom: 30vh;
}

/* Contraste accent sur blanc ~10:1 (AA). */
.jl-semaine-bandeau--cette-semaine {
  background: var(--color-accent);
  color: var(--color-bg);
}

/* Contraste accent sur jaune de marque ~9:1 (AA). */
.jl-semaine-bandeau--semaine-prochaine {
  background: var(--color-brand-yellow);
  color: var(--color-accent);
}

.jl-semaine-ancres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.jl-ancre {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 var(--space-md);
  border: 1px solid var(--color-accent);
  border-radius: 999px;
  color: var(--color-accent);
  background: var(--color-bg);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}

.jl-ancre:hover {
  background: var(--color-hover);
}

.jl-slot--compact .jl-slot-link {
  min-height: 40px;
  padding: var(--space-xs);
}

/* Un motif d'héritage long enroule au lieu de déborder à 360px. */
.jl-slot--compact .jl-slot-tag {
  white-space: normal;
}

.jl-slot-bilan {
  margin-left: auto;
  font-size: 14px;
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* Défilement doux des pastilles d'ancre, scopé à l'écran semaine et
   seulement sans préférence de mouvement réduit. */
@media (prefers-reduced-motion: no-preference) {
  html:has(.jl-semaine) {
    scroll-behavior: smooth;
  }
}

/* Un créneau fermé n'a pas de ratio à droite (le gabarit ne le rend pas),
   mais garde sa métadonnée dans le même ton discret que les lignes
   ouvertes — aucun second porteur d'information requis puisque la pastille
   texte ci-dessus et le motif suffisent déjà (WCAG 1.4.1). */
.jl-slot--closed .jl-slot-meta {
  color: var(--color-text-muted);
}

/* Valeur numérique mise en avant à côté de son libellé : 700 sur le
   chiffre, 400 sur le libellé (amendement §3.0). Point 13 (260915-g7p) :
   un simple effectif, plus de dénominateur ni de « on tourne » par ligne —
   le défi vit désormais dans l'encart agrégé `.jl-defi` ci-dessous. */
.jl-slot-ratio {
  flex: 0 0 auto;
  min-width: 64px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.jl-slot-ratio b {
  font-size: 20px;
  font-weight: 700;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

.jl-slot-ratio small {
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-muted);
}

/* 260918-fgh : atténuation visuelle DISCRÈTE d'une ligne passée, EN PLUS
   des porteurs textuels (quantième, « étaient là »), JAMAIS À LEUR
   PLACE (D-34, WCAG 1.4.1). Ce n'est PAS le porteur de l'information — le
   retirer ne changerait rien à ce qu'un lecteur d'écran ou une page
   imprimée en noir et blanc peut comprendre. Rien d'autre : ni opacité, ni
   barré, ni graisse dégradée. */
.jl-slot--past .jl-slot-ratio b {
  color: var(--color-text-muted);
}

/* Méta d'une ligne ouverte : qui ouvre (vert, tokens success) ou personne
   pour les clés (ambre) — point 13. */
.jl-slot-cles {
  color: var(--color-success-border);
}

.jl-slot-sans-cles {
  color: var(--color-amber-text);
}

/* Encart « défi » réécrit (point 14, 260915-g7p) : fond accent, texte
   blanc (`--color-bg`, seul token blanc du projet), gros chiffres jaunes
   de marque. Les nombres de joueurs et les noms de salles sont des DONNÉES
   du paquet de saison (`semaine.defis`), jamais des littéraux. */
.jl-defi {
  margin: var(--space-lg) 0 0;
  padding: var(--space-md);
  border-radius: 4px;
  background: var(--color-accent);
  color: var(--color-bg);
}

.jl-defi-titre {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
}

.jl-defi-rangee {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-top: var(--space-sm);
}

.jl-defi-rangee span {
  font-size: 14px;
}

.jl-defi-rangee b {
  display: block;
  font-size: 20px;
  font-weight: 700;
  color: var(--color-brand-yellow);
  font-variant-numeric: tabular-nums;
}

.jl-defi-note {
  margin: var(--space-sm) 0 0;
  font-size: 14px;
}

/* Les deux liens de bas de page (point 15). */
.jl-liens {
  display: grid;
  margin: var(--space-md) 0 0;
}

.jl-lien {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 44px;
  color: var(--color-accent);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid var(--color-border);
}

.jl-lien::after {
  content: "›";
  margin-left: auto;
  font-size: 20px;
  color: var(--color-text-muted);
}

/* --- Écran d'administration : interrupteurs de créneau (plan 20-09,
   20-UI-SPEC.md §5.4/§6.5 — écran « mes rappels » qui les introduisait a
   été retiré, quick 260914-u8d ; ces styles restent utilisés par
   app/templates/jeulibre/admin/admin.html) ------------------------------- */

/* Une ligne de libellé : depuis 260918-u99, elle ne porte plus que
   `.jl-admin-row-label` — la bande d'état (`.jl-admin-etat`, plus bas) est
   sortie de cette ligne pour occuper toute la largeur en dessous. Le
   séparateur qu'elle portait migre sur `.jl-admin-slot`, qui encadre
   désormais le bloc entier (libellé + bande + motif). */
.jl-admin-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.jl-admin-row-label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 16px;
}

.jl-admin-row-label small {
  display: block;
  font-size: 14px;
  color: var(--color-text-muted);
}

/* Le rail `.jl-switch` (technique `input.sr-only + label`, cible tactile de
   44px autour d'un rail visuel de 26px) est retiré par 260918-u99 : l'état
   n'existait qu'en COULEUR (manquement WCAG 1.4.1) et la ligne fermée ne se
   distinguait pas de la vue adhérent. Il est remplacé par une bande pleine
   largeur, `.jl-admin-etat` ci-dessous (sketch 038, variante C), qui énonce
   l'état PUIS le geste — l'input `.jl-switch-input` et sa technique
   sr-only + label + `:checked` restent inchangés, seul l'habillage change. */

/* --- Écrans 8 et 9 : l'administration (plan 20-10, 20-UI-SPEC.md §5.8/§5.9/
   §6.1/§6.9/§6.10) ------------------------------------------------------- */

/* Écran 8 — une ligne de créneau réutilise `.jl-admin-row`. Refondu par
   260915-wtx (sketch 033, variante A) : l'effectif rejoint la ligne de méta
   (`.jl-admin-row-label small`), et le motif de fermeture n'existe plus
   qu'en `<select>` révélé sur un créneau fermé — pourquoi CSS `:has()` et
   jamais un script : D-18 (aucun JavaScript sur les écrans
   d'administration), et `:has()` est déjà en production dans ce fichier
   (`body:has(.jl-page)`, l. 202). Le motif n'est pas décoratif : c'est lui
   qui est rendu aux adhérents sur le tableau d'un créneau fermé
   (`app/jeulibre/tableau.py::textes_fermeture`), et c'est aussi lui qui
   rend inutile toute table de vacances scolaires ou de jours fériés (D-42).
   Puis par 260918-u99 (sketch 038, variante C) : `.jl-admin-etat` remplace
   le rail `.jl-switch`, et c'est ce bloc, désormais, qui porte le
   séparateur entre créneaux (déplacé depuis `.jl-admin-row`, qui n'est plus
   qu'une ligne de libellé). */
.jl-admin-slot {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--color-surface);
}

/* Teinte de fond discrète d'un créneau fermé (260915-wtx) : l'état se voit
   au balayage sans lire l'interrupteur. Ambre, et non le couple
   --color-stop-* — celui-ci reste RÉSERVÉ à la ligne rouge à zéro du
   journal (§4.4, garde
   test_le_couple_stop_du_journal_ne_sert_qu_a_la_ligne_a_zero). L'ambre est
   la même famille que le dépliant de vidage ci-dessous, déjà en usage sur
   cet écran. */
.jl-admin-slot:has(.jl-switch-input:not(:checked)) {
  background: var(--color-amber-bg);
}

/* Bande d'état pleine largeur (260918-u99, sketch 038 variante C) : énonce
   l'ÉTAT puis le GESTE, jamais la seule couleur (WCAG 1.4.1). L'habillage
   par défaut est celui de l'état FERMÉ — le coché (ci-dessous) le réécrit.
   `cursor: pointer` parce que c'est le `<label>`, et non plus un petit
   rail, qui porte toute la zone cliquable. */
.jl-admin-etat {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-xs);
  padding: 0 var(--space-sm);
  border: 1px solid var(--color-amber-border);
  border-radius: 4px;
  background: var(--color-amber-bg);
  color: var(--color-amber-text);
  /* 16px, et non les 15px de la maquette : `test_aucune_taille_de_police_hors_de_l_echelle_du_projet`
     limite ce fichier à l'échelle {14, 16, 20, 28} (260918-u99, ajustement
     découvert à l'exécution, hors plan). */
  font-size: 16px;
  cursor: pointer;
}

.jl-admin-etat-on,
.jl-admin-etat-off {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  width: 100%;
}

/* Par défaut (créneau fermé), seul le bloc « Fermé » est visible ; le
   `:checked` ci-dessous inverse les deux affichages. */
.jl-admin-etat-on {
  display: none;
}

.jl-admin-etat-libelle {
  font-weight: 800;
}

/* Pas d'`opacity` ici : la maquette en pose une sur le geste, elle
   dégraderait le contraste du texte ambre. */
.jl-admin-etat-geste {
  /* 14px, et non les 13px de la maquette : même contrainte d'échelle que
     `.jl-admin-etat` ci-dessus (260918-u99). */
  font-size: 14px;
  font-weight: 600;
}

.jl-switch-input:checked + .jl-admin-etat {
  border-color: var(--color-accent);
  background: var(--color-bg);
  color: var(--color-accent);
}

.jl-switch-input:checked + .jl-admin-etat .jl-admin-etat-on {
  display: flex;
}

.jl-switch-input:checked + .jl-admin-etat .jl-admin-etat-off {
  display: none;
}

/* Halo de focus REPRIS À L'IDENTIQUE de `.segmented-radio` : l'input est
   `sr-only`, donc c'est le label qui doit porter le contour visible (§7). */
.jl-switch-input:focus-visible + .jl-admin-etat {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* La ligne fermée est ATTÉNUÉE (260918-u99) : le fond ambre du créneau
   fermé est déjà porté par `.jl-admin-slot:has(.jl-switch-input:not(:checked))`
   ci-dessus — cette règle ne le duplique pas, elle assourdit seulement le
   libellé. */
.jl-admin-slot:has(.jl-switch-input:not(:checked)) .jl-admin-row-label b {
  color: var(--color-text-muted);
}

/* Le motif n'apparaît QUE sur un créneau fermé — révélé sans une ligne de
   JavaScript (D-18). */
.jl-admin-motif {
  display: none;
  margin-top: var(--space-xs);
}

.jl-admin-slot:has(.jl-switch-input:not(:checked)) .jl-admin-motif {
  display: block;
}

.jl-admin-motif select {
  width: 100%;
  min-height: 44px;
  padding: 0 var(--space-sm);
  border: 1px solid var(--color-surface);
  border-radius: 4px;
  font-size: 16px;
  color: var(--color-text);
  background: var(--color-bg);
}

.jl-admin-motif select:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Puce de collision de compétition (260915-wtx) : SUR la ligne du créneau
   concerné, plus un bandeau en fin de formulaire. Compacte, même famille
   ambre que le reste de l'écran — la salle reste OUVERTE un jour de
   championnat, la puce signale, elle ne ferme rien. */
.jl-admin-collision {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 14px;
  font-weight: 700;
  color: var(--color-amber-text);
  background: var(--color-amber-bg);
  border: 1px solid var(--color-amber-border);
  border-radius: 999px;
  padding: 1px 8px;
  white-space: nowrap;
}

/* Vider un créneau (260914-lmu) — pratique en période de tests. Confirmation
   par un <details> natif faute de JavaScript (D-18) ; le bouton, lui, vit
   dans un <form> à part rejoint par l'attribut HTML `form`, jamais dans le
   formulaire de semaine. Le couple --color-stop-* est RÉSERVÉ à la ligne
   rouge à zéro du journal (§4.4, garde
   test_le_couple_stop_du_journal_ne_sert_qu_a_la_ligne_a_zero) : ce dépliant
   réutilise l'ambre existant, déjà employé ailleurs comme avertissement
   avant une action à confirmer (.warning-banner), et --color-error pour le
   texte du bouton — jamais comme fond, même convention que le reste du
   fichier. */
.jl-admin-vider > summary {
  cursor: pointer;
  color: var(--color-amber-text);
  min-height: 44px;
  display: flex;
  align-items: center;
}

.jl-admin-vider[open] {
  background: var(--color-amber-bg);
  border: 1px solid var(--color-amber-border);
  padding: var(--space-sm);
  border-radius: 4px;
}

/* 261023-k5m — liste nominative d'une séance (suivi mairie), <details> natif. */
.jl-lognames > summary {
  cursor: pointer;
  min-height: 44px;
  display: flex;
  align-items: center;
}

.jl-lognames ul {
  margin: 0;
  padding-left: var(--space-md);
}

.jl-admin-vider-confirm {
  color: var(--color-error);
}

/* Écran 9 — sélecteur de mois. Des liens `<a>` CLASSIQUES, pilotés par le
   serveur : aucune ligne de script sur cet écran (D-18), et donc aucun état
   à synchroniser entre le navigateur et la page. La barre défile
   horizontalement quand la saison compte plus de mois que la largeur du
   téléphone — jamais une grille, la colonne unique reste la règle (§8). */
.jl-monthbar {
  display: flex;
  gap: var(--space-xs);
  overflow-x: auto;
}

/* Cible tactile de 40px, en LITTÉRAL : `var(--tap-target)` n'existe pas dans
   ce fichier et reste proscrit (§0.6). */
.jl-month-link {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 var(--space-md);
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  color: var(--color-text-muted);
  background: var(--color-surface);
}

.jl-month-link.is-active {
  color: var(--color-bg);
  background: var(--color-accent);
}

.jl-month-link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Les trois compteurs du mois (D-34) : passages, créneaux ouverts, sans
   volontaire. Ils sont sur une rangée qui enroule plutôt que sur une grille :
   le troisième passe à la ligne sur un téléphone étroit sans qu'aucune piste
   n'ait à être déclarée. */
.jl-tallies {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.jl-tally {
  display: flex;
  align-items: baseline;
  gap: var(--space-xs);
  flex: 1 1 auto;
  padding: var(--space-sm) var(--space-md);
  border-radius: 4px;
  background: var(--color-surface);
}

.jl-tally b {
  font-size: 20px;
  font-weight: 700;
}

.jl-tally span {
  font-size: 14px;
  color: var(--color-text-muted);
}

/* Le couple « stop » (§4.4) : un créneau sans volontaire n'est ni une erreur
   de formulaire ni un avertissement à corriger — c'est un CONSTAT sur un
   créneau déjà passé. La couleur n'est JAMAIS le seul signal : le libellé
   « sans volontaire » est rendu en texte à côté du fond (WCAG 1.4.1). */
.jl-tally--miss {
  background: var(--color-stop-bg);
}

.jl-tally--miss b,
.jl-tally--miss span {
  color: var(--color-stop-text);
}

/* Une section par jour. Le dimanche garde ses DEUX créneaux, jamais
   additionnés (D-34) : c'est la vue pure qui le garantit, et aucune règle
   d'ici ne doit les faire passer pour une seule ligne. */
.jl-logday {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: var(--space-lg) 0 0;
}

/* Poids 700, au titre de l'amendement §3.0 : même rôle, et même traitement,
   que `.cal-day-header` et `.list-date` déjà en production. */
.jl-logday-title {
  margin: 0 0 var(--space-xs);
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.jl-logrow {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border-radius: 4px;
}

/* LIGNE TOUJOURS RENDUE, jamais omise (D-34) : un créneau que personne n'a
   ouvert est l'information la plus utile de l'écran. Le mot « personne » est
   rendu en TEXTE dans `.jl-log-who` — la teinte ne fait que le confirmer. */
.jl-logrow--miss {
  background: var(--color-stop-bg);
}

.jl-logrow--miss .jl-log-who,
.jl-logrow--miss .jl-log-count {
  color: var(--color-stop-text);
}

/* La colonne du milieu prend ce qui reste entre les deux largeurs fixes
   de §2 (82px et 42px, déclarées plus haut avec le reste du contrat de
   largeur de la phase). */
.jl-log-who {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14px;
}

/* Page publique du planning des entraînements (Phase 16, 16-UI-SPEC.md §9 —
   nomenclature source de ce bloc). Espace de noms `.planning-*` dédié,
   1 seul token nouveau (--color-surface-strong, déclaré en :root ci-dessus),
   1 seul point de rupture propre à cette page. */

.planning-page {
  max-width: 720px;
  margin: 0 auto;
}

/* Bandeau navy plein largeur — compensation par marges négatives exactes
   des paddings de `main` (jamais position: fixed ni 100vw, qui
   déclencheraient un défilement horizontal, SC-3 ; jamais de modification
   de la règle `main`). */
.planning-masthead {
  background: var(--color-accent);
  color: var(--color-bg);
  /* Padding horizontal réduit (revue utilisateur du 2026-08-26, quick
     260826-ojy, point 7) : le titre du bandeau doit tenir sur une ligne
     même sur un téléphone étroit — les marges négatives ci-dessous restent
     INCHANGÉES, elles compensent le padding de `main`, pas celui d'ici. */
  padding: var(--space-lg) var(--space-md);
  margin: calc(-1 * var(--space-xl)) calc(-1 * var(--space-lg)) var(--space-lg);
}

/* Règle de la ligne d'identité supprimée (quick 260826-wwp, point 1) : la
   ligne « SMMTT · Tennis de table · Montrouge » était redondante avec le
   lockup de marque déjà porté par base.html — règle et paragraphe morts,
   retirés ensemble. */

/* Titre ramené à la taille pleine de l'échelle typographique (quick
   260826-wwp, point 2) : la déclaration réduite précédente descendait sous
   les 16px du texte courant à 320px, inversant la hiérarchie. Aucun
   remplacement de taille ni de contrainte de largeur n'est réintroduit —
   l'arithmétique du plan démontre qu'aucune valeur déclarée ne referme la
   marge sans retomber sous 16px ; le titre se répartit donc sur deux
   lignes en dessous de ~483px, sans débordement horizontal (mot le plus
   long largement sous la largeur utile). `margin: 0` annule la marge UA du
   `h1` imbriqué (0.83em, soit 23px en haut ET en bas à 28px) : c'est ce
   poste qui finance la réduction de hauteur globale du bandeau. */
.planning-title {
  margin: 0;
}

.planning-since {
  display: inline-block;
  background: var(--color-brand-yellow);
  color: var(--color-accent);
  font-size: 14px;
  font-weight: 600;
  padding: var(--space-xs) var(--space-sm);
  border-radius: 999px;
  margin: var(--space-sm) 0 0;
}

/* .planning-since span supprimée (quick 260826-wwp, point 3) : la pastille
   ne porte plus que les dates de validité, sur un seul <span> implicite ;
   la mise en garde vacances est sortie dans .planning-notice ci-dessous,
   la marge basse structurelle de .planning-since revient donc à 0 (c'est
   la nouvelle ligne qui pose son propre écart). */

/* Ligne de mise en garde autonome sous la pastille (quick 260826-wwp,
   point 3) : depuis la décision D-14 de la Phase 16, aucun mécanisme ne
   signale les vacances scolaires — cette phrase est le seul garde-fou qui
   évite qu'un parent se déplace vers un gymnase fermé. Ni `color`, ni
   `opacity`, ni `font-size` déclarés ici : elle hérite de la couleur
   pleine du bandeau (blanc sur navy, portée par .planning-masthead) et des
   16px de .text-body — jamais atténuée comme l'était la ligne d'identité
   supprimée ci-dessus, à opacité réduite. */
.planning-notice {
  margin: var(--space-sm) 0 0;
  font-weight: 600;
}

.planning-week {
  padding: var(--space-md);
  border-radius: 8px;
}

.planning-week--busy {
  background: var(--color-amber-bg);
  color: var(--color-amber-text);
  border: 1px solid var(--color-amber-border);
}

.planning-week--calm {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}

.planning-page h2 {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--color-text-muted);
  /* Inter-sections resserrées (revue utilisateur du 2026-08-26, quick
     260826-ojy, point 3) — taille de police inchangée. */
  margin: var(--space-lg) 0 var(--space-sm);
}

/* Accordéon par public — <details>/<summary> natif, zéro JS (16-UI-SPEC.md §6). */
.planning-group {
  border: 1px solid var(--color-border);
  border-radius: 8px;
  margin-bottom: var(--space-sm);
}

.planning-group-summary {
  list-style: none;
  min-height: 44px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  cursor: pointer;
  background: var(--color-surface);
  border-radius: 8px;
}

.planning-group-summary::-webkit-details-marker {
  display: none;
}

.planning-group-summary::before {
  content: "▸";
  color: var(--color-accent);
  transition: transform .1s ease;
}

details[open] .planning-group-summary::before {
  transform: rotate(90deg);
}

.planning-group-summary:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.planning-group-name {
  flex: 1 1 auto;
  min-width: 0;
  white-space: normal;
  font-size: 16px;
  font-weight: 600;
  color: var(--color-accent);
}

.planning-group-days {
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-muted);
}

.planning-group--libre .planning-group-summary,
.planning-group--libre .planning-group-summary::before {
  background: var(--color-amber-bg);
  color: var(--color-amber-text);
}

.planning-group-body {
  /* Padding resserré à var(--space-sm) au mobile (quick 260826-qd3) :
     un des deux resserrements qui rendent le budget de 360px de
     .planning-slot atteignable. Restauré à var(--space-md) au palier
     ≥620px ci-dessous — la place ne manque plus. */
  padding: var(--space-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Gabarit de grille fixe à 3 pistes (quick 260826-qd3, corrige une
   décision erronée du quick 260826-prh qui jugeait .planning-slot « déjà
   aligné par construction »). Sous l'ancien flex, seule la piste jour
   avait une largeur déclarée : la piste horaire était dimensionnée par
   son contenu, donc la piste salle héritait d'un reliquat VARIABLE d'une
   ligne à l'autre — le même nom de salle tenait sur une ligne face à un
   horaire court et se cassait en deux face à un horaire long. Une piste
   horaire à largeur fixe élimine cette variabilité : le reliquat de la
   piste salle (`minmax(0, 1fr)`) devient constant.
   Chaîne de largeur à 360px (téléphone étroit de référence) qui justifie
   82px/132px ci-dessous :
     viewport 360
     − main { padding: var(--space-xl) var(--space-lg) } -> 2 × 24  = 312
     − .planning-group { border: 1px }                   -> 2 × 1  = 310
     − .planning-group-body { padding: var(--space-sm) }  -> 2 × 8  = 294
     − 2 gouttières .planning-slot { gap: var(--space-xs) } -> 2×4  = 286  (budget des 3 pistes)
   Vérifié mécaniquement contre les libellés réels de
   config/seasons/2627/planning_entrainements.yaml par
   tests/test_planning_layout_css.py::test_budget_de_largeur_des_pistes_de_l_accordeon_a_360px. */
.planning-slot {
  display: grid;
  grid-template-columns: 82px 132px minmax(0, 1fr);
  align-items: baseline;
  gap: var(--space-xs);
  font-size: 16px;
}

.planning-slot-day {
  font-weight: 600;
}

.planning-slot-hours {
  font-weight: 400;
  white-space: nowrap;
}

.planning-slot-room {
  font-size: 14px;
  color: var(--color-text-muted);
  min-width: 0;
  overflow-wrap: break-word;
}

/* Timeline verticale par jour (§4 — seule section codant la salle par
   couleur ; l'accordéon reste neutre). */
.planning-timeline {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.planning-day {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.planning-day-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-accent);
  background: var(--color-surface);
  padding: var(--space-xs) var(--space-sm);
  margin: 0;
}

.planning-day-empty {
  color: var(--color-text-muted);
}

/* Gabarit de grille fixe à 3 pistes (quick 260826-prh) : sous l'ancien
   flex, .planning-entry-salle n'avait AUCUNE largeur déclarée et
   .planning-entry-nom était en `flex: 1 1 auto` — la colonne salle était
   donc dimensionnée par le reliquat. Nom court ("Corpo") -> salle chassée
   contre le bord droit ; nom long ("Adultes compétition") -> "Maurice
   Genevoix" se cassait en deux et la ligne s'allongeait. Une grille à
   pistes fixes élimine les deux : chaque colonne a sa largeur propre,
   identique sur toutes les lignes.
   Chaîne de largeur à 360 px (téléphone étroit de référence) qui justifie
   60px/76px ci-dessous :
     viewport 360
     − main { padding: var(--space-xl) var(--space-lg) } -> 2 × 24  = 312
     − .planning-entry : border-left 3px + padding horiz. 2 × 8     = 293
     − 2 gouttières gap: var(--space-sm) -> 2 × 8                   = 277  (budget des 3 pistes)
   Vérifié mécaniquement contre les libellés réels de
   config/seasons/2627/planning_entrainements.yaml par
   tests/test_planning_layout_css.py::test_budget_de_largeur_des_pistes_a_360px. */
.planning-entry {
  display: grid;
  grid-template-columns: 60px minmax(0, 1fr) 76px;
  align-items: start;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  font-size: 14px;
  line-height: 1.25;
  min-height: 2.5em;
}

/* Horaire sur exactement deux lignes, à toute largeur (point 4) : deux
   <span> enfants dans le gabarit, jamais un retour dépendant de la
   largeur. */
.planning-entry-hours {
  display: flex;
  flex-direction: column;
  white-space: nowrap;
  line-height: 1.2;
}

.planning-entry-nom {
  min-width: 0;
  overflow-wrap: break-word;
}

/* Suffixe de groupe insécable sur sa propre ligne (point 9). */
.planning-entry-suffixe {
  display: block;
  white-space: nowrap;
}

.planning-entry--salle-1 {
  background: var(--color-surface-strong);
  border-left: 3px solid var(--color-accent);
}

.planning-entry--salle-2 {
  background: var(--color-surface);
  border-left: 3px dashed var(--color-border);
}

/* Déclarée après les deux règles de densité de salle pour l'emporter :
   l'ambre "sous réserve" prime toujours sur le codage salle (§4). */
.planning-entry--libre {
  background: var(--color-amber-bg);
  border-left-color: var(--color-amber-border);
}

.planning-entry-salle {
  min-width: 0;
  overflow-wrap: break-word;
  color: var(--color-text-muted);
}

.planning-rooms {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.planning-room {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: var(--space-md);
}

.planning-room-name {
  font-size: 20px;
  font-weight: 600;
  color: var(--color-accent);
  margin: 0 0 var(--space-xs);
}

.planning-room-address {
  font-size: 16px;
  font-weight: 400;
  margin: 0;
}

.planning-week p,
.planning-group-body p,
.planning-footer {
  max-width: 680px;
}

.planning-footer {
  color: var(--color-text-muted);
}

/* Colonne unique à TOUTES les largeurs (revue utilisateur du 2026-08-26,
   quick 260826-ojy, point 1) — révoque partiellement 16-UI-SPEC.md §
   passage en 2 colonnes : refusé après revue en production. Le contrat de
   colonne unique prime, y compris sur les écrans larges. */
@media (min-width: 620px) {
  .planning-masthead {
    padding: var(--space-xl);
  }

  /* Palier ≥ 620px (quick 260826-prh) : la place ne manque plus, la piste
     salle s'élargit pour laisser "Maurice Genevoix" tenir sur une seule
     ligne (≈ 118px à 14px). */
  .planning-entry {
    grid-template-columns: 60px minmax(0, 1fr) 132px;
  }

  /* Annule les deux resserrements mobile de l'accordéon (quick
     260826-qd3) : la place ne manque plus au-delà de ce palier. La piste
     salle de .planning-slot (minmax(0, 1fr)) s'élargit d'elle-même, sans
     grid-template-columns dédié ici. */
  .planning-group-body {
    padding: var(--space-md);
  }

  .planning-slot {
    gap: var(--space-sm);
  }
}

/* Respect de « réduire les animations » (Phase 20, 20-UI-SPEC.md §7) —
   règle GLOBALE, absente de ce fichier jusqu'ici. Elle bénéficie à TOUTES
   les pages du site (aucune transition existante n'était couverte) ; elle
   est posée par cette phase parce que c'est elle qui introduit les
   premières transitions interactives notables du site public (bascule du
   bloc heure d'arrivée, interrupteurs de rappel). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   Onglet « Équipes » du jeu libre (quick 261025-q2e, maquette validée le
   2026-10-25). Jetons uniquement. Pastilles V / N / D RONDES partout ; la
   lettre est toujours rendue, la couleur ne porte jamais seule le résultat.
   ========================================================================== */
.jl-eq { display: flex; flex-direction: column; gap: var(--space-md); }
.jl-eq > h2 { margin-top: var(--space-sm); }
.jl-eq-maj { margin: calc(-1 * var(--space-sm)) 0 0; color: var(--color-text-muted); font-size: 14px; }

/* Quick 261025-v5h (maquette v5) : un gabarit de ligne unique — pastille de
   division sur deux lignes | nom (toute la largeur) / ligne grise | valeur à
   droite. Les détails des fiches reprennent la même grille, alignés sur la
   colonne du nom (44 + 12 px). */
.jl-eq-cartes, .jl-eq-lignes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.jl-eq-cartes { gap: var(--space-sm); }
.jl-eq-lignes { gap: var(--space-md); }
.jl-eq-carte {
  display: flex; flex-direction: column; gap: var(--space-sm);
  padding: var(--space-md); border-radius: 12px; background: var(--color-surface);
}
.jl-eq-reception { gap: var(--space-md); border: 1px solid var(--color-amber-border); background: var(--color-amber-bg); }
.jl-eq-carte-tete { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-xs) var(--space-sm); }
.jl-eq-carte-tete b { font-size: 16px; }
.jl-eq-carte-tete span { color: var(--color-amber-text); font-size: 14px; }

.jl-eq-l, .jl-eq-det { display: flex; align-items: center; gap: 12px; }
.jl-eq-code {
  display: inline-grid; place-items: center; flex: none; width: 44px; height: 44px; border-radius: 8px;
  background: var(--color-accent); color: var(--color-bg); font-weight: 700; font-size: 14px;
}
.jl-eq-code--fem { background: var(--cat-feminine); }
.jl-eq-l-corps { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.jl-eq-l-nom, .jl-eq-det-nom { font-size: 16px; line-height: 1.3; }
.jl-eq-l-bas { display: flex; align-items: center; gap: 12px; }
.jl-eq-l-sous { flex: 1; min-width: 0; color: var(--color-text-muted); font-size: 14px; line-height: 1.3; }
.jl-eq-l-droite { flex: none; display: flex; align-items: center; gap: var(--space-sm); }
.jl-eq-score { font-size: 16px; font-variant-numeric: tabular-nums; }
/* Fiche : le rang occupe les deux lignes à droite, points dessous. */
.jl-eq-l-rang { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.jl-eq-l-rang > span { display: flex; align-items: baseline; }
.jl-eq-l-rang small { color: var(--color-text-muted); font-size: 14px; }
.jl-eq-rang { color: var(--color-accent); font-size: 20px; line-height: 1; font-variant-numeric: tabular-nums; }
.jl-eq-rang sup { font-size: 14px; }

.jl-eq-pastille {
  display: inline-grid; place-items: center; flex: none; width: 24px; height: 24px; border-radius: 50%;
  color: var(--color-bg); font-style: normal; font-size: 14px; font-weight: 700;
}
.jl-eq-pastille--V { background: var(--color-success-border); }
.jl-eq-pastille--N { background: var(--color-text-muted); }
/* 261025-npg : défaite au même gris que le site public, lettre foncée
   (contraste du blanc insuffisant sur ce gris clair). */
.jl-eq-pastille--D { background: var(--color-result-loss); color: var(--color-text); }

.jl-eq-det { margin-left: 56px; padding-top: var(--space-sm); border-top: 1px solid var(--color-border); }

/* Ligne « 🏆 » sous un créneau fermé un jour de réception (écran Semaine). */
.jl-slot-match { margin-top: var(--space-xs); color: var(--color-amber-text); font-size: 14px; }
