feat(ui): modularisation du css et fidélisation des composants à la maquette figma

- découpage du css par composant (navbar, hero, footer, global)
- ajustement pixel-perfect du footer (newsletter full-bleed, alignements, typographies)
- documentation et commentaires détaillés du code react et css
This commit is contained in:
MohamedLoum1001
2026-09-23 17:27:05 +02:00
parent 517dc0711d
commit d3b9318dee
41 changed files with 11551 additions and 2101 deletions

View File

@ -0,0 +1,175 @@
import React, { useState } from 'react';
import bannerBg from '../img/homepageactuality.png';
const CATEGORIES = ['Tout', 'Actualités', 'Publications', 'Évènements', 'Projets'];
const NEWS_ITEMS = [
{ id: 1, tag: 'En ce moment' },
{ id: 2, tag: 'En ce moment' },
{ id: 3, tag: 'En ce moment' },
{ id: 4, tag: 'En ce moment' },
];
export default function ActualitySection() {
const [activeCategory, setActiveCategory] = useState('Tout');
const [email, setEmail] = useState('');
const handleSubmitNewsletter = (e) => {
e.preventDefault();
if (email) {
alert(`Inscription enregistrée pour : ${email}`);
setEmail('');
}
};
return (
<section className="w-full bg-white">
{/* 1. BANNIÈRE SUPÉRIEURE PLEINE LARGEUR */}
<div
className="relative w-full min-h-[460px] lg:min-h-[500px] bg-cover bg-right bg-no-repeat flex items-center overflow-hidden"
style={{ backgroundImage: `url("${bannerBg}")` }}
>
{/* Dégradé Figma : Blanc pur à gauche avec transition douce vers le paysage */}
<div
className="absolute inset-0 pointer-events-none"
style={{
background:
'linear-gradient(90deg, #ffffff 0%, #ffffff 38%, rgba(255,255,255,0.85) 48%, rgba(255,255,255,0) 68%)',
}}
/>
<div className="relative z-10 max-w-[1440px] w-full mx-auto px-6 sm:px-12 lg:px-16 py-12 flex flex-col lg:flex-row lg:items-center justify-between gap-10">
{/* Bloc Texte & CTA Gauche */}
<div className="max-w-md lg:max-w-lg shrink-0">
<span className="text-[0.72rem] font-bold tracking-[0.2em] text-[#8c8f3e] uppercase block mb-3">
ACTUALITÉ
</span>
<h2 className="text-3xl sm:text-4xl lg:text-[2.65rem] font-extrabold text-[#0b1320] leading-[1.18] tracking-tight mb-4">
Restez informé, <br />
Inspirer le changement
</h2>
<p className="text-xs sm:text-sm text-slate-500 mb-6 max-w-sm leading-relaxed">
Découvrez nos dernières actualités, publications, événements et initiatives sur le terrain
</p>
<a
href="#actualites"
className="inline-block bg-[#798334] hover:bg-[#656d2b] text-white text-xs sm:text-sm font-semibold px-6 py-2.5 rounded-full transition-colors shadow-sm"
>
Voir toute les actualité
</a>
</div>
{/* Carte Blanche Flottante "En ce moment" */}
<div className="bg-white rounded-3xl p-6 sm:p-8 shadow-[0_12px_36px_rgba(0,0,0,0.1)] border border-slate-100/60 w-full lg:max-w-[460px] h-[210px] sm:h-[230px] flex flex-col justify-between shrink-0">
<span className="text-xs font-semibold text-[#8c8f3e]">
En ce moment
</span>
<div className="text-slate-300 text-xs italic">
{/* Espace prévu pour le résumé de l'article à la une */}
</div>
<div className="h-2 w-14 bg-[#edf0db] rounded-full"></div>
</div>
</div>
</div>
{/* 2. ONGLETS DE FILTRES + GRILLE D'ACTUALITÉS */}
<div className="max-w-[1440px] mx-auto px-6 sm:px-12 lg:px-16 pt-10 pb-16">
{/* Menu épuré avec soulignement */}
<nav className="flex items-center gap-8 sm:gap-12 mb-10 overflow-x-auto no-scrollbar">
{CATEGORIES.map((cat) => {
const isActive = activeCategory === cat;
return (
<button
key={cat}
type="button"
onClick={() => setActiveCategory(cat)}
className={`text-sm tracking-tight transition-colors pb-1.5 relative whitespace-nowrap bg-transparent border-none cursor-pointer p-0 ${isActive
? 'text-slate-900 font-bold'
: 'text-slate-600 hover:text-slate-900 font-medium'
}`}
>
{cat}
{isActive && (
<span className="absolute bottom-0 left-0 w-full h-[2px] bg-slate-900 rounded-full" />
)}
</button>
);
})}
</nav>
{/* Grille des 4 cartes d'articles */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-16">
{NEWS_ITEMS.map((item) => (
<div
key={item.id}
className="bg-[#edf0db] rounded-2xl h-[300px] p-6 flex flex-col justify-between border border-[#e2e6cb] hover:shadow-md transition-shadow cursor-pointer"
>
<span className="text-xs font-semibold text-[#798334]">
{item.tag}
</span>
<div className="space-y-2.5">
<div className="h-3 w-4/5 bg-white/80 rounded-full"></div>
<div className="h-3 w-3/5 bg-white/80 rounded-full"></div>
</div>
</div>
))}
</div>
{/* 3. BANDEAU NEWSLETTER AUX COULEURS DE FIGMA */}
<div className="bg-[#C3A569] rounded-2xl p-6 sm:p-10 flex flex-col lg:flex-row items-center justify-between gap-6 shadow-sm">
{/* Icône enveloppe + Textes */}
<div className="flex items-center gap-6 max-w-xl">
{/* Cercle d'arrière-plan #D9D9D9 */}
<div className="w-16 h-16 rounded-full bg-[#D9D9D9] flex items-center justify-center shrink-0 shadow-inner">
{/* Icône violette #6568B9 */}
<svg
className="w-8 h-8 text-[#6568B9]"
fill="none"
stroke="currentColor"
strokeWidth="1.9"
strokeLinecap="round"
strokeLinejoin="round"
viewBox="0 0 24 24"
>
<rect x="3" y="5" width="18" height="14" rx="2" />
<polyline points="3 7 12 13 21 7" />
</svg>
</div>
<div>
<h3 className="text-lg sm:text-xl font-bold text-white leading-tight">
Ne manquez rien de nos actualités
</h3>
<p className="text-xs sm:text-sm text-white/90 mt-1 leading-relaxed">
Abonnez-vous à notre Newsletter pour recevoir nos dernières nouvelles et publications.
</p>
</div>
</div>
{/* Formulaire d'inscription */}
<form onSubmit={handleSubmitNewsletter} className="w-full lg:w-auto flex-1 max-w-md">
<div className="relative flex items-center">
<input
type="email"
required
placeholder="Votre adresse e-mail"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full bg-white/20 text-white placeholder-white/80 border border-white/50 rounded-full px-5 py-3 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-white pr-32"
/>
<button
type="submit"
className="absolute right-1.5 bg-[#8c8f3e] hover:bg-[#737630] text-white text-xs font-semibold px-5 py-2 rounded-full transition-colors shadow-sm"
>
S'abonner
</button>
</div>
<span className="text-[0.68rem] text-white/80 block mt-2 ml-4">
Nous respectons votre vie privée. Désinscription possible à tout moment.
</span>
</form>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,300 @@
import React from 'react';
import carreerBg from '../img/carreer.png';
export default function CareersSection() {
const parcours = [
{
label: 'Insertion professionnelle',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
</svg>
),
},
{
label: 'Développement de compétences',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H8V4h12v12z" />
</svg>
),
},
{
label: 'Leadership & coordination',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" />
</svg>
),
},
{
label: 'Entrepreneuriat & innovation',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7z" />
</svg>
),
},
{
label: 'Recherche appliquée',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<circle cx="11" cy="11" r="7" />
<path strokeLinecap="round" strokeLinejoin="round" d="m20 20-3.5-3.5" />
</svg>
),
},
{
label: 'Alumni & réseau',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 3 1 9l11 6 9-4.91V17h2V9L12 3z M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82z" />
</svg>
),
},
];
const competencesCol1 = [
{
label: 'Autonomie',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
</svg>
),
},
{
label: 'Gestion de projet',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z" />
</svg>
),
},
{
label: 'Coordination',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<circle cx="9" cy="9" r="4" />
<path d="M9 15c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4zm7.76-9.64-1.41 1.41 1.41 1.41-1.41 1.42 1.41 1.41 1.42-1.41 1.41 1.41 1.41-1.41-1.41-1.42 1.41-1.41-1.41-1.41-1.42 1.41z" />
</svg>
),
},
{
label: 'Communication',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z" />
</svg>
),
},
{
label: 'Stratégie',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="9" />
<circle cx="12" cy="12" r="5" />
<circle cx="12" cy="12" r="1" />
</svg>
),
},
];
const competencesCol2 = [
{
label: 'Leadership',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 17.27 18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" />
</svg>
),
},
{
label: 'Compétences opérationnelles',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="m14.7 6.3 3 3-9.4 9.4H5.3v-3l9.4-9.4z" />
</svg>
),
},
{
label: 'Stabilité professionnelle',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 1 3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z" />
</svg>
),
},
{
label: 'Capacité d’adaptation',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z" />
</svg>
),
},
{
label: 'Intelligence systémique',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" />
</svg>
),
},
];
const opportunites = [
{
label: 'Stages',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z" />
</svg>
),
},
{
label: 'Mandats',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z" />
</svg>
),
},
{
label: 'Bénévolats',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="m12 21.35-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>
),
},
{
label: 'Collaborations',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" />
</svg>
),
},
{
label: 'Projets entrepreneuriaux',
icon: (
<svg className="w-4 h-4 text-[#88ACE3]" fill="currentColor" viewBox="0 0 24 24">
<path d="M13.13 2.51c.37-.47.96-.71 1.57-.64.81.1 1.41.81 1.35 1.63L15.6 9h4.4c.73 0 1.35.52 1.48 1.24.13.72-.25 1.44-.9 1.74l-9 4.15c-.48.22-1.04.14-1.45-.2-.41-.34-.58-.88-.44-1.39L10.8 11H6.5c-.71 0-1.33-.5-1.47-1.2-.14-.7.22-1.41.86-1.72l7.24-5.57z" />
</svg>
),
},
];
return (
<section className="w-full bg-white pb-20">
{/* 1. BANNIÈRE SUPÉRIEURE PLEINE LARGEUR */}
<div
className="relative w-full min-h-[460px] lg:min-h-[500px] bg-cover bg-right bg-no-repeat flex items-center overflow-hidden"
style={{ backgroundImage: `url("${carreerBg}")` }}
>
{/* Dégradé Figma : Sombre à gauche avec fondu vers les personnes à droite */}
<div
className="absolute inset-0 pointer-events-none"
style={{
background:
'linear-gradient(90deg, #000000 0%, rgba(0,0,0,0.92) 34%, rgba(0,0,0,0.68) 52%, rgba(0,0,0,0) 75%)',
}}
/>
<div className="relative z-10 max-w-[1440px] w-full mx-auto px-6 sm:px-12 lg:px-16 py-12">
<div className="max-w-xl text-white">
<span className="text-[0.72rem] font-bold tracking-[0.2em] text-[#C3A569] uppercase block mb-3">
CAREERS & DEVELOPMENT
</span>
<h2 className="text-3xl sm:text-4xl lg:text-[2.65rem] font-extrabold leading-[1.15] tracking-tight mb-4">
Construire des trajectoires, <br />
pas seuement des emplois
</h2>
<p className="text-xs sm:text-sm text-slate-300 mb-2 max-w-lg leading-relaxed font-light">
Chez gaea21, les participants développent des compétences réelles à travers des projets réels, dans un environnement structuré, collaboratif et évolutif.
</p>
<p className="text-xs sm:text-sm text-slate-300 mb-8 max-w-lg leading-relaxed font-light">
L'objectif n'est pas un simple recrutement, mais la construction de trajectoires humaines et professionnelles durables.
</p>
<a
href="#parcours"
className="inline-flex items-center gap-2 bg-[#98984B] hover:bg-[#868641] text-white text-xs sm:text-sm font-semibold px-6 py-2.5 rounded-full transition-colors shadow-sm"
>
<span>Explorer les parcours</span>
<svg className="w-4 h-4" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</a>
</div>
</div>
</div>
{/* 2. GRILLE DES 3 CARTES DU BAS (BEIGE #F1F0E0) */}
<div className="max-w-[1440px] mx-auto px-6 sm:px-12 lg:px-16 pt-10">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-stretch">
{/* CARTE 1 : Parcours disponibles */}
<div className="lg:col-span-4 bg-[#F1F0E0] rounded-2xl p-6 sm:p-8 flex flex-col justify-start">
<h3 className="text-xs font-semibold text-slate-700 mb-5">
Parcours disponibles
</h3>
<ul className="space-y-3.5">
{parcours.map((item, idx) => (
<li key={idx} className="flex items-center gap-3 text-xs sm:text-[0.82rem] text-slate-800 font-medium">
<span className="shrink-0">{item.icon}</span>
<span>{item.label}</span>
</li>
))}
</ul>
</div>
{/* CARTE 2 : Ce que les participants développent */}
<div className="lg:col-span-5 bg-[#F1F0E0] rounded-2xl p-6 sm:p-8 flex flex-col justify-start">
<h3 className="text-xs font-semibold text-slate-700 mb-5">
Ce que les participants développent
</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-3.5">
<ul className="space-y-3.5">
{competencesCol1.map((item, idx) => (
<li key={idx} className="flex items-center gap-3 text-xs sm:text-[0.82rem] text-slate-800 font-medium">
<span className="shrink-0">{item.icon}</span>
<span>{item.label}</span>
</li>
))}
</ul>
<ul className="space-y-3.5">
{competencesCol2.map((item, idx) => (
<li key={idx} className="flex items-center gap-3 text-xs sm:text-[0.82rem] text-slate-800 font-medium">
<span className="shrink-0">{item.icon}</span>
<span>{item.label}</span>
</li>
))}
</ul>
</div>
</div>
{/* CARTE 3 : Opportunités */}
<div className="lg:col-span-3 bg-[#F1F0E0] rounded-2xl p-6 sm:p-8 flex flex-col justify-start">
<h3 className="text-xs font-semibold text-slate-700 mb-5">
Opportunités
</h3>
<ul className="space-y-3.5">
{opportunites.map((item, idx) => (
<li key={idx} className="flex items-center gap-3 text-xs sm:text-[0.82rem] text-slate-800 font-medium">
<span className="shrink-0">{item.icon}</span>
<span>{item.label}</span>
</li>
))}
</ul>
</div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,166 @@
import React, { useState } from 'react';
// Importation du logo vectoriel ou transparent de gaea21
import logo from '../img/logo_gaea21.png';
// Importation de la feuille de style dédiée au Footer
import '../styles/footer.css';
export default function Footer() {
// État local pour stocker l'adresse email saisie par l'utilisateur
const [email, setEmail] = useState('');
// Gestionnaire de soumission du formulaire d'abonnement à la newsletter
const handleSubmitNewsletter = (e) => {
e.preventDefault();
if (email) {
alert(`Inscription enregistrée pour : ${email}`);
setEmail('');
}
};
// Liste ordonnée des liens pour la colonne "Écosystème"
const ecosystemLinks = [
'Individus',
'Entreprises',
'Institutions',
'Universités',
'Praticiens',
];
return (
<footer className="gaea-footer">
{/* -------------------------------------------------------------------
1. SECTION SUPÉRIEURE (Centrée sur 1440px)
Comprend : Le logo + Réseaux sociaux, le trait doré et les colonnes
------------------------------------------------------------------- */}
<div className="gaea-footer-inner">
<div className="footer-top-row">
{/* Bloc Marque : Logo gaea21 et liens sociaux */}
<div className="footer-brand-col">
<a href="/" className="footer-logo">
<img src={logo} alt="Logo gaea21" />
</a>
<div className="footer-socials">
{/* Icône LinkedIn */}
<a href="#linkedin" aria-label="LinkedIn">
<svg viewBox="0 0 24 24">
<path d="M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14m-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.28 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93h2.75M6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37h2.77z" />
</svg>
</a>
{/* Icône Instagram */}
<a href="#instagram" aria-label="Instagram">
<svg viewBox="0 0 24 24">
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.13-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z" />
</svg>
</a>
{/* Icône YouTube */}
<a href="#youtube" aria-label="YouTube">
<svg viewBox="0 0 24 24">
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</svg>
</a>
</div>
</div>
{/* Ligne verticale dorée (#A8985A) de séparation */}
<div className="footer-gold-divider" />
{/* Les 3 colonnes de liens de navigation */}
<div className="footer-nav-columns">
{/* Colonne 1 : Écosystème */}
<div>
<h4 className="footer-col-title">Écosystème</h4>
<ul className="footer-links-list">
{ecosystemLinks.map((item, idx) => (
<li key={idx}>
<a href={`#${item.toLowerCase()}`}>{item}</a>
</li>
))}
</ul>
</div>
{/* Colonne 2 : Ressources */}
<div>
<h4 className="footer-col-title">Ressources</h4>
</div>
{/* Colonne 3 : À propos */}
<div>
<h4 className="footer-col-title">À propos</h4>
</div>
</div>
</div>
</div>
{/* -------------------------------------------------------------------
2. BANDEAU NEWSLETTER (Pleine Largeur / Full bleed)
S'étend d'un bord à l'autre de l'écran avec la couleur #D8DCAB
------------------------------------------------------------------- */}
<div className="footer-newsletter-fullwidth">
<div className="footer-newsletter-content">
{/* Côté gauche : Pastille ronde grise avec icône violette + Textes */}
<div className="newsletter-brand-side">
<div className="newsletter-icon-circle">
<svg fill="none" stroke="currentColor" strokeWidth="1.8" viewBox="0 0 24 24">
<rect x="3" y="5" width="18" height="14" rx="2" />
<polyline points="3 7 12 13 21 7" />
</svg>
</div>
<div className="newsletter-texts">
<h3>Ne manquez rien de nos actualités</h3>
<p>
Abonnez vous a notre Newsletters pour recevoir <br />
nos dernières nouvelles et publications
</p>
</div>
</div>
{/* Côté droit : Formulaire (Champ ovale noir + Bouton vert olive) */}
<form onSubmit={handleSubmitNewsletter} className="newsletter-form-side">
<div className="newsletter-input-group">
<input
type="email"
required
placeholder="Votre adresse e-mail"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="newsletter-input"
/>
<button type="submit" className="newsletter-btn">
S'abonner
</button>
</div>
<span className="newsletter-privacy-text">
Nous respectons votre vie privée. Désinscription possible à tout moment
</span>
</form>
</div>
</div>
{/* -------------------------------------------------------------------
3. BARRE DE MENTIONS LÉGALES TOUT EN BAS (Centrée sur 1440px)
------------------------------------------------------------------- */}
<div className="gaea-footer-inner">
<div className="footer-bottom-bar">
<div>gaea21 2026 - Tout droit réservés</div>
<div className="footer-bottom-links">
<a href="#mentions-legales">Mentions légales</a>
<span className="divider">|</span>
<a href="#confidentialite">Politiques de confidentialités</a>
<span className="divider">|</span>
<a href="#cookies">Cookies</a>
</div>
</div>
</div>
</footer>
);
}

View File

@ -0,0 +1,119 @@
// 1. IMPORTATION DES MODULES ET ASSETS
import React, { useState } from 'react';
// Import du logo gaea21 via Webpack Encore (gestion du hashing et du chemin public)
import logo from '../img/logo_gaea21.png';
/**
* 2. COMPOSANT PRINCIPAL : Header
*
* Rôle fonctionnel :
* - Fournit la barre de navigation principale (Navbar) fixée ou relative en haut de page.
* - Gère l'affichage responsive : menu étendu sur desktop, tiroir repliable (drawer) sur mobile.
* - Propose un bouton d'action principal (« Participer ») pour convertir le visiteur.
*/
export default function Header() {
/**
* ÉTAT LOCAL (State) : isOpen
* - false : le menu mobile est fermé
* - true : le menu mobile est ouvert (déroulé/visible)
*/
const [isOpen, setIsOpen] = useState(false);
return (
/*
Balise sémantique HTML5 <header> :
- navbar-container : gère le fond, la bordure inférieure éventuelle,
la hauteur fixe et le z-index pour rester au-dessus du contenu de la page.
*/
<header className="navbar-container">
{/*
Conteneur interne centré :
- navbar-content : applique un flexbox (justify-content: space-between, align-items: center)
et borne la largeur maximale avec un padding latéral propre.
*/}
<div className="navbar-content">
{/* =========================================================
A. LOGO DE LA MARQUE
========================================================= */}
<a href="/" className="navbar-logo" aria-label="Retour à l'accueil gaea21">
<img src={logo} alt="Logo gaea21" />
</a>
{/* =========================================================
B. BOUTON BURGER (Affiché uniquement sur écrans mobiles)
========================================================= */}
<button
type="button"
/*
Basculement dynamique de la classe 'active' :
Permet en CSS d'animer les 3 barres pour former une croix 'X' quand isOpen vaut true.
*/
className={`hamburger ${isOpen ? 'active' : ''}`}
/* Inverse la valeur booléenne au clic (ouvre ou ferme le menu) */
onClick={() => setIsOpen(!isOpen)}
/* Attributs d'accessibilité ARIA pour les lecteurs d'écran */
aria-label={isOpen ? 'Fermer le menu de navigation' : 'Ouvrir le menu de navigation'}
aria-expanded={isOpen}
>
{/* Les 3 barres horizontales du burger dessinées en CSS */}
<span></span>
<span></span>
<span></span>
</button>
{/* =========================================================
C. MENU DE NAVIGATION (DESKTOP & TIROIR MOBILE)
========================================================= */}
{/*
La classe 'open' est ajoutée conditionnellement pour déclencher
l'apparition du tiroir de navigation en version mobile.
*/}
<nav className={`navbar-nav ${isOpen ? 'open' : ''}`}>
{/* Liste ordonnée des ancres de navigation */}
<ul className="nav-links">
<li>
{/* Le onClick={() => setIsOpen(false)} referme automatiquement le menu mobile après le clic */}
<a href="#parcours" onClick={() => setIsOpen(false)}>
Parcours
</a>
</li>
<li>
<a href="#projet" onClick={() => setIsOpen(false)}>
Projet
</a>
</li>
<li>
<a href="#ecosysteme" onClick={() => setIsOpen(false)}>
Écosystème
</a>
</li>
<li>
<a href="#contact" onClick={() => setIsOpen(false)}>
Contact
</a>
</li>
</ul>
{/* =========================================================
D. BOUTON D'ACTION PRINCIPAL (CALL-TO-ACTION)
========================================================= */}
<div className="navbar-action">
<a
href="#participer"
className="btn-participer"
// Ferme le menu mobile si cliqué
onClick={() => setIsOpen(false)}
>
Participer
</a>
</div>
</nav>
</div>
</header>
);
}

104
assets/components/Hero.jsx Normal file
View File

@ -0,0 +1,104 @@
// 1. IMPORTATIONS DES MODULES ET DES ASSETS
import React from 'react';
// Import de l'image de bannière principale via Webpack Encore
// Webpack génère automatiquement le bon chemin d'accès avec hachage pour le cache
import heroBg from '../img/hero-banner.png';
/**
* 2. COMPOSANT PRINCIPAL : Hero
*
* Rôle fonctionnel et visuel :
* - Première section visible sous le Header (zone "Above the Fold").
* - Transmet la proposition de valeur principale de gaea21 via un titre H1 percutant.
* - Propose un bouton d'action (Call-to-Action) guidant l'utilisateur vers la découverte de l'écosystème.
*/
export default function Hero() {
return (
/*
SECTION ENGOBANTE DU HERO :
- className="hero-section" : applique les dimensions (hauteur minimale/100vh),
le positionnement d'arrière-plan (background-size: cover, background-position: center)
et le positionnement relatif pour caler les calques enfants.
- style={{ backgroundImage: ... }} : injection dynamique de l'URL de l'image
traitée par le bundler Webpack.
*/
<section
className="hero-section"
style={{ backgroundImage: `url("${heroBg}")` }}
>
{/*
CALQUE D'OVERLAY SOMBRE :
- Élément positionné en absolu couvrant toute la surface de la section.
- Applique un gradient ou un filtre semi-transparent sombre afin d'assurer
un contraste suffisant (normes d'accessibilité WCAG) entre le texte blanc
et les zones claires de l'image d'arrière-plan.
*/}
<div className="hero-overlay"></div>
{/*
CONTENEUR DE CONTENU CENTRÉ :
- Assure le centrage horizontal du bloc de texte sur la largeur maximale de la page.
- z-index supérieur à l'overlay pour que les textes et boutons restent cliquables et visibles.
*/}
<div className="hero-content">
{/*
BLOC TEXTUEL :
- Limite la largeur maximale du texte pour éviter qu'il ne s'étale trop sur grands écrans,
ce qui garantit un confort de lecture optimal.
*/}
<div className="hero-text-block">
{/*
TITRE PRINCIPAL H1 :
- Balise unique et essentielle pour le référencement naturel (SEO).
- Les balises <br /> forcent des retours à la ligne précis pour sculpter
la silhouette du texte exactement comme sur la maquette Figma.
*/}
<h1 className="hero-title">
Construire <br />
les infrastructures <br />
humaines, économiques <br />
et territoriales de demain
</h1>
{/*
PARAGRAPHE D'INTRODUCTION :
- Résume succinctement les axes d'impact et la légitimité historique (20 ans) de gaea21.
*/}
<p className="hero-description">
Depuis plus de 20 ans, gaea21 relie insertion professionnelle, santé mentale,
économie locale, transition durable, recherche appliquée et innovation systémique.
</p>
{/*
BOUTON D'APPEL À L'ACTION (CTA) :
- Lien d'ancrage vers la section "#ecosysteme".
- Composé d'un label textuel et d'une flèche SVG vectorielle indiquant l'action/la direction.
*/}
<a href="#ecosysteme" className="btn-hero">
<span>Explorer l'écosystème</span>
{/* Flèche droite indicative (alignée horizontalement avec le texte) */}
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
// Masque l'icône purement décorative des lecteurs d'écran
aria-hidden="true"
>
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</a>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,82 @@
// 1. IMPORTATION DES DÉPENDANCES ET COMPOSANTS ENFANTS
import React from 'react';
// Barre de navigation supérieure (Logo, Liens, Boutons d'action)
import Header from './Header';
// Section d'accroche principale (Titre fort, boutons d'appel à l'action, visuel héroïque)
import Hero from './Hero';
// Cartes des profils cibles ("Qui êtes-vous ?") : Individus, Entreprises, Institutions, etc.
import ProfileCards from './ProfileCards';
// Section explicative de la méthodologie ("Comment ça fonctionne ?") en 6 étapes + chiffres clés
import Methodology from './Methodology';
// Section de preuve d'impact chiffrée avec fond immersif et carrousel
import Impact from './Impact';
// Section actualités : événements, articles, publications et formulaire de newsletter intermédiaire
import ActualitySection from './ActualitySection';
// Section opportunités et carrières ("Construire des trajectoires, pas seulement des emplois")
import CareersSection from './CareersSection';
// Pied de page complet (Navigation secondaire, réseaux sociaux, newsletter pleine largeur, mentions légales)
import Footer from './Footer';
/**
* 2. COMPOSANT RACINE DE LA PAGE D'ACCUEIL : Home
*
* Rôle architectural :
* - Sert de page principale (Page Component / Container) orchestrant la disposition verticale
* de toutes les sections de la page d'accueil de gaea21.
* - Assure une hiérarchie sémantique HTML5 claire pour le référencement (SEO) et l'accessibilité.
*/
export default function Home() {
return (
/*
CONTENEUR GLOBAL DE PAGE :
- relative : sert de référentiel de positionnement pour d'éventuels éléments absolus.
- min-h-screen : garantit que la page couvre au minimum 100% de la hauteur de la fenêtre (évite les fonds blancs coupés).
- bg-white : couleur de fond par défaut pour l'ensemble du site.
- overflow-x-hidden : masque tout débordement horizontal causé par des animations, des bannières pleine largeur ou des marges négatives.
*/
<div className="relative min-h-screen bg-white overflow-x-hidden">
{/*
A. EN-TÊTE DU SITE
Balise <Header /> contenant la navigation principale fixée ou relative en haut de page.
*/}
<Header />
{/*
B. CONTENU PRINCIPAL DE LA PAGE
La balise sémantique HTML5 <main> délimite le cœur informationnel unique de la page d'accueil.
*/}
<main>
{/* Bannière d'introduction */}
<Hero />
{/* Sélection des parcours utilisateurs */}
<ProfileCards />
{/* Présentation du modèle "Learning by Doing" */}
<Methodology />
{/* Statistiques d'impact et résultats sur 20 ans */}
<Impact />
{/* Actualités, publications et veille */}
<ActualitySection />
{/* Opportunités professionnelles et développement de compétences */}
<CareersSection />
{/* Pied de page et liens institutionnels */}
<Footer />
</main>
</div>
);
}

View File

@ -0,0 +1,180 @@
// 1. IMPORTATIONS DES MODULES ET ASSETS
import React, { useState } from 'react';
// Webpack Encore gère l'empreinte de hachage (hash) et le chemin public de l'image de fond
import impactBg from '../img/impact.png';
/**
* 2. STRUCTURE DE DONNÉES STATIQUE : DIAPOSITIVES DU CARROUSEL
* Externaliser les données hors du composant évite de recréer ce tableau
* en mémoire à chaque nouveau cycle de rendu (re-render) du composant.
*
* Le tableau est structuré sous forme de tableau de diapositives (slides).
* SLIDES[0] contient le groupe actif affiché à l'écran.
*/
const SLIDES = [
[
{
value: '3000 +',
label: 'personnes accompagnées',
size: 'w-16 h-16 sm:w-20 sm:h-20', // Taille standard des pastilles latérales
},
{
value: '94% - 97%',
label: 'de réussites en insertion',
size: 'w-24 h-24 sm:w-28 sm:h-28', // Pastille centrale plus imposante pour créer une hiérarchie visuelle
},
{
value: '20 ANS',
label: 'de recherches appliquées',
size: 'w-16 h-16 sm:w-20 sm:h-20', // Taille standard
},
],
];
/**
* 3. COMPOSANT PRINCIPAL : Impact
* Présente la section "NOTRE IMPACT" avec :
* - Un arrière-plan immersif en pleine largeur
* - 3 indicateurs chiffrés majeurs avec pastilles colorées
* - Des contrôles de carrousel (flèches gauche/droite et indicateurs à points)
*/
export default function Impact() {
/**
* ÉTAT LOCAL (State) : Indice du point actif de pagination
* Initialisé à 2 pour correspondre au point central parmi les 5 points du design Figma.
*/
const [currentSlide, setCurrentSlide] = useState(2);
return (
/*
Balise sémantique <section> :
- relative : point d'ancrage pour positionner l'overlay sombre absolu
- w-full : occupe toute la largeur de l'écran (full bleed)
- py-24 sm:py-32 : padding vertical généreux pour donner de l'espace à la section
- px-6 sm:px-12 : padding horizontal adaptatif
- bg-cover bg-center : l'image remplit tout l'arrière-plan sans déformation et reste centrée
- overflow-hidden : empêche tout débordement lié aux animations de zoom
*/
<section
className="relative w-full py-24 sm:py-32 px-6 sm:px-12 bg-cover bg-center overflow-hidden"
style={{ backgroundImage: `url("${impactBg}")` }}
>
{/*
CALQUE D'OVERLAY SOMBRE :
- absolute inset-0 : couvre 100% de la surface de la section
- bg-black/40 : voile noir à 40% d'opacité pour garantir la lisibilité des textes blancs
- pointer-events-none : empêche ce filtre de capturer les clics
*/}
<div className="absolute inset-0 bg-black/40 pointer-events-none" />
{/*
CONTENEUR DE CONTENU :
- relative z-10 : positionne le contenu au-dessus du voile sombre
- max-w-[1400px] mx-auto : largeur maximale cadrée et centrée
- text-center text-white : typographie blanche centrée par défaut
*/}
<div className="relative z-10 max-w-[1400px] mx-auto text-center text-white">
{/* ====================================================================
A. EN-TÊTE : SURTITRE & TITRE PRINCIPAL
==================================================================== */}
{/* Surtitre en majuscules avec espacement étendu (tracking-wide) */}
<span className="text-xs sm:text-sm font-bold tracking-[0.25em] uppercase text-white/90 block mb-2">
NOTRE IMPACT
</span>
{/* Titre descriptif sobre avec marge basse importante avant les chiffres */}
<h2 className="text-lg sm:text-xl font-normal text-white/95 mb-16 sm:mb-20">
Des résultats concrets et durables
</h2>
{/* ====================================================================
B. ZONE CENTRALE : FLÈCHE GAUCHE + 3 MÉTRIQUES + FLÈCHE DROITE
- flex items-center justify-between : alignement horizontal sur une seule ligne
==================================================================== */}
<div className="flex items-center justify-between gap-4 sm:gap-8">
{/* Bouton de navigation gauche (Précédent) */}
<button
type="button"
className="w-10 h-10 sm:w-12 sm:h-12 rounded-full border border-white/60 flex items-center justify-center text-white hover:bg-white/20 transition-colors shrink-0"
aria-label="Précédent"
>
{/* Icône flèche retour */}
<svg className="w-5 h-5 sm:w-6 sm:h-6" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
</svg>
</button>
{/*
Grille des 3 métriques chiffrées :
- 1 colonne sur mobile (grid-cols-1)
- 3 colonnes à partir des écrans moyens (md:grid-cols-3)
- items-end : aligne les éléments par le bas pour absorber la différence de taille du cercle central
- flex-1 max-w-4xl mx-auto : occupe l'espace central disponible de façon équilibrée
*/}
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-8 flex-1 items-end max-w-4xl mx-auto">
{SLIDES[0].map((item, index) => (
<div key={index} className="flex flex-col items-center">
{/*
Pastille ronde vert olive (#9da74d) :
- Taille dynamique issue de la donnée (item.size)
- shadow-[...] : ombre portée sombre accentuant la profondeur
- hover:scale-105 : micro-interaction au survol
*/}
<div
className={`${item.size} rounded-full bg-[#9da74d] mb-6 shadow-[0_4px_16px_rgba(0,0,0,0.35)] transition-transform duration-300 hover:scale-105`}
/>
{/* Valeur numérique mise en avant (ex: 3000 +, 94% - 97%) */}
<span className="text-3xl sm:text-4xl md:text-5xl font-normal tracking-tight text-white mb-2">
{item.value}
</span>
{/* Libellé descriptif sous le chiffre */}
<span className="text-xs sm:text-sm font-light text-white/85 max-w-[200px]">
{item.label}
</span>
</div>
))}
</div>
{/* Bouton de navigation droit (Suivant) */}
<button
type="button"
className="w-10 h-10 sm:w-12 sm:h-12 rounded-full border border-white/60 flex items-center justify-center text-white hover:bg-white/20 transition-colors shrink-0"
aria-label="Suivant"
>
{/* Icône flèche suivant */}
<svg className="w-5 h-5 sm:w-6 sm:h-6" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>
</button>
</div>
{/* ====================================================================
C. INDICATEURS DE PAGINATION (CAROUSEL DOTS)
- 5 points horizontaux centrés en bas de section
==================================================================== */}
<div className="flex justify-center items-center gap-2 mt-16 sm:mt-20">
{[0, 1, 2, 3, 4].map((dot) => (
<span
key={dot}
/*
Style dynamique du point :
- Si c'est le point actif (dot === currentSlide) : couleur vert olive (#a3ad53) et taille augmentée (scale-125)
- Sinon : blanc translucide (bg-white/50)
*/
className={`w-2.5 h-2.5 rounded-full transition-all duration-200 ${dot === currentSlide
? 'bg-[#a3ad53] scale-125'
: 'bg-white/50'
}`}
/>
))}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,259 @@
// 1. IMPORTATION DE REACT
import React from 'react';
/**
* 2. TABLEAU DE DONNÉES STATIQUE : LES 6 ÉTAPES DE LA MÉTHODOLOGIE
* Externaliser les étapes hors du composant évite de recréer ce tableau
* en mémoire à chaque nouveau cycle de rendu (re-render) du composant.
*/
const STEPS = [
{
number: '01',
title: 'Diagnostiquer',
description: 'Comprendre les besoins humains, professionnels et systémiques.',
// Icône SVG : Loupe d'analyse / audit
icon: (
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<circle cx="11" cy="11" r="7" />
<path strokeLinecap="round" strokeLinejoin="round" d="m20 20-3.5-3.5" />
</svg>
),
},
{
number: '02',
title: 'Structurer',
description: 'Créer un cadre, des outils et des trajectoires cohérentes.',
// Icône SVG : Arborescence / Organigramme hiérarchique
icon: (
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<rect x="9" y="3" width="6" height="5" rx="1" />
<rect x="3" y="16" width="6" height="5" rx="1" />
<rect x="15" y="16" width="6" height="5" rx="1" />
<path strokeLinecap="round" strokeLinejoin="round" d="M12 8v4m0 0H6v4m6-4h6v4" />
</svg>
),
},
{
number: '03',
title: 'Appliquer',
description: 'Travailler sur des projets réels dans un environnement collaboratif.',
// Icône SVG : Outil clé / mise en œuvre pratique
icon: (
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="m14.7 6.3 3 3-9.4 9.4H5.3v-3l9.4-9.4z" />
<path strokeLinecap="round" strokeLinejoin="round" d="m13 8 3 3" />
</svg>
),
},
{
number: '04',
title: 'Transformer',
description: 'Développer autonomie, compétences et stabilité.',
// Icône SVG : Pousse végétale / croissance durable
icon: (
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 21V11" />
<path strokeLinecap="round" strokeLinejoin="round" d="M12 11c0-4 4-7 8-7-1 4-4 7-8 7z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M12 13c0-3.5-3.5-6-7-6 .9 3.5 3.5 6 7 6z" />
</svg>
),
},
{
number: '05',
title: 'Connecter',
description: 'Relier humain, économie, territoires et opportunités.',
// Icône SVG : Maillons de chaîne / réseau
icon: (
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
<path strokeLinecap="round" strokeLinejoin="round" d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
</svg>
),
},
{
number: '06',
title: 'Régénérer',
description: 'Créer des boucles de valeur autonomes et durables.',
// Icône SVG : Flèches d'économie circulaire / régénération
icon: (
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" strokeWidth="2.2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M21 12a9 9 0 0 0-15.5-6.36L3 8" />
<path strokeLinecap="round" strokeLinejoin="round" d="M3 3v5h5" />
<path strokeLinecap="round" strokeLinejoin="round" d="M3 12a9 9 0 0 0 15.5 6.36L21 16" />
<path strokeLinecap="round" strokeLinejoin="round" d="M16 16h5v5" />
</svg>
),
},
];
/**
* 3. COMPOSANT PRINCIPAL : Methodology
* Présente la section "Comment ça fonctionne ?" en deux parties :
* - Haut : Titre + descriptif à gauche (4 cols) et les 6 étapes à droite (8 cols).
* - Bas : Bandeau beige avec statistiques clés et bouton d'action.
*/
export default function Methodology() {
return (
/*
Section englobante :
- py-20 : padding vertical de 5rem (80px)
- px-6 sm:px-12 : padding horizontal adaptatif
- bg-white : fond blanc assurant la continuité avec les autres blocs
*/
<section className="py-20 px-6 sm:px-12 bg-white">
{/* Conteneur centré borné à 1400px (largeur standard de la maquette Figma) */}
<div className="max-w-[1400px] mx-auto">
{/* ====================================================================
A. PARTIE SUPÉRIEURE : EN-TÊTE + GRILLE DES 6 ÉTAPES
- Grille CSS de 12 colonnes sur grand écran (lg:grid-cols-12)
- 1 colonne unique sur mobile / tablette
==================================================================== */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-10 items-start mb-16">
{/* COLONNE GAUCHE (4 colonnes sur 12) : Titres et texte explicatif */}
<div className="lg:col-span-4 pr-0 lg:pr-6">
{/* Surtitre vert olive (#798334) */}
<span className="text-[0.85rem] font-bold text-[#798334] tracking-wide block mb-2">
Notre méthodologie
</span>
{/* Titre principal */}
<h2 className="text-3xl sm:text-4xl font-extrabold text-slate-900 leading-tight mb-5">
Comment <br /> ça fonctionne ?
</h2>
{/* Sous-titre mis en avant */}
<h3 className="text-sm font-bold text-slate-900 mb-3">
Une méthodologie “Learning by Doing” validée sur plus de 20 ans.
</h3>
{/* Paragraphe descriptif avec largeur bridée pour une lecture confortable */}
<p className="text-xs sm:text-sm text-slate-500 leading-relaxed max-w-sm">
gaea21 transforme la recherche en action et les trajectoires humaines en capacités concrètes à travers une méthodologie systémique structurée.
</p>
</div>
{/* COLONNE DROITE (8 colonnes sur 12) : Les 6 étapes numérotées */}
{/*
Découpage responsive :
- Mobile : 2 colonnes (grid-cols-2)
- Tablette : 3 colonnes (sm:grid-cols-3)
- Desktop : 6 colonnes alignées (md:grid-cols-6)
*/}
<div className="lg:col-span-8 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-6 text-center">
{STEPS.map((step) => (
/*
group : permet à la bulle d'icône d'animer son zoom et sa couleur
dès que le curseur entre dans le bloc de l'étape
*/
<div key={step.number} className="flex flex-col items-center group">
{/* Numéro de l'étape (01, 02, etc.) */}
<span className="text-xs sm:text-sm font-semibold text-slate-400 mb-2">
{step.number}
</span>
{/* Bulle d'icône vert olive (#8c8f3e) avec transition fluide */}
<div className="w-16 h-16 rounded-full bg-[#8c8f3e] flex items-center justify-center mb-3 shadow-md group-hover:bg-[#737630] group-hover:scale-105 transition-all duration-200">
{step.icon}
</div>
{/* Titre de l'étape */}
<h4 className="text-sm font-bold text-slate-900 mb-1.5 leading-snug">
{step.title}
</h4>
{/* Description de l'étape */}
<p className="text-[0.68rem] sm:text-[0.72rem] text-slate-500 leading-snug">
{step.description}
</p>
</div>
))}
</div>
</div>
{/* ====================================================================
B. BANDEAU INFÉRIEUR : STATISTIQUES & BOUTON D'ACTION (CTA)
- bg-[#f7f8f0] : fond beige/vert très clair doux
- border-[#e8ebd5] : bordure fine d'encadrement
- rounded-2xl : coins arrondis
==================================================================== */}
<div className="bg-[#f7f8f0] border border-[#e8ebd5] rounded-2xl p-6 sm:p-8 flex flex-col lg:flex-row items-center justify-between gap-6 shadow-sm">
{/* Bloc Accroche textuelle à gauche */}
<div className="lg:w-1/4 text-center lg:text-left">
<h4 className="text-sm sm:text-base font-bold text-slate-900 leading-snug">
Une approche systémique et humaine
</h4>
<span className="text-xs sm:text-sm text-[#798334] font-medium block mt-0.5">
pour des résultats concrets et durables.
</span>
</div>
{/* Ligne séparatrice verticale visible uniquement sur desktop (lg) */}
<div className="hidden lg:block w-px h-12 bg-slate-300"></div>
{/* Statistique 1 : Ancienneté (20+ ans) */}
<div className="flex items-center gap-4">
{/* Icône cible / horloge */}
<div className="text-[#8c8f3e]">
<svg className="w-8 h-8" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="9" />
<path strokeLinecap="round" strokeLinejoin="round" d="M12 7v5l3 3" />
</svg>
</div>
<div>
<span className="text-xl sm:text-2xl font-black text-slate-900 block leading-tight">
20 + ans
</span>
<span className="text-xs text-slate-500 font-medium">
d'expérimentation
</span>
</div>
</div>
{/* Ligne séparatrice verticale visible uniquement sur desktop (lg) */}
<div className="hidden lg:block w-px h-12 bg-slate-300"></div>
{/* Statistique 2 : Nombre de bénéficiaires (3 000+ personnes) */}
<div className="flex items-center gap-4">
{/* Icône groupe / communauté */}
<div className="text-[#8c8f3e]">
<svg className="w-8 h-8" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path strokeLinecap="round" strokeLinejoin="round" d="M22 21v-2a4 4 0 0 0-3-3.87" />
<path strokeLinecap="round" strokeLinejoin="round" d="M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
</div>
<div>
<span className="text-xl sm:text-2xl font-black text-slate-900 block leading-tight">
3 000 +
</span>
<span className="text-xs text-[#798334] font-medium">
personnes accompagnées
</span>
</div>
</div>
{/* Bouton CTA d'ancrage vers le détail de la méthodologie */}
<div className="mt-2 lg:mt-0">
<a
href="#methodologie"
className="inline-flex items-center gap-2 bg-[#798334] hover:bg-[#656d2b] text-white text-xs sm:text-sm font-semibold px-6 py-3 rounded-full transition-all duration-200 shadow-sm hover:shadow-md transform hover:-translate-y-0.5"
>
<span>Découvrir la méthodologie</span>
{/* Flèche droite indicative */}
<svg className="w-4 h-4" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</a>
</div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,147 @@
// 1. IMPORTATIONS DES MODULES ET DES ASSETS
import React from 'react';
// Webpack Encore gère l'empreinte de hachage (hashing) et le chemin public des images
import individusImg from '../img/individus.png';
import entrepriseImg from '../img/entreprise.png';
import institutionsImg from '../img/institutions.png';
import coachImg from '../img/coach.png';
/**
* 2. STRUCTURE DE DONNÉES STATIQUE : LISTE DES PROFILS CIBLES
* Externaliser les données hors du composant évite de recréer
* ce tableau en mémoire à chaque nouveau rendu (re-render) de React.
*/
const PROFILES = [
{
id: 1,
title: 'Individus',
image: individusImg,
href: '#individus', // Ancre ou future route vers le parcours Individus
},
{
id: 2,
title: 'Entreprises',
image: entrepriseImg,
href: '#entreprises',
},
{
id: 3,
title: 'Institutions',
image: institutionsImg,
href: '#institutions',
},
{
id: 4,
// Le caractère d'échappement '\n' force un saut de ligne contrôlé dans le titre
title: 'Université /\nCentre de recherche',
// Fallback d'image en attendant l'asset spécifique de l'université
image: institutionsImg,
href: '#universite',
},
{
id: 5,
title: 'Coach ou\nPraticien',
image: coachImg,
href: '#coach',
},
];
/**
* 3. COMPOSANT PRINCIPAL : ProfileCards
* Affiche la section de choix de profil utilisateur (« Qui êtes-vous ? »)
* sous forme d'une grille interactive de cartes verticales avec effet glass/hover.
*/
export default function ProfileCards() {
return (
/*
Balise sémantique <section> :
- py-20 : grand espacement vertical (padding-top & padding-bottom)
- px-6 sm:px-12 : padding horizontal adaptatif selon la taille d'écran
- bg-white : fond blanc assurant la continuité visuelle
*/
<section className="py-20 px-6 sm:px-12 bg-white">
{/*
Conteneur centré borné à 1380px max (conforme au Figma)
mx-auto permet de centrer horizontalement le conteneur dans la page
*/}
<div className="max-w-[1380px] mx-auto text-center">
{/* =========================================================
A. EN-TÊTE DE SECTION (Surtitre + Titre principal)
========================================================= */}
{/* Surtitre en majuscules avec espacement étendu entre les lettres (tracking) */}
<span className="text-[0.72rem] tracking-[0.22em] font-bold text-slate-400 uppercase block mb-3">
QUI ÊTES-VOUS ?
</span>
{/* Titre principal centré avec largeur maximale contrôlée (max-w-xl) */}
<h2 className="text-xl sm:text-2xl md:text-3xl font-semibold text-slate-800 leading-snug max-w-xl mx-auto mb-14">
Chaque profil est une porte d’entrée <br />
vers un écosystème de transformation
</h2>
{/* =========================================================
B. GRILLE RESPONSIVE DES 5 CARTES
- 2 colonnes sur mobile (grid-cols-2)
- 3 colonnes sur tablette (sm:grid-cols-3)
- 5 colonnes alignées sur desktop (md:grid-cols-5)
- Gouttières (gap) dynamiques
========================================================= */}
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3.5 sm:gap-4 lg:gap-5 justify-center">
{PROFILES.map((profile) => (
/*
Chaque carte est un lien interactif cliquable <a> :
- key={profile.id} : clé unique obligatoire pour l'algorithme de réconciliation de React
- group : permet aux éléments enfants (image, overlay) de réagir au survol du parent
- relative + overflow-hidden : indispensable pour contraindre l'image zoomée à l'arrondi (rounded-xl)
- h-[...] : hauteurs fixes échelonnées par breakpoint pour garder un ratio portrait élancé
- transform hover:-translate-y-1.5 : translation douce vers le haut au survol pour donner du relief
*/
<a
key={profile.id}
href={profile.href}
className="group relative block w-full h-[360px] sm:h-[420px] md:h-[460px] rounded-xl overflow-hidden shadow-sm hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1.5"
>
{/*
CALQUE 1 (Fond) : Photographie du profil
- object-cover : l'image remplit tout le conteneur sans se déformer
- group-hover:scale-105 : léger zoom cinématographique quand la souris survole la carte
- transition-transform duration-500 : animation fluide du zoom
*/}
<img
src={profile.image}
alt={profile.title.replace('\n', ' ')} // Remplace le saut de ligne par un espace pour l'accessibilité SEO
className="w-full h-full object-cover object-center group-hover:scale-105 transition-transform duration-500 ease-out"
/>
{/*
CALQUE 2 (Intermédiaire) : Dégradé sombre pour la lisibilité
- bg-gradient-to-t : dégradé vertical partant du bas vers le haut
- from-black/85 via-black/30 to-transparent : foncé en bas pour le texte, transparent en haut
- pointer-events-none : empêche ce calque décoratif de bloquer les clics
*/}
<div className="absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-transparent pointer-events-none" />
{/*
CALQUE 3 (Premier plan) : Titre textuel de la carte
- absolute inset-x-0 bottom-0 : positionné en bas de la carte sur toute la largeur
- flex items-end justify-center : aligne le texte en bas et au centre
*/}
<div className="absolute inset-x-0 bottom-0 p-4 pb-6 flex items-end justify-center text-center">
{/*
- whitespace-pre-line : interprète les retours à la ligne '\n' définis dans le tableau PROFILES
- drop-shadow-md : ombre portée sur le texte pour garantir un contraste optimal
*/}
<span className="text-white text-base sm:text-lg font-bold leading-snug whitespace-pre-line tracking-tight drop-shadow-md">
{profile.title}
</span>
</div>
</a>
))}
</div>
</div>
</section>
);
}