/*
 * title-reveal.css — Animation "shutter/slice" par caractère (site-wide)
 * ========================================================================
 * Ce fichier contient UNIQUEMENT les styles et keyframes de l'animation
 * de titres par lettre. Chargé dans base.html.twig pour toutes les pages.
 *
 *
 * STRUCTURE DOM PRODUITE PAR LE JS
 * ---------------------------------
 * Le JS enveloppe maintenant les lettres en deux niveaux :
 *
 *   "Bonjour Monde" devient :
 *
 *   <span class="tr-word">                              ← conteneur du MOT
 *     <span class="tr-char">                            ← conteneur d'une LETTRE
 *       <span class="tr-char__main">B</span>            ← lettre principale (fondu+flou)
 *       <span class="tr-char__slice tr-char__slice--top"></span>  ← bande haute (vide)
 *       <span class="tr-char__slice tr-char__slice--mid"></span>  ← bande milieu (vide)
 *       <span class="tr-char__slice tr-char__slice--bot"></span>  ← bande basse (vide)
 *     </span>
 *     ... (autres lettres du mot)
 *   </span>
 *   " "                                                 ← VRAI nœud texte espace
 *   <span class="tr-word">...</span>                    ← mot suivant
 *
 * Les slices sont des éléments VIDES (pas de texte). Leur fond coloré
 * (var(--accent) via CSS) crée l'effet de balayage décoratif.
 *
 * La lettre principale fait un fondu + blur (opacity 0->1, blur 10px->0).
 * Les 3 bandes "balaient" horizontalement via translateX et opacity,
 * chacune clippée sur une tranche de la hauteur de la lettre.
 *
 * Le stagger (délai croissant par lettre) est piloté par la custom
 * property CSS --i posée en inline style par le JS sur chaque .tr-char.
 *
 *
 * CORRECTION DE MISE EN PAGE (vs version précédente)
 * ---------------------------------------------------
 * Problème résolu : avec des .tr-char directement dans le titre, chaque
 * lettre étant un inline-block, le navigateur pouvait :
 *   1. Couper une ligne au milieu d'un mot (chaque lettre = point de coupure).
 *   2. Changer l'alignement (le contenu inline-block se comporte différemment
 *      d'un flux texte normal selon la largeur du conteneur et le text-align).
 *   3. Modifier l'interligne (chaque inline-block a sa propre baseline).
 *
 * Solution :
 *   - .tr-word { display:inline; white-space:nowrap } : les mots restent inline
 *     (flux texte normal), ne se coupent jamais en deux, et le text-align du
 *     titre est respecté sans modification.
 *   - Les espaces entre mots sont de VRAIS nœuds texte ' ' (pas des spans)
 *     => largeur naturelle de la fonte, points de coupure naturels.
 *   - .tr-char { display:inline-block } reste pour overflow:hidden (contenir
 *     les slices animées), mais est maintenant à l'intérieur d'un .tr-word.
 *
 *
 * PROGRESSIVE ENHANCEMENT
 * -----------------------
 * Principe fondamental : sans JS, les titres sont visibles normalement.
 *
 * Le JS ajoute la classe .js-title-reveal-ready sur <html> AVANT de
 * modifier le DOM des titres. Toutes les règles CSS qui cachent ou
 * transforment les éléments sont préfixées par .js-title-reveal-ready
 * => sans JS, aucune règle de cache n'est active => titres lisibles.
 *
 *
 * DÉCLENCHEMENT DE L'ANIMATION
 * -----------------------------
 * L'animation est pilotée par CSS (keyframes + animation-delay).
 * Le JS se contente d'ajouter/retirer la classe .is-revealing sur
 * chaque titre quand il entre dans le viewport (IntersectionObserver).
 *
 * Pour REJOUER l'animation à la ré-entrée (scroll down ET scroll up) :
 *   1. Le JS retire .is-revealing quand le titre sort du viewport.
 *   2. Un forçage de reflow (lecture de offsetWidth) réinitialise
 *      l'état de l'animation CSS avant de re-poser .is-revealing.
 *
 * Le titre hero (above the fold) reçoit .is-revealing immédiatement
 * au chargement, sans attendre le scroll.
 *
 *
 * PERFORMANCE
 * -----------
 * Les animations CSS sont exécutées sur le GPU (transform + opacity).
 * filter:blur() a un coût : on le limite à la lettre principale uniquement.
 * Les bandes utilisent translateX + opacity (zéro reflow).
 *
 * will-change est posé uniquement sous .js-title-reveal-ready pour ne
 * pas gaspiller des ressources GPU sur des éléments non animés.
 *
 * overflow:hidden sur .tr-char empêche les bandes translateX de créer
 * un scroll horizontal.
 *
 *
 * PREFERS-REDUCED-MOTION
 * ----------------------
 * Si l'OS demande une réduction des animations, le JS sort immédiatement
 * sans modifier le DOM. Le bloc @media ci-dessous force la visibilité
 * totale comme filet de sécurité (cas où la classe aurait quand même
 * été posée, par ex. si le media query change après l'init).
 *
 *
 * TOKEN CSS UTILISÉ
 * -----------------
 * Les bandes utilisent var(--accent) = #FFCB10 (vert acide, thème Street).
 * Ce token est déclaré dans design-tokens.css et disponible partout.
 */


/* ══════════════════════════════════════════════════════════════════════════
   KEYFRAMES DE L'ANIMATION
   Déclarées en dehors de tout sélecteur => disponibles globalement.
   Nommage préfixé tr- (title-reveal) pour éviter toute collision.
══════════════════════════════════════════════════════════════════════════ */

/*
 * tr-char-main : animation de la lettre principale
 *   - opacity  : 0 -> 1 (fondu d'entrée)
 *   - blur     : 10px -> 0px (la lettre devient nette progressivement)
 *   Durée : 0.8s, déclenchée par le stagger --i sur chaque .tr-char.
 *
 * Note : on évite de cacher complètement au-delà de 0 car certains
 * navigateurs peuvent avoir un "flicker" sur les animations de blur.
 */
@keyframes tr-char-main {
    /* Plancher d'opacité remonté à 0.5 : les lettres restent bien visibles
       pendant toute l'apparition (fondu plus léger, demande Gaëlle option B). */
    0%   { opacity: 0.5; filter: blur(10px); }
    100% { opacity: 1;   filter: blur(0px); }
}

/*
 * tr-slice-top : bande haute (clip-path = 0% à 35% de hauteur)
 *   translateX -100% -> 100% : entre depuis la gauche, part vers la droite.
 *   opacity 0 -> 1 -> 0 : flash d'apparition au passage.
 *   Durée : 0.7s.
 */
@keyframes tr-slice-top {
    0%   { transform: translateX(-100%); opacity: 0; }
    30%  { opacity: 1; }
    70%  { opacity: 1; }
    100% { transform: translateX(100%);  opacity: 0; }
}

/*
 * tr-slice-mid : bande milieu (clip-path = 35% à 65% de hauteur)
 *   Sens OPPOSÉ à la bande haute : 100% -> -100% (entre depuis la droite).
 *   Même courbe opacity.
 *   Ce sens alterné crée l'effet "shutter" (volets qui se ferment/ouvrent
 *   dans des directions alternées, inspiré des shutters d'impression).
 */
@keyframes tr-slice-mid {
    0%   { transform: translateX(100%);  opacity: 0; }
    30%  { opacity: 1; }
    70%  { opacity: 1; }
    100% { transform: translateX(-100%); opacity: 0; }
}

/*
 * tr-slice-bot : bande basse (clip-path = 65% à 100% de hauteur)
 *   Même sens que la bande haute : -100% -> 100%.
 *   Alternance haute/milieu/basse = effet dynamique à trois couches.
 */
@keyframes tr-slice-bot {
    0%   { transform: translateX(-100%); opacity: 0; }
    30%  { opacity: 1; }
    70%  { opacity: 1; }
    100% { transform: translateX(100%);  opacity: 0; }
}


/* ══════════════════════════════════════════════════════════════════════════
   STRUCTURE : .tr-word, .tr-char et leurs enfants
   ─────────────────────────────────────────────────────────────────────────
   Ces règles structurelles ne cachent rien par défaut.
   Le CSS d'armement (qui cache les lettres avant l'animation) est
   placé sous .js-title-reveal-ready pour garantir le progressive enhancement.
══════════════════════════════════════════════════════════════════════════ */

/*
 * Conteneur d'un MOT entier.
 *
 * display:inline : le mot s'intègre dans le flux texte du titre, EXACTEMENT
 * comme le texte brut d'origine. L'alignement (text-align), le line-height,
 * et la position dans la page restent identiques à la version non animée.
 *
 * white-space:nowrap : le navigateur ne coupe JAMAIS le mot en deux.
 * La coupure de ligne ne peut se faire qu'entre les mots (avant ou après
 * le nœud texte ' ' qui sépare deux .tr-word consécutifs), exactement
 * comme pour du texte non animé.
 *
 * Pas de display:inline-block ici : inline-block changerait le comportement
 * de text-align et introduirait des décalages de baseline.
 */
.tr-word {
    display:     inline;
    white-space: nowrap;
}

/*
 * Conteneur d'une lettre unique.
 * display:inline-block : nécessaire pour que overflow:hidden fonctionne
 * sur un élément inline (les slices en position:absolute doivent être
 * contenues dans la boîte de la lettre).
 * overflow:hidden : contient les bandes translateX dans les limites
 * de la boîte de la lettre => PAS de scroll horizontal.
 * position:relative : référence pour les bandes en position:absolute.
 * vertical-align:bottom : aligne les inline-blocks sur leur bord bas
 * (évite le décalage de baseline de 1-2px entre les lettres du même mot).
 *
 * line-height:inherit : sans ça, passer de inline à inline-block
 * réinitialise le line-height au "normal" du navigateur (~1.2).
 * Les titres utilisent des line-heights serrés (.88, .92…) — inherit
 * garantit que chaque .tr-char adopte le line-height de son parent h1/h2
 * via le .tr-word, qui lui-même hérite du titre.
 *
 * Note sur l'interligne :
 * Avec display:inline-block, le navigateur aligne les lettres sur leur
 * "baseline" (ou "bottom" grâce à vertical-align:bottom). La hauteur de
 * ligne effective de chaque ligne du titre est celle du .tr-word le plus
 * grand de cette ligne (inline) * le line-height hérité. Cela reproduit
 * fidèlement le comportement du texte non animé.
 */
.tr-char {
    display:        inline-block;
    /* On clippe UNIQUEMENT l'axe horizontal (overflow-x:clip) pour contenir le
       balayage translateX des bandes, et on laisse l'axe vertical VISIBLE.
       Pourquoi : les titres au line-height très serré (.88 pour le H1 hero)
       produisent une boîte de lettre plus courte que le glyphe en capitales
       (et que les accents : « É »). Avec l'ancien `overflow:hidden`, le haut
       et le bas des lettres étaient ROGNÉS (titre tronqué). overflow-x:clip
       conserve la protection anti-scroll horizontal des bandes, sans rien
       couper verticalement. NB : `clip` + `visible` est la seule combinaison
       d'axes valide (clip ne force pas l'autre axe à devenir un scroll). */
    overflow-x:     clip;
    overflow-y:     visible;
    vertical-align: bottom;
    position:       relative;
    line-height:    inherit;
}

/*
 * Lettre principale : hérite de la couleur de son parent (couleur naturelle
 * du titre — blanc sur fond sombre pour le hero, --ink ailleurs).
 * display:block : s'assure que la lettre occupe toute la largeur du .tr-char
 * (important pour les bandes en position:absolute qui utilisent width:100%).
 */
.tr-char__main {
    display: block;
}

/*
 * Bandes de balayage (slice) communes.
 * position:absolute : se superposent à la lettre principale.
 * inset:0 : couvrent exactement la boîte du .tr-char (haut/bas/gauche/droite).
 * clip-path : limite l'affichage à une tranche de la hauteur de la lettre.
 * pointer-events:none : les bandes ne captent pas les clics.
 *
 * Couleur : var(--accent) = #FFCB10 (vert acide Bazaart, thème Street).
 * La bande est colorée pour créer l'effet de "peinture qui passe".
 * Elle ne CONTIENT PAS le texte de la lettre (seul .tr-char__main le fait) —
 * les slices sont de purs éléments décoratifs colorés.
 */
.tr-char__slice {
    position:       absolute;
    inset:          0;
    background:     var(--accent, #FFCB10); /* fallback si token non chargé */
    pointer-events: none;
    /* par défaut invisible : l'animation est déclenchée par .is-revealing */
    opacity:        0;
}

/*
 * Bande haute : zone 0% → 35% de la hauteur.
 * clip-path polygon : (x1 y1, x2 y2, x3 y3, x4 y4) dans le sens horaire.
 *   (0 0) : coin haut-gauche
 *   (100% 0) : coin haut-droite
 *   (100% 35%) : bas-droite à 35%
 *   (0 35%) : bas-gauche à 35%
 */
.tr-char__slice--top {
    clip-path: polygon(0 0, 100% 0, 100% 35%, 0 35%);
}

/*
 * Bande milieu : zone 35% → 65% de la hauteur.
 */
.tr-char__slice--mid {
    clip-path: polygon(0 35%, 100% 35%, 100% 65%, 0 65%);
}

/*
 * Bande basse : zone 65% → 100% de la hauteur.
 */
.tr-char__slice--bot {
    clip-path: polygon(0 65%, 100% 65%, 100% 100%, 0 100%);
}

/*
 * NOTE : la classe .tr-space (ancien système) est supprimée.
 * Les espaces entre mots sont maintenant de VRAIS nœuds texte ' '
 * insérés directement dans le DOM par le JS — pas de span intermédiaire.
 * Avantage :
 *   - Largeur naturelle de la fonte (pas de valeur arbitraire 0.3em).
 *   - Pas de pollution du DOM avec des spans inutiles.
 *   - Comportement 100% identique au texte non animé.
 */


/* ══════════════════════════════════════════════════════════════════════════
   ARMEMENT : actif UNIQUEMENT quand .js-title-reveal-ready est sur <html>
   ─────────────────────────────────────────────────────────────────────────
   Ce bloc cache la lettre principale AVANT l'animation (opacity:0).
   Sans .js-title-reveal-ready (pas de JS ou erreur), les titres sont
   visibles normalement (aucune règle de cache active).
══════════════════════════════════════════════════════════════════════════ */

/*
 * Lettre principale masquée par défaut une fois le JS armé.
 * L'animation tr-char-main la fera apparaître avec fondu+blur.
 *
 * opacity:0 SANS animation : état stable invisible, en attente de
 * .is-revealing sur l'élément parent titre.
 */
.js-title-reveal-ready .tr-char__main {
    /* plancher 0.5 (au lieu de 0) : lettres visibles pendant l'apparition (option B). */
    opacity:    0.5;
    filter:     blur(10px);
    will-change: opacity, filter;
}

/*
 * Quand le titre reçoit .is-revealing (titre entrant dans le viewport),
 * on lance les animations CSS sur les enfants .tr-char.
 *
 * animation-delay : calculé par le JS en inline style sur chaque .tr-char :
 *   style="--i: 3" signifie que c'est la 4e lettre (index 3).
 *   animation-delay = calc(var(--i, 0) * 0.05s + 0.05s)
 *     => lettre 0 : delay 0.05s
 *     => lettre 10 : delay 0.45s
 *   Le +0.05s de base laisse un "silence" avant que la première lettre parte.
 *
 * animation-fill-mode:forwards : conserve l'état final (opacity:1, blur:0)
 * après la fin de l'animation. Sans ça, la lettre reviendrait à opacity:0.
 */
.js-title-reveal-ready .is-revealing .tr-char__main {
    animation:
        tr-char-main 1s cubic-bezier(0.22, 0.61, 0.36, 1)
        calc(var(--i, 0) * 0.05s + 0.05s)
        forwards;
}

/*
 * Bandes : lancées avec la même durée et le même stagger que la lettre principale.
 * Durée 0.7s (légèrement moins que 0.8s de la lettre) pour que les bandes
 * finissent de balayer AVANT que la lettre soit complètement nette.
 */
.js-title-reveal-ready .is-revealing .tr-char__slice--top {
    animation:
        tr-slice-top 0.9s cubic-bezier(0.65, 0, 0.35, 1)
        calc(var(--i, 0) * 0.05s + 0.05s)
        forwards;
}
.js-title-reveal-ready .is-revealing .tr-char__slice--mid {
    animation:
        tr-slice-mid 0.9s cubic-bezier(0.65, 0, 0.35, 1)
        calc(var(--i, 0) * 0.05s + 0.05s)
        forwards;
}
.js-title-reveal-ready .is-revealing .tr-char__slice--bot {
    animation:
        tr-slice-bot 0.9s cubic-bezier(0.65, 0, 0.35, 1)
        calc(var(--i, 0) * 0.05s + 0.05s)
        forwards;
}

/*
 * Quand l'animation est terminée et que la classe .is-done est posée sur le titre,
 * on retire will-change pour libérer les ressources GPU.
 * Le JS ajoute .is-done à la fin du dernier délai + durée d'animation.
 */
.js-title-reveal-ready .is-done .tr-char__main {
    will-change: auto;
}


/* ══════════════════════════════════════════════════════════════════════════
   PREFERS-REDUCED-MOTION : filet de sécurité
   ─────────────────────────────────────────────────────────────────────────
   Le JS détecte ce media query et sort IMMÉDIATEMENT sans modifier le DOM.
   Ces règles CSS sont donc un garde-fou pour le cas (théorique) où le JS
   aurait quand même posé .js-title-reveal-ready.
   !important nécessaire pour surcharger les éventuelles valeurs inline.
══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
    /*
     * Force la visibilité totale des lettres et supprime toute animation.
     * Seuls les sélecteurs actifs du système actuel sont listés :
     *   - .tr-char__main : lettre principale (fondu + blur)
     *   - .tr-char__slice : bandes de balayage (translateX + opacity)
     * La classe .tr-space est supprimée (ancien système).
     */
    .tr-char__main,
    .tr-char__slice {
        opacity:    1     !important;
        filter:     none  !important;
        transform:  none  !important;
        animation:  none  !important;
        will-change: auto !important;
    }
}
