// HeroZone — the reserved interactive hero. For now a constrained placeholder: // a chamfer-clipped panel of warm chiaroscuro whose light source drifts toward // the pointer (the one signature motion moment), with the ink stroke parallaxing // and the watermark beneath. A toggle reveals the zone spec + safe area so any // future interactive element inherits the same frame. const ASSET = '../../assets'; function HeroZone() { const [p, setP] = React.useState({ x: 0.62, y: 0.28 }); const [showSpec, setShowSpec] = React.useState(false); const ref = React.useRef(null); const reduce = React.useRef( typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches ); function onMove(e) { if (reduce.current) return; const r = ref.current.getBoundingClientRect(); setP({ x: Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)), y: Math.min(1, Math.max(0, (e.clientY - r.top) / r.height)), }); } function onLeave() { setP({ x: 0.62, y: 0.28 }); } // light follows pointer; ink drifts opposite for depth (short travel). const lx = 30 + p.x * 50; const ly = 8 + p.y * 40; const inkShift = `translate(${(0.5 - p.x) * 26}px, ${(0.5 - p.y) * 16}px)`; const hz = { frame: { position: 'relative', width: '100%', aspectRatio: '4 / 5', borderRadius: 'var(--radius-cutout)', background: `radial-gradient(120% 95% at ${lx}% ${ly}%, #C9B299 0%, #8A6A47 30%, #4A3826 60%, #221E18 92%)`, transition: 'background 700ms var(--ease-out)', overflow: 'hidden', cursor: 'crosshair', boxShadow: 'var(--shadow-2)', }, accent: { position: 'absolute', right: '9%', top: '12%', width: '76px', opacity: 0.16, filter: 'invert(1)', transform: inkShift, transition: 'transform 900ms var(--ease-out)', pointerEvents: 'none', }, grain: { position: 'absolute', inset: 0, backgroundImage: `url(${ASSET}/texture-grain.svg)`, backgroundSize: '220px', opacity: 0.12, mixBlendMode: 'overlay', pointerEvents: 'none', }, watermark: { position: 'absolute', left: '8%', bottom: '8%', width: '128px', opacity: 0.1, filter: 'invert(1)', pointerEvents: 'none', }, toggle: { position: 'absolute', right: '8%', bottom: '7%', fontFamily: 'var(--font-mono)', fontSize: '10px', letterSpacing: '0.12em', textTransform: 'uppercase', color: 'rgba(239,231,218,0.55)', background: 'transparent', border: '1px solid rgba(239,231,218,0.22)', borderRadius: '2px', padding: '5px 9px', cursor: 'pointer', zIndex: 3, }, safe: { position: 'absolute', inset: '9% 9% 9% 9%', border: '1px dashed rgba(239,231,218,0.4)', pointerEvents: 'none', display: showSpec ? 'block' : 'none', }, safeLabel: { position: 'absolute', top: '-9px', left: '10px', background: '#221E18', padding: '0 6px', fontFamily: 'var(--font-mono)', fontSize: '9px', letterSpacing: '0.1em', color: 'rgba(239,231,218,0.7)', }, placeholderTag: { position: 'absolute', left: '7%', top: '8%', fontFamily: 'var(--font-mono)', fontSize: '10px', letterSpacing: '0.14em', textTransform: 'uppercase', color: 'rgba(239,231,218,0.45)', display: showSpec ? 'block' : 'none', }, }; return (
Hero zone — interactive · TBD
SAFE AREA · 1:1.25 · pad 9%
); } Object.assign(window, { HeroZone });