/* ==========================================================================
   SUCCEO — global.css
   Reset léger, typographie de base, mise en page et utilitaires.
   Dépend de variables.css (à charger avant).
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. RESET
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Décale les ancres pour qu'elles ne passent pas sous le header collant. */
  scroll-padding-top: calc(var(--header-h) + var(--sp-4));

  /* Garde-fou anti-débordement horizontal. `clip` (et non `hidden`) évite
     de transformer <html> en conteneur de défilement, ce qui casserait
     position: sticky sur le header. */
  overflow-x: clip;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-md);
  line-height: var(--lh-base);
  color: var(--c-text);
  background-color: var(--c-surface);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }

/* L'attribut hidden doit toujours l'emporter.
   Sans cette règle, une déclaration de composant comme
   `.footer-social a { display: inline-flex }` (spécificité 0,1,1) écrase le
   `display: none` du navigateur (0,1,0) : l'élément reste visible alors que
   le HTML ou le JavaScript le déclarent masqué. */
[hidden] { display: none !important; }

ul[class], ol[class] { list-style: none; margin: 0; padding: 0; }

img, picture, svg, video {
  display: block;
  max-width: 100%;
}

img { height: auto; }

svg { fill: none; }

input, button, textarea, select {
  font: inherit;
  color: inherit;
}

button { cursor: pointer; }

table { border-collapse: collapse; width: 100%; }

hr {
  border: 0;
  border-top: 1px solid var(--c-border);
  margin: var(--sp-10) 0;
}

/* --------------------------------------------------------------------------
   2. TYPOGRAPHIE
   -------------------------------------------------------------------------- */

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  color: var(--c-ink);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

h1 { font-size: var(--fs-4xl); font-weight: var(--fw-black); }
h2 { font-size: var(--fs-3xl); }
h3 { font-size: var(--fs-xl); line-height: var(--lh-snug); }
h4 { font-size: var(--fs-lg); line-height: var(--lh-snug); font-weight: var(--fw-semi); }

p { text-wrap: pretty; }

strong, b { font-weight: var(--fw-semi); color: var(--c-ink); }

small { font-size: var(--fs-sm); }

a {
  color: var(--c-accent-text);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: color var(--dur-fast) var(--ease);
}

a:hover { color: var(--c-ink); }

/* --------------------------------------------------------------------------
   3. ACCESSIBILITÉ
   -------------------------------------------------------------------------- */

/* Focus visible homogène et toujours perceptible. */
:focus-visible {
  outline: 3px solid var(--c-accent-text);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

/* On ne retire jamais le focus : on le neutralise seulement à la souris. */
:focus:not(:focus-visible) { outline: none; }

/* Contenu réservé aux lecteurs d'écran. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Lien d'évitement (premier élément focusable de chaque page). */
.skip-link {
  position: absolute;
  top: 0;
  left: var(--sp-4);
  z-index: var(--z-toast);
  transform: translateY(-120%);
  padding: var(--sp-3) var(--sp-5);
  background: var(--c-ink);
  color: var(--c-on-dark);
  font-weight: var(--fw-semi);
  text-decoration: none;
  border-radius: 0 0 var(--radius) var(--radius);
  transition: transform var(--dur) var(--ease);
}

.skip-link:focus { transform: translateY(0); color: var(--c-on-dark); }

/* --------------------------------------------------------------------------
   4. MISE EN PAGE
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.container--narrow { max-width: var(--container-narrow); }
.container--wide   { max-width: var(--container-wide); }

.section { padding-block: var(--section-y); }
.section--lg { padding-block: var(--section-y-lg); }
.section--tight { padding-block: clamp(2rem, 4vw, 3.5rem); }

.section--alt { background-color: var(--c-surface-alt); }

.section--dark {
  background-color: var(--c-ink);
  color: var(--c-on-dark-muted);
}

.section--dark h2,
.section--dark h3,
.section--dark h4 { color: var(--c-on-dark); }

/* Filet de séparation discret entre deux sections blanches. */
.section--bordered { border-top: 1px solid var(--c-border); }

/* Grilles génériques : le nombre de colonnes est géré dans responsive.css. */
.grid { display: grid; gap: var(--sp-6); }
.grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; }

.stack > * + * { margin-top: var(--sp-4); }
.stack-lg > * + * { margin-top: var(--sp-8); }

.flow > * + * { margin-top: var(--sp-4); }

/* --------------------------------------------------------------------------
   5. EN-TÊTES DE SECTION
   -------------------------------------------------------------------------- */

.section-head { max-width: 62ch; margin-bottom: var(--sp-10); }
.section-head--center { margin-inline: auto; text-align: center; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--c-accent);
}

/* Petit trait vert avant le sur-titre. */
.eyebrow::before {
  content: "";
  width: 22px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--c-accent);
}

.section--dark .eyebrow { color: var(--c-accent); }

.lead {
  font-size: var(--fs-lg);
  color: var(--c-muted);
  line-height: 1.6;
}

.section--dark .lead { color: var(--c-on-dark-muted); }

.section-head > .lead { margin-top: var(--sp-4); }

/* --------------------------------------------------------------------------
   6. CONTENU RÉDACTIONNEL (pages légales, articles pédagogiques)
   -------------------------------------------------------------------------- */

.prose { max-width: 72ch; }
.prose > * + * { margin-top: var(--sp-5); }
.prose h2 { margin-top: var(--sp-12); font-size: var(--fs-2xl); }
.prose h3 { margin-top: var(--sp-8); }
.prose ul, .prose ol { padding-left: var(--sp-5); }
.prose li + li { margin-top: var(--sp-2); }
.prose ol { list-style: decimal; }

/* Puces des listes de contenu.
   La puce disque du navigateur est minuscule, noire et collée au texte. Un
   carré vert posé en ::marker donne un repère net et raccroche la liste à la
   charte — sans toucher au balisage, et sans casser le retrait de la première
   ligne comme le ferait un ::before en position absolue. */
.prose ul { list-style: none; }

.prose ul > li {
  position: relative;
  padding-left: var(--sp-5);
}

.prose ul > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--c-accent);
}

.prose ol { padding-left: var(--sp-6); }
.prose ol > li::marker { color: var(--c-accent-text); font-weight: var(--fw-semi); }

/* Listes imbriquées : puce creuse, pour distinguer les deux niveaux. */
.prose ul ul > li::before {
  background: transparent;
  border: 1.5px solid var(--c-accent);
}

/*
   Liste à puces cochées, utilisée dans les blocs d'arguments.

   La pastille et sa coche sont dimensionnées en em, et non en pixels : sur un
   écran étroit la taille du texte baisse, et une pastille figée à 18px prenait
   alors une place disproportionnée. Le retrait suit la même logique, ce qui
   resserre naturellement la liste sur mobile sans règle de rupture.
*/
.list-check { display: grid; gap: var(--sp-2); }

.list-check li {
  position: relative;
  padding: 0.7em 0.7em 0.7em 2.3em;
  margin-left: -0.7em;
  border-radius: var(--radius);
  line-height: 1.6;
  transition: background-color 0.18s ease;
}

.list-check li:hover { background: var(--c-accent-soft); }

.list-check li::before {
  content: "";
  position: absolute;
  left: 0.7em;
  top: 0.62em;
  width: 1.3em;
  height: 1.3em;
  border-radius: 50%;
  background: var(--c-accent-soft);
  border: 1px solid var(--c-accent-line);
  transition: background-color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}

.list-check li:hover::before {
  background: var(--c-accent);
  border-color: var(--c-accent);
  transform: scale(1.08);
}

.list-check li::after {
  content: "";
  position: absolute;
  left: 1.18em;
  top: 0.84em;
  width: 0.38em;
  height: 0.62em;
  border-right: 2px solid var(--c-accent-text);
  border-bottom: 2px solid var(--c-accent-text);
  transform: rotate(42deg);
  transition: border-color 0.18s ease;
}

.list-check li:hover::after { border-color: var(--c-on-dark); }

.section--dark .list-check li:hover { background: rgba(255, 255, 255, 0.05); }

.section--dark .list-check li::before {
  background: rgba(0, 237, 0, 0.14);
  border-color: rgba(0, 237, 0, 0.4);
}

.section--dark .list-check li::after {
  border-color: var(--c-accent);
}

.section--dark .list-check li:hover::before {
  background: var(--c-accent);
  border-color: var(--c-accent);
}

.section--dark .list-check li:hover::after { border-color: var(--c-ink); }

/* --------------------------------------------------------------------------
   7. UTILITAIRES
   -------------------------------------------------------------------------- */

.text-center { text-align: center; }
.text-muted  { color: var(--c-muted); }
.text-sm     { font-size: var(--fs-sm); }
.text-xs     { font-size: var(--fs-xs); }

.mt-0  { margin-top: 0; }
.mt-4  { margin-top: var(--sp-4); }
.mt-6  { margin-top: var(--sp-6); }
.mt-8  { margin-top: var(--sp-8); }
.mt-10 { margin-top: var(--sp-10); }
.mt-12 { margin-top: var(--sp-12); }
.mt-16 { margin-top: var(--sp-16); }
.mb-0  { margin-bottom: 0; }

.center-x { margin-inline: auto; }

.no-wrap { white-space: nowrap; }

.text-left { text-align: left; }

/*
   Alignements héritant du bloc parent.
   --------------------------------------------------------------------------
   Ces règles remplacent des attributs « style="justify-content:center" » posés
   sur chaque élément. Ils étaient BLOQUÉS PAR LA CSP — « style-src 'self' »
   interdit les styles en ligne — et n'avaient donc aucun effet, tout en
   remplissant la console d'erreurs. Un bloc centré centre désormais ses
   enfants, sans qu'on ait à le redire sur chacun.
*/
.section-head--center .eyebrow,
.text-center .eyebrow { justify-content: center; }

.text-center .btn-group { justify-content: center; }

.hidden { display: none !important; }

/* Conteneur de défilement horizontal pour les tableaux larges. */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* --------------------------------------------------------------------------
   8. APPARITION AU DÉFILEMENT
   --------------------------------------------------------------------------
   Les styles ne s'appliquent que si JavaScript est actif (classe .js posée
   par un script en ligne dans le <head>). Sans JS, le contenu reste visible.
   -------------------------------------------------------------------------- */

/*
   GLISSEMENT VERS LE HAUT (« slide in up »), MOBILE COMPRIS.

   Trois décisions, chacune tirée d'un essai qui n'avait pas fonctionné :

   1. UNE ANIMATION, ET NON UNE TRANSITION.
      Une transition ne joue que si le navigateur a PEINT l'état de départ : il
      faut qu'il ait vu « opacity: 0 » avant de voir « opacity: 1 ». Or pour les
      blocs déjà à l'écran au chargement — la première hauteur d'écran, donc la
      première chose que voit le visiteur — l'IntersectionObserver notifie AVANT
      la première peinture. La classe arrivait dans la même image que l'état
      initial, aucune transition ne démarrait, et les blocs apparaissaient d'un
      coup. Une animation joue ses images clés dans tous les cas.

   2. « translate3d » ET NON « translateY ».
      C'est ce qui rend le glissement fluide sur un téléphone. La forme 3D
      promeut l'élément sur une couche composée par le GPU ; en 2D, le
      navigateur mobile repeint sur le fil principal, en concurrence avec le
      défilement — d'où le mouvement saccadé.

   3. UNE DISTANCE EN PIXELS, PAS EN POURCENTAGE.
      Le « slideInUp » d'Animate.css part de « translate3d(0, 100%, 0) », soit
      la hauteur entière de l'élément. Sur une section de 800px de haut, le bloc
      démarre 800px plus bas : il recouvre le suivant et arrive à toute vitesse.
      28px suffisent à lire un glissement.
*/
.js .reveal { opacity: 0; }

.js .reveal.is-visible {
  animation: slide-in-up var(--dur-reveal) var(--ease-reveal) both;

  /* Évite le scintillement d'une couche composée sur WebKit mobile. */
  backface-visibility: hidden;
}

@keyframes slide-in-up {
  from { opacity: 0; transform: translate3d(0, var(--reveal-shift), 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* Léger décalage en cascade pour les grilles de cartes : elles arrivent l'une
   après l'autre au lieu de surgir en bloc. */
.js .reveal[data-delay="1"] { animation-delay: 70ms; }
.js .reveal[data-delay="2"] { animation-delay: 140ms; }
.js .reveal[data-delay="3"] { animation-delay: 210ms; }
.js .reveal[data-delay="4"] { animation-delay: 280ms; }
.js .reveal[data-delay="5"] { animation-delay: 350ms; }
.js .reveal[data-delay="6"] { animation-delay: 420ms; }

/* Variante « agrandissement », pour les chiffres clés : elle accompagne mieux
   un compteur qui défile qu'une translation. Sans risque de débordement, donc
   active à toutes les largeurs. */
.js .reveal[data-reveal="scale"].is-visible { animation-name: reveal-scale; }

@keyframes reveal-scale {
  from { opacity: 0; transform: scale3d(0.96, 0.96, 1); }
  to   { opacity: 1; transform: scale3d(1, 1, 1); }
}

/*
   Les glissements LATÉRAUX attendent 1024px, la largeur à laquelle
   .layout-split passe réellement en deux colonnes. En dessous, la colonne
   occupe toute la largeur et 22px de décalage la poussent hors de l'écran,
   texte coupé à droite. Le débordement est invisible dans les mesures —
   « overflow-x: clip » sur body empêche la page de défiler latéralement — mais
   bien réel à l'œil.
*/
@media (min-width: 1024px) {
  .js .reveal[data-reveal="left"].is-visible  { animation-name: slide-in-left; }
  .js .reveal[data-reveal="right"].is-visible { animation-name: slide-in-right; }
}

@keyframes slide-in-left {
  from { opacity: 0; transform: translate3d(-22px, 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@keyframes slide-in-right {
  from { opacity: 0; transform: translate3d(22px, 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* Mouvement réduit : --reveal-shift et --dur-reveal sont déjà neutralisés dans
   variables.css. On coupe l'animation par sécurité, pour ne dépendre d'aucune
   valeur. */
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; }
  .js .reveal.is-visible { animation: none; }
}

/* Interrupteur de secours, posé par main.js lorsque l'environnement se révèle
   incapable d'animer (IntersectionObserver muet, images d'animation jamais
   émises faute de compositing). Sans lui, l'opacité resterait bloquée à 0 et la
   page paraîtrait vide. */
.js.reveal-off .reveal {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
  animation: none !important;
}

/* --------------------------------------------------------------------------
   8 bis. MOUVEMENT
   --------------------------------------------------------------------------
   Tous les effets de cette section suivent la même règle que .reveal : ils ne
   s'appliquent qu'avec la classe .js, et le repli est toujours « contenu
   visible ». Un effet raté ne doit jamais coûter une information.

   Ils n'animent que « transform » et « opacity », les deux seules propriétés
   que le navigateur peut composer sans recalculer la mise en page.
   -------------------------------------------------------------------------- */

/* --- Jauge de progression de lecture ------------------------------------- */

/* Filet vert en haut de la fenêtre, qui se remplit avec le défilement.
   Injecté par main.js, donc absent du HTML et sans effet sans JavaScript. */
.scroll-progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 3px;
  z-index: calc(var(--z-header) + 1);
  background: var(--c-accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .scroll-progress { display: none; }
}

/* --- Machine à écrire ---------------------------------------------------- */

/*
   Le texte reste ÉCRIT EN ENTIER dans le HTML : main.js enveloppe chaque
   caractère dans un <span> puis les révèle un à un. Deux conséquences qui
   valent la contrainte :

     - aucun décalage de mise en page. Les caractères occupent leur place dès
       le premier rendu, seule leur visibilité change. Une frappe qui insère
       les lettres dans le texte, elle, déplace la page à chaque caractère ;
     - le contenu reste indexable et sélectionnable, et il s'affiche
       intégralement sans JavaScript.
*/
.js .typewriter > .tw-char { visibility: hidden; }
.js .typewriter > .tw-char.is-on { visibility: visible; }

/* Le curseur suit le dernier caractère affiché. */
.js .typewriter > .tw-char.is-caret { position: relative; }

.js .typewriter > .tw-char.is-caret::after {
  content: "";
  position: absolute;
  top: 0.1em;
  left: 100%;
  width: 0.06em;
  min-width: 2px;
  height: 0.92em;
  margin-left: 0.06em;
  background: var(--c-accent);
  animation: tw-blink 900ms steps(2, jump-none) infinite;
}

@keyframes tw-blink { 50% { opacity: 0; } }

/* Frappe terminée : plus de curseur, tout est lisible.
   Sélecteur de DESCENDANCE, et non d'enfant direct : les caractères du mot
   surligné ont pour parent le <span class="hl">, qui porte lui aussi la classe
   .typewriter mais jamais .is-done. Avec « > », ces onze caractères restaient
   masqués pour toujours — le titre s'arrêtait à « sans perdre en ». */
.js .typewriter.is-done .tw-char { visibility: visible; }
.js .typewriter.is-done .tw-char::after { display: none; }

@media (prefers-reduced-motion: reduce) {
  .js .typewriter .tw-char { visibility: visible; }
  .js .typewriter .tw-char::after { display: none; }
}

/* Même interrupteur de secours que .reveal : si l'environnement se révèle
   incapable d'animer, le titre s'affiche, point. */
.js.reveal-off .tw-char { visibility: visible !important; }
.js.reveal-off .tw-char::after { display: none !important; }

/* --- Apparition mot à mot ------------------------------------------------ */

/* Utilisé sur les titres de section, à partir de 900px : chaque mot monte avec
   un très léger décalage, porté par une variable posée par main.js.

   Animation et non transition, et translate3d et non translateY, pour les mêmes
   raisons que .reveal — voir son commentaire, plus haut. */
.js .words > .word {
  display: inline-block;
  opacity: 0;
}

.js .words.is-visible > .word {
  animation: word-rise var(--dur-slow) var(--ease-reveal) var(--word-delay, 0ms) both;
  backface-visibility: hidden;
}

@keyframes word-rise {
  from { opacity: 0; transform: translate3d(0, 0.45em, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .js .words > .word { opacity: 1; }
  .js .words.is-visible > .word { animation: none; }
}

.js.reveal-off .words > .word {
  opacity: 1 !important;
  transform: none !important;
  animation: none !important;
}

/* --- Surlignage vert qui se dessine -------------------------------------- */

/* .hl porte déjà le dégradé. On l'anime en largeur : le trait se trace de
   gauche à droite au lieu d'apparaître d'un bloc. */
.js .hl {
  background-repeat: no-repeat;
  background-size: 0% 100%;
  transition: background-size 620ms var(--ease-out);
}

.js .hl.is-drawn { background-size: 100% 100%; }

@media (prefers-reduced-motion: reduce) {
  .js .hl { background-size: 100% 100%; transition: none; }
}

.js.reveal-off .hl { background-size: 100% 100% !important; transition: none !important; }

/* --- Parallaxe du visuel d'accueil --------------------------------------- */

/* Décalage vertical très léger, piloté par main.js via --shift. Réservé aux
   grands écrans : sur mobile le visuel occupe presque toute la largeur, et le
   moindre décalage y devient visible au lieu d'être ressenti. */
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  .js .parallax { will-change: transform; }
  .js .parallax > * { transform: translate3d(0, var(--shift, 0px), 0); }
}

/* --------------------------------------------------------------------------
   9. IMPRESSION
   -------------------------------------------------------------------------- */

@media print {
  .site-header,
  .site-footer,
  .cookie-banner,
  .btn { display: none !important; }

  body { font-size: 11pt; color: #000; }
  .section { padding-block: 1rem; }
}
