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:
127
app/globals.css
127
app/globals.css
@@ -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); }
|
||||
|
||||
655
app/page.tsx
655
app/page.tsx
@@ -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">“{t.text}”</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">“{t.text}”</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 &<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'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'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">&</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'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'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'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>
|
||||
|
||||
@@ -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'urgence, appelez directement le{" "}
|
||||
<a href="tel:0674453089" className="text-orange font-bold">
|
||||
Benoît vous rappelle sous 24h. En cas d'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 & sans engagement — Réponse sous 24h
|
||||
</p>
|
||||
</form>
|
||||
|
||||
@@ -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 (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">
|
||||
© {new Date().getFullYear()} {name} — {dirigeant} · 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>
|
||||
© {new Date().getFullYear()} {name} — {dirigeant}{" "}
|
||||
· 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 · Mouchin · Douai · Valenciennes ·
|
||||
Saint-Amand-les-Eaux —{" "}
|
||||
<span className="text-white/30">Site réalisé par HookLab</span>
|
||||
<p className="text-center">
|
||||
Orchies · Mouchin · Douai · Valenciennes · Saint-Amand-les-Eaux
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user