/* ========================================
   WOW LAYER — couche d'améliorations v2
   Se superpose à styles.css sans le modifier
   ======================================== */

:root {
    /* Palette v2 : noir / blanc (pas anthracite) */
    --color-bg: #000000;
    --color-bg-elevated: #050505;
    --color-bg-card: #000000;
    --color-surface: #0a0a0a;
    --color-border: rgba(255, 255, 255, 0.12);
    --color-border-hover: rgba(255, 255, 255, 0.28);
    --color-text: #ffffff;
    --color-text-secondary: #ffffff;
    --color-text-muted: #c8c8c8;
    --color-accent: #ffffff;
    --color-accent-light: #ffffff;
    --color-accent-dark: #ffffff;

    --wow-accent: #ffffff;
    --wow-accent-dim: rgba(255, 255, 255, 0.12);
    --wow-ease: cubic-bezier(0.16, 1, 0.3, 1);
    --wow-panel: #000000;
    --wow-shadow: 0 40px 100px -34px rgba(0, 0, 0, 0.98);
}

body {
    background-color: #000000;
    color: #ffffff;
}

/* Texte blanc pur + léger éclat sur titres / corps principal */
.hero-title,
.hero-title-line,
.hero-title-accent,
.hero-subtitle,
.hero-description,
.hero-typewriter,
.section-title,
.contact-title,
.exp-title,
.formation-title,
.destination-name,
.nav-link,
.logo {
    color: #ffffff !important;
}

.hero-title,
.hero-title-accent {
    text-shadow:
        0 0 1px rgba(255, 255, 255, 0.85),
        0 0 18px rgba(255, 255, 255, 0.22),
        0 0 40px rgba(255, 255, 255, 0.1);
}

.hero-subtitle,
.section-title,
.contact-title {
    text-shadow: 0 0 12px rgba(255, 255, 255, 0.12);
}

.hero-description {
    opacity: 0.92;
}

:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: 3px;
    border-radius: 4px;
}

/* ========================================
   1 · HERO — titre cinétique + aurore + grain
   ======================================== */
.hero-title-line {
    perspective: 700px;
}

.wow-char {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.5em) rotateX(-55deg);
    /* Pas de filter:blur — animé, ça bug / lag (comme le dust) */
    will-change: transform, opacity;
}

.hero-title.active .wow-char {
    animation: wowCharIn 0.85s var(--wow-ease) forwards;
    animation-delay: calc(var(--i, 0) * 26ms + 100ms);
}

@keyframes wowCharIn {
    to {
        opacity: 1;
        transform: none;
    }
}

.hero-title.wow-chars-done .wow-char {
    opacity: 1;
    transform: none;
}

/* Onde de lumière qui traverse le nom, lettre par lettre */
.hero-title.active .hero-title-accent .wow-char {
    animation:
        wowCharIn 0.85s var(--wow-ease) forwards calc(var(--i, 0) * 26ms + 100ms),
        wowGlowWave 7s ease-in-out infinite calc(var(--i, 0) * 55ms + 2s);
}

@keyframes wowGlowWave {
    0%, 16%, 100% { text-shadow: none; color: inherit; }
    6% { text-shadow: 0 0 18px rgba(255, 255, 255, 0.35), 0 0 4px rgba(255, 255, 255, 0.25); }
}

/* Héros : aurore supra-légère (à peine perceptible) */
.hero {
    background:
        radial-gradient(ellipse 75% 60% at 22% 30%, rgba(255, 255, 255, 0.045), transparent 58%),
        radial-gradient(ellipse 55% 45% at 80% 18%, rgba(255, 255, 255, 0.03), transparent 55%),
        #000000 !important;
}

.hero-pattern {
    display: none !important;
}

.hero-bg {
    z-index: 0 !important;
}

.hero-gradient {
    background: transparent !important;
    z-index: 0;
}

.hero-container {
    position: relative;
    z-index: 2;
}

.wow-aurora {
    position: absolute;
    top: -10%;
    left: -10%;
    width: 120% !important;
    height: 120% !important;
    opacity: 0.18;
    filter: blur(32px) saturate(0%);
    pointer-events: none;
    z-index: 1;
}

.hero-bg::before {
    display: none;
}

/* Portrait cutout N&B sur fond noir (visage = pixels originaux) */
.hero-flip-inner:has(.hero-image-cutout),
.hero-flip-front:has(.hero-image-cutout) {
    background: #000 !important;
}

.hero-flip-front:has(.hero-image-cutout)::after {
    display: none;
}

.hero-image-cutout {
    object-fit: cover;
    object-position: 50% 18%;
    filter: none;
}

/* Portrait 3D : le canvas remplace visuellement l'image (conservée pour SEO / repli).
   Fond transparent pour ne pas découper un rectangle noir dans l'aurore du héros. */
.hero-flip-front.has-portrait3d,
.hero-flip-inner:has(.has-portrait3d) {
    background: transparent !important;
}

/* Buste halftone : fond transparent (aurore / page derrière la découpe) */
.hero-flip-front.has-herobust,
.hero-flip-inner:has(.has-herobust) {
    background: transparent !important;
}
.hero-flip-front.has-herobust .hero-bust-canvas {
    background: transparent;
    /* Buffer Retina (HERO_SS×2–3) — lissage navigateur, jamais crisp-edges */
    image-rendering: auto;
    -ms-interpolation-mode: bicubic;
    width: 100% !important;
    height: 100% !important;
    display: block;
}

/* Pas de tilt CSS sur le shell tant que le buste WebGL est actif */
.hero-tilt-shell.hero-tilt-disabled,
.hero-tilt-shell:has(.has-herobust) {
    transform: none !important;
    transition: none !important;
    will-change: auto !important;
}

/**
 * Le voile de gradation de la photo — `.hero-flip-front::after`, un
 * `rgba(15,15,15,.14)` posé sur toute la carte — s'appliquait aussi au nuage de
 * points, qui passe derrière la carte une fois posé. Mesuré : le portrait
 * perdait 15 % de sa lumière et la totalité de ses blancs (plafonnés à 219 au
 * lieu de 255) à l'instant où le canvas rejoignait le héros, alors qu'il volait
 * jusque-là au-dessus de la page, non voilé. C'était le « changement de visage »
 * à la fin de l'animation. La photo étant masquée sous le nuage, ce voile n'a
 * plus d'objet.
 */
.hero-flip-front.has-portrait3d::after {
    display: none;
}

/* Tant que le monogramme n'est pas consumé, la page reste éteinte — mais c'est
   le splash lui-même qui la couvre, opaque et plein écran. Le fond noir
   supplémentaire qui était posé ici n'avait plus d'objet : il servait à masquer
   la page pendant tout le vol, alors qu'elle se révèle désormais dès le premier
   tiers, dans le sillage de la poussière. */
body.wow-assembling {
    overflow: hidden !important;
}

/* `overflow` posé sur <body> ne bloque rien : le conteneur de défilement est
   <html>. À la molette, la page défilait donc derrière le calque pendant
   l'assemblage, et le portrait atterrissait dans un héros déjà remonté. La
   gouttière est réservée en permanence pour que l'apparition puis la
   disparition de la barre ne décalent pas la mise en page de quinze pixels. */
html {
    scrollbar-gutter: stable;
}

html:has(body.wow-assembling) {
    overflow: hidden;
}

/**
 * LE VOILE D'ASSEMBLAGE S'EFFACE DEVANT LA CHAÎNE.
 *
 * Ces règles cachent le héros pendant que le portrait s'assemble, et c'était juste tant que
 * la page ne s'allumait qu'**après**. Maintenant le sable remplit le héros étape par étape
 * pendant l'assemblage : le voile masquait donc tout ce que la chaîne venait de remplir, et
 * on ne voyait le héros qu'au moment où il tombait, d'un bloc. C'est ce qui donnait « on n'a
 * même pas vu le sable arriver ».
 *
 * `wow-chaine`, posé par `portrait3d.js` quand la chaîne démarre, lève ce voile. Ce n'est pas
 * pour autant tout montrer : chaque élément reste tenu par `wow-sable-cache` jusqu'à ce que
 * son propre écoulement le découvre.
 */
body.wow-assembling:not(.wow-chaine) .nav,
body.wow-assembling:not(.wow-chaine) .hero-text,
body.wow-assembling:not(.wow-chaine) .logo-marquee,
body.wow-assembling:not(.wow-chaine) .hero-visual,
/* Pas de <main> dans le HTML : sections = enfants directs de body */
body.wow-assembling > section:not(#hero),
body.wow-assembling .footer,
body.wow-assembling .wow-top {
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
}

/*
 * Parcours + suite cachés jusqu’au reveal globe.
 * IMPORTANT : pas de sélecteur `main >` — le markup n’a pas de <main>,
 * donc l’ancien sélecteur ne matchait jamais → « 01 Mon Parcours » fuyait
 * dès la fin du loader (pendant densify / morph).
 */
body.bust-intro:not(.globe-reveal) > section:not(#hero),
body.bust-intro:not(.globe-reveal) .footer {
    display: none !important;
}

body.wow-assembling:not(.wow-chaine) .hero .reveal {
    opacity: 0 !important;
    transform: none !important;
}

/* Sur <body> pendant l'assemblage — au-dessus du loader (.loader = 10050).
   Les dimensions viennent de portrait3d.js : elles doivent rester exactement
   celles du héros, avant comme après l'atterrissage, sinon le portrait change
   d'aspect au changement de taille du tampon. */
body.wow-assembling .wow-portrait3d:not(.hero-bust-canvas),
.wow-portrait3d.wow-overlay:not(.hero-bust-canvas) {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    z-index: 10060 !important;
}

/* Hero buste — dans la carte hors morph / hors vol / hors handoff fixed */
body.wow-assembling .hero-bust-canvas:not(.hero-bust-morph):not(.hero-bust-flyin):not(.hero-bust-handoff) {
    position: absolute !important;
    inset: 0 !important;
    top: auto !important;
    left: auto !important;
    z-index: 2 !important;
    opacity: 1 !important;
    visibility: visible !important;
}

/* Vol de grains splash → héros (doit passer AU-DESSUS du loader noir).
   100% (pas 100vw/vh) : sur mobile vw/vh ≠ visual viewport → décalage des grains. */
.hero-float-points {
    position: fixed !important;
    left: 0 !important;
    top: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    width: 100% !important;
    height: 100% !important;
    z-index: 10070 !important;
    pointer-events: none !important;
    background: #000 !important;
}

/* Pendant le snapshot + vol : canvas héros INVISIBLE
   (sinon opacity:1 !important de .wow-assembling le flashait en bas à droite) */
.hero-bust-canvas.hero-bust-flyin {
    position: fixed !important;
    z-index: 10065 !important;
    pointer-events: none !important;
    background: transparent !important;
    opacity: 0 !important;
    visibility: hidden !important;
}

/* Handoff mobile : canvas figé en fixed aux coords du vol (avant revealPage) */
.hero-bust-canvas.hero-bust-handoff {
    position: fixed !important;
    inset: auto !important;
    z-index: 10068 !important;
    pointer-events: none !important;
    background: transparent !important;
    opacity: 1 !important;
    visibility: visible !important;
}

body.wow-morphing {
    background: #000 !important;
}
/* Pendant morph : tout reste noir pur (pas d’aurore héros qui flash derrière l’overlay) */
body.wow-morphing .hero,
body.wow-morphing .hero-gradient,
body.wow-morphing .wow-aurora {
    background: #000 !important;
    opacity: 0 !important;
}
/* Loader noir sous l’overlay (opaque) — plus de transparent qui laissait voir la page */
body.wow-morphing .loader,
body.wow-morphing .loader.loader-has-bust {
    background: #000 !important;
    z-index: 10040 !important;
}

/* Dissolution : fondu seul — plus de letter-spacing / scale (ça déplaçait EB sur mobile) */
.loader.loader-scattering .loader-text,
.loader.loader-scattering .loader-bar {
    transition: opacity 0.25s ease;
}

/* Carte héros cachée jusqu’à wow-chaine — évite un 2e buste en bas à droite
   pendant que le splash/morph tourne encore au centre */
body.wow-assembling:not(.wow-chaine) .hero-visual:has(.has-herobust) {
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
}

/* Curseur custom : masqué jusqu'à la fin réelle de l'intro */
body.wow-assembling .cursor,
body.wow-assembling .cursor-follower,
body.wow-chaine .cursor,
body.wow-chaine .cursor-follower {
    display: none !important;
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
}

/* Le monogramme n'est jamais remplacé par une copie en points : il est consumé
   par un masque linéaire piloté depuis portrait3d.js, dans le sens du vol. Le
   fond noir du loader reste, lui, jusqu'à la fin de la consomption. */
body.wow-assembling .loader {
    background: #000 !important;
    transition: none !important;
}

body.wow-assembling .loader-bar::after {
    /* Pas de double barre (::after + fill) pendant la consomption */
    content: none !important;
    display: none !important;
    animation: none !important;
}

/**
 * Chaque élément de la page se matérialise en poussière.
 *
 * Un fondu ordinaire ne suffisait pas : la nappe de points couvrait l'écran, mais
 * le texte, la nav et les boutons apparaissaient en douceur par-dessus, comme
 * s'ils n'avaient rien à voir avec elle. Ici chacun est masqué par une trame de
 * pastilles qui grossissent jusqu'à se rejoindre : l'élément se remplit de grain
 * avant d'être plein, exactement comme si la poussière venait s'y déposer.
 *
 * Deux trames superposées de pas différents, pour que la maille ne se lise pas
 * comme une grille. Le rayon est une propriété enregistrée, donc animable.
 */
@property --wow-dust {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

/**
 * LE SABLE — nappe de particules par-dessus la page.
 *
 * Elle couvre la fenêtre et ne reçoit aucun clic. Au-dessus du contenu, en dessous de
 * l'écran de chargement : le sable doit passer devant le texte qu'il forme, mais pas
 * devant le monogramme, qui a sa propre disparition.
 */
.wow-sable {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    display: block;
    pointer-events: none;
    z-index: 9000;
}

/**
 * Un élément confié au sable est invisible mais **toujours mesurable**.
 *
 * L'opacité seule, jamais `visibility` ni `display` : la forme de l'élément est
 * rastérisée à partir de la boîte de chacune de ses lettres, et un élément qu'on retire
 * de la mise en page n'a plus de boîte. Le sable ne saurait alors plus quoi former.
 */
.wow-sable-cache {
    opacity: 0 !important;
    pointer-events: none;
}

/**
 * LE SABLE GOUVERNE SEUL LA RÉVÉLATION.
 *
 * `script.js` a sa propre mécanique d'apparition : `.reveal` passe de zéro à un par une
 * transition, avec des retards échelonnés (`reveal-delay-1`, `-2`, ...). Elle tournait en
 * parallèle de la nôtre et durait bien plus longtemps : le front de sable traversait
 * l'élément en huit dixièmes de seconde, puis il fallait encore attendre la transition
 * maison pour le voir apparaître.
 *
 * D'où les deux reproches d'Etienne, qui n'en faisaient qu'un : « on n'a même pas vu le
 * sable arriver » et « ça met des plombes avant de s'afficher ». Là où le sable est en
 * charge, la transition du site est donc coupée net : l'élément est visible dès que le
 * masque du front le découvre, et rien d'autre ne décide plus du moment.
 */
/**
 * La marque n'existe plus que dans le héros, le bandeau et la barre de logos : cette règle est
 * donc bornée à eux par construction. Ailleurs, les transitions d'origine sont intactes.
 */
[data-sable] {
    transition: none !important;
    transition-delay: 0s !important;
}

/* Intro héros : plus de cascade reveal-delay-1…4 */
body.wow-chaine .hero .reveal,
body.wow-assembling .hero .reveal,
body.wow-morphing .hero .reveal {
    transition-delay: 0s !important;
    transition-duration: 0.35s !important;
}

/**
 * LE FRONT DE VAGUE, DU CÔTÉ DE LA PAGE.
 *
 * Les grains ne suffisent pas : il faut aussi voir la **matière se défaire**. Un masque
 * oblique balaie donc l'élément, dans le même sens et à la même vitesse que le front qui
 * arrache les grains — 112 degrés, du bas-gauche vers le haut-droite, comme la vague du
 * portrait. L'élément est consumé là où les grains viennent de partir, et révélé là où ils
 * viennent de se poser.
 *
 * La durée n'est pas écrite ici mais passée par `sable.js` dans `--wow-onde-duree` : deux
 * durées écrites à deux endroits finissent toujours par diverger, et un front de sable
 * décalé du front du masque se verrait immédiatement.
 */
@property --wow-onde {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

/**
 * Il y avait ici un second masque, `.wow-sable-sort`, qui consumait un élément en train de se
 * désintégrer. Plus rien ne se désintègre : le sable ne sert qu'à remplir le héros à l'ouverture.
 */
.wow-sable-entre {
    opacity: 1 !important;
    will-change: mask-image;
}

/**
 * Entrée : opaque derrière le front. La matière se reforme au passage de la vague.
 *
 * Le **retard** est ce qui rend la déferlante unique. Chaque élément reçoit de `sable.js` l'instant
 * où le front l'atteint et le temps qu'il met à le traverser : les masques de tous les éléments
 * composent alors une seule ligne de révélation qui balaie le héros. Sans lui, ils démarraient
 * tous ensemble et chacun clignotait pour son compte — « le sable vient de partout et ne déferle
 * pas comme un seul bloc ».
 *
 * `backwards` est indispensable : sans elle, un élément que le front n'a pas encore atteint
 * s'afficherait entier pendant tout son retard, avant de se faire masquer d'un coup.
 */
.wow-sable-entre {
    -webkit-mask-image: linear-gradient(112deg, #000 var(--wow-onde),
        transparent calc(var(--wow-onde) + 14%));
    mask-image: linear-gradient(112deg, #000 var(--wow-onde),
        transparent calc(var(--wow-onde) + 14%));
    animation: wowOnde var(--wow-onde-duree, 0.62s) var(--wow-onde-retard, 0s) linear both;
}

@keyframes wowOnde {
    /* Le front déborde des deux côtés : à zéro l'élément est intact, à un il est
       entièrement traité, bord compris. */
    from { --wow-onde: -16%; }
    to { --wow-onde: 106%; }
}

@media (prefers-reduced-motion: reduce) {
    .wow-sable { display: none; }
    .wow-sable-cache,
    .wow-sable-entre {
        opacity: 1 !important;
        -webkit-mask-image: none;
        mask-image: none;
        animation: none;
    }
}

/**
 * Trois états, et un seul est animé.
 *
 * `wow-dust-in` remplit l'élément de grain, `wow-dust-out` l'y dissout, et
 * `wow-dust-hidden` est l'état de repos de ce qui est hors de l'écran : ni masque,
 * ni animation, donc rien à composer. C'est cette distinction qui permet de
 * marquer tout le bas du document au démarrage sans lancer cent cinquante
 * animations au moment précis où le portrait s'assemble.
 */
.wow-dust-hidden {
    opacity: 0 !important;
    pointer-events: none;
}

.wow-dust-in,
.wow-dust-out {
    --wow-dust: 0%;
    /**
     * Deux trames de pas premiers entre eux, et un liseré large.
     *
     * Sur un aplat — un bouton blanc, par exemple — une seule trame de cinq pixels
     * se lit comme un quadrillage ; deux trames décalées donnent du grain. Il y en
     * avait trois, dont une de deux pixels : chaque couche est un masque à carreler
     * sur toute la surface de l'élément, et la plus fine est la plus chère. Retirée,
     * le grain est le même à l'œil pour un tiers de travail en moins.
     */
    -webkit-mask-image:
        radial-gradient(circle, #000 var(--wow-dust), transparent calc(var(--wow-dust) + 22%)),
        radial-gradient(circle, #000 var(--wow-dust), transparent calc(var(--wow-dust) + 22%));
    mask-image:
        radial-gradient(circle, #000 var(--wow-dust), transparent calc(var(--wow-dust) + 22%)),
        radial-gradient(circle, #000 var(--wow-dust), transparent calc(var(--wow-dust) + 22%));
    -webkit-mask-size: 3px 3px, 5px 5px;
    mask-size: 3px 3px, 5px 5px;
    -webkit-mask-position: 0 0, 2px 3px;
    mask-position: 0 0, 2px 3px;
    -webkit-mask-composite: source-over;
    mask-composite: add;
}

.wow-dust-in {
    animation: wowDustIn 0.66s cubic-bezier(0.33, 1, 0.68, 1) both;
}

/* Plus court que l'arrivée, et d'une courbe inverse : ce qui quitte l'écran n'a pas
   à retenir le regard, il doit s'effacer devant ce qui arrive. */
.wow-dust-out {
    animation: wowDustOut 0.42s cubic-bezier(0.32, 0, 0.67, 0) forwards;
}

/**
 * Aucun `filter` dans ces images-clés, et c'est mesuré.
 *
 * Il y avait un `blur(2px)` aux deux extrémités. Un flou animé ne peut pas être
 * composé : le navigateur alloue une surface de rendu par élément et y applique un
 * flou gaussien en deux passes à chaque image, en plus du masque. Mesuré au
 * défilement, cent soixante-quatre cibles : le temps par image doublait.
 *
 * Le grain suffit largement à porter l'effet — c'est lui qu'on regarde, pas le flou.
 */
@keyframes wowDustIn {
    from {
        --wow-dust: 0%;
        opacity: 0;
    }
    to {
        --wow-dust: 95%;
        opacity: 1;
    }
}

@keyframes wowDustOut {
    from {
        --wow-dust: 95%;
        opacity: 1;
    }
    to {
        --wow-dust: 0%;
        opacity: 0;
    }
}

body:not(.wow-assembling) .nav,
body:not(.wow-assembling) .hero-badge-un,
body:not(.wow-assembling) .hero-title,
body:not(.wow-assembling) .hero-subtitle,
body:not(.wow-assembling) .hero-description,
body:not(.wow-assembling) .hero-cta,
body:not(.wow-assembling) .hero-visual,
body:not(.wow-assembling) .logo-marquee {
    transition: opacity 0.5s ease, visibility 0.5s ease, transform 0.5s ease;
}

body:not(.wow-assembling) .hero .reveal {
    transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
                transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.wow-portrait3d {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
    z-index: 1;
    transition: opacity 0.35s ease 0.3s;
}

/**
 * La stèle : le visage en relief devant, les coordonnées au dos, sur un noir
 * plat. Le nuage n'est plus effacé par une règle CSS au retournement — c'est
 * portrait3d.js qui le fait disparaître en fonction de l'angle réel, pour qu'il
 * s'éteigne exactement quand on passe le profil.
 */
.hero-flip-back {
    background: #000 !important;
    border: none !important;
    box-shadow: none !important;
}

.hero-image-wrapper:not(.hero-flip-disabled) {
    cursor: grab;
    /* La photo ne doit pas être saisissable comme une image : le glisser-déposer
       natif prendrait la main sur la rotation. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-user-drag: none;
}

.hero-image-wrapper:not(.hero-flip-disabled) img {
    -webkit-user-drag: none;
    pointer-events: none;
}

.hero-image-wrapper.wow-stele-grab {
    cursor: grabbing;
}

/**
 * L'orientation du visage ne change qu'au vrai geste, jamais au survol.
 *
 * `initTilt()` de script.js écoute `mousemove` sur `[data-tilt]` et y écrit un
 * `rotateY` proportionnel à la position du curseur : le visage tournait donc au
 * simple passage de la souris, en plus de la stèle. C'était ça le coupable — le
 * nuage de points, lui, ne suivait plus le curseur depuis un moment.
 *
 * Neutralisé ici plutôt que dans script.js, qui est la prod : une déclaration
 * `!important` bat le style en ligne que le script écrit à chaque mouvement, et le
 * calque v2 reste seul responsable. La perspective du retournement ne vient pas
 * d'ici mais de `.hero-image-wrapper`, elle n'est donc pas touchée.
 */
.hero-tilt-shell[data-tilt] {
    transform: none !important;
    transition: none !important;
}

/* Pendant la prise en main, aucune transition ne doit contrarier l'angle. */
.hero-image-wrapper.wow-stele-grab .hero-flip-inner {
    transition: none !important;
}

/* Jamais de pré-buste photo : le JPG reste hors écran (SEO only) */
.hero-flip-front .hero-image,
.hero-flip-front.has-portrait3d .hero-image,
.hero-flip-front.has-herobust .hero-image {
    opacity: 0 !important;
    visibility: hidden !important;
    display: none !important;
    pointer-events: none !important;
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    overflow: hidden !important;
}

/* Carte héros vide/transparente tant que le canvas 3D n’est pas là */
.hero-flip-front.has-herobust:not(:has(.hero-bust-canvas)) {
    background: transparent !important;
    min-height: 100%;
}

/* Buste mesh + QR : interaction drag / clic (touch-action:none = rotation mobile) */
.hero-bust-canvas,
.hero-flip-front.has-herobust,
.hero-flip-front.has-herobust .wow-portrait3d,
.hero-image-wrapper.hero-flip-disabled:has(.has-herobust) {
    pointer-events: auto !important;
    touch-action: none !important;
    cursor: grab;
    z-index: 2;
}
.hero-bust-canvas:active,
.hero-flip-front.has-herobust:active,
.hero-flip-front.has-herobust .wow-portrait3d:active {
    cursor: grabbing;
}

.nav {
    background: rgba(0, 0, 0, 0.92) !important;
}

.logo-marquee {
    background: #000000;
}

/* ONU featured : overflow visible (ruban) → arrondir la galerie PJ elle-même */
.exp-card-featured .exp-gallery,
.exp-card-featured .exp-gallery-with-captions {
    border-radius: 12px;
    overflow: hidden;
}

.exp-card-featured .exp-gallery-figure:first-child,
.exp-card-featured .exp-gallery-figure:first-child img,
.exp-card-featured .exp-gallery-figure:first-child .exp-gallery-video {
    border-top-left-radius: 12px;
    border-bottom-left-radius: 12px;
}

.exp-card-featured .exp-gallery-figure:last-child,
.exp-card-featured .exp-gallery-figure:last-child img,
.exp-card-featured .exp-gallery-figure:last-child .exp-gallery-video {
    border-top-right-radius: 12px;
    border-bottom-right-radius: 12px;
}

/* ========================================
   4 · TIMELINE — ligne de progression + jalon actif
   ======================================== */
.wow-tl-fill {
    position: absolute;
    left: var(--wow-tl-left, 64px);
    top: 0;
    width: 1px;
    height: 0;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 14%, rgba(255, 255, 255, 0.9) 86%, rgba(255, 255, 255, 0) 100%);
    box-shadow: 0 0 14px rgba(255, 255, 255, 0.35);
    pointer-events: none;
    z-index: 1;
}

.timeline-item .timeline-marker::after {
    transition: background 0.35s ease, box-shadow 0.35s ease, transform 0.35s ease;
}

.timeline-item.wow-on .timeline-marker::after {
    background: #ffffff;
    transform: scale(1.25);
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.07), 0 0 16px rgba(255, 255, 255, 0.55);
}

.timeline-item.wow-on .timeline-date {
    color: #ffffff;
}

/* ========================================
   8 · BOUTON RETOUR HAUT (anneau de progression)
   ======================================== */
.wow-top {
    position: fixed;
    right: 18px;
    bottom: 18px;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: rgba(0, 0, 0, 0.92);
    border: 1px solid var(--color-border);
    color: var(--color-text);
    z-index: 950;
    opacity: 0;
    transform: translateY(10px) scale(0.9);
    pointer-events: none;
    transition: opacity 0.35s ease, transform 0.35s var(--wow-ease), border-color 0.25s ease;
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
}

.wow-top.is-on {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}

.wow-top:hover {
    border-color: var(--color-border-hover);
}

.wow-top-ring {
    position: absolute;
    inset: 0;
    transform: rotate(-90deg);
}

.wow-top-ring circle {
    fill: none;
    stroke: #ffffff;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-dasharray: 132;
    stroke-dashoffset: calc(132 - 132 * var(--p, 0));
    opacity: 0.85;
}

/* ========================================
   10 · DIVERS — nav active, marquee, progress
   ======================================== */
.logo-marquee:hover .logo-marquee-track {
    animation-play-state: paused;
}

.scroll-progress {
    background: #ffffff;
    box-shadow: 0 0 12px rgba(255, 255, 255, 0.35);
}

/* Barre de navigation escamotable au défilement */
.nav {
    transition: background var(--transition-base), padding var(--transition-base), transform 0.42s var(--wow-ease);
}

.nav.wow-nav-hidden {
    transform: translateY(-105%);
}

/* ========================================
   11 · ACCESSIBILITÉ / MOTION
   ======================================== */
@media (prefers-reduced-motion: reduce) {
    .wow-char {
        opacity: 1;
        transform: none;
        filter: none;
        animation: none !important;
    }

    .hero-title-accent { animation: none; }
    .wow-aurora,
    .hero-bg::before { display: none; }
    .nav { transition: background var(--transition-base), padding var(--transition-base); }
    .nav.wow-nav-hidden { transform: none; }
}
