// ProjectsExpedition — la « découverte des projets » de l'accueil.
// Chaque projet est une « étape d'expédition » : vignette topo + fiche carnet,
// en alternance gauche/droite, avec usage IA, cordée, durée et un CTA.
// Règle du dénivelé : 100 M = 1 techno/outil appris sur le projet.
//
// Les données vivent dans projects-data.js (window.EXPEDITION).
// L'accueil n'affiche que les projets « featured »; la page projets.html
// affiche l'ensemble. Pour ajouter un projet : voir projects-data.js.
// NB : ne jamais nommer cette constante « EXPEDITION ». Babel compile les
// const de ce fichier en `var` au niveau global — `var EXPEDITION` écraserait
// alors window.EXPEDITION avec la liste filtrée, et projets.html (chargé
// après ce fichier) n'y verrait plus que les projets « featured ».
const FEATURED_STEPS = (window.EXPEDITION || []).filter(p => p.featured);
// little topo-rings vignette behind each photo
function TopoRings({ seed }) {
const rings = [];
const n = 6;
const cx = 50 + (seed % 3) * 6;
const cy = 50 + (seed % 5) * 4;
for (let i = 0; i < n; i++) {
const r = 12 + i * 9;
rings.push();
}
return (
);
}
function AiUsage({ level }) {
return (
USAGE IA
{[1,2,3,4,5].map(i => (
))}
);
}
function ExpeditionCard({ e, i, onOpen }) {
const side = i % 2 === 0 ? "left" : "right";
// Ouverture de la fiche — partagée entre la photo et le bouton "Explorer"
const open = () => onOpen({
id: e.id, title: e.title, img: e.img, video: e.video,
date: e.subtitle.split(' · ')[1] || '',
stamp: e.n, role: e.subtitle, desc: e.desc, tags: e.tags, url: e.url || '#',
});
return (
{/* Connector dot anchored to the central trail */}
EXPÉDITION 2024 — 2026{count} ÉTAPES EN VEDETTEBOUCLE COMPLÈTE
Decouvre mes aventures.
Trois ans de projets menés en entreprise, en cours
et en autonomie. Chaque fiche présente le projet, la technique et le contexte. Ouvrez une étape pour découvrir le projet en détail.
);
}
function ProjectModal({ project, onClose }) {
React.useEffect(() => {
const onKey = (e) => e.key === "Escape" && onClose();
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [onClose]);
// Pas d'animation JS sur le contenu : la carte a déjà son entrée en CSS
// (vm-pop). Un gsap.from + autoAlpha laissait le dernier élément — le
// bouton "VOIR LE PROJET" — bloqué à visibility:hidden si le tween ne
// se terminait pas. On garantit ici que tout le contenu reste visible.
return (