@charset "UTF-8";

/* ==========================================================================
   Casa Paradisu — Hero piloté au défilement (« scrub »)

   Trois pièces, comme pour l'ancien hero séquentiel :

     .hero--scrub        la section, qui ne fait plus que contenir
     .hero-scrub__track  une piste haute de N écrans, dont l'unique rôle est
                         de donner au défilement la course nécessaire
     .hero-scrub__stage  une scène collante d'exactement un écran, qui reste
                         fixée pendant toute la traversée

   La hauteur de la piste est portée par `--hero-track-screens`. Elle vaut UN
   écran par défaut : sans JavaScript, sur écran tactile, sous
   `prefers-reduced-motion` ou si la vidéo est introuvable, la section est un
   hero classique d'une hauteur de fenêtre. Le script la remplace par la course
   réelle une fois la vidéo prête, et jamais avant.

   Ce fichier est chargé APRÈS `responsive.css` : à spécificité égale, c'est
   lui qui l'emporte sur les règles de `.hero`.
   ========================================================================== */

/* Repli pour les moteurs sans `svh`, à l'identique du hero séquentiel. */
@supports not (height: 100svh) {
  .hero--scrub { --hero-screen: 100vh; }
}

.hero--scrub {
  /* `.hero` est une boîte flexible d'une hauteur d'écran, qui coupe son
     débordement. Les trois sont à défaire ici : un ancêtre qui coupe le
     débordement désactive silencieusement `position: sticky` chez ses
     descendants. */
  display: block;
  min-height: 0;
  height: auto;
  overflow: visible;
}

/* Le voile supérieur de `.hero` défilerait avec la section, qui mesure
   plusieurs écrans : il ne protégerait la barre de navigation que sur le
   premier. Il est repris à l'identique par la scène, qui elle reste fixe. */
.hero--scrub::before { content: none; }

.hero-scrub__track {
  position: relative;
  height: calc(var(--hero-screen, 100svh) * var(--hero-track-screens, 1));
}

.hero-scrub__stage {
  position: sticky;
  top: 0;
  height: var(--hero-screen, 100svh);
  overflow: hidden;
  isolation: isolate;
  /* Fond d'attente en tons de marque, jamais un aplat noir : tant que la
     photographie n'est pas peinte, le bloc ne se lit pas comme une erreur. */
  background-color: var(--c-charcoal);
  background-image: linear-gradient(160deg, #2C302A 0%, #20231F 55%, #171A16 100%);
}

/* Voile sous l'en-tête, repris de `.hero::before` et posé sur la scène. */
.hero-scrub__stage::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: calc(var(--header-h) + 2rem);
  z-index: 4;
  pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(8, 14, 16, 0.64) 0%,
    rgba(8, 14, 16, 0.46) 40%,
    rgba(8, 14, 16, 0) 100%);
}

/* --- Médias ----------------------------------------------------------------
   La photographie est TOUJOURS là, dessous. La vidéo se pose par-dessus et
   n'est révélée qu'une fois sa première image décodée : aucun écran noir ne
   peut apparaître, ni pendant le chargement, ni si le format est refusé.
   -------------------------------------------------------------------------- */
.hero-scrub__media {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.hero-scrub__media img,
.hero-scrub__media video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Même convention que l'ancien hero : un `--focal` posé sur le média suffit
     à déplacer le point de cadrage, sans toucher à cette règle. */
  object-position: var(--focal, 50% 50%);
}

.hero-scrub__media video {
  z-index: 2;
  opacity: 0;
  transition: opacity 700ms var(--ease, ease);
}
.hero--scrub.is-video-ready .hero-scrub__media video { opacity: 1; }

/* --- Rapprochement de la photographie --------------------------------------
   Les trois valeurs sont posées par `hero-scrub.js` à chaque image, et elles
   ne valent que sur le hero qui porte `data-hero-zoom`.

   L'ORDRE COMPTE : en CSS les transformations se lisent de droite à gauche.
   Le déplacement s'applique donc d'abord, dans le repère propre de l'image,
   puis l'agrandissement l'amplifie autour du centre. C'est ce qui permet de
   raisonner en fractions de l'image sans dépendre de l'échelle en cours.

   Aucune transition ici : la position suit le défilement au pixel près. Une
   durée ajouterait une traîne qui continuerait après l'arrêt du geste. */

/* --- La caméra -------------------------------------------------------------
   Les valeurs sont posées par `hero-scrub.js` à chaque image, et elles ne
   valent que sur le hero qui porte `data-hero-zoom`.

   L'ORDRE COMPTE : en CSS les transformations se lisent de droite à gauche. Le
   déplacement s'applique donc d'abord, dans le repère propre de l'image, puis
   l'agrandissement l'amplifie autour du centre, puis la caméra tourne, et la
   perspective projette enfin le tout. C'est ce qui permet de raisonner en
   fractions de l'image sans dépendre de l'échelle en cours.

   `perspective()` est écrite DANS la liste plutôt que posée sur le parent :
   sa distance peut ainsi varier en cours de route — c'est elle qui donne le
   travelling compensé — et aucun contexte 3D n'est créé sur la scène, où
   `overflow: hidden` et `position: sticky` restent intacts.

   La brume reçoit exactement la même transformation que l'image : elle lui est
   donc solidaire, et son dégradé reste sur l'horizon au lieu de glisser
   dessus.

   Aucune transition ici : la position suit le défilement au pixel près. Une
   durée ajouterait une traîne qui continuerait après l'arrêt du geste. */
.hero-scrub__plan,
.hero-scrub__brume {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.hero--scrub.is-zoom-ready .hero-scrub__plan,
.hero--scrub.is-zoom-ready .hero-scrub__brume {
  transform:
    perspective(var(--zoom-focale, 1500px))
    rotateX(var(--zoom-tangage, 0deg))
    rotateY(var(--zoom-lacet, 0deg))
    rotateZ(var(--zoom-roulis, 0deg))
    scale(var(--zoom-echelle, 1))
    translate(var(--zoom-x, 0%), var(--zoom-y, 0%));
  /* La composition est annoncée au moteur pendant le hero seulement : la
     classe tombe dès que le rapprochement est désactivé. */
  will-change: transform;
}

/* --- Brume du lointain ------------------------------------------------------
   Un voile froid sur le fond, qui se retire à mesure qu'on avance : on sort de
   la brume en s'approchant. Ses deux bornes viennent du script, en fractions
   de la photographie ramenées dans la boîte, de sorte qu'elle s'arrête au
   feuillage quel que soit le format de l'écran. */
.hero-scrub__brume {
  z-index: 2;
  pointer-events: none;
  opacity: var(--zoom-brume, 0);
  background: linear-gradient(to bottom,
    rgba(198, 214, 226, 0.95) 0%,
    rgba(198, 214, 226, 0.60) var(--zoom-brume-haut, 34%),
    rgba(198, 214, 226, 0) var(--zoom-brume-bas, 52%));
}

/* --- Vignette ---------------------------------------------------------------
   En coordonnées d'ÉCRAN, et pas dans le plan : les angles qui s'assombrissent
   appartiennent à l'objectif, pas au paysage. Elle se resserre à mesure qu'on
   avance, ce qui ajoute au mouvement sans toucher à l'image.
   Sous le voile de l'en-tête (z-index 4) et sous la signature (5). */
.hero-scrub__vignette {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: var(--zoom-vignette, 0);
  background: radial-gradient(118% 84% at 50% 48%,
    rgba(10, 13, 11, 0) 40%,
    rgba(10, 13, 11, 0.18) 74%,
    rgba(10, 13, 11, 0.46) 100%);
}

@media (prefers-reduced-motion: reduce) {
  .hero-scrub__media video { transition: none; }
  /* Le hero redevient une photographie fixe, cadrée telle quelle : aucune
     caméra, aucun voile. */
  .hero--scrub .hero-scrub__plan {
    transform: none;
    will-change: auto;
  }
  .hero--scrub .hero-scrub__brume,
  .hero--scrub .hero-scrub__vignette { display: none; }
}

/* --- Signature de marque ---------------------------------------------------
   Reprise telle quelle du hero séquentiel : `.hero-intro__content`,
   `.hero-intro__title` et `.hero-intro__discover` sont déjà définis dans
   `pages.css` et `responsive.css`. Rien n'est redéclaré ici, seul le plan
   d'empilement est fixé — au-dessus du voile de l'en-tête.
   -------------------------------------------------------------------------- */
.hero--scrub .hero-intro__content { z-index: 5; }


/* ==========================================================================
   Séquence photographique
   --------------------------------------------------------------------------
   Cinq plans empilés, le premier au-dessus. Le module fait disparaître celui
   du dessus pour découvrir le suivant, qui est déjà en place et déjà opaque.

   POURQUOI PAS UN VRAI FONDU CROISÉ. Faire monter l'un pendant que l'autre
   descend les amène tous deux à 50 % d'opacité au même instant, et le fond
   de la scène se voit alors au travers : un creux de luminosité à chaque
   transition, d'autant plus visible que le fond est sombre. En empilant, il
   n'y a jamais qu'une seule opacité qui bouge, et jamais de trou.
   ========================================================================== */

.hero-seq {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.hero-seq__plan {
  position: absolute;
  inset: 0;
  margin: 0;
  display: flex;
  /* Le libellé est le SEUL enfant dans le flux : l'image est en position
     absolue. Ces deux axes suffisent donc à le placer dans l'écran. */
  padding: calc(var(--header-h) + 5vh) 7vw 12vh;
  /* Aucune transition : l'opacité suit le défilement au pixel près. Une durée
     ajouterait une traîne qui continuerait après l'arrêt du geste. */
  opacity: 1;
  will-change: opacity;
}

/* Le premier plan est au-dessus, le dernier au fond. */
.hero-seq__plan:nth-child(1) { z-index: 5; }
.hero-seq__plan:nth-child(2) { z-index: 4; }
.hero-seq__plan:nth-child(3) { z-index: 3; }
.hero-seq__plan:nth-child(4) { z-index: 2; }
.hero-seq__plan:nth-child(5) { z-index: 1; }

.hero-seq__image {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--focal, 50% 50%);
}

/* --- Les quatre coins ------------------------------------------------------
   Une phrase par plan, jamais deux fois au même endroit : le regard se
   déplace d'une image à l'autre au lieu de s'installer. */
[data-mot="haut-gauche"] { align-items: flex-start; justify-content: flex-start; }
[data-mot="haut-droite"] { align-items: flex-start; justify-content: flex-end;  }
[data-mot="bas-gauche"]  { align-items: flex-end;   justify-content: flex-start; }
[data-mot="bas-droite"]  { align-items: flex-end;   justify-content: flex-end;  }
[data-mot="centre"]      { align-items: center;     justify-content: center;    }

/* Le bouton « passer l'introduction » vit en bas à droite de la scène : une
   phrase placée là viendrait s'y poser dessus. */
[data-mot="bas-droite"] { padding-bottom: 22vh; }

/* --- La phrase -------------------------------------------------------------
   Corps mesuré et non décoratif : elle doit se lire d'un coup d'œil pendant
   que le plan est immobile, sans jamais concurrencer l'accueil final. */
/* --- Le voile de lisibilité ------------------------------------------------
   Il est posé SUR LE BORD DU CADRE, pas derrière le texte.

   La première version cernait le bloc de texte d'un dégradé elliptique. Sur
   un ciel ou un maquis personne ne le voyait ; sur le plafond clair du plan
   3, il se lisait comme une tache sombre posée sur l'image. Un dégradé centré
   sur le texte a toujours ce défaut : son centre est quelque part au milieu
   de la photographie, donc son bord aussi, et un bord au milieu d'une image
   finit par se voir.

   Un dégradé qui part d'un BORD du cadre n'a pas ce problème : il n'a aucune
   limite visible puisqu'il commence hors champ et s'éteint progressivement.
   C'est le filtre dégradé du photographe, pas une plaque posée sur la photo.
   Il suit le coin où la phrase est placée, et rien d'autre.

   `z-index: 0` : au-dessus de l'image, sous le texte. Sans lui, le
   pseudo-élément, dernier dans l'arbre, se peindrait PAR-DESSUS la phrase. */
.hero-seq__plan::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
[data-mot^="bas"]::after {
  background: linear-gradient(to top,
    rgba(10, 14, 12, 0.56) 0%,
    rgba(10, 14, 12, 0.28) 24%,
    rgba(10, 14, 12, 0) 54%);
}
[data-mot^="haut"]::after {
  background: linear-gradient(to bottom,
    rgba(10, 14, 12, 0.50) 0%,
    rgba(10, 14, 12, 0.24) 26%,
    rgba(10, 14, 12, 0) 54%);
}
/* L'accueil est au centre : aucun bord ne peut l'aider. On ferme donc
   légèrement le diaphragme sur toute l'image, un peu plus en haut et en bas
   qu'au milieu. Une image entière assombrie de façon continue ne se lit
   jamais comme un ajout. */
[data-mot="centre"]::after {
  /* La bande médiane était passée à 0,26 pour compenser un titre qui semblait
     gris. La cause était ailleurs, dans la vignette de la scène retirée plus
     haut ; elle revient donc à 0,20, et la photographie avec elle. Mesuré :
     le contraste du titre ne bouge pas (15,1:1 contre 14,7:1). */
  background: linear-gradient(to bottom,
    rgba(10, 14, 12, 0.40) 0%,
    rgba(10, 14, 12, 0.20) 32%,
    rgba(10, 14, 12, 0.22) 64%,
    rgba(10, 14, 12, 0.46) 100%);
}

.hero-seq__mot {
  position: relative;
  z-index: 1;
  max-width: 22ch;
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: clamp(1.25rem, 0.9rem + 1.5vw, 2rem);
  line-height: 1.28;
  letter-spacing: -0.005em;
  color: #fff;
  text-wrap: balance;
  /* Deux ombres SERRÉES, et c'est tout. Les quatre précédentes montaient à
     28px de flou : elles ne détachaient plus les lettres, elles leur posaient
     un nuage autour, très visible dès que le fond était clair. Le voile du
     cadre fait désormais le travail du contraste ; il ne reste ici qu'à poser
     une arête sur les glyphes. */
  text-shadow:
    0 1px 2px rgba(10, 14, 12, 0.40),
    0 0 8px rgba(10, 14, 12, 0.22);
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  will-change: opacity, transform;
}
[data-mot="haut-droite"] .hero-seq__mot,
[data-mot="bas-droite"]  .hero-seq__mot { text-align: right; }

/* --- La vignette de la scène, neutralisée ici ------------------------------
   `premium.css` pose `.hero-scrub__stage::after` : un dégradé radial à 0,42
   d'opacité EN SON CENTRE, qui s'éclaircit vers les bords, posé sur TOUTE la
   scène au-dessus des plans (z-index 4). Ce n'est pas une vignette, qui
   assombrit les angles : c'est l'inverse, un voile le plus dense au milieu.

   Or c'est au milieu que la séquence pose la marque et le titre d'accueil.
   Mesuré au pixel : le titre déclaré `#fff` ne sortait qu'à 190 sur 255, et
   le logo à 174. La page entière sous l'en-tête plafonnait à 190. Ni le voile
   des plans, ni les ombres, ni la graisse n'y étaient pour quelque chose ;
   ces pistes ont été essayées et écartées une à une.

   La séquence porte déjà son propre voile, plan par plan, calé sur l'endroit
   où se pose la phrase. Un second voile centré s'y ajoutait sans le savoir.
   En le retirant, le titre atteint 255 et son contraste passe de 9:1 à 15:1 :
   le texte gagne beaucoup plus que le fond, parce que le fond y est déjà
   sombre.

   La règle reste entière pour le hero à rapprochement, qui n'a pas de voile
   par plan et pour lequel elle a été écrite. */
.hero--seq .hero-scrub__stage::after { display: none; }

/* --- L'accueil -------------------------------------------------------------
   Le dernier plan ne s'efface pas : c'est sur lui que la page s'ouvre au
   contenu. Il porte la marque, le titre et l'appel à l'action. */
.hero-seq__accueil {
  max-width: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(14px, 2vw, 24px);
  text-align: center;
}

/* Le logo s'écrit. Le fichier de la marque est dévoilé de la gauche vers la
   droite par un masque dont la position vient de `--trace`, que le module
   fait progresser au défilement. Le bord du masque est adouci sur 22 % de la
   largeur : l'encre paraît couler, elle n'est pas coupée au couteau.

   Ce n'est PAS un tracé redessiné. Le logo est un mot manuscrit ; en refaire
   les courbes à la main aurait donné une imitation, jamais la marque. */
.hero-seq__logo {
  display: block;
  width: clamp(190px, 26vw, 330px);
  height: auto;
  /* Variante claire du fichier, comme au pied de page et sur l'en-tête. */
  /* Variante claire du fichier, plus deux ombres portees : sur un ciel clair
     le trait blanc disparaissait purement et simplement. Elles se posent sur
     le DESSIN, pas sur une boite : le logo est detoure. */
  filter: brightness(0) invert(1)
          drop-shadow(0 1px 5px rgba(10, 14, 12, 0.45));
  /* Plein, et non 0,96 : ce dernier pour-cent avait été concédé quand la
     scène portait encore sa vignette. La marque doit être du blanc franc. */
  opacity: 1;
  -webkit-mask-image: linear-gradient(90deg,
    #000 calc(var(--trace, 1) * 130% - 22%),
    rgba(0, 0, 0, 0) calc(var(--trace, 1) * 130%));
  mask-image: linear-gradient(90deg,
    #000 calc(var(--trace, 1) * 130% - 22%),
    rgba(0, 0, 0, 0) calc(var(--trace, 1) * 130%));
}

/* La voix des grands titres de l'accueil, et non celle des textes éditoriaux.

   Il était en Libre Baskerville, comme les phrases des quatre plans. Mais
   c'est le titre de la page, pas une légende : sur ce site les grands titres
   sont en linéale LÉGÈRE et resserrée (`.home-title`, `.hero-intro__title`),
   graisse 300. Baskerville n'a d'ailleurs pas de graisse légère, ce qui
   rendait ce titre plus épais que tous les autres du site.

   La taille reste le jeton `--fs-display-home`, celui du `h1` de l'accueil :
   l'échelle typographique du projet est une source unique, et aucune taille
   ne doit être choisie localement. Seuls la famille, la graisse et la chasse
   changent. La chasse est exprimée en cadratins et non en pixels, pour rester
   juste du téléphone au grand écran. */
/* `.hero--seq` en tête n'est pas décoratif, c'est ce qui fait gagner la
   règle. `pages.css` porte `.hero h1 { color: var(--c-ivory) }` : une classe
   PLUS un élément, soit (0,1,1). `.hero-seq__titre` seul ne pèse que (0,1,0)
   et perdait : le titre était peint en ivoire #F4F0E7, pas en blanc, et c'est
   de là que venait le gris. Le préfixe porte la règle à (0,2,0), qui passe
   devant. */
.hero--seq .hero-seq__titre {
  margin: 0;
  font-family: var(--font-azimut, var(--font-hero));
  font-weight: var(--fw-light);
  font-size: var(--fs-display-home);
  line-height: 1.02;
  letter-spacing: -0.028em;
  color: #fff;
  text-wrap: balance;
  /* UNE ombre, serrée, et faible. Le titre était en `#fff` et paraissait
     pourtant gris.

     L'ombre est peinte DERRIÈRE le texte : elle ne touche pas l'intérieur
     des lettres. Mais elle transparaît sous leurs bords adoucis, et sur une
     graisse 300 ces bords représentent près de la moitié de la largeur d'un
     trait. Un halo de 12px à 0,24 suffisait alors à ternir la lettre entière.
     Plus le titre est fin, moins il supporte d'ombre.

     Le contraste vient donc du FOND, assombri juste sous le titre, et non
     d'un halo posé sur les lettres. */
  text-shadow: 0 1px 2px rgba(10, 14, 12, 0.30);
}

.hero-seq__accueil .hero-intro__discover { margin-top: clamp(4px, 1vw, 12px); }

/* --- Sans le module --------------------------------------------------------
   Pas de JavaScript, mouvement réduit, module en panne : la classe n'est
   jamais posée. La section reste alors un hero d'un seul écran, et il faut
   qu'elle soit COMPLÈTE — la première photographie, la marque, le titre et
   le lien. Sans cette règle, l'accueil resterait au fond de la pile, invisible,
   et la page n'aurait plus de titre du tout.

   L'accueil est donc remonté sur le premier plan, et les quatre phrases se
   taisent : elles n'ont de sens qu'en séquence. */
/* `:not(.hero-seq__plan--accueil)` n'est pas une precaution, c'est la
   correction d'une collision. Sans elle, ce selecteur pese quatre classes
   contre trois a la regle suivante : il l'emportait, l'accueil restait a
   `opacity: 0`, et un visiteur sans JavaScript ou en mouvement reduit
   n'avait plus ni marque, ni titre, ni appel a l'action. La page perdait
   son seul `h1`. */
.hero--seq:not(.is-seq-ready) .hero-seq__plan:not(:nth-child(1)):not(.hero-seq__plan--accueil) { opacity: 0; }
.hero--seq:not(.is-seq-ready) .hero-seq__plan--accueil {
  opacity: 1;
  z-index: 6;
  background: none;
}
.hero--seq:not(.is-seq-ready) .hero-seq__plan--accueil .hero-seq__image { display: none; }
.hero--seq:not(.is-seq-ready) .hero-seq__accueil { opacity: 1; transform: none; }
.hero--seq:not(.is-seq-ready) .hero-seq__logo { --trace: 1; }

/* --- Écrans étroits -------------------------------------------------------- */
@media (max-width: 767.98px) {
  .hero-seq__plan { padding: calc(var(--header-h) + 4vh) 8vw 14vh; }
  [data-mot="bas-droite"] { padding-bottom: 24vh; }
  /* `:not(.hero-seq__accueil)` corrige une collision, pas une préférence.
     Le bloc d'accueil porte les deux classes : sans cette exception, la règle
     du palier étroit l'emportait sur le `max-width: none` du bloc de base,
     et l'accueil se retrouvait bridé à la largeur d'une légende, 229px sur
     un écran de 375 qui en offre 315. Le titre y cassait en deux lignes
     pour une raison qui n'avait rien à voir avec lui. */
  .hero-seq__mot:not(.hero-seq__accueil) { max-width: 16ch; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-seq__mot { opacity: 1; transform: none; }
  .hero-seq__logo { --trace: 1; }
}
