/* Styles publics et jetons de thème (§16). Les tests de thème vérifient les
   contrastes AA et la parité des affectations clair/sombre. Les palettes sont
   centralisées pour éviter toute divergence entre choix explicite et système. */

:root {
  /* Couleurs de marque fixes, communes aux deux thèmes. */
  --marque-corail: #F04B23;
  --marque-orange: #FA9200;
  --marque-jaune: #FFBE08;
  --marque-encre: #361D16;
  --marque-halo: #FA920012;
  --marque-fond: #FFF0D8;
  --marque-logo: #241E1B;
  --marque-pastille: #FFFFFFB3;
  --marque-voile: #FA920018;
  --clair-relief-bouton: #361D16;
  --sombre-relief-bouton: #B96B18;

  /* Thème clair — §16.3 */
  --clair-encre:      #131A19;   /* texte, noir légèrement vert */
  --clair-fond:       #EFF2F1;   /* fond de page, gris froid pâle */
  --clair-surface:    #FFFFFF;   /* blocs de contenu */
  --clair-j1:         #2A4B7C;   /* journée 1 — chirurgie en direct */
  --clair-j2:         #597726;   /* journée 2 — dissection */
  --clair-alerte:     #A32E1E;   /* complet, erreurs */
  --clair-trait:      #CFD6D3;   /* filets décoratifs */
  --clair-trait-fort: #848A88;   /* filets porteurs de structure — 3:1 (§16.12) */
  --clair-sourdine:   #5E6B67;   /* texte secondaire */

  /* Thème sombre — §16.13. Le fond sombre EST l'encre du clair, et l'encre
     sombre EST son fond : le couple est repris littéralement, ce qui garde au
     site la même identité d'un thème à l'autre. */
  --sombre-encre:      #EFF2F1;
  --sombre-fond:       #131A19;
  --sombre-surface:    #222928;
  --sombre-j1:         #849BCE;
  --sombre-j2:         #BFDC89;
  --sombre-alerte:     #F07860;
  --sombre-trait:      #2A2F2D;
  --sombre-trait-fort: #6E7471;
  --sombre-sourdine:   #86948F;

  /* Bandeaux d'état du back-office, dans les deux thèmes. */
  --clair-bandeau-test:        #FFF3CD;
  --clair-bandeau-production:  #D1E7DD;
  --clair-bandeau-inconnu:     #F8D7DA;
  --clair-bandeau-provisoire:  #E4DDF2;   /* contenus pas encore réels (§25) */
  --clair-ligne-alerte:        #FDECEA;
  --clair-succes:              #E7F1E9;
  --sombre-bandeau-test:       #3A3320;
  --sombre-bandeau-production: #1F3327;
  --sombre-bandeau-inconnu:    #3B2320;
  --sombre-bandeau-provisoire: #2C2440;
  --sombre-ligne-alerte:       #33211E;
  --sombre-succes:             #1E2C22;

  /* Thème appliqué. Le clair est le défaut : une feuille dont les valeurs de base
     seraient sombres afficherait une page noire à qui n'a pas de support des
     media queries. */
  color-scheme: light;
  --relief-bouton: var(--clair-relief-bouton);
  --encre:      var(--clair-encre);
  --fond:       var(--clair-fond);
  --surface:    var(--clair-surface);
  --j1:         var(--clair-j1);
  --j2:         var(--clair-j2);
  --alerte:     var(--clair-alerte);
  --trait:      var(--clair-trait);
  --trait-fort: var(--clair-trait-fort);
  --sourdine:   var(--clair-sourdine);
  --bandeau-test:       var(--clair-bandeau-test);
  --bandeau-production: var(--clair-bandeau-production);
  --bandeau-inconnu:    var(--clair-bandeau-inconnu);
  --bandeau-provisoire: var(--clair-bandeau-provisoire);
  --ligne-alerte:       var(--clair-ligne-alerte);
  --succes:             var(--clair-succes);

  /* ── Typographie (§16.3) ────────────────────────────────────────────
     Deux familles, deux rôles. Bricolage Grotesque a des proportions
     volontairement irrégulières qui évoquent l'atelier plutôt que le logiciel ;
     Literata est dessinée pour la lecture longue et donne au programme un
     caractère de manuel, ce qui est le registre d'un cours.

     Les replis système gardent la page lisible pendant le chargement ou si les
     fontes sont indisponibles. Les variantes latines sont livrées dans le dépôt. */
  --titre: "Bricolage Grotesque", system-ui, -apple-system, "Segoe UI", sans-serif;
  --texte: "Literata", Georgia, "Times New Roman", serif;

  --t-xs:  0.8125rem;  /* 13px — mentions légales, méta */
  --t-s:   0.9375rem;  /* 15px — libellés, notes */
  --t-b:   1.125rem;   /* 18px — corps */
  --t-md:  1.375rem;   /* 22px — chapô */
  --t-lg:  1.75rem;    /* 28px — titres de section */
  --t-xl:  2.25rem;    /* 36px — titres de journée */
  --t-2xl: 2.875rem;   /* 46px — titre de page */
  --t-3xl: 3.625rem;   /* 58px — chiffre de places */

  /* Espacement, base 4 (§16.3). Une échelle et non des valeurs au jugé : c'est
     ce qui fait que deux blocs écrits à trois semaines d'écart s'alignent. */
  --e1: 4px;   --e2: 8px;   --e3: 12px;  --e4: 16px;
  --e5: 24px;  --e6: 32px;  --e7: 48px;  --e8: 64px;
  --e9: 96px;  --e10: 128px;

  /* Structure (§16.3) */
  --largeur-max: 1120px;
  --largeur-prose: 62ch;
  --rayon: 3px;          /* uniquement sur les contrôles interactifs */
}

/* Polices auto-hébergées (§16.3) : « pas de CDN de police — c'est un transfert de
   données personnelles vers un tiers, une dépendance externe, et un point de
   lenteur ». La CSP de §17.5 le rend d'ailleurs impossible : `default-src 'self'`
   n'autorise aucune origine tierce pour les fichiers de fonte.

   Les variantes latines variables couvrent les textes français et anglais.
   Sources et licences SIL OFL accompagnent les fichiers dans static/fonts/. */
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("/static/fonts/bricolage-grotesque.woff2") format("woff2");
  font-weight: 200 800;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("/static/fonts/literata.woff2") format("woff2");
  font-weight: 200 900;
  font-display: swap;
}

/* Le sombre s'applique par deux chemins qui ne se recouvrent pas.

   1. Le réglage du système, quand le visiteur n'a rien choisi. La garde
      `:not([data-theme="clair"])` est ce qui rend le choix du clair efficace — le
      soleil de la bascule : sans elle, choisir le clair sur un système réglé en
      sombre n'aurait aucun effet, la media query l'emportant sur un `:root` nu.
   2. Le choix explicite, posé en `data-theme` par le serveur (§16.13). Il vient
      après, donc il gagne — dans les deux sens. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) {
    color-scheme: dark;
    --relief-bouton: var(--sombre-relief-bouton);
    --encre:      var(--sombre-encre);
    --fond:       var(--sombre-fond);
    --surface:    var(--sombre-surface);
    --j1:         var(--sombre-j1);
    --j2:         var(--sombre-j2);
    --alerte:     var(--sombre-alerte);
    --trait:      var(--sombre-trait);
    --trait-fort: var(--sombre-trait-fort);
    --sourdine:   var(--sombre-sourdine);
    --bandeau-test:       var(--sombre-bandeau-test);
    --bandeau-production: var(--sombre-bandeau-production);
    --bandeau-inconnu:    var(--sombre-bandeau-inconnu);
    --bandeau-provisoire: var(--sombre-bandeau-provisoire);
    --ligne-alerte:       var(--sombre-ligne-alerte);
    --succes:             var(--sombre-succes);
  }
}

:root[data-theme="sombre"] {
  color-scheme: dark;
  --relief-bouton: var(--sombre-relief-bouton);
  --encre:      var(--sombre-encre);
  --fond:       var(--sombre-fond);
  --surface:    var(--sombre-surface);
  --j1:         var(--sombre-j1);
  --j2:         var(--sombre-j2);
  --alerte:     var(--sombre-alerte);
  --trait:      var(--sombre-trait);
  --trait-fort: var(--sombre-trait-fort);
  --sourdine:   var(--sombre-sourdine);
  --bandeau-test:       var(--sombre-bandeau-test);
  --bandeau-production: var(--sombre-bandeau-production);
  --bandeau-inconnu:    var(--sombre-bandeau-inconnu);
  --bandeau-provisoire: var(--sombre-bandeau-provisoire);
  --ligne-alerte:       var(--sombre-ligne-alerte);
  --succes:             var(--sombre-succes);
}

/* Le clair explicite redéclare `color-scheme` : sans cette ligne, un système en
   sombre continuerait de rendre les contrôles de formulaire en sombre sur une
   page redevenue claire. */
:root[data-theme="clair"] { color-scheme: light; }

/* ═══════════════════════════════════════════════════════════════════════
   Mise en page (§16.4, §16.5)

   La page d'accueil n'est plus une liste de produits mais l'emploi du temps des
   deux journées : deux colonnes, un bandeau qui les enjambe, et les compteurs de
   places DANS les colonnes. §16.2 : « on ne vend pas une promesse, on montre un
   programme ».
   ═══════════════════════════════════════════════════════════════════════ */

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

body {
  margin: 0;
  /* Le fond est posé explicitement. Un `body` sans fond laisse voir celui du
     navigateur, qui suit le système et non le choix du visiteur : c'est
     exactement la panne que la bascule est censée supprimer (§16.13). */
  color: var(--encre);
  background: var(--fond);
  font-family: var(--texte);
  font-size: var(--t-b);
  /* Interlignage à 1.65 pour la serif, un cran au-dessus de ce qu'on donnerait à
     une sans (§16.3). */
  line-height: 1.65;
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3, .journee__nom, .compteur__nombre, .journee__prix, .bundle__prix {
  font-family: var(--titre);
  line-height: 1.15;
  /* Les titres se coupent en lignes équilibrées plutôt qu'en une veuve d'un mot.
     Ignoré par les navigateurs qui ne le connaissent pas, sans conséquence. */
  text-wrap: balance;
}

h1 { font-size: var(--t-2xl); margin: 0 0 var(--e4); font-weight: 700; }
h2 { font-size: var(--t-lg);  margin: 0 0 var(--e3); font-weight: 600; }
h3 { font-size: var(--t-md);  margin: var(--e6) 0 var(--e2); font-weight: 600; }

p { margin: 0 0 var(--e4); }
a { color: var(--j1); }

/* La prose garde une mesure lisible même dans un bloc large (§16.3). */
main > section > p, .journee__repli, .composer { max-width: var(--largeur-prose); }

/* Lien d'évitement (§16.12). Il n'occupe aucune place tant qu'il n'a pas le focus,
   et il ne se contente pas d'apparaître : il se pose EN HAUT et au-dessus du reste,
   sans quoi il s'ouvrirait derrière la coiffe collante. */
.evitement {
  position: absolute; left: var(--e4); top: -4em; z-index: 10;
  padding: var(--e2) var(--e4); border-radius: var(--rayon);
  background: var(--encre); color: var(--fond); text-decoration: none;
}
.evitement:focus { top: var(--e2); }

/* ── Coiffe et pied ─────────────────────────────────────────────────── */

.coiffe {
  display: flex; flex-wrap: wrap; gap: var(--e4);
  align-items: center; justify-content: space-between;
  max-width: var(--largeur-max); margin: 0 auto;
  padding: var(--e5) var(--e4);
  border-bottom: 1px solid var(--trait);
}
.coiffe__nom {
  font-family: var(--titre); font-size: var(--t-md); font-weight: 700;
  color: inherit; text-decoration: none;
}
.coiffe__nav a { font-size: var(--t-s); }
/* La navigation et les deux réglages de lecture se rangent ensemble à droite, et
   passent à la ligne ensemble sur un écran étroit : la marge automatique les garde à
   droite de la seconde ligne comme de la première. */
.coiffe__droite {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: var(--e4); margin-left: auto;
}

main { max-width: var(--largeur-max); margin: 0 auto; padding: var(--e6) var(--e4) var(--e8); }

.pied {
  max-width: var(--largeur-max); margin: 0 auto;
  padding: var(--e6) var(--e4) var(--e7);
  border-top: 1px solid var(--trait);
  font-size: var(--t-s); color: var(--sourdine);
}
.pied__liens { display: flex; flex-wrap: wrap; gap: var(--e2) var(--e5); }

/* Cibles tactiles de la coiffe et du pied (§16.12). */
.coiffe__nom,
.coiffe__nav a { display: inline-flex; align-items: center; min-height: 44px; }
.pied__liens a { display: flex; align-items: center; min-height: 44px; }
.pied .mention { margin: var(--e4) 0 0; font-size: var(--t-xs); }

/* ── Chapô (§16.4) ──────────────────────────────────────────────────── */
/* Il répond en dix secondes à « qu'est-ce qu'on y fait, quand, où, combien »
   (§16.1). Les faits sont des `span` séparés par un point médian posé en CSS : le
   séparateur est décoratif, il n'a pas à être lu ni à survivre à un copier-coller. */

.chapo { margin-bottom: var(--e7); }
.chapo__faits {
  display: flex; flex-wrap: wrap; gap: var(--e2) var(--e4);
  max-width: none;
  font-size: var(--t-md); color: var(--sourdine);
}
/* Le séparateur est porté par le fait qui PRÉCÈDE, et non par celui qui suit.
   Avec `span + span::before`, une coupe de ligne laisse un point médian seul en
   tête de ligne, où il se lit comme une puce mal formée — constaté à 1280 px sur
   « · 12 participants au maximum ». Porté par le précédent, il reste en fin de
   ligne, à sa place. Décoratif : il n'est pas dans le texte, donc pas dans un
   copier-coller ni dans la restitution vocale. */
.chapo__faits span:not(:last-child)::after { content: "·"; margin-left: var(--e4); }

/* ── La grille des journées (§16.4) ─────────────────────────────────── */
/* Deux colonnes et un bandeau qui les enjambe. `auto-fit` plutôt qu'un nombre
   fixe : trois journées se rangeraient d'elles-mêmes, et le jour où il n'y en a
   qu'une, elle n'occupe pas une demi-page vide. */

/* Deux colonnes explicites au-dessus de 720 px. Le bandeau occupe toutes
   les pistes : auto-fit en créerait une troisième vide sur grand écran. */
.grille {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--e5);
}
@media (min-width: 45em) {
  .grille { grid-template-columns: 1fr 1fr; }
}

.journee {
  display: flex; flex-direction: column;
  padding: var(--e5);
  background: var(--surface);
  /* Toute surface élevée porte une bordure en `--trait-fort`, qui tient ses 3:1 et
     qui est, elle, un vrai composant au sens de WCAG 1.4.11. L'élévation elle-même
     ne se mesure pas en rapport de contraste (§16.13). */
  border: 1px solid var(--trait-fort);
}

/* Le filet de couleur : c'est lui qui identifie la journée, et la couleur voyage
   ensuite jusqu'au billet PDF (§16.2). Il est en `<p aria-hidden>` et non en
   `::before` pour une raison précise : en mode contraste forcé, les couleurs de
   fond des pseudo-éléments sont supprimées et le filet disparaîtrait entièrement.
   Un élément réel garde une bordure, que le mode forcé conserve. */
.journee__filet, .bundle__filet {
  height: 6px; margin: 0 0 var(--e4); border: 0;
}
.journee--j1 .journee__filet { background: var(--j1); }
.journee--j2 .journee__filet { background: var(--j2); }
/* Le bandeau porte les deux couleurs l'une après l'autre : la forme dit ce que le
   produit est (§16.4). Un dégradé à arrêt net, donc deux aplats et une frontière. */
.bundle__filet {
  background: linear-gradient(to right, var(--j1) 0 50%, var(--j2) 50% 100%);
}
@media (forced-colors: active) {
  /* Les aplats tombent ; la structure reste par une bordure, que le mode conserve. */
  .journee__filet, .bundle__filet { border-top: 6px solid; height: 0; }
}

.journee__nom  { font-size: var(--t-xl); margin: 0; }
.journee__quoi { margin: 0 0 var(--e4); color: var(--sourdine); font-size: var(--t-md); }
.journee__quoi::first-letter { text-transform: uppercase; }
.journee__achat {
  margin-bottom: var(--e5);
  padding-bottom: var(--e5);
  border-bottom: 1px solid var(--trait);
}
.journee__prix { font-size: var(--t-lg); margin: 0 0 var(--e4); font-weight: 600; }
.journee__repli, .journee__sans-grille { font-size: var(--t-s); color: var(--sourdine); }
/* La ligne « quand et où » de /programme. Le lieu suit la date sur la même ligne
   quand la place le permet, et passe dessous sinon — d'où le `display: block` plutôt
   qu'un séparateur, qui pendrait en fin de ligne à la coupe. */
.journee__quand { font-size: var(--t-s); color: var(--sourdine); }
.journee__lieu { display: block; }

/* La bordure signale l'état complet sans atténuer le programme : cette information
   reste utile même lorsqu'aucune place n'est disponible (§16.7). */
.journee--complet { border-style: dashed; }
/* Le nom garde son encre pleine : la mise en retrait dit que le produit n'est plus
   achetable, pas qu'on ne sait plus lequel c'est. */
.journee--complet .journee__nom { color: var(--encre); }
.journee--complet .alerte { color: var(--alerte); }

/* ── La grille horaire ──────────────────────────────────────────────── */

.journee__programme { margin: 0 0 var(--e5); }

/* La commande de pliage est habillée POUR TOUTES LES LARGEURS, et masquée par la
   seule règle ci-dessous. */
.journee__programme > summary {
  display: list-item;
  min-height: 44px; padding: var(--e2) 0;
  font-family: var(--titre); font-weight: 600;
  color: var(--j1); cursor: pointer;
}
.journee--j2 .journee__programme > summary { color: var(--j2); }

/* Au-dessus de 720 px il n'y a rien à replier : la colonne est déjà courte, et un
   contrôle qui ne sert à rien encombre le parcours clavier. Le `<details>` reste
   `open` dans le HTML, donc le contenu est là même sans script (§16.12).

   `[open]` est ce qui distingue « inutile » de « nécessaire ». Sur un bloc ouvert,
   la commande n'a rien à offrir et sort du parcours clavier. Sur un bloc fermé,
   elle est le seul moyen de revoir le programme : la masquer l'enfermerait. */
@media (min-width: 45em) {
  .journee__programme[open] > summary { display: none; }
}

.horaire { width: 100%; border-collapse: collapse; font-size: var(--t-s); }
.horaire tr + tr th, .horaire tr + tr td { border-top: 1px solid var(--trait); }
.horaire th, .horaire td { padding: var(--e2) 0; text-align: left; vertical-align: baseline; }
/* `.horaire .horaire__heure` et non `.horaire__heure` seul : `.horaire th`, juste au-dessus,
   pèse une classe et un élément contre une seule classe, et remettait à zéro le retrait
   de droite. L'heure touchait alors l'intitulé, et « 12:00–13:00 » suivi de « 6 — PTH… »
   se lisait « 12:00–13:006 » — constaté le 2026-09-16 sur la grille de la migration 013. */
.horaire .horaire__heure {
  width: 4.5em; padding-right: var(--e3); font-weight: 600;
  font-family: var(--titre);
  /* Les heures s'alignent en colonne : sans chiffres tabulaires, « 09:00 » et
     « 14:00 » n'ont pas la même largeur et la colonne ondule. */
  font-variant-numeric: tabular-nums;
  color: var(--sourdine);
  /* Une plage « 08:20–09:05 » ne se coupe pas au tiret : la fin passée à la ligne se
     lirait comme l'heure d'une autre case. `width` reste le minimum d'une colonne dont
     aucune case n'a de fin. */
  white-space: nowrap;
}
.horaire__quoi { font-family: var(--texte); }
/* Les intervenants sous l'intitulé, en retrait : ils disent avec qui, pas quoi. */
.horaire__qui { display: block; font-family: var(--titre); color: var(--sourdine); }

/* ── Le compteur de places (§16.6) ──────────────────────────────────── */
/* Le seul endroit de la page où l'œil doit s'arrêter net, donc le seul alignement
   centré (§16.4). Il vient après le programme et avant le prix : on lit ce qu'on
   achète, puis ce qu'il en reste, puis ce que ça coûte. */

.compteur { margin: auto 0 var(--e4); text-align: center; }
.compteur__nombre {
  font-size: var(--t-3xl); font-weight: 700; margin: 0;
  font-variant-numeric: tabular-nums;
  /* Le nombre change au rafraîchissement (§16.8) ; sans hauteur stable, la colonne
     saute d'une ligne quand on passe de 10 à 9. */
  line-height: 1;
}
/* « Nombre en graisse supérieure » pour l'état de tension (§16.7). */
.compteur__avant {
  margin: 0; font-family: var(--titre); font-size: var(--t-s); font-weight: 600;
  color: var(--alerte);
}
.compteur__nombre--tension { color: var(--alerte); font-weight: 800; }
.compteur__mot--tension { color: var(--alerte); }
.compteur__mot { margin: var(--e1) 0 0; font-size: var(--t-s); color: var(--sourdine); }
.compteur__mot.alerte { font-size: var(--t-lg); font-family: var(--titre); font-weight: 700; }

/* La jauge, un segment par place. Pas de largeur calculée : `style-src 'self'`
   (§17.5) interdit l'attribut `style`, donc tout pourcentage posé à l'affichage.
   Les segments s'en passent et sont exacts — une place vendue, un segment qui
   change — là où un pourcentage arrondi ferait mentir la dernière place. */
.jauge {
  display: flex; flex-wrap: wrap; gap: 2px;
  justify-content: center; margin: var(--e3) 0 0;
}
.jauge__pris, .jauge__libre { width: 10px; height: 10px; }
.jauge__libre { background: var(--trait); }
/* Le repli d'abord : sans couleur de journée — un troisième produit, une autre page
   — le segment pris doit rester visible, il ne doit jamais se confondre avec un
   segment libre. Les deux règles suivantes le spécialisent. */
.jauge__pris { background: var(--trait-fort); }
.journee--j1 .jauge__pris { background: var(--j1); }
.journee--j2 .jauge__pris { background: var(--j2); }
@media (forced-colors: active) {
  /* Les aplats tombent : on redit l'état par la forme, pleine contre creuse. */
  .jauge__pris  { border: 5px solid; }
  .jauge__libre { border: 1px solid; }
}

/* Le seul moment orchestré de la page (§16.9) : les deux jauges se dessinent depuis
   zéro en 600 ms, décalées de 80 ms l'une de l'autre. */
@keyframes jauge-se-dessine {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
.jauge { animation: jauge-se-dessine 600ms ease-out backwards; }
.journee--j2 .jauge { animation-delay: 80ms; }

/* ── Le bandeau du couple (§16.4) ───────────────────────────────────── */
/* Il enjambe les deux colonnes. `1 / -1` et non `span 2` : avec `auto-fit`, le
   nombre de colonnes dépend de la largeur, et `span 2` déborderait sur une seule. */

/* Le conteneur actualisable laisse le bandeau occuper les deux colonnes. */
.actualisation-bundle { display: contents; }
.bundle {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; gap: var(--e5);
  align-items: flex-end; justify-content: space-between;
  padding: var(--e5);
  background: var(--surface); border: 1px solid var(--trait-fort);
}
.bundle__filet { flex-basis: 100%; }
.bundle__texte { flex: 1 1 24rem; max-width: var(--largeur-prose); }
.bundle__texte h2 { font-size: var(--t-xl); }
.bundle__texte p:last-child { margin-bottom: 0; }
.bundle__achat { display: flex; flex-direction: column; align-items: flex-start; gap: var(--e3); }
.bundle__prix { font-size: var(--t-xl); font-weight: 700; margin: 0; }

/* Le bandeau complet reprend la mise en retrait de la colonne de journée (§16.7) :
   même grisé, même bordure discontinue. */
.bundle--complet, .bundle--bundle_bloque { color: var(--sourdine); border-style: dashed; }
.bundle--complet .bundle__texte h2, .bundle--bundle_bloque .bundle__texte h2 { color: var(--encre); }
.bundle--complet .alerte, .bundle--bundle_bloque .alerte { color: var(--alerte); }

/* ── Boutons et liens d'action (§16.11, §16.12) ─────────────────────── */
/* Cibles de 44 px minimum. `inline-flex` plutôt qu'un `padding` calculé : la
   hauteur est alors garantie quel que soit le texte, et le libellé reste centré. */

.bouton, button {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: var(--e2) var(--e5);
  font: inherit; font-family: var(--titre); font-weight: 600;
  border: 1px solid var(--encre); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
  text-decoration: none; cursor: pointer;
}
.bouton--principal { background: var(--encre); color: var(--fond); }
/* Un bouton désactivé relève de l'exception de 1.4.3, mais une opacité le rend
   illisible sans rien apprendre à personne. Il garde donc un contraste réel : ce
   sont l'attribut et le curseur qui disent l'état. */
button[disabled] {
  background: var(--surface); color: var(--sourdine);
  border-color: var(--trait-fort); cursor: not-allowed;
}

/* Les deux lignes discrètes qui ferment la page (§16.4) : « Composer ma commande »
   et le renvoi vers le parcours entreprise. Même registre, donc même règle — les
   séparer inviterait à ne styler que l'une le jour où l'autre change. Entre elles
   prendront place les cinq sections de prose que §16.4 attend de l'organisateur. */
.composer,
.renvoi-entreprise { margin-top: var(--e6); font-size: var(--t-s); color: var(--sourdine); }
.composer a,
.renvoi-entreprise a { font-family: var(--titre); font-weight: 600; }

/* ── Formulaires (le tunnel d'achat) ────────────────────────────────── */

fieldset { border: 1px solid var(--trait-fort); margin: var(--e5) 0; padding: var(--e4); }
legend { font-family: var(--titre); font-weight: 600; }
label { display: block; margin: var(--e3) 0; }
input, select, textarea {
  font: inherit; padding: var(--e2); border-radius: var(--rayon);
  color: var(--encre); background: var(--surface); border: 1px solid var(--trait-fort);
}
/* §10.2 : une place unique s'inscrit au nom de l'acheteur, et son bloc participant
   se replie derrière un `<summary>`. Celui-ci se lit comme le lien qu'il est ; rendu
   avec le style de `button`, il passerait pour l'envoi du formulaire. */
.mention-acheteur { margin: var(--e3) 0 0; color: var(--sourdine); }
/* Le plancher de 44 px est DÉCLARÉ et non obtenu par accident. */
.pli-acheteur > summary { cursor: pointer; color: var(--j1); width: fit-content;
                          min-height: 44px; padding: var(--e2) 0; }
.produits { list-style: none; padding: 0; }
.produit { border: 1px solid var(--trait-fort); padding: var(--e4);
           margin-bottom: var(--e4); background: var(--surface); }
.alerte, .erreurs { color: var(--alerte); }
/* Les trois encarts d'état du tunnel — le bandeau d'un produit bloqué, la note d'un
   recalcul, l'attente d'un paiement. */
.bandeau, .note, .attente {
  background: var(--surface); border: 1px solid var(--trait-fort);
  padding: var(--e3);
}

/* Le rappel d'aperçu (§16.15) : la boutique fermée au public, montrée à la seule
   session du back-office. Au-dessus de la coiffe, à la largeur du site, et marqué
   d'un filet d'alerte : ce n'est pas l'état d'un produit mais celui de la page
   entière, et il doit se voir avant elle. */
.apercu {
  max-width: var(--largeur-max); margin: 0 auto;
  border-left: 4px solid var(--alerte);
}

/* La page d'attente (§16.15) : le formulaire vient après les deux colonnes et le
   lien vers le programme, séparé d'eux comme une section de prose. */
.prevenir { margin-top: var(--e7); }
/* Ce que la page promet de l'adresse (§18.4) se lit sous le champ, en second : un cran
   plus petit et en `--sourdine`, le couple du pied de page, qui tient son contraste
   dans les deux thèmes (§16.12). */
.tunnel--prevenir .aide {
  margin: var(--e2) 0 0;
  font-size: var(--t-s); color: var(--sourdine);
}
/* Le bouton prend sa distance lui-même : l'erreur du champ, quand il y en a une, vient
   entre l'aide et lui, et une marge posée sur l'aide l'aurait laissée collée au bouton. */
.tunnel--prevenir button { margin-top: var(--e5); }

/* ── Composition mobile (§16.5) ─────────────────────────────────────── */
/* Sous 720 px les colonnes s'empilent — la grille le fait d'elle-même — et la
   grille horaire passe derrière un `<details>` replié, « pour que les deux
   compteurs de places et les deux boutons restent atteignables sans défilement
   interminable ». Le repli est posé par le script ; sans lui, tout reste déplié,
   ce qui est le bon sens de la panne. La commande de pliage, elle, est habillée
   plus haut, pour toutes les largeurs : sa présence suit l'état du bloc et non la
   largeur de la fenêtre. */

@media (max-width: 44.99em) {
  h1 { font-size: var(--t-xl); }
  .journee__nom, .bundle__texte h2 { font-size: var(--t-lg); }
  .compteur__nombre { font-size: var(--t-2xl); }
  .chapo__faits { font-size: var(--t-b); }
  main { padding-top: var(--e5); }

  /* Empilés, les faits du chapô n'ont plus de séparateur à porter : un point médian
     en fin de chaque ligne pend dans le vide. C'est l'espacement qui sépare quand la
     ligne se rompt. */
  .chapo__faits { flex-direction: column; gap: var(--e1); }
  .chapo__faits span:not(:last-child)::after { content: none; }
}

/* Focus visible (§16.12) — 2 px décalés de 2 px, jamais `outline: none` sans
   remplacement. L'anneau est en `--encre` : c'est le seul jeton dont le contraste
   au fond est garanti dans les deux thèmes (15,67:1 de part et d'autre), donc le
   seul qui n'oblige pas à définir une couleur d'anneau par thème. */
:focus-visible { outline: 2px solid var(--encre); outline-offset: 2px; }

/* ── Réglages de lecture : la langue et le thème (§16.13, §16.14) ───── */
/* Deux interrupteurs discrets, à droite de la coiffe : les deux langues, celle de la
   page enfoncée, et une icône qui propose l'autre couleur. Discrets à l'œil — ni
   cadre, ni fond, la couleur du texte secondaire, qui tient ses 4,5:1 dans les deux
   thèmes —, pas au doigt : chacun garde sa cible de 44 px (§16.12), et le contour de
   focus reste celui de tout le site. */
.coiffe__reglages { display: flex; align-items: center; gap: var(--e1); }
.bascule { display: flex; margin: 0; }
.bascule__bouton {
  min-height: 44px; min-width: 44px;
  padding: 0 var(--e2);
  background: none; border: 0;
  color: var(--sourdine);
  font-family: var(--texte); font-size: var(--t-s); font-weight: 400;
}
.bascule__bouton:hover { color: var(--encre); text-decoration: underline; }
.bascule__bouton svg { display: block; }

/* La langue : un interrupteur à deux positions, « Français | English ». La position
   courante est dite par `aria-pressed`, et redite à l'œil par l'encre, la graisse et
   le soulignement — la couleur ne porte jamais seule un état (§16.12). Un filet d'un
   em sépare les deux noms, dessiné en fond : il ne prend aucune place et ne se lit pas. */
.bascule__groupe { display: flex; align-items: center; }
.bascule__bouton[aria-pressed="true"] {
  color: var(--encre); font-weight: 600;
  text-decoration: underline; text-underline-offset: 4px;
}
.bascule__groupe .bascule__bouton + .bascule__bouton {
  background: linear-gradient(var(--trait-fort), var(--trait-fort)) left center / 1px 1em
    no-repeat;
}

/* L'icône propose l'AUTRE couleur : des deux boutons, un seul est rendu, selon la
   couleur effective. Sans choix, c'est celle du système, et c'est la garde des jetons
   de §16.13 qui la dit — la media query, sauf si le clair est choisi. Le bouton masqué
   l'est par `display: none` : ni vu, ni atteint au clavier, ni lu. */
.bascule__bouton--vers-clair { display: none; }
:root[data-theme="sombre"] .bascule__bouton--vers-sombre { display: none; }
:root[data-theme="sombre"] .bascule__bouton--vers-clair { display: inline-flex; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="clair"]) .bascule__bouton--vers-sombre { display: none; }
  :root:not([data-theme="clair"]) .bascule__bouton--vers-clair { display: inline-flex; }
}

/* `[hidden]` doit gagner. La feuille du navigateur pose bien `[hidden]{display:none}`,
   mais toute règle d'auteur définissant `display` la neutralise : `button` plus haut
   est déclaré `inline-block`, si bien qu'un `bouton.hidden = true` ne masquait rien.
   Le défaut est silencieux — l'élément reste visible ET reste dans le parcours
   clavier, sans que rien ne signale que le script a pourtant fait son travail. */
[hidden] { display: none !important; }

/* Texte réservé au lecteur d'écran. Ni `display: none` ni `visibility: hidden`,
   qui le retireraient aussi de la restitution vocale : le texte reste dans le flux,
   réduit à un pixel découpé. Il porte le nom de la ligne sur les boutons « Retirer »
   du récapitulatif, où « Retirer » répété cinq fois ne dirait rien (§16.12). */
.lecteur-seul { position: absolute; width: 1px; height: 1px; overflow: hidden;
                clip-path: inset(50%); white-space: nowrap; }

/* Le champ leurre de §17.3. */
.leurre { display: none; }

/* ═══════════════════════════════════════════════════════════════════════
   Le tunnel d'achat (§16.10)

   « Une seule colonne, largeur 480 px maximum, libellés au-dessus des champs. */

.tunnel { max-width: var(--largeur-max); }

/* Une colonne, 480 px. Les champs prennent toute la largeur : un `input` à sa
   largeur naturelle donne une colonne de champs de longueurs inégales, qui se lit
   comme un formulaire mal aligné. */
.tunnel fieldset { max-width: 480px; }
.tunnel label { max-width: 480px; }
.tunnel input:not([type="checkbox"]), .tunnel textarea, .tunnel select { width: 100%; }
.tunnel label > small { display: block; margin-top: var(--e1);
                        font-size: var(--t-s); color: var(--sourdine); }
/* La case à cocher reste à côté de son texte : elle n'est pas un champ de saisie
   mais une réponse par oui ou non, et l'aligner en bloc au-dessus de son libellé la
   séparerait de ce qu'elle coche. */
.tunnel input[type="checkbox"] { width: auto; margin-right: var(--e2); }

/* La case elle-même fait 13 px et rien ne l'agrandit sans déformer son glyphe : la
   cible, c'est le LIBELLÉ, et c'est lui qui doit atteindre 44 px (§16.12). Il les
   atteint par un remplissage vertical et non par un `display: flex` : le libellé des
   CGV est une phrase qui contient un lien, et la disposition flexible en ferait
   trois éléments distincts qui cesseraient de se replier comme un texte. */
.consentement, .choix { display: block; min-height: 44px; padding-block: var(--e2); }

/* Les blocs participants sont indentés SOUS leur ligne et portent le filet de la ou
   des journées concernées (§16.10 point 3). « Le rattachement est visuel autant que
   technique » : c'est la forme du formulaire qui garantit qu'un participant sait à
   quelle ligne il appartient, et donc quels billets lui revenir. */
.participants {
  margin: var(--e3) 0 var(--e5) var(--e3);
  padding-left: var(--e4);
  border-left: 3px solid var(--trait-fort);
}
/* Le formulaire entreprise range ses blocs participants derrière un dépliant, qui
   partage cette classe sans être le même élément — un `<details>` là, un `<div>` ici. */
details.participants > summary {
  min-height: 44px; padding: var(--e2) 0; cursor: pointer;
}
.participants--j1 { border-left-color: var(--j1); }
.participants--j2 { border-left-color: var(--j2); }
/* Le couple porte les deux couleurs, comme le bandeau de la page d'accueil. Le
   `border-image` s'applique aux quatre côtés : les trois autres sont à zéro, seul
   le gauche a une épaisseur. */
.participants--j1-j2 {
  border-image: linear-gradient(to bottom, var(--j1) 0 50%, var(--j2) 50% 100%) 1;
}
@media (forced-colors: active) {
  /* Les couleurs tombent ; le filet reste, et avec lui le rattachement visuel. */
  .participants { border-left: 3px solid; border-image: none; }
}

.participant { margin: var(--e4) 0; }
.ligne { margin-bottom: var(--e4); }
.ligne label { font-family: var(--titre); font-weight: 600; }
/* « Mettre à jour » est le chemin SANS JavaScript (§16.10) : il poste le formulaire
   et le serveur renvoie la page recalculée. Il reste donc un vrai bouton, discret
   mais lisible, à côté de son sélecteur — et non caché par le script, qui pourrait
   ne pas s'exécuter. */
.ligne .maj { margin-left: var(--e2); padding: var(--e2) var(--e3); font-size: var(--t-s); }
.ligne .tension { display: block; margin-top: var(--e1);
                  font-size: var(--t-s); color: var(--alerte); }

/* Le récapitulatif. `--surface` et une bordure porteuse : c'est une surface élevée,
   et §16.13 veut qu'elle se distingue par un écart perceptif plus une bordure en
   `--trait-fort`, qui tient ses 3:1 là où l'élévation seule ne le peut pas. */
.recapitulatif {
  padding: var(--e4);
  background: var(--surface);
  border: 1px solid var(--trait-fort);
}
.recapitulatif h2 { margin-top: 0; font-size: var(--t-md); }
/* Cinq colonnes — offre, quantité, prix unitaire, total, retrait — dans une
   colonne latérale, c'est étroit. Le corps descend d'un cran et l'espacement se
   resserre : à `--t-b`, « Journée 1 — cours » passait sur trois lignes et poussait
   les montants hors du cadre. */
.recapitulatif table { width: 100%; border-collapse: collapse; font-size: var(--t-s); }
.recapitulatif th, .recapitulatif td {
  padding: var(--e2) var(--e1); text-align: left;
  border-bottom: 1px solid var(--trait);
  vertical-align: middle;
}
/* La colonne « Offre » prend ce qui reste ; les quatre autres se serrent sur leur
   contenu, faute de quoi le libellé, qui est le plus long, se fait comprimer par
   des colonnes de chiffres qui n'en ont pas besoin. */
.recapitulatif td:first-child, .recapitulatif th:first-child { width: 40%; }
.recapitulatif thead th { font-size: var(--t-xs); color: var(--sourdine); font-weight: 600; }
/* §16.7 : « une ligne déjà présente est signalée en `--alerte` et empêche le paiement
   tant qu'elle n'est pas retirée. » Le fond redouble la couleur du texte, qui ne porte
   de toute façon pas seule l'information (§16.6, §16.12) : la ligne du produit écrit
   « Complet », dit ce qu'il faut faire, ses deux commandes de quantité sont
   désactivées, et `valider` refuse le POST. */
.recapitulatif tr.alerte th, .recapitulatif tr.alerte td { background: var(--ligne-alerte); }
/* La suggestion du couple (§16.10). Registre de la note et non de l'alerte : c'est
   une information, pas un état de la commande — d'où `--sourdine` et la taille des
   mentions, comme les deux lignes discrètes qui ferment la page d'accueil. */
.suggestion { font-size: var(--t-s); color: var(--sourdine); margin: var(--e3) 0 0; }
.recapitulatif tfoot th, .recapitulatif tfoot td { font-weight: 700; border-bottom: 0; }
/* Les montants s'alignent sur leur virgule, ce que seuls des chiffres de largeur
   fixe permettent : sans cela, un total à quatre chiffres décale la colonne. */
.recapitulatif td:nth-child(3), .recapitulatif td:nth-child(4),
.recapitulatif tfoot td { text-align: right; font-variant-numeric: tabular-nums; }
.recapitulatif button {
  padding: var(--e1) var(--e2); font-size: var(--t-s);
  background: var(--surface); color: var(--encre); border-color: var(--trait-fort);
}

/* La cellule de quantité porte trois choses sur une ligne : « − », le nombre, « + ».
   Le nombre est un `span` et non un champ — la commande est le bouton, le nombre
   n'est qu'un état affiché. */
.recapitulatif .quantite { white-space: nowrap; }
.recapitulatif .quantite span[data-role] {
  display: inline-block; min-width: 2rem; text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Cibles tactiles de 44 px (§16.12), sur toutes les commandes du panier. Les deux
   boutons de quantité tomberaient à la largeur d'un caractère sans `min-width` :
   c'est la dimension horizontale qui manque à un « + », pas la verticale. */
.recapitulatif button, .ligne .maj { min-height: 44px; min-width: 44px; }
.payer, .ligne select { min-height: 44px; }

/* Le bouton de paiement porte le montant (§16.10, §18.2) ; il reste lisible quand
   le script le désactive au clic — c'est `button[disabled]` qui s'en charge, sans
   opacité. Il ferme la colonne, en pleine largeur : c'est l'action de la page. */
.payer {
  width: 100%; max-width: 480px;
  margin-top: var(--e5);
  background: var(--encre); color: var(--fond);
  font-size: var(--t-b);
}

/* ── Le récapitulatif toujours visible (§16.10) ─────────────────────── */

/* Grand écran : colonne latérale, collée. `align-self: start` est ce qui rend le
   `sticky` opérant — un élément étiré sur toute la hauteur de sa zone n'a nulle part
   où coller. */
@media (min-width: 62em) {
  .tunnel {
    display: grid;
    grid-template-columns: minmax(0, 480px) minmax(320px, 28rem);
    gap: var(--e5) var(--e7);
    align-items: start;
  }
  .tunnel > .recapitulatif {
    grid-column: 2; grid-row: 1 / -1;
    align-self: start; position: sticky; top: var(--e5);
  }
  .tunnel > *:not(.recapitulatif) { grid-column: 1; }
}

/* Sous 32 em, cinq colonnes ne tiennent pas. */
@media (max-width: 32em) {
  .recapitulatif th:nth-child(3), .recapitulatif td:nth-child(3) { display: none; }
  /* Les gouttières cèdent avant les cibles tactiles : c'est l'ordre des priorités de
     §16.12, où les 44 px ne sont pas négociables et l'espacement l'est. Quatre
     colonnes restantes à deux pixels de marge de part et d'autre rendent les dix-huit
     pixels qui manquaient encore. */
  .recapitulatif { padding: var(--e3); }
  .recapitulatif th, .recapitulatif td { padding: var(--e2) 2px; }
  .recapitulatif .quantite span[data-role] { min-width: 1.5rem; }
}

/* Petit écran : collé en haut. La hauteur est bornée et le débordement défile, sans
   quoi un panier de cinq lignes occuperait tout l'écran d'un téléphone et cacherait
   précisément les champs qu'on est en train de remplir — le récapitulatif doit rester
   visible, pas devenir la page. */
@media (max-width: 61.99em) {
  /* « Une seule colonne, largeur 480 px maximum » (§16.10). */
  .tunnel > * { max-width: 480px; }
  .recapitulatif {
    position: sticky; top: 0; z-index: 2;
    max-height: 45vh; overflow-y: auto;
  }
}

/* Le bloc d'erreurs ouvre la page, avant le formulaire (§16.10). `role="alert"` le
   fait annoncer ; la bordure et la couleur le disent à l'œil, et le fond le sépare
   du reste sans dépendre de la seule couleur du texte. */
.erreurs {
  max-width: 480px; margin: var(--e5) 0;
  border: 1px solid var(--alerte); border-left-width: 5px;
  border-radius: 12px;
  padding: var(--e5);
  background: var(--surface);
  font-family: var(--titre); font-size: var(--t-s); line-height: 1.6;
}
.erreurs p:first-child { font-weight: 700; font-size: var(--t-md); margin: 0 0 var(--e3); }
.erreurs a { color: var(--alerte); text-decoration-thickness: 1px; text-underline-offset: .2em; }
.erreurs a:hover { text-decoration-thickness: 2px; }
/* Le focus automatique reste visible, avec un espace qui le distingue du cadre. */
.erreurs:focus { outline: 2px solid var(--encre); outline-offset: 4px; }

/* L'erreur d'un champ, sous le champ (§16.10). Elle ne s'appuie pas sur la seule
   couleur : le texte dit quoi faire, `aria-invalid` porte l'état pour les
   technologies d'assistance, et la barre à gauche le redit en mode contraste forcé,
   où les couleurs du site sont remplacées. */
.erreur-champ {
  margin: var(--e1) 0 0;
  padding: var(--e2) var(--e3);
  border-left: 3px solid var(--alerte);
  border-radius: 0 6px 6px 0;
  font-family: var(--titre);
  font-size: var(--t-s);
  color: var(--alerte);
}
/* Le champ fautif se signale aussi lui-même. `aria-invalid` plutôt qu'une classe :
   l'attribut est déjà là pour le lecteur d'écran, et le styler évite d'entretenir un
   second marqueur qui finirait par ne plus correspondre. */
.tunnel [aria-invalid="true"] { border-color: var(--alerte); border-width: 2px; }
.erreurs ul { margin: 0; padding-left: var(--e5); }
.erreurs li + li { margin-top: var(--e2); }
.erreurs p:last-child { margin-bottom: 0; }

/* §16.9. Le mouvement répond aux actions, jamais tout seul — et il se coupe
   entièrement pour qui l'a demandé au système. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    /* Les délais aussi, et c'est le décalage de 80 ms de §16.9 qui l'impose. Une
       durée ramenée à 0,01 ms avec un délai laissé intact tiendrait la jauge de J2
       masquée par `backwards` pendant ses 80 ms, puis la ferait apparaître d'un
       coup : couper la durée sans couper le délai ne supprime pas le mouvement, il
       le remplace par un clignotement — précisément ce dont se protège celui qui a
       demandé moins d'animation. */
    animation-delay: 0s !important;
    transition-delay: 0s !important;
  }
}

/* Identité Lyon School : les couleurs chaudes du logo accompagnent les
   couleurs de repérage des journées, conservées dans tout le parcours. */

body {
  border-top: 5px solid var(--marque-orange);
  background-image: radial-gradient(ellipse at top left, var(--marque-halo), transparent 55%);
}
.coiffe { border-bottom-color: var(--marque-orange); }
.chapo--accueil {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  align-items: center;
  gap: 40px;
  overflow: hidden;
  padding: 48px 40px;
  margin-bottom: 32px;
  background: var(--marque-fond);
  color: var(--marque-encre);
  border-radius: 20px;
}
.chapo--accueil::before, .chapo--accueil::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
  border-radius: 50%;
}
.chapo--accueil::before {
  width: 340px; height: 340px;
  right: -200px; top: -200px;
  border: 65px solid var(--marque-orange);
  background: var(--marque-jaune);
}
.chapo--accueil::after {
  width: 240px; height: 240px;
  bottom: -218px; left: 34%;
  border: 40px solid var(--marque-corail);
}
.chapo__identite {
  padding: 12px;
  background: var(--marque-logo);
  border-radius: 12px;
  transform: rotate(-3deg);
  box-shadow: 8px 8px 0 var(--marque-jaune);
}
.chapo__logo { display: block; width: 100%; height: auto; }
.chapo--accueil h1 {
  margin: 0 0 24px;
  max-width: 18ch;
  font-size: clamp(2rem, 4.2vw, 3.5rem);
  line-height: 1.06;
  letter-spacing: -0.035em;
  text-wrap: balance;
}
.chapo--accueil .chapo__faits {
  margin: 0;
  color: var(--marque-encre);
  font-family: var(--titre);
  font-size: 1rem;
  gap: 8px;
}
.chapo--accueil .chapo__faits span {
  background: var(--marque-pastille);
  padding: 8px 12px;
  border-radius: 6px;
}
.chapo--accueil .chapo__faits span:not(:last-child)::after { content: none; }
.bundle {
  border-radius: 12px;
  background: linear-gradient(110deg, var(--marque-voile), transparent 75%), var(--surface);
}
.journee { border-radius: 12px; }
.journee__filet { height: 8px; border-radius: 4px; }
.journee--j1 .journee__nom { color: var(--j1); }
.journee--j2 .journee__nom { color: var(--j2); }
.journee__achat {
  background: color-mix(in srgb, var(--j1) 7%, var(--surface));
  border-radius: 8px;
  padding: 20px;
}
.journee--j2 .journee__achat {
  background: color-mix(in srgb, var(--j2) 7%, var(--surface));
}
.bouton--principal {
  background: var(--marque-orange);
  color: var(--marque-encre);
  border-color: var(--relief-bouton);
  border-radius: 7px;
  box-shadow: 0 4px 0 var(--relief-bouton);
  transition: background-color 150ms, transform 150ms, box-shadow 150ms;
}
.bouton--principal:hover {
  background: var(--marque-jaune); transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--relief-bouton);
}
.bouton--principal:active {
  transform: translateY(2px); box-shadow: 0 2px 0 var(--relief-bouton);
}
.chapo--accueil :focus-visible { outline-color: var(--marque-encre); }
@media (max-width: 44.99em) {
  .chapo--accueil { grid-template-columns: 1fr; gap: 28px; padding: 28px 24px; }
  .chapo__identite { width: 150px; }
  .chapo--accueil h1 { max-width: 20ch; }
  .chapo--accueil .chapo__faits { flex-direction: column; align-items: flex-start; }
  .journee__achat { padding: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .bouton--principal:hover, .bouton--principal:active { transform: none; }
}

/* Les offres présentent l’essentiel ; le détail reste sur /programme. */
.journee__resume { font-size: var(--t-s); margin-bottom: var(--e5); }
.chapo--accueil + .grille .journee__achat { margin-top: auto; }
.journee__detail {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e3); min-height: 44px;
  font-family: var(--titre); font-weight: 600;
  text-underline-offset: 4px;
}
.journee--j2 .journee__detail { color: var(--j2); }
[id^="programme-"] { scroll-margin-top: var(--e5); }

/* Réservation : rôles explicites et identité réutilisable, même sans script. */
.participant__choix { display: flex; flex-wrap: wrap; gap: var(--e3); }
.tunnel .participant__choix label {
  display: flex; align-items: center; gap: var(--e2); min-height: 44px;
  padding: var(--e2) var(--e3); border: 1px solid var(--trait-fort);
  border-radius: 7px; margin: 0;
}
.tunnel .participant__choix input[type="radio"] { width: auto; margin: 0; }
.participant__choix label:has(:checked) {
  background: var(--marque-fond); color: var(--marque-encre);
  border-color: var(--marque-encre);
}
.participant__moi { display: none; }
.participant:has(input[value="moi"]:checked) .participant__identite { display: none; }
.participant:has(input[value="moi"]:checked) .participant__moi {
  display: block; margin-top: var(--e4); padding: var(--e3);
  background: var(--surface); border: 1px solid var(--trait-fort);
  border-left: 3px solid var(--j1);
}
.aide-reservation, .reservation-destinataires { font-size: var(--t-s); }
.reservation-destinataires {
  padding: var(--e4); background: var(--surface); border: 1px solid var(--trait-fort);
}
.reservation-destinataires strong, [data-role="destinataire-facture"] { display: block; }
[data-role="recap-participants"] { padding-left: var(--e4); overflow-wrap: anywhere; }
.liste-participants .participants { margin-left: 0; }
.liste-participants .participants:not(:has(.participant:not([hidden]))) { display: none; }
.apercu-admin {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e2) var(--e4);
  margin: 0 0 var(--e5);
  color: var(--sourdine);
  font-family: var(--titre);
  font-size: var(--t-xs);
}
.apercu-admin a { display: inline-flex; align-items: center; min-height: 44px; }
