// hero.jsx — 3 variations du HERO de la landing
// Toutes partagent le titre "Fais un vœu. Ton territoire l'exauce."
// V1: Constellation cinétique (signature) — la carte au centre, contenu autour
// V2: Typographie géante "à la Apple" — mot par mot révélé, constellation discrète
// V3: Photo immersive aurore — collage cinématique de placeholders
function useAnimatedCounter(target, durationMs = 1800) {
const [v, setV] = React.useState(0);
React.useEffect(() => {
const start = performance.now();
let raf;
const tick = (now) => {
const t = Math.min(1, (now - start) / durationMs);
// Ease-out cubic
const eased = 1 - Math.pow(1 - t, 3);
setV(Math.round(target * eased));
if (t < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [target]);
return v;
}
function MagneticButton({ children, href, primary = true, ...rest }) {
const ref = React.useRef(null);
const onMove = (e) => {
const el = ref.current;
if (!el) return;
const rect = el.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
el.style.transform = `translate(${x * 0.18}px, ${y * 0.22}px) scale(1.02)`;
};
const onLeave = () => {
const el = ref.current;
if (el) el.style.transform = "";
};
const Cmp = href ? "a" : "button";
return (
{children}
);
}
function LiveCounter({ base = 1240 }) {
const v = useAnimatedCounter(base, 2200);
return (
{v.toLocaleString("fr-FR")}
vœux exaucés cette semaine
);
}
function HeroEyebrow() {
return (
V1
La Pévèle Augmentée — Le Génie Territorial
);
}
function HeroActions() {
return (
Commencer
→
Voir comment ça marche
);
}
/* ─── V1 : Constellation cinétique ────────────────────────────────────── */
function HeroConstellation() {
return (
Fais un vœu.
Ton territoire
l'exauce.
La Pévèle Carembault s'illumine quand 100 000 habitants se relient.
38 communes, 10 univers, un seul geste : le Vœu.
Le Génie matche localement. Le territoire exauce.
);
}
/* ─── V2 : Typographie géante "Apple" ─────────────────────────────────── */
function HeroTypographic() {
// Mots révélés en cascade
const words = ["Fais", "un", "vœu."];
const words2 = ["Ton", "territoire", "l'exauce."];
return (
{/* Discrete constellation top right */}
{words.map((w, i) => (
{w}
))}
{words2.map((w, i) => (
{w}
))}
Le réseau d'intentions de la Pévèle Carembault. Un vœu local. Un match
en moins de 60 secondes. Un Exaucement.
);
}
/* ─── V3 : Photo immersive aurore ─────────────────────────────────────── */
// Placeholder cards — fallback dégradé aurore + emoji du brief (jamais vide)
const PH_CARDS = [
{ emoji: "🥖", label: "Pain frais — Orchies", grad: "linear-gradient(135deg, #F59E0B, #FB7185)",
pos: { top: "8%", left: "0%", width: 220, height: 280, rotate: -4 } },
{ emoji: "🌱", label: "Plantation collective", grad: "linear-gradient(135deg, #10B981, #34D399)",
pos: { top: "0%", right: "10%", width: 260, height: 200, rotate: 3 } },
{ emoji: "🤝", label: "Coup de main voisin", grad: "linear-gradient(135deg, #7C5CFF, #22D3EE)",
pos: { top: "44%", left: "18%", width: 240, height: 190, rotate: -2 } },
{ emoji: "🎻", label: "Concert au village", grad: "linear-gradient(135deg, #F472B6, #A78BFA)",
pos: { top: "38%", right: "0%", width: 280, height: 220, rotate: 4 } },
{ emoji: "🌅", label: "Pévèle, golden hour", grad: "linear-gradient(135deg, #F5B544, #FB7185)",
pos: { bottom: "0%", left: "8%", width: 300, height: 200, rotate: 2 } },
{ emoji: "🛠", label: "Atelier réparation", grad: "linear-gradient(135deg, #22D3EE, #60A5FA)",
pos: { bottom: "4%", right: "16%", width: 230, height: 170, rotate: -3 } },
];
function PhotoCard({ card, delay = 0 }) {
const { pos, grad, emoji, label } = card;
return (
);
}
function HeroImmersive() {
return (
Fais un vœu.
Ton territoire
l'exauce.
Mille visages, trente-huit communes, dix univers. Ici, on ne poste pas —
on demande, on aide, on s'exauce. Et le territoire s'illumine.
{PH_CARDS.map((c, i) => (
))}
);
}
window.HeroConstellation = HeroConstellation;
window.HeroTypographic = HeroTypographic;
window.HeroImmersive = HeroImmersive;
window.MagneticButton = MagneticButton;
window.useAnimatedCounter = useAnimatedCounter;