116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
"use client";
|
||
|
||
import React, { useRef } from "react";
|
||
import { motion, useTransform } from "motion/react";
|
||
import { useSectionProgress } from "@/lib/scroll";
|
||
import { Parallax } from "@/components/parallax/Parallax";
|
||
import { Reveal } from "@/components/motion/Reveal";
|
||
import { Stagger } from "@/components/motion/Stagger";
|
||
import { TRANSITIONS } from "@/lib/animation";
|
||
|
||
export function ProcessSection() {
|
||
const sectionRef = useRef<HTMLElement>(null!);
|
||
const progress = useSectionProgress(sectionRef);
|
||
const pathLength = useTransform(progress, [0, 1], [0, 1]);
|
||
|
||
const steps = [
|
||
{
|
||
title: "Discover",
|
||
desc:
|
||
"Align on goals, audience, and tone. Define constraints and success metrics.",
|
||
},
|
||
{
|
||
title: "Design",
|
||
desc:
|
||
"Establish visual language, motion rhythm, and section-level compositions.",
|
||
},
|
||
{
|
||
title: "Build",
|
||
desc:
|
||
"Implement Lenis scroll orchestration and Framer Motion systems with a11y in mind.",
|
||
},
|
||
{
|
||
title: "Polish",
|
||
desc:
|
||
"Optimize performance, refine micro‑interactions, and tune parallax depth.",
|
||
},
|
||
];
|
||
|
||
return (
|
||
<section
|
||
id="process"
|
||
ref={sectionRef}
|
||
aria-label="Process"
|
||
className="relative w-full overflow-clip py-28 md:py-36"
|
||
>
|
||
{/* Ambient vignette */}
|
||
<Parallax speed={0.04} className="pointer-events-none absolute inset-0 -z-10">
|
||
<div className="absolute inset-0 bg-[radial-gradient(900px_480px_at_10%_20%,rgba(255,255,255,0.08),transparent_70%)]" />
|
||
</Parallax>
|
||
|
||
<div className="mx-auto w-full max-w-6xl px-6">
|
||
<div className="text-center mb-12">
|
||
<motion.h2
|
||
className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight text-neutral-100"
|
||
initial={{ opacity: 0, y: 10 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={TRANSITIONS.base}
|
||
>
|
||
Process
|
||
</motion.h2>
|
||
<motion.p
|
||
className="mt-2 text-sm text-neutral-400"
|
||
initial={{ opacity: 0, y: 6 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ ...TRANSITIONS.base, delay: 0.05 }}
|
||
>
|
||
A simple path from idea to cinematic, performant delivery.
|
||
</motion.p>
|
||
</div>
|
||
|
||
{/* Timeline drawing */}
|
||
<div className="relative">
|
||
<motion.svg
|
||
width="100%"
|
||
height="220"
|
||
viewBox="0 0 1200 220"
|
||
className="hidden md:block"
|
||
>
|
||
<motion.path
|
||
d="M 40 180 C 320 60, 880 300, 1160 80"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
className="text-neutral-700"
|
||
strokeWidth="2"
|
||
strokeLinecap="round"
|
||
style={{ pathLength }}
|
||
/>
|
||
</motion.svg>
|
||
|
||
<div className="mt-0 grid grid-cols-1 gap-6 md:-mt-16 md:grid-cols-4">
|
||
<Stagger delayChildren={0.05}>
|
||
{steps.map((s, i) => (
|
||
<Reveal key={s.title} delay={i * 0.05} distance={16}>
|
||
<article className="relative rounded-2xl glass p-5">
|
||
<div className="mb-2 text-xs font-semibold uppercase tracking-wider text-neutral-400">
|
||
{String(i + 1).padStart(2, "0")}
|
||
</div>
|
||
<h3 className="text-lg font-semibold text-neutral-100">
|
||
{s.title}
|
||
</h3>
|
||
<p className="mt-2 text-sm text-neutral-300">
|
||
{s.desc}
|
||
</p>
|
||
</article>
|
||
</Reveal>
|
||
))}
|
||
</Stagger>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|