Fix params/searchParams types for Next.js app router

This commit is contained in:
Enguerrand Ozano
2026-02-28 20:00:04 +01:00
parent 7651a45586
commit a48468ae45
28 changed files with 23 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
import { NextResponse } from "next/server";
export const runtime = "nodejs";
export async function POST(request: Request) {
try {
const body = await request.json();
const { nom, telephone, email, typeProjet, description, budget, zone } = body as {
nom?: string;
telephone?: string;
email?: string;
typeProjet?: string;
description?: string;
budget?: string;
zone?: string;
};
if (!nom || !telephone || !typeProjet) {
return NextResponse.json(
{ error: "Nom, téléphone et type de projet sont requis." },
{ status: 400 }
);
}
if (!process.env.RESEND_API_KEY) {
// Pas de clé API — on log simplement et on retourne succès
console.log("Nouvelle demande devis OBC Maçonnerie:", { nom, telephone, email, typeProjet, zone });
return NextResponse.json({ success: true }, { status: 200 });
}
const { Resend } = await import("resend");
const resend = new Resend(process.env.RESEND_API_KEY);
const fromEmail = process.env.RESEND_FROM_EMAIL || "OBC Maçonnerie <contact@obc-maconnerie.fr>";
const adminEmail = process.env.ADMIN_EMAIL || "contact@obc-maconnerie.fr";
await resend.emails.send({
from: fromEmail,
to: adminEmail,
subject: `Nouvelle demande de devis — ${nom} (${typeProjet})`,
html: `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="margin:0;padding:0;background:#f4f4f5;font-family:Arial,Helvetica,sans-serif;">
<div style="max-width:560px;margin:0 auto;padding:32px 16px;">
<div style="background:#ffffff;border-radius:16px;padding:32px;border:1px solid #e4e4e7;">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:24px;">
<div style="width:40px;height:40px;background:#1B2A4A;border-radius:8px;display:flex;align-items:center;justify-content:center;">
<span style="color:#E8772E;font-weight:bold;font-size:11px;">OBC</span>
</div>
<h2 style="margin:0;color:#111827;font-size:18px;">Nouvelle demande de devis</h2>
</div>
<table style="width:100%;border-collapse:collapse;">
<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;width:40%;">Nom</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;font-weight:600;">${nom}</td>
</tr>
<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;">Téléphone</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#E8772E;font-size:14px;font-weight:600;">${telephone}</td>
</tr>
${email ? `<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;">Email</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;">${email}</td>
</tr>` : ""}
<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;">Type de projet</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;font-weight:600;">${typeProjet}</td>
</tr>
${zone ? `<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;">Zone</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;">${zone}</td>
</tr>` : ""}
${budget ? `<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;">Budget</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;">${budget}</td>
</tr>` : ""}
${description ? `<tr>
<td style="padding:10px 0;color:#6b7280;font-size:14px;vertical-align:top;">Description</td>
<td style="padding:10px 0;color:#111827;font-size:14px;">${description}</td>
</tr>` : ""}
</table>
<p style="margin:24px 0 0 0;color:#9ca3af;font-size:12px;">Reçu le ${new Date().toLocaleDateString("fr-FR", { day: "2-digit", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit" })}</p>
</div>
</div>
</body>
</html>
`,
});
return NextResponse.json({ success: true }, { status: 200 });
} catch (err) {
console.error("Erreur API contact OBC:", err);
return NextResponse.json(
{ error: "Erreur serveur. Appelez le 06 74 45 30 89." },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,91 @@
import { NextResponse } from "next/server";
export const runtime = "nodejs";
export async function POST(request: Request) {
try {
const body = await request.json();
const { name, phone, ville, description, projectType } = body as {
name?: string;
phone?: string;
ville?: string;
description?: string;
projectType?: string;
};
if (!name || !phone || !ville || !projectType) {
return NextResponse.json(
{ error: "Les champs nom, téléphone, ville et type de projet sont requis." },
{ status: 400 }
);
}
if (!process.env.RESEND_API_KEY) {
return NextResponse.json(
{ error: "Service email non configuré." },
{ status: 500 }
);
}
const { Resend } = await import("resend");
const resend = new Resend(process.env.RESEND_API_KEY);
const fromEmail =
process.env.RESEND_FROM_EMAIL || "HookLab <onboarding@resend.dev>";
const adminEmail = process.env.ADMIN_EMAIL || "enguerrandbusiness@outlook.com";
await resend.emails.send({
from: fromEmail,
to: adminEmail,
subject: `Nouvelle demande de devis - ${projectType} (${ville})`,
html: `
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="margin:0;padding:0;background:#f4f4f5;font-family:Arial,Helvetica,sans-serif;">
<div style="max-width:560px;margin:0 auto;padding:32px 16px;">
<div style="background:#ffffff;border-radius:16px;padding:32px;border:1px solid #e4e4e7;">
<h2 style="margin:0 0 24px 0;color:#111827;font-size:20px;">Nouvelle demande de devis maçonnerie</h2>
<table style="width:100%;border-collapse:collapse;">
<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;width:40%;">Nom</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;font-weight:600;">${name}</td>
</tr>
<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;">Téléphone</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;font-weight:600;">${phone}</td>
</tr>
<tr>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:14px;">Type de projet</td>
<td style="padding:10px 0;border-bottom:1px solid #f3f4f6;color:#111827;font-size:14px;font-weight:600;">${projectType}</td>
</tr>
<tr>
<td style="padding:10px 0;${description ? "border-bottom:1px solid #f3f4f6;" : ""}color:#6b7280;font-size:14px;">Ville</td>
<td style="padding:10px 0;${description ? "border-bottom:1px solid #f3f4f6;" : ""}color:#111827;font-size:14px;font-weight:600;">${ville}</td>
</tr>
${
description
? `<tr>
<td style="padding:10px 0;vertical-align:top;color:#6b7280;font-size:14px;">Description</td>
<td style="padding:10px 0;color:#111827;font-size:14px;">${description}</td>
</tr>`
: ""
}
</table>
<p style="margin:24px 0 0 0;color:#6b7280;font-size:13px;">Reçu le ${new Date().toLocaleDateString("fr-FR", { day: "2-digit", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit" })}</p>
</div>
</div>
</body>
</html>
`,
});
return NextResponse.json({ success: true }, { status: 200 });
} catch (err) {
console.error("Erreur API devis:", err);
return NextResponse.json(
{ error: "Erreur serveur. Veuillez réessayer." },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,51 @@
import type { Metadata } from "next";
import ServicePageLayout from "@/components/marketing/ServicePageLayout";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Assainissement Maison Nord 59 | OBC Maçonnerie",
description:
"Création et mise aux normes de systèmes d'assainissement dans le Nord (59). OBC Maçonnerie intervient à Orchies, Douai, Valenciennes et alentours. Devis gratuit.",
keywords: ["assainissement maison Nord", "assainissement individuel Nord 59", "fosse septique Nord", "mise aux normes assainissement"],
alternates: { canonical: `${config.url}/assainissement` },
};
}
const items = [
{ icon: "🔍", title: "Diagnostic", desc: "Analyse de votre installation existante et vérification de sa conformité aux normes en vigueur." },
{ icon: "🏗️", title: "Création de fosse", desc: "Installation d'une fosse toutes eaux ou d'une micro-station d'épuration adaptée à votre terrain." },
{ icon: "🌱", title: "Épandage", desc: "Création ou réhabilitation du dispositif d'épandage pour un traitement optimal des eaux usées." },
{ icon: "🔧", title: "Réhabilitation", desc: "Remise en état d'une installation vieillissante ou non conforme pour éviter les sanctions." },
{ icon: "📋", title: "Mise aux normes", desc: "Mise en conformité suite à un contrôle SPANC ou en cas de vente immobilière." },
{ icon: "💧", title: "Raccordement réseau", desc: "Connexion au réseau d'assainissement collectif lorsque celui-ci est disponible." },
];
export default async function AssainissementPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return (
<ServicePageLayout
label="Assainissement"
title="Assainissement dans le Nord"
subtitle="Mise aux normes, création ou réhabilitation de votre système d'assainissement — OBC Maçonnerie intervient dans les règles de l'art."
phone={phone}
phoneRaw={phoneRaw}
items={items}
itemsSectionTitle="Nos prestations assainissement"
seoTitle="Assainissement dans le Nord (59)"
seoText={
<>
<p>
OBC Maçonnerie réalise vos travaux d&apos;<strong className="text-text">assainissement non collectif dans le Nord</strong> fosse toutes eaux, micro-station, épandage, réhabilitation. Benoît Colin vous accompagne de l&apos;étude de votre terrain jusqu&apos;à la réception des travaux.
</p>
<p>
Que vous ayez besoin d&apos;une <strong className="text-text">mise aux normes suite à un contrôle SPANC</strong>, d&apos;une nouvelle installation pour une construction neuve ou d&apos;une réhabilitation de l&apos;existant, OBC Maçonnerie intervient à Orchies, Douai, Valenciennes, Mouchin et dans toutes les communes avoisinantes.
</p>
</>
}
contactTitle="Votre projet d'assainissement"
/>
);
}

View File

@@ -0,0 +1,174 @@
import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import { getBlogPost, getBlogPosts, getSiteConfig } from "@/lib/content";
type Props = { params: Promise<{ slug: string }> };
// Corps des articles — FUTURE: champ rich text Payload CMS
const articleContenu: Record<string, string[]> = {
"combien-coute-construction-maison-nord": [
"La construction d'une maison individuelle dans le Nord représente un investissement significatif. En 2025, le coût moyen se situe entre 1 200 € et 1 800 € par m² hors terrain et hors raccordements, selon les matériaux choisis et la complexité du projet.",
"**Le prix du terrain** est souvent la première variable. Dans le secteur d'Orchies et de Mouchin, comptez entre 50 000 € et 120 000 € pour une parcelle constructible de 400 à 600 m².",
"**Le gros œuvre** (fondations, murs, dalle, toiture) représente environ 40 à 50% du budget total. C'est là qu'intervient OBC Maçonnerie avec son savoir-faire et son réseau de partenaires pour optimiser les coûts sans sacrifier la qualité.",
"**Les finitions et corps de métier** (électricité, plomberie, chauffage, isolation, menuiserie, carrelage, peinture) représentent les 50 à 60% restants.",
"Pour un projet de 100 m² habitable à Orchies ou Douai, un budget total entre 180 000 € et 280 000 € (hors terrain) est réaliste selon le niveau de finition souhaité.",
"Le meilleur conseil : contactez Benoît Colin pour une évaluation gratuite de votre projet. Il vous donnera une estimation précise adaptée à votre terrain et vos envies.",
],
"etapes-renovation-maison-ancienne": [
"Rénover une maison ancienne dans le Nord demande une méthodologie rigoureuse. Voici les grandes étapes pour mener votre projet à bien.",
"**1. Le diagnostic** : Avant tout, il faut évaluer l'état du bâtiment. Charpente, toiture, murs porteurs, fondations, réseaux électriques et plomberie — tout doit être passé en revue. Un maçon expérimenté comme Benoît Colin peut repérer les problèmes invisibles à l'œil nu.",
"**2. La démolition et le curage** : On enlève ce qui est vétuste ou inadapté — cloisons obsolètes, chapes abîmées, enduits défaillants — pour repartir sur de bonnes bases.",
"**3. Le gros œuvre** : Reprises de fondations si nécessaire, traitement des murs humides, création d'ouvertures, modification de la structure. C'est le cœur du métier d'OBC Maçonnerie.",
"**4. Les corps de métier** : Électricité, plomberie, chauffage, isolation. Grâce à son réseau de partenaires, Benoît coordonne chaque intervention dans le bon ordre.",
"**5. Les finitions** : Menuiseries, carrelage, peinture, revêtements de sol. La touche finale qui donne tout son caractère à votre maison rénovée.",
"Chaque rénovation est unique. Contactez OBC Maçonnerie pour une évaluation gratuite de votre projet.",
],
"assainissement-non-collectif-obligations": [
"En France, environ 5 millions de logements sont équipés d'un assainissement non collectif (ANC). Si votre maison n'est pas raccordée au réseau public, vous êtes soumis à des obligations précises.",
"**Le contrôle SPANC** : Le Service Public d'Assainissement Non Collectif peut contrôler votre installation. En cas de non-conformité, vous avez en principe 4 ans pour mettre aux normes, ou moins si vous vendez le bien.",
"**La vente immobilière** : Depuis 2011, un diagnostic d'assainissement est obligatoire lors de toute vente. S'il révèle une non-conformité, l'acheteur doit réaliser les travaux dans l'année suivant l'acte de vente.",
"**Les principales normes** : Votre installation doit traiter correctement les eaux usées avant rejet dans le sol. Les normes imposent une fosse toutes eaux (ou une micro-station) et un dispositif d'épandage adapté à la surface disponible.",
"**OBC Maçonnerie vous accompagne** dans la mise aux normes ou la création de votre système d'assainissement non collectif. Nous intervenons sur Orchies, Douai, Valenciennes et toute la zone.",
],
"ossature-bois-avantages": [
"La construction en ossature bois connaît un vrai succès dans le Nord. Et pour cause : ce mode constructif présente de nombreux avantages techniques et économiques.",
"**Performance thermique** : Le bois est un excellent isolant naturel. Une construction ossature bois bien conçue atteint facilement les exigences RE2020.",
"**Rapidité de chantier** : Les éléments préfabriqués permettent de monter les murs en quelques jours. Le clos-couvert est obtenu très rapidement.",
"**Légèreté** : L'ossature bois pèse 5 à 8 fois moins qu'une construction maçonnée, ce qui allège les fondations — un avantage sur les terrains argileux fréquents dans le Nord.",
"**Polyvalence architecturale** : L'ossature bois permet des formes architecturales variées, des larges baies vitrées et une grande liberté de conception.",
"**La combinaison idéale** : OBC Maçonnerie maîtrise la construction ossature bois et la maçonnerie traditionnelle. Benoît vous conseille sur la solution la plus adaptée à votre terrain et vos envies.",
],
"travaux-renovation-sans-permis-construction": [
"Avant de démarrer des travaux de rénovation, il est important de savoir si vous avez besoin d'une autorisation administrative.",
"**Aucune démarche requise** : Les travaux purement intérieurs (peinture, revêtements, redistribution de cloisons non porteuses, remplacement de fenêtres à l'identique) ne nécessitent généralement aucune démarche.",
"**Déclaration préalable** : Pour les extensions jusqu'à 40 m² (en zone urbaine PLU), les changements de façade, les travaux modifiant l'aspect extérieur.",
"**Permis de construire** : Obligatoire pour les extensions de plus de 40 m², la création d'une surface de plancher supérieure à 20 m² en dehors des zones PLU, ou les changements de destination.",
"**Cas des zones protégées** : Si votre maison est en zone ABF (Architecte des Bâtiments de France), les règles sont plus strictes. Renseignez-vous en mairie.",
"En cas de doute, OBC Maçonnerie vous accompagne dans vos démarches administratives. Benoît connaît bien les règles locales dans le secteur d'Orchies, Douai et Valenciennes.",
],
"fondations-maison-quels-types": [
"Les fondations sont la base de toute construction. Mal dimensionnées ou inadaptées au sol, elles peuvent entraîner des désordres graves. Voici les principaux types.",
"**Les semelles filantes** : Le type le plus courant pour les maisons individuelles. Elles répartissent les charges des murs porteurs sur une bande de terrain. Adaptées aux sols stables et homogènes.",
"**Le radier** : Une dalle béton armé qui couvre toute la surface de la maison. Recommandé sur les terrains argileux, instables ou avec présence d'eau. Fréquent dans certains secteurs du Nord.",
"**Les pieux** : Utilisés quand le sol de surface est insuffisant pour porter la maison. Des pieux sont enfoncés jusqu'à une couche de sol plus résistante.",
"**L'étude de sol** : Avant toute construction, une étude géotechnique (étude de sol) est vivement recommandée — et même obligatoire dans certains cas (zones argileuses). Elle permet de choisir le bon type de fondations.",
"OBC Maçonnerie réalise vos fondations avec rigueur, après analyse du terrain. Benoît vous conseille sur la solution la plus adaptée à votre projet dans le Nord.",
],
};
export async function generateStaticParams() {
// FUTURE: utilise getBlogPosts() pour générer les slugs depuis Payload CMS
const posts = await getBlogPosts();
return posts.map((p) => ({ slug: p.slug }));
}
export async function generateMetadata(
{ params }: Props
): Promise<Metadata> {
const { slug } = await params;
const [post, config] = await Promise.all([getBlogPost(slug), getSiteConfig()]);
if (!post) return { title: "Article introuvable" };
return {
title: post.titre,
description: post.extrait,
alternates: { canonical: `${config.url}/blog/${slug}` },
};
}
export default async function BlogArticlePage({ params }: Props) {
const { slug } = await params;
const post = await getBlogPost(slug);
if (!post) notFound();
const contenu = articleContenu[slug] ?? [];
return (
<main id="main-content" className="min-h-screen">
<Navbar />
<section className="bg-navy py-12 md:py-16">
<div className="max-w-3xl mx-auto px-4 sm:px-6">
<ScrollReveal direction="up">
<Link
href="/blog"
className="inline-flex items-center gap-1.5 text-white/50 hover:text-white text-sm mb-6 transition-colors"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Retour au blog
</Link>
<div className="flex items-center gap-3 mb-4">
<span className="bg-orange/20 text-orange text-xs font-semibold px-2.5 py-1 rounded-full">
{post.cat}
</span>
<span className="text-white/40 text-xs">{post.date}</span>
<span className="text-white/40 text-xs">· {post.readTime} de lecture</span>
</div>
<h1 className="text-2xl md:text-4xl font-bold text-white leading-tight">
{post.titre}
</h1>
</ScrollReveal>
</div>
</section>
<section className="py-12 md:py-16 bg-bg">
<div className="max-w-3xl mx-auto px-4 sm:px-6">
<ScrollReveal direction="up">
<div className="bg-bg-white border border-border rounded-2xl p-6 md:p-10">
<div className="flex items-center gap-3 mb-8 pb-6 border-b border-border">
<div className="w-10 h-10 bg-navy rounded-full flex items-center justify-center shrink-0">
<span className="text-white font-bold text-xs">OBC</span>
</div>
<div>
<p className="text-navy font-semibold text-sm">Benoît Colin</p>
<p className="text-text-muted text-xs">OBC Maçonnerie Mouchin (59)</p>
</div>
</div>
<div className="space-y-5 text-text leading-relaxed">
{contenu.map((para, i) => (
<p key={i} className="text-base text-text-light">
{para.split(/(\*\*[^*]+\*\*)/).map((part, j) => {
if (part.startsWith("**") && part.endsWith("**")) {
return (
<strong key={j} className="text-navy font-semibold">
{part.slice(2, -2)}
</strong>
);
}
return part;
})}
</p>
))}
</div>
<div className="mt-10 pt-8 border-t border-border">
<div className="bg-stone-bg rounded-xl p-5 flex flex-col sm:flex-row items-center gap-4">
<div className="flex-1">
<p className="text-navy font-bold mb-1">Vous avez un projet ?</p>
<p className="text-text-light text-sm">
Benoît se déplace gratuitement pour évaluer votre chantier et vous donner un devis précis.
</p>
</div>
<Link
href="/contact"
className="shrink-0 bg-orange hover:bg-orange-hover text-white font-bold px-5 py-2.5 rounded-xl text-sm transition-colors"
>
Devis gratuit
</Link>
</div>
</div>
</div>
</ScrollReveal>
</div>
</section>
<Footer />
</main>
);
}

116
app/(app)/blog/page.tsx Normal file
View File

@@ -0,0 +1,116 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import { getBlogPosts, getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Blog Maçonnerie & Construction | Conseils OBC Maçonnerie",
description:
"Conseils, guides et actualités sur la construction de maison, la rénovation et le gros œuvre dans le Nord (59). Blog OBC Maçonnerie par Benoît Colin.",
alternates: { canonical: `${config.url}/blog` },
};
}
const cats = ["Tous", "Construction", "Rénovation", "Assainissement"];
export default async function BlogPage() {
const articles = await getBlogPosts();
return (
<main id="main-content" className="min-h-screen">
<Navbar />
<section className="bg-navy py-16 md:py-20">
<div className="max-w-4xl mx-auto px-4 sm:px-6 text-center">
<ScrollReveal direction="up">
<span className="text-orange text-sm font-semibold uppercase tracking-widest">Conseils & guides</span>
<h1 className="text-3xl md:text-5xl font-bold text-white mt-2 mb-4">Blog OBC Maçonnerie</h1>
<p className="text-white/70 text-lg max-w-xl mx-auto">
Construction, rénovation, assainissement Benoît partage son expertise pour vous aider dans vos projets.
</p>
</ScrollReveal>
</div>
</section>
{/* Filtres */}
<section className="py-6 bg-bg border-b border-border">
<div className="max-w-5xl mx-auto px-4 sm:px-6">
<div className="flex flex-wrap gap-2 justify-center">
{cats.map((cat) => (
<span
key={cat}
className={`px-4 py-2 rounded-full text-sm font-medium cursor-default ${
cat === "Tous"
? "bg-navy text-white"
: "bg-bg-white border border-border text-text-light"
}`}
>
{cat}
</span>
))}
</div>
</div>
</section>
{/* Articles */}
<section className="py-16 md:py-20 bg-bg">
<div className="max-w-5xl mx-auto px-4 sm:px-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{articles.map((a, i) => (
<ScrollReveal key={a.slug} direction="up" delay={i * 70}>
<Link
href={`/blog/${a.slug}`}
className="group block bg-bg-white border border-border rounded-2xl overflow-hidden hover:border-orange hover:shadow-lg transition-all card-hover"
>
<div className="bg-navy h-32 flex items-center justify-center">
<span className="text-orange font-bold text-4xl">0{i + 1}</span>
</div>
<div className="p-5">
<div className="flex items-center gap-2 mb-3">
<span className="bg-orange/10 text-orange text-xs font-semibold px-2.5 py-1 rounded-full">
{a.cat}
</span>
<span className="text-text-muted text-xs">{a.readTime} de lecture</span>
</div>
<h2 className="text-navy font-bold text-base mb-2 leading-snug group-hover:text-orange transition-colors">
{a.titre}
</h2>
<p className="text-text-light text-sm leading-relaxed line-clamp-2">{a.extrait}</p>
<div className="mt-4 flex items-center justify-between">
<span className="text-text-muted text-xs">{a.date}</span>
<span className="text-orange text-xs font-semibold">Lire </span>
</div>
</div>
</Link>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* CTA */}
<section className="py-14 bg-stone-bg">
<div className="max-w-2xl mx-auto px-4 text-center">
<ScrollReveal direction="up">
<h2 className="text-2xl font-bold text-navy mb-3">Un projet en tête ?</h2>
<p className="text-text-light text-sm mb-6">
Benoît vous conseille gratuitement et vous remet un devis sous 24h.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 bg-orange hover:bg-orange-hover text-white font-bold px-7 py-3.5 rounded-xl transition-colors"
>
Demander un devis gratuit
</Link>
</ScrollReveal>
</div>
</section>
<Footer />
</main>
);
}

111
app/(app)/cgv/page.tsx Normal file
View File

@@ -0,0 +1,111 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
export const metadata: Metadata = {
title: "Conditions Générales de Vente | OBC Maçonnerie",
description:
"Conditions générales de vente d'OBC Maçonnerie — Benoît Colin, maçon à Mouchin (59310). Prestations de construction, rénovation et gros œuvre.",
alternates: { canonical: "https://obc-maconnerie.fr/cgv" },
robots: { index: false, follow: false },
};
export default function CGV() {
return (
<main id="main-content" className="min-h-screen bg-bg">
<Navbar />
<div className="max-w-3xl mx-auto px-4 sm:px-6 py-16 md:py-20">
<Link href="/" className="inline-flex items-center gap-2 mb-8 text-text-light hover:text-navy text-sm transition-colors group">
<svg className="w-4 h-4 transition-transform group-hover:-translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Retour à l&apos;accueil
</Link>
<h1 className="text-3xl md:text-4xl font-bold text-navy mb-10">Conditions Générales de Vente</h1>
<div className="space-y-8 text-text-light text-sm leading-relaxed">
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 1 Objet</h2>
<p>
Les présentes Conditions Générales de Vente (CGV) régissent les prestations de travaux de maçonnerie, construction, rénovation, assainissement, création d&apos;accès et démolition proposées par <strong className="text-text">OBC Maçonnerie</strong>, entreprise individuelle dirigée par Benoît COLIN, SIREN 531 827 871, dont le siège est situé au 221 Route de Saint-Amand, 59310 Mouchin.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 2 Devis et commandes</h2>
<p>
Toute prestation fait l&apos;objet d&apos;un devis préalable gratuit. Le devis est établi après visite du chantier. Il est valable 30 jours à compter de sa date d&apos;émission. La signature du devis par le client vaut acceptation des présentes CGV et commande ferme.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 3 Prix et paiement</h2>
<p>
Les prix sont indiqués hors taxes ou TTC selon le régime fiscal applicable. Un acompte de 30% peut être demandé à la commande, le solde étant payable à la réception des travaux. En cas de retard de paiement, des pénalités de retard seront appliquées conformément aux dispositions légales.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 4 Délais d&apos;exécution</h2>
<p>
Les délais d&apos;exécution sont communiqués à titre indicatif dans le devis. OBC Maçonnerie s&apos;engage à respecter les délais convenus sauf cas de force majeure, conditions météorologiques défavorables ou retard imputable au client ou à des tiers.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 5 Garanties</h2>
<p>
OBC Maçonnerie est couvert par les garanties légales applicables aux travaux de construction :
</p>
<ul className="mt-3 space-y-1 list-disc list-inside">
<li><strong className="text-text">Garantie décennale</strong> : couvre les dommages compromettant la solidité de l&apos;ouvrage pendant 10 ans.</li>
<li><strong className="text-text">Garantie biennale</strong> : couvre les éléments d&apos;équipement dissociables pendant 2 ans.</li>
<li><strong className="text-text">Garantie de parfait achèvement</strong> : couvre les défauts signalés à la réception pendant 1 an.</li>
</ul>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 6 Responsabilité</h2>
<p>
OBC Maçonnerie est assuré en responsabilité civile professionnelle et décennale. La responsabilité d&apos;OBC Maçonnerie ne saurait être engagée pour des dommages résultant d&apos;une utilisation non conforme des ouvrages réalisés ou d&apos;une intervention de tiers après réception.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 7 Réception des travaux</h2>
<p>
La réception des travaux est prononcée contradictoirement entre OBC Maçonnerie et le client. Elle fait l&apos;objet d&apos;un procès-verbal. Les réserves éventuelles y sont consignées et levées dans les délais convenus.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 8 Données personnelles</h2>
<p>
Les données personnelles collectées sont traitées conformément à notre{" "}
<Link href="/confidentialite" className="text-orange hover:underline">
politique de confidentialité
</Link>.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">Article 9 Droit applicable et litiges</h2>
<p>
Les présentes CGV sont soumises au droit français. En cas de litige, une solution amiable sera recherchée en priorité. À défaut, le tribunal compétent sera celui de Valenciennes.
</p>
</section>
<p className="text-text-muted text-xs pt-4 border-t border-border">
Dernière mise à jour : Février 2026
</p>
</div>
</div>
<Footer />
</main>
);
}

View File

@@ -0,0 +1,133 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import { siteConfig } from "@/lib/site-config";
export const metadata: Metadata = {
title: "Politique de Confidentialité | OBC Maçonnerie",
description:
"Politique de confidentialité et protection des données personnelles du site OBC Maçonnerie, conformément au RGPD.",
alternates: { canonical: "https://obc-maconnerie.fr/confidentialite" },
robots: { index: false, follow: false },
};
export default function Confidentialite() {
return (
<main id="main-content" className="min-h-screen bg-bg">
<Navbar />
<div className="max-w-3xl mx-auto px-4 sm:px-6 py-16 md:py-20">
<Link href="/" className="inline-flex items-center gap-2 mb-8 text-text-light hover:text-navy text-sm transition-colors group">
<svg className="w-4 h-4 transition-transform group-hover:-translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Retour à l&apos;accueil
</Link>
<h1 className="text-3xl md:text-4xl font-bold text-navy mb-10">Politique de Confidentialité</h1>
<div className="space-y-8 text-text-light text-sm leading-relaxed">
<section>
<h2 className="text-lg font-bold text-navy mb-3">1. Responsable du traitement</h2>
<div className="bg-bg-white border border-border rounded-xl p-5 space-y-1">
<p><strong className="text-text">Benoît COLIN OBC Maçonnerie</strong></p>
<p>SIREN : 531 827 871</p>
<p>221 Route de Saint-Amand, 59310 Mouchin</p>
<p>Tél : <a href={`tel:${siteConfig.phoneRaw}`} className="text-orange hover:underline">{siteConfig.phone}</a></p>
<p>Email : <a href="mailto:contact@obc-maconnerie.fr" className="text-orange hover:underline">contact@obc-maconnerie.fr</a></p>
</div>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">2. Données collectées</h2>
<p>Nous collectons uniquement les données que vous nous transmettez via le formulaire de contact :</p>
<ul className="mt-3 space-y-1 list-disc list-inside ml-2">
<li>Nom et prénom</li>
<li>Numéro de téléphone</li>
<li>Adresse email (optionnelle)</li>
<li>Type de projet et description</li>
<li>Budget approximatif (optionnel)</li>
<li>Commune / zone d&apos;intervention</li>
</ul>
<p className="mt-3">
<strong className="text-text">Aucune donnée bancaire</strong> n&apos;est collectée sur ce site.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">3. Finalités du traitement</h2>
<p>Vos données sont utilisées exclusivement pour :</p>
<ul className="mt-3 space-y-1 list-disc list-inside ml-2">
<li>Répondre à votre demande de devis et vous recontacter</li>
<li>Préparer et établir un devis adapté à votre projet</li>
<li>Gérer la relation commerciale si vous confiez votre chantier à OBC Maçonnerie</li>
</ul>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">4. Base légale</h2>
<ul className="space-y-1 list-disc list-inside ml-2">
<li><strong className="text-text">Consentement :</strong> Pour les données transmises via le formulaire de contact.</li>
<li><strong className="text-text">Exécution du contrat :</strong> Pour les données nécessaires à la réalisation du chantier.</li>
<li><strong className="text-text">Obligation légale :</strong> Pour la conservation des documents comptables (10 ans).</li>
</ul>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">5. Partage des données</h2>
<p>
Vos données ne sont jamais vendues à des tiers. Elles peuvent être transmises uniquement aux prestataires techniques nécessaires au fonctionnement du site :
</p>
<ul className="mt-3 space-y-1 list-disc list-inside ml-2">
<li><strong className="text-text">Vercel :</strong> Hébergement du site web</li>
<li><strong className="text-text">Resend :</strong> Envoi des emails de notification</li>
</ul>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">6. Durée de conservation</h2>
<ul className="space-y-1 list-disc list-inside ml-2">
<li><strong className="text-text">Prospects :</strong> 3 ans après le dernier contact</li>
<li><strong className="text-text">Clients :</strong> 5 ans après la fin de la relation contractuelle</li>
<li><strong className="text-text">Documents comptables :</strong> 10 ans (obligation légale)</li>
</ul>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">7. Vos droits (RGPD)</h2>
<p>
Conformément au RGPD, vous disposez d&apos;un droit d&apos;accès, de rectification, d&apos;effacement et de portabilité de vos données. Pour exercer ces droits, contactez-nous :
</p>
<p className="mt-3">
<a href="mailto:contact@obc-maconnerie.fr" className="text-orange font-semibold hover:underline">
contact@obc-maconnerie.fr
</a>
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">8. Cookies</h2>
<p>
Ce site utilise uniquement des cookies techniques nécessaires à son bon fonctionnement. Aucun cookie publicitaire ou de traçage tiers n&apos;est utilisé.
</p>
</section>
<section>
<h2 className="text-lg font-bold text-navy mb-3">9. Sécurité</h2>
<p>
Toutes les communications entre votre navigateur et notre site sont chiffrées via le protocole HTTPS. Vos données sont traitées avec le plus grand soin et ne sont accessibles qu&apos;aux personnes habilitées.
</p>
</section>
<p className="text-text-muted text-xs pt-4 border-t border-border">
Dernière mise à jour : Février 2026
</p>
</div>
</div>
<Footer />
</main>
);
}

View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Maçon Douai | Construction & Rénovation | OBC Maçonnerie",
description:
"OBC Maçonnerie intervient à Douai pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["construction maison Douai", "maçon Douai", "rénovation Douai", "gros oeuvre Douai", "maçon rénovation Douai"],
alternates: { canonical: "https://obc-maconnerie.fr/construction-maison-douai" },
};
export default function ConstructionMaisonDouaiPage() {
return (
<LocalSEOPage
ville="Douai"
departement="Nord (59)"
servicesPrincipaux={["Construction de maison", "Rénovation"]}
description="Construction de maison et rénovation à Douai — OBC Maçonnerie intervient dans toute la commune et ses alentours."
texteIntro="Votre projet de construction ou de rénovation à Douai mérite un maçon de confiance. OBC Maçonnerie intervient dans toute l'agglomération douaisienne avec rigueur et professionnalisme."
texteLocal={`OBC Maçonnerie intervient à Douai et dans toute son agglomération pour vos travaux de maçonnerie. Que vous souhaitiez construire une maison neuve, rénover un bien existant ou réaliser des travaux d'assainissement, Benoît Colin est à votre disposition.\n\nDouai est une ville que nous connaissons bien, avec ses spécificités : maisons de ville à rénover, terrains en zone urbaine, règles d'urbanisme particulières. Notre expérience locale vous garantit un projet réalisé dans les règles de l'art et dans les délais.\n\nN'hésitez pas à contacter OBC Maçonnerie pour un devis gratuit à Douai. Benoît se déplace pour évaluer votre projet.`}
distanceMouchin="À environ 20 km"
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Maçon Orchies | Construction & Rénovation | OBC Maçonnerie",
description:
"OBC Maçonnerie intervient à Orchies pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["construction maison Orchies", "maçon Orchies", "rénovation Orchies", "gros oeuvre Orchies"],
alternates: { canonical: "https://obc-maconnerie.fr/construction-maison-orchies" },
};
export default function ConstructionMaisonOrchiesPage() {
return (
<LocalSEOPage
ville="Orchies"
departement="Nord (59)"
servicesPrincipaux={["Construction de maison", "Rénovation"]}
description="Construction de maison et rénovation à Orchies — OBC Maçonnerie intervient dans toute la commune."
texteIntro="Vous habitez à Orchies ou ses alentours et vous avez un projet de construction ou de rénovation ? OBC Maçonnerie intervient dans toute la commune avec expertise et disponibilité."
texteLocal={`OBC Maçonnerie, basée à Mouchin (59310), est votre entreprise de maçonnerie de proximité à Orchies. Benoît Colin intervient sur tous vos chantiers : construction de maison individuelle, rénovation complète ou partielle, assainissement, création d'accès et démolition.\n\nOrchies est au cœur de notre zone d'intervention. Nous y réalisons régulièrement des chantiers de construction neuve et de rénovation. Notre connaissance du tissu local, des entreprises et des contraintes de terrain de la commune est un vrai atout pour votre projet.\n\nSi vous cherchez un maçon à Orchies, disponible, à l'écoute et capable de vous accompagner de A à Z, contactez Benoît Colin au 06 74 45 30 89 pour un devis gratuit.`}
distanceMouchin="À environ 10 km"
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Maçon Valenciennes | Construction & Rénovation | OBC Maçonnerie",
description:
"OBC Maçonnerie intervient à Valenciennes pour vos travaux de construction de maison, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["construction maison Valenciennes", "maçon Valenciennes", "rénovation Valenciennes", "gros oeuvre Valenciennes"],
alternates: { canonical: "https://obc-maconnerie.fr/construction-maison-valenciennes" },
};
export default function ConstructionMaisonValenciennesPage() {
return (
<LocalSEOPage
ville="Valenciennes"
departement="Nord (59)"
servicesPrincipaux={["Construction de maison", "Rénovation"]}
description="Construction de maison et rénovation à Valenciennes — OBC Maçonnerie intervient dans toute la commune et le Valenciennois."
texteIntro="Vous recherchez un maçon de confiance à Valenciennes ? OBC Maçonnerie intervient dans tout le Valenciennois pour vos projets de construction neuve et de rénovation."
texteLocal={`OBC Maçonnerie étend son intervention jusqu'à Valenciennes et son agglomération. Benoît Colin et son équipe réalisent des chantiers de construction de maison individuelle, de rénovation complète et de gros œuvre dans tout le secteur valenciennois.\n\nNotre savoir-faire en construction neuve et rénovation s'adapte aux projets du Valenciennois : constructions traditionnelles, maisons en ossature bois, rénovation de maisons de ville anciennes. Chaque projet est traité avec la même rigueur.\n\nContactez OBC Maçonnerie pour un devis gratuit à Valenciennes et dans les communes environnantes. Benoît se déplace pour évaluer votre projet.`}
distanceMouchin="À environ 25 km"
/>
);
}

View File

@@ -0,0 +1,197 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Construction de Maison dans le Nord | OBC Maçonnerie Orchies",
description:
"Construction neuve, fondations, ossature bois dans le Nord (59). OBC Maçonnerie vous accompagne de A à Z. Devis gratuit.",
keywords: [
"construction maison Nord",
"maçon construction maison Orchies",
"fondation ossature bois Nord",
"gros œuvre Nord",
"construction maison Douai",
"construction maison Valenciennes",
],
alternates: { canonical: `${config.url}/construction-maison` },
};
}
const etapes = [
{ num: "01", title: "Étude & conseil", desc: "Benoît analyse votre terrain, votre plan et vos envies. Il adapte si besoin les plans d'architecte et vous conseille sur les matériaux." },
{ num: "02", title: "Fondations", desc: "Terrassement, fouilles, semelles filantes ou radier — la fondation, c'est la base de tout. Réalisée avec rigueur pour durer des décennies." },
{ num: "03", title: "Gros œuvre", desc: "Élévation des murs porteurs, dalles, planchers, chaînages — tout le squelette de votre maison prend forme." },
{ num: "04", title: "Ossature bois (option)", desc: "Construction en ossature bois légère et performante thermiquement, parfaitement maîtrisée par OBC Maçonnerie." },
{ num: "05", title: "Coordination des artisans", desc: "Grâce au réseau de partenaires, Benoît coordonne électriciens, plombiers, couvreurs et autres corps de métier." },
{ num: "06", title: "Remise des clés", desc: "Livraison de votre maison dans les délais convenus, avec un chantier propre et soigné." },
];
export default async function ConstructionMaisonPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* Hero */}
<section className="bg-navy texture-dark py-16 md:py-24 relative overflow-hidden">
<div className="hero-diagonal-panel" />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<Link
href="/services"
className="inline-flex items-center gap-2 text-white/40 hover:text-white text-xs font-bold uppercase tracking-widest mb-8 transition-colors"
>
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Tous les services
</Link>
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Gros œuvre
</span>
<h1 className="text-4xl md:text-6xl font-black text-white uppercase leading-none tracking-tight mb-5 max-w-3xl">
Construction de maison dans le Nord
</h1>
<p className="text-white/55 text-base md:text-lg max-w-xl mb-10">
Benoît Colin, maçon expert à Mouchin, vous accompagne dans la construction de votre maison individuelle de la première fondation à la remise des clés.
</p>
<div className="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>Devis gratuit</span>
<span>
<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>
</span>
</Link>
<a href={`tel:${phoneRaw}`} className="btn btn-outline-light px-8 py-4 text-xs uppercase tracking-[0.2em]">
<span>{phone}</span>
</a>
</div>
</ScrollReveal>
</div>
</section>
{/* Stats */}
<section className="bg-stone-bg border-b border-border py-10">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
{[
{ val: "15+", label: "ans d'expérience" },
{ val: "100+", label: "maisons construites" },
{ val: "30km", label: "rayon d'action" },
{ val: "A→Z", label: "accompagnement complet" },
].map((s) => (
<div key={s.label} className="border-l-2 border-orange pl-4">
<div className="text-2xl font-black text-orange">{s.val}</div>
<div className="text-text-muted text-xs uppercase tracking-wider mt-0.5">{s.label}</div>
</div>
))}
</div>
</div>
</section>
{/* Étapes */}
<section className="py-16 md:py-20 bg-navy-light">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Notre méthode
</span>
<h2 className="text-white font-black text-2xl md:text-4xl uppercase leading-tight tracking-tight mb-12">
Comment se déroule<br />votre construction ?
</h2>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-px bg-white/5">
{etapes.map((e, i) => (
<ScrollReveal key={e.num} direction="up" delay={i * 80}>
<div className="service-card-dark bg-white/[0.03] p-7 h-full">
<span className="text-orange font-black text-4xl leading-none block mb-4">{e.num}</span>
<h3 className="text-white font-black text-base uppercase tracking-wide mb-3">{e.title}</h3>
<p className="text-white/50 text-sm leading-relaxed">{e.desc}</p>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* SEO text */}
<section className="py-14 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="grid md:grid-cols-2 gap-12 items-start">
<div>
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Notre expertise
</span>
<h2 className="text-navy font-black text-2xl md:text-3xl uppercase leading-tight tracking-tight mb-6">
Votre maçon constructeur<br />dans le Nord (59)
</h2>
</div>
<div className="space-y-4 text-text-light text-sm leading-relaxed">
<p>
OBC Maçonnerie, dirigé par Benoît Colin, est une entreprise de maçonnerie spécialisée dans la <strong className="text-text">construction de maison individuelle dans le Nord</strong>. Basés à Mouchin (59310), nous intervenons sur Orchies, Douai, Valenciennes, Flines-lès-Raches, Saint-Amand-les-Eaux et toutes les communes dans un rayon de 30 km.
</p>
<p>
Que vous souhaitiez construire une maison en <strong className="text-text">parpaing</strong>, en <strong className="text-text">béton banché</strong> ou en <strong className="text-text">ossature bois</strong>, Benoît vous conseille et adapte chaque solution à votre terrain, votre budget et vos envies.
</p>
<p>
Grâce à son réseau de partenaires (électricien, plombier, charpentier, couvreur, menuisier, carreleur, peintre), OBC Maçonnerie coordonne l&apos;ensemble des corps de métier pour vous livrer une maison complète, dans les délais.
</p>
</div>
</div>
</ScrollReveal>
</div>
</section>
{/* Contact split */}
<section className="grid lg:grid-cols-2">
<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-diagonal-panel" />
<ScrollReveal direction="left">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Votre projet de construction
</span>
<h2 className="text-white font-black text-2xl uppercase tracking-tight mb-6">
Parlons de votre<br />future maison
</h2>
<p className="text-white/50 text-sm leading-relaxed mb-8 max-w-sm">
Benoît se déplace gratuitement pour évaluer votre terrain et vous remettre un devis détaillé sous 24h.
</p>
<div className="space-y-4">
<div className="flex items-center gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-orange" />
<span className="text-white/60 text-sm">Devis gratuit &amp; déplacement offert</span>
</div>
<div className="flex items-center gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-orange" />
<span className="text-white/60 text-sm">Réponse sous 24h</span>
</div>
<div className="flex items-center gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-orange" />
<span className="text-white/60 text-sm">Sans engagement</span>
</div>
</div>
</ScrollReveal>
</div>
<div className="bg-stone-bg py-16 md:py-20 px-8 md:px-12 lg:px-16">
<ScrollReveal direction="right">
<ContactForm />
</ScrollReveal>
</div>
</section>
<Footer />
</main>
);
}

145
app/(app)/contact/page.tsx Normal file
View File

@@ -0,0 +1,145 @@
import type { Metadata } from "next";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Contact & Devis Gratuit | OBC Maçonnerie Nord",
description:
"Contactez OBC Maçonnerie pour un devis gratuit. Benoît Colin intervient à Orchies, Douai, Valenciennes et dans un rayon de 30km autour de Mouchin (59).",
alternates: { canonical: `${config.url}/contact` },
};
}
export default async function ContactPage() {
const config = await getSiteConfig();
const { phone, phoneRaw, email, address, zones, zoneDescription } = config;
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* Page hero */}
<section className="bg-navy texture-dark py-16 md:py-20 relative overflow-hidden">
<div className="hero-diagonal-panel" />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Devis gratuit Réponse sous 24h
</span>
<h1 className="text-4xl md:text-6xl font-black text-white uppercase leading-none tracking-tight">
Parlons de<br />votre projet
</h1>
</ScrollReveal>
</div>
</section>
{/* Split content */}
<section className="grid lg:grid-cols-2">
{/* Gauche — infos */}
<div className="bg-navy-light py-16 md:py-20 px-8 md:px-12 lg:px-16">
<ScrollReveal direction="left">
<h2 className="text-white font-black text-2xl uppercase tracking-tight mb-8">
Nos coordonnées
</h2>
<div className="space-y-6 mb-10">
{/* Téléphone */}
<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-xl group-hover:text-orange transition-colors">{phone}</p>
<p className="text-white/30 text-xs mt-0.5">LunSam 7h19h</p>
</div>
</a>
{/* Email */}
<a href={`mailto:${email}`} className="flex items-center gap-4 group">
<div className="w-11 h-11 border border-white/15 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-white/40 group-hover:text-orange transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<div>
<p className="text-white/40 text-xs uppercase tracking-widest mb-0.5">Email</p>
<p className="text-white/80 font-bold text-sm group-hover:text-orange transition-colors">{email}</p>
<p className="text-white/30 text-xs mt-0.5">Réponse sous 24h</p>
</div>
</a>
{/* Adresse */}
<div className="flex items-start 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">{address}</p>
<p className="text-white/30 text-xs mt-0.5">Rayon d&apos;intervention : {zoneDescription}</p>
</div>
</div>
</div>
{/* Zones */}
<div className="border-t border-white/10 pt-8">
<p className="text-white/40 text-xs uppercase tracking-widest mb-4">Zone d&apos;intervention</p>
<div className="flex flex-wrap gap-2">
{zones.map((z) => (
<span
key={z}
className="inline-flex items-center gap-1.5 border border-white/15 text-white/60 text-xs font-bold px-3 py-1.5 uppercase tracking-wide"
>
<span className="w-1 h-1 rounded-full bg-orange shrink-0" />
{z}
</span>
))}
</div>
</div>
{/* Garanties */}
<div className="mt-10 grid grid-cols-2 gap-5 border-t border-white/10 pt-8">
{[
{ val: "Gratuit", label: "Devis + déplacement" },
{ val: "24h", label: "Délai de réponse" },
{ val: "15+", label: "Ans d'expérience" },
{ val: "Sans engagement", label: "Aucune obligation" },
].map((s) => (
<div key={s.label}>
<div className="text-xl font-black text-orange">{s.val}</div>
<div className="text-white/35 text-xs uppercase tracking-wider mt-0.5 leading-tight">{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">
Demande de devis
</span>
<h2 className="text-navy font-black text-2xl uppercase tracking-tight mb-8">
Votre projet
</h2>
<ContactForm />
</ScrollReveal>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -0,0 +1,51 @@
import type { Metadata } from "next";
import ServicePageLayout from "@/components/marketing/ServicePageLayout";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Création d'Accès, Voiries & Entrées | OBC Maçonnerie Nord",
description:
"Création d'accès, voiries privées, entrées de propriété et chemins dans le Nord (59). OBC Maçonnerie à Orchies. Devis gratuit.",
keywords: ["création accès maison Nord", "voirie privée Nord 59", "entrée propriété Nord", "chemin béton Nord"],
alternates: { canonical: `${config.url}/creation-acces` },
};
}
const items = [
{ icon: "🚗", title: "Entrées de propriété", desc: "Création d'une entrée soignée en béton, béton imprimé, pavés ou gravier stabilisé — adaptée à votre maison." },
{ icon: "🛤️", title: "Voiries privées", desc: "Aménagement de voiries sur propriété privée, chemin d'accès à un bâtiment agricole ou industriel." },
{ icon: "🌾", title: "Chemins ruraux", desc: "Création ou réfection de chemins en gravier compacté, grave non traitée ou béton désactivé." },
{ icon: "🏗️", title: "Travaux de terrassement", desc: "Terrassement, nivellement et compactage du terrain avant réalisation de votre accès." },
{ icon: "🔳", title: "Béton imprimé", desc: "Effet pavés, dalles ou pierre naturelle — le béton imprimé apporte une touche décorative durable." },
{ icon: "💧", title: "Drainage & évacuation", desc: "Mise en place de caniveaux, avaloirs et systèmes de drainage pour éviter les accumulations d'eau." },
];
export default async function CreationAccesPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return (
<ServicePageLayout
label="Voiries & accès"
title="Création d'accès dans le Nord"
subtitle="Voiries, entrées de propriété, chemins — OBC Maçonnerie crée vos accès sur mesure avec les matériaux adaptés à vos besoins."
phone={phone}
phoneRaw={phoneRaw}
items={items}
itemsSectionTitle="Nos réalisations d'accès"
seoTitle="Voirie & accès dans le Nord (59)"
seoText={
<>
<p>
OBC Maçonnerie réalise vos <strong className="text-text">travaux de voirie et création d&apos;accès dans le Nord</strong>. Entrées de propriété en béton imprimé, chemins ruraux en gravier compacté ou voiries privées Benoît Colin s&apos;adapte à votre terrain et vos envies.
</p>
<p>
Chaque projet commence par une étude du terrain pour choisir les matériaux et la technique les mieux adaptés : béton, pavés, gravier, béton désactivé. L&apos;objectif : un accès durable, esthétique et parfaitement drainé.
</p>
</>
}
contactTitle="Votre projet d'accès"
/>
);
}

View File

@@ -0,0 +1,51 @@
import type { Metadata } from "next";
import ServicePageLayout from "@/components/marketing/ServicePageLayout";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Démolition Maison Nord 59 | OBC Maçonnerie",
description:
"Démolition totale ou partielle de maison, murs porteurs, bâtiments dans le Nord (59). OBC Maçonnerie à Orchies. Toutes garanties de sécurité. Devis gratuit.",
keywords: ["démolition maison Nord 59", "démolition bâtiment Nord", "démolition mur porteur Nord", "démolition partielle Nord"],
alternates: { canonical: `${config.url}/demolition` },
};
}
const items = [
{ icon: "🏚️", title: "Démolition totale", desc: "Destruction complète d'un bâtiment résidentiel ou annexe, avec évacuation des gravats et remise en état du terrain." },
{ icon: "🧱", title: "Démolition partielle", desc: "Démolition ciblée d'une partie du bâtiment pour permettre une extension ou une restructuration." },
{ icon: "🏗️", title: "Suppression murs porteurs", desc: "Ouverture de murs porteurs avec pose de poutres et reprises en sous-œuvre pour sécuriser la structure." },
{ icon: "⛏️", title: "Dépose de dalles", desc: "Retrait de chapes béton, dalles existantes, fondations obsolètes pour préparer un nouveau sol." },
{ icon: "🚛", title: "Évacuation des gravats", desc: "Transport et évacuation de tous les déchets de démolition vers les filières de recyclage agréées." },
{ icon: "🏠", title: "Curage intérieur", desc: "Enlèvement complet des éléments intérieurs (cloisons, planchers, revêtements) avant une rénovation lourde." },
];
export default async function DemolitionPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return (
<ServicePageLayout
label="Démolition"
title="Démolition dans le Nord"
subtitle="Démolition totale ou partielle, avec tout le matériel et les garanties de sécurité. OBC Maçonnerie gère votre chantier du début à la fin."
phone={phone}
phoneRaw={phoneRaw}
items={items}
itemsSectionTitle="Nos prestations de démolition"
seoTitle="Démolition dans le Nord (59)"
seoText={
<>
<p>
OBC Maçonnerie intervient pour toutes vos <strong className="text-text">opérations de démolition dans le Nord</strong>. Qu&apos;il s&apos;agisse de détruire un bâtiment entier, d&apos;ouvrir un mur porteur ou de curer l&apos;intérieur avant rénovation, Benoît Colin prend en charge votre chantier avec rigueur.
</p>
<p>
Avant toute démolition, OBC Maçonnerie vérifie la présence éventuelle d&apos;amiante, de plomb ou d&apos;autres matériaux dangereux, et fait appel aux spécialistes agréés si nécessaire. <strong className="text-text">La sécurité du chantier et de ses riverains est une priorité absolue.</strong>
</p>
</>
}
contactTitle="Votre projet de démolition"
/>
);
}

487
app/(app)/globals.css Normal file
View File

@@ -0,0 +1,487 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');
@import "tailwindcss";
/* ================================================
DA OBC Maçonnerie — Rouge brique + Gris ardoise + Blanc cassé
Évoque la brique du Nord, le béton, la pierre, le sérieux artisan
================================================ */
@theme inline {
/* ── Couleur sombre (sections hero, navbar, CTA band) ── */
/* Charbon chaud : quasi-noir avec légère tonalité terreuse */
--color-navy: #1C1A18;
--color-navy-light: #2E2B28;
--color-navy-dark: #100F0D;
/* ── Accent principal : Rouge brique du logo ── */
--color-orange: #8B1A1A;
--color-orange-hover: #6B1414;
--color-orange-light: #A52424;
/* ── Ardoise (gris secondaire du logo) ── */
--color-stone: #6B7B7A;
--color-stone-light: #8B9A9A;
--color-stone-bg: #F0EDEA;
/* ── Fonds ── */
--color-bg: #F8F6F4;
--color-bg-white: #FFFFFF;
--color-bg-card: #FFFFFF;
--color-bg-muted: #F0EDEA;
/* ── Texte ── */
--color-text: #1C1C1C;
--color-text-light: #4A4A4A;
--color-text-muted: #6B7B7A;
/* ── Bordures ── */
--color-border: #DDD8D3;
--color-border-light: #EAE7E4;
/* ── Statuts ── */
--color-success: #4A7C59;
--color-warning: #C9832A;
--color-error: #C0392B;
/* ── Aliases ── */
--color-primary: #8B1A1A;
--color-primary-hover: #6B1414;
/* ── Typographie ── */
--font-sans: "Inter", sans-serif;
}
/* ================================================
BASE
================================================ */
body {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
}
html {
scroll-behavior: smooth;
}
/* ================================================
CARD HOVER — ombre teintée brique
================================================ */
.card-hover {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card-hover:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(139, 26, 26, 0.10);
}
/* ================================================
CTA PULSE — glow rouge brique
================================================ */
@keyframes pulse-glow {
0%, 100% {
box-shadow: 0 0 16px rgba(139, 26, 26, 0.35);
}
50% {
box-shadow: 0 0 36px rgba(139, 26, 26, 0.55);
}
}
.pulse-glow {
animation: pulse-glow 2.5s ease-in-out infinite;
}
/* ================================================
SÉLECTION DE TEXTE
================================================ */
::selection {
background: rgba(139, 26, 26, 0.15);
color: var(--color-text);
}
/* ================================================
SKIP TO CONTENT
================================================ */
.skip-to-content {
position: absolute;
left: -9999px;
z-index: 999;
padding: 0.75rem 1.5rem;
background: var(--color-navy);
color: #ffffff;
font-weight: 600;
border-radius: 0 0 8px 0;
text-decoration: none;
}
.skip-to-content:focus {
left: 0;
top: 0;
}
/* ================================================
FOCUS VISIBLE
================================================ */
*:focus-visible {
outline: 2px solid var(--color-orange);
outline-offset: 2px;
}
/* ================================================
SCROLLBAR
================================================ */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: var(--color-bg);
}
::-webkit-scrollbar-thumb {
background: var(--color-border);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-stone);
}
/* ================================================
ACCENT DIAGONALE — élément décoratif hero
Évoque une maçonnerie en brique posée en diagonale
================================================ */
.hero-accent {
position: absolute;
top: 0;
right: 0;
width: 45%;
height: 100%;
background: linear-gradient(135deg, transparent 40%, rgba(139, 26, 26, 0.07) 100%);
pointer-events: none;
}
.hero-accent-line {
position: absolute;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(180deg, transparent, #8B1A1A 30%, #8B1A1A 70%, transparent);
opacity: 0.5;
}
/* ================================================
SECTION LABEL — étiquette catégorie
================================================ */
.section-label {
display: inline-block;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(--color-orange);
margin-bottom: 0.5rem;
}
.section-label-light {
color: rgba(139, 26, 26, 0.75);
}
/* ================================================
BRICK DIVIDER — séparateur rouge brique
================================================ */
.brick-divider {
width: 3rem;
height: 3px;
background: var(--color-orange);
border-radius: 2px;
}
/* ================================================
STAT GLOW — compteur chiffres clés
================================================ */
@keyframes stat-glow {
0%, 100% {
text-shadow: 0 0 10px rgba(139, 26, 26, 0.25);
}
50% {
text-shadow: 0 0 22px rgba(139, 26, 26, 0.45);
}
}
.animate-stat-glow {
animation: stat-glow 2.5s ease-in-out infinite;
}
/* ================================================
HERO TEXT ANIMATIONS — staggered reveal
================================================ */
@keyframes hero-text-appear {
0% {
opacity: 0;
transform: translateY(30px);
filter: blur(4px);
}
100% {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
}
.animate-hero-text-1 {
opacity: 0;
animation: hero-text-appear 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.1s forwards;
}
.animate-hero-text-2 {
opacity: 0;
animation: hero-text-appear 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.3s forwards;
}
.animate-hero-text-3 {
opacity: 0;
animation: hero-text-appear 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.5s forwards;
}
/* ================================================
FADE IN ANIMATIONS
================================================ */
@keyframes fade-in-down {
0% { opacity: 0; transform: translateY(-20px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes fade-in-up {
0% { opacity: 0; transform: translateY(20px); }
100% { opacity: 1; transform: translateY(0); }
}
.animate-fade-in-down {
opacity: 0;
animation: fade-in-down 0.6s ease-out forwards;
}
.animate-fade-in-up {
opacity: 0;
animation: fade-in-up 0.6s ease-out forwards;
}
/* Animation delays */
.animation-delay-200 { animation-delay: 200ms; }
.animation-delay-400 { animation-delay: 400ms; }
.animation-delay-600 { animation-delay: 600ms; }
.animation-delay-800 { animation-delay: 800ms; }
.animation-delay-1000 { animation-delay: 1000ms; }
/* ================================================
UNDERLINE GROW
================================================ */
@keyframes underline-grow {
0% { width: 0; }
100% { width: 100%; }
}
.animate-underline-grow {
animation: underline-grow 1s cubic-bezier(0.16, 1, 0.3, 1) 0.8s forwards;
width: 0;
}
/* ================================================
BOUNCE SLOW
================================================ */
@keyframes bounce-slow {
0%, 100% { transform: translate(-50%, 0); }
50% { transform: translate(-50%, 8px); }
}
.animate-bounce-slow {
animation: bounce-slow 2s ease-in-out infinite;
}
/* ================================================
SCROLL REVEAL
================================================ */
.scroll-reveal-up,
.scroll-reveal-down,
.scroll-reveal-left,
.scroll-reveal-right,
.scroll-reveal-fade {
opacity: 0;
transition-property: opacity, transform;
transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.scroll-reveal-up { transform: translateY(40px); }
.scroll-reveal-down { transform: translateY(-40px); }
.scroll-reveal-left { transform: translateX(-40px); }
.scroll-reveal-right { transform: translateX(40px); }
.scroll-reveal-fade { transform: scale(0.95); }
.scroll-revealed {
opacity: 1 !important;
transform: translateY(0) translateX(0) scale(1) !important;
}
/* ================================================
TEXTURE BÉTON — overlay sur sections sombres
Subtile granularité qui évoque le béton brut
================================================ */
.texture-dark {
position: relative;
}
.texture-dark::before {
content: "";
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.03'/%3E%3C/svg%3E");
background-size: 200px 200px;
opacity: 0.4;
pointer-events: none;
z-index: 0;
}
.texture-dark > * {
position: relative;
z-index: 1;
}
/* ================================================
GRADIENT UTILITIES (legacy — non utilisé OBC)
================================================ */
.gradient-text {
background: linear-gradient(135deg, #8B1A1A, #C0392B);
-webkit-background-clip: text;
-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); }

195
app/(app)/layout.tsx Normal file
View File

@@ -0,0 +1,195 @@
import type { Metadata } from "next";
import CookieBanner from "@/components/CookieBanner";
import { siteConfig } from "@/lib/site-config";
import "./globals.css";
const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || siteConfig.url;
export const metadata: Metadata = {
metadataBase: new URL(BASE_URL),
title: {
default: "OBC Maçonnerie | Constructeur & Maçon à Orchies (Nord 59)",
template: "%s | OBC Maçonnerie",
},
description:
"Benoît Colin, maçon expert à Mouchin. Construction de maison, rénovation, assainissement et gros œuvre dans un rayon de 30km autour d'Orchies. Devis gratuit.",
keywords: [
"construction maison Nord",
"maçon construction maison Orchies",
"rénovation maison Nord 59",
"entreprise maçonnerie Mouchin",
"fondation ossature bois Nord",
"assainissement maison Nord",
"création accès maison Nord",
"démolition maison Nord 59",
"maçon rénovation Douai",
"maçon rénovation Valenciennes",
"gros œuvre Nord",
"entrepreneur maçon Nord 59",
"OBC Maçonnerie",
"Benoît Colin maçon",
"maçon Mouchin",
"construction maison Orchies",
],
authors: [{ name: "OBC Maçonnerie - Benoît Colin" }],
creator: "OBC Maçonnerie",
publisher: "OBC Maçonnerie",
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
"max-video-preview": -1,
"max-image-preview": "large",
"max-snippet": -1,
},
},
icons: {
icon: [{ url: "/favicon.svg", type: "image/svg+xml" }],
apple: [
{ url: "/apple-touch-icon.svg", type: "image/svg+xml", sizes: "180x180" },
],
},
manifest: "/site.webmanifest",
openGraph: {
type: "website",
locale: "fr_FR",
url: BASE_URL,
siteName: "OBC Maçonnerie",
title: "OBC Maçonnerie | Constructeur & Maçon dans le Nord (59)",
description:
"Construction de maison, rénovation, assainissement et gros œuvre autour d'Orchies, Douai, Valenciennes. Benoît Colin vous accompagne de A à Z.",
images: [
{
url: "/og-image.jpg",
width: 1200,
height: 630,
alt: "OBC Maçonnerie - Construction et rénovation dans le Nord",
},
],
},
twitter: {
card: "summary_large_image",
title: "OBC Maçonnerie | Maçon constructeur dans le Nord (59)",
description:
"Construction de maison, rénovation, assainissement. Orchies, Douai, Valenciennes.",
images: ["/og-image.jpg"],
},
alternates: {
canonical: BASE_URL,
},
verification: {
google: process.env.GOOGLE_SITE_VERIFICATION || undefined,
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const jsonLdBusiness = {
"@context": "https://schema.org",
"@type": "LocalBusiness",
"@id": `${BASE_URL}/#business`,
name: "OBC Maçonnerie",
description:
"Construction de maison, rénovation, assainissement et gros œuvre dans le Nord",
telephone: siteConfig.phone,
email: siteConfig.email,
url: BASE_URL,
logo: `${BASE_URL}/icon-512.svg`,
image: `${BASE_URL}/og-image.jpg`,
priceRange: "$$",
address: {
"@type": "PostalAddress",
streetAddress: "221 Route de Saint-Amand",
addressLocality: "Mouchin",
postalCode: "59310",
addressRegion: "Hauts-de-France",
addressCountry: "FR",
},
geo: {
"@type": "GeoCoordinates",
latitude: 50.4817,
longitude: 3.3342,
},
areaServed: [
{ "@type": "City", name: "Orchies" },
{ "@type": "City", name: "Mouchin" },
{ "@type": "City", name: "Douai" },
{ "@type": "City", name: "Valenciennes" },
{ "@type": "City", name: "Flines-lès-Raches" },
{ "@type": "City", name: "Saint-Amand-les-Eaux" },
{ "@type": "City", name: "Château-l'Abbaye" },
{ "@type": "City", name: "Mérignies" },
],
hasOfferCatalog: {
"@type": "OfferCatalog",
name: "Services de maçonnerie",
itemListElement: [
{ "@type": "Offer", "name": "Construction de maison" },
{ "@type": "Offer", "name": "Rénovation" },
{ "@type": "Offer", "name": "Assainissement" },
{ "@type": "Offer", "name": "Création d'accès" },
{ "@type": "Offer", "name": "Démolition" },
],
},
openingHoursSpecification: {
"@type": "OpeningHoursSpecification",
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
opens: "07:00",
closes: "19:00",
},
contactPoint: {
"@type": "ContactPoint",
telephone: siteConfig.phone,
contactType: "customer service",
availableLanguage: "French",
},
founder: {
"@type": "Person",
name: "Benoît Colin",
jobTitle: "Maçon - Gérant OBC Maçonnerie",
},
sameAs: [],
};
const jsonLdWebSite = {
"@context": "https://schema.org",
"@type": "WebSite",
"@id": `${BASE_URL}/#website`,
name: "OBC Maçonnerie",
url: BASE_URL,
description:
"Site officiel d'OBC Maçonnerie, entreprise de construction et rénovation dans le Nord (59).",
publisher: {
"@id": `${BASE_URL}/#business`,
},
inLanguage: "fr-FR",
};
return (
<html lang="fr">
<head>
<meta name="theme-color" content="#1B2A4A" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/apple-touch-icon.svg" />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify([jsonLdBusiness, jsonLdWebSite]),
}}
/>
</head>
<body className="antialiased">
<a href="#main-content" className="skip-to-content">
Aller au contenu principal
</a>
{children}
<CookieBanner />
</body>
</html>
);
}

View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Maçon Flines-lès-Raches | Construction & Rénovation | OBC Maçonnerie",
description:
"OBC Maçonnerie intervient à Flines-lès-Raches pour vos travaux de construction, rénovation et gros œuvre. Benoît Colin, maçon expert. Devis gratuit.",
keywords: ["maçon Flines-lès-Raches", "construction Flines Raches", "rénovation Flines Raches", "maçon Flines Nord"],
alternates: { canonical: "https://obc-maconnerie.fr/macon-flines-lez-raches" },
};
export default function MaconFlinesPage() {
return (
<LocalSEOPage
ville="Flines-lès-Raches"
departement="Nord (59148)"
servicesPrincipaux={["Construction de maison", "Rénovation"]}
description="Maçon à Flines-lès-Raches — OBC Maçonnerie intervient dans la commune pour vos travaux de construction et rénovation."
texteIntro="Vous avez un projet de maçonnerie à Flines-lès-Raches ? OBC Maçonnerie, basée à quelques kilomètres à Mouchin, intervient rapidement dans la commune."
texteLocal={`Flines-lès-Raches est l'une des communes que OBC Maçonnerie dessert régulièrement. Benoît Colin y réalise des chantiers de construction neuve, de rénovation de maison et d'assainissement.\n\nVotre maçon de proximité est à Mouchin, soit à quelques minutes de Flines-lès-Raches. Cette proximité garantit une réactivité optimale pour vos urgences et une meilleure coordination du chantier.\n\nContactez OBC Maçonnerie pour un devis gratuit à Flines-lès-Raches. Benoît se déplace pour évaluer votre projet et vous proposer la meilleure solution.`}
distanceMouchin="À environ 5 km"
/>
);
}

View File

@@ -0,0 +1,23 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Maçon Mouchin | Construction & Rénovation | OBC Maçonnerie",
description:
"OBC Maçonnerie est basée à Mouchin (59310). Benoît Colin, maçon expert local. Construction, rénovation, assainissement, gros œuvre. Devis gratuit.",
keywords: ["maçon Mouchin", "entreprise maçonnerie Mouchin", "construction Mouchin", "rénovation Mouchin"],
alternates: { canonical: "https://obc-maconnerie.fr/macon-mouchin" },
};
export default function MaconMouchinPage() {
return (
<LocalSEOPage
ville="Mouchin"
departement="Nord (59310)"
servicesPrincipaux={["Construction de maison", "Rénovation", "Assainissement"]}
description="OBC Maçonnerie, basée à Mouchin — votre entreprise de maçonnerie locale. Benoît Colin vous accompagne pour tous vos travaux."
texteIntro="Basée à Mouchin (59310), OBC Maçonnerie est votre entreprise de maçonnerie de proximité. Benoît Colin connaît parfaitement le secteur et intervient sur tous vos chantiers locaux."
texteLocal={`OBC Maçonnerie a ses racines à Mouchin (221 Route de Saint-Amand, 59310). C'est ici que Benoît Colin a fondé son entreprise, avec une ambition claire : offrir un service de maçonnerie expert, disponible et honnête aux particuliers et professionnels du secteur.\n\nMouchin et ses environs sont au cœur de notre zone d'intervention. Nous connaissons les terrains, les réglementations locales et les contraintes spécifiques à la commune. Cette proximité est un vrai avantage pour vos projets de construction, rénovation ou assainissement.\n\nEn tant qu'entreprise locale mouchinoise, OBC Maçonnerie est fier de contribuer au développement et à la rénovation du bâti de la commune et des villages environnants. Contactez Benoît pour un devis gratuit.`}
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Maçon Saint-Amand-les-Eaux | Construction & Rénovation | OBC Maçonnerie",
description:
"OBC Maçonnerie intervient à Saint-Amand-les-Eaux pour vos travaux de construction, rénovation, assainissement et gros œuvre. Devis gratuit.",
keywords: ["maçon Saint-Amand-les-Eaux", "construction Saint-Amand", "rénovation Saint-Amand les Eaux", "maçon Saint-Amand Nord"],
alternates: { canonical: "https://obc-maconnerie.fr/macon-saint-amand-les-eaux" },
};
export default function MaconSaintAmandPage() {
return (
<LocalSEOPage
ville="Saint-Amand-les-Eaux"
departement="Nord (59230)"
servicesPrincipaux={["Construction de maison", "Rénovation", "Assainissement"]}
description="Maçon à Saint-Amand-les-Eaux — OBC Maçonnerie intervient dans la commune pour tous vos travaux de maçonnerie."
texteIntro="Vous recherchez un maçon de confiance à Saint-Amand-les-Eaux ? OBC Maçonnerie intervient dans toute la commune et ses alentours pour vos projets de construction et rénovation."
texteLocal={`Saint-Amand-les-Eaux est une commune importante de notre zone d'intervention. OBC Maçonnerie y réalise régulièrement des chantiers de construction de maison individuelle, de rénovation et d'assainissement non collectif.\n\nLa commune de Saint-Amand-les-Eaux est connue pour son patrimoine architectural. Benoît Colin apprécie travailler sur les maisons de la région, souvent en pierre ou en brique, qui nécessitent une connaissance spécifique des matériaux traditionnels.\n\nPour un devis gratuit à Saint-Amand-les-Eaux, contactez OBC Maçonnerie au 06 74 45 30 89 ou via notre formulaire de contact.`}
distanceMouchin="À environ 10 km"
/>
);
}

View File

@@ -0,0 +1,102 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import { siteConfig } from "@/lib/site-config";
export const metadata: Metadata = {
title: "Mentions Légales | OBC Maçonnerie",
description:
"Mentions légales du site OBC Maçonnerie — Benoît Colin, maçon à Mouchin (59310). SIREN 531 827 871.",
alternates: { canonical: "https://obc-maconnerie.fr/mentions-legales" },
robots: { index: false, follow: false },
};
export default function MentionsLegales() {
return (
<main id="main-content" className="min-h-screen bg-bg">
<Navbar />
<div className="max-w-3xl mx-auto px-4 sm:px-6 py-16 md:py-20">
<Link
href="/"
className="inline-flex items-center gap-2 mb-8 text-text-light hover:text-navy text-sm transition-colors group"
>
<svg className="w-4 h-4 transition-transform group-hover:-translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Retour à l&apos;accueil
</Link>
<h1 className="text-3xl md:text-4xl font-bold text-navy mb-10">Mentions Légales</h1>
<div className="space-y-10 text-text-light text-sm leading-relaxed">
<p className="italic text-text-muted">
Conformément aux dispositions de la loi n° 2004-575 du 21 juin 2004 pour la confiance en l&apos;économie numérique (LCEN), voici les informations légales du site <strong className="text-text">obc-maconnerie.fr</strong>.
</p>
<section>
<h2 className="text-xl font-bold text-navy mb-4">1. Édition du site</h2>
<div className="bg-bg-white border border-border rounded-xl p-6 space-y-2">
<p>Le présent site est édité par :</p>
<p><strong className="text-text">Benoît COLIN</strong><br />Exerçant sous l&apos;enseigne commerciale <strong className="text-text">OBC Maçonnerie</strong></p>
<ul className="space-y-1 mt-3">
<li><strong className="text-text">Statut :</strong> Entreprise individuelle</li>
<li><strong className="text-text">SIREN :</strong> 531 827 871</li>
<li><strong className="text-text">Siège social :</strong> 221 Route de Saint-Amand, 59310 Mouchin, France</li>
<li><strong className="text-text">Téléphone :</strong> <a href={`tel:${siteConfig.phoneRaw}`} className="text-orange hover:underline">{siteConfig.phone}</a></li>
<li><strong className="text-text">Email :</strong> <a href="mailto:contact@obc-maconnerie.fr" className="text-orange hover:underline">contact@obc-maconnerie.fr</a></li>
</ul>
<p className="mt-3"><strong className="text-text">Directeur de la publication :</strong> Benoît COLIN</p>
</div>
</section>
<section>
<h2 className="text-xl font-bold text-navy mb-4">2. Conception & réalisation</h2>
<div className="bg-bg-white border border-border rounded-xl p-6">
<p>Ce site a é conçu et réalisé par <strong className="text-text">HookLab</strong> Enguerrand Ozano, agence web spécialisée dans les sites pour artisans du bâtiment dans le Nord.</p>
</div>
</section>
<section>
<h2 className="text-xl font-bold text-navy mb-4">3. Hébergement</h2>
<div className="bg-bg-white border border-border rounded-xl p-6">
<p>Le site est hébergé par <strong className="text-text">Vercel Inc.</strong></p>
<p className="mt-2">440 N Barranca Ave #4133, Covina, CA 91723, États-Unis</p>
</div>
</section>
<section>
<h2 className="text-xl font-bold text-navy mb-4">4. Propriété intellectuelle</h2>
<p>
L&apos;ensemble du contenu de ce site (textes, visuels, structure) est la propriété d&apos;OBC Maçonnerie. Toute reproduction est interdite sans autorisation préalable écrite de Benoît COLIN.
</p>
</section>
<section>
<h2 className="text-xl font-bold text-navy mb-4">5. Données personnelles</h2>
<p>
Les données collectées via le formulaire de contact sont utilisées uniquement pour répondre à vos demandes de devis. Conformément au RGPD, vous disposez d&apos;un droit d&apos;accès, de rectification et de suppression.{" "}
<Link href="/confidentialite" className="text-orange hover:underline">
Voir notre politique de confidentialité
</Link>.
</p>
</section>
<section>
<h2 className="text-xl font-bold text-navy mb-4">6. Droit applicable</h2>
<p>
Tout litige en relation avec l&apos;utilisation du site est soumis au droit français. Juridiction compétente : Tribunal de Valenciennes.
</p>
</section>
<p className="text-text-muted text-xs pt-4 border-t border-border">
Dernière mise à jour : Février 2026
</p>
</div>
</div>
<Footer />
</main>
);
}

604
app/(app)/page.tsx Normal file
View File

@@ -0,0 +1,604 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import ContactForm from "@/components/marketing/ContactForm";
import {
getSiteConfig,
getServices,
getTestimonials,
getFAQ,
getValues,
getPartners,
getRealisations,
} from "@/lib/content";
import type { Service, Testimonial, FAQItem } from "@/types/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: config.seo.title,
description: config.seo.description,
alternates: { canonical: config.url },
};
}
/* ── Arrow SVG ── */
function Arrow({ className = "w-4 h-4" }: { className?: string }) {
return (
<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 — light bg, numbered ── */
function ServiceCard({ service, index }: { service: Service; index: number }) {
const href = service.slug === "conseil" ? "/contact" : `/${service.slug}`;
return (
<Link
href={href}
className="group block bg-white border border-border p-8 hover:border-orange hover:shadow-xl transition-all duration-300"
>
<div className="text-5xl font-black text-border group-hover:text-orange/25 transition-colors mb-6 leading-none tracking-tighter">
{String(index + 1).padStart(2, "0")}
</div>
<h3 className="text-navy font-black text-base uppercase tracking-wider mb-3 group-hover:text-orange transition-colors">
{service.title}
</h3>
<p className="text-text-light text-sm leading-relaxed mb-7">{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>
);
}
/* ── Testimonial card — light bg ── */
function TestimonialCard({ t }: { t: Testimonial }) {
const serviceLabel: Record<string, string> = {
"construction-maison": "Construction",
renovation: "Rénovation",
assainissement: "Assainissement",
"creation-acces": "Création d'accès",
demolition: "Démolition",
};
return (
<div className="bg-white border border-border p-7 flex flex-col h-full">
<div className="flex gap-0.5 mb-5">
{Array.from({ length: 5 }).map((_, i) => (
<svg
key={i}
className={`w-3.5 h-3.5 ${i < t.rating ? "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>
<p className="text-text-light text-sm leading-relaxed flex-1 italic mb-6">
&ldquo;{t.text}&rdquo;
</p>
<div className="border-t border-border pt-5">
<p className="text-navy font-bold text-sm">{t.name}</p>
<p className="text-text-muted text-xs uppercase tracking-wider mt-0.5">
{t.ville} {serviceLabel[t.service] ?? t.service}
</p>
</div>
</div>
);
}
/* ── FAQ item ── */
function FAQItem({ item }: { item: FAQItem }) {
return (
<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}
</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="pb-5 text-text-light text-sm leading-relaxed -mt-1">
{item.answer}
</div>
</details>
);
}
/* Photos Unsplash construction / maçonnerie */
const HERO_PHOTO =
"https://images.unsplash.com/photo-1504307651254-35680f356dfd?auto=format&fit=crop&w=1920&q=80";
const CTA_PHOTO =
"https://images.unsplash.com/photo-1541888946425-d81bb19240f5?auto=format&fit=crop&w=1920&q=80";
/* ══════════════════════════════════════════════════
PAGE PRINCIPALE
══════════════════════════════════════════════════ */
export default async function HomePage() {
const [config, services, testimonials, faqItems, values, partners, realisations] =
await Promise.all([
getSiteConfig(),
getServices(),
getTestimonials(),
getFAQ(),
getValues(),
getPartners(),
getRealisations(),
]);
const { hero, zones, zoneDescription, phone, phoneRaw } = config;
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* ══ 1 — HERO PHOTO ══ */}
<section className="relative min-h-screen flex items-center overflow-hidden">
{/* Photo de fond */}
<div className="absolute inset-0 z-0">
<div
className="w-full h-full bg-center bg-cover"
style={{ backgroundImage: `url('${HERO_PHOTO}')` }}
/>
{/* Gradient gauche → droite pour lisibilité du texte */}
<div className="absolute inset-0 bg-gradient-to-r from-black/85 via-black/60 to-black/20" />
{/* Gradient bas → haut pour les stats */}
<div className="absolute inset-0 bg-gradient-to-t from-black/55 via-transparent to-transparent" />
</div>
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 w-full py-24 md:py-32">
<div className="grid lg:grid-cols-12 gap-12 items-center">
{/* Contenu gauche */}
<div className="lg:col-span-7">
<div className="animate-hero-text-1 flex items-center gap-3 mb-7">
<div className="w-8 h-px bg-orange" />
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em]">
Maçon &amp; Constructeur · Nord 59
</span>
</div>
<h1 className="animate-hero-text-2 text-[clamp(2.8rem,7vw,5.5rem)] font-black text-white leading-[0.9] tracking-tight uppercase mb-8">
Maçon<br />
<span className="text-orange">&amp;</span>{" "}
Construc<span className="text-white/30">teur</span>
</h1>
<p className="animate-hero-text-3 text-white/70 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 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>
<span>{phone}</span>
</a>
</div>
{/* Stats */}
<div className="mt-14 pt-10 border-t border-white/15 flex gap-10 flex-wrap">
{hero.stats.map((s) => (
<div key={s.label}>
<div className="text-3xl md:text-4xl font-black text-white">{s.val}</div>
<div className="text-white/40 text-xs uppercase tracking-widest mt-1">{s.label}</div>
</div>
))}
</div>
</div>
{/* Carte glassmorphisme droite */}
<div className="hidden lg:block lg:col-span-5">
<div className="bg-white/10 backdrop-blur-sm border border-white/20 p-8">
<p className="text-white/55 text-xs font-bold uppercase tracking-[0.2em] mb-6 flex items-center gap-3">
<span className="w-5 h-px bg-orange" />
Pourquoi OBC Maçonnerie ?
</p>
<div className="space-y-6">
{values.slice(0, 3).map((v) => (
<div key={v.title} className="flex items-start gap-4">
<div className="w-8 h-8 border border-orange/50 bg-orange/10 flex items-center justify-center shrink-0 mt-0.5">
<div className="w-2 h-2 bg-orange" />
</div>
<div>
<p className="text-white font-bold text-sm uppercase tracking-wider">{v.title}</p>
<p className="text-white/55 text-xs mt-1 leading-relaxed">{v.description}</p>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</section>
{/* ══ 2 — NOS SERVICES (fond blanc) ══ */}
<section className="bg-white 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-navy font-black text-3xl md:text-5xl uppercase leading-tight tracking-tight">
Nos<br />services
</h2>
</div>
<Link
href="/services"
className="btn-arrow text-text-muted hover:text-navy 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-4">
{services.map((s, i) => (
<ScrollReveal key={s.slug} direction="up" delay={i * 60}>
<ServiceCard service={s} index={i} />
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ══ 3 — BANDE CTA (photo + overlay rouge brique) ══ */}
<section className="relative py-16 md:py-20 overflow-hidden">
<div
className="absolute inset-0"
style={{
backgroundImage: `url('${CTA_PHOTO}')`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
/>
<div className="absolute inset-0 bg-orange/90" />
<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 z-10 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>
<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>
{/* ══ 4 — RÉALISATIONS ══ */}
<section className="py-20 md:py-24 bg-bg">
<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 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-2 lg:grid-cols-3 gap-4">
{realisations.map((r, i) => {
const bgColors = [
"bg-stone-500", "bg-zinc-600", "bg-stone-600",
"bg-zinc-500", "bg-stone-700", "bg-zinc-700",
];
return (
<ScrollReveal key={r.title} direction="up" delay={i * 80}>
<div className="realisation-card relative overflow-hidden aspect-[4/3] cursor-pointer group">
<div className={`${bgColors[i % bgColors.length]} w-full h-full flex items-center justify-center`}>
<span className="text-white/10 font-black text-9xl select-none">
{String(i + 1).padStart(2, "0")}
</span>
</div>
{/* Barre orange repos */}
<div className="absolute bottom-0 left-0 w-full h-0.5 bg-orange/50 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/75 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 */}
<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/75 text-xs leading-relaxed">{r.description}</p>
</div>
</div>
</ScrollReveal>
);
})}
</div>
</div>
</section>
{/* ══ 5 — TÉMOIGNAGES (fond pierre clair) ══ */}
<section className="py-20 md:py-24 bg-stone-bg">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<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-navy 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-5">
{testimonials.map((t, i) => (
<ScrollReveal key={t.name} direction="up" delay={i * 100}>
<TestimonialCard t={t} />
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ══ 6 — ZONE D'INTERVENTION ══ */}
<section className="py-20 md:py-24 bg-white">
<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) => (
<span
key={v}
className="inline-flex items-center gap-1.5 bg-bg 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 bg-orange shrink-0" />
{v}
</span>
))}
<span className="inline-flex items-center gap-1.5 bg-orange/8 border border-orange/25 text-orange font-bold text-xs px-4 py-2.5 uppercase tracking-wider">
+ communes voisines
</span>
</div>
</ScrollReveal>
</div>
</div>
</section>
{/* ══ 7 — PARTENAIRES ══ */}
<section className="py-16 bg-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-white border border-border p-4 text-center hover:border-orange transition-all duration-200 cursor-default">
<span className="text-navy font-bold text-xs uppercase tracking-wide group-hover:text-orange transition-colors leading-tight block">
{p.label}
</span>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ══ 8 — FAQ ══ */}
<section className="py-20 md:py-24 bg-white">
<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>
</div>
</div>
</section>
{/* ══ 9 — CONTACT SPLIT ══ */}
<section id="contact" className="grid lg:grid-cols-2">
{/* Gauche — sombre avec photo en arrière-plan */}
<div className="relative bg-navy py-16 md:py-20 px-8 md:px-12 lg:px-16 overflow-hidden">
<div
className="absolute inset-0 opacity-15"
style={{
backgroundImage: `url('${HERO_PHOTO}')`,
backgroundSize: "cover",
backgroundPosition: "center right",
}}
/>
<div className="absolute inset-0 bg-navy/80" />
<div className="relative z-10">
<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: "15+", 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>
</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>
</section>
<Footer />
</main>
);
}

View File

@@ -0,0 +1,132 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import { getPartners, getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Notre Réseau de Partenaires | OBC Maçonnerie Nord",
description:
"OBC Maçonnerie coordonne un réseau d'artisans partenaires de confiance pour livrer votre maison de A à Z : électricité, plomberie, charpente, isolation, menuiserie, carrelage, peinture.",
alternates: { canonical: `${config.url}/partenaires` },
};
}
export default async function PartenairesPage() {
const partenaires = await getPartners();
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* Hero */}
<section className="bg-navy texture-dark py-16 md:py-20 relative overflow-hidden">
<div className="hero-diagonal-panel" />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Un réseau de confiance
</span>
<h1 className="text-4xl md:text-6xl font-black text-white uppercase leading-none tracking-tight">
Nos<br />partenaires
</h1>
<p className="text-white/50 text-base mt-5 max-w-2xl">
Seul on va vite, ensemble on va plus loin. Benoît coordonne un réseau d&apos;artisans sélectionnés pour que votre maison prenne forme de A à Z.
</p>
</ScrollReveal>
</div>
</section>
{/* Message interlocuteur unique */}
<section className="bg-stone-bg border-b border-border py-12 md:py-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<div className="grid md:grid-cols-2 gap-8 md:gap-16 items-center">
<div>
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Notre approche
</span>
<h2 className="text-navy font-black text-2xl md:text-3xl uppercase leading-tight tracking-tight mb-4">
Un seul interlocuteur<br />pour tout votre projet
</h2>
<p className="text-text-light text-sm leading-relaxed">
Benoît Colin sélectionne et coordonne des artisans avec lesquels il travaille depuis des années. Vous n&apos;avez qu&apos;un seul contact pour piloter l&apos;intégralité de votre chantier dans les délais et le budget convenus.
</p>
</div>
<div className="grid grid-cols-2 gap-4">
{[
{ val: "10+", label: "Corps de métier" },
{ val: "15+", label: "Ans de partenariat" },
{ val: "100%", label: "Artisans qualifiés" },
{ val: "1 seul", label: "Interlocuteur" },
].map((s) => (
<div key={s.label} className="border-l-2 border-orange pl-4">
<div className="text-2xl font-black text-orange">{s.val}</div>
<div className="text-text-muted text-xs uppercase tracking-wider mt-0.5">{s.label}</div>
</div>
))}
</div>
</div>
</ScrollReveal>
</div>
</section>
{/* Grille partenaires */}
<section className="py-16 md:py-20 bg-bg">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-3">
Corps de métier
</span>
<h2 className="text-navy font-black text-2xl md:text-3xl uppercase tracking-tight mb-10">
Nos domaines d&apos;expertise
</h2>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{partenaires.map((p, i) => (
<ScrollReveal key={p.label} direction="up" delay={i * 60}>
<div className="group bg-bg-white border border-border hover:border-orange p-6 h-full transition-all duration-200 cursor-default">
<div className="text-3xl mb-4 grayscale group-hover:grayscale-0 transition-all">{p.icon}</div>
<h3 className="text-navy font-black text-base uppercase tracking-wide mb-2 group-hover:text-orange transition-colors">
{p.label}
</h3>
<p className="text-text-light text-xs leading-relaxed">{p.desc}</p>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* CTA dark */}
<section className="bg-navy texture-dark py-16 md:py-20 relative overflow-hidden">
<div className="hero-diagonal-panel" />
<div className="relative max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<ScrollReveal direction="up">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Projet de A à Z
</span>
<h2 className="text-white font-black text-3xl md:text-4xl uppercase leading-tight tracking-tight mb-8">
Que vous construisiez ou rénowiez,<br />
OBC orchestre chaque corps de métier
</h2>
<Link href="/contact" className="btn btn-fill px-8 py-4 text-xs uppercase tracking-[0.2em]">
<span>Parler de mon projet à Benoît</span>
<span>
<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>
</span>
</Link>
</ScrollReveal>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -0,0 +1,130 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import { getRealisations, getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Nos Réalisations | Chantiers OBC Maçonnerie Nord",
description:
"Découvrez les réalisations d'OBC Maçonnerie : constructions de maisons, rénovations, assainissement et créations d'accès dans le Nord (59). Galerie photos.",
alternates: { canonical: `${config.url}/realisations` },
};
}
const cats = ["Tous", "Construction neuve", "Rénovation", "Assainissement", "Création d'accès", "Démolition"];
export default async function RealisationsPage() {
const [realisations, config] = await Promise.all([getRealisations(), getSiteConfig()]);
const { phone, phoneRaw } = config;
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* Hero */}
<section className="bg-navy texture-dark py-16 md:py-20 relative overflow-hidden">
<div className="hero-diagonal-panel" />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
Portfolio Chantiers Nord (59)
</span>
<h1 className="text-4xl md:text-6xl font-black text-white uppercase leading-none tracking-tight">
Nos<br />réalisations
</h1>
<p className="text-white/50 text-base mt-5 max-w-xl">
Chaque chantier est unique. Découvrez quelques-unes de nos réalisations dans le Nord.
</p>
</ScrollReveal>
</div>
</section>
{/* Filtres */}
<section className="bg-bg border-b border-border py-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex flex-wrap gap-2">
{cats.map((cat) => (
<span
key={cat}
className={`text-xs font-bold uppercase tracking-wider px-4 py-2 cursor-default transition-colors ${
cat === "Tous"
? "bg-navy text-white"
: "bg-bg-white border border-border text-text-light hover:border-orange hover:text-orange"
}`}
>
{cat}
</span>
))}
</div>
</div>
</section>
{/* Galerie */}
<section className="py-16 md:py-20 bg-bg">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{realisations.map((r, i) => {
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 * 80}>
<div className="realisation-card relative overflow-hidden aspect-[4/3] group cursor-pointer">
{/* Fond */}
<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>
{/* Badge catégorie */}
<div className="absolute top-4 left-4 z-10">
<span className="bg-orange text-white text-xs font-bold px-3 py-1 uppercase tracking-wider">
{r.categorie}
</span>
</div>
{/* Infos au repos */}
<div className="absolute bottom-0 left-0 right-0 p-5 bg-gradient-to-t from-black/70 to-transparent 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 */}
<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/75 text-sm leading-relaxed">{r.description}</p>
</div>
</div>
</ScrollReveal>
);
})}
</div>
{/* CTA bottom */}
<ScrollReveal direction="up" delay={200}>
<div className="mt-16 bg-navy py-12 px-8 md:px-12 text-center">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-3">
Un projet similaire ?
</span>
<h2 className="text-white font-black text-2xl md:text-3xl uppercase tracking-tight mb-6">
Benoît se déplace gratuitement<br />pour évaluer votre chantier
</h2>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/contact" className="btn btn-fill px-8 py-4 text-xs uppercase tracking-[0.2em]">
<span>Demander un devis gratuit</span>
</Link>
<a href={`tel:${phoneRaw}`} className="btn btn-outline-light px-8 py-4 text-xs uppercase tracking-[0.2em]">
<span>{phone}</span>
</a>
</div>
</div>
</ScrollReveal>
</div>
</section>
<Footer />
</main>
);
}

View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Rénovation Maison Douai | Maçon | OBC Maçonnerie",
description:
"Rénovation de maison et appartement à Douai. OBC Maçonnerie, maçon expert en rénovation dans le Nord (59). Devis gratuit.",
keywords: ["rénovation maison Douai", "maçon rénovation Douai", "rénovation appartement Douai", "travaux rénovation Douai"],
alternates: { canonical: "https://obc-maconnerie.fr/renovation-maison-douai" },
};
export default function RenovationMaisonDouaiPage() {
return (
<LocalSEOPage
ville="Douai"
departement="Nord (59)"
servicesPrincipaux={["Rénovation"]}
description="Rénovation de maison à Douai — OBC Maçonnerie, spécialiste de la rénovation dans le Douaisis."
texteIntro="Vous recherchez un maçon pour rénover votre maison ou appartement à Douai ? OBC Maçonnerie intervient dans tout le Douaisis avec expertise et rigueur."
texteLocal={`Le Douaisis compte de nombreuses maisons de ville anciennes à rénover. OBC Maçonnerie est parfaitement adapté pour ce type de chantier : restructuration intérieure, mise aux normes, ravalement de façade, création de salles de bains modernes.\n\nBenoît Colin connaît les spécificités des maisons de la région douaisienne et sait travailler sur des bâtis anciens sans compromettre la solidité de la structure. Chaque chantier est une nouvelle aventure.\n\nGrâce à son réseau de partenaires (électricien, plombier, carreleur, peintre), Benoît coordonne l'intégralité de votre rénovation à Douai pour vous livrer un logement entièrement transformé.`}
distanceMouchin="À environ 20 km"
/>
);
}

View File

@@ -0,0 +1,24 @@
import type { Metadata } from "next";
import LocalSEOPage from "@/components/marketing/LocalSEOPage";
export const metadata: Metadata = {
title: "Rénovation Maison Orchies | Maçon | OBC Maçonnerie",
description:
"Rénovation de maison et appartement à Orchies. OBC Maçonnerie, maçon expert en rénovation dans le Nord (59). Devis gratuit.",
keywords: ["rénovation maison Orchies", "maçon rénovation Orchies", "rénovation appartement Orchies"],
alternates: { canonical: "https://obc-maconnerie.fr/renovation-maison-orchies" },
};
export default function RenovationMaisonOrchiesPage() {
return (
<LocalSEOPage
ville="Orchies"
departement="Nord (59)"
servicesPrincipaux={["Rénovation"]}
description="Rénovation de maison à Orchies — OBC Maçonnerie, maçon expert en rénovation dans le secteur d'Orchies."
texteIntro="Vous avez un projet de rénovation à Orchies ? OBC Maçonnerie est votre spécialiste local pour tous vos travaux de rénovation de maison ou d'appartement."
texteLocal={`La rénovation est au cœur du métier d'OBC Maçonnerie. À Orchies comme dans toute la région, Benoît Colin transforme les logements existants en s'adaptant à chaque projet : restructuration intérieure, rénovation de façade, création d'ouvertures, extension.\n\nBenoît a une approche unique : il réfléchit avec vous à l'optimisation de vos espaces. Modifier une cage d'escalier, abattre une cloison pour ouvrir un séjour, créer une suite parentale — chaque idée est examinée et mise en œuvre avec soin.\n\nContactez OBC Maçonnerie pour un devis de rénovation gratuit à Orchies. Nous intervenons rapidement et dans les délais convenus.`}
distanceMouchin="À environ 10 km"
/>
);
}

View File

@@ -0,0 +1,54 @@
import type { Metadata } from "next";
import ServicePageLayout from "@/components/marketing/ServicePageLayout";
import { getSiteConfig } from "@/lib/content";
export async function generateMetadata(): Promise<Metadata> {
const config = await getSiteConfig();
return {
title: "Rénovation Maison & Appartement Nord 59 | OBC Maçonnerie",
description:
"Rénovation complète ou partielle de maison et appartement dans le Nord. Benoît Colin vous conseille et adapte chaque projet. Devis gratuit.",
keywords: ["rénovation maison Nord 59", "rénovation appartement Nord", "maçon rénovation Douai", "maçon rénovation Valenciennes"],
alternates: { canonical: `${config.url}/renovation` },
};
}
const items = [
{ icon: "🏚️", title: "Rénovation complète", desc: "Restructuration totale d'une maison ancienne, de la démolition des cloisons existantes à la pose des revêtements." },
{ icon: "🧱", title: "Maçonnerie intérieure", desc: "Création ou suppression de cloisons, doublages, cages d'escalier, adaptation de plans d'architecte." },
{ icon: "🏗️", title: "Extension", desc: "Agrandissement de votre maison par extension latérale ou surélévation, en parfaite continuité avec l'existant." },
{ icon: "🪟", title: "Ouvertures", desc: "Création de baies vitrées, portes, fenêtres — avec reprise de linteaux et traitement des murs porteurs." },
{ icon: "🏢", title: "Rénovation de façade", desc: "Ravalement, rejointoiement, isolation par l'extérieur (ITE) pour améliorer le confort et l'esthétique." },
{ icon: "🏠", title: "Rénovation appartement", desc: "Transformation d'appartements : redistribution des pièces, mise aux normes, travaux de second œuvre." },
];
export default async function RenovationPage() {
const config = await getSiteConfig();
const { phone, phoneRaw } = config;
return (
<ServicePageLayout
label="Rénovation"
title="Rénovation maison & appartement dans le Nord"
subtitle="Chaque rénovation est unique. Benoît Colin s'adapte à votre projet, votre budget et vos envies pour transformer votre logement."
phone={phone}
phoneRaw={phoneRaw}
items={items}
itemsSectionTitle="Nos spécialités en rénovation"
seoTitle="Maçon rénovation dans le Nord (59)"
seoText={
<>
<p>
OBC Maçonnerie intervient pour tous vos travaux de <strong className="text-text">rénovation dans le Nord</strong>. Que vous soyez à Orchies, Douai, Valenciennes ou dans les communes environnantes, Benoît Colin se déplace pour évaluer votre projet.
</p>
<p>
Sa passion : adapter les espaces. Modifier une cage d&apos;escalier, abattre une cloison, adapter un plan pour coller à votre mode de vie Benoît réfléchit avec vous et vous éclaire dans vos décisions.
</p>
<p>
Grâce à son réseau de partenaires, il coordonne aussi les corps de métier complémentaires (électricité, plomberie, carrelage, peinture) pour une rénovation complète avec un seul interlocuteur.
</p>
</>
}
contactTitle="Votre projet de rénovation"
/>
);
}

139
app/(app)/services/page.tsx Normal file
View File

@@ -0,0 +1,139 @@
import type { Metadata } from "next";
import Link from "next/link";
import Navbar from "@/components/marketing/Navbar";
import Footer from "@/components/marketing/Footer";
import ScrollReveal from "@/components/animations/ScrollReveal";
import { getServices, getSiteConfig } from "@/lib/content";
export const metadata: Metadata = {
title: "Nos Services | Construction, Rénovation, Assainissement",
description:
"Tous les services d'OBC Maçonnerie : construction de maison, rénovation, assainissement, création d'accès et démolition dans le Nord (59). Devis gratuit.",
alternates: { canonical: "https://obc-maconnerie.fr/services" },
};
function Arrow() {
return (
<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>
);
}
export default async function ServicesPage() {
const [services, config] = await Promise.all([getServices(), getSiteConfig()]);
const { phone, phoneRaw } = config;
return (
<main id="main-content" className="min-h-screen">
<Navbar />
{/* Hero */}
<section className="bg-navy texture-dark py-16 md:py-20 relative overflow-hidden">
<div className="hero-diagonal-panel" />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<ScrollReveal direction="up">
<span className="text-orange text-xs font-bold uppercase tracking-[0.25em] block mb-4">
OBC Maçonnerie Nord (59)
</span>
<h1 className="text-4xl md:text-6xl font-black text-white uppercase leading-none tracking-tight">
Nos services<br />de maçonnerie
</h1>
<p className="text-white/50 text-base mt-5 max-w-xl">
Construction, rénovation, assainissement et gros œuvre Benoît Colin vous accompagne de A à Z.
</p>
</ScrollReveal>
</div>
</section>
{/* Services list — dark editorial */}
<section className="bg-navy-light py-16 md:py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="space-y-px">
{services.map((s, i) => {
const href = s.slug === "conseil" ? "/contact" : `/${s.slug}`;
return (
<ScrollReveal key={s.slug} direction="up" delay={i * 60}>
<div className="group bg-white/[0.02] border-t border-white/8 hover:bg-white/[0.05] transition-all duration-300">
<div className="max-w-7xl mx-auto px-0 py-8 grid md:grid-cols-12 gap-6 items-start">
{/* Numéro + icône */}
<div className="md:col-span-2 flex items-center gap-4 md:flex-col md:items-start md:gap-2">
<span className="text-white/15 font-black text-3xl md:text-4xl leading-none">
0{i + 1}
</span>
<span className="text-2xl">{s.icon}</span>
</div>
{/* Titre + description */}
<div className="md:col-span-7">
<h2 className="text-white font-black text-xl md:text-2xl uppercase tracking-tight mb-3 group-hover:text-orange transition-colors">
{s.title}
</h2>
<p className="text-white/50 text-sm leading-relaxed mb-4">
{s.longDescription}
</p>
<div className="flex flex-wrap gap-2">
{s.keywords.map((k) => (
<span
key={k}
className="border border-white/10 text-white/35 text-xs font-bold px-3 py-1 uppercase tracking-wide"
>
{k}
</span>
))}
</div>
</div>
{/* CTA */}
<div className="md:col-span-3 flex md:justify-end md:pt-1">
<Link
href={href}
className="btn-arrow text-orange text-xs uppercase tracking-widest"
>
En savoir plus
<span className="arrow-icon"><Arrow /></span>
</Link>
</div>
</div>
</div>
</ScrollReveal>
);
})}
</div>
</div>
</section>
{/* CTA band */}
<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-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<ScrollReveal direction="up">
<h2 className="text-white font-black text-3xl md:text-4xl uppercase leading-tight tracking-tight mb-8">
Vous avez un projet ?<br />
Parlons-en.
</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>Devis gratuit</span>
<span>
<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>
</span>
</Link>
<a href={`tel:${phoneRaw}`} className="btn btn-outline-light px-8 py-4 text-xs uppercase tracking-[0.2em]">
<span>{phone}</span>
</a>
</div>
</ScrollReveal>
</div>
</section>
<Footer />
</main>
);
}