/* global React, gsap */ function Hero({ onCta }) { const t = window.useT(); const lang = React.useContext(window.LangContext).lang; const videoRef = React.useRef(null); const headlineRef = React.useRef(null); const eyebrowRef = React.useRef(null); const subRef = React.useRef(null); const ctaRef = React.useRef(null); const [videoReady, setVideoReady] = React.useState(false); const posterSrc = 'images/hero/poster.webp'; React.useEffect(() => { const v = videoRef.current; if (!v) return; let cancelled = false; let revealTimer = null; const reveal = () => { if (cancelled || revealTimer) return; // Hold the poster a moment before crossfading to video revealTimer = setTimeout(() => { if (!cancelled) setVideoReady(true); }, 600); }; // Prefer frame-perfect handoff: wait for first decoded frame to be painted if ('requestVideoFrameCallback' in v) { v.requestVideoFrameCallback(() => reveal()); } // Fallbacks for older browsers / safety net const onPlaying = () => reveal(); const onCanPlayThrough = () => reveal(); v.addEventListener('playing', onPlaying, { once: true }); v.addEventListener('canplaythrough', onCanPlayThrough, { once: true }); // If already playable, schedule reveal on next frame to avoid sync stutter if (v.readyState >= 4) { requestAnimationFrame(() => reveal()); } return () => { cancelled = true; if (revealTimer) clearTimeout(revealTimer); v.removeEventListener('playing', onPlaying); v.removeEventListener('canplaythrough', onCanPlayThrough); }; }, []); // Cinematic intro: split-words reveal + slow zoom-out video React.useEffect(() => { if (!window.gsap) return; if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { if (eyebrowRef.current) eyebrowRef.current.style.opacity = 1; if (subRef.current) subRef.current.style.opacity = 1; if (ctaRef.current) ctaRef.current.style.opacity = 1; return; } const ctx = gsap.context(() => { // Slow zoom-out on video & poster gsap.fromTo([videoRef.current, '.hero-poster'], { scale: 1.35 }, { scale: 1, duration: 5, ease: 'none' } ); // Split headline into words wrapped in mask const h = headlineRef.current; if (h) { const walk = (node) => { const out = []; node.childNodes.forEach((c) => { if (c.nodeType === 3) { const words = c.textContent.split(/(\s+)/); words.forEach((w) => { if (w.trim() === '') { out.push(document.createTextNode(w)); } else { const mask = document.createElement('span'); mask.className = 'word-mask'; mask.style.cssText = 'display:inline-block;overflow:hidden;vertical-align:bottom;line-height:1;'; const inner = document.createElement('span'); inner.className = 'word-inner'; inner.style.cssText = 'display:inline-block;transform:translateY(110%);'; inner.textContent = w; mask.appendChild(inner); out.push(mask); } }); } else if (c.nodeType === 1) { const tag = c.tagName.toLowerCase(); if (tag === 'br') { out.push(c.cloneNode()); } else { // For wrap content recursively but keep wrapper styling const clone = c.cloneNode(false); walk(c).forEach((n) => clone.appendChild(n)); clone.dataset.italicAccent = (tag === 'em') ? '1' : ''; out.push(clone); } } }); return out; }; const replacement = walk(h); h.innerHTML = ''; replacement.forEach((n) => h.appendChild(n)); const inners = h.querySelectorAll('.word-inner'); const accentInners = h.querySelectorAll('[data-italic-accent="1"] .word-inner'); const tl = gsap.timeline({ defaults: { ease: 'expo.out' } }); // Eyebrow first tl.fromTo(eyebrowRef.current, { opacity: 0, y: 14 }, { opacity: 1, y: 0, duration: 0.8 }, 0.15); // Words rise tl.to(inners, { y: 0, duration: 1.1, stagger: 0.06 }, 0.25); // Accent overshoot if (accentInners.length) { tl.fromTo(accentInners, { y: 0 }, { y: -8, duration: 0.45, ease: 'back.out(2.6)', yoyo: true, repeat: 1 }, '+=0.05'); } // Sub tl.fromTo(subRef.current, {opacity: 0, y: 18}, {opacity: 1, y: 0, duration: 0.9}, '-=0.6'); // CTAs tl.fromTo(ctaRef.current, {opacity: 0, y: 16}, {opacity: 1, y: 0, duration: 0.55, ease: 'expo.out'}, '+=0.12'); } }); return () => ctx.revert(); }, []); return (
{/* Poster image — visible until video is ready */}
{/* Video background */} {/* Scrim */}
{/* Headline */}
{t('hero.eyebrow')}

{t('hero.title.main').split('\n')[0]}
{t('hero.title.main').split('\n')[1].replace(t('hero.title.emphasis'), '')}{t('hero.title.emphasis')}

{t('hero.title.mobile').replace(t('hero.title.emphasis'), '')}{t('hero.title.emphasis')}

{t('hero.subtitle')}

); } window.Hero = Hero;