/* ==========================================================================
   JOLIHERO — ouverture en deux temps
   1. ARRIVÉE     : les photos convergent des bords vers le logo.
   2. DÉFILEMENT  : un geste = une photo, pendant que le logo s'efface.

   Aucune dépendance. Tout est en transform/opacity/filter : le navigateur
   compose sur le GPU, rien ne déclenche de recalcul de mise en page.

   La scène tient sur UN écran. Il n'y a pas de piste de défilement à dérouler :
   c'est le geste qui déclenche la photo suivante (voir jolihero.js), et la page
   ne descend qu'une fois l'introduction vue.

   Réglages : les variables de .jolihero ci-dessous. Ne touche pas au reste.
   ========================================================================== */

.jolihero {
  /* --- ce que tu règles --- */
  --jh-fond: #0d0d12;
  --jh-encre: #f6f3ec;
  --jh-accent: #d8b878;
  --jh-voile: 0.42;              /* assombrissement des photos, 0 à 1 */
  --jh-desat: 0.55;              /* désaturation des photos, 0 = N&B, 1 = couleur */
  --jh-arrivee: 1200ms;          /* durée de la convergence à l'arrivée */
  --jh-glisse: 1100ms;           /* durée d'un fondu — ET temps minimum entre
                                    deux photos : une seule valeur pour les deux,
                                    impossible de les désaccorder */
  --jh-marge: clamp(1rem, 4vw, 4rem);

  --jh-sortie: cubic-bezier(0.16, 1, 0.3, 1);   /* décélération franche */
  --jh-douce: cubic-bezier(0.65, 0, 0.35, 1);

  position: relative;
  background: var(--jh-fond);
  color: var(--jh-encre);
}

/* --------------------------------------------------------------------------
   SOCLE SANS JAVASCRIPT
   Sans JS, la scène ne se joue pas : les photos s'empilent, le logo est là,
   tout est lisible. Un visiteur ne perd que l'animation, jamais le contenu.
   -------------------------------------------------------------------------- */

.jolihero__scene {
  position: relative;
  min-block-size: 100svh;
  overflow: clip;
}

.jolihero__photos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 2px;
}

.jolihero__photo {
  position: relative;
  margin: 0;
  min-height: 42svh;
  overflow: hidden;
}

.jolihero__photo img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}

.jolihero__marque {
  padding: clamp(3rem, 10vw, 8rem) var(--jh-marge);
  text-align: center;
}

.jolihero__logo {
  display: block;
  margin-inline: auto;
  max-inline-size: min(80vw, 30rem);
  block-size: auto;
}

.jolihero__titre {
  margin: 0;
  font-size: clamp(2.4rem, 9vw, 7rem);
  line-height: 0.92;
  letter-spacing: -0.02em;
}

.jolihero__sous-titre {
  margin: 1.2rem 0 0;
  opacity: 0.75;
  font-size: clamp(0.9rem, 2vw, 1.1rem);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.jolihero__passer,
.jolihero__indice,
.jolihero__jauge {
  display: none;
}

/* --------------------------------------------------------------------------
   SCÈNE ANIMÉE — activée seulement quand .jh-actif est sur <html>

   Cette classe est posée par l'amorce en tête de <head> (voir SKILL.md), PAS
   par jolihero.js : le script arrive trop tard, le navigateur a déjà peint le
   socle et le visiteur voit une rangée de vignettes avant que la scène
   n'apparaisse. C'est le premier défaut qu'un client remarque.
   -------------------------------------------------------------------------- */

.jh-actif .jolihero {
  block-size: 100svh;
}

.jh-actif .jolihero__scene {
  position: absolute;
  inset: 0;
  min-block-size: 0;
  display: grid;
  place-items: center;
}

.jh-actif .jolihero__photos {
  position: absolute;
  inset: 0;
  display: block;
  z-index: 0;
}

.jh-actif .jolihero__photo {
  position: absolute;
  inset: 0;
  min-height: 0;
  opacity: 0;
  will-change: transform, opacity;
}

.jh-actif .jolihero__photo img {
  position: absolute;
  inset: 0;
  filter: saturate(var(--jh-desat));
}

/* Le voile : sans lui, aucune typographie ne survit sur une photo. */
.jh-actif .jolihero__photo::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 90% at 50% 45%, transparent 20%, color-mix(in oklab, var(--jh-fond) 92%, transparent) 100%),
    linear-gradient(to bottom, color-mix(in oklab, var(--jh-fond) 55%, transparent), transparent 45%, var(--jh-fond));
  opacity: var(--jh-voile);
}

/* Ce voile-là s'éteint en bas de photo : le radial y est presque transparent et
   le linéaire retombe. Une légende posée sur une image claire — une carte
   postale ancienne, un mur au soleil — y devient illisible.
   Le composant n'utilise que ::after sur la photo : ::before est libre, il passe
   au-dessus de l'image et sous la légende (z-index 3). */
.jh-actif .jolihero__photo::before {
  content: '';
  position: absolute;
  inset: auto 0 0;
  z-index: 1;
  pointer-events: none;
  block-size: clamp(9rem, 34svh, 22rem);
  background: linear-gradient(
    to top,
    color-mix(in oklab, var(--jh-fond) 78%, transparent),
    color-mix(in oklab, var(--jh-fond) 30%, transparent) 48%,
    transparent
  );
}

/* Voile du haut. Presque tous les sites posent un en-tête transparent par-dessus
   le héros : sur une photo claire, ses liens disparaissent. Mettre à 0 si
   l'en-tête du site est déjà opaque. */
.jolihero__scene::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  z-index: 2;
  pointer-events: none;
  block-size: clamp(5rem, 15svh, 12rem);
  background: linear-gradient(
    to bottom,
    color-mix(in oklab, var(--jh-fond) 66%, transparent),
    color-mix(in oklab, var(--jh-fond) 24%, transparent) 55%,
    transparent
  );
}

/* ------------------------------ TEMPS 1 : L'ARRIVÉE ------------------------
   Les photos entrent par les bords, filent vers le centre, et se fondent là où
   le logo apparaît. La première ne part pas : elle s'ouvre en plein écran et
   devient le décor du logo.

   Deux animations distinctes, parce que la première photo a un rôle différent
   des autres. Une seule animation pour toutes laissait la DERNIÈRE recouvrir
   l'écran et masquer le logo — le premier essai s'est cassé exactement là.
   -------------------------------------------------------------------------- */

/* Les photos qui convergent. Elles disparaissent TANT QU'ELLES SONT ENCORE
   GRANDES : 46 % de l'écran au plus petit visible.
   Une version précédente les faisait rétrécir jusqu'à 10 % avant de les
   effacer. Sur des photos ordinaires, ça ne se lit pas comme une convergence
   mais comme des vignettes qui chargent mal — deux clients l'ont signalé dans
   ces termes-là. Ne pas y revenir. */
@keyframes jh-converge {
  0% {
    opacity: 0;
    transform: translate3d(var(--jh-de-x, 0), var(--jh-de-y, 0), 0) scale(0.66)
      rotate(var(--jh-rot, 0deg));
  }
  18% {
    opacity: 0.8;
  }
  55% {
    opacity: 0.55;
    transform: translate3d(0, 0, 0) scale(0.46) rotate(0deg);
  }
  72% {
    opacity: 0;
  }
  100% {
    opacity: 0;
    transform: translate3d(0, 0, 0) scale(0.42) rotate(0deg);
  }
}

/* La première photo reste. Elle s'ouvre, floue puis nette : c'est sur elle que
   le logo se pose, et c'est elle qui ouvre le défilement. */
@keyframes jh-ouvre {
  0% {
    opacity: 0;
    transform: scale(1.32);
    filter: blur(22px);
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: scale(1.045);
    filter: blur(0);
  }
}

/* Les photos suivantes attendent leur tour, invisibles.

   Elles convergeaient auparavant vers le logo en passant par le centre de
   l'écran à 46 % de leur taille. Deux clients de suite ont signalé la même
   chose, dans les mêmes termes : on aperçoit une demi-seconde des vignettes
   qu'on n'a pas le temps de regarder, et l'on croit que la page est en train
   de charger. L'effet coûtait donc la première impression qu'il devait servir.

   On garde une animation de MÊME durée et de même cadence : le script clôt la
   phase d'arrivée sur l'« animationend » de la dernière photo. Sans animation,
   cet événement ne partirait jamais et la phase ne tomberait qu'au bout du
   délai de secours de quatre secondes. */
@keyframes jh-attend {
  from, to { opacity: 0; }
}

.jh-actif .jolihero[data-jh-phase='arrivee'] .jolihero__photo {
  z-index: 1;
  clip-path: inset(0%);
  animation: jh-attend var(--jh-arrivee) linear both;
  animation-delay: calc(var(--jh-rang, 0) * 150ms);
}

.jh-actif .jolihero[data-jh-phase='arrivee'] .jolihero__photo:first-child {
  z-index: 0;
  animation-name: jh-ouvre;
  animation-duration: calc(var(--jh-arrivee) * 1.15);
  animation-timing-function: var(--jh-sortie);
  animation-delay: 0ms;
}

/* Les légendes ne s'affichent pas pendant la convergence : elles passeraient
   en accéléré et personne ne les lirait. */
.jh-actif .jolihero[data-jh-phase='arrivee'] .jolihero__legende {
  opacity: 0;
}

/* La légende de la PREMIÈRE photo reste masquée tant que la marque est là :
   titre, sous-titre, accroche, boutons et légende sur une seule image, ça fait
   cinq blocs de texte empilés. Elle revient dès la deuxième photo. */
.jh-actif .jolihero[data-jh-vue='0'] .jolihero__photo:first-child .jolihero__legende {
  opacity: 0;
}

/* Le logo se pose au point de convergence, quand les vignettes y arrivent. */
@keyframes jh-logo-entre {
  from {
    opacity: 0;
    transform: translate3d(0, 1.2rem, 0) scale(0.92);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.jh-actif .jolihero[data-jh-phase='arrivee'] .jolihero__marque {
  z-index: 3;
  animation: jh-logo-entre 1000ms var(--jh-sortie) both;
  animation-delay: calc(var(--jh-arrivee) * 0.62);
}

/* Entre le premier rendu et l'exécution du script, .jh-actif est déjà posée
   mais aucune photo n'est encore désignée : la scène est alors l'image de
   départ de l'animation, un fond uni. L'habillage attend son tour. */
.jh-actif .jolihero:not([data-jh-vue]) .jolihero__indice,
.jh-actif .jolihero:not([data-jh-vue]) .jolihero__jauge,
.jh-actif .jolihero[data-jh-phase='arrivee'] .jolihero__indice,
.jh-actif .jolihero[data-jh-phase='arrivee'] .jolihero__jauge {
  opacity: 0;
  transition: opacity 600ms var(--jh-douce);
}

/* ---------------------------- TEMPS 2 : LE DÉFILEMENT ----------------------
   Une photo visible à la fois. data-jh-vue porte l'index courant ; le script
   ne fait que l'écrire, tout le mouvement est ici.
   -------------------------------------------------------------------------- */

.jh-actif .jolihero__marque {
  position: relative;
  z-index: 2;
  text-align: center;
  padding: 0 var(--jh-marge);
  transition:
    opacity 700ms var(--jh-douce),
    transform 900ms var(--jh-sortie);
}

/* Le voile du composant est transparent au centre : il suppose une photo qui
   laisse la place au logo. Beaucoup de photos réelles sont chargées jusqu'au
   milieu. Ce halo est sous la marque seulement — la photo reste lisible tout
   autour. */
.jh-actif .jolihero__marque::before {
  content: '';
  position: absolute;
  inset: -30% -10%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
    66% 62% at 50% 50%,
    color-mix(in oklab, var(--jh-fond) 80%, transparent) 0%,
    color-mix(in oklab, var(--jh-fond) 55%, transparent) 45%,
    transparent 72%
  );
}

/* Le logo s'efface et remonte dès qu'on quitte le premier plan.
   L'attribut est exigé (:not([data-jh-vue='0']) seul masquerait la marque tant
   que le script n'a pas tourné — donc un écran vide au chargement). */
.jh-actif .jolihero[data-jh-vue]:not([data-jh-vue='0']) .jolihero__marque {
  opacity: 0;
  transform: translate3d(0, -2.5rem, 0) scale(0.96);
  pointer-events: none;   /* sinon des boutons invisibles restent cliquables */
}

.jh-actif .jolihero__photo {
  transition:
    opacity var(--jh-glisse) var(--jh-douce),
    transform calc(var(--jh-glisse) * 1.4) var(--jh-sortie),
    clip-path calc(var(--jh-glisse) * 1.2) var(--jh-douce);
  clip-path: inset(6%);
  transform: scale(1.12) translate3d(0, 2%, 0);
}

.jh-actif .jolihero__photo[data-jh-visible] {
  opacity: 1;
  clip-path: inset(0%);
  /* Le léger surdimensionnement qui reste donne la dérive lente. */
  transform: scale(1.045) translate3d(0, 0, 0);
}

/* La photo déjà vue sort par le haut au lieu de disparaître sur place. */
.jh-actif .jolihero__photo[data-jh-passe] {
  opacity: 0;
  transform: scale(1.02) translate3d(0, -4%, 0);
  clip-path: inset(12% 6%);
}

/* --------------------------- LÉGENDES PAR PHOTO ---------------------------- */

.jolihero__legende {
  position: absolute;
  inset-inline: var(--jh-marge);
  inset-block-end: clamp(4rem, 12svh, 8rem);
  z-index: 3;
  margin: 0;
  max-inline-size: 28ch;
  font-size: clamp(1.6rem, 4.2vw, 3.4rem);
  line-height: 1.08;
  text-wrap: balance;
  text-shadow: 0 1px 18px color-mix(in oklab, var(--jh-fond) 75%, transparent);
  opacity: 0;
  transform: translate3d(0, 1.5rem, 0);
  transition:
    opacity 800ms var(--jh-douce),
    transform 900ms var(--jh-sortie);
  pointer-events: none;
}

.jh-actif .jolihero__photo[data-jh-visible] .jolihero__legende {
  opacity: 1;
  transform: none;
  transition-delay: 220ms;
}

/* Sans JS, les légendes se lisent simplement sous leur photo.
   Le sélecteur porte sur <html> : c'est là qu'est .jh-actif. Une version
   précédente testait une classe .jh-scene qui n'existait nulle part, donc la
   règle s'appliquait TOUJOURS — les trois légendes restaient affichées en même
   temps par-dessus la scène. */
html:not(.jh-actif) .jolihero__legende {
  position: static;
  opacity: 1;
  transform: none;
  text-shadow: none;
  padding: 1rem var(--jh-marge) 2rem;
  font-size: clamp(1.2rem, 3vw, 1.8rem);
}

/* ------------------------------ HABILLAGE ---------------------------------- */

.jh-actif .jolihero__jauge {
  display: flex;
  position: absolute;
  inset-block-end: var(--jh-marge);
  inset-inline: var(--jh-marge);
  z-index: 4;
  gap: 0.5rem;
  align-items: center;
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  opacity: 0.6;
}

.jolihero__jauge-piste {
  flex: 1;
  block-size: 1px;
  background: currentColor;
  opacity: 0.25;
}

.jolihero__jauge-piste i {
  display: block;
  block-size: 100%;
  background: var(--jh-accent);
  transform-origin: left;
  transform: scaleX(var(--jh-progression, 0));
  transition: transform var(--jh-glisse) var(--jh-douce);
}

/* L'invite reste visible pendant TOUTE l'introduction : c'est la seule chose
   qui dit au visiteur que son geste sert à quelque chose pendant qu'on retient
   la page. Elle ne disparaît qu'une fois l'introduction relâchée. */
.jh-actif .jolihero__indice {
  display: block;
  position: absolute;
  inset-block-end: calc(var(--jh-marge) + 2.5rem);
  inset-inline: 0;
  z-index: 4;
  text-align: center;
  font-size: 0.68rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  opacity: 0.55;
  transition: opacity 500ms;
}

.jh-actif .jolihero[data-jh-fini] .jolihero__indice {
  opacity: 0;
}

/* Lien d'évitement : c'est LA sortie de secours du visiteur pendant que la page
   est retenue. Toujours accessible au clavier, invisible sinon. */
.jh-actif .jolihero__passer {
  display: block;
  position: absolute;
  inset-block-start: 1rem;
  inset-inline-start: 1rem;
  z-index: 10;
  padding: 0.6rem 1rem;
  background: var(--jh-fond);
  color: var(--jh-encre);
  border: 1px solid var(--jh-accent);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform 200ms var(--jh-sortie);
}

.jolihero__passer:focus-visible {
  transform: none;
}

/* ------------------------------- MOBILE ------------------------------------
   La convergence coûte cher sur GPU mobile : on la remplace par un fondu.
   La scène, elle, fait déjà un écran — rien à raccourcir.
   -------------------------------------------------------------------------- */

@media (width < 48rem) {
  .jolihero {
    --jh-arrivee: 900ms;
  }

  .jh-actif .jolihero[data-jh-phase='arrivee'] .jolihero__photo {
    animation-name: jh-fondu;
  }

  @keyframes jh-fondu {
    from { opacity: 0; }
    to { opacity: 1; }
  }
}

/* --------------------------- MOUVEMENT RÉDUIT ------------------------------
   Exigence, pas option. Rien ne bouge, la page n'est jamais retenue, et TOUT
   reste visible : on retombe sur le socle sans JavaScript, complet par
   construction.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .jh-actif .jolihero,
  .jh-actif .jolihero__scene {
    block-size: auto;
    position: static;
    display: block;
  }

  /* `place-items: center` continue de s'appliquer sur un conteneur en bloc :
     les navigateurs Chromium l'appliquent aux boîtes de bloc, et la grille des
     photos se retrouvait rétrécie au contenu et centrée — une colonne étroite
     au lieu de la rangée pleine largeur. */
  .jh-actif .jolihero__scene {
    place-items: normal;
  }

  .jh-actif .jolihero__photos {
    position: static;
    display: grid;
  }

  /* [data-jh-visible] et [data-jh-passe] sont plus spécifiques que la classe
     seule : sans les nommer ici, la photo courante gardait son scale(1.045) et
     débordait de sa case, et les précédentes restaient à opacité 0. */
  .jh-actif .jolihero__photo,
  .jh-actif .jolihero__photo[data-jh-visible],
  .jh-actif .jolihero__photo[data-jh-passe] {
    position: relative;
    inset: auto;
    min-height: 42svh;
    opacity: 1;
    transform: none;
    clip-path: none;
    animation: none;
    transition: none;
  }

  .jh-actif .jolihero__photo img {
    position: absolute;
  }

  .jh-actif .jolihero__photo::before,
  .jolihero__scene::before,
  .jh-actif .jolihero__marque::before {
    display: none;
  }

  .jh-actif .jolihero__marque {
    opacity: 1;
    transform: none;
    animation: none;
    padding-block: clamp(3rem, 10vw, 8rem);
  }

  .jolihero__legende {
    position: static;
    opacity: 1;
    transform: none;
    text-shadow: none;
    padding: 1rem var(--jh-marge) 2rem;
    font-size: clamp(1.2rem, 3vw, 1.8rem);
  }

  .jh-actif .jolihero__jauge,
  .jh-actif .jolihero__indice {
    display: none;
  }
}

/* ------------------------------- IMPRESSION --------------------------------
   La scène ne rendrait qu'une grande page sombre : on n'imprime que le titre.
   -------------------------------------------------------------------------- */

@media print {
  .jolihero {
    background: none !important;
    color: #000 !important;
  }

  .jolihero__photos,
  .jolihero__indice,
  .jolihero__jauge,
  .jolihero__passer {
    display: none !important;
  }

  .jh-actif .jolihero,
  .jh-actif .jolihero__scene {
    block-size: auto !important;
    min-block-size: 0 !important;
    position: static !important;
    display: block !important;
  }

  .jh-actif .jolihero__marque {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
    padding-block: 0;
  }

  .jh-actif .jolihero__marque::before,
  .jolihero__scene::before {
    display: none !important;
  }
}
