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:
280
app/globals.css
280
app/globals.css
@@ -108,3 +108,283 @@ html {
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
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;
|
||||
}
|
||||
|
||||
61
components/animations/AnimatedCounter.tsx
Normal file
61
components/animations/AnimatedCounter.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
20
components/animations/FloatingElements.tsx
Normal file
20
components/animations/FloatingElements.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
122
components/animations/ParallaxRocket.tsx
Normal file
122
components/animations/ParallaxRocket.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
62
components/animations/ScrollReveal.tsx
Normal file
62
components/animations/ScrollReveal.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { urlFor } from "@/lib/sanity/client";
|
||||
import type { SiteSettings } from "@/lib/sanity/queries";
|
||||
import ScrollReveal from "@/components/animations/ScrollReveal";
|
||||
import AnimatedCounter from "@/components/animations/AnimatedCounter";
|
||||
|
||||
interface AboutMeProps {
|
||||
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">
|
||||
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-12">
|
||||
<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">
|
||||
Votre expert local
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em]">
|
||||
Pas une plateforme anonyme.{" "}
|
||||
<span className="text-orange">Un voisin.</span>
|
||||
</h2>
|
||||
</div>
|
||||
<ScrollReveal direction="up">
|
||||
<div className="text-center mb-12">
|
||||
<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">
|
||||
Votre expert local
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em]">
|
||||
Pas une plateforme anonyme.{" "}
|
||||
<span className="text-orange">Un voisin.</span>
|
||||
</h2>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-10 items-center">
|
||||
{/* Left - Photo */}
|
||||
<div className="flex justify-center">
|
||||
<div className="relative">
|
||||
<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">
|
||||
{photoUrl ? (
|
||||
<Image
|
||||
src={photoUrl}
|
||||
alt={`Photo de ${name}`}
|
||||
fill
|
||||
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">
|
||||
<svg className="w-10 h-10 text-navy/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<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>
|
||||
<ScrollReveal direction="left">
|
||||
<div className="flex justify-center">
|
||||
<div className="relative">
|
||||
<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">
|
||||
{photoUrl ? (
|
||||
<Image
|
||||
src={photoUrl}
|
||||
alt={`Photo de ${name}`}
|
||||
fill
|
||||
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">
|
||||
<svg className="w-10 h-10 text-navy/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<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>
|
||||
<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 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>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Right - Text */}
|
||||
<div>
|
||||
{bio ? (
|
||||
<p className="text-text text-base sm:text-lg leading-relaxed mb-4">
|
||||
{bio}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<ScrollReveal direction="right">
|
||||
<div>
|
||||
{bio ? (
|
||||
<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
|
||||
visibilité locale et la construction de{" "}
|
||||
<strong className="text-navy">systèmes de confiance en ligne</strong>{" "}
|
||||
pour les TPE/PME du Nord.
|
||||
{bio}
|
||||
</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’avez pas
|
||||
le temps de gérer “un truc internet” et que vous voulez des résultats
|
||||
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 — du concret.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<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
|
||||
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 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’avez pas
|
||||
le temps de gérer “un truc internet” et que vous voulez des résultats
|
||||
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 — du concret.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<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-text-muted text-xs mt-1">Local Nord</p>
|
||||
</div>
|
||||
<div className="bg-bg-white border border-border rounded-xl p-4 text-center">
|
||||
<p className="text-2xl font-bold text-navy">24h</p>
|
||||
<p className="text-text-muted text-xs mt-1">Délai de réponse</p>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="bg-bg-white border border-border rounded-xl p-4 text-center">
|
||||
<p className="text-2xl font-bold text-navy">
|
||||
<AnimatedCounter end={100} suffix="%" />
|
||||
</p>
|
||||
<p className="text-text-muted text-xs mt-1">Local Nord</p>
|
||||
</div>
|
||||
<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>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
|
||||
{/* Map */}
|
||||
<div className="mt-12 bg-bg-white border border-border rounded-2xl overflow-hidden">
|
||||
<div className="p-4 border-b border-border flex items-center gap-2">
|
||||
<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="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>
|
||||
<span className="text-navy font-semibold text-sm">Zone d’intervention : Douai, Orchies, Arleux, Valenciennes et environs</span>
|
||||
<ScrollReveal direction="up" className="mt-12">
|
||||
<div className="bg-bg-white border border-border rounded-2xl overflow-hidden">
|
||||
<div className="p-4 border-b border-border flex items-center gap-2">
|
||||
<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="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>
|
||||
<span className="text-navy font-semibold text-sm">Zone d’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 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>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import Button from "@/components/ui/Button";
|
||||
import ScrollReveal from "@/components/animations/ScrollReveal";
|
||||
|
||||
export default function Contact() {
|
||||
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="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left - Text */}
|
||||
<div>
|
||||
<span className="inline-block px-3 py-1.5 bg-white/10 rounded-full text-orange text-xs font-semibold mb-4">
|
||||
Audit gratuit
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-4">
|
||||
Prêt à arrêter de courir après les chantiers ?
|
||||
</h2>
|
||||
<p className="text-white/70 text-base leading-relaxed mb-6">
|
||||
Réservez votre audit gratuit. Je regarde votre situation Google,
|
||||
votre visibilité actuelle, et je vous dis concrètement ce
|
||||
qu’on peut améliorer. Sans engagement, sans jargon.
|
||||
</p>
|
||||
<ScrollReveal direction="left">
|
||||
<div>
|
||||
<span className="inline-block px-3 py-1.5 bg-white/10 rounded-full text-orange text-xs font-semibold mb-4">
|
||||
Audit gratuit
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-4">
|
||||
Prêt à arrêter de courir après les chantiers ?
|
||||
</h2>
|
||||
<p className="text-white/70 text-base leading-relaxed mb-6">
|
||||
Réservez votre audit gratuit. Je regarde votre situation Google,
|
||||
votre visibilité actuelle, et je vous dis concrètement ce
|
||||
qu’on peut améliorer. Sans engagement, sans jargon.
|
||||
</p>
|
||||
|
||||
<ul className="space-y-3">
|
||||
{[
|
||||
"Audit de votre pr\u00e9sence Google actuelle",
|
||||
"Analyse de vos concurrents locaux",
|
||||
"Plan d\u2019action concret et chiffr\u00e9",
|
||||
"R\u00e9ponse sous 24h",
|
||||
].map((item, i) => (
|
||||
<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">
|
||||
<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" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-white/80 text-sm">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<ul className="space-y-3">
|
||||
{[
|
||||
"Audit de votre présence Google actuelle",
|
||||
"Analyse de vos concurrents locaux",
|
||||
"Plan d\u2019action concret et chiffré",
|
||||
"Réponse sous 24h",
|
||||
].map((item, i) => (
|
||||
<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">
|
||||
<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" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-white/80 text-sm">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Right - Form */}
|
||||
<div className="bg-white rounded-2xl p-6 sm:p-8 shadow-xl">
|
||||
{submitted ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="w-16 h-16 bg-success/10 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg className="w-8 h-8 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-navy font-bold text-xl mb-2">Demande envoyé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é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"
|
||||
/>
|
||||
<ScrollReveal direction="right">
|
||||
<div className="bg-white rounded-2xl p-6 sm:p-8 shadow-xl">
|
||||
{submitted ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="w-16 h-16 bg-success/10 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<svg className="w-8 h-8 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</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 · Sans engagement · Réponse sous 24h
|
||||
<h3 className="text-navy font-bold text-xl mb-2">Demande envoyée !</h3>
|
||||
<p className="text-text-light text-sm">
|
||||
Je vous recontacte sous 24h pour planifier votre audit.
|
||||
</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 · Sans engagement · Réponse sous 24h
|
||||
</p>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import Card from "@/components/ui/Card";
|
||||
import Link from "next/link";
|
||||
import ScrollReveal from "@/components/animations/ScrollReveal";
|
||||
|
||||
const demos = [
|
||||
{
|
||||
title: "L\u2019Expertise Solide",
|
||||
subtitle: "Pour ceux dont le travail doit durer 100 ans.",
|
||||
pourQui: "Ma\u00e7ons, Couvreurs, Charpentiers.",
|
||||
pointFort: "Slider \u00ab\u00a0Avant / Apr\u00e8s\u00a0\u00bb interactif + badges garanties (D\u00e9cennale, Qualibat, RGE) immanquables.",
|
||||
fonctionnalite: "Formulaire intelligent : si Urgence Fuite \u2192 bouton rouge \u00ab\u00a0APPELER LE PATRON\u00a0\u00bb.",
|
||||
cta: "Voir la D\u00e9mo Ma\u00e7onnerie",
|
||||
pourQui: "Maçons, Couvreurs, Charpentiers.",
|
||||
pointFort: "Slider «\u00a0Avant / Après\u00a0» interactif + badges garanties (Décennale, Qualibat, RGE) immanquables.",
|
||||
fonctionnalite: "Formulaire intelligent : si Urgence Fuite \u2192 bouton rouge «\u00a0APPELER LE PATRON\u00a0».",
|
||||
cta: "Voir la Démo Maçonnerie",
|
||||
href: "/macon",
|
||||
icon: (
|
||||
<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.",
|
||||
pourQui: "Paysagistes, Peintres, D\u00e9corateurs.",
|
||||
pointFort: "Galerie filtrable par type + saisonnalit\u00e9 intelligente (le site change selon la saison).",
|
||||
fonctionnalite: "Bouton WhatsApp flottant \u00ab\u00a0Je veux le m\u00eame jardin\u00a0\u00bb + immersion locale par ville.",
|
||||
cta: "Voir la D\u00e9mo Paysagiste",
|
||||
pourQui: "Paysagistes, Peintres, Décorateurs.",
|
||||
pointFort: "Galerie filtrable par type + saisonnalité intelligente (le site change selon la saison).",
|
||||
fonctionnalite: "Bouton WhatsApp flottant «\u00a0Je veux le même jardin\u00a0» + immersion locale par ville.",
|
||||
cta: "Voir la Démo Paysagiste",
|
||||
href: "/paysagiste",
|
||||
icon: (
|
||||
<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",
|
||||
subtitle: "Pour ceux qui sauvent la mise (et veulent \u00eatre pay\u00e9s vite).",
|
||||
pourQui: "Plombiers, \u00c9lectriciens, Serruriers.",
|
||||
title: "L\u2019Intervention Éclair",
|
||||
subtitle: "Pour ceux qui sauvent la mise (et veulent être payés vite).",
|
||||
pourQui: "Plombiers, Électriciens, Serruriers.",
|
||||
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.",
|
||||
cta: "Voir la D\u00e9mo Plombier",
|
||||
fonctionnalite: "Diagnostic en 3 clics : qualifie la panne + détecte si hors zone.",
|
||||
cta: "Voir la Démo Plombier",
|
||||
href: "/plombier",
|
||||
icon: (
|
||||
<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() {
|
||||
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="text-center mb-14">
|
||||
<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">
|
||||
Démos Live
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3">
|
||||
Ne signez pas sans voir.{" "}
|
||||
<span className="text-orange">Testez votre futur site maintenant.</span>
|
||||
</h2>
|
||||
<p className="text-text-light text-base md:text-lg max-w-2xl mx-auto">
|
||||
Je ne vous demande pas de me croire sur parole. J’ai construit 3 modèles
|
||||
de « Dossiers de Confiance » optimisés pour vos métiers.
|
||||
Cliquez, naviguez, et imaginez votre logo à la place.
|
||||
</p>
|
||||
</div>
|
||||
<ScrollReveal direction="up">
|
||||
<div className="text-center mb-14">
|
||||
<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">
|
||||
Démos Live
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3">
|
||||
Ne signez pas sans voir.{" "}
|
||||
<span className="text-orange">Testez votre futur site maintenant.</span>
|
||||
</h2>
|
||||
<p className="text-text-light text-base md:text-lg max-w-2xl mx-auto">
|
||||
Je ne vous demande pas de me croire sur parole. J’ai construit 3 modèles
|
||||
de « Dossiers de Confiance » optimisés pour vos métiers.
|
||||
Cliquez, naviguez, et imaginez votre logo à la place.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{demos.map((demo, i) => (
|
||||
<Card key={i} hover className="flex flex-col p-0 overflow-hidden">
|
||||
{/* Header visuel */}
|
||||
<div className="bg-navy p-6 text-center">
|
||||
<div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-3 text-orange">
|
||||
{demo.icon}
|
||||
</div>
|
||||
<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é clé</p>
|
||||
<p className="text-text-light text-sm">{demo.fonctionnalite}</p>
|
||||
<ScrollReveal key={i} direction="up" delay={i * 200}>
|
||||
<Card hover className="flex flex-col p-0 overflow-hidden h-full">
|
||||
{/* Header visuel */}
|
||||
<div className="bg-navy p-6 text-center">
|
||||
<div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-3 text-orange">
|
||||
{demo.icon}
|
||||
</div>
|
||||
<h3 className="text-white font-bold text-lg">{demo.title}</h3>
|
||||
<p className="text-orange text-sm font-semibold">{demo.subtitle}</p>
|
||||
</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 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 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>
|
||||
{/* 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é 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>
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import ScrollReveal from "@/components/animations/ScrollReveal";
|
||||
|
||||
const faqs = [
|
||||
{
|
||||
q: "J\u2019ai d\u00e9j\u00e0 une page Facebook, \u00e7a 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.",
|
||||
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è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?",
|
||||
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.",
|
||||
q: "Est-ce que je pourrai changer mes photos moi-même\u00a0?",
|
||||
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?",
|
||||
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.",
|
||||
q: "C\u2019est quoi la différence avec un site gratuit\u00a0?",
|
||||
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?",
|
||||
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.",
|
||||
q: "Combien coûte un site avec HookLab\u00a0?",
|
||||
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?",
|
||||
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.",
|
||||
q: "Combien de temps pour avoir des résultats\u00a0?",
|
||||
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?",
|
||||
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.",
|
||||
q: "J\u2019y connais rien en informatique, c\u2019est un problème\u00a0?",
|
||||
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 (
|
||||
<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 */}
|
||||
<script
|
||||
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">
|
||||
{/* Header */}
|
||||
<div className="text-center mb-12">
|
||||
<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">
|
||||
FAQ
|
||||
</span>
|
||||
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3">
|
||||
Questions Franches
|
||||
</h3>
|
||||
</div>
|
||||
<ScrollReveal direction="up">
|
||||
<div className="text-center mb-12">
|
||||
<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">
|
||||
FAQ
|
||||
</span>
|
||||
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-3">
|
||||
Questions Franches
|
||||
</h3>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Accordion */}
|
||||
<div className="space-y-3">
|
||||
{faqs.map((faq, i) => (
|
||||
<div
|
||||
key={i}
|
||||
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"
|
||||
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"
|
||||
<ScrollReveal key={i} direction="up" delay={i * 100}>
|
||||
<div className="bg-bg-white border border-border rounded-xl overflow-hidden hover:border-orange/20 transition-colors duration-300">
|
||||
<button
|
||||
className="w-full flex items-center justify-between p-5 text-left cursor-pointer hover:bg-bg-muted/50 transition-colors"
|
||||
onClick={() => setOpenIndex(openIndex === i ? null : i)}
|
||||
aria-expanded={openIndex === i}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
{openIndex === i && (
|
||||
<div className="px-5 pb-5">
|
||||
<p className="text-text-light text-sm leading-relaxed">
|
||||
{faq.a}
|
||||
</p>
|
||||
<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 duration-300 ${
|
||||
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" />
|
||||
</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>
|
||||
</ScrollReveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,63 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import ScrollReveal from "@/components/animations/ScrollReveal";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<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="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* Brand */}
|
||||
<div>
|
||||
<Link href="/" className="flex items-center gap-2 mb-3" aria-label="HookLab - Accueil">
|
||||
<div className="w-8 h-8 bg-navy rounded-lg flex items-center justify-center">
|
||||
<span className="text-white font-bold text-sm">H</span>
|
||||
</div>
|
||||
<span className="text-lg font-bold text-navy">
|
||||
Hook<span className="text-orange">Lab</span>
|
||||
</span>
|
||||
</Link>
|
||||
<p className="text-text-light text-sm leading-relaxed max-w-xs">
|
||||
Création de sites pour le Bâtiment et l’Artisanat.
|
||||
</p>
|
||||
<p className="text-text-muted text-xs mt-3">
|
||||
59148 Flines-lez-Raches
|
||||
</p>
|
||||
</div>
|
||||
<ScrollReveal direction="up">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* Brand */}
|
||||
<div>
|
||||
<Link href="/" className="flex items-center gap-2 mb-3" aria-label="HookLab - Accueil">
|
||||
<div className="w-8 h-8 bg-navy rounded-lg flex items-center justify-center">
|
||||
<span className="text-white font-bold text-sm">H</span>
|
||||
</div>
|
||||
<span className="text-lg font-bold text-navy">
|
||||
Hook<span className="text-orange">Lab</span>
|
||||
</span>
|
||||
</Link>
|
||||
<p className="text-text-light text-sm leading-relaxed max-w-xs">
|
||||
Création de sites pour le Bâtiment et l’Artisanat.
|
||||
</p>
|
||||
<p className="text-text-muted text-xs mt-3">
|
||||
59148 Flines-lez-Raches
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Expertises SEO */}
|
||||
<div>
|
||||
<h4 className="text-navy font-semibold text-sm mb-4">
|
||||
Expertises
|
||||
</h4>
|
||||
<ul className="space-y-2 text-text-light text-sm">
|
||||
<li>Site internet Couvreur</li>
|
||||
<li>SEO Maçonnerie</li>
|
||||
<li>Webmaster Paysagiste</li>
|
||||
<li>Visibilité Menuisier</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* Expertises SEO */}
|
||||
<div>
|
||||
<h4 className="text-navy font-semibold text-sm mb-4">
|
||||
Expertises
|
||||
</h4>
|
||||
<ul className="space-y-2 text-text-light text-sm">
|
||||
<li>Site internet Couvreur</li>
|
||||
<li>SEO Maçonnerie</li>
|
||||
<li>Webmaster Paysagiste</li>
|
||||
<li>Visibilité Menuisier</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Legal */}
|
||||
<div>
|
||||
<h4 className="text-navy font-semibold text-sm mb-4">Légal</h4>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<Link href="/mentions-legales" className="text-text-light hover:text-navy text-sm transition-colors">
|
||||
Mentions légales
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/confidentialite" className="text-text-light hover:text-navy text-sm transition-colors">
|
||||
Politique de Confidentialité
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/plan-du-site" className="text-text-light hover:text-navy text-sm transition-colors">
|
||||
Plan du site
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
{/* Legal */}
|
||||
<div>
|
||||
<h4 className="text-navy font-semibold text-sm mb-4">Légal</h4>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<Link href="/mentions-legales" className="text-text-light hover:text-navy text-sm transition-colors">
|
||||
Mentions légales
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/confidentialite" className="text-text-light hover:text-navy text-sm transition-colors">
|
||||
Politique de Confidentialité
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/plan-du-site" className="text-text-light hover:text-navy text-sm transition-colors">
|
||||
Plan du site
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* Bottom SEO */}
|
||||
<div className="border-t border-border mt-8 pt-6 flex flex-col md:flex-row items-center justify-between gap-3">
|
||||
|
||||
@@ -1,47 +1,142 @@
|
||||
"use client";
|
||||
|
||||
import Button from "@/components/ui/Button";
|
||||
import ParallaxRocket from "@/components/animations/ParallaxRocket";
|
||||
import FloatingElements from "@/components/animations/FloatingElements";
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<section className="relative py-20 md:py-32 bg-navy overflow-hidden" aria-label="Introduction">
|
||||
{/* Overlay pattern */}
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_50%,rgba(255,255,255,0.05),transparent_70%)]" />
|
||||
<section
|
||||
className="relative min-h-[90vh] md:min-h-screen flex items-center bg-navy overflow-hidden"
|
||||
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">
|
||||
{/* Badge */}
|
||||
<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">
|
||||
{/* Badge animé */}
|
||||
<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)
|
||||
</span>
|
||||
|
||||
{/* H1 SEO */}
|
||||
<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">
|
||||
Artisans du Nord : Votre site web doit être aussi solide que{" "}
|
||||
<span className="text-orange">vos ouvrages.</span>
|
||||
{/* H1 avec animation staggered */}
|
||||
<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">
|
||||
<span className="block animate-hero-text-1">
|
||||
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>
|
||||
|
||||
{/* Subtitle */}
|
||||
<p className="text-white/70 text-base sm:text-lg md:text-xl leading-relaxed mb-8 max-w-2xl">
|
||||
Un site professionnel à la hauteur de votre savoir-faire.
|
||||
Depuis Flines-lez-Raches, je conçois des vitrines numériques
|
||||
performantes qui inspirent confiance et génèrent des demandes
|
||||
qualifiées à Douai, Orchies et Valenciennes.
|
||||
{/* Sous-titre avec fade in */}
|
||||
<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 à la hauteur de votre savoir-faire.
|
||||
Depuis Flines-lez-Raches, je conçois des vitrines numériques
|
||||
performantes qui inspirent confiance et génèrent des demandes
|
||||
qualifiées.
|
||||
</p>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
{/* CTA buttons avec animation */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 animate-fade-in-up animation-delay-800">
|
||||
<a href="#contact">
|
||||
<Button size="lg" className="w-full sm:w-auto pulse-glow text-base">
|
||||
DÉMARRER MON AUDIT GRATUIT
|
||||
<Button size="lg" className="w-full sm:w-auto pulse-glow text-base group">
|
||||
<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>
|
||||
</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>
|
||||
|
||||
{/* Trust line */}
|
||||
<p className="mt-5 text-white/50 text-sm">
|
||||
Réponse sous 24h · Pas de jargon · 100% Géré pour vous.
|
||||
</p>
|
||||
{/* Trust line animée */}
|
||||
<div className="mt-8 flex flex-wrap items-center gap-6 animate-fade-in-up animation-delay-1000">
|
||||
<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">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>
|
||||
|
||||
{/* 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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,56 +1,69 @@
|
||||
"use client";
|
||||
|
||||
import ScrollReveal from "@/components/animations/ScrollReveal";
|
||||
import AnimatedCounter from "@/components/animations/AnimatedCounter";
|
||||
|
||||
export default function Problematique() {
|
||||
return (
|
||||
<section className="py-16 md:py-24 bg-bg" aria-label="La problé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="text-center mb-12">
|
||||
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-4">
|
||||
Vous êtes un pro sur le chantier.{" "}
|
||||
<span className="text-orange">Pourquoi votre présence en ligne dit-elle le contraire ?</span>
|
||||
</h3>
|
||||
<p className="text-text-light text-base md:text-lg">
|
||||
Marc, je sais ce que vous vivez :
|
||||
</p>
|
||||
</div>
|
||||
<ScrollReveal direction="up">
|
||||
<div className="text-center mb-12">
|
||||
<h3 className="text-2xl md:text-3xl lg:text-4xl font-bold text-navy tracking-[-0.02em] mb-4">
|
||||
Vous êtes un pro sur le chantier.{" "}
|
||||
<span className="text-orange">Pourquoi votre présence en ligne dit-elle le contraire ?</span>
|
||||
</h3>
|
||||
<p className="text-text-light text-base md:text-lg">
|
||||
Marc, je sais ce que vous vivez :
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="space-y-5 mb-12">
|
||||
{[
|
||||
{
|
||||
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",
|
||||
text: "Des concurrents moins qualifi\u00e9s que vous sortent avant vous sur Google Maps.",
|
||||
title: "L\u2019invisibilité",
|
||||
text: "Des concurrents moins qualifiés que vous sortent avant vous sur Google Maps.",
|
||||
},
|
||||
{
|
||||
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) => (
|
||||
<div key={i} className="flex items-start gap-4 bg-bg-white border border-border rounded-xl p-5">
|
||||
<div className="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center shrink-0 mt-0.5">
|
||||
<svg className="w-4 h-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
<ScrollReveal key={i} direction="left" delay={i * 150}>
|
||||
<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">
|
||||
<div className="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center shrink-0 mt-0.5">
|
||||
<svg className="w-4 h-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<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>
|
||||
<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>
|
||||
</ScrollReveal>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Stat choc */}
|
||||
<div className="bg-navy rounded-2xl p-6 md:p-8 text-center">
|
||||
<p className="text-white text-base md:text-lg leading-relaxed">
|
||||
<strong className="text-orange text-2xl md:text-3xl font-extrabold block mb-2">93%</strong>
|
||||
des acheteurs jugent votre crédibilité sur le design de votre site.{" "}
|
||||
<span className="text-white/70">
|
||||
Si votre site est lent ou « moche », le client pense que votre travail l’est aussi.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<ScrollReveal direction="none">
|
||||
<div className="bg-navy rounded-2xl p-6 md:p-8 text-center">
|
||||
<p className="text-white text-base md:text-lg leading-relaxed">
|
||||
<strong className="text-orange text-2xl md:text-3xl font-extrabold block mb-2 animate-stat-glow">
|
||||
<AnimatedCounter end={93} suffix="%" />
|
||||
</strong>
|
||||
des acheteurs jugent votre crédibilité sur le design de votre site.{" "}
|
||||
<span className="text-white/70">
|
||||
Si votre site est lent ou « moche », le client pense que votre travail l’est aussi.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import Card from "@/components/ui/Card";
|
||||
import ScrollReveal from "@/components/animations/ScrollReveal";
|
||||
|
||||
const features = [
|
||||
{
|
||||
@@ -10,7 +13,7 @@ const features = [
|
||||
title: "Vitesse Supersonique",
|
||||
subtitle: "Google adore",
|
||||
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: (
|
||||
@@ -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" />
|
||||
</svg>
|
||||
),
|
||||
title: "S\u00e9curit\u00e9 Militaire",
|
||||
title: "Sécurité Militaire",
|
||||
subtitle: "HTTPS inclus",
|
||||
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: (
|
||||
@@ -30,45 +33,49 @@ const features = [
|
||||
</svg>
|
||||
),
|
||||
title: "100% Mobile First",
|
||||
subtitle: "Con\u00e7u pour le pouce",
|
||||
subtitle: "Conçu pour le pouce",
|
||||
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() {
|
||||
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 */}
|
||||
<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="text-center mb-14">
|
||||
<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">
|
||||
La Solution HookLab
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-3">
|
||||
Le « Dossier de Confiance » :{" "}
|
||||
<span className="text-orange">Plus qu’un site, une infrastructure.</span>
|
||||
</h2>
|
||||
<p className="text-white/60 text-base md:text-lg max-w-2xl mx-auto">
|
||||
Je ne fais pas de sites « bricolés » sur WordPress qui plantent si on ne les
|
||||
met pas à jour. J’utilise la technologie des géants adaptée aux artisans du Nord.
|
||||
</p>
|
||||
</div>
|
||||
<ScrollReveal direction="up">
|
||||
<div className="text-center mb-14">
|
||||
<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">
|
||||
La Solution HookLab
|
||||
</span>
|
||||
<h2 className="text-2xl md:text-3xl lg:text-4xl font-bold text-white tracking-[-0.02em] mb-3">
|
||||
Le « Dossier de Confiance » :{" "}
|
||||
<span className="text-orange">Plus qu’un site, une infrastructure.</span>
|
||||
</h2>
|
||||
<p className="text-white/60 text-base md:text-lg max-w-2xl mx-auto">
|
||||
Je ne fais pas de sites « bricolés » sur WordPress qui plantent si on ne les
|
||||
met pas à jour. J’utilise la technologie des géants adaptée aux artisans du Nord.
|
||||
</p>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{features.map((f, i) => (
|
||||
<Card key={i} hover className="text-center bg-white/5 border-white/10 backdrop-blur-sm">
|
||||
<div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-5 text-orange">
|
||||
{f.icon}
|
||||
</div>
|
||||
<h3 className="text-white font-bold text-lg mb-1">{f.title}</h3>
|
||||
<p className="text-orange text-sm font-semibold mb-3">{f.subtitle}</p>
|
||||
<p className="text-white/60 text-sm leading-relaxed">
|
||||
{f.description}
|
||||
</p>
|
||||
</Card>
|
||||
<ScrollReveal key={i} direction="up" delay={i * 200}>
|
||||
<Card hover className="text-center bg-white/5 border-white/10 backdrop-blur-sm h-full">
|
||||
<div className="w-16 h-16 bg-orange/20 rounded-2xl flex items-center justify-center mx-auto mb-5 text-orange">
|
||||
{f.icon}
|
||||
</div>
|
||||
<h3 className="text-white font-bold text-lg mb-1">{f.title}</h3>
|
||||
<p className="text-orange text-sm font-semibold mb-3">{f.subtitle}</p>
|
||||
<p className="text-white/60 text-sm leading-relaxed">
|
||||
{f.description}
|
||||
</p>
|
||||
</Card>
|
||||
</ScrollReveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user