104 lines
3.5 KiB
TypeScript
104 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { motion } from "motion/react";
|
|
import { Parallax } from "@/components/parallax/Parallax";
|
|
import { TRANSITIONS } from "@/lib/animation";
|
|
|
|
/**
|
|
* Lightweight logo/text marquee with reveal-in animations.
|
|
* Replace placeholder items with real logos as needed.
|
|
*/
|
|
export function TestimonialsSection() {
|
|
const items = [
|
|
"Biohazard VFX",
|
|
"Fortura Data",
|
|
"Cinematic Labs",
|
|
"Nebula Studio",
|
|
"Pixel Foundry",
|
|
"Prisma Motion",
|
|
];
|
|
|
|
return (
|
|
<section
|
|
id="testimonials"
|
|
aria-label="Testimonials"
|
|
className="relative w-full overflow-clip py-24 md:py-32"
|
|
>
|
|
<Parallax speed={0.05} className="pointer-events-none absolute inset-0 -z-10">
|
|
<div className="absolute inset-0 bg-[radial-gradient(1100px_520px_at_50%_0%,rgba(255,255,255,0.08),transparent_70%)]" />
|
|
</Parallax>
|
|
|
|
<div className="mx-auto w-full max-w-6xl px-6">
|
|
<div className="mb-10 text-center">
|
|
<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}
|
|
>
|
|
Trusted by teams who care about craft
|
|
</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 }}
|
|
>
|
|
Subtle marquee with tasteful depth and reveals.
|
|
</motion.p>
|
|
</div>
|
|
|
|
<div className="relative overflow-hidden rounded-2xl glass md:rounded-3xl">
|
|
{/* Edge gradient masks */}
|
|
<div className="pointer-events-none absolute inset-y-0 left-0 w-24 bg-gradient-to-r from-black to-transparent" />
|
|
<div className="pointer-events-none absolute inset-y-0 right-0 w-24 bg-gradient-to-l from-black to-transparent" />
|
|
|
|
{/* Marquee row 1 */}
|
|
<motion.div
|
|
className="flex w-[200%] gap-8 py-6 will-change-transform"
|
|
initial={{ x: 0 }}
|
|
whileInView={{ x: ["0%", "-50%"] }}
|
|
viewport={{ once: false, amount: 0.3 }}
|
|
transition={{ duration: 20, ease: "linear", repeat: Infinity }}
|
|
>
|
|
{[...items, ...items].map((it, i) => (
|
|
<LogoPill key={`a-${i}`} text={it} />
|
|
))}
|
|
</motion.div>
|
|
|
|
{/* Marquee row 2 (reverse) */}
|
|
<motion.div
|
|
className="flex w-[200%] gap-8 py-6 will-change-transform"
|
|
initial={{ x: "-50%" }}
|
|
whileInView={{ x: ["-50%", "0%"] }}
|
|
viewport={{ once: false, amount: 0.3 }}
|
|
transition={{ duration: 22, ease: "linear", repeat: Infinity }}
|
|
>
|
|
{[...items, ...items].map((it, i) => (
|
|
<LogoPill key={`b-${i}`} text={it} />
|
|
))}
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function LogoPill({ text }: { text: string }) {
|
|
return (
|
|
<motion.div
|
|
className="inline-flex min-w-40 items-center justify-center rounded-full glass px-4 py-2 text-sm font-medium text-neutral-300"
|
|
initial={{ opacity: 0, y: 6 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: false, amount: 0.6 }}
|
|
whileHover={{ scale: 1.03 }}
|
|
transition={TRANSITIONS.base}
|
|
>
|
|
{text}
|
|
</motion.div>
|
|
);
|
|
}
|