const Cursor = () => { const mouseRef = React.useRef({ x: -200, y: -200 }); const ringRef = React.useRef({ x: -200, y: -200 }); const rafRef = React.useRef(null); const [dot, setDot] = React.useState({ x: -200, y: -200 }); const [ring, setRing] = React.useState({ x: -200, y: -200 }); const [hovered, setHovered] = React.useState(false); const [onDark, setOnDark] = React.useState(false); React.useEffect(() => { if ('ontouchstart' in window) return; // Walk up from the hovered element to the first opaque background, return its luminance const isDarkUnder = (el) => { let node = el; while (node && node !== document.documentElement) { const bg = getComputedStyle(node).backgroundColor; const m = bg.match(/rgba?\(([^)]+)\)/); if (m) { const parts = m[1].split(',').map(s => parseFloat(s.trim())); const [r, g, b] = parts; const a = parts.length > 3 ? parts[3] : 1; if (a > 0.35) { const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255; return lum < 0.5; } } node = node.parentElement; } return false; // default = light paper background }; const onMove = (e) => { mouseRef.current = { x: e.clientX, y: e.clientY }; setDot({ x: e.clientX, y: e.clientY }); const el = document.elementFromPoint(e.clientX, e.clientY); setHovered(!!(el && el.closest('button, a, input, textarea, select, [data-hover]'))); setOnDark(el ? isDarkUnder(el) : false); }; const lerp = (a, b, t) => a + (b - a) * t; const tick = () => { ringRef.current = { x: lerp(ringRef.current.x, mouseRef.current.x, 0.10), y: lerp(ringRef.current.y, mouseRef.current.y, 0.10), }; setRing({ x: ringRef.current.x, y: ringRef.current.y }); rafRef.current = requestAnimationFrame(tick); }; window.addEventListener('mousemove', onMove); rafRef.current = requestAnimationFrame(tick); return () => { window.removeEventListener('mousemove', onMove); cancelAnimationFrame(rafRef.current); }; }, []); if (typeof window !== 'undefined' && 'ontouchstart' in window) return null; const dotSize = hovered ? 4 : 7; const ringSize = hovered ? 44 : 28; // Hover → gold accent (legible on any bg); otherwise invert against the background const base = onDark ? '#F4F1EC' : '#2C2C2A'; const color = hovered ? '#D4AF6A' : base; return React.createElement(React.Fragment, null, React.createElement('div', { style: { position: 'fixed', left: dot.x, top: dot.y, width: dotSize, height: dotSize, background: color, borderRadius: '50%', transform: 'translate(-50%,-50%)', pointerEvents: 'none', zIndex: 99999, transition: 'width 150ms, height 150ms, background 150ms', willChange: 'transform', } }), React.createElement('div', { style: { position: 'fixed', left: ring.x, top: ring.y, width: ringSize, height: ringSize, border: `1px solid ${color}`, borderRadius: '50%', transform: 'translate(-50%,-50%)', pointerEvents: 'none', zIndex: 99998, transition: 'width 280ms, height 280ms, border-color 280ms', willChange: 'transform', } }) ); }; Object.assign(window, { Cursor });