"use client";
import * as React from "react";
import { motion, useReducedMotion, type Variants } from "motion/react";
import { Blocks, Gauge, Globe2, LockKeyhole, Sparkles, Workflow, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
export interface FeatureItem {
icon: LucideIcon;
title: string;
description: string;
}
export interface FeaturesIconGridProps {
eyebrow?: string;
title?: React.ReactNode;
description?: string;
features?: FeatureItem[];
className?: string;
}
const DEFAULT_FEATURES: FeatureItem[] = [
{ icon: Gauge, title: "Instant by default", description: "Edge rendering and smart caching keep every page under 100 ms, everywhere your users are." },
{ icon: LockKeyhole, title: "Secure from day one", description: "SSO, audit logs and field-level encryption ship on every plan — no enterprise upsell." },
{ icon: Workflow, title: "Automations that think", description: "Chain triggers, conditions and actions visually, then let the runtime retry on failure." },
{ icon: Globe2, title: "Global by design", description: "Localised content, currencies and time zones handled for you in 40+ regions." },
{ icon: Blocks, title: "Composable blocks", description: "Snap together prebuilt modules or bring your own components with a typed SDK." },
{ icon: Sparkles, title: "AI where it helps", description: "Drafts, summaries and suggestions appear in context — never in the way of real work." },
];
export function FeaturesIconGrid({
eyebrow = "Why teams switch",
title = (
<>
Everything you need to ship,
<br className="hidden sm:block" /> <span className="text-muted-foreground">nothing you don't.</span>
</>
),
description = "A focused toolkit that replaces a patchwork of plugins, scripts and dashboards with one calm, fast workspace.",
features = DEFAULT_FEATURES,
className,
}: FeaturesIconGridProps) {
const reduce = useReducedMotion();
const container: Variants = {
hidden: {},
show: { transition: { staggerChildren: reduce ? 0 : 0.08, delayChildren: 0.1 } },
};
const card: Variants = {
hidden: reduce ? { opacity: 0 } : { opacity: 0, y: 24, filter: "blur(6px)" },
show: { opacity: 1, y: 0, filter: "blur(0px)", transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] } },
};
return (
<section className={cn("relative w-full overflow-x-clip bg-background py-16 sm:py-20", className)}>
{/* soft dotted backdrop */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 opacity-60 [background-image:radial-gradient(var(--border)_1px,transparent_1px)] [background-size:22px_22px] [mask-image:radial-gradient(ellipse_70%_60%_at_50%_0%,black,transparent)]"
/>
<div className="relative mx-auto max-w-6xl px-4 sm:px-6">
<motion.div
initial={{ opacity: 0, y: reduce ? 0 : 16 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.5 }}
transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
className="mx-auto max-w-2xl text-center"
>
<p className="inline-flex items-center gap-2 rounded-full border bg-card px-3 py-1 text-xs font-medium text-muted-foreground shadow-sm">
<span className="size-1.5 rounded-full bg-primary" />
{eyebrow}
</p>
<h2 className="mt-5 text-balance text-3xl font-semibold tracking-tight text-foreground sm:text-4xl lg:text-[2.75rem] lg:leading-[1.1]">
{title}
</h2>
<p className="mx-auto mt-4 max-w-xl text-pretty text-base text-muted-foreground sm:text-lg">{description}</p>
</motion.div>
<motion.ul
variants={container}
initial="hidden"
whileInView="show"
viewport={{ once: true, amount: 0.15 }}
className="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
>
{features.map((f) => (
<motion.li key={f.title} variants={card} className="h-full">
<GlowCard feature={f} />
</motion.li>
))}
</motion.ul>
</div>
</section>
);
}
function GlowCard({ feature }: { feature: FeatureItem }) {
const ref = React.useRef<HTMLDivElement>(null);
const Icon = feature.icon;
const onMove = (e: React.PointerEvent<HTMLDivElement>) => {
const el = ref.current;
if (!el) return;
const r = el.getBoundingClientRect();
el.style.setProperty("--gx", `${e.clientX - r.left}px`);
el.style.setProperty("--gy", `${e.clientY - r.top}px`);
};
return (
<div
ref={ref}
onPointerMove={onMove}
className="group relative h-full rounded-2xl bg-border p-px transition-shadow duration-300 [--gx:50%] [--gy:0px] hover:shadow-xl hover:shadow-primary/5"
>
{/* border glow that follows the cursor */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 rounded-2xl opacity-0 transition-opacity duration-300 group-hover:opacity-100"
style={{ background: "radial-gradient(220px circle at var(--gx) var(--gy), var(--primary), transparent 70%)" }}
/>
<div className="relative h-full overflow-hidden rounded-[15px] bg-card p-6">
{/* inner wash */}
<div
aria-hidden
className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-300 group-hover:opacity-100"
style={{
background:
"radial-gradient(320px circle at var(--gx) var(--gy), color-mix(in oklab, var(--primary) 12%, transparent), transparent 70%)",
}}
/>
<div className="relative">
<div className="relative inline-flex">
<div
aria-hidden
className="absolute -inset-3 rounded-2xl bg-primary/30 opacity-0 blur-xl transition-opacity duration-300 group-hover:opacity-100"
/>
<div className="relative flex size-11 items-center justify-center rounded-xl border bg-gradient-to-b from-background to-muted text-foreground shadow-sm transition-colors duration-300 group-hover:border-primary/40 group-hover:text-primary">
<Icon className="size-5" strokeWidth={1.75} aria-hidden />
</div>
</div>
<h3 className="mt-5 text-base font-semibold text-card-foreground">{feature.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">{feature.description}</p>
</div>
</div>
</div>
);
}