/* La vie de la page : ce qui bouge en dehors de l'ascenseur. « À part
   l'ascenseur, tout est très figé » (Jonathan, 11/09). Sept gestes, tous en
   CSS, aucun second contexte WebGL (la cabine tient le seul de la page) :

   1. le nom du groupe s'allume comme un néon dans le hero ;
   2. les titres de salle se révèlent comme un rideau qu'on lève ;
   3. les portraits montent en entrant dans l'écran et répondent au survol ;
   4. la liste des morceaux : la ligne en lecture pulse, la barre rebondit ;
   5. les décors de l'accueil dérivent moins vite que le contenu ;
   6. les dates arrivent comme des plaques qu'on encastre ;
   7. le nom géant du pied se remplit de la photo du groupe.

   Deux règles de construction :
   — Un élément caché « au repos » ne l'est QUE si JavaScript a posé
     `html[data-vivant]` (voir js/vivant.js) : sans script, sans observateur,
     ou en mouvement réduit, tout est visible, tout de suite.
   — Les entrées animent `translate` et `opacity`, jamais `transform` : le
     survol garde `transform` pour lui, et les deux se composent au lieu de
     s'écraser (une animation à `fill: both` gagne sinon sur le survol). */

/* ---- 1. Le néon du hero ---- */
/* Allumage en deux temps (clignotements à pas francs), puis la lumière
   respire, avec un tic toutes les quatre secondes et demie — un néon vit.
   La respiration change une ombre portée sur un très grand texte : elle
   n'est servie qu'en bureau, un téléphone repeindrait trop cher. */
body[data-etape="palier-rdc"] .arrivee__nom {
  animation: neon-allumage 1.3s steps(1, end) both;
}
@media (min-width: 721px) {
  body[data-etape="palier-rdc"] .arrivee__nom {
    animation: neon-allumage 1.3s steps(1, end) both,
               neon-respiration 4.5s 1.3s ease-in-out infinite;
  }
}
@keyframes neon-allumage {
  0%   { opacity: 0; }
  7%   { opacity: .55; }
  12%  { opacity: .04; }
  20%  { opacity: .9; }
  27%  { opacity: .25; }
  34%  { opacity: 1; }
  44%  { opacity: .8; }
  52%  { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes neon-respiration {
  0%, 100% { opacity: 1; text-shadow: 0 0 12px rgba(224, 251, 171, .5), 0 0 40px rgba(224, 251, 171, .2), 0 4px 30px rgba(31, 31, 31, .85); }
  50%      { opacity: 1; text-shadow: 0 0 18px rgba(224, 251, 171, .7), 0 0 60px rgba(224, 251, 171, .32), 0 4px 30px rgba(31, 31, 31, .85); }
  71%      { opacity: 1; }
  72%      { opacity: .8; }
  73.5%    { opacity: 1; }
}

/* ---- 2. Les titres de salle : le rideau se lève ---- */
/* Le masque part du bas ; `-15 %` en haut pour ne pas rogner l'accent
   d'« É » (l'interlignage de .92 fait déborder les accents de la boîte). */
html[data-vivant] body[data-vue="accueil"] main > .salle > h2 {
  clip-path: inset(-15% 0 100% 0);
  translate: 0 .25em;
  transition: clip-path .75s cubic-bezier(.2, .7, .2, 1), translate .75s cubic-bezier(.2, .7, .2, 1);
}
html[data-vivant] body[data-vue="accueil"] main > .salle.est-visible > h2 {
  clip-path: inset(-15% 0 -12% 0);
  translate: 0 0;
}
/* Quand le navigateur sait suivre le défilement, le rideau suit la molette
   au lieu d'une minuterie — c'est l'effet « révélation par masque » du lab. */
@supports (animation-timeline: view()) {
  html[data-vivant] body[data-vue="accueil"] main > .salle > h2 {
    transition: none;
    animation: titre-rideau linear both;
    animation-timeline: view();
    animation-range: entry 10% cover 30%;
  }
}
@keyframes titre-rideau {
  from { clip-path: inset(-15% 0 100% 0); translate: 0 .25em; }
  to   { clip-path: inset(-15% 0 -12% 0); translate: 0 0; }
}

/* ---- 3. Les portraits ---- */
html[data-vivant] .membres:not(.est-visible) .membre { opacity: 0; }
html[data-vivant] .membres.est-visible .membre {
  animation: monter .7s cubic-bezier(.2, .7, .2, 1) both;
  animation-delay: calc(var(--i, 0) * 90ms);
}
@keyframes monter {
  from { opacity: 0; translate: 0 28px; }
  to   { opacity: 1; translate: 0 0; }
}
@media (hover: hover) {
  .membre {
    transition: transform .4s cubic-bezier(.2, .7, .2, 1), box-shadow .4s ease;
    transform-origin: 50% 100%;
  }
  .membre img { transition: transform .6s cubic-bezier(.2, .7, .2, 1), filter .4s ease; }
  .membre__nom, .membre__role { transition: color .3s ease, translate .3s ease; }
  .membre:hover {
    transform: perspective(900px) rotateX(3deg) translateY(-6px);
    box-shadow: 0 22px 44px rgba(0, 0, 0, .38);
  }
  .membre:hover img { transform: scale(1.045); filter: brightness(1.06); }
  .membre:hover .membre__nom { color: var(--tilleul); }
  .membre:hover .membre__role { color: var(--blanc); translate: -3px 0; }
}

/* ---- 4. La platine ---- */
html[data-vivant] .morceaux:not(.est-visible) .morceau { opacity: 0; }
html[data-vivant] .morceaux.est-visible .morceau {
  animation: monter .55s cubic-bezier(.2, .7, .2, 1) both;
  animation-delay: calc(var(--i, 0) * 60ms);
}
.morceau { transition: transform .25s ease; }
.morceau__titre { transition: color .25s ease; }
@media (hover: hover) {
  .morceau:hover { transform: translateX(6px); }
  .morceau:hover .morceau__titre { color: var(--tilleul); }
  .morceau.en-cours:hover .morceau__titre { color: var(--ambre); }
}
/* La ligne en lecture : un anneau ambre qui s'élargit et s'efface, au pouls
   d'une platine — pas un clignotement. */
.morceau.en-cours .morceau__lire { animation: platine-pouls 1.7s ease-out infinite; }
@keyframes platine-pouls {
  0%   { box-shadow: 0 0 0 0 rgba(255, 190, 0, .55); }
  70%  { box-shadow: 0 0 0 10px rgba(255, 190, 0, 0); }
  100% { box-shadow: 0 0 0 10px rgba(255, 190, 0, 0); }
}
/* Le bouton de la barre : enfoncé vite, relâché avec un petit rebond. */
.lecteur__boutons .lecteur__lire { transition: transform .26s cubic-bezier(.34, 1.56, .64, 1), filter .2s ease; }
.lecteur__boutons .lecteur__lire:active { transform: scale(.86); transition-duration: .07s; }
.morceau__lire { transition: transform .26s cubic-bezier(.34, 1.56, .64, 1), border-color .2s, color .2s; }
.morceau__lire:active { transform: scale(.86); transition-duration: .07s; }

/* ---- 5. Les décors dérivent ---- */
/* Le fond glisse de 6 % dans l'autre sens pendant que la section traverse
   l'écran : il paraît plus loin que le texte — la parallaxe de l'ascenseur,
   prolongée sur la page. Le pseudo-élément est agrandi de 10 % en haut et
   en bas pour rester couvrant ; `overflow-y: clip` borne la section
   verticalement et `overflow-x: visible` laisse passer le décor pleine
   largeur (⚠ `clip-path: inset(0 -100vw)` ne le fait PAS : les insets
   négatifs sont ramenés à zéro, le décor se retrouvait coupé à la colonne
   de texte — vu en capture). Bureau seulement : quatre calques pleine page
   à composer, un téléphone n'a pas à les payer. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 721px) {
    body[data-vue="accueil"] main > .salle { overflow-x: visible; overflow-y: clip; }
    body[data-vue="accueil"] main > .salle::before {
      top: -10%; bottom: -10%;
      animation: decor-derive linear both;
      animation-timeline: view();
    }
  }
}
@keyframes decor-derive {
  from { translate: 0 -6%; }
  to   { translate: 0 6%; }
}

/* ---- 6. Les dates : des plaques qu'on encastre ---- */
html[data-vivant] .dates-liste:not(.est-visible) .date { opacity: 0; }
html[data-vivant] .dates-liste.est-visible .date {
  animation: encastrer .55s cubic-bezier(.2, .7, .2, 1) both;
  animation-delay: calc(var(--i, 0) * 110ms);
}
@keyframes encastrer {
  from { opacity: 0; translate: -28px 0; }
  to   { opacity: 1; translate: 0 0; }
}
/* L'encart ambre se pose avec la section, un souffle après le titre. */
html[data-vivant] main > .salle:not(.est-visible) .encart-info { opacity: 0; }
html[data-vivant] main > .salle.est-visible .encart-info {
  animation: poser .6s .25s cubic-bezier(.2, .7, .2, 1) both;
}
@keyframes poser {
  from { opacity: 0; scale: .96; translate: 0 12px; }
  to   { opacity: 1; scale: 1; translate: 0 0; }
}

/* ---- 7. Le nom du pied, découpé dans la photo ---- */
/* ORDRE DES RÈGLES, piège documenté au lab : la couleur pleine est la base,
   sans condition ; `color: transparent` et la photo n'existent QUE dans le
   bloc @supports qui garantit la découpe. Une inversion rend le titre
   invisible sans la moindre erreur. La photo est celle du hero, déjà
   chargée : zéro octet de plus. Le calque tilleul par-dessus garde au nom
   sa couleur de charte, la photo vit dedans. */
@supports (background-clip: text) or (-webkit-background-clip: text) {
  .pied__nom {
    position: relative;
    /* La photo ne se peint que DANS la boîte : avec l'interlignage à .9, les
       accents d'« È » et « É » en sortaient et disparaissaient (vu en
       capture). Une marge intérieure en haut les ramène dedans, la marge
       négative garde la mise en page. */
    padding-top: .16em;
    margin-top: -.16em;
    background-image:
      linear-gradient(rgba(224, 251, 171, .58), rgba(224, 251, 171, .58)),
      url('/assets/img/site/groupe-ascenseur.webp?v=1b9facbc');
    background-size: cover;
    background-position: 50% 30%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
  /* Le remplissage : un double du nom, en tilleul plein, s'efface pendant
     que le pied entre à l'écran — la photo apparaît dans les lettres au fur
     et à mesure qu'on atteint le bas. Sans `view()`, pas de double : le nom
     est découpé d'emblée, immobile. */
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .pied__nom::after {
        content: attr(data-texte);
        position: absolute; inset: 0;
        padding-top: inherit;        /* même boîte, même ligne de base que le nom */
        color: var(--tilleul);
        -webkit-text-fill-color: var(--tilleul);
        background: none;
        pointer-events: none;
        animation: pied-devoiler linear both;
        animation-timeline: view();
        animation-range: entry 15% entry 100%;
      }
      .pied__nom {
        animation: pied-derive linear both;
        animation-timeline: view();
        animation-range: entry 0% cover 100%;
      }
    }
  }
}
@keyframes pied-devoiler { from { opacity: 1; } to { opacity: 0; } }
@keyframes pied-derive { from { background-position: 50% 22%; } to { background-position: 50% 42%; } }

/* ---- Mouvement réduit : tout est posé, rien ne bouge ---- */
/* Défense en profondeur : js/vivant.js ne pose pas `data-vivant` dans ce
   cas, mais si un état caché s'appliquait quand même, il tombe ici. */
@media (prefers-reduced-motion: reduce) {
  .membre, .morceau, .date, .encart-info, main > .salle > h2, .pied__nom, .pied__nom::after,
  .arrivee__nom, .morceau__lire, .lecteur__lire {
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
    clip-path: none !important;
  }
  .pied__nom::after { content: none !important; }
  body[data-vue="accueil"] main > .salle::before { animation: none !important; translate: none !important; }
}
