feat: refonte UI éditoriale — style Messager + animations boutons

Navbar :
- Style éditorial : liens uppercase tracking-wide, underline slide au hover
- Bouton "Devis gratuit" avec animation slide-fill
- Active state par pathname

Page d'accueil :
- Hero split : texte gauche (clamp typography) + panneau diagonal brique droit
- Services : fond navy-light, cards avec bordure top orange, emoji + arrow animée
- Bande CTA : bg-orange full-width, btn-fill-white + btn-outline-light
- Réalisations : grid avec overlay slide-from-bottom au hover (rouge brique)
- Témoignages : dark navy, cards avec border-top
- Zone intervention : split 2 cols, pills uppercase
- Partenaires : grid 8 cols, grayscale → couleur au hover
- FAQ : split 2 cols, accordion avec icône + rotation
- Contact : split dark/light — infos gauche + formulaire droit

Globals.css :
- Classes .btn, .btn-fill, .btn-outline-dark/light, .btn-fill-white, .btn-arrow
- Animation slide-from-left via ::before pseudo-element
- .realisation-overlay (slide-up), .hero-diagonal-panel (clip-path)
- .nav-link (underline grow), .service-card-dark, .faq-icon

ContactForm + Footer : style éditorial squares, uppercase labels

https://claude.ai/code/session_01Uec4iHjcPwB1pU41idWEdF
This commit is contained in:
Claude
2026-02-27 13:36:10 +00:00
parent a133f195c2
commit 5493d6a660
5 changed files with 763 additions and 475 deletions

View File

@@ -358,3 +358,130 @@ html {
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* ================================================
BOUTONS — animations premium slide-fill
================================================ */
.btn {
position: relative;
overflow: hidden;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
font-weight: 700;
cursor: pointer;
text-decoration: none;
transition: transform 0.2s ease, box-shadow 0.3s ease;
}
.btn:active { transform: scale(0.97) !important; }
.btn > span, .btn > svg { position: relative; z-index: 1; }
/* Primaire rouge brique — slide depuis la gauche */
.btn-fill { background: var(--color-orange); color: #fff; }
.btn-fill::before {
content: ""; position: absolute; inset: 0;
background: var(--color-orange-hover);
transform: translateX(-101%);
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); z-index: 0;
}
.btn-fill:hover::before { transform: translateX(0); }
.btn-fill:hover { transform: translateY(-3px); box-shadow: 0 12px 32px rgba(139, 26, 26, 0.40); }
/* Outline dark — s'inverse en navy */
.btn-outline-dark { background: transparent; color: var(--color-navy); border: 2px solid var(--color-navy); }
.btn-outline-dark::before {
content: ""; position: absolute; inset: 0;
background: var(--color-navy);
transform: translateX(-101%);
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); z-index: 0;
}
.btn-outline-dark:hover::before { transform: translateX(0); }
.btn-outline-dark:hover { color: #fff; transform: translateY(-2px); }
.btn-outline-dark > span, .btn-outline-dark > svg { position: relative; z-index: 1; }
/* Outline light — pour fonds sombres */
.btn-outline-light { background: transparent; color: #fff; border: 2px solid rgba(255,255,255,0.35); }
.btn-outline-light::before {
content: ""; position: absolute; inset: 0;
background: rgba(255,255,255,0.12);
transform: translateX(-101%);
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); z-index: 0;
}
.btn-outline-light:hover::before { transform: translateX(0); }
.btn-outline-light:hover { border-color: rgba(255,255,255,0.65); transform: translateY(-2px); }
.btn-outline-light > span, .btn-outline-light > svg { position: relative; z-index: 1; }
/* Fill blanc — pour sections colorées */
.btn-fill-white { background: #fff; color: var(--color-navy); }
.btn-fill-white::before {
content: ""; position: absolute; inset: 0;
background: var(--color-bg-muted);
transform: translateX(-101%);
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); z-index: 0;
}
.btn-fill-white:hover::before { transform: translateX(0); }
.btn-fill-white:hover { transform: translateY(-3px); box-shadow: 0 10px 28px rgba(0,0,0,0.25); }
.btn-fill-white > span, .btn-fill-white > svg { position: relative; z-index: 1; }
/* Lien-flèche animé */
.btn-arrow { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 700; transition: color 0.2s ease; }
.btn-arrow .arrow-icon { display: inline-flex; transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1); }
.btn-arrow:hover .arrow-icon { transform: translateX(7px); }
/* ================================================
NAVBAR — underline animé
================================================ */
.nav-link { position: relative; }
.nav-link::after {
content: ""; position: absolute; bottom: -2px; left: 0;
width: 0; height: 2px; background: var(--color-orange);
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav-link:hover::after, .nav-link.active::after { width: 100%; }
/* ================================================
RÉALISATION CARD — slide-up overlay
================================================ */
.realisation-overlay {
transform: translateY(100%);
transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.realisation-card:hover .realisation-overlay { transform: translateY(0); }
/* ================================================
HERO — panneau diagonal brique
================================================ */
.hero-diagonal-panel {
position: absolute; top: 0; right: 0;
width: 44%; height: 100%;
background: var(--color-orange);
clip-path: polygon(14% 0%, 100% 0%, 100% 100%, 0% 100%);
opacity: 0.13;
pointer-events: none;
}
.hero-diagonal-border {
position: absolute; top: 0; right: 0;
width: 44%; height: 100%;
clip-path: polygon(14% 0%, 100% 0%, 100% 100%, 0% 100%);
border-left: 3px solid rgba(139,26,26,0.5);
pointer-events: none;
}
/* ================================================
SERVICE CARD — bordure top rouge brique
================================================ */
.service-card-dark {
border-top: 3px solid var(--color-orange);
transition: background 0.3s ease, transform 0.2s ease;
}
.service-card-dark:hover {
background: rgba(255,255,255,0.04);
transform: translateY(-4px);
}
/* ================================================
FAQ — icône rotation
================================================ */
.faq-icon { transition: transform 0.3s ease; }
details[open] .faq-icon { transform: rotate(45deg); }

View File

@@ -24,76 +24,90 @@ export async function generateMetadata(): Promise<Metadata> {
};
}
function ServiceCard({ service }: { service: Service }) {
const href = service.slug === "conseil" ? "/contact" : `/${service.slug}`;
/* ── Arrow SVG ── */
function Arrow({ className = "w-4 h-4" }: { className?: string }) {
return (
<Link
href={href}
className="group block bg-bg-white border border-border rounded-2xl p-6 hover:border-orange hover:shadow-lg transition-all duration-300 card-hover"
>
<div className="text-3xl mb-4">{service.icon}</div>
<h3 className="text-navy font-bold text-lg mb-2 group-hover:text-orange transition-colors">
{service.title}
</h3>
<p className="text-text-light text-sm leading-relaxed">{service.shortDescription}</p>
<div className="mt-4 flex items-center gap-1 text-orange text-sm font-semibold">
En savoir plus
<svg className="w-4 h-4 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
);
}
/* ── Service card — dark bg editorial ── */
function ServiceCard({ service, index }: { service: Service; index: number }) {
const href = service.slug === "conseil" ? "/contact" : `/${service.slug}`;
return (
<Link href={href} className="service-card-dark group block bg-white/[0.03] p-7 cursor-pointer">
<div className="text-3xl mb-5 grayscale group-hover:grayscale-0 transition-all duration-300">
{service.icon}
</div>
<h3 className="text-white font-black text-base uppercase tracking-wider mb-3 group-hover:text-orange transition-colors">
{service.title}
</h3>
<p className="text-white/50 text-sm leading-relaxed mb-6">{service.shortDescription}</p>
<span className="btn-arrow text-orange text-xs uppercase tracking-widest">
Découvrir
<span className="arrow-icon">
<Arrow className="w-3.5 h-3.5" />
</span>
</span>
</Link>
);
}
function StarRating({ note }: { note: number }) {
return (
<div className="flex gap-0.5">
{Array.from({ length: 5 }).map((_, i) => (
<svg key={i} className={`w-4 h-4 ${i < note ? "text-orange" : "text-border"}`} fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
))}
</div>
);
}
/* ── Testimonial card ── */
function TestimonialCard({ t }: { t: Testimonial }) {
const serviceLabel: Record<string, string> = {
"construction-maison": "Construction de maison",
"construction-maison": "Construction",
renovation: "Rénovation",
assainissement: "Assainissement",
"creation-acces": "Création d'accès",
demolition: "Démolition",
};
return (
<div className="bg-white/5 border border-white/10 rounded-2xl p-6 h-full flex flex-col">
<StarRating note={t.rating} />
<p className="text-white/80 text-sm leading-relaxed mt-4 flex-1 italic">&ldquo;{t.text}&rdquo;</p>
<div className="mt-5 pt-4 border-t border-white/10">
<p className="text-white font-semibold text-sm">{t.name}</p>
<p className="text-white/40 text-xs">{t.ville} {serviceLabel[t.service] ?? t.service}</p>
<div className="border-t border-white/15 pt-6 flex flex-col h-full">
<div className="flex gap-0.5 mb-4">
{Array.from({ length: 5 }).map((_, i) => (
<svg key={i} className={`w-3.5 h-3.5 ${i < t.rating ? "text-orange" : "text-white/15"}`} fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
))}
</div>
<p className="text-white/70 text-sm leading-relaxed flex-1 italic">&ldquo;{t.text}&rdquo;</p>
<div className="mt-5">
<p className="text-white font-bold text-sm">{t.name}</p>
<p className="text-white/35 text-xs uppercase tracking-wider mt-0.5">
{t.ville} {serviceLabel[t.service] ?? t.service}
</p>
</div>
</div>
);
}
function FAQAccordion({ item }: { item: FAQItem }) {
/* ── FAQ item ── */
function FAQItem({ item }: { item: FAQItem }) {
return (
<details className="group bg-bg-white border border-border rounded-2xl overflow-hidden">
<summary className="flex items-center justify-between px-6 py-4 cursor-pointer font-semibold text-navy hover:text-orange transition-colors list-none">
<details className="group border-b border-border last:border-0">
<summary className="flex items-center justify-between py-5 cursor-pointer list-none select-none">
<span className="text-navy font-bold text-sm pr-6 group-open:text-orange transition-colors">
{item.question}
<svg className="w-5 h-5 text-text-muted group-open:rotate-180 transition-transform shrink-0 ml-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</span>
<span className="faq-icon shrink-0 w-5 h-5 flex items-center justify-center text-orange">
<svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth={2.5} viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 5v14M5 12h14" />
</svg>
</span>
</summary>
<div className="px-6 pb-5 text-text-light text-sm leading-relaxed border-t border-border-light pt-4">
<div className="pb-5 text-text-light text-sm leading-relaxed -mt-1">
{item.answer}
</div>
</details>
);
}
/* ══════════════════════════════════════════════════
PAGE PRINCIPALE
══════════════════════════════════════════════════ */
export default async function HomePage() {
const [config, services, testimonials, faqItems, values, partners, realisations] =
await Promise.all([
@@ -106,217 +120,228 @@ export default async function HomePage() {
getRealisations(),
]);
const { hero, zones, zoneDescription, partnersTitle, partnersMessage, phone, phoneRaw } = config;
const { hero, zones, zoneDescription, phone, phoneRaw } = config;
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* ── SECTION 1 — HERO ── */}
<section className="relative bg-navy overflow-hidden texture-dark pt-20 pb-24 md:pt-28 md:pb-32">
{/* Accent diagonal brique */}
<div className="hero-accent" />
<div className="hero-accent-line" style={{ right: "30%" }} />
{/* ══ 1 — HERO ══ */}
<section className="relative bg-navy overflow-hidden texture-dark">
{/* Panneaux décoratifs diagonaux */}
<div className="hero-diagonal-panel" />
<div className="hero-accent-line" style={{ right: "44%" }} />
<div className="relative max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<div className="inline-flex items-center gap-2 bg-white/8 border border-white/15 rounded-full px-4 py-1.5 mb-8">
<span className="w-1.5 h-1.5 bg-orange rounded-full" />
<span className="text-white/70 text-xs font-medium tracking-wide uppercase">{hero.badge}</span>
</div>
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid lg:grid-cols-5 min-h-[92vh] items-center gap-0">
<h1 className="text-5xl md:text-7xl font-black text-white leading-none tracking-tight mb-6">
Maçon &amp;<br />
<span className="text-orange">Constructeur</span><br />
<span className="text-white/60 text-3xl md:text-4xl font-bold tracking-normal">dans le Nord (59)</span>
</h1>
<p className="text-white/60 text-base md:text-lg max-w-xl mx-auto mb-10 leading-relaxed">{hero.subtitle}</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/contact" className="inline-flex items-center justify-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-8 py-4 rounded-xl text-base transition-colors pulse-glow">
{hero.cta}
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
<Link href="/realisations" className="inline-flex items-center justify-center gap-2 bg-transparent hover:bg-white/8 text-white font-semibold px-8 py-4 rounded-xl text-base transition-colors border border-white/25">
{hero.ctaSecondary}
</Link>
</div>
<div className="mt-16 grid grid-cols-3 gap-8 max-w-md mx-auto border-t border-white/10 pt-10">
{hero.stats.map((s) => (
<div key={s.label} className="text-center">
<div className="text-3xl md:text-4xl font-black text-orange animate-stat-glow">{s.val}</div>
<div className="text-white/40 text-xs mt-1 uppercase tracking-wider">{s.label}</div>
</div>
))}
</div>
</div>
</section>
{/* ── SECTION 2 — NOS SERVICES ── */}
<section className="py-20 md:py-24 bg-bg">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="text-center mb-12">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Ce que nous faisons</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Nos services de maçonnerie</h2>
<p className="text-text-light mt-3 max-w-xl mx-auto">
De la construction neuve à la rénovation, Benoît Colin et son équipe prennent en charge tous vos travaux de gros œuvre dans le Nord.
</p>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{services.map((s, i) => (
<ScrollReveal key={s.slug} direction="up" delay={i * 80}>
<ServiceCard service={s} />
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ── SECTION 3 — POURQUOI CHOISIR OBC ── */}
<section className="py-20 md:py-24 bg-stone-bg">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="text-center mb-12">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Notre différence</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Pourquoi choisir OBC Maçonnerie ?</h2>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{values.map((v, i) => (
<ScrollReveal key={v.title} direction="up" delay={i * 100}>
<div className="bg-bg-white rounded-2xl p-6 border border-border text-center h-full">
<div className="text-4xl mb-4">{v.icon}</div>
<h3 className="text-navy font-bold text-lg mb-3">{v.title}</h3>
<p className="text-text-light text-sm leading-relaxed">{v.description}</p>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ── SECTION 4 — RÉSEAU PARTENAIRES ── */}
<section className="py-20 md:py-24 bg-bg">
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="text-center mb-12">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Un réseau solide</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">{partnersTitle}</h2>
<p className="text-text-light mt-4 max-w-xl mx-auto">{partnersMessage}</p>
</div>
</ScrollReveal>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
{partners.map((p, i) => (
<ScrollReveal key={p.label} direction="up" delay={i * 60}>
<div className="bg-bg-white border border-border rounded-xl p-4 text-center hover:border-orange hover:shadow-md transition-all">
<div className="text-2xl mb-2">{p.icon}</div>
<span className="text-navy font-semibold text-sm">{p.label}</span>
</div>
</ScrollReveal>
))}
</div>
<ScrollReveal direction="up" delay={200}>
<div className="mt-10 bg-navy rounded-2xl p-6 md:p-8 text-center">
<p className="text-white text-base md:text-lg font-medium">
Un seul interlocuteur pour coordonner l&apos;ensemble de votre projet de la démolition à la remise des clés.
</p>
<Link href="/partenaires" className="inline-flex items-center gap-2 mt-4 text-orange-light hover:text-white font-semibold transition-colors">
Découvrir notre réseau
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
</div>
</ScrollReveal>
</div>
</section>
{/* ── SECTION 5 — ZONE D'INTERVENTION ── */}
<section className="py-20 md:py-24 bg-stone-bg">
<div className="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<ScrollReveal direction="up">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Secteur d&apos;activité</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2 mb-4">Nous intervenons dans toute la région</h2>
<p className="text-text-light max-w-xl mx-auto mb-10">
OBC Maçonnerie intervient dans un rayon de {zoneDescription}.
</p>
</ScrollReveal>
<div className="flex flex-wrap justify-center gap-3 mb-8">
{zones.map((v, i) => (
<ScrollReveal key={v} direction="up" delay={i * 50}>
<span className="inline-flex items-center gap-1.5 bg-bg-white border border-border text-navy font-medium text-sm px-4 py-2 rounded-full hover:border-orange hover:shadow-sm transition-all">
<span className="text-orange">📍</span>{v}
{/* Contenu gauche */}
<div className="lg:col-span-3 py-20 md:py-28">
<div className="animate-hero-text-1">
<div className="flex items-center gap-3 mb-8">
<div className="w-8 h-px bg-orange" />
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em]">
{hero.badge}
</span>
</div>
<h1 className="text-[clamp(3rem,8vw,6.5rem)] font-black text-white leading-[0.9] tracking-tight uppercase mb-8">
Maçon<br />
<span className="text-orange">&amp;</span><br />
Construc<span className="text-white/30">teur</span>
</h1>
</div>
<p className="animate-hero-text-2 text-white/55 text-base md:text-lg max-w-md leading-relaxed mb-10">
{hero.subtitle}
</p>
<div className="animate-hero-text-3 flex flex-col sm:flex-row gap-4">
<Link
href="/contact"
className="btn btn-fill px-8 py-4 text-xs uppercase tracking-[0.2em]"
>
<span>{hero.cta}</span>
<span className="relative z-10"><Arrow className="w-4 h-4" /></span>
</Link>
<a
href={`tel:${phoneRaw}`}
className="btn btn-outline-light px-8 py-4 text-xs uppercase tracking-[0.2em]"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
<span>{phone}</span>
</a>
</div>
{/* Stats */}
<div className="mt-14 pt-10 border-t border-white/10 flex gap-10">
{hero.stats.map((s) => (
<div key={s.label}>
<div className="text-3xl md:text-4xl font-black text-white animate-stat-glow">
{s.val}
</div>
<div className="text-white/35 text-xs uppercase tracking-widest mt-1">{s.label}</div>
</div>
))}
</div>
</div>
{/* Panneau droit — stats éditoriales sur fond diagonal */}
<div className="hidden lg:flex lg:col-span-2 flex-col justify-center items-start pl-16 py-28 relative z-10">
<div className="space-y-8">
{values.slice(0, 3).map((v, i) => (
<div key={v.title} className={`flex items-start gap-4 animate-hero-text-${Math.min(i + 1, 3)}`}>
<div className="w-10 h-10 border border-orange/40 flex items-center justify-center shrink-0 text-lg">
{v.icon}
</div>
<div>
<p className="text-white font-bold text-sm uppercase tracking-wider">{v.title}</p>
<p className="text-white/40 text-xs mt-1 leading-relaxed max-w-[200px]">{v.description}</p>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</section>
{/* ══ 2 — NOS SERVICES (dark editorial) ══ */}
<section className="bg-navy-light py-20 md:py-24">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="flex items-end justify-between mb-12 gap-6 flex-wrap">
<div>
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-3">
Nos savoir-faire
</span>
<h2 className="text-white font-black text-3xl md:text-5xl uppercase leading-tight tracking-tight">
Nos<br />services
</h2>
</div>
<Link
href="/services"
className="btn-arrow text-white/50 hover:text-white text-xs uppercase tracking-widest"
>
Tous les services
<span className="arrow-icon"><Arrow className="w-3.5 h-3.5" /></span>
</Link>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-px bg-white/5">
{services.map((s, i) => (
<ScrollReveal key={s.slug} direction="up" delay={i * 60}>
<ServiceCard service={s} index={i} />
</ScrollReveal>
))}
</div>
<ScrollReveal direction="up" delay={100}>
<p className="text-text-light text-sm italic">
Et dans toutes les communes à {zoneDescription} contactez-nous pour vérifier votre zone.
</div>
</section>
{/* ══ 3 — BANDE CTA ══ */}
<section className="bg-orange py-16 md:py-20 relative overflow-hidden">
<div className="absolute inset-0 opacity-5" style={{
backgroundImage: "repeating-linear-gradient(45deg, #fff 0px, #fff 1px, transparent 1px, transparent 50%)",
backgroundSize: "20px 20px"
}} />
<div className="relative max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<ScrollReveal direction="up">
<p className="text-white/70 text-xs font-bold uppercase tracking-[0.3em] mb-4">
Devis gratuit Réponse sous 24h
</p>
<Link href="/contact" className="inline-flex items-center gap-2 mt-6 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors">
Demander un devis dans ma commune
<h2 className="text-white font-black text-3xl md:text-5xl uppercase leading-tight tracking-tight mb-8">
Votre projet mérite<br />
un artisan de confiance
</h2>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link
href="/contact"
className="btn btn-fill-white px-8 py-4 text-xs uppercase tracking-[0.2em]"
>
<span>Obtenir mon devis gratuit</span>
<span><Arrow className="w-4 h-4" /></span>
</Link>
<a
href={`tel:${phoneRaw}`}
className="btn btn-outline-light px-8 py-4 text-xs uppercase tracking-[0.2em]"
>
<span>Appeler Benoît {phone}</span>
</a>
</div>
</ScrollReveal>
</div>
</section>
{/* ── SECTION 6 — RÉALISATIONS ── */}
{/* ══ 4 — RÉALISATIONS ══ */}
<section className="py-20 md:py-24 bg-bg">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="text-center mb-12">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Nos chantiers</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Aperçu de nos réalisations</h2>
<div className="flex items-end justify-between mb-12 flex-wrap gap-6">
<div>
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-3">
Nos chantiers
</span>
<h2 className="text-navy font-black text-3xl md:text-5xl uppercase leading-tight tracking-tight">
Réalisations
</h2>
</div>
<Link
href="/realisations"
className="btn-arrow text-text-muted hover:text-navy text-xs uppercase tracking-widest"
>
Voir tout
<span className="arrow-icon"><Arrow className="w-3.5 h-3.5" /></span>
</Link>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{realisations.map((r, i) => {
const colors = ["bg-navy", "bg-stone", "bg-orange"];
const bgColors = ["bg-navy", "bg-stone", "bg-navy-light", "bg-stone", "bg-navy", "bg-navy-light"];
return (
<ScrollReveal key={r.title} direction="up" delay={i * 100}>
<div className="bg-bg-white rounded-2xl overflow-hidden border border-border hover:shadow-lg transition-all group card-hover">
<div className={`${colors[i % colors.length]} h-44 flex items-center justify-center`}>
<span className="text-white/20 text-8xl font-bold">{i + 1}</span>
<ScrollReveal key={r.title} direction="up" delay={i * 80}>
<div className="realisation-card relative overflow-hidden aspect-[4/3] cursor-pointer group">
{/* Fond placeholder */}
<div className={`${bgColors[i % bgColors.length]} w-full h-full flex items-center justify-center`}>
<span className="text-white/8 font-black text-9xl select-none">0{i + 1}</span>
</div>
<div className="p-5">
<span className="inline-block bg-bg-muted text-text-light text-xs font-semibold px-2 py-1 rounded-full mb-2">{r.ville}</span>
<h3 className="text-navy font-bold text-base mb-1 group-hover:text-orange transition-colors">{r.title}</h3>
<p className="text-text-light text-sm">{r.description}</p>
{/* Ligne orange bas (repos) */}
<div className="absolute bottom-0 left-0 w-full h-0.5 bg-orange/40 transition-all duration-500 group-hover:h-full group-hover:bg-orange/0" />
{/* Infos au repos */}
<div className="absolute bottom-0 left-0 right-0 p-5 bg-gradient-to-t from-black/70 to-transparent translate-y-0 group-hover:opacity-0 transition-opacity duration-300">
<span className="text-white/50 text-xs uppercase tracking-widest block mb-1">{r.ville}</span>
<h3 className="text-white font-black text-sm uppercase tracking-wide">{r.title}</h3>
</div>
{/* Overlay hover — slide from bottom */}
<div className="realisation-overlay absolute inset-0 bg-orange flex flex-col justify-end p-6">
<span className="text-white/60 text-xs uppercase tracking-[0.2em] mb-2">{r.ville}</span>
<h3 className="text-white font-black text-lg uppercase tracking-tight mb-2">{r.title}</h3>
<p className="text-white/70 text-xs leading-relaxed">{r.description}</p>
</div>
</div>
</ScrollReveal>
);
})}
</div>
<ScrollReveal direction="up" delay={150}>
<div className="text-center mt-8">
<Link href="/realisations" className="inline-flex items-center gap-2 border-2 border-navy text-navy hover:bg-navy hover:text-white font-bold px-7 py-3.5 rounded-xl transition-colors">
Voir toutes nos réalisations
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
</div>
</ScrollReveal>
</div>
</section>
{/* ── SECTION 7 — TÉMOIGNAGES ── */}
<section className="py-20 md:py-24 bg-navy">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
{/* ══ 5 — TÉMOIGNAGES (dark) ══ */}
<section className="py-20 md:py-24 bg-navy texture-dark">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="text-center mb-12">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Ce qu&apos;ils en disent</span>
<h2 className="text-3xl md:text-4xl font-bold text-white mt-2">Témoignages clients</h2>
<div className="mb-14">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-3">
Ce qu&apos;ils en disent
</span>
<h2 className="text-white font-black text-3xl md:text-5xl uppercase leading-tight tracking-tight">
Clients<br />satisfaits
</h2>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12">
{testimonials.map((t, i) => (
<ScrollReveal key={t.name} direction="up" delay={i * 100}>
<TestimonialCard t={t} />
@@ -326,39 +351,199 @@ export default async function HomePage() {
</div>
</section>
{/* ── SECTION 8 — FAQ ── */}
{/* ══ 6 — ZONE D'INTERVENTION ══ */}
<section className="py-20 md:py-24 bg-bg">
<div className="max-w-3xl mx-auto px-4 sm:px-6">
<ScrollReveal direction="up">
<div className="text-center mb-12">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Questions fréquentes</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">FAQ</h2>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid lg:grid-cols-2 gap-16 items-center">
<ScrollReveal direction="left">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Secteur d&apos;activité
</span>
<h2 className="text-navy font-black text-3xl md:text-5xl uppercase leading-tight tracking-tight mb-6">
On intervient<br />dans tout le Nord
</h2>
<p className="text-text-light text-sm leading-relaxed mb-8 max-w-md">
OBC Maçonnerie rayonne sur {zoneDescription} autour de Mouchin de Douai à Valenciennes, Orchies et Saint-Amand-les-Eaux.
</p>
<Link
href="/contact"
className="btn btn-outline-dark px-7 py-3.5 text-xs uppercase tracking-widest"
>
<span>Vérifier ma commune</span>
<span><Arrow className="w-4 h-4" /></span>
</Link>
</ScrollReveal>
<ScrollReveal direction="right">
<div className="flex flex-wrap gap-2">
{zones.map((v, i) => (
<span
key={v}
className="inline-flex items-center gap-1.5 bg-bg-white border border-border text-navy font-bold text-xs px-4 py-2.5 uppercase tracking-wider hover:border-orange hover:text-orange transition-all duration-200 cursor-default"
>
<span className="w-1.5 h-1.5 rounded-full bg-orange shrink-0" />
{v}
</span>
))}
<span className="inline-flex items-center gap-1.5 bg-orange/10 border border-orange/30 text-orange font-bold text-xs px-4 py-2.5 uppercase tracking-wider">
+ communes voisines
</span>
</div>
</ScrollReveal>
<div className="space-y-4">
{faqItems.map((f, i) => (
<ScrollReveal key={f.question} direction="up" delay={i * 60}>
<FAQAccordion item={f} />
</div>
</div>
</section>
{/* ══ 7 — PARTENAIRES ══ */}
<section className="py-16 bg-stone-bg border-t border-border">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-6 mb-10">
<div>
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-2">
Un réseau solide
</span>
<h2 className="text-navy font-black text-xl md:text-2xl uppercase tracking-tight">
Nos partenaires
</h2>
</div>
<Link
href="/partenaires"
className="btn-arrow text-text-muted hover:text-navy text-xs uppercase tracking-widest"
>
Découvrir le réseau
<span className="arrow-icon"><Arrow className="w-3.5 h-3.5" /></span>
</Link>
</div>
</ScrollReveal>
<div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-3">
{partners.map((p, i) => (
<ScrollReveal key={p.label} direction="up" delay={i * 40}>
<div className="group bg-bg-white border border-border p-4 text-center hover:border-orange transition-all duration-200 cursor-default">
<div className="text-xl mb-2 grayscale group-hover:grayscale-0 transition-all">{p.icon}</div>
<span className="text-navy font-bold text-xs uppercase tracking-wide group-hover:text-orange transition-colors">
{p.label}
</span>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ── SECTION 9 — FORMULAIRE DE CONTACT ── */}
<section className="py-20 md:py-24 bg-stone-bg" id="contact">
<div className="max-w-2xl mx-auto px-4 sm:px-6">
<ScrollReveal direction="up">
<div className="text-center mb-10">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Devis gratuit</span>
<h2 className="text-3xl md:text-4xl font-bold text-navy mt-2">Parlez-nous de votre projet</h2>
<p className="text-text-light mt-3">
Réponse sous 24h ou appelez directement Benoît au{" "}
<a href={`tel:${phoneRaw}`} className="text-orange font-bold hover:underline">{phone}</a>
{/* ══ 8 — FAQ ══ */}
<section className="py-20 md:py-24 bg-bg">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid lg:grid-cols-2 gap-16">
<ScrollReveal direction="left">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Questions fréquentes
</span>
<h2 className="text-navy font-black text-3xl md:text-5xl uppercase leading-tight tracking-tight mb-6">
On répond<br />à vos questions
</h2>
<p className="text-text-light text-sm leading-relaxed mb-8 max-w-sm">
Pas de réponse à votre question ? Appelez directement Benoît au{" "}
<a href={`tel:${phoneRaw}`} className="text-orange font-bold hover:underline">{phone}</a>.
</p>
<Link
href="/contact"
className="btn btn-fill px-7 py-3.5 text-xs uppercase tracking-widest"
>
<span>Poser ma question</span>
<span><Arrow className="w-4 h-4" /></span>
</Link>
</ScrollReveal>
<ScrollReveal direction="right">
<div>
{faqItems.map((f) => (
<FAQItem key={f.question} item={f} />
))}
</div>
</ScrollReveal>
<ScrollReveal direction="up" delay={100}>
</div>
</div>
</section>
{/* ══ 9 — CONTACT SPLIT ══ */}
<section id="contact" className="grid lg:grid-cols-2">
{/* Gauche — dark info */}
<div className="bg-navy texture-dark py-16 md:py-20 px-8 md:px-12 lg:px-16 relative overflow-hidden">
<div className="hero-accent" />
<ScrollReveal direction="left">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Contactez-nous
</span>
<h2 className="text-white font-black text-3xl md:text-4xl uppercase leading-tight tracking-tight mb-8">
Parlons de<br />votre projet
</h2>
<div className="space-y-6 mb-10">
<a href={`tel:${phoneRaw}`} className="flex items-center gap-4 group">
<div className="w-11 h-11 border border-orange/40 flex items-center justify-center shrink-0 group-hover:border-orange group-hover:bg-orange/10 transition-all">
<svg className="w-5 h-5 text-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</div>
<div>
<p className="text-white/40 text-xs uppercase tracking-widest mb-0.5">Téléphone</p>
<p className="text-white font-bold text-lg group-hover:text-orange transition-colors">{phone}</p>
</div>
</a>
<div className="flex items-center gap-4">
<div className="w-11 h-11 border border-white/15 flex items-center justify-center shrink-0">
<svg className="w-5 h-5 text-white/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<div>
<p className="text-white/40 text-xs uppercase tracking-widest mb-0.5">Siège</p>
<p className="text-white/70 text-sm">{config.address}</p>
</div>
</div>
<div className="flex items-center gap-4">
<div className="w-11 h-11 border border-white/15 flex items-center justify-center shrink-0">
<svg className="w-5 h-5 text-white/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<div>
<p className="text-white/40 text-xs uppercase tracking-widest mb-0.5">Délai de réponse</p>
<p className="text-white/70 text-sm">Sous 24h Du lundi au samedi</p>
</div>
</div>
</div>
{/* Garanties */}
<div className="border-t border-white/10 pt-8 grid grid-cols-2 gap-4">
{[
{ val: "Gratuit", label: "Devis" },
{ val: "24h", label: "Réponse" },
{ val: "10+", label: "Ans d'expérience" },
{ val: "100%", label: "Satisfaction" },
].map((s) => (
<div key={s.label}>
<div className="text-2xl font-black text-orange">{s.val}</div>
<div className="text-white/35 text-xs uppercase tracking-wider mt-0.5">{s.label}</div>
</div>
))}
</div>
</ScrollReveal>
</div>
{/* Droite — formulaire */}
<div className="bg-stone-bg py-16 md:py-20 px-8 md:px-12 lg:px-16">
<ScrollReveal direction="right">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Devis gratuit
</span>
<h3 className="text-navy font-black text-2xl uppercase tracking-tight mb-8">
Votre demande
</h3>
<ContactForm />
</ScrollReveal>
</div>

View File

@@ -57,165 +57,95 @@ export default function ContactForm() {
if (status === "success") {
return (
<div className="bg-bg-white border border-success rounded-2xl p-8 text-center">
<div className="text-4xl mb-4"></div>
<h3 className="text-navy font-bold text-xl mb-2">Demande envoyée !</h3>
<div className="border-t-4 border-success bg-bg-white p-8 text-center">
<div className="w-12 h-12 bg-success/10 flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 className="text-navy font-black text-lg uppercase tracking-wide mb-2">Demande envoyée !</h3>
<p className="text-text-light text-sm">
Benoît vous rappellera dans les 24h. En cas d&apos;urgence, appelez directement le{" "}
<a href="tel:0674453089" className="text-orange font-bold">
Benoît vous rappelle sous 24h. En cas d&apos;urgence :{" "}
<a href="tel:0674453089" className="text-orange font-bold hover:underline">
06 74 45 30 89
</a>
.
</p>
</div>
);
}
const ic = "w-full border border-border bg-bg-white px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors";
const lc = "block text-xs font-bold uppercase tracking-widest text-navy mb-2";
return (
<form
onSubmit={handleSubmit}
className="bg-bg-white border border-border rounded-2xl p-6 md:p-8 space-y-4"
>
<form onSubmit={handleSubmit} className="space-y-5">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label htmlFor="nom" className="block text-sm font-semibold text-navy mb-1">
Nom <span className="text-orange">*</span>
</label>
<input
id="nom"
name="nom"
type="text"
value={form.nom}
onChange={handleChange}
placeholder="Votre nom"
className="w-full border border-border rounded-xl px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors bg-bg"
required
/>
<label htmlFor="nom" className={lc}>Nom <span className="text-orange">*</span></label>
<input id="nom" name="nom" type="text" value={form.nom} onChange={handleChange}
placeholder="Votre nom" className={ic} required />
</div>
<div>
<label htmlFor="telephone" className="block text-sm font-semibold text-navy mb-1">
Téléphone <span className="text-orange">*</span>
</label>
<input
id="telephone"
name="telephone"
type="tel"
value={form.telephone}
onChange={handleChange}
placeholder="06 XX XX XX XX"
className="w-full border border-border rounded-xl px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors bg-bg"
required
/>
<label htmlFor="telephone" className={lc}>Téléphone <span className="text-orange">*</span></label>
<input id="telephone" name="telephone" type="tel" value={form.telephone} onChange={handleChange}
placeholder="06 XX XX XX XX" className={ic} required />
</div>
</div>
<div>
<label htmlFor="email" className="block text-sm font-semibold text-navy mb-1">
Email
</label>
<input
id="email"
name="email"
type="email"
value={form.email}
onChange={handleChange}
placeholder="votre@email.fr"
className="w-full border border-border rounded-xl px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors bg-bg"
/>
<label htmlFor="email" className={lc}>Email</label>
<input id="email" name="email" type="email" value={form.email} onChange={handleChange}
placeholder="votre@email.fr" className={ic} />
</div>
<div>
<label htmlFor="typeProjet" className="block text-sm font-semibold text-navy mb-1">
Type de projet <span className="text-orange">*</span>
</label>
<select
id="typeProjet"
name="typeProjet"
value={form.typeProjet}
onChange={handleChange}
className="w-full border border-border rounded-xl px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors bg-bg"
required
>
<label htmlFor="typeProjet" className={lc}>Type de projet <span className="text-orange">*</span></label>
<select id="typeProjet" name="typeProjet" value={form.typeProjet} onChange={handleChange}
className={ic} required>
<option value="">Choisissez un type de projet</option>
{typesProjets.map((t) => (
<option key={t} value={t}>
{t}
</option>
))}
{typesProjets.map((t) => <option key={t} value={t}>{t}</option>)}
</select>
</div>
<div>
<label htmlFor="description" className="block text-sm font-semibold text-navy mb-1">
Description du projet
</label>
<textarea
id="description"
name="description"
value={form.description}
onChange={handleChange}
rows={4}
placeholder="Décrivez votre projet : surface, localisation, contraintes particulières..."
className="w-full border border-border rounded-xl px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors bg-bg resize-none"
/>
<label htmlFor="description" className={lc}>Description du projet</label>
<textarea id="description" name="description" value={form.description} onChange={handleChange}
rows={4} placeholder="Surface, localisation, contraintes particulières..."
className={`${ic} resize-none`} />
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label htmlFor="budget" className="block text-sm font-semibold text-navy mb-1">
Budget approximatif
</label>
<input
id="budget"
name="budget"
type="text"
value={form.budget}
onChange={handleChange}
placeholder="ex : 80 000 €"
className="w-full border border-border rounded-xl px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors bg-bg"
/>
<label htmlFor="budget" className={lc}>Budget approximatif</label>
<input id="budget" name="budget" type="text" value={form.budget} onChange={handleChange}
placeholder="ex : 80 000 €" className={ic} />
</div>
<div>
<label htmlFor="zone" className="block text-sm font-semibold text-navy mb-1">
Commune / Zone
</label>
<input
id="zone"
name="zone"
type="text"
value={form.zone}
onChange={handleChange}
placeholder="ex : Orchies, Douai..."
className="w-full border border-border rounded-xl px-4 py-3 text-sm text-text focus:outline-none focus:border-orange transition-colors bg-bg"
/>
<label htmlFor="zone" className={lc}>Commune / Zone</label>
<input id="zone" name="zone" type="text" value={form.zone} onChange={handleChange}
placeholder="ex : Orchies, Douai..." className={ic} />
</div>
</div>
{error && (
<p className="text-error text-sm bg-stone-bg border border-error/30 rounded-xl px-4 py-3">
<p className="text-error text-xs bg-stone-bg border border-error/30 px-4 py-3 uppercase tracking-wide">
{error}
</p>
)}
<button
type="submit"
disabled={status === "sending"}
className="w-full bg-orange hover:bg-orange-hover text-white font-bold py-4 rounded-xl transition-colors disabled:opacity-60 disabled:cursor-not-allowed text-base"
>
{status === "sending" ? "Envoi en cours..." : "Envoyer ma demande de devis"}
<button type="submit" disabled={status === "sending"}
className="btn btn-fill w-full py-4 text-xs uppercase tracking-[0.2em] disabled:opacity-60 disabled:cursor-not-allowed">
<span>{status === "sending" ? "Envoi en cours..." : "Envoyer ma demande"}</span>
</button>
{status === "error" && (
<p className="text-error text-sm text-center">
Une erreur est survenue. Appelez directement le{" "}
<a href="tel:0674453089" className="font-bold underline">
06 74 45 30 89
</a>
.
<p className="text-error text-xs text-center">
Erreur. Appelez le{" "}
<a href="tel:0674453089" className="font-bold underline">06 74 45 30 89</a>.
</p>
)}
<p className="text-text-muted text-xs text-center">
<p className="text-text-muted text-xs text-center uppercase tracking-widest">
Devis gratuit &amp; sans engagement Réponse sous 24h
</p>
</form>

View File

@@ -15,47 +15,50 @@ export default function Footer() {
} = siteConfig;
return (
<footer className="bg-navy text-white pt-12 pb-6">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8 pb-10 border-b border-white/10">
<footer className="bg-navy-dark text-white">
{/* Top band */}
<div className="border-b border-white/8">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
<div className="grid grid-cols-1 md:grid-cols-12 gap-10 md:gap-8">
{/* Brand */}
<div className="md:col-span-2">
<div className="flex items-center gap-2.5 mb-4">
<div className="w-10 h-10 bg-orange rounded-lg flex items-center justify-center shrink-0">
<span className="text-white font-bold text-xs">OBC</span>
<div className="md:col-span-4">
<div className="flex items-center gap-3 mb-6">
<div className="w-11 h-11 bg-orange flex items-center justify-center shrink-0">
<span className="text-white font-black text-xs tracking-wider">OBC</span>
</div>
<div className="flex flex-col leading-tight">
<span className="text-white font-bold text-base leading-none">OBC</span>
<span className="text-orange-light font-bold text-base leading-none">Maçonnerie</span>
<div className="flex flex-col leading-none gap-0.5">
<span className="text-white font-black text-sm tracking-[0.18em] uppercase">OBC</span>
<span className="text-orange-light font-bold text-xs tracking-[0.18em] uppercase">Maçonnerie</span>
</div>
</div>
<p className="text-white/70 text-sm leading-relaxed mb-4 max-w-xs">
{dirigeant}, maçon expert en construction de maison, rénovation et gros
œuvre dans le Nord. De la première pierre à la remise des clés.
<p className="text-white/50 text-sm leading-relaxed mb-6 max-w-xs">
{dirigeant}, maçon expert en construction de maison, rénovation et gros œuvre dans le Nord&nbsp;(59).
</p>
<a
href={`tel:${phoneRaw}`}
className="inline-flex items-center gap-2 text-orange-light font-bold text-base hover:text-white transition-colors"
className="inline-flex items-center gap-2 text-orange font-bold text-base hover:text-white transition-colors group"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="w-4 h-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
{phone}
</a>
<p className="text-white/40 text-xs mt-2">{address}</p>
<p className="text-white/30 text-xs mt-2">{address}</p>
</div>
{/* Services */}
<div>
<h4 className="text-white font-semibold text-sm mb-4 uppercase tracking-wide">
<div className="md:col-span-3">
<h4 className="text-white font-black text-xs uppercase tracking-[0.2em] mb-5 flex items-center gap-3">
<span className="w-4 h-px bg-orange" />
Services
</h4>
<ul className="space-y-2">
<ul className="space-y-2.5">
{footerServicesNav.map((item) => (
<li key={item.href}>
<Link
href={item.href}
className="text-white/60 hover:text-white text-sm transition-colors"
className="text-white/45 hover:text-white text-sm transition-colors hover:translate-x-1 inline-block duration-200"
>
{item.label}
</Link>
@@ -64,51 +67,64 @@ export default function Footer() {
</ul>
</div>
{/* Navigation + Légal */}
<div>
<h4 className="text-white font-semibold text-sm mb-4 uppercase tracking-wide">
{/* Navigation */}
<div className="md:col-span-2">
<h4 className="text-white font-black text-xs uppercase tracking-[0.2em] mb-5 flex items-center gap-3">
<span className="w-4 h-px bg-orange" />
Navigation
</h4>
<ul className="space-y-2">
<ul className="space-y-2.5">
{footerMainNav.map((item) => (
<li key={item.href}>
<Link
href={item.href}
className="text-white/60 hover:text-white text-sm transition-colors"
className="text-white/45 hover:text-white text-sm transition-colors hover:translate-x-1 inline-block duration-200"
>
{item.label}
</Link>
</li>
))}
</ul>
<h4 className="text-white font-semibold text-sm mb-3 mt-5 uppercase tracking-wide">
</div>
{/* Légal + CTA */}
<div className="md:col-span-3">
<h4 className="text-white font-black text-xs uppercase tracking-[0.2em] mb-5 flex items-center gap-3">
<span className="w-4 h-px bg-orange" />
Légal
</h4>
<ul className="space-y-2">
<ul className="space-y-2.5 mb-8">
{footerLegalNav.map((item) => (
<li key={item.href}>
<Link
href={item.href}
className="text-white/60 hover:text-white text-sm transition-colors"
className="text-white/45 hover:text-white text-sm transition-colors hover:translate-x-1 inline-block duration-200"
>
{item.label}
</Link>
</li>
))}
</ul>
<Link
href="/contact"
className="btn btn-fill text-xs uppercase tracking-widest px-5 py-3 w-full justify-center"
>
<span>Devis gratuit</span>
</Link>
</div>
</div>
</div>
</div>
{/* Bottom */}
<div className="pt-6 flex flex-col md:flex-row items-center justify-between gap-3">
<p className="text-white/40 text-xs text-center md:text-left">
&copy; {new Date().getFullYear()} {name} &mdash; {dirigeant} &middot; SIREN{" "}
{siren.replace(/(\d{3})(\d{3})(\d{3})/, "$1 $2 $3")}
{/* Bottom bar */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-5">
<div className="flex flex-col md:flex-row items-center justify-between gap-3 text-white/25 text-xs">
<p>
&copy; {new Date().getFullYear()} {name} &mdash; {dirigeant}{" "}
&middot; SIREN {siren.replace(/(\d{3})(\d{3})(\d{3})/, "$1 $2 $3")}
</p>
<p className="text-white/40 text-xs text-center md:text-right">
Orchies &middot; Mouchin &middot; Douai &middot; Valenciennes &middot;
Saint-Amand-les-Eaux &mdash;{" "}
<span className="text-white/30">Site réalisé par HookLab</span>
<p className="text-center">
Orchies &middot; Mouchin &middot; Douai &middot; Valenciennes &middot; Saint-Amand-les-Eaux
</p>
</div>
</div>

View File

@@ -2,100 +2,130 @@
import { useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { siteConfig } from "@/lib/site-config";
export default function Navbar() {
const [open, setOpen] = useState(false);
const { name, phone, phoneRaw, nav } = siteConfig;
const { phone, phoneRaw, nav } = siteConfig;
const pathname = usePathname();
return (
<nav
className="sticky top-0 z-50 bg-bg-white/95 backdrop-blur-md border-b border-border"
className="sticky top-0 z-50 bg-bg-white border-b border-border"
role="navigation"
aria-label="Navigation principale"
>
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16 md:h-[68px]">
{/* Logo */}
<Link href="/" className="flex items-center gap-2.5" aria-label={`${name} - Accueil`}>
<div className="w-9 h-9 bg-navy rounded-lg flex items-center justify-center shrink-0">
<span className="text-white font-bold text-xs">OBC</span>
<Link href="/" className="flex items-center gap-3 shrink-0" aria-label="OBC Maçonnerie — Accueil">
<div className="w-10 h-10 bg-navy flex items-center justify-center shrink-0">
<span className="text-white font-black text-xs tracking-wider">OBC</span>
</div>
<div className="flex flex-col leading-tight">
<span className="text-navy font-bold text-sm leading-none">OBC</span>
<span className="text-orange font-bold text-sm leading-none">Maçonnerie</span>
<div className="flex flex-col leading-none gap-0.5">
<span className="text-navy font-black text-sm tracking-[0.18em] uppercase">OBC</span>
<span className="text-orange font-bold text-xs tracking-[0.18em] uppercase">Maçonnerie</span>
</div>
</Link>
{/* Desktop links */}
<div className="hidden md:flex items-center gap-6">
{nav.map((link) => (
{/* Desktop nav links */}
<div className="hidden lg:flex items-center gap-7">
{nav.map((link) => {
const active = pathname === link.href || (link.href !== "/" && pathname.startsWith(link.href));
return (
<Link
key={link.href}
href={link.href}
className="text-text-light hover:text-navy text-sm font-medium transition-colors"
className={`nav-link text-xs font-bold uppercase tracking-[0.14em] transition-colors pb-0.5 ${
active ? "text-orange active" : "text-text-light hover:text-navy"
}`}
>
{link.label}
</Link>
))}
);
})}
</div>
{/* CTA desktop */}
<div className="hidden md:block">
{/* Right — phone + CTA */}
<div className="hidden lg:flex items-center gap-5">
<a
href={`tel:${phoneRaw}`}
className="inline-flex items-center gap-2 bg-orange text-white font-bold text-sm px-5 py-2.5 rounded-xl hover:bg-orange-hover transition-colors"
className="flex items-center gap-2 text-navy font-bold text-sm hover:text-orange transition-colors"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="w-4 h-4 text-orange shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
{phone}
</a>
<Link
href="/contact"
className="btn btn-fill text-xs uppercase tracking-widest px-5 py-2.5"
>
<span>Devis gratuit</span>
</Link>
</div>
{/* Mobile menu button */}
{/* Mobile burger */}
<button
className="md:hidden p-2 text-text-light hover:text-navy transition-colors cursor-pointer"
className="lg:hidden p-2 text-navy transition-colors"
onClick={() => setOpen(!open)}
aria-label={open ? "Fermer le menu" : "Ouvrir le menu"}
aria-expanded={open}
>
{open ? (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
) : (
<div className={`w-5 space-y-1.5 transition-all ${open ? "opacity-0 scale-75" : ""}`}>
{!open ? (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
) : (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
)}
</div>
</button>
</div>
{/* Mobile menu */}
{open && (
<div className="md:hidden border-t border-border py-4 space-y-1">
{nav.map((link) => (
<div className="lg:hidden border-t border-border pb-5">
<div className="pt-2">
{nav.map((link) => {
const active = pathname === link.href || (link.href !== "/" && pathname.startsWith(link.href));
return (
<Link
key={link.href}
href={link.href}
onClick={() => setOpen(false)}
className="block text-text-light hover:text-navy text-sm font-medium py-2.5 px-2 rounded-lg hover:bg-bg-muted transition-colors"
className={`flex items-center justify-between text-xs font-bold uppercase tracking-widest py-3.5 px-1 border-b border-border-light transition-colors ${
active ? "text-orange" : "text-text-light hover:text-navy"
}`}
>
{link.label}
{active && <span className="w-1.5 h-1.5 rounded-full bg-orange" />}
</Link>
))}
<div className="pt-2">
);
})}
</div>
<div className="mt-5 flex flex-col gap-3">
<a
href={`tel:${phoneRaw}`}
onClick={() => setOpen(false)}
className="flex items-center justify-center gap-2 bg-orange text-white font-bold text-sm px-5 py-3 rounded-xl mt-2"
className="flex items-center gap-2 text-navy font-bold text-sm"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="w-4 h-4 text-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
{phone}
</a>
<Link
href="/contact"
onClick={() => setOpen(false)}
className="btn btn-fill text-xs uppercase tracking-widest px-5 py-3.5 w-full justify-center"
>
<span>Demander un devis gratuit</span>
</Link>
</div>
</div>
)}