/* motion.css — révélations, signature du hero, compteurs, transitions entre pages, repli reduced-motion.
   DESIGN.md § 6 et § 8. On n'anime que transform, opacity, clip-path, background-size,
   text-decoration-thickness et les couleurs. */

/* ---------- Révélations au scroll (A5, A6, A7) ---------- */
/* Sans JS (pas de html.js), tout est visible : les règles ci-dessous ne s'appliquent qu'avec la classe .js. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity var(--t-reveal) var(--ease-out),
    transform var(--t-reveal) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 60ms);
}

/* Cascade : data-i="1" à "6" (pas d'attribut style, CSP oblige). Sert aussi au hero (.hero__l, .hero__fade). */
[data-i="1"] { --i: 1; }
[data-i="2"] { --i: 2; }
[data-i="3"] { --i: 3; }
[data-i="4"] { --i: 4; }
[data-i="5"] { --i: 5; }
[data-i="6"] { --i: 6; }

/* Cadres (A6) : l'image n'est jamais rognée — un élément entièrement rogné par clip-path n'intersecte jamais dans Chromium,
   ni pour IntersectionObserver ni pour le chargement paresseux des images, qui restaient vides. Un voile couleur papier
   posé sur le cadre se retire de haut en bas (scaleY 1 → 0, origine en bas) : la capture apparaît « posée » sur la table.
   inset: -20px : le voile couvre aussi les repères de coin (.marks__m, à -14px).
   Le voile est peint en `var(--paper)` : dans le chapitre sombre (.section--dark, DESIGN.md § 2 bis) ce jeton vaut
   l'encre, donc le voile y est sombre sans une règle de plus. */
.js [data-reveal="frame"],
.js .fig[data-reveal] .fig__clip {
  position: relative;
  transform: none;
  opacity: 1;
  transition: none;
}

.js [data-reveal="frame"]::after,
.js .fig[data-reveal] .fig__clip::after {
  content: "";
  position: absolute;
  inset: -20px;
  z-index: 5;
  background: var(--paper);
  transform-origin: bottom;
  transition: transform var(--t-frame) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 60ms);
  pointer-events: none;
}

/* Ligne de cote (A7) : seul le filet s'étire, les tirets et l'étiquette apparaissent ensuite — le conteneur ne bouge pas. */
.js [data-reveal="dim"] {
  opacity: 1;
  transform: none;
  transition: none;
}

.js [data-reveal="dim"] .dim__line {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-draw) var(--ease-in-out);
}

.js [data-reveal="dim"] .dim__tick,
.js [data-reveal="dim"] .dim__label {
  opacity: 0;
  transition: opacity 240ms ease;
}

.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

.js [data-reveal="frame"].is-in::after,
.js .fig[data-reveal].is-in .fig__clip::after {
  transform: scaleY(0);
}

.js [data-reveal="dim"].is-in .dim__line {
  transform: scaleX(1);
}

.js [data-reveal="dim"].is-in .dim__tick {
  opacity: 1;
  transition-delay: var(--t-draw);
}

.js [data-reveal="dim"].is-in .dim__label {
  opacity: 1;
  transition-delay: 300ms;
}

/* Figures des pages projet (A6) : motion.js observe la <figure data-reveal>, le voile est sur .fig__clip et la légende
   suit en fondu. */
.js .fig[data-reveal] {
  opacity: 1;
  transform: none;
  transition: none;
}

.js .fig[data-reveal] figcaption {
  opacity: 0;
  transition: opacity var(--t-reveal) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 60ms + 200ms);
}

.js .fig[data-reveal].is-in figcaption {
  opacity: 1;
}

/* ---------- Signature du hero : « la cote qui se trace » (A1 à A4) ---------- */
/* Balisage attendu : .hero > .dim (.dim__line, .dim__tick, .dim__label — partial views/partials/dim.ejs),
   h1 > .hero__l (un par ligne, data-i), .hero__fade (sous-titre, boutons, tarifs, data-i),
   .hero__frame (cadre principal), .hero__phone (cadre téléphone), .marks__m.
   Toutes les animations sont en fill-mode `backwards` : l'état final de chaque image-clé est l'état statique de l'élément,
   et un clip-path retenu par `forwards` fait disparaître un élément positionné dans les captures pleine page de Chromium.
   Les cadres ne sont plus animés ici : ils portent `data-reveal="frame"` et se révèlent par le voile A6.
   Un clip-path posé sur le cadre s'arrête sur sa bordure, que Chromium laisse bavocher (un filet gris flottant seul
   pendant 400 ms) ; un fondu d'opacité posé sur une image donne une double exposition. */

@keyframes hero-draw {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@keyframes hero-fade {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

@keyframes hero-opacity {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes hero-clip-left {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 -2% 0 0); }
}

.hero .dim__line {
  animation: hero-draw var(--t-draw) var(--ease-in-out) backwards;
}

.hero .dim__tick {
  animation: hero-opacity 120ms ease backwards;
  animation-delay: var(--t-draw);
}

.hero .dim__label {
  animation: hero-opacity 240ms ease backwards;
  animation-delay: 300ms;
}

/* Le titre s'engage tout de suite : à 120 ms la page ne doit plus être vide sous l'en-tête. */
.hero__l {
  display: block;
  animation: hero-clip-left 520ms var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * 90ms);
}

.hero__fade {
  animation: hero-fade 360ms var(--ease-out) backwards;
  animation-delay: calc(380ms + var(--i, 0) * 70ms);
}

/* Cadres du hero : même geste que A6 (voile couleur papier retiré de haut en bas), porté par
   `data-reveal="frame"` — donc par une TRANSITION, pas par une @keyframes. Chromium peint une @keyframes
   posée sur un pseudo-élément situé sous la ligne de flottaison à son état de départ dans les captures
   pleine page : le voile restait en place et le cadre disparaissait de _qa/*.png (vérifié, cf. DESIGN.md § 6).
   Sans JS, les cadres sont simplement visibles d'emblée. */
.hero__frame,
.hero__phone {
  position: relative;
}

.hero .marks__m {
  animation: hero-opacity 240ms ease backwards;
  animation-delay: 1000ms;
}

/* ---------- Menu mobile (A13) ---------- */
.nav-panel {
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity var(--t-ui) var(--ease-out), transform var(--t-ui) var(--ease-out);
}

.nav-panel.is-open {
  opacity: 1;
  transform: none;
}

/* ---------- Défilement lent des captures hautes (A9) ---------- */
/* Le mécanisme de fond est le défilement natif de la région (components.css) : il marche au clavier,
   au doigt, à la molette et sous reduced-motion. Cette animation n'est plus qu'un APERÇU au survol,
   réservé aux pointeurs fins. Les deux ne se marchent pas dessus : tant que la souris survole, la
   région passe en `overflow: hidden` (ce qui ramène le défilement à zéro) et le voile du bas signale
   qu'il y a une suite ; dès que la région prend le focus, l'aperçu s'arrête et l'utilisateur reprend
   la main. `:focus-within` sur le cadre, car le focus va sur l'enfant .frame__scroll. */
@keyframes frame-scroll {
  from { transform: translateY(0); }
  to { transform: translateY(calc(-100% + 32rem)); }
}

@media (hover: hover) and (pointer: fine) {
  .frame--tall[data-scroll]:hover:not(:focus-within) .frame__scroll {
    overflow-y: hidden;
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 4rem), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 4rem), transparent);
  }

  .frame--tall[data-scroll]:hover:not(:focus-within) img {
    animation: frame-scroll 6s linear infinite alternate;
  }
}

/* ---------- Transitions entre pages (A14) ---------- */
@view-transition {
  navigation: auto;
}

::view-transition-old(root) {
  animation: 120ms ease both view-fade-out;
}

::view-transition-new(root) {
  animation: 180ms var(--ease-out) both view-fade-in;
}

@keyframes view-fade-out {
  to { opacity: 0; }
}

@keyframes view-fade-in {
  from { opacity: 0; }
}

/* ---------- Repli : prefers-reduced-motion ---------- */
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }

  .js [data-reveal],
  .js [data-reveal="frame"],
  .js [data-reveal="dim"],
  .js [data-reveal="dim"] .dim__line,
  .js [data-reveal="dim"] .dim__tick,
  .js [data-reveal="dim"] .dim__label,
  .js .fig[data-reveal] .fig__clip,
  .js .fig[data-reveal] figcaption {
    opacity: 1;
    transform: none;
    clip-path: none;
    transition: none;
  }

  .js [data-reveal="frame"]::after,
  .js .fig[data-reveal] .fig__clip::after {
    display: none;
  }

  .hero .dim__line,
  .hero .dim__tick,
  .hero .dim__label,
  .hero__l,
  .hero__fade,
  .hero .marks__m {
    animation: none;
  }

  /* L'aperçu au survol disparaît ; la région reste défilante, donc « page complète » reste vrai. */
  .frame--tall[data-scroll]:hover:not(:focus-within) .frame__scroll {
    overflow-y: auto;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .frame--tall[data-scroll]:hover:not(:focus-within) img {
    animation: none;
  }

  .nav-panel {
    transform: none;
    transition-duration: 1ms;
  }

  .btn,
  .link,
  .link-arrow,
  .contact__big,
  .prose a,
  .nav__link::after,
  .proj::before,
  .frame,
  .icon-arrow,
  .input,
  .select,
  .textarea {
    transition-duration: 1ms;
  }

  .btn:active {
    transform: none;
  }

  html {
    scroll-behavior: auto;
  }
}
