// Hero Component const { useState, useEffect, useRef } = React; // SplitText: renders text with per-char stagger animation, but wraps each // WORD in an inline-block so line-breaks only happen on whitespace. // (Previous version broke mid-word because every char was an inline-block.) function SplitText({ text, delay = 0, className = "" }) { const words = String(text).split(' '); let gi = 0; const nodes = []; words.forEach((word, wi) => { if (word.length > 0) { const chars = Array.from(word); nodes.push( {chars.map((c, i) => { const idx = gi++; return ( {c} ); })} ); } // real space between words = valid break opportunity if (wi < words.length - 1) { nodes.push( ); } }); return {nodes}; } function Hero({ t, variant }) { const canvasRef = useRef(null); useEffect(() => { if (canvasRef.current && window.initHeroCanvas) { window.initHeroCanvas( canvasRef.current, () => document.documentElement.getAttribute('data-hero') || 'mesh', () => document.documentElement.getAttribute('data-theme') || 'light' ); } }, []); return (
{t.hero.meta_role}
{t.hero.meta_location}
{t.hero.meta_available}

{t.hero.description}

{/* Primary "View work" CTA hidden while Projects section is offline. When re-enabling, restore the original two-button layout. */} {t.hero.cta_secondary} {t.hero.cta_explore}
); } window.Hero = Hero; window.SplitText = SplitText;