// hifi-sections.jsx — sections of the hi-fi La Foire page. // ────── HERO (3 variants : cubes / monster / classic) ────── function Hero({ variant = 'cubes' }) { return (
{/* Bulb garland at the top */}
{/* CONTENT — switches on variant */} {variant === 'cubes' && } {variant === 'monster' && } {variant === 'classic' && } {/* Sub & CTAs (shared across variants) */}
{CONTENT.hero.subBold}{CONTENT.hero.subRest}
{CONTENT.hero.pun}
); } // Variant 1 — Lettres-cubes (default, brand signature) function HeroCubes() { return (
{'LAFOIRE'.split('').map((c, i) => ( {c} ))}
); } // Variant 2 — Typo monstre function HeroMonster() { return (
LA
FOIRE
les stands !
); } // Variant 3 — Classic function HeroClassic() { return (
La Foire
les stands.
); } // ────── PITCH section ────────────────────────────────────── const PLATE_VARIANTS = ['', 'yellow', '']; const PLATE_ROTATES = [-2.5, 1.5, -1.5]; function PitchSection() { const { eyebrow, titleLine1, titleWord, titleLine2, lead, note, plates } = CONTENT.pitch; const noteLines = note.split('\n'); return (
{eyebrow}

{titleLine1}
la {titleWord} {titleLine2}

{lead}

{plates.map((p, i) => (
{p.num}{p.unit && {p.unit}}
{p.label}
))}
); } // ────── HOW IT WORKS ─────────────────────────────────────── function HowSection() { const steps = [ { n: '1', t: 'Tu badges', d: "Récupère ton bracelet RFID à l'accueil." }, { n: '2', t: 'Tu joues', d: '7 stands en libre-accès, 1h30, 2 manches automatiques.' }, { n: '3', t: 'Tu marques', d: 'Chaque action te rapporte des points, en temps réel.' }, { n: '4', t: 'Tu triomphes', d: "Leaderboard live affiché en salle pour l'équipe." }]; return (
Comment ça marche

Quatre temps,
zéro friction.

{steps.map((s, i) => {s.n}
{s.t}
{s.d}
)}
Pas d'opérateur. Pas de monnaie. Pas de queue.
Que de la compète entre potes.
); } // ────── STANDS ───────────────────────────────────────────── function StandsSection() { const { eyebrow, titleLine1, titleLine2 } = CONTENT.stands; return (
{eyebrow}

{titleLine1}
{titleLine2}

{STANDS_DATA.map((s, i) =>
{s.n}
{s.photo ? {s.name} : Photo à fournir }

{s.name}

{s.desc}

)}
); } // ────── LEADERBOARD ──────────────────────────────────────── const ROW_H = 46; // px — must match padding + content height in CSS function LeaderboardSection() { const [players, setPlayers] = React.useState(CONTENT.leaderboard.players); // { id → { delta, key } } — one popup per player at a time const [popups, setPopups] = React.useState({}); const [clock, setClock] = React.useState('21:47'); // Clock ticks forward 1 min every 8 s React.useEffect(() => { let total = 21 * 60 + 47; const id = setInterval(() => { total++; const h = Math.floor(total / 60) % 24; const m = total % 60; setClock(`${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}`); }, 8000); return () => clearInterval(id); }, []); // Score engine — each tick, 1-2 random players score React.useEffect(() => { // Different base intervals per player (ms) — "Toi" is the hungriest const speeds = { 1: 3200, 2: 2600, 3: 3800, 4: 2900, 5: 2100 }; const timers = Object.entries(speeds).map(([idStr, interval]) => { const pid = Number(idStr); const tick = () => { // 25% chance of missing this tick if (Math.random() < 0.25) return; const delta = Math.floor(Math.random() * 170) + 40; const popKey = Date.now(); setPlayers(prev => prev.map(p => p.id === pid ? { ...p, score: p.score + delta } : p) ); setPopups(prev => ({ ...prev, [pid]: { delta, key: popKey } })); setTimeout(() => setPopups(prev => { if (prev[pid]?.key !== popKey) return prev; const next = { ...prev }; delete next[pid]; return next; }), 1100); }; // Stagger startup so all players don't fire at once const jitter = Math.random() * interval; let timer; const startup = setTimeout(() => { tick(); timer = setInterval(tick, interval); }, jitter); return { startup, get timer() { return timer; } }; }); return () => timers.forEach(t => { clearTimeout(t.startup); clearInterval(t.timer); }); }, []); const sorted = [...players] .sort((a, b) => b.score - a.score) .map((p, i) => ({ ...p, rank: i + 1 })); return ( <>
{CONTENT.leaderboard.eyebrow}

{CONTENT.leaderboard.title}
{CONTENT.leaderboard.titleHighlight}

{CONTENT.leaderboard.copy}

Live · {CONTENT.leaderboard.teamName} {clock}
{sorted.map((p, idx) => { const popup = popups[p.id]; return (
{p.rank}
{p.name}
{p.score.toLocaleString('fr-FR')} {popup && ( +{popup.delta} )}
); })}
); } // ────── F&B (variant B · Cartes polaroid) ───────────────── const FB_ROTATES = [-2.5, 2.5]; const FB_KINDS = ['food', 'bar']; function FBSection() { const { eyebrow, title, intro, blocks: rawBlocks } = CONTENT.fb; const blocks = rawBlocks.map((b, i) => ({ ...b, kind: FB_KINDS[i], cap: b.caption, ctaLabel: b.cta, rotate: FB_ROTATES[i], })); return (
{eyebrow}

{title}
pour trinquer & manger.

{intro}

{blocks.map((b, i) => (
{b.eyebrow}

{b.name}

{b.copy}

))}
); } // ────── FINAL CTA ────────────────────────────────────────── function FinalCTASection() { const { titleLine1, titleLine2, sub, cta1, cta2 } = CONTENT.final; return (

{titleLine1}
{titleLine2}

{sub} {cta1} {cta2}
); } // ────── FOOTER ───────────────────────────────────────────── function Footer() { return ( ); } Object.assign(window, { Hero, PitchSection, HowSection, StandsSection, LeaderboardSection, FBSection, FinalCTASection, Footer });