diff --git a/protected/bifrost.js b/protected/bifrost.js index 75a36e5..c7ab935 100644 --- a/protected/bifrost.js +++ b/protected/bifrost.js @@ -71,6 +71,11 @@ el.style.opacity = '1'; el.style.transform = 'none'; }); + // The hero-wrap is hidden by CSS (`.js .hero-wrap { opacity: 0 }`) + // to prevent a flash during page activation — unhide it here since + // we're skipping the fade-in tween. + const heroWrap = document.querySelector('.hero-wrap'); + if (heroWrap) heroWrap.style.opacity = '1'; // Still fade the Europe map fully in — it's the scene background. const mapEl = document.getElementById('overview-globe'); if (mapEl) mapEl.style.opacity = '1'; @@ -360,50 +365,17 @@ // Script 1 body — HERO + SCENE 2 (architecture stack) + SCENE 3 (words) + SCENE 4 (bifrost arc) /* ------------------------------------------------------------- - HERO — staggered intro on load + HERO — single overall fade-in. The wrap is hidden via CSS + (`.js .hero-wrap { opacity: 0 }`) so the hero stays invisible + during the page-activation transition, then fades in once + Bifrost has booted. ------------------------------------------------------------- */ - const heroTl = gsap.timeline({ defaults: { ease: 'power3.out' } }); - - // Split the hero title into lines-ish spans for a nicer reveal - const heroTitle = document.querySelector('.hero-title'); - if (heroTitle) { - // preserve
, wrap visible text chunks in spans - const walk = (node) => { - const kids = [...node.childNodes]; - kids.forEach(k => { - if (k.nodeType === Node.TEXT_NODE && k.textContent.trim()) { - const frag = document.createDocumentFragment(); - k.textContent.split(/(\s+)/).forEach(tok => { - if (tok.trim()) { - const w = document.createElement('span'); - w.className = 'htw'; - w.style.display = 'inline-block'; - w.style.overflow = 'hidden'; - const inner = document.createElement('span'); - inner.style.display = 'inline-block'; - inner.style.transform = 'translateY(110%)'; - inner.style.willChange = 'transform'; - inner.textContent = tok; - w.appendChild(inner); - frag.appendChild(w); - } else { - frag.appendChild(document.createTextNode(tok)); - } - }); - node.replaceChild(frag, k); - } else if (k.nodeType === Node.ELEMENT_NODE && k.tagName !== 'BR') { - walk(k); - } - }); - }; - walk(heroTitle); - } - - heroTl - .from('.eyebrow', { opacity: 0, y: 14, duration: 0.7 }, 0.15) - .to('.hero-title .htw > span', { y: '0%', duration: 1.05, stagger: 0.045, ease: 'power4.out' }, 0.2) - .from('.hero-lede', { opacity: 0, y: 20, duration: 0.9 }, 0.7) - .from('.hero-foot', { opacity: 0, y: 14, duration: 0.8 }, 0.9); + gsap.to('.hero-wrap', { + opacity: 1, + duration: 1.0, + ease: 'power2.out', + delay: 0.1, + }); /* ------------------------------------------------------------- ARCHITECTURE — two-phase scrubbed sequence diff --git a/protected/index.html b/protected/index.html index 91a5948..63836c4 100644 --- a/protected/index.html +++ b/protected/index.html @@ -790,6 +790,10 @@ html { margin-inline: auto; padding-top: clamp(2rem, 6vh, 4rem); } + /* Hide the hero on first paint while JS is booting so it doesn't + flash in raw form during the page-activation transition. The + Bifrost init fades it in once ScrollTriggers are wired up. */ + .js .hero-wrap { opacity: 0; } .eyebrow { font-size: var(--step-sm);