/* ==========================================================================
   Timesbook landing — couche de MOUVEMENT
   --------------------------------------------------------------------------
   Thèse : le sujet du produit est le TEMPS COMPTÉ. Le défilement de la page
   est donc lu comme une ligne de temps, et le mouvement ne sert qu'à ça —
   faire avancer la chaîne « pointage → contrôle → paie » et faire monter des
   heures. Aucun effet n'est ici pour décorer.

   Ce fichier est chargé APRÈS `landing.css` : il surcharge délibérément la
   révélation générique `.reveal` pour les figures, et il a besoin de gagner à
   spécificité égale. Le déplacer avant éteindrait le volet clip-path sans
   lever la moindre erreur — la figure se contenterait de son fondu.

   Rien ici n'est requis pour lire le site : tout part d'un état final VISIBLE
   et n'est armé que par `html.tb-motion`, classe posée par `motion.js`. Sans
   JavaScript, sans WebGL, ou en `prefers-reduced-motion`, la page reste
   exactement celle d'avant.
   ========================================================================== */

:root {
  /* Décélération « arrivée confiante ». Plus exponentielle que --ease, elle
     est réservée aux mouvements de grande amplitude (volets, épinglage) ;
     --ease reste l'easing des micro-états (survols, focus). */
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);

  /* Amplitudes. Volontairement basses : au-delà, la parallaxe désynchronise
     l'image de sa légende et le regard décroche du texte. */
  --par-shot: 22px;   /* dérive MAXIMALE de l'image dans son cadre */
  --par-glow: 40px;   /* dérive des halos de fond */

  /* Agrandissement qui donne à la dérive sa marge de manœuvre. Déclaré ici
     parce que `motion.js` le LIT : c'est lui qui borne la dérive à la marge
     réellement disponible, et deux valeurs séparées se sépareraient. */
  --par-scale: 1.06;

  /* Hauteur du chrome collant : barre de navigation (84px) + fil de lecture
     (58px). Une séquence épinglée à `top: 0` glisserait SOUS ces deux barres —
     elles sont elles-mêmes `sticky`, donc elles gagnent. Le défaut est muet :
     la séquence fonctionne, son premier tiers est simplement recouvert.
     `motion.js` REMESURE cette valeur au chargement et au redimensionnement ;
     ce littéral n'est que le repli si le script ne tourne pas. */
  --tb-chrome: 142px;
}

/* ==========================================================================
   1. SURFACES DU NAVIGATEUR
   --------------------------------------------------------------------------
   Les parties qu'on ne dessine pas portent quand même la charte. `::selection`
   et `:focus-visible` sont déjà traités dans landing.css ; restent la barre de
   défilement et les chiffres.
   ========================================================================== */

html { scrollbar-color: var(--brand-pale) var(--brand-wash); }

/* WebKit ne lit pas `scrollbar-color`. La barre est fine et sans flèches :
   sur une page longue elle sert de repère, pas de commande. */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--brand-wash); }
::-webkit-scrollbar-thumb {
  background: var(--brand-pale);
  border-radius: 999px;
  border: 3px solid var(--brand-wash);   /* marge optique */
}
::-webkit-scrollbar-thumb:hover { background: var(--brand-light); }

/* Un chiffre qui change au défilement ne doit pas changer de largeur : sans
   `tabular-nums`, le compteur d'heures fait sautiller la ligne entière à
   chaque incrément. */
[data-count], .chain-count b, .chain-hours { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   2. RÉVÉLATION DES FIGURES — volet clip-path
   --------------------------------------------------------------------------
   Une capture d'écran n'apparaît pas, elle se DÉCOUVRE : le cadre se lève du
   bas vers le haut pendant que l'image contenue descend légèrement à
   contre-sens. C'est le geste d'une feuille qu'on retire, et il reprend les
   équerres d'angle déjà posées sur les chapitres d'accueil.

   La figure elle-même reste OPAQUE (on annule le fondu générique) : c'est le
   cadre qui bouge, pas le bloc. Sans cette annulation, l'image ferait les deux
   à la fois et le volet deviendrait illisible.
   ========================================================================== */

html.tb-motion .shot.reveal { opacity: 1; transform: none; }

html.tb-motion .shot.reveal .shot-frame {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.05s var(--ease-out-expo);
  will-change: clip-path;
}
html.tb-motion .shot.reveal.in .shot-frame { clip-path: inset(0 0 0 0); }

/* Contre-mouvement de l'image : elle finit à sa place, donc rien ne dépend de
   ce décalage pour être lisible. */
html.tb-motion .shot.reveal .shot-stage > img,
html.tb-motion .shot.reveal .shot-ph {
  transform: translateY(-26px) scale(1.04);
  transition: transform 1.25s var(--ease-out-expo);
}
html.tb-motion .shot.reveal.in .shot-stage > img,
html.tb-motion .shot.reveal.in .shot-ph { transform: none; }

/* La légende suit le volet, décalée : elle nomme ce qu'on vient de voir. */
html.tb-motion .shot.reveal figcaption {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .5s var(--ease) .34s, transform .5s var(--ease) .34s;
}
html.tb-motion .shot.reveal.in figcaption { opacity: 1; transform: none; }

/* `will-change` n'est porté que pendant l'animation : laissé en place, il
   réserve une couche GPU par figure sur toute la page. */
html.tb-motion .shot.reveal.in.is-settled .shot-frame { will-change: auto; }

/* ==========================================================================
   3. PARALLAXE
   --------------------------------------------------------------------------
   Deux profondeurs seulement, et JAMAIS sur du texte ni sur un contrôle : la
   parallaxe sur du corps de texte nuit à la lecture et provoque du mal des
   transports. Les cibles sont l'image dans son cadre (déjà rognée par
   `.shot-stage { overflow: hidden }`) et les halos de fond.
   ========================================================================== */

html.tb-motion [data-par] { will-change: transform; }

/* L'image dérive dans son cadre. Le `scale` donne la marge nécessaire pour
   que la dérive ne découvre jamais le fond du cadre. */
html.tb-motion .shot.is-parallax .shot-stage > img {
  transform: translate3d(0, var(--par-y, 0px), 0) scale(var(--par-scale, 1.06));
  transition: none;                      /* piloté image par image */
}
/* Le volet d'entrée prime : tant que la figure n'est pas posée, c'est la
   transition ci-dessus (§2) qui commande. */
html.tb-motion .shot.reveal:not(.is-settled) .shot-stage > img { transition: transform 1.25s var(--ease-out-expo); }

/* ==========================================================================
   4. LA CHAÎNE — la séquence épinglée (accueil)
   --------------------------------------------------------------------------
   Le seul moment épinglé du site. La page se fige, et le défilement fait
   avancer une phrase mot à mot pendant que trois maillons se relient et qu'un
   compteur d'heures monte.

   Le geste porte la promesse du produit : au maillon « contrôle », le compteur
   DÉPASSE d'une heure, puis retombe — l'heure en trop est repérée avant
   d'être payée. C'est le seul endroit du site où le mouvement dit quelque
   chose que le texte ne dit pas.

   `.chain-track` donne la hauteur de défilement ; `.chain-stage` est ce qui
   reste à l'écran. Sans JS, le suivi vaut 1 et tout est lisible d'emblée.
   ========================================================================== */

.chain { position: relative; background: var(--bg); }
.chain-track { position: relative; }
html.tb-motion .chain-track { height: 260vh; }

.chain-stage {
  display: grid;
  place-content: center;
  gap: clamp(22px, 4.4vw, 54px);
  /* ⚠️ Rembourrage indexé sur la HAUTEUR de l'écran, pas sur sa largeur.
     Une scène épinglée ne peut pas défiler : ce qui dépasse est simplement
     invisible, sans erreur ni barre de défilement pour le signaler. À 78px
     fixes, le bas de la scène sortait de l'écran sur un téléphone court
     (360×640) alors qu'il tenait sur un 390×844. */
  padding: clamp(28px, 6vh, 78px) 0;
}
html.tb-motion .chain-stage {
  position: sticky;
  top: var(--tb-chrome);
  min-height: calc(100vh - var(--tb-chrome));
}

/* --- la phrase, révélée mot à mot ---------------------------------------- */
.chain-say {
  margin: 0 auto;
  max-width: 19ch;                        /* mesure d'affichage, pas de lecture */
  text-align: center;
  font-size: clamp(2rem, 5.6vw, 4rem);
  font-weight: 800;
  letter-spacing: -.035em;
  line-height: 1.06;
  color: var(--title);
  text-wrap: balance;
}
.chain-say .w {
  /* État par défaut LISIBLE : sans JS, le mot est simplement là. C'est
     `html.tb-motion` qui l'estompe pour que le défilement le rallume. */
  display: inline-block;
  margin-right: .26em;
}
html.tb-motion .chain-say .w {
  opacity: .16;
  transition: opacity .18s linear;
}
html.tb-motion .chain-say .w.is-lit { opacity: 1; }
.chain-say .pt { color: var(--acc); }

/* --- les trois maillons --------------------------------------------------- */
.chain-links {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: start;
  gap: 0;
  max-width: 760px;
  margin: 0 auto;
  padding: 0 20px;
}
.chain-link { position: relative; text-align: center; padding: 0 8px; }

/* Le fil qui relie les maillons : un rail éteint (`::before`) et, par-dessus,
   la part parcourue (`::after`) qui se dessine par `scaleX` depuis la gauche.

   ⚠️ L'ORDRE DES DEUX PSEUDO-ÉLÉMENTS EST LE MÉCANISME, pas une convention.
   `::after` vient après `::before` dans l'ordre de l'arbre, donc il peint
   par-dessus : c'est ce qui met le fil parcouru au-dessus du rail. Les
   intervertir cacherait le fil sous le rail, sans lever la moindre erreur.
   Et surtout : PAS de `z-index: -1` sur le rail pour le glisser dessous.
   `.chain-link` est `position: relative` SANS `z-index`, donc il ne crée
   aucun contexte d'empilement — un enfant en z-index négatif passe alors
   derrière le fond blanc de la section et devient invisible. C'était le cas :
   la part non parcourue du fil ne se voyait nulle part, et le fil paraissait
   toujours plein.

   `scaleX` plutôt qu'une transition de `width` : une largeur animée
   relayouterait la rangée à chaque image. */
.chain-link::before {                   /* rail éteint */
  content: "";
  position: absolute;
  top: 21px;
  left: -50%;
  width: 100%;
  height: 2px;
  background: var(--line);
  border-radius: 2px;
}
.chain-link::after {                    /* part parcourue */
  content: "";
  position: absolute;
  top: 21px;
  left: -50%;
  width: 100%;
  height: 2px;
  background: var(--acc);
  border-radius: 2px;
  transform: scaleX(var(--fill, 1));
  transform-origin: left center;
}
.chain-link:first-child::before,
.chain-link:first-child::after { display: none; }

.chain-dot {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: 0 auto 16px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--line);
  color: var(--muted);
  font-size: 1rem;
  transition: border-color .3s var(--ease), color .3s var(--ease),
              background .3s var(--ease), transform .3s var(--ease-out-expo);
}
.chain-link.is-on .chain-dot {
  border-color: var(--acc);
  background: var(--acc);
  color: #fff;
  transform: scale(1.08);
}
.chain-link b {
  display: block;
  font-size: 1.02rem;
  color: var(--title);
  letter-spacing: -.01em;
}
/* ⚠️ `:not(.chain-dot)` N'EST PAS UNE PRÉCAUTION, C'EST LE MÉCANISME.
   C'est le piège déjà nommé dans le bloc mobile plus bas — la pastille est
   elle aussi un `<span>` —, resté ouvert ici. `.chain-link span` (0,1,1) est
   déclaré APRÈS `.chain-dot` (0,1,0), donc il le bat : `display: block`
   écrasait la grille, `place-items: center` devenait inerte, et l'icône se
   posait sur la première ligne de texte — à 5px du haut d'un cercle de 44px,
   soit 10px trop haut ; `font-size: .88rem` la rapetissait au passage.
   Purement visuel, aucune erreur, aucun log, et l'écart ne se voit que si
   l'on mesure la boîte de l'icône dans celle de sa pastille.
   ⚠️ On nomme la CLASSE, jamais `span:not(.chain-dot)` : ce dernier vaut
   (0,2,1) et battrait le `display: none` du bloc mobile (0,2,0), qui masque
   cette description sous 720px — la pastille redevenait centrée et la
   description réapparaissait sur téléphone, un défaut pour un autre. */
.chain-link .chain-say-sub {
  display: block;
  margin-top: 5px;
  font-size: .88rem;
  color: var(--muted);
  line-height: 1.45;
}

/* --- le compteur ---------------------------------------------------------- */
.chain-count {
  text-align: center;
  display: grid;
  gap: 8px;
  justify-items: center;
}
.chain-hours {
  font-size: clamp(2.6rem, 7vw, 4.6rem);
  font-weight: 800;
  letter-spacing: -.045em;
  line-height: 1;
  color: var(--title);
  transition: color .3s var(--ease);
}
/* L'heure en trop : le chiffre passe à l'accent le temps du dépassement, puis
   revient. La couleur n'est pas le message — le message est que le nombre
   redescend — mais elle rend le dépassement visible à l'œil qui suit le texte. */
.chain-count.is-over .chain-hours { color: var(--acc-ink); }
.chain-note {
  font-size: .95rem;
  color: var(--muted);
  min-height: 1.5em;                       /* pas de saut quand le texte change */
  transition: opacity .22s var(--ease);
}
.chain-count.is-over .chain-note { color: var(--acc-ink); }

@media (max-width: 720px) {
  html.tb-motion .chain-track { height: 220vh; }
  .chain-links { max-width: 420px; gap: 0; }
  /* ⚠️ On masque la DESCRIPTION, jamais « tous les span du maillon » :
     `.chain-dot` est lui aussi un `<span>`, et un sélecteur `.chain-link span`
     emportait les trois pastilles avec — la chaîne perdait ses maillons et il
     ne restait que trois mots au-dessus d'un trait. Purement visuel, aucune
     erreur, et invisible sur poste de travail où la règle ne s'applique pas. */
  .chain-link .chain-say-sub { display: none; }
  .chain-link b { font-size: .92rem; }
}

/* ==========================================================================
   5. DISTORSION WEBGL AU SURVOL (vignettes)
   --------------------------------------------------------------------------
   Le canevas est posé PAR-DESSUS l'image, à l'identique, et n'existe que
   pendant le survol : un contexte à la fois pour toute la page. L'image reste
   dessous, intacte — si le contexte échoue, rien ne manque à l'écran.
   ========================================================================== */

.shot-gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity .26s var(--ease);
  pointer-events: none;                    /* le survol reste à la figure */
}
.shot-gl.is-on { opacity: 1; }
/* Tant que le canevas est visible, l'image dessous est masquée : sans ça, un
   liseré de l'original dépasse au bord de la zone réfractée. */
.shot-stage.gl-active > img { opacity: 0; }

/* ==========================================================================
   6. MOUVEMENT RÉDUIT
   --------------------------------------------------------------------------
   Le repli n'est pas « plus rien » : c'est la même page, aux états finaux,
   avec les transitions de survol et de focus conservées — elles confirment une
   action, elles ne racontent rien. `motion.js` ne pose de toute façon pas
   `html.tb-motion` dans ce mode ; ce bloc est la seconde ligne de défense,
   pour le cas où la préférence change en cours de session.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html.tb-motion .shot.reveal .shot-frame { clip-path: none; transition: none; }
  html.tb-motion .shot.reveal .shot-stage > img,
  html.tb-motion .shot.reveal .shot-ph,
  html.tb-motion .shot.is-parallax .shot-stage > img { transform: none; transition: none; }
  html.tb-motion .shot.reveal figcaption { opacity: 1; transform: none; transition: none; }

  html.tb-motion .chain-track { height: auto; }
  html.tb-motion .chain-stage { position: static; min-height: 0; }
  html.tb-motion .chain-say .w { opacity: 1; }
  .chain-link::after { transform: scaleX(1); }
  .chain-dot { border-color: var(--acc); background: var(--acc); color: #fff; }

  .shot-gl { display: none; }
}
