/* ==========================================================================
   accueil.css — page d'accueil uniquement. Identité « Nocturne d'étude ».
   L'arrivée pose l'ambiance en moins d'une seconde et ne retarde personne :
   rien n'attend le JavaScript pour être lisible.
   ========================================================================== */

/* --- Respiration entre le bandeau et le contenu ------------------------------
   Un vide franc, et un séparateur qui VIT : le filet se trace de gauche à droite
   au rythme du défilement, et une lueur le parcourt en boucle lente. */
.hero + .wrap { padding-top: 88px; position: relative; }
.hero + .wrap::before {
  content: ""; position: absolute; left: 20px; right: 20px; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--border) 120px,
              var(--border) calc(100% - 120px), transparent);
  transform: scaleX(0); transform-origin: center;
  animation: spacer-trace 700ms var(--e-sortie) both;
}
@keyframes spacer-trace { to { transform: scaleX(1); } }

/* La lueur : elle part du centre, s'étend, s'efface. 4,8 s, en boucle. */
.hero + .wrap::after {
  content: ""; position: absolute; left: 50%; top: -1px; width: 220px; height: 3px;
  margin-left: -110px; border-radius: 99px; pointer-events: none;
  background: radial-gradient(closest-side,
              color-mix(in srgb, var(--text) 85%, transparent), transparent);
  animation: spacer-lueur 4.8s var(--e-doux) infinite;
}
@keyframes spacer-lueur {
  0%        { transform: scaleX(.15); opacity: 0; }
  18%       { opacity: .95; }
  55%, 100% { transform: scaleX(2.6); opacity: 0; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Le filet se trace au défilement plutôt qu'à l'horloge : il suit le
       poignet, et se rembobine si l'on remonte. */
    .hero + .wrap::before {
      animation: spacer-trace linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 34%;
    }
  }
}

/* Ce conteneur ne doit JAMAIS porter de transform : il deviendrait le bloc
   conteneur du champ en `position: fixed`, qui cesserait alors d'être fixé à la
   fenêtre et se remettrait à défiler avec le bandeau. */
.hero > .wrap { transform: none !important; }

/* ==========================================================================
   Ambiances du bandeau — trois traitements du même champ.
   Pilotées par data-ambiance sur <html> ; aucune ne touche à la structure.
   ========================================================================== */

/* Multiplicateur de mouvement : 1 = nominal. Les surcharges de durée sont
   placées en fin de fichier, après les raccourcis `animation:` qui les
   réinitialiseraient. */
:root { --mvt: 1; }

/* --- CONSTELLATION : une aurore lente tourne derrière le champ, les glyphes
   portent leur propre lumière. C'est l'ambiance la plus habitée. --- */
:root[data-ambiance="constellation"] .hero-champ::before {
  content: ""; position: absolute; inset: -35%; z-index: -1; pointer-events: none;
  background: conic-gradient(from 0deg at 50% 50%,
    transparent 0deg,
    color-mix(in srgb, var(--accent) 22%, transparent) 55deg,
    transparent 130deg,
    color-mix(in srgb, var(--accent) 14%, transparent) 215deg,
    transparent 300deg, transparent 360deg);
  filter: blur(70px);
  /* Une seule couche composée : le flou est rastérisé une fois, la rotation ne
     coûte ensuite plus rien. */
  will-change: transform;
  animation: aurore calc(64s / var(--mvt)) linear infinite;
}
@keyframes aurore { to { transform: rotate(360deg); } }

:root[data-ambiance="constellation"] .hero-formule {
  text-shadow: 0 0 26px color-mix(in srgb, var(--accent) 55%, transparent);
}
:root[data-ambiance="constellation"] .hero-formule.hero-nom {
  text-shadow: 0 0 22px color-mix(in srgb, var(--text) 30%, transparent);
}
/* Chaque glyphe pulse très légèrement : le champ respire au lieu de flotter. */
:root[data-ambiance="constellation"] .hero-formule.hero-nom::after {
  background: linear-gradient(90deg, currentColor, transparent);
  opacity: .8;
}

/* --- MANUSCRIT : le champ devient une page de brouillon. Tout passe en
   Newsreader italique, à l'encre du texte, sans lueur. Plus lent, plus calme. */
:root[data-ambiance="manuscrit"] .hero-formule {
  font-family: var(--font-titre);
  font-style: italic;
  color: color-mix(in srgb, var(--text) 62%, transparent);
  text-shadow: none;
}
:root[data-ambiance="manuscrit"] .hero-formule:nth-child(even) {
  color: color-mix(in srgb, var(--text) 48%, transparent);
}
:root[data-ambiance="manuscrit"] .hero-formule.hero-nom {
  color: color-mix(in srgb, var(--text) 88%, transparent);
}
/* Un filet sous chaque élément, comme une note soulignée à la main. */
:root[data-ambiance="manuscrit"] .hero-formule::after {
  content: ""; display: block; height: 1px; margin-top: .2em;
  background: linear-gradient(90deg, transparent, currentColor 20%, transparent);
  opacity: .35;
}
:root[data-ambiance="manuscrit"] .hero::before,
:root[data-ambiance="manuscrit"] .hero::after { opacity: .14 !important; }

/* --- SOBRE : les nappes s'effacent presque, le champ seul porte l'ambiance. */
:root[data-ambiance="sobre"] .hero::before,
:root[data-ambiance="sobre"] .hero::after { opacity: .12 !important; }
:root[data-ambiance="sobre"] .hero-champ { opacity: .55 !important; }

@media (prefers-reduced-motion: reduce) {
  :root[data-ambiance="constellation"] .hero-champ::before { animation: none !important; }
}

/* --- Bandeau d'ouverture --------------------------------------------------- */
/* `main` porte un padding-top global (style.css) pour les pages sans hero :
   ici il ne fait que pousser le bandeau sous la nav, laissant une bande de
   fond nu — sans halo ni voile — entre les deux. Le hero gère déjà son
   propre espace via son padding interne ; il doit démarrer collé à la nav. */
main { padding-top: 0; }

.hero {
  position: relative;
  overflow: hidden;
  padding: 96px 0 96px;
}

/* Essai local : nappes desactivees pour voir le bandeau sans elles --
   rien d'autre n'y touche, un simple `display: none` suffit a revenir en
   arriere si besoin. */
.hero::before,
.hero::after { display: none; }

/* Deux nappes d'accent, très lentes. Présence, pas agitation. */
.hero::before,
.hero::after {
  content: "";
  position: absolute;
  z-index: -1;
  border-radius: 50%;
  filter: blur(90px);
  will-change: transform;
  pointer-events: none;
}
.hero::before {
  width: 640px; height: 640px;
  top: -320px; left: -180px;
  background: radial-gradient(circle, color-mix(in srgb, var(--text) 62%, transparent), transparent 68%);
  opacity: 0;
  animation: nappe-arrive 900ms var(--e-sortie) both,
             derive-a 34s ease-in-out 900ms infinite alternate;
}
.hero::after {
  width: 540px; height: 540px;
  top: -200px; right: -160px;
  background: radial-gradient(circle, color-mix(in srgb, var(--text) 40%, transparent), transparent 68%);
  opacity: 0;
  animation: nappe-arrive 900ms var(--e-sortie) 120ms both,
             derive-b 40s ease-in-out 1020ms infinite alternate;
}
@keyframes nappe-arrive { to { opacity: .42; } }
:root[data-theme="dark"] .hero::before { animation-name: nappe-arrive-sombre, derive-a; }
:root[data-theme="dark"] .hero::after  { animation-name: nappe-arrive-sombre, derive-b; }
@keyframes nappe-arrive-sombre { to { opacity: .30; } }

/* Translation seule : une échelle sur une couche floutée de 640 px la force à
   se re-rastériser à chaque image. */
@keyframes derive-a {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(120px, 70px, 0); }
}
@keyframes derive-b {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-110px, 90px, 0); }
}

/* --- Le champ : formules, économistes, gestionnaires ------------------------
   Fixé à la fenêtre : il couvre TOUTE la page, pas seulement le bandeau. Il
   reste donc présent pendant la lecture, derrière les cartes (qui sont opaques),
   visible dans les marges. Composé par KaTeX : vectoriel, net à toute densité. */
.hero-champ {
  /* Démarre sous la nav, pas sous inset:0 : sans ça, les entrées les plus
     hautes du champ (top:4 %, 6 %…) tombent derrière la barre — semi-
     transparente, donc ni masquées ni lisibles. --topbar-h est mesurée en
     JS (page-accueil.js), la hauteur réelle de la nav change au palier
     mobile où elle passe sur deux lignes ; 62px n'est qu'un repli. */
  position: fixed; top: var(--topbar-h, 62px); right: 0; bottom: 0; left: 0; z-index: -1;
  overflow: hidden; pointer-events: none;
  opacity: 0;
  animation: champ-arrive 1100ms var(--e-doux) 180ms both;
  /* Le masque d'origine, intact : une ellipse qui creuse le centre de l'écran.

     J'avais d'abord ajouté un second masque calé sur la colonne de lecture,
     pour empêcher les formules de passer sur le titre. C'était une erreur : à
     1280 px la colonne occupe presque tout l'écran, et le champ ne survivait
     plus que sous forme de noms tronqués au bord — « Joh… », « Géra… ». On
     sauvait la lisibilité en jetant l'identité.

     La lisibilité se règle en fait du côté du TEXTE, pas du décor : le titre
     et l'accroche portent maintenant leur propre halo (voir plus bas). Le
     champ garde ainsi toute sa surface, noms entiers compris. */
  -webkit-mask-image: radial-gradient(62% 62% at 50% 50%,
                      transparent 0 22%, rgba(0, 0, 0, .65) 52%, #000 80%);
          mask-image: radial-gradient(62% 62% at 50% 50%,
                      transparent 0 22%, rgba(0, 0, 0, .65) 52%, #000 80%);
}

/* Le halo qui remplace le masque.

   Une ombre portée de la couleur du fond, sans décalage : elle ne fait pas
   d'ombre, elle pose une plaque diffuse derrière chaque lettre. Le titre
   reste net même quand une formule passe dessous, et le champ n'a rien à
   céder. Deux rayons superposés — un serré pour détacher le contour, un
   large pour éteindre ce qui traîne autour.

   var(--bg) suit le thème : la plaque est claire en journée, sombre le soir,
   sans une ligne de plus. */
.hero h1,
.hero .accroche,
.hero .hero-eyebrow {
  text-shadow:
    0 0 10px var(--bg),
    0 0 26px var(--bg),
    0 0 46px var(--bg);
}
@keyframes champ-arrive { to { opacity: 1; } }
@keyframes champ-arrive-ancien { to { opacity: 1; } }
.hero-formule {
  position: absolute;
  white-space: nowrap;
  color: color-mix(in srgb, var(--text) 92%, transparent);
  /* La saturation est portée par les keyframes, pas par une déclaration à côté :
     `cycle-apparition` anime `filter`, et une animation en cours écrase toute
     valeur statique de la même propriété. */
  --sat: 1.9;
  --bri: 1.08;
  /* Deux animations superposées : une dérive lente et continue, et un cycle
     d'apparition/disparition de 21 s. Chaque élément est décalé de 1,3 s,
     donc il y en a toujours trois ou quatre de visibles, jamais les mêmes. */
  opacity: 0;
  animation: derive 30s ease-in-out infinite alternate,
             cycle-apparition 26s var(--e-doux) infinite;
}
.hero-formule:nth-child(even) { color: color-mix(in srgb, var(--text) 78%, transparent); }

/* Les noms : même cycle, mais en Newsreader italique et en gris de texte.
   Ce sont des repères, pas de la décoration — on les reconnaît au passage. */
/* Les noms : même cycle, mais en Newsreader italique et dans la couleur du
   texte. Sélecteur doublé pour passer devant `.hero-formule:nth-child(even)`,
   qui sinon les repeint en accent pâle et les rend illisibles. */
.hero-formule.hero-nom {
  font-family: var(--font-titre);
  font-weight: 400;
  letter-spacing: -.01em;
  color: color-mix(in srgb, var(--text) 92%, transparent);
}
.hero-formule.hero-nom::after {
  content: "";
  display: block;
  height: 1px;
  margin-top: .18em;
  background: linear-gradient(90deg, currentColor, transparent);
  opacity: .5;
}

/* Le fondu se fait à l'opacité et à l'échelle, jamais au flou : un `blur()`
   animé sur vingt-quatre éléments oblige le navigateur à re-rastériser chaque
   glyphe à chaque image. La saturation, elle, est constante : elle sort donc
   des keyframes et redevient une simple déclaration. */
.hero-formule { filter: saturate(var(--sat, 1)) brightness(var(--bri, 1)); }
@keyframes cycle-apparition {
  0%           { opacity: 0; scale: .94; }
  3%, 15%      { opacity: 1; scale: 1; }
  22%, 100%    { opacity: 0; scale: 1.05; }
}

/* Décalages : l'ordre d'apparition ne suit PAS l'ordre du document, sinon le
   champ se lit comme une vague qui descend. La permutation ci-dessous fait
   sauter chaque entrée d'une zone à l'autre, tout en gardant l'équilibre entre
   le haut et le bas de la page. La dérive reçoit en plus un décalage négatif
   propre à chaque élément, pour qu'aucun ne bouge en cadence avec son voisin. */
.hero-champ > *:nth-child(1)  { animation-delay:  -2s,  0s; }
.hero-champ > *:nth-child(2)  { animation-delay: -17s,  9.9s; }
.hero-champ > *:nth-child(3)  { animation-delay:  -8s, 19.8s; }
.hero-champ > *:nth-child(4)  { animation-delay: -23s,  4.4s; }
.hero-champ > *:nth-child(5)  { animation-delay: -11s, 15.4s; }
.hero-champ > *:nth-child(6)  { animation-delay:  -5s,  7.7s; }
.hero-champ > *:nth-child(7)  { animation-delay: -26s, 23.1s; }
.hero-champ > *:nth-child(8)  { animation-delay: -14s,  2.2s; }
.hero-champ > *:nth-child(9)  { animation-delay:  -3s, 13.2s; }
.hero-champ > *:nth-child(10) { animation-delay: -20s, 25.3s; }
.hero-champ > *:nth-child(11) { animation-delay:  -9s, 11s; }
.hero-champ > *:nth-child(12) { animation-delay: -28s, 20.9s; }
.hero-champ > *:nth-child(13) { animation-delay: -15s,  5.5s; }
.hero-champ > *:nth-child(14) { animation-delay:  -6s, 16.5s; }
.hero-champ > *:nth-child(15) { animation-delay: -22s,  8.8s; }
.hero-champ > *:nth-child(16) { animation-delay: -12s, 24.2s; }
.hero-champ > *:nth-child(17) { animation-delay: -25s,  1.1s; }
.hero-champ > *:nth-child(18) { animation-delay:  -4s, 14.3s; }
.hero-champ > *:nth-child(19) { animation-delay: -18s, 22s; }
.hero-champ > *:nth-child(20) { animation-delay: -10s,  6.6s; }
.hero-champ > *:nth-child(21) { animation-delay: -29s, 18.7s; }
.hero-champ > *:nth-child(22) { animation-delay: -16s,  3.3s; }
.hero-champ > *:nth-child(23) { animation-delay:  -7s, 12.1s; }
.hero-champ > *:nth-child(24) { animation-delay: -21s, 17.6s; }

@keyframes derive {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(26px, -32px, 0); }
}

/* --- Arrivée : trois temps de 60 ms, tout est posé en 700 ms ---------------- */
@keyframes monte {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.hero-eyebrow { animation: monte 460ms var(--e-sortie) 40ms both; }
/* Le titre ne monte pas : il se dévoile, comme s'il s'écrivait d'un trait.
   520 ms, masque qui remonte du bas vers le haut. */
.hero h1 {
  animation: titre-devoile 620ms var(--e-sortie) 100ms both;
}
@keyframes titre-devoile {
  from { opacity: 0; transform: translateY(20px); clip-path: inset(0 0 100% 0); }
  60%  { opacity: 1; }
  to   { opacity: 1; transform: none; clip-path: inset(0 0 -25% 0); }
}
.hero .accroche { animation: monte 520ms var(--e-sortie) 170ms both; }
.hero-actions { animation: monte 520ms var(--e-sortie) 240ms both; }
.compteurs { animation: monte 560ms var(--e-sortie) 320ms both; }

/* Bandeau centré, comme la maquette externe envoyée par Tawfik : le titre, le
   chapeau et les boutons sont posés au milieu. Les compteurs juste en dessous
   restent alignés à gauche dans leur grille — ce n'était pas ce qui était
   montré, et un chiffre centré dans sa carte perd en lisibilité. */
.hero > .wrap { text-align: center; }
.hero .compteurs { text-align: left; }

.hero-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 13px; border-radius: 999px;
  background: transparent; color: var(--accent-text);
  font-family: var(--mono);
  font-size: 11px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.hero h1 {
  font-family: var(--font-titre);
  font-size: clamp(2.3rem, 5.8vw, 3.9rem);
  font-weight: 480;
  line-height: 1.04;
  letter-spacing: -.03em;
  margin: 22px auto 20px;
  max-width: 16ch;
  text-wrap: balance;
}
/* Pas de dégradé sur le texte : un soulignement d'accent, tracé à l'arrivée. */
.hero h1 .surligne {
  position: relative;
  color: var(--text);
  -webkit-text-fill-color: currentColor;
}
.hero h1 .surligne::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: .06em; height: .07em;
  background: var(--accent); border-radius: 2px;
  transform: scaleX(0); transform-origin: left;
  animation: souligne 620ms var(--e-sortie) 520ms both;
}
@keyframes souligne { to { transform: scaleX(1); } }

.hero .accroche {
  font-size: clamp(1.02rem, 2vw, 1.18rem);
  color: var(--text-soft);
  max-width: 54ch;
  margin: 0 auto 32px;
}
.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }

/* Bouton plein + bouton contour, tous deux en pilule : c'est la paire que
   Tawfik voulait reproduire depuis une maquette externe. Portée au bandeau
   d'ouverture seulement — le reste du site garde ses boutons à contour, sans
   aplat (voir le commentaire sur .btn-primary dans style.css). */
.hero-actions .btn { border-radius: 999px; }
.hero-actions .btn-primary {
  background: var(--accent); border-color: var(--accent); color: var(--sur-plein);
}
.hero-actions .btn-primary:hover {
  background: color-mix(in srgb, var(--accent) 85%, var(--bg-elev));
  border-color: var(--accent); color: var(--sur-plein);
}
.hero-actions .btn:not(.btn-primary) { border-color: var(--accent); color: var(--text); }

/* Le bandeau se retire quand on le quitte : le contenu monte et s'estompe au
   rythme exact du défilement. Rien ne clignote, la page respire. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero > .wrap {
      animation: hero-se-retire linear both;
      animation-timeline: view();
      animation-range: exit 10% exit 88%;
    }
  }
}
/* Uniquement l'opacité : toute transformation sur ce conteneur en ferait le
   bloc conteneur des éléments `position: fixed` qu'il contient, et le champ
   cesserait d'être fixé à la fenêtre. */
@keyframes hero-se-retire {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* --- Compteurs ------------------------------------------------------------- */
.compteurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-top: 58px;
}
.compteur {
  background: color-mix(in srgb, var(--bg-elev) 62%, transparent);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
}
.compteur {
  transition: border-color var(--t-court) var(--e-doux), transform var(--t-court) var(--e-sortie);
}
.compteur:hover { border-color: color-mix(in srgb, var(--accent) 40%, transparent); transform: translateY(-2px); }
.compteur .n {
  font-family: var(--font-titre);
  font-size: 2.3rem; font-weight: 500; letter-spacing: -.035em; line-height: 1;
  font-variant-numeric: tabular-nums;
  /* Le chiffre roule vers le haut à l'arrivée : quatre nombres qui se posent
     coup sur coup, 40 ms d'écart. */
  animation: chiffre-roule 620ms var(--e-elan) both;
}
.compteur:nth-child(2) .n { animation-delay: 60ms; }
.compteur:nth-child(3) .n { animation-delay: 120ms; }
.compteur:nth-child(4) .n { animation-delay: 180ms; }
@keyframes chiffre-roule {
  from { opacity: 0; transform: translateY(.5em) rotateX(-58deg); }
  to   { opacity: 1; transform: none; }
}
.compteurs { perspective: 800px; }
.compteur .l {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-faint); margin-top: 8px;
}

/* --- Sélecteur de semestre -------------------------------------------------- */
.semestres {
  display: inline-flex; gap: 4px; padding: 4px;
  background: var(--bg-sunk); border: 1px solid var(--border);
  border-radius: 999px; margin-bottom: 28px;
}
.semestres button {
  padding: 9px 22px; border-radius: 999px; border: 0;
  background: transparent; color: var(--text-soft);
  font-size: 14px; font-weight: 550; cursor: pointer;
  transition: background var(--t-court) var(--e-doux), color var(--t-court) var(--e-doux);
}
.semestres button:hover { color: var(--text); }
.semestres button.actif {
  background: var(--bg-elev); color: var(--accent-text); font-weight: 620;
  box-shadow: var(--shadow);
}

/* --- Unités d'enseignement --------------------------------------------------- */
.ue-entete {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin: 40px 0 16px; padding-bottom: 12px; position: relative;
}
.ue-entete::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--border) 48px,
              var(--border) calc(100% - 48px), transparent);
}
.ue-code {
  font-family: var(--mono);
  font-size: 11px; font-weight: 600; letter-spacing: .1em;
  padding: 3px 9px; border-radius: 4px;
  background: transparent; color: var(--accent-text);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.ue-entete h3 { margin: 0; font-size: 1.14rem; }
.ue-entete .ects {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em;
  color: var(--text-faint); margin-left: auto; white-space: nowrap;
}

/* --- Cartes de matières ------------------------------------------------------
   Survol : 180 ms. La carte se lève de 3 px, le rail passe de 3 à 5 px, un
   halo d'accent se pose, la flèche avance. Un reflet balaie la carte en 620 ms. */
.matieres { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 12px; }

.matiere {
  position: relative;
  display: flex; flex-direction: column;
  padding: 20px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text);
  overflow: hidden;
  transition: transform var(--t-court) var(--e-sortie),
              box-shadow var(--t-court) var(--e-doux),
              border-color var(--t-court) var(--e-doux);
}
.matiere::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 3px;
  background: var(--border-strong);
  transition: width var(--t-court) var(--e-sortie), background var(--t-court) var(--e-doux);
}
/* Le survol incline légèrement la carte vers l'arrière et la soulève : 180 ms,
   assez pour donner une matière, trop court pour être remarqué. */
.matieres { perspective: 1200px; }
a.matiere:hover {
  text-decoration: none;
  transform: translateY(-5px) rotateX(3deg) scale(1.012);
  box-shadow: var(--shadow-lg),
              0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent),
              0 22px 50px -30px color-mix(in srgb, var(--accent) 80%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
a.matiere:active { transform: translateY(-2px) scale(.996); transition-duration: var(--t-vif); }
a.matiere:hover::before { width: 5px; background: var(--accent); }

/* Reflet qui balaie la carte au survol */
a.matiere::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 38%,
              color-mix(in srgb, var(--accent) 14%, transparent) 50%, transparent 62%);
  transform: translateX(-100%);
  transition: transform 620ms var(--e-doux);
  pointer-events: none;
}
a.matiere:hover::after { transform: translateX(100%); }

.matiere .tete { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 12px; }
/* Un pictogramme distinct par matière (voir ICONES dans page-accueil.js), en
   noir et blanc plein — --sur-plein garantit le contraste sur --accent quel
   que soit le thème. Le rail et le halo de la carte, qui portaient encore
   la teinte (--hue) de la matière, sont passés au même traitement : chaque
   icône suffit à distinguer les matières entre elles. */
.matiere .emoji {
  width: 42px; height: 42px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--sur-plein);
  transition: transform var(--t-court) var(--e-elan);
}
.matiere .emoji svg { width: 22px; height: 22px; }
a.matiere:hover .emoji { transform: scale(1.06) rotate(-2deg); }
.matiere h4 {
  font-family: var(--font-titre);
  margin: 0; font-size: 1.08rem; line-height: 1.28; font-weight: 560; letter-spacing: -.012em;
}
.matiere .volume { font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; color: var(--text-faint); margin-top: 5px; }

.matiere .pied {
  display: flex; align-items: center; gap: 8px;
  margin-top: auto; padding-top: 14px;
}
.badge-fiches {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 500; letter-spacing: .05em;
  padding: 3px 9px; border-radius: 999px;
  background: var(--good-soft); color: var(--good);
  border: 1px solid color-mix(in srgb, var(--good) 30%, transparent);
}
/* Même traitement que .badge-fiches (contenu réel, positif) mais pour une
   matière qui renvoie ailleurs que vers des fiches rédigées — voir m.lien
   dans programme.js et carte() dans page-accueil.js. */
.badge-lien {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 500; letter-spacing: .05em;
  padding: 3px 9px; border-radius: 999px;
  background: var(--good-soft); color: var(--good);
  border: 1px solid color-mix(in srgb, var(--good) 30%, transparent);
}
.badge-avenir {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: 10.5px; font-weight: 500; letter-spacing: .05em;
  padding: 3px 9px; border-radius: 999px;
  background: transparent; color: var(--text-faint);
  border: 1px dashed var(--border-strong);
}
.matiere .fleche {
  margin-left: auto; color: var(--text-faint);
  transition: transform var(--t-court) var(--e-sortie), color var(--t-court) var(--e-doux);
}
a.matiere:hover .fleche { transform: translateX(5px); color: var(--accent); }

.matiere.inactive .emoji { opacity: .6; }
.matiere.inactive h4 { color: var(--text-soft); }

/* --- Apparition au défilement ------------------------------------------------
   Le contenu est visible par défaut : c'est le script qui pose « anim » sur
   <body>. Si quoi que ce soit échoue, la page reste lisible. */
body.anim .reveler { opacity: 0; transform: translateY(18px); }
body.anim .reveler.visible {
  opacity: 1; transform: none;
  transition: opacity 480ms var(--e-doux), transform 480ms var(--e-sortie);
}

/* --- Bandeau de fin ---------------------------------------------------------- */
.appel {
  position: relative;
  margin-top: 72px; padding: 46px 38px;
  border-radius: var(--radius);
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
  background: var(--bg-elev);
  text-align: center;
  overflow: hidden;
}
.appel::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  opacity: .7;
}
.appel h2 { margin-bottom: 12px; font-size: 1.6rem; }
.appel p { color: var(--text-soft); max-width: 58ch; margin: 0 auto 24px; }

/* --- Adaptations ------------------------------------------------------------- */
/* Sous 900 px, la colonne de lecture prend toute la largeur : le champ n'a
   plus de marge propre, on le retire plutôt que de l'affaiblir. */
@media (max-width: 900px) {
  .hero-champ { display: none; }
}

@media (max-width: 720px) {
  .hero { padding: 62px 0 56px; }
  .hero + .wrap { padding-top: 52px; }
  .hero-champ { display: none; }
  .compteurs { grid-template-columns: repeat(2, 1fr); margin-top: 40px; }
  .appel { padding: 32px 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .hero::before, .hero::after, .hero-formule, .hero-champ,
  .hero-eyebrow, .hero h1, .hero .accroche, .hero-actions, .compteurs,
  .compteur .n, .hero > .wrap, .hero h1 .surligne::after {
    animation: none !important; opacity: 1 !important; transform: none !important;
  }
  /* Sans mouvement, le champ resterait figé sur seize éléments superposés :
     on le retire plutôt que de l'afficher tous à la fois. */
  .hero-champ { display: none !important; }
  .hero + .wrap::before { animation: none !important; transform: scaleX(1) !important; }
  .hero + .wrap::after { display: none !important; }
  .hero::before { opacity: .34 !important; }
  .hero::after  { opacity: .26 !important; }
  .hero h1 .surligne::after { transform: scaleX(1) !important; }
  .hero h1 { clip-path: none !important; }
  .reveler { opacity: 1 !important; transform: none !important; transition: none !important; }
  a.matiere:hover { transform: none; }
  a.matiere::after { display: none; }
}

/* ==========================================================================
   Fin de fichier — surcharges qui DOIVENT venir en dernier.
   Les règles du champ emploient le raccourci `animation:`, qui réinitialise
   `animation-duration`. Toute surcharge placée plus haut serait annulée.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  .hero-formule { animation-duration: calc(30s / var(--mvt, 1)), calc(26s / var(--mvt, 1)); }
  .hero::before { animation-duration: 900ms, calc(34s / var(--mvt, 1)); }
  .hero::after  { animation-duration: 900ms, calc(40s / var(--mvt, 1)); }
  :root[data-ambiance="constellation"] .hero-champ::before {
    animation-duration: calc(64s / var(--mvt, 1));
  }
}

/* En thème clair le fond est pâle : une teinte transparente y disparaît.
   Le champ gagne en densité, et la lueur de « constellation » — qui n'a de
   sens que sur un fond sombre — est retirée. */
:root:not([data-theme="dark"]) .hero-formule {
  color: var(--accent);
  --sat: 1.7;
  --bri: 1;
}
:root:not([data-theme="dark"]) .hero-formule:nth-child(even) {
  color: color-mix(in srgb, var(--accent) 92%, transparent);
}
:root:not([data-theme="dark"]) .hero-formule.hero-nom {
  color: color-mix(in srgb, var(--text) 84%, transparent);
}
:root:not([data-theme="dark"])[data-ambiance="constellation"] .hero-formule,
:root:not([data-theme="dark"])[data-ambiance="constellation"] .hero-formule.hero-nom {
  text-shadow: none;
}

/* Le champ est un décor d'accueil, pas un fond de lecture : dès qu'on quitte
   le bandeau il s'efface presque entièrement, au rythme du défilement. En haut
   de page il est plein, à hauteur du premier contenu il ne reste qu'un voile.
   Rien ne passe plus derrière un texte qu'on lit. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .hero-champ {
      animation: champ-arrive 1100ms var(--e-doux) 180ms both,
                 champ-seclipse linear both;
      animation-timeline: auto, scroll(root block);
      animation-range: normal, 0 92vh;
    }
  }
}
@keyframes champ-seclipse {
  from { opacity: 1; }
  to   { opacity: .1; }
}
