// ============================================================= // Galerie Guzman — App // Site immersif. Toggle allégorique/moderne global. // Curseur perso, parallaxe douce, fondus longs, lightbox. // ============================================================= const { useState, useEffect, useRef, useCallback, useMemo } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "grain": 4, "tempo": "medium", "hero": "triptyque", "glass": false } /*EDITMODE-END*/; // ---------- Custom cursor ---------- function CustomCursor() { const dotRef = useRef(null); const ringRef = useRef(null); useEffect(() => { if (matchMedia('(pointer: coarse)').matches) return; document.body.classList.add('has-cursor'); let mx = window.innerWidth / 2,my = window.innerHeight / 2; let rx = mx,ry = my; let dx = mx,dy = my; let raf; const onMove = (e) => {mx = e.clientX;my = e.clientY;}; const tick = () => { rx += (mx - rx) * 0.18; ry += (my - ry) * 0.18; dx += (mx - dx) * 0.42; dy += (my - dy) * 0.42; if (ringRef.current) ringRef.current.style.transform = `translate3d(${rx}px, ${ry}px, 0) translate(-50%, -50%)`; if (dotRef.current) dotRef.current.style.transform = `translate3d(${dx}px, ${dy}px, 0) translate(-50%, -50%)`; raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); const onOver = (e) => { if (!ringRef.current) return; const art = e.target.closest('[data-cursor="art"]'); const link = e.target.closest('a, button, [role="button"], input, textarea, select'); ringRef.current.classList.toggle('is-art', !!art); ringRef.current.classList.toggle('is-link', !art && !!link); if (dotRef.current) dotRef.current.classList.toggle('is-art', !!art); }; window.addEventListener('mousemove', onMove); document.addEventListener('mouseover', onOver); return () => { cancelAnimationFrame(raf); window.removeEventListener('mousemove', onMove); document.removeEventListener('mouseover', onOver); document.body.classList.remove('has-cursor'); }; }, []); return ( <>
); } // ---------- Reveal-on-scroll ---------- function useReveal() { useEffect(() => { const els = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('is-in'); io.unobserve(e.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -10% 0px' }); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }); } // ---------- Parallax (gentle) ---------- function useParallax() { useEffect(() => { let raf; const elems = () => document.querySelectorAll('[data-parallax]'); const onScroll = () => { if (raf) return; raf = requestAnimationFrame(() => { const vh = window.innerHeight; elems().forEach((el) => { const speed = parseFloat(el.dataset.parallax) || 0.08; const r = el.getBoundingClientRect(); const center = r.top + r.height / 2 - vh / 2; el.style.transform = `translate3d(0, ${center * -speed}px, 0) scale(1.06)`; }); raf = 0; }); }; onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll); return () => {window.removeEventListener('scroll', onScroll);window.removeEventListener('resize', onScroll);cancelAnimationFrame(raf);}; }, []); } // ---------- Header ---------- function Header({ vision, setVision }) { return (
Guzman PEINTRE · ARTISANT PROVENÇAL
); } // ---------- Intro Hero — common, with auto-cycling carousel ---------- function IntroHero({ setVision }) { const slides = useMemo(() => [ { src: 'assets/intro-1.svg', caption: 'lisière du soir, atelier' }, { src: 'assets/intro-2.svg', caption: 'aube en forêt, Sologne' }, { src: 'assets/intro-3.svg', caption: 'voile d’eau, étude' }, { src: 'assets/intro-4.svg', caption: 'partition bleue et verte' }], []); const [active, setActive] = useState(0); useEffect(() => { const t = setInterval(() => setActive((a) => (a + 1) % slides.length), 6000); return () => clearInterval(t); }, [slides.length]); const enter = (vision) => { setVision(vision); setTimeout(() => { document.getElementById('salle')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 50); }; return (
{slides.map((s, i) =>
)}
GALERIE YVON GUZMAN

Faire vivre
l'impossible.

Une passion pour la création, deux salles ouvertes. Une exposition à parcourir comme on parcourt un poème.

{slides.map((_, i) => )}
Entrer
); } // ---------- Hero — triptyque ---------- function HeroTriptyque({ vision, onOpenOeuvre, oeuvres }) { const featured = useMemo(() => { const allegoMain = ['passage-soir', 'recits-laiton', 'songe-orange']; const moderneMain = ['suspension-4', 'blanc-rouge', 'geometrie-jaune']; const ids = vision === 'allegorique' ? allegoMain : moderneMain; return ids.map((id) => oeuvres.find((o) => o.id === id)).filter(Boolean); }, [vision, oeuvres]); return (
{vision === 'allegorique' ? "Mot de l'artiste" : 'Salle des formes · 2026'}
{vision === 'allegorique' ?

Un regard,
3 chefs d'oeuvre.

:

La couleur,
posée.

}
{featured.map((o, i) => )}
); } // ---------- Hero — fullbleed ---------- function HeroFullbleed({ vision, onOpenOeuvre, oeuvres }) { const work = useMemo(() => { const id = vision === 'allegorique' ? 'songe-orange' : 'geometrie-jaune'; return oeuvres.find((o) => o.id === id); }, [vision, oeuvres]); return (
onOpenOeuvre(work.id)}> {work.titre}
{vision === 'allegorique' ? 'Salle des récits · printemps 2026' : 'Salle des formes · printemps 2026'}
Œuvre courante
{work.titre}
{vision === 'allegorique' ?

Le regard,
au ralenti.

:

La couleur,
posée.

}
); } // ---------- Hero — editorial ---------- function HeroEditorial({ vision, onOpenOeuvre, oeuvres }) { const work = useMemo(() => { const id = vision === 'allegorique' ? 'recits-laiton' : 'blanc-rouge'; return oeuvres.find((o) => o.id === id); }, [vision, oeuvres]); return (
{vision === 'allegorique' ? 'Salle des récits · printemps 2026' : 'Salle des formes · printemps 2026'}
{vision === 'allegorique' ?

Le regard,
au ralenti.

:

La couleur,
posée.

}

{vision === 'allegorique' ? "Quinze toiles où la matière raconte. Une exposition lente, à parcourir comme on parcourt un poème." : "Douze œuvres récentes. Géométrie, couleur, équilibre — l'épure d'un regard contemporain."}

Entrer dans la salle Lire la note d'intention
); } // ---------- Intent ribbon ---------- function IntentRibbon() { return (
« L'art a toujours été partie intégrante dans ma vie. Aujourd'hui, je vous partage ma passion »
— YVON GUZMAN
); } // ---------- Salle (mur de musée) ---------- function Salle({ vision, onOpenOeuvre, oeuvres }) { const works = oeuvres.filter((o) => o.salle === vision); return (
{vision === 'allegorique' ? 'Salle 01 · des récits' : 'Salle 02 · des formes'}

{vision === 'allegorique' ? <>Dix toiles,
qui vous transportent. : <>Dix œuvres
où la couleur se pose. }

{vision === 'allegorique' ? "Laissez vous transporter dans mes pensées à travers mes toiles. Un style authentique, où chaque couleur trouve sa place. " : "Une exposition à hauteur d'œil. Chaque tableau cherche un équilibre de couleur, un instant qui tient — comme une partition jouée sans préméditation."}

{works.map((o, i) => )}
); } // ---------- Intention ---------- function Intention({ data }) { return (
Note d'intention
{data.intention.map((p, i) =>

{p}

)}
Y. Guzman
ALLAUCH · 2026
); } // ---------- Bio + Expositions ---------- function Biographie({ data }) { return (
Biographie

L'artiste,
en quelques mots.

Portrait d'Yvon Guzman en atelier

Yvon Guzman vit et travaille à Allauch depuis plus de 30 ans. Il adopte un point de vue unique, partagé entre figuration allégorique et sculptures ou verreries.

{data.bio}

Expositions sélectionnées

    {data.expositions.map((e, i) =>
  • {e.annee} {e.titre ? {e.titre} : Martinique} {e.lieu}{e.ville ? (e.lieu ? `, ${e.ville.toUpperCase()}` : e.ville.toUpperCase()) : ''} {e.type}
  • )}
); } // ---------- Contact ---------- function Contact() { const [sent, setSent] = useState(false); const [sending, setSending] = useState(false); const [error, setError] = useState(null); const [form, setForm] = useState({ nom: '', email: '', sujet: 'oeuvre', oeuvre: '', message: '' }); const submit = async (e) => { e.preventDefault(); setSending(true); setError(null); try { const response = await fetch('https://formspree.io/f/xqevvdrw', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form) }); if (response.ok) { setSent(true); setForm({ nom: '', email: '', sujet: 'oeuvre', oeuvre: '', message: '' }); } else { setError('Erreur lors de l\'envoi. Veuillez réessayer.'); } } catch (err) { setError('Problème de connexion. Veuillez réessayer.'); console.error(err); } finally { setSending(false); } }; return (
Acquérir · échanger

Pour recevoir
la note d'une toile.

Les prix ne figurent pas sur le site, chaque œuvre fait l'objet d'un échange. Écrivez à Yvon Guzman, il vous renseignera personnellement.

Atelier Allauch, France
Écrire yvonguzman@neuf.fr
setForm({ ...form, nom: e.target.value })} placeholder="Camille Béranger" />
setForm({ ...form, email: e.target.value })} placeholder="vous@exemple.fr" />
{form.sujet === 'oeuvre' &&
setForm({ ...form, oeuvre: e.target.value })} placeholder="" />
}
{sent ?
Votre message est parti. Yvon Guzman vous répondra dans les jours qui viennent.
: error ?
{error}
: }
); } // ---------- Footer ---------- function Footer({ onNavigate }) { return ( ); } // ---------- Lightbox ---------- function Lightbox({ id, oeuvres, onClose, onNav }) { const open = id != null; const current = oeuvres.find((o) => o.id === id); const sameSalle = current ? oeuvres.filter((o) => o.salle === current.salle) : []; const idx = current ? sameSalle.findIndex((o) => o.id === id) : 0; const prevId = sameSalle.length ? sameSalle[(idx - 1 + sameSalle.length) % sameSalle.length].id : null; const nextId = sameSalle.length ? sameSalle[(idx + 1) % sameSalle.length].id : null; useEffect(() => { document.body.classList.toggle('lightbox-open', open); if (!open) return; const onKey = (e) => { if (e.key === 'Escape') onClose(); if (e.key === 'ArrowLeft' && prevId) onNav(prevId); if (e.key === 'ArrowRight' && nextId) onNav(nextId); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [open, prevId, nextId, onClose, onNav]); if (!current) { return ; } return (
{prevId && } {nextId && }
{current.titre}
{current.salle === 'allegorique' ? 'Salle des récits' : 'Salle des formes'} · n° {current.num}

{current.titre}

{current.note}

{current.salle !== 'moderne' && ( <>
Technique
{current.technique}
Dimensions
{current.dim}
)}
Disponibilité
{current.statut}
{current.statut === 'disponible' || current.statut === 'réservée' ? Contacter pour acquérir : Cette œuvre a trouvé sa place. }
); } // ---------- Philosophie artistique ---------- function PhilosophieSection() { const cards = [ { num: '01', titre: 'La dualité', texte: 'Deux univers se côtoient dans mon œuvre — le récit allégorique et la forme abstraite. Non pas comme une contradiction, mais comme deux langages d\'une même pensée.' }, { num: '02', titre: 'L\'authenticité', texte: 'Peindre sans compromis. Chaque toile est un engagement envers la matière, envers les couleurs, envers ce qui m\'a poussé à poser le premier coup de pinceau. ' }, { num: '03', titre: 'L\'innovation', texte: 'Explorer sans relâche les possibilités de la matière. L\'innovation n\'est pas une rupture, c\'est une curiosité qui dure.' }]; return (
Ma philosophie artistique

Deux regards,
une seule main.

{cards.map((c, i) =>
{c.num}

{c.titre}

{c.texte}

)}
); } // ---------- Stats bandeau ---------- function StatsBandeau() { const stats = [ { valeur: '+30', unite: '', label: 'ans d\'atelier' }, { valeur: '20', unite: '+', label: 'expositions' }, { valeur: '100', unite: '+', label: 'oeuvres' }, { valeur: '2', unite: '', label: 'outres mers' }]; return (
{stats.map((s, i) =>
{s.valeur} {s.unite && {s.unite}}
{s.label}
)}
); } // ---------- App ---------- function App() { const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); const [vision, setVision] = useState('allegorique'); const [lightboxId, setLightboxId] = useState(null); const [page, setPage] = useState('galerie'); const oeuvres = window.GUZMAN_DATA.oeuvres; const navigateTo = useCallback((p) => { setPage(p); window.scrollTo({ top: 0, behavior: 'smooth' }); }, []); const backToGalerie = useCallback(() => navigateTo('galerie'), [navigateTo]); // sync vision to html attr useEffect(() => { document.documentElement.setAttribute('data-vision', vision); }, [vision]); // sync tweaks to html attrs / css vars useEffect(() => { document.documentElement.style.setProperty('--grain', tweaks.grain / 100); document.documentElement.setAttribute('data-tempo', tweaks.tempo); document.documentElement.setAttribute('data-glass', tweaks.glass ? 'on' : 'off'); }, [tweaks.grain, tweaks.tempo, tweaks.glass]); useReveal(); useParallax(); const openOeuvre = useCallback((id) => setLightboxId(id), []); const closeOeuvre = useCallback(() => setLightboxId(null), []); // Render hero by variant let Hero; if (tweaks.hero === 'fullbleed') Hero = HeroFullbleed;else if (tweaks.hero === 'editorial') Hero = HeroEditorial;else Hero = HeroTriptyque; if (page === 'mentions') { return ( <> ); } return ( <>