/* ============================================================ Composants partagés — Site Dr Lozach Curseur, Nav, Footer, Reveal hooks, Marquee ============================================================ */ const { useState, useEffect, useRef } = React; /* ---------- Custom cursor ---------- */ function Cursor() { const dot = useRef(null); const ring = useRef(null); useEffect(() => { if (window.matchMedia('(max-width: 900px)').matches) return; let mx = window.innerWidth / 2,my = window.innerHeight / 2; let rx = mx,ry = my; const move = (e) => {mx = e.clientX;my = e.clientY;if (dot.current) {dot.current.style.left = mx + 'px';dot.current.style.top = my + 'px';}}; const tick = () => { rx += (mx - rx) * 0.18; ry += (my - ry) * 0.18; if (ring.current) {ring.current.style.left = rx + 'px';ring.current.style.top = ry + 'px';} requestAnimationFrame(tick); }; const enter = (e) => {if (ring.current && e.target.closest('a, button, .hoverable, input, textarea')) ring.current.classList.add('hover');}; const leave = (e) => {if (ring.current && e.target.closest('a, button, .hoverable, input, textarea')) ring.current.classList.remove('hover');}; window.addEventListener('mousemove', move); document.addEventListener('mouseover', enter); document.addEventListener('mouseout', leave); requestAnimationFrame(tick); return () => { window.removeEventListener('mousemove', move); document.removeEventListener('mouseover', enter); document.removeEventListener('mouseout', leave); }; }, []); return ( <>
>); } /* ---------- Reveal on scroll ---------- */ function useReveal() { useEffect(() => { const els = document.querySelectorAll('.reveal, .line-mask'); const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) {e.target.classList.add('in');io.unobserve(e.target);} }); }, { threshold: 0.15, rootMargin: '0px 0px -50px 0px' }); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }); } /* ---------- Nav ---------- */ function Nav({ active, dark }) { const [scrolled, setScrolled] = useState(false); const [open, setOpen] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 20); window.addEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll); }, []); useEffect(() => { document.body.style.overflow = open ? 'hidden' : ''; return () => { document.body.style.overflow = ''; }; }, [open]); const links = [ { href: 'index.html', label: 'Accueil', key: 'accueil' }, { href: 'pathologies.html', label: 'Pathologies', key: 'pathologies' }, { href: 'chirurgie.html', label: 'Chirurgies', key: 'chirurgie' }, { href: 'parcours.html', label: 'Parcours', key: 'parcours' }, { href: 'faq.html', label: 'FAQ', key: 'faq' }, { href: 'contact.html', label: 'Contact', key: 'contact' }, ]; return ( <> {open && (