// Hero with animated dot-grid canvas, parallax, floating tech panels function HeroCanvas() { const ref = React.useRef(null); React.useEffect(() => { const canvas = ref.current; if (!canvas) return; const ctx = canvas.getContext('2d'); let w,h,dpr = Math.min(window.devicePixelRatio || 1, 2); let mouse = { x: 0.5, y: 0.4, tx: 0.5, ty: 0.4 }; let raf; function resize() { w = canvas.clientWidth;h = canvas.clientHeight; canvas.width = w * dpr;canvas.height = h * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); } resize(); window.addEventListener('resize', resize); const onMove = (e) => { const r = canvas.getBoundingClientRect(); mouse.tx = (e.clientX - r.left) / r.width; mouse.ty = (e.clientY - r.top) / r.height; }; window.addEventListener('mousemove', onMove); const spacing = 40; let t = 0; function frame() { mouse.x += (mouse.tx - mouse.x) * 0.06; mouse.y += (mouse.ty - mouse.y) * 0.06; ctx.clearRect(0, 0, w, h); t += 0.012; const cols = Math.ceil(w / spacing) + 1; const rows = Math.ceil(h / spacing) + 1; const mx = mouse.x * w; const my = mouse.y * h; for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { const x = i * spacing; const y = j * spacing; const dx = x - mx,dy = y - my; const dist = Math.sqrt(dx * dx + dy * dy); const wave = Math.sin(dist * 0.012 - t * 1.6) * 0.5 + 0.5; const falloff = Math.max(0, 1 - dist / 520); const alpha = 0.06 + wave * 0.18 * falloff; const size = 1 + falloff * 1.6; if (falloff > 0.05) { ctx.fillStyle = `rgba(255, 46, 77, ${alpha * 0.8})`; } else { ctx.fillStyle = `rgba(180, 180, 200, ${0.05 + wave * 0.04})`; } ctx.beginPath(); ctx.arc(x, y, size, 0, Math.PI * 2); ctx.fill(); } } ctx.save(); const grad = ctx.createLinearGradient(0, 0, w, h); grad.addColorStop(0, 'rgba(255,46,77,0)'); grad.addColorStop(0.5, 'rgba(255,46,77,0.10)'); grad.addColorStop(1, 'rgba(92,240,255,0)'); ctx.fillStyle = grad; ctx.globalCompositeOperation = 'lighter'; ctx.fillRect(0, 0, w, h); ctx.restore(); raf = requestAnimationFrame(frame); } frame(); return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); window.removeEventListener('mousemove', onMove); }; }, []); return ; } function ScrambleWord({ text, delay = 0 }) { const [out, setOut] = React.useState(text); React.useEffect(() => { const chars = "ABCDEF0123456789#$&*"; let frame = 0; const total = 18; const start = setTimeout(() => { const id = setInterval(() => { frame++; const reveal = Math.floor(frame / total * text.length); const next = text.split('').map((c, i) => { if (c === ' ') return ' '; if (i < reveal) return c; return chars[Math.floor(Math.random() * chars.length)]; }).join(''); setOut(next); if (frame >= total) {clearInterval(id);setOut(text);} }, 38); }, delay); return () => clearTimeout(start); }, [text, delay]); return {out}; } /* ===== HERO SIDE — animated, interactive tech panels ===== */ // Typing code block — cycles through lines being written function TypingCode() { const lines = React.useMemo(() => [ { jsx: <>// 666/ws — production endpoint, plain: '// 666/ws — production endpoint' }, { jsx: <>export const deploy = async () => {'{'}, plain: "export const deploy = async () => {" }, { jsx: <>  const r = await build({'{'} target: 'edge' {'}'});, plain: " const r = await build({ target: 'edge' });" }, { jsx: <>  await ship(r, 'asia-southeast1');, plain: " await ship(r, 'asia-southeast1');" }, { jsx: <>  return {'{'} ok: true {'}'};, plain: " return { ok: true };" }, { jsx: <>{'}'};, plain: "};" }], []); const [step, setStep] = React.useState(0); const [typed, setTyped] = React.useState(0); // chars typed of current line React.useEffect(() => { let cancelled = false; const lineLen = lines[step].plain.length; if (typed < lineLen) { const t = setTimeout(() => !cancelled && setTyped(typed + 1), 18 + Math.random() * 30); return () => {cancelled = true;clearTimeout(t);}; } // line done; pause then advance const t = setTimeout(() => { if (cancelled) return; if (step < lines.length - 1) { setStep(step + 1);setTyped(0); } else { // restart after pause setStep(0);setTyped(0); } }, step === lines.length - 1 ? 2400 : 240); return () => {cancelled = true;clearTimeout(t);}; }, [step, typed, lines]); return ( <> {lines.map((ln, i) => { const fullyDone = i < step; const currentTyping = i === step; const future = i > step; if (future) return
 
; if (fullyDone) return
{ln.jsx}
; // currentTyping — render plain prefix then caret const slice = ln.plain.slice(0, typed); return (
{slice}
); })} ); } // Lighthouse metrics — animate bars + score numbers wobble function LiveMetrics() { const { t: tr } = useLang(); const targets = [ { lab: tr('hero.metric.performance'), t: 99, c: 'green' }, { lab: tr('hero.metric.accessibility'), t: 100, c: 'green' }, { lab: tr('hero.metric.bestpractices'), t: 100, c: 'green' }, { lab: tr('hero.metric.seo'), t: 100, c: 'green' }]; const [v, setV] = React.useState(targets.map(() => 0)); React.useEffect(() => { let raf, start; const dur = 1600; const tick = (ts) => { if (!start) start = ts; const p = Math.min((ts - start) / dur, 1); const e = 1 - Math.pow(1 - p, 3); setV(targets.map((x) => x.t * e)); if (p < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, []); return ( <> {targets.map((x, i) =>
{x.lab} {Math.round(v[i])} / 100
)}
); } // Deploy stages — cycle through running -> done, append new build function DeployFeed() { const { t: tr } = useLang(); const [tick, setTick] = React.useState(0); React.useEffect(() => { const id = setInterval(() => setTick((t) => t + 1), 1400); return () => clearInterval(id); }, []); // 4 stages with rotating "running" position const stages = [tr('hero.deploy.stage.build'), tr('hero.deploy.stage.tests'), tr('hero.deploy.stage.deploy'), tr('hero.deploy.stage.smoke')]; const running = tick % (stages.length + 1); // sometimes "all done" const buildN = 4821 + Math.floor(tick / (stages.length + 1)); return ( <>
DEPLOY #{buildN}
{tr('hero.panel.deploy.live')}
{stages.map((s, i) => { const state = i < running ? 'done' : i === running ? 'running' : 'pending'; return (
{s} {state === 'done' && } {state === 'running' && }
); })} ); } // 3D-tilt wrapper — tracks mouse over the hero-side container function TiltStage({ children }) { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; let target = { x: 0, y: 0 }; let current = { x: 0, y: 0 }; let raf; let inside = false; let floatT = 0; const onMove = (e) => { const r = el.getBoundingClientRect(); const cx = r.left + r.width / 2; const cy = r.top + r.height / 2; // distance to center, clamped to a falloff radius const dx = (e.clientX - cx) / (r.width / 2); const dy = (e.clientY - cy) / (r.height / 2); target.x = Math.max(-1.2, Math.min(1.2, dx)); target.y = Math.max(-1.2, Math.min(1.2, dy)); inside = Math.abs(dx) < 1.6 && Math.abs(dy) < 1.6; }; const tick = () => { current.x += (target.x - current.x) * 0.08; current.y += (target.y - current.y) * 0.08; floatT += 0.016; el.style.setProperty('--mx', current.x.toFixed(3)); el.style.setProperty('--my', current.y.toFixed(3)); el.style.setProperty('--ft', floatT.toFixed(3)); el.style.setProperty('--active', inside ? 1 : 0); raf = requestAnimationFrame(tick); }; window.addEventListener('mousemove', onMove); raf = requestAnimationFrame(tick); return () => { window.removeEventListener('mousemove', onMove); cancelAnimationFrame(raf); }; }, []); return
{children}
; } // Animated connection lines + circuit grid behind panels function CircuitOverlay() { return ( {/* main circuit traces */} {/* animated energy along main trace */} {/* nodes */} ); } function Hero() { const { t } = useLang(); return (
{t('hero.status.online')} · {t('hero.status.booking', window.nextQuarter())}

{t('hero.headline.1')} {t('hero.headline.2')} {t('hero.headline.3')} {t('hero.headline.4')}

{['NEXT.JS', 'LARAVEL', 'TAILWIND', 'POSTGRES', 'AWS', 'STRIPE', 'SUPABASE', 'FIGMA'].map((s) => {s} )}
80+
{t('hero.stat.projects')}
4{t('hero.years.suffix')}
{t('hero.stat.years')}
99.9%
{t('hero.stat.uptime')}
KL·MY
{t('hero.stat.local')}
{t('hero.panel.code.name')}
{t('hero.panel.perf.title')}
{t('hero.panel.perf.healthy')}
); } window.Hero = Hero;