import React, { useEffect, useRef } from 'react'; // Single-file, GH Pages–friendly, no external asset imports. // Upgrades: // - Parallax XYZ layer stack reacting to mouse with depth mapping. // - 3D non-rotating "artifact core" with pointer tilt only. // - Glyphy, ultra-light typographic system using Lexend (200/300) + Montserrat. // - Accessible, reduced-motion-safe; no runtime state hooks. // - Self-tests kept & expanded; no external images. export default function Hero() { const orbRef = useRef(null); const layersRef = useRef([]); const rafRef = useRef(0); const mouseRef = useRef({ x: 0, y: 0 }); // Compute tilt for the core (no perpetual rotation) useEffect(() => { const onMove = (e) => { mouseRef.current.x = e.clientX; mouseRef.current.y = e.clientY; }; window.addEventListener('mousemove', onMove, { passive: true }); return () => window.removeEventListener('mousemove', onMove); }, []); // Animation loop to apply both tilt and parallax (rAF to coalesce) useEffect(() => { let running = true; const loop = () => { if (!running) return; const { innerWidth: w = 1, innerHeight: h = 1 } = window; const { x, y } = mouseRef.current; // Core tilt if (orbRef.current) { const { rx, ry } = computeTilt(x, y, w, h, 18); orbRef.current.style.transform = `rotateX(${rx}deg) rotateY(${ry}deg)`; } // Parallax on each layer const nx = (x / w - 0.5) * 2; // [-1,1] const ny = (y / h - 0.5) * 2; // [-1,1] for (const el of layersRef.current) { if (!el) continue; const depth = Number(el.dataset.depth || 0); // Smaller movement for deeper layers const dx = -nx * depth * 10; // px const dy = -ny * depth * 10; // px const dz = depth * 40; // px into screen el.style.transform = `translate3d(${dx}px, ${dy}px, ${-dz}px)`; } rafRef.current = requestAnimationFrame(loop); }; // Respect reduced motion const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); if (mq.matches) return; // do not animate rafRef.current = requestAnimationFrame(loop); return () => { running = false; cancelAnimationFrame(rafRef.current); }; }, []); // Helper to attach refs for layers const layerRef = (i) => (el) => (layersRef.current[i] = el); return (
{/* Inline CSS so the sandbox/bundler doesn't choke on separate CSS files */}
{/* 2-letter AV monogram (inline SVG) */} Launch App
{/* Soft backdrop wash */}
{/* 3D Parallax Stage */}
{/* Depth order: far (0.8) -> near (0.2) */}
{/* Wireframe cage */}
{/* Glass monoliths */}
{/* Scanlines + dot noise */}
{/* Artifact core (interactive tilt) */}
{/* Subtle orbits */}

Artifact Virtual

Immersive, intelligent, and autonomous systems

); } // ===== Pure helpers & tests (no DOM/sandbox-safe) ===== export function computeTilt(clientX, clientY, width, height, maxDeg = 18) { if (!width || !height) return { rx: 0, ry: 0 }; const nx = (clientX / width - 0.5) * 2; // [-1, 1] const ny = (clientY / height - 0.5) * 2; // [-1, 1] const clamp = (v, m) => Math.max(-m, Math.min(m, v)); return { rx: clamp(-ny * maxDeg, maxDeg), ry: clamp(nx * maxDeg, maxDeg) }; } (function selfTests() { try { const eq = (a, b, eps = 1e-9) => Math.abs(a - b) < eps; // Existing tests (unchanged) let { rx, ry } = computeTilt(50, 50, 100, 100, 20); if (!eq(rx, 0) || !eq(ry, 0)) throw new Error('center tilt should be 0/0'); ({ rx, ry } = computeTilt(0, 0, 100, 100, 10)); if (!eq(rx, 10) || !eq(ry, -10)) throw new Error('top-left should clamp to 10/-10'); ({ rx, ry } = computeTilt(100, 100, 100, 100, 15)); if (!eq(rx, -15) || !eq(ry, 15)) throw new Error('bottom-right should be -15/15'); ({ rx, ry } = computeTilt(150, 50, 300, 100, 12)); if (!eq(rx, 0) || !eq(ry, 0)) throw new Error('midpoint of non-square should be 0/0'); // Additional tests ({ rx, ry } = computeTilt(300, 50, 300, 100, 18)); if (!eq(rx, 0) || !eq(ry, 18)) throw new Error('right edge should be +max ry'); ({ rx, ry } = computeTilt(0, 100, 300, 100, 12)); if (!eq(rx, -12) || !eq(ry, -12)) throw new Error('bottom-left should be -max/+ -max'); ({ rx, ry } = computeTilt(0, 0, 0, 0, 99)); if (!eq(rx, 0) || !eq(ry, 0)) throw new Error('zero dims should return 0/0'); } catch (err) { console.error('Hero computeTilt self-tests failed:', err); } })();