231 lines
7.0 KiB
TypeScript
231 lines
7.0 KiB
TypeScript
"use client";
|
|
|
|
import { motion } from "framer-motion";
|
|
import { useInView } from "framer-motion";
|
|
import { useRef } from "react";
|
|
import { CheckCircle, Calendar, Truck, Sparkles } from "lucide-react";
|
|
|
|
interface ProcessStep {
|
|
id: number;
|
|
title: string;
|
|
description: string;
|
|
icon: React.ReactNode;
|
|
color: string;
|
|
bgColor: string;
|
|
}
|
|
|
|
const processSteps: ProcessStep[] = [
|
|
{
|
|
id: 1,
|
|
title: "Get a Quote",
|
|
description: "Fill out our discovery form to get a personalized estimate tailored to your specific project needs.",
|
|
icon: <Sparkles className="h-8 w-8" />,
|
|
color: "text-olive-700",
|
|
bgColor: "bg-olive-100"
|
|
},
|
|
{
|
|
id: 2,
|
|
title: "Schedule",
|
|
description: "We'll coordinate a convenient time for your project that works with your schedule and timeline.",
|
|
icon: <Calendar className="h-8 w-8" />,
|
|
color: "text-taupe-700",
|
|
bgColor: "bg-taupe-100"
|
|
},
|
|
{
|
|
id: 3,
|
|
title: "Deliver",
|
|
description: "Professional service with quality craftsmanship and attention to every detail of your project.",
|
|
icon: <CheckCircle className="h-8 w-8" />,
|
|
color: "text-olive-700",
|
|
bgColor: "bg-olive-100"
|
|
}
|
|
];
|
|
|
|
export default function EnhancedProcess() {
|
|
const ref = useRef(null);
|
|
const isInView = useInView(ref, { once: true, amount: 0.3 });
|
|
|
|
// Container animation variants
|
|
const containerVariants = {
|
|
hidden: { opacity: 0 },
|
|
visible: {
|
|
opacity: 1,
|
|
transition: {
|
|
staggerChildren: 0.3,
|
|
delayChildren: 0.2
|
|
}
|
|
}
|
|
};
|
|
|
|
// Step card animation variants
|
|
const stepVariants = {
|
|
hidden: { opacity: 0, y: 50, rotateX: -15 },
|
|
visible: {
|
|
opacity: 1,
|
|
y: 0,
|
|
rotateX: 0,
|
|
transition: {
|
|
duration: 0.6,
|
|
type: "spring" as const,
|
|
stiffness: 100,
|
|
damping: 15
|
|
}
|
|
}
|
|
};
|
|
|
|
// Connecting line animation variants
|
|
const lineVariants = {
|
|
hidden: { scaleX: 0 },
|
|
visible: {
|
|
scaleX: 1,
|
|
transition: {
|
|
duration: 0.6,
|
|
ease: "easeOut" as const
|
|
}
|
|
}
|
|
};
|
|
|
|
// Icon animation variants
|
|
const iconVariants = {
|
|
hidden: { scale: 0, rotate: -180 },
|
|
visible: {
|
|
scale: 1,
|
|
rotate: 0,
|
|
transition: {
|
|
type: "spring" as const,
|
|
stiffness: 200,
|
|
damping: 15
|
|
}
|
|
}
|
|
};
|
|
|
|
// Number animation variants
|
|
const numberVariants = {
|
|
hidden: { scale: 0 },
|
|
visible: {
|
|
scale: 1,
|
|
transition: {
|
|
type: "spring" as const,
|
|
stiffness: 300,
|
|
damping: 20
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section ref={ref} className="py-20 bg-white">
|
|
<div className="max-w-7xl mx-auto px-4">
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
|
transition={{ duration: 0.6 }}
|
|
className="text-center mb-12"
|
|
>
|
|
<h2 className="text-3xl md:text-4xl font-semibold text-taupe-900 mb-4 [text-wrap:balance]">
|
|
Our Process
|
|
</h2>
|
|
<p className="text-taupe-700 text-lg max-w-2xl mx-auto">
|
|
A simple, transparent process from quote to completion
|
|
</p>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
variants={containerVariants}
|
|
initial="hidden"
|
|
animate={isInView ? "visible" : "hidden"}
|
|
className="relative"
|
|
>
|
|
{/* Connecting lines */}
|
|
<div className="absolute top-24 left-0 right-0 hidden md:block">
|
|
{processSteps.slice(0, -1).map((_, i) => (
|
|
<motion.div
|
|
key={i}
|
|
variants={lineVariants}
|
|
className="absolute h-1 bg-gradient-to-r from-olive-300 to-taupe-300"
|
|
style={{
|
|
left: `${(i * 33.33) + 16.66}%`,
|
|
width: `${33.33}%`,
|
|
top: '2rem'
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 relative">
|
|
{processSteps.map((step, i) => (
|
|
<motion.div
|
|
key={step.id}
|
|
variants={stepVariants}
|
|
whileHover={{
|
|
scale: 1.02,
|
|
transition: { duration: 0.2 }
|
|
}}
|
|
className="group"
|
|
custom={i}
|
|
>
|
|
<div className="text-center rounded-2xl border border-taupe-200 bg-white p-8 shadow-lg hover:shadow-xl transition-all duration-300">
|
|
{/* Step number with animation */}
|
|
<motion.div
|
|
variants={numberVariants}
|
|
className="w-16 h-16 rounded-full bg-gradient-to-br from-olive-500 to-olive-600 text-white flex items-center justify-center mx-auto mb-6 font-bold text-xl shadow-lg"
|
|
>
|
|
{step.id}
|
|
</motion.div>
|
|
|
|
{/* Icon with animation */}
|
|
<motion.div
|
|
variants={iconVariants}
|
|
className={`w-16 h-16 rounded-full ${step.bgColor} ${step.color} flex items-center justify-center mx-auto mb-4`}
|
|
>
|
|
{step.icon}
|
|
</motion.div>
|
|
|
|
{/* Title with hover effect */}
|
|
<motion.h3
|
|
className="text-xl font-bold text-taupe-900 mb-3 group-hover:text-olive-700 transition-colors"
|
|
whileHover={{ scale: 1.05 }}
|
|
>
|
|
{step.title}
|
|
</motion.h3>
|
|
|
|
{/* Description */}
|
|
<motion.p
|
|
className="text-taupe-700 leading-relaxed"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ delay: i * 0.2 + 0.4 }}
|
|
>
|
|
{step.description}
|
|
</motion.p>
|
|
|
|
{/* Hover indicator */}
|
|
<motion.div
|
|
className="mt-4 h-1 bg-gradient-to-r from-olive-400 to-taupe-400 rounded-full"
|
|
initial={{ scaleX: 0 }}
|
|
whileInView={{ scaleX: 1 }}
|
|
transition={{ delay: i * 0.2 + 0.6, duration: 0.4 }}
|
|
/>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Bottom decoration */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
|
|
transition={{ delay: 1.2, duration: 0.6 }}
|
|
className="text-center mt-12"
|
|
>
|
|
<div className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-olive-100 to-taupe-100 rounded-full border border-olive-200">
|
|
<div className="w-2 h-2 bg-olive-500 rounded-full animate-pulse" />
|
|
<span className="text-olive-700 font-medium">Ready to start your project?</span>
|
|
<div className="w-2 h-2 bg-olive-500 rounded-full animate-pulse" />
|
|
</div>
|
|
</motion.div>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|