feat: animated hero with parallax rocket + scroll reveal animations

- Add animated hero section with parallax rocket SVG that descends on scroll
- Add floating decorative particles and gradient layers in hero
- Add staggered text reveal animation on hero h1
- Create ScrollReveal component (IntersectionObserver-based fade/slide)
- Create AnimatedCounter component for stat numbers
- Add scroll animations to all sections (Problematique, System, Demos, AboutMe, FAQ, Contact, Footer)
- Add smooth FAQ accordion transitions
- Add extensive CSS keyframe animations (float, flame, particles, stat glow)

https://claude.ai/code/session_01V8YAjpqRQ3bfBYsABYsEgo
This commit is contained in:
Claude
2026-02-16 19:09:16 +00:00
parent e94a03f302
commit 6555969c30
13 changed files with 1143 additions and 444 deletions

View File

@@ -108,3 +108,283 @@ html {
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background: var(--color-navy); background: var(--color-navy);
} }
/* ================================================
HERO ANIMATIONS - Staggered text 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 ANIMATION
================================================ */
@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 (scroll indicator)
================================================ */
@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;
}
/* ================================================
FLOATING ELEMENTS
================================================ */
@keyframes float-slow {
0%, 100% {
transform: translateY(0) scale(1);
opacity: 0.4;
}
50% {
transform: translateY(-20px) scale(1.1);
opacity: 0.7;
}
}
@keyframes float-medium {
0%, 100% {
transform: translateY(0) translateX(0);
opacity: 0.3;
}
33% {
transform: translateY(-15px) translateX(10px);
opacity: 0.6;
}
66% {
transform: translateY(-25px) translateX(-5px);
opacity: 0.5;
}
}
@keyframes float-fast {
0%, 100% {
transform: translateY(0) rotate(0deg);
opacity: 0.3;
}
50% {
transform: translateY(-12px) rotate(180deg);
opacity: 0.6;
}
}
.animate-float-slow {
animation: float-slow 6s ease-in-out infinite;
}
.animate-float-medium {
animation: float-medium 5s ease-in-out infinite;
}
.animate-float-fast {
animation: float-fast 4s ease-in-out infinite;
}
/* ================================================
ROCKET FLAME ANIMATION
================================================ */
@keyframes flame-flicker {
0%, 100% {
transform: scaleY(1) scaleX(1);
opacity: 0.9;
}
25% {
transform: scaleY(1.1) scaleX(0.95);
opacity: 0.8;
}
50% {
transform: scaleY(0.9) scaleX(1.05);
opacity: 1;
}
75% {
transform: scaleY(1.05) scaleX(0.97);
opacity: 0.85;
}
}
.animate-flame {
transform-origin: center top;
animation: flame-flicker 0.3s ease-in-out infinite;
}
/* ================================================
PARTICLE ANIMATIONS (rocket trail)
================================================ */
@keyframes particle-1 {
0%, 100% {
transform: translateY(0) scale(1);
opacity: 0.6;
}
50% {
transform: translateY(8px) scale(0.5);
opacity: 0;
}
}
@keyframes particle-2 {
0%, 100% {
transform: translateY(0) scale(1);
opacity: 0.4;
}
50% {
transform: translateY(12px) scale(0.3);
opacity: 0;
}
}
@keyframes particle-3 {
0%, 100% {
transform: translateY(0) scale(1);
opacity: 0.5;
}
50% {
transform: translateY(10px) scale(0.4);
opacity: 0;
}
}
.animate-particle-1 { animation: particle-1 1.2s ease-in-out infinite; }
.animate-particle-2 { animation: particle-2 1.5s ease-in-out infinite 0.2s; }
.animate-particle-3 { animation: particle-3 1.3s ease-in-out infinite 0.4s; }
/* ================================================
SCROLL REVEAL ANIMATIONS
================================================ */
.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);
}
/* Revealed state */
.scroll-revealed {
opacity: 1 !important;
transform: translateY(0) translateX(0) scale(1) !important;
}
/* ================================================
ANIMATED COUNTER GLOW
================================================ */
@keyframes stat-glow {
0%, 100% {
text-shadow: 0 0 10px rgba(232, 119, 46, 0.3);
}
50% {
text-shadow: 0 0 20px rgba(232, 119, 46, 0.6);
}
}
.animate-stat-glow {
animation: stat-glow 2s ease-in-out infinite;
}

View File

@@ -0,0 +1,61 @@
"use client";
import { useEffect, useRef, useState } from "react";
interface AnimatedCounterProps {
end: number;
duration?: number;
suffix?: string;
prefix?: string;
className?: string;
}
export default function AnimatedCounter({
end,
duration = 2000,
suffix = "",
prefix = "",
className = "",
}: AnimatedCounterProps) {
const [count, setCount] = useState(0);
const ref = useRef<HTMLSpanElement>(null);
const hasAnimated = useRef(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && !hasAnimated.current) {
hasAnimated.current = true;
const startTime = performance.now();
const animate = (currentTime: number) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Ease out cubic
const eased = 1 - Math.pow(1 - progress, 3);
setCount(Math.floor(eased * end));
if (progress < 1) {
requestAnimationFrame(animate);
}
};
requestAnimationFrame(animate);
}
},
{ threshold: 0.5 }
);
observer.observe(el);
return () => observer.disconnect();
}, [end, duration]);
return (
<span ref={ref} className={className}>
{prefix}{count}{suffix}
</span>
);
}

View File

@@ -0,0 +1,20 @@
"use client";
export default function FloatingElements() {
return (
<div className="absolute inset-0 overflow-hidden pointer-events-none">
{/* Particules flottantes */}
<div className="absolute top-1/4 left-[10%] w-2 h-2 bg-orange/20 rounded-full animate-float-slow" />
<div className="absolute top-1/3 left-[25%] w-3 h-3 bg-white/5 rounded-full animate-float-medium" />
<div className="absolute top-1/2 left-[70%] w-2.5 h-2.5 bg-orange/15 rounded-full animate-float-fast" />
<div className="absolute top-[20%] left-[80%] w-1.5 h-1.5 bg-white/10 rounded-full animate-float-slow" />
<div className="absolute top-[60%] left-[15%] w-2 h-2 bg-orange/10 rounded-full animate-float-medium" />
<div className="absolute top-[70%] left-[50%] w-1 h-1 bg-white/15 rounded-full animate-float-fast" />
{/* Cercles décoratifs */}
<div className="absolute -top-20 -right-20 w-80 h-80 border border-white/5 rounded-full" />
<div className="absolute -bottom-32 -left-32 w-96 h-96 border border-orange/5 rounded-full" />
<div className="absolute top-1/4 right-[30%] w-48 h-48 border border-white/3 rounded-full" />
</div>
);
}

View File

@@ -0,0 +1,122 @@
"use client";
import { useEffect, useState } from "react";
export default function ParallaxRocket() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY);
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
// Rocket descends as user scrolls, with slight rotation
const translateY = scrollY * 0.6;
const rotate = Math.min(scrollY * 0.02, 15);
const opacity = Math.max(1 - scrollY / 1200, 0);
return (
<div
className="absolute right-4 md:right-12 lg:right-24 top-16 md:top-20 z-10 pointer-events-none"
style={{
transform: `translateY(${translateY}px) rotate(${rotate}deg)`,
opacity,
transition: "opacity 0.3s ease",
}}
>
{/* Rocket SVG */}
<svg
width="120"
height="200"
viewBox="0 0 120 200"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="w-20 h-32 md:w-28 md:h-48 lg:w-32 lg:h-52 drop-shadow-2xl"
>
{/* Flammes (animées) */}
<g className="animate-flame">
<path
d="M45 160 L60 195 L75 160"
fill="#E8772E"
opacity="0.9"
/>
<path
d="M50 160 L60 185 L70 160"
fill="#F5A623"
opacity="0.8"
/>
<path
d="M54 160 L60 175 L66 160"
fill="#FFD700"
opacity="0.7"
/>
</g>
{/* Corps principal de la fusée */}
<path
d="M40 140 L40 80 Q40 30 60 10 Q80 30 80 80 L80 140 Z"
fill="#1B2A4A"
stroke="#2A3D66"
strokeWidth="1"
/>
{/* Reflet sur le corps */}
<path
d="M50 130 L50 70 Q50 35 60 18 Q62 35 62 70 L62 130 Z"
fill="white"
opacity="0.08"
/>
{/* Fenêtre hublot */}
<circle cx="60" cy="70" r="12" fill="#2A3D66" stroke="#E8772E" strokeWidth="2" />
<circle cx="60" cy="70" r="8" fill="#111D36" />
<circle cx="56" cy="67" r="3" fill="white" opacity="0.3" />
{/* Ailerons gauche */}
<path
d="M40 120 L20 155 L40 145 Z"
fill="#E8772E"
/>
{/* Ailerons droit */}
<path
d="M80 120 L100 155 L80 145 Z"
fill="#E8772E"
/>
{/* Détails - lignes sur le corps */}
<line x1="40" y1="100" x2="80" y2="100" stroke="#2A3D66" strokeWidth="1" opacity="0.5" />
<line x1="40" y1="130" x2="80" y2="130" stroke="#2A3D66" strokeWidth="1" opacity="0.5" />
{/* Nez de la fusée - highlight */}
<path
d="M55 20 Q60 10 65 20"
fill="white"
opacity="0.15"
/>
{/* Logo H sur la fusée */}
<text
x="60"
y="108"
textAnchor="middle"
fill="white"
fontSize="16"
fontWeight="bold"
fontFamily="Inter, sans-serif"
>
H
</text>
</svg>
{/* Particules / traînée */}
<div className="absolute -bottom-4 left-1/2 -translate-x-1/2 flex gap-1">
<div className="w-1.5 h-1.5 bg-orange rounded-full animate-particle-1 opacity-60" />
<div className="w-1 h-1 bg-orange-light rounded-full animate-particle-2 opacity-40" />
<div className="w-1.5 h-1.5 bg-orange rounded-full animate-particle-3 opacity-50" />
</div>
</div>
);
}

View File

@@ -0,0 +1,62 @@
"use client";
import { useEffect, useRef, type ReactNode } from "react";
interface ScrollRevealProps {
children: ReactNode;
className?: string;
delay?: number;
direction?: "up" | "down" | "left" | "right" | "none";
duration?: number;
once?: boolean;
}
export default function ScrollReveal({
children,
className = "",
delay = 0,
direction = "up",
duration = 700,
once = true,
}: ScrollRevealProps) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
el.style.transitionDelay = `${delay}ms`;
el.classList.add("scroll-revealed");
if (once) observer.unobserve(el);
} else if (!once) {
el.classList.remove("scroll-revealed");
}
},
{ threshold: 0.15, rootMargin: "0px 0px -50px 0px" }
);
observer.observe(el);
return () => observer.disconnect();
}, [delay, once]);
const directionClass = {
up: "scroll-reveal-up",
down: "scroll-reveal-down",
left: "scroll-reveal-left",
right: "scroll-reveal-right",
none: "scroll-reveal-fade",
}[direction];
return (
<div
ref={ref}
className={`${directionClass} ${className}`}
style={{ transitionDuration: `${duration}ms` }}
>
{children}
</div>
);
}

View File

@@ -1,6 +1,10 @@
"use client";
import Image from "next/image"; import Image from "next/image";
import { urlFor } from "@/lib/sanity/client"; import { urlFor } from "@/lib/sanity/client";
import type { SiteSettings } from "@/lib/sanity/queries"; import type { SiteSettings } from "@/lib/sanity/queries";
import ScrollReveal from "@/components/animations/ScrollReveal";
import AnimatedCounter from "@/components/animations/AnimatedCounter";
interface AboutMeProps { interface AboutMeProps {
settings?: SiteSettings | null; settings?: SiteSettings | null;
@@ -18,106 +22,118 @@ export default function AboutMe({ settings }: AboutMeProps) {
<section id="qui-suis-je" className="py-16 md:py-24 bg-bg" aria-label="Qui suis-je"> <section id="qui-suis-je" className="py-16 md:py-24 bg-bg" aria-label="Qui suis-je">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Header */} {/* Header */}
<div className="text-center mb-12"> <ScrollReveal direction="up">
<span className="inline-block px-3 py-1.5 bg-navy/5 border border-navy/10 rounded-full text-navy text-xs font-semibold mb-4"> <div className="text-center mb-12">
Votre expert local <span className="inline-block px-3 py-1.5 bg-navy/5 border border-navy/10 rounded-full text-navy text-xs font-semibold mb-4">
</span> Votre expert local
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em]"> </span>
Pas une plateforme anonyme.{" "} <h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em]">
<span className="text-orange">Un voisin.</span> Pas une plateforme anonyme.{" "}
</h2> <span className="text-orange">Un voisin.</span>
</div> </h2>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-10 items-center"> <div className="grid grid-cols-1 md:grid-cols-2 gap-10 items-center">
{/* Left - Photo */} {/* Left - Photo */}
<div className="flex justify-center"> <ScrollReveal direction="left">
<div className="relative"> <div className="flex justify-center">
<div className="w-64 h-72 sm:w-72 sm:h-80 bg-bg-muted border-2 border-border rounded-2xl flex items-center justify-center overflow-hidden"> <div className="relative">
{photoUrl ? ( <div className="w-64 h-72 sm:w-72 sm:h-80 bg-bg-muted border-2 border-border rounded-2xl flex items-center justify-center overflow-hidden">
<Image {photoUrl ? (
src={photoUrl} <Image
alt={`Photo de ${name}`} src={photoUrl}
fill alt={`Photo de ${name}`}
className="object-cover" fill
sizes="(max-width: 640px) 256px, 288px" className="object-cover"
/> sizes="(max-width: 640px) 256px, 288px"
) : ( />
<div className="text-center p-6"> ) : (
<div className="w-20 h-20 bg-navy/10 rounded-full flex items-center justify-center mx-auto mb-4"> <div className="text-center p-6">
<svg className="w-10 h-10 text-navy/40" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div className="w-20 h-20 bg-navy/10 rounded-full flex items-center justify-center mx-auto mb-4">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /> <svg className="w-10 h-10 text-navy/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
<p className="text-text-muted text-sm">Votre photo ici</p>
<p className="text-text-muted text-xs mt-1">(configurable via Sanity)</p>
</div> </div>
<p className="text-text-muted text-sm">Votre photo ici</p> )}
<p className="text-text-muted text-xs mt-1">(configurable via Sanity)</p> </div>
</div> <div className="absolute -bottom-3 left-1/2 -translate-x-1/2 bg-orange text-white text-xs font-bold px-4 py-1.5 rounded-full shadow-md whitespace-nowrap">
)} Basé à {address.split(",")[0]}
</div> </div>
<div className="absolute -bottom-3 left-1/2 -translate-x-1/2 bg-orange text-white text-xs font-bold px-4 py-1.5 rounded-full shadow-md whitespace-nowrap">
Basé à {address.split(",")[0]}
</div> </div>
</div> </div>
</div> </ScrollReveal>
{/* Right - Text */} {/* Right - Text */}
<div> <ScrollReveal direction="right">
{bio ? ( <div>
<p className="text-text text-base sm:text-lg leading-relaxed mb-4"> {bio ? (
{bio}
</p>
) : (
<>
<p className="text-text text-base sm:text-lg leading-relaxed mb-4"> <p className="text-text text-base sm:text-lg leading-relaxed mb-4">
Je suis <strong className="text-navy">{name}</strong>, spécialisé dans la {bio}
visibilité locale et la construction de{" "}
<strong className="text-navy">systèmes de confiance en ligne</strong>{" "}
pour les TPE/PME du Nord.
</p> </p>
<p className="text-text-light text-base leading-relaxed mb-4"> ) : (
Je ne suis pas un call center parisien. Je connais la réalité de vos <>
chantiers à Douai, Orchies ou Valenciennes. Je sais que vous n&rsquo;avez pas <p className="text-text text-base sm:text-lg leading-relaxed mb-4">
le temps de gérer &ldquo;un truc internet&rdquo; et que vous voulez des résultats Je suis <strong className="text-navy">{name}</strong>, spécialisé dans la
concrets : des appels de <strong>vrais</strong> clients. visibilité locale et la construction de{" "}
</p> <strong className="text-navy">systèmes de confiance en ligne</strong>{" "}
</> pour les TPE/PME du Nord.
)} </p>
<p className="text-text-light text-base leading-relaxed mb-6"> <p className="text-text-light text-base leading-relaxed mb-4">
Mon approche : je vous construis un <strong className="text-navy">dossier de confiance</strong>{" "} Je ne suis pas un call center parisien. Je connais la réalité de vos
(Google + site + preuves) qui transforme votre bouche-à-oreille en système chantiers à Douai, Orchies ou Valenciennes. Je sais que vous n&rsquo;avez pas
permanent. Pas de jargon, pas de blabla &mdash; du concret. le temps de gérer &ldquo;un truc internet&rdquo; et que vous voulez des résultats
</p> concrets : des appels de <strong>vrais</strong> clients.
</p>
</>
)}
<p className="text-text-light text-base leading-relaxed mb-6">
Mon approche : je vous construis un <strong className="text-navy">dossier de confiance</strong>{" "}
(Google + site + preuves) qui transforme votre bouche-à-oreille en système
permanent. Pas de jargon, pas de blabla &mdash; du concret.
</p>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="bg-bg-white border border-border rounded-xl p-4 text-center"> <div className="bg-bg-white border border-border rounded-xl p-4 text-center">
<p className="text-2xl font-bold text-navy">100%</p> <p className="text-2xl font-bold text-navy">
<p className="text-text-muted text-xs mt-1">Local Nord</p> <AnimatedCounter end={100} suffix="%" />
</div> </p>
<div className="bg-bg-white border border-border rounded-xl p-4 text-center"> <p className="text-text-muted text-xs mt-1">Local Nord</p>
<p className="text-2xl font-bold text-navy">24h</p> </div>
<p className="text-text-muted text-xs mt-1">Délai de réponse</p> <div className="bg-bg-white border border-border rounded-xl p-4 text-center">
<p className="text-2xl font-bold text-navy">
<AnimatedCounter end={24} suffix="h" />
</p>
<p className="text-text-muted text-xs mt-1">Délai de réponse</p>
</div>
</div> </div>
</div> </div>
</div> </ScrollReveal>
</div> </div>
{/* Map */} {/* Map */}
<div className="mt-12 bg-bg-white border border-border rounded-2xl overflow-hidden"> <ScrollReveal direction="up" className="mt-12">
<div className="p-4 border-b border-border flex items-center gap-2"> <div className="bg-bg-white border border-border rounded-2xl overflow-hidden">
<svg className="w-5 h-5 text-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div className="p-4 border-b border-border flex items-center gap-2">
<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" /> <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="M15 11a3 3 0 11-6 0 3 3 0 016 0z" /> <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" />
</svg> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
<span className="text-navy font-semibold text-sm">Zone d&rsquo;intervention : Douai, Orchies, Arleux, Valenciennes et environs</span> </svg>
<span className="text-navy font-semibold text-sm">Zone d&rsquo;intervention : Douai, Orchies, Arleux, Valenciennes et environs</span>
</div>
<div className="relative h-48 sm:h-64">
<iframe
src={`https://www.openstreetmap.org/export/embed.html?bbox=${lng - 0.45}%2C${lat - 0.18}%2C${lng + 0.45}%2C${lat + 0.12}&layer=mapnik&marker=${lat}%2C${lng}`}
className="absolute inset-0 w-full h-full border-0"
title={`Carte de localisation - ${address.split(",")[0]}`}
loading="lazy"
/>
</div>
</div> </div>
<div className="relative h-48 sm:h-64"> </ScrollReveal>
<iframe
src={`https://www.openstreetmap.org/export/embed.html?bbox=${lng - 0.45}%2C${lat - 0.18}%2C${lng + 0.45}%2C${lat + 0.12}&layer=mapnik&marker=${lat}%2C${lng}`}
className="absolute inset-0 w-full h-full border-0"
title={`Carte de localisation - ${address.split(",")[0]}`}
loading="lazy"
/>
</div>
</div>
</div> </div>
</section> </section>
); );

View File

@@ -2,6 +2,7 @@
import { useState } from "react"; import { useState } from "react";
import Button from "@/components/ui/Button"; import Button from "@/components/ui/Button";
import ScrollReveal from "@/components/animations/ScrollReveal";
export default function Contact() { export default function Contact() {
const [submitted, setSubmitted] = useState(false); const [submitted, setSubmitted] = useState(false);
@@ -16,119 +17,123 @@ export default function Contact() {
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
{/* Left - Text */} {/* Left - Text */}
<div> <ScrollReveal direction="left">
<span className="inline-block px-3 py-1.5 bg-white/10 rounded-full text-orange text-xs font-semibold mb-4"> <div>
Audit gratuit <span className="inline-block px-3 py-1.5 bg-white/10 rounded-full text-orange text-xs font-semibold mb-4">
</span> Audit gratuit
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-4"> </span>
Pr&ecirc;t &agrave; arr&ecirc;ter de courir apr&egrave;s les chantiers ? <h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-4">
</h2> Prêt à arrêter de courir après les chantiers ?
<p className="text-white/70 text-base leading-relaxed mb-6"> </h2>
R&eacute;servez votre audit gratuit. Je regarde votre situation Google, <p className="text-white/70 text-base leading-relaxed mb-6">
votre visibilit&eacute; actuelle, et je vous dis concr&egrave;tement ce Réservez votre audit gratuit. Je regarde votre situation Google,
qu&rsquo;on peut am&eacute;liorer. Sans engagement, sans jargon. votre visibilité actuelle, et je vous dis concrètement ce
</p> qu&rsquo;on peut améliorer. Sans engagement, sans jargon.
</p>
<ul className="space-y-3"> <ul className="space-y-3">
{[ {[
"Audit de votre pr\u00e9sence Google actuelle", "Audit de votre présence Google actuelle",
"Analyse de vos concurrents locaux", "Analyse de vos concurrents locaux",
"Plan d\u2019action concret et chiffr\u00e9", "Plan d\u2019action concret et chiffré",
"R\u00e9ponse sous 24h", "Réponse sous 24h",
].map((item, i) => ( ].map((item, i) => (
<li key={i} className="flex items-center gap-3"> <li key={i} className="flex items-center gap-3">
<div className="w-5 h-5 bg-orange/20 rounded-full flex items-center justify-center shrink-0"> <div className="w-5 h-5 bg-orange/20 rounded-full flex items-center justify-center shrink-0">
<svg className="w-3 h-3 text-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-3 h-3 text-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg> </svg>
</div> </div>
<span className="text-white/80 text-sm">{item}</span> <span className="text-white/80 text-sm">{item}</span>
</li> </li>
))} ))}
</ul> </ul>
</div> </div>
</ScrollReveal>
{/* Right - Form */} {/* Right - Form */}
<div className="bg-white rounded-2xl p-6 sm:p-8 shadow-xl"> <ScrollReveal direction="right">
{submitted ? ( <div className="bg-white rounded-2xl p-6 sm:p-8 shadow-xl">
<div className="text-center py-8"> {submitted ? (
<div className="w-16 h-16 bg-success/10 rounded-full flex items-center justify-center mx-auto mb-4"> <div className="text-center py-8">
<svg className="w-8 h-8 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div className="w-16 h-16 bg-success/10 rounded-full flex items-center justify-center mx-auto mb-4">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" /> <svg className="w-8 h-8 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</div> </svg>
<h3 className="text-navy font-bold text-xl mb-2">Demande envoy&eacute;e !</h3>
<p className="text-text-light text-sm">
Je vous recontacte sous 24h pour planifier votre audit.
</p>
</div>
) : (
<>
<h3 className="text-navy font-bold text-xl mb-1">
R&eacute;server mon audit gratuit
</h3>
<p className="text-text-light text-sm mb-6">
Remplissez le formulaire, je vous recontacte rapidement.
</p>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="contact-name" className="block text-sm font-medium text-text mb-1.5">
Votre nom
</label>
<input
id="contact-name"
type="text"
required
placeholder="Marc Dupont"
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div> </div>
<div> <h3 className="text-navy font-bold text-xl mb-2">Demande envoyée !</h3>
<label htmlFor="contact-phone" className="block text-sm font-medium text-text mb-1.5"> <p className="text-text-light text-sm">
T&eacute;l&eacute;phone Je vous recontacte sous 24h pour planifier votre audit.
</label>
<input
id="contact-phone"
type="tel"
required
placeholder="06 12 34 56 78"
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div>
<div>
<label htmlFor="contact-metier" className="block text-sm font-medium text-text mb-1.5">
Votre m&eacute;tier
</label>
<input
id="contact-metier"
type="text"
required
placeholder="Couvreur, Menuisier, Paysagiste..."
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div>
<div>
<label htmlFor="contact-ville" className="block text-sm font-medium text-text mb-1.5">
Ville / Zone
</label>
<input
id="contact-ville"
type="text"
required
placeholder="Douai, Valenciennes, Orchies..."
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div>
<Button type="submit" size="lg" className="w-full">
R&eacute;server mon Audit Gratuit
</Button>
<p className="text-text-muted text-xs text-center">
Gratuit &middot; Sans engagement &middot; R&eacute;ponse sous 24h
</p> </p>
</form> </div>
</> ) : (
)} <>
</div> <h3 className="text-navy font-bold text-xl mb-1">
Réserver mon audit gratuit
</h3>
<p className="text-text-light text-sm mb-6">
Remplissez le formulaire, je vous recontacte rapidement.
</p>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="contact-name" className="block text-sm font-medium text-text mb-1.5">
Votre nom
</label>
<input
id="contact-name"
type="text"
required
placeholder="Marc Dupont"
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div>
<div>
<label htmlFor="contact-phone" className="block text-sm font-medium text-text mb-1.5">
Téléphone
</label>
<input
id="contact-phone"
type="tel"
required
placeholder="06 12 34 56 78"
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div>
<div>
<label htmlFor="contact-metier" className="block text-sm font-medium text-text mb-1.5">
Votre métier
</label>
<input
id="contact-metier"
type="text"
required
placeholder="Couvreur, Menuisier, Paysagiste..."
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div>
<div>
<label htmlFor="contact-ville" className="block text-sm font-medium text-text mb-1.5">
Ville / Zone
</label>
<input
id="contact-ville"
type="text"
required
placeholder="Douai, Valenciennes, Orchies..."
className="w-full px-4 py-3 bg-bg border border-border rounded-xl text-text text-sm placeholder:text-text-muted focus:border-orange focus:ring-1 focus:ring-orange outline-none transition-colors"
/>
</div>
<Button type="submit" size="lg" className="w-full">
Réserver mon Audit Gratuit
</Button>
<p className="text-text-muted text-xs text-center">
Gratuit &middot; Sans engagement &middot; Réponse sous 24h
</p>
</form>
</>
)}
</div>
</ScrollReveal>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -1,14 +1,17 @@
"use client";
import Card from "@/components/ui/Card"; import Card from "@/components/ui/Card";
import Link from "next/link"; import Link from "next/link";
import ScrollReveal from "@/components/animations/ScrollReveal";
const demos = [ const demos = [
{ {
title: "L\u2019Expertise Solide", title: "L\u2019Expertise Solide",
subtitle: "Pour ceux dont le travail doit durer 100 ans.", subtitle: "Pour ceux dont le travail doit durer 100 ans.",
pourQui: "Ma\u00e7ons, Couvreurs, Charpentiers.", pourQui: "Maçons, Couvreurs, Charpentiers.",
pointFort: "Slider \u00ab\u00a0Avant / Apr\u00e8s\u00a0\u00bb interactif + badges garanties (D\u00e9cennale, Qualibat, RGE) immanquables.", pointFort: "Slider «\u00a0Avant / Après\u00a0» interactif + badges garanties (Décennale, Qualibat, RGE) immanquables.",
fonctionnalite: "Formulaire intelligent : si Urgence Fuite \u2192 bouton rouge \u00ab\u00a0APPELER LE PATRON\u00a0\u00bb.", fonctionnalite: "Formulaire intelligent : si Urgence Fuite \u2192 bouton rouge «\u00a0APPELER LE PATRON\u00a0».",
cta: "Voir la D\u00e9mo Ma\u00e7onnerie", cta: "Voir la Démo Maçonnerie",
href: "/macon", href: "/macon",
icon: ( icon: (
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -17,12 +20,12 @@ const demos = [
), ),
}, },
{ {
title: "L\u2019Artisan Cr\u00e9ateur", title: "L\u2019Artisan Créateur",
subtitle: "Pour ceux qui vendent du beau et du confort.", subtitle: "Pour ceux qui vendent du beau et du confort.",
pourQui: "Paysagistes, Peintres, D\u00e9corateurs.", pourQui: "Paysagistes, Peintres, Décorateurs.",
pointFort: "Galerie filtrable par type + saisonnalit\u00e9 intelligente (le site change selon la saison).", pointFort: "Galerie filtrable par type + saisonnalité intelligente (le site change selon la saison).",
fonctionnalite: "Bouton WhatsApp flottant \u00ab\u00a0Je veux le m\u00eame jardin\u00a0\u00bb + immersion locale par ville.", fonctionnalite: "Bouton WhatsApp flottant «\u00a0Je veux le même jardin\u00a0» + immersion locale par ville.",
cta: "Voir la D\u00e9mo Paysagiste", cta: "Voir la Démo Paysagiste",
href: "/paysagiste", href: "/paysagiste",
icon: ( icon: (
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -31,12 +34,12 @@ const demos = [
), ),
}, },
{ {
title: "L\u2019Intervention \u00c9clair", title: "L\u2019Intervention Éclair",
subtitle: "Pour ceux qui sauvent la mise (et veulent \u00eatre pay\u00e9s vite).", subtitle: "Pour ceux qui sauvent la mise (et veulent être payés vite).",
pourQui: "Plombiers, \u00c9lectriciens, Serruriers.", pourQui: "Plombiers, Électriciens, Serruriers.",
pointFort: "Avis Google en haut + tarifs transparents + bouton d\u2019appel sticky sur mobile.", pointFort: "Avis Google en haut + tarifs transparents + bouton d\u2019appel sticky sur mobile.",
fonctionnalite: "Diagnostic en 3 clics : qualifie la panne + d\u00e9tecte si hors zone.", fonctionnalite: "Diagnostic en 3 clics : qualifie la panne + détecte si hors zone.",
cta: "Voir la D\u00e9mo Plombier", cta: "Voir la Démo Plombier",
href: "/plombier", href: "/plombier",
icon: ( icon: (
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -48,65 +51,69 @@ const demos = [
export default function DemosLive() { export default function DemosLive() {
return ( return (
<section id="demos" className="py-16 md:py-24 bg-bg" aria-label="D\u00e9mos live"> <section id="demos" className="py-16 md:py-24 bg-bg" aria-label="Démos live">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-14"> <ScrollReveal direction="up">
<span className="inline-block px-3 py-1.5 bg-orange/10 border border-orange/20 rounded-full text-orange text-xs font-semibold mb-4"> <div className="text-center mb-14">
D&eacute;mos Live <span className="inline-block px-3 py-1.5 bg-orange/10 border border-orange/20 rounded-full text-orange text-xs font-semibold mb-4">
</span> Démos Live
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3"> </span>
Ne signez pas sans voir.{" "} <h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3">
<span className="text-orange">Testez votre futur site maintenant.</span> Ne signez pas sans voir.{" "}
</h2> <span className="text-orange">Testez votre futur site maintenant.</span>
<p className="text-text-light text-base md:text-lg max-w-2xl mx-auto"> </h2>
Je ne vous demande pas de me croire sur parole. J&rsquo;ai construit 3 mod&egrave;les <p className="text-text-light text-base md:text-lg max-w-2xl mx-auto">
de &laquo;&nbsp;Dossiers de Confiance&nbsp;&raquo; optimis&eacute;s pour vos m&eacute;tiers. Je ne vous demande pas de me croire sur parole. J&rsquo;ai construit 3 modèles
Cliquez, naviguez, et imaginez votre logo &agrave; la place. de «&nbsp;Dossiers de Confiance&nbsp;» optimisés pour vos métiers.
</p> Cliquez, naviguez, et imaginez votre logo à la place.
</div> </p>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{demos.map((demo, i) => ( {demos.map((demo, i) => (
<Card key={i} hover className="flex flex-col p-0 overflow-hidden"> <ScrollReveal key={i} direction="up" delay={i * 200}>
{/* Header visuel */} <Card hover className="flex flex-col p-0 overflow-hidden h-full">
<div className="bg-navy p-6 text-center"> {/* Header visuel */}
<div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-3 text-orange"> <div className="bg-navy p-6 text-center">
{demo.icon} <div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-3 text-orange">
</div> {demo.icon}
<h3 className="text-white font-bold text-lg">{demo.title}</h3>
<p className="text-orange text-sm font-semibold">{demo.subtitle}</p>
</div>
{/* Content */}
<div className="p-5 flex-1 flex flex-col">
<div className="space-y-3 flex-1">
<div>
<p className="text-navy font-semibold text-xs uppercase tracking-wider mb-1">Pour qui ?</p>
<p className="text-text-light text-sm">{demo.pourQui}</p>
</div>
<div>
<p className="text-navy font-semibold text-xs uppercase tracking-wider mb-1">Le point fort</p>
<p className="text-text-light text-sm">{demo.pointFort}</p>
</div>
<div>
<p className="text-navy font-semibold text-xs uppercase tracking-wider mb-1">Fonctionnalit&eacute; cl&eacute;</p>
<p className="text-text-light text-sm">{demo.fonctionnalite}</p>
</div> </div>
<h3 className="text-white font-bold text-lg">{demo.title}</h3>
<p className="text-orange text-sm font-semibold">{demo.subtitle}</p>
</div> </div>
{/* CTA */} {/* Content */}
<Link <div className="p-5 flex-1 flex flex-col">
href={demo.href} <div className="space-y-3 flex-1">
className="mt-5 flex items-center justify-center gap-2 bg-orange text-white font-bold text-sm px-5 py-3 rounded-xl hover:bg-orange/90 transition-colors" <div>
> <p className="text-navy font-semibold text-xs uppercase tracking-wider mb-1">Pour qui ?</p>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <p className="text-text-light text-sm">{demo.pourQui}</p>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </div>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /> <div>
</svg> <p className="text-navy font-semibold text-xs uppercase tracking-wider mb-1">Le point fort</p>
{demo.cta} <p className="text-text-light text-sm">{demo.pointFort}</p>
</Link> </div>
</div> <div>
</Card> <p className="text-navy font-semibold text-xs uppercase tracking-wider mb-1">Fonctionnalité clé</p>
<p className="text-text-light text-sm">{demo.fonctionnalite}</p>
</div>
</div>
{/* CTA */}
<Link
href={demo.href}
className="mt-5 flex items-center justify-center gap-2 bg-orange text-white font-bold text-sm px-5 py-3 rounded-xl hover:bg-orange/90 hover:scale-[1.02] transition-all duration-300"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
{demo.cta}
</Link>
</div>
</Card>
</ScrollReveal>
))} ))}
</div> </div>
</div> </div>

View File

@@ -1,31 +1,32 @@
"use client"; "use client";
import { useState } from "react"; import { useState } from "react";
import ScrollReveal from "@/components/animations/ScrollReveal";
const faqs = [ const faqs = [
{ {
q: "J\u2019ai d\u00e9j\u00e0 une page Facebook, \u00e7a suffit non\u00a0?", q: "J\u2019ai déjà une page Facebook, ça suffit non\u00a0?",
a: "Facebook, c\u2019est pour les amis. Google, c\u2019est pour les clients qui ont un carnet de ch\u00e8ques et une urgence. Un site professionnel inspire 56% plus de confiance qu\u2019une simple page sociale.", a: "Facebook, c\u2019est pour les amis. Google, c\u2019est pour les clients qui ont un carnet de chèques et une urgence. Un site professionnel inspire 56% plus de confiance qu\u2019une simple page sociale.",
}, },
{ {
q: "Est-ce que je pourrai changer mes photos moi-m\u00eame\u00a0?", q: "Est-ce que je pourrai changer mes photos moi-même\u00a0?",
a: "Oui. Je vous donne acc\u00e8s \u00e0 une interface simplifi\u00e9e (Sanity). C\u2019est aussi simple que d\u2019envoyer un SMS. Vous changez une photo, le site se met \u00e0 jour tout seul.", a: "Oui. Je vous donne accès à une interface simplifiée (Sanity). C\u2019est aussi simple que d\u2019envoyer un SMS. Vous changez une photo, le site se met à jour tout seul.",
}, },
{ {
q: "C\u2019est quoi la diff\u00e9rence avec un site gratuit\u00a0?", q: "C\u2019est quoi la différence avec un site gratuit\u00a0?",
a: "Un site gratuit, c\u2019est comme construire une maison sans fondations. \u00c7a ne tient pas, Google ne le voit pas, et \u00e7a fait fuir les gros chantiers. Je vous construis un actif num\u00e9rique durable.", a: "Un site gratuit, c\u2019est comme construire une maison sans fondations. Ça ne tient pas, Google ne le voit pas, et ça fait fuir les gros chantiers. Je vous construis un actif numérique durable.",
}, },
{ {
q: "Combien co\u00fbte un site avec HookLab\u00a0?", q: "Combien coûte un site avec HookLab\u00a0?",
a: "Chaque projet est diff\u00e9rent. Je propose un audit gratuit pour comprendre votre situation et vous faire une proposition adapt\u00e9e \u00e0 votre activit\u00e9. Pas d\u2019abonnement cach\u00e9, pas de surprise.", a: "Chaque projet est différent. Je propose un audit gratuit pour comprendre votre situation et vous faire une proposition adaptée à votre activité. Pas d\u2019abonnement caché, pas de surprise.",
}, },
{ {
q: "Combien de temps pour avoir des r\u00e9sultats\u00a0?", q: "Combien de temps pour avoir des résultats\u00a0?",
a: "Le site est en ligne en 2-3 semaines. Les premiers r\u00e9sultats Google arrivent en 4 \u00e0 8 semaines selon votre zone et votre m\u00e9tier. Mais le site commence \u00e0 travailler pour vous d\u00e8s le jour 1.", a: "Le site est en ligne en 2-3 semaines. Les premiers résultats Google arrivent en 4 à 8 semaines selon votre zone et votre métier. Mais le site commence à travailler pour vous dès le jour 1.",
}, },
{ {
q: "J\u2019y connais rien en informatique, c\u2019est un probl\u00e8me\u00a0?", q: "J\u2019y connais rien en informatique, c\u2019est un problème\u00a0?",
a: "C\u2019est justement mon m\u00e9tier. Je m\u2019occupe de tout\u00a0: cr\u00e9ation, mise en ligne, r\u00e9f\u00e9rencement. Vous, vous continuez vos chantiers. Et si vous voulez modifier quelque chose, un simple message suffit.", a: "C\u2019est justement mon métier. Je m\u2019occupe de tout\u00a0: création, mise en ligne, référencement. Vous, vous continuez vos chantiers. Et si vous voulez modifier quelque chose, un simple message suffit.",
}, },
]; ];
@@ -43,7 +44,7 @@ export default function FAQ() {
}; };
return ( return (
<section id="faq" className="py-16 md:py-24 bg-bg" aria-label="Questions fr\u00e9quentes"> <section id="faq" className="py-16 md:py-24 bg-bg" aria-label="Questions fréquentes">
{/* JSON-LD for Google */} {/* JSON-LD for Google */}
<script <script
type="application/ld+json" type="application/ld+json"
@@ -52,49 +53,54 @@ export default function FAQ() {
<div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Header */} {/* Header */}
<div className="text-center mb-12"> <ScrollReveal direction="up">
<span className="inline-block px-3 py-1.5 bg-navy/5 border border-navy/10 rounded-full text-navy text-xs font-semibold mb-4"> <div className="text-center mb-12">
FAQ <span className="inline-block px-3 py-1.5 bg-navy/5 border border-navy/10 rounded-full text-navy text-xs font-semibold mb-4">
</span> FAQ
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3"> </span>
Questions Franches <h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3">
</h3> Questions Franches
</div> </h3>
</div>
</ScrollReveal>
{/* Accordion */} {/* Accordion */}
<div className="space-y-3"> <div className="space-y-3">
{faqs.map((faq, i) => ( {faqs.map((faq, i) => (
<div <ScrollReveal key={i} direction="up" delay={i * 100}>
key={i} <div className="bg-bg-white border border-border rounded-xl overflow-hidden hover:border-orange/20 transition-colors duration-300">
className="bg-bg-white border border-border rounded-xl overflow-hidden" <button
> className="w-full flex items-center justify-between p-5 text-left cursor-pointer hover:bg-bg-muted/50 transition-colors"
<button onClick={() => setOpenIndex(openIndex === i ? null : i)}
className="w-full flex items-center justify-between p-5 text-left cursor-pointer hover:bg-bg-muted/50 transition-colors" aria-expanded={openIndex === i}
onClick={() => setOpenIndex(openIndex === i ? null : i)}
aria-expanded={openIndex === i}
>
<span className="text-navy font-semibold text-sm sm:text-base pr-4">
{faq.q}
</span>
<svg
className={`w-5 h-5 text-text-muted shrink-0 transition-transform ${
openIndex === i ? "rotate-180" : ""
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
> >
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /> <span className="text-navy font-semibold text-sm sm:text-base pr-4">
</svg> {faq.q}
</button> </span>
{openIndex === i && ( <svg
<div className="px-5 pb-5"> className={`w-5 h-5 text-text-muted shrink-0 transition-transform duration-300 ${
<p className="text-text-light text-sm leading-relaxed"> openIndex === i ? "rotate-180" : ""
{faq.a} }`}
</p> fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
<div
className={`overflow-hidden transition-all duration-300 ${
openIndex === i ? "max-h-96 opacity-100" : "max-h-0 opacity-0"
}`}
>
<div className="px-5 pb-5">
<p className="text-text-light text-sm leading-relaxed">
{faq.a}
</p>
</div>
</div> </div>
)} </div>
</div> </ScrollReveal>
))} ))}
</div> </div>
</div> </div>

View File

@@ -1,63 +1,68 @@
"use client";
import Link from "next/link"; import Link from "next/link";
import ScrollReveal from "@/components/animations/ScrollReveal";
export default function Footer() { export default function Footer() {
return ( return (
<footer className="border-t border-border py-10 md:py-12 bg-bg-white"> <footer className="border-t border-border py-10 md:py-12 bg-bg-white">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8"> <ScrollReveal direction="up">
{/* Brand */} <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<div> {/* Brand */}
<Link href="/" className="flex items-center gap-2 mb-3" aria-label="HookLab - Accueil"> <div>
<div className="w-8 h-8 bg-navy rounded-lg flex items-center justify-center"> <Link href="/" className="flex items-center gap-2 mb-3" aria-label="HookLab - Accueil">
<span className="text-white font-bold text-sm">H</span> <div className="w-8 h-8 bg-navy rounded-lg flex items-center justify-center">
</div> <span className="text-white font-bold text-sm">H</span>
<span className="text-lg font-bold text-navy"> </div>
Hook<span className="text-orange">Lab</span> <span className="text-lg font-bold text-navy">
</span> Hook<span className="text-orange">Lab</span>
</Link> </span>
<p className="text-text-light text-sm leading-relaxed max-w-xs"> </Link>
Cr&eacute;ation de sites pour le B&acirc;timent et l&rsquo;Artisanat. <p className="text-text-light text-sm leading-relaxed max-w-xs">
</p> Création de sites pour le Bâtiment et l&rsquo;Artisanat.
<p className="text-text-muted text-xs mt-3"> </p>
59148 Flines-lez-Raches <p className="text-text-muted text-xs mt-3">
</p> 59148 Flines-lez-Raches
</div> </p>
</div>
{/* Expertises SEO */} {/* Expertises SEO */}
<div> <div>
<h4 className="text-navy font-semibold text-sm mb-4"> <h4 className="text-navy font-semibold text-sm mb-4">
Expertises Expertises
</h4> </h4>
<ul className="space-y-2 text-text-light text-sm"> <ul className="space-y-2 text-text-light text-sm">
<li>Site internet Couvreur</li> <li>Site internet Couvreur</li>
<li>SEO Ma&ccedil;onnerie</li> <li>SEO Maçonnerie</li>
<li>Webmaster Paysagiste</li> <li>Webmaster Paysagiste</li>
<li>Visibilit&eacute; Menuisier</li> <li>Visibilité Menuisier</li>
</ul> </ul>
</div> </div>
{/* Legal */} {/* Legal */}
<div> <div>
<h4 className="text-navy font-semibold text-sm mb-4">L&eacute;gal</h4> <h4 className="text-navy font-semibold text-sm mb-4">Légal</h4>
<ul className="space-y-2"> <ul className="space-y-2">
<li> <li>
<Link href="/mentions-legales" className="text-text-light hover:text-navy text-sm transition-colors"> <Link href="/mentions-legales" className="text-text-light hover:text-navy text-sm transition-colors">
Mentions l&eacute;gales Mentions légales
</Link> </Link>
</li> </li>
<li> <li>
<Link href="/confidentialite" className="text-text-light hover:text-navy text-sm transition-colors"> <Link href="/confidentialite" className="text-text-light hover:text-navy text-sm transition-colors">
Politique de Confidentialit&eacute; Politique de Confidentialité
</Link> </Link>
</li> </li>
<li> <li>
<Link href="/plan-du-site" className="text-text-light hover:text-navy text-sm transition-colors"> <Link href="/plan-du-site" className="text-text-light hover:text-navy text-sm transition-colors">
Plan du site Plan du site
</Link> </Link>
</li> </li>
</ul> </ul>
</div>
</div> </div>
</div> </ScrollReveal>
{/* Bottom SEO */} {/* Bottom SEO */}
<div className="border-t border-border mt-8 pt-6 flex flex-col md:flex-row items-center justify-between gap-3"> <div className="border-t border-border mt-8 pt-6 flex flex-col md:flex-row items-center justify-between gap-3">

View File

@@ -1,47 +1,142 @@
"use client";
import Button from "@/components/ui/Button"; import Button from "@/components/ui/Button";
import ParallaxRocket from "@/components/animations/ParallaxRocket";
import FloatingElements from "@/components/animations/FloatingElements";
export default function Hero() { export default function Hero() {
return ( return (
<section className="relative py-20 md:py-32 bg-navy overflow-hidden" aria-label="Introduction"> <section
{/* Overlay pattern */} className="relative min-h-[90vh] md:min-h-screen flex items-center bg-navy overflow-hidden"
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_50%,rgba(255,255,255,0.05),transparent_70%)]" /> aria-label="Introduction"
>
{/* Background gradient layers */}
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_20%_50%,rgba(232,119,46,0.08),transparent_60%)]" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_80%_20%,rgba(255,255,255,0.04),transparent_50%)]" />
<div className="relative max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> {/* Grid pattern overlay */}
<div
className="absolute inset-0 opacity-[0.03]"
style={{
backgroundImage:
"linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px)",
backgroundSize: "60px 60px",
}}
/>
{/* Floating decorative elements */}
<FloatingElements />
{/* Parallax Rocket */}
<ParallaxRocket />
<div className="relative z-20 max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 py-20 md:py-32">
<div className="max-w-3xl"> <div className="max-w-3xl">
{/* Badge */} {/* Badge animé */}
<span className="inline-block px-3 py-1.5 bg-orange/20 border border-orange/30 rounded-full text-orange text-xs font-semibold mb-6"> <span className="inline-flex items-center gap-2 px-4 py-2 bg-orange/15 border border-orange/25 rounded-full text-orange text-xs font-semibold mb-8 animate-fade-in-down">
<span className="w-2 h-2 bg-orange rounded-full animate-pulse" />
Flines-lez-Raches, Nord (59) Flines-lez-Raches, Nord (59)
</span> </span>
{/* H1 SEO */} {/* H1 avec animation staggered */}
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-[3.25rem] font-extrabold text-white leading-tight tracking-[-0.02em] mb-6"> <h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-extrabold text-white leading-[1.1] tracking-[-0.03em] mb-6">
Artisans du Nord : Votre site web doit &ecirc;tre aussi solide que{" "} <span className="block animate-hero-text-1">
<span className="text-orange">vos ouvrages.</span> Artisans du Nord :
</span>
<span className="block animate-hero-text-2">
Votre site web doit
</span>
<span className="block animate-hero-text-2">
être aussi solide que{" "}
</span>
<span className="block text-orange animate-hero-text-3 relative">
vos ouvrages.
{/* Underline animée */}
<span className="absolute -bottom-2 left-0 h-1 bg-orange/40 rounded-full animate-underline-grow" />
</span>
</h1> </h1>
{/* Subtitle */} {/* Sous-titre avec fade in */}
<p className="text-white/70 text-base sm:text-lg md:text-xl leading-relaxed mb-8 max-w-2xl"> <p className="text-white/65 text-lg sm:text-xl md:text-2xl leading-relaxed mb-10 max-w-2xl animate-fade-in-up animation-delay-600">
Un site professionnel &agrave; la hauteur de votre savoir-faire. Un site professionnel à la hauteur de votre savoir-faire.
Depuis Flines-lez-Raches, je con&ccedil;ois des vitrines num&eacute;riques Depuis Flines-lez-Raches, je conçois des vitrines numériques
performantes qui inspirent confiance et g&eacute;n&egrave;rent des demandes performantes qui inspirent confiance et génèrent des demandes
qualifi&eacute;es &agrave; Douai, Orchies et Valenciennes. qualifiées.
</p> </p>
{/* CTA */} {/* CTA buttons avec animation */}
<div className="flex flex-col sm:flex-row gap-4"> <div className="flex flex-col sm:flex-row gap-4 animate-fade-in-up animation-delay-800">
<a href="#contact"> <a href="#contact">
<Button size="lg" className="w-full sm:w-auto pulse-glow text-base"> <Button size="lg" className="w-full sm:w-auto pulse-glow text-base group">
D&Eacute;MARRER MON AUDIT GRATUIT <span className="flex items-center gap-2">
DÉMARRER MON AUDIT GRATUIT
<svg
className="w-5 h-5 transition-transform group-hover:translate-x-1"
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>
</Button> </Button>
</a> </a>
<a
href="#demos"
className="inline-flex items-center justify-center gap-2 px-6 py-3.5 border border-white/20 text-white font-semibold text-sm rounded-xl hover:bg-white/10 hover:border-white/30 transition-all duration-300"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
Découvrir nos réalisations
</a>
</div> </div>
{/* Trust line */} {/* Trust line animée */}
<p className="mt-5 text-white/50 text-sm"> <div className="mt-8 flex flex-wrap items-center gap-6 animate-fade-in-up animation-delay-1000">
R&eacute;ponse sous 24h &middot; Pas de jargon &middot; 100% G&eacute;r&eacute; pour vous. <div className="flex items-center gap-2">
</p> <div className="w-5 h-5 bg-success/20 rounded-full flex items-center justify-center">
<svg className="w-3 h-3 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
</div>
<span className="text-white/50 text-sm">Réponse sous 24h</span>
</div>
<div className="flex items-center gap-2">
<div className="w-5 h-5 bg-success/20 rounded-full flex items-center justify-center">
<svg className="w-3 h-3 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
</div>
<span className="text-white/50 text-sm">Pas de jargon</span>
</div>
<div className="flex items-center gap-2">
<div className="w-5 h-5 bg-success/20 rounded-full flex items-center justify-center">
<svg className="w-3 h-3 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
</div>
<span className="text-white/50 text-sm">100% Géré pour vous</span>
</div>
</div>
</div> </div>
</div> </div>
{/* Scroll indicator */}
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 z-20 animate-bounce-slow">
<a href="#problematique" aria-label="Défiler vers le bas" className="flex flex-col items-center gap-2 text-white/30 hover:text-white/50 transition-colors">
<span className="text-xs font-medium tracking-wider uppercase">Scroll</span>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
</svg>
</a>
</div>
</section> </section>
); );
} }

View File

@@ -1,56 +1,69 @@
"use client";
import ScrollReveal from "@/components/animations/ScrollReveal";
import AnimatedCounter from "@/components/animations/AnimatedCounter";
export default function Problematique() { export default function Problematique() {
return ( return (
<section className="py-16 md:py-24 bg-bg" aria-label="La probl&eacute;matique"> <section id="problematique" className="py-16 md:py-24 bg-bg" aria-label="La problématique">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12"> <ScrollReveal direction="up">
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-4"> <div className="text-center mb-12">
Vous &ecirc;tes un pro sur le chantier.{" "} <h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-4">
<span className="text-orange">Pourquoi votre pr&eacute;sence en ligne dit-elle le contraire&nbsp;?</span> Vous êtes un pro sur le chantier.{" "}
</h3> <span className="text-orange">Pourquoi votre présence en ligne dit-elle le contraire&nbsp;?</span>
<p className="text-text-light text-base md:text-lg"> </h3>
Marc, je sais ce que vous vivez&nbsp;: <p className="text-text-light text-base md:text-lg">
</p> Marc, je sais ce que vous vivez&nbsp;:
</div> </p>
</div>
</ScrollReveal>
<div className="space-y-5 mb-12"> <div className="space-y-5 mb-12">
{[ {[
{ {
title: "Le temps perdu", title: "Le temps perdu",
text: "Vos soir\u00e9es passent \u00e0 faire des devis pour des gens qui cherchent \u00ab\u00a0le moins cher\u00a0\u00bb sur Leboncoin.", text: "Vos soirées passent à faire des devis pour des gens qui cherchent «\u00a0le moins cher\u00a0» sur Leboncoin.",
}, },
{ {
title: "L\u2019invisibilit\u00e9", title: "L\u2019invisibilité",
text: "Des concurrents moins qualifi\u00e9s que vous sortent avant vous sur Google Maps.", text: "Des concurrents moins qualifiés que vous sortent avant vous sur Google Maps.",
}, },
{ {
title: "La peur de la technique", title: "La peur de la technique",
text: "On vous a parl\u00e9 de WordPress, de mises \u00e0 jour, de piratage\u2026 Vous voulez un outil, pas un deuxi\u00e8me travail.", text: "On vous a parlé de WordPress, de mises à jour, de piratage\u2026 Vous voulez un outil, pas un deuxième travail.",
}, },
].map((item, i) => ( ].map((item, i) => (
<div key={i} className="flex items-start gap-4 bg-bg-white border border-border rounded-xl p-5"> <ScrollReveal key={i} direction="left" delay={i * 150}>
<div className="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center shrink-0 mt-0.5"> <div className="flex items-start gap-4 bg-bg-white border border-border rounded-xl p-5 hover:border-orange/30 transition-colors duration-300">
<svg className="w-4 h-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div className="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center shrink-0 mt-0.5">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M6 18L18 6M6 6l12 12" /> <svg className="w-4 h-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
</svg> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<div>
<p className="text-navy font-bold text-base mb-1">{item.title}</p>
<p className="text-text-light text-sm leading-relaxed">{item.text}</p>
</div>
</div> </div>
<div> </ScrollReveal>
<p className="text-navy font-bold text-base mb-1">{item.title}</p>
<p className="text-text-light text-sm leading-relaxed">{item.text}</p>
</div>
</div>
))} ))}
</div> </div>
{/* Stat choc */} {/* Stat choc */}
<div className="bg-navy rounded-2xl p-6 md:p-8 text-center"> <ScrollReveal direction="none">
<p className="text-white text-base md:text-lg leading-relaxed"> <div className="bg-navy rounded-2xl p-6 md:p-8 text-center">
<strong className="text-orange text-2xl md:text-3xl font-extrabold block mb-2">93%</strong> <p className="text-white text-base md:text-lg leading-relaxed">
des acheteurs jugent votre cr&eacute;dibilit&eacute; sur le design de votre site.{" "} <strong className="text-orange text-2xl md:text-3xl font-extrabold block mb-2 animate-stat-glow">
<span className="text-white/70"> <AnimatedCounter end={93} suffix="%" />
Si votre site est lent ou &laquo;&nbsp;moche&nbsp;&raquo;, le client pense que votre travail l&rsquo;est aussi. </strong>
</span> des acheteurs jugent votre crédibilité sur le design de votre site.{" "}
</p> <span className="text-white/70">
</div> Si votre site est lent ou «&nbsp;moche&nbsp;», le client pense que votre travail l&rsquo;est aussi.
</span>
</p>
</div>
</ScrollReveal>
</div> </div>
</section> </section>
); );

View File

@@ -1,4 +1,7 @@
"use client";
import Card from "@/components/ui/Card"; import Card from "@/components/ui/Card";
import ScrollReveal from "@/components/animations/ScrollReveal";
const features = [ const features = [
{ {
@@ -10,7 +13,7 @@ const features = [
title: "Vitesse Supersonique", title: "Vitesse Supersonique",
subtitle: "Google adore", subtitle: "Google adore",
description: description:
"Votre site se charge instantan\u00e9ment sur mobile. C\u2019est crucial pour le r\u00e9f\u00e9rencement et pour vos clients press\u00e9s. La technologie des g\u00e9ants (Netflix, Airbnb) adapt\u00e9e aux artisans du Nord.", "Votre site se charge instantanément sur mobile. C\u2019est crucial pour le référencement et pour vos clients pressés. La technologie des géants (Netflix, Airbnb) adaptée aux artisans du Nord.",
}, },
{ {
icon: ( icon: (
@@ -18,10 +21,10 @@ const features = [
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg> </svg>
), ),
title: "S\u00e9curit\u00e9 Militaire", title: "Sécurité Militaire",
subtitle: "HTTPS inclus", subtitle: "HTTPS inclus",
description: description:
"Pas de base de donn\u00e9es \u00e0 pirater. Votre site est statique, blind\u00e9 et s\u00e9curis\u00e9 par d\u00e9faut (cadenas vert HTTPS obligatoire). Vos clients sont rassur\u00e9s.", "Pas de base de données à pirater. Votre site est statique, blindé et sécurisé par défaut (cadenas vert HTTPS obligatoire). Vos clients sont rassurés.",
}, },
{ {
icon: ( icon: (
@@ -30,45 +33,49 @@ const features = [
</svg> </svg>
), ),
title: "100% Mobile First", title: "100% Mobile First",
subtitle: "Con\u00e7u pour le pouce", subtitle: "Conçu pour le pouce",
description: description:
"Vos clients vous cherchent sur leur smartphone. Votre site est con\u00e7u pour le pouce, avec des boutons d\u2019appel \u00e9normes et accessibles.", "Vos clients vous cherchent sur leur smartphone. Votre site est conçu pour le pouce, avec des boutons d\u2019appel énormes et accessibles.",
}, },
]; ];
export default function System() { export default function System() {
return ( return (
<section id="systeme" className="relative py-16 md:py-24 bg-navy" aria-label="Le syst\u00e8me"> <section id="systeme" className="relative py-16 md:py-24 bg-navy" aria-label="Le système">
{/* Subtle pattern */} {/* Subtle pattern */}
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_30%,rgba(255,255,255,0.03),transparent_60%)]" /> <div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_30%,rgba(255,255,255,0.03),transparent_60%)]" />
<div className="relative max-w-6xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="relative max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-14"> <ScrollReveal direction="up">
<span className="inline-block px-3 py-1.5 bg-orange/20 border border-orange/30 rounded-full text-orange text-xs font-semibold mb-4"> <div className="text-center mb-14">
La Solution HookLab <span className="inline-block px-3 py-1.5 bg-orange/20 border border-orange/30 rounded-full text-orange text-xs font-semibold mb-4">
</span> La Solution HookLab
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-3"> </span>
Le &laquo;&nbsp;Dossier de Confiance&nbsp;&raquo; :{" "} <h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-3">
<span className="text-orange">Plus qu&rsquo;un site, une infrastructure.</span> Le «&nbsp;Dossier de Confiance&nbsp;» :{" "}
</h2> <span className="text-orange">Plus qu&rsquo;un site, une infrastructure.</span>
<p className="text-white/60 text-base md:text-lg max-w-2xl mx-auto"> </h2>
Je ne fais pas de sites &laquo;&nbsp;bricol&eacute;s&nbsp;&raquo; sur WordPress qui plantent si on ne les <p className="text-white/60 text-base md:text-lg max-w-2xl mx-auto">
met pas &agrave; jour. J&rsquo;utilise la technologie des g&eacute;ants adapt&eacute;e aux artisans du Nord. Je ne fais pas de sites «&nbsp;bricolés&nbsp;» sur WordPress qui plantent si on ne les
</p> met pas à jour. J&rsquo;utilise la technologie des géants adaptée aux artisans du Nord.
</div> </p>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{features.map((f, i) => ( {features.map((f, i) => (
<Card key={i} hover className="text-center bg-white/5 border-white/10 backdrop-blur-sm"> <ScrollReveal key={i} direction="up" delay={i * 200}>
<div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-5 text-orange"> <Card hover className="text-center bg-white/5 border-white/10 backdrop-blur-sm h-full">
{f.icon} <div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-5 text-orange">
</div> {f.icon}
<h3 className="text-white font-bold text-lg mb-1">{f.title}</h3> </div>
<p className="text-orange text-sm font-semibold mb-3">{f.subtitle}</p> <h3 className="text-white font-bold text-lg mb-1">{f.title}</h3>
<p className="text-white/60 text-sm leading-relaxed"> <p className="text-orange text-sm font-semibold mb-3">{f.subtitle}</p>
{f.description} <p className="text-white/60 text-sm leading-relaxed">
</p> {f.description}
</Card> </p>
</Card>
</ScrollReveal>
))} ))}
</div> </div>
</div> </div>