Files
nearle_web_nextjs/src/components/why-nearle/BeforeAfterFlow.tsx
2026-07-19 23:07:18 +05:30

197 lines
6.9 KiB
TypeScript

"use client";
import { motion, useReducedMotion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { fadeUp, staggerContainer, viewport } from "@/lib/animations";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const TRADITIONAL = [
{ icon: "edit_note", label: "Manual Catalog" },
{ icon: "store", label: "Offline Store" },
{ icon: "directions_walk", label: "Walk-in Customers" },
{ icon: "receipt_long", label: "Manual Billing" },
];
const NEARLE = [
{ icon: "auto_awesome", label: "ProductLLM" },
{ icon: "storefront", label: "Digital Storefront" },
{ icon: "shopping_bag", label: "Omnichannel Ordering" },
{ icon: "travel_explore", label: "AI Powered Discovery" },
];
const nodeVariant = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0, transition: { duration: 0.5, ease: EASE } },
};
const connectorVariant = {
hidden: { scaleY: 0 },
visible: { scaleY: 1, transition: { duration: 0.5, ease: EASE } },
};
function FlowColumn({
eyebrow,
items,
tone,
reduce,
}: {
eyebrow: string;
items: { icon: string; label: string }[];
tone: "muted" | "brand";
reduce: boolean | null;
}) {
const isBrand = tone === "brand";
return (
<motion.div
variants={{ hidden: {}, visible: { transition: { staggerChildren: 0.12 } } }}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
className={`relative flex flex-col items-center rounded-[32px] border p-8 md:p-10 ${
isBrand
? "border-white/10 bg-gradient-to-br from-[#1A0322] via-[#2B0040] to-[#0A000D] shadow-[0_24px_60px_rgba(20,0,30,0.32)]"
: "border-[#6330D6]/10 bg-[#FAF7FD] shadow-[0_14px_40px_rgba(22,0,29,0.05)]"
}`}
>
<motion.p
variants={nodeVariant}
className={`text-xs font-black uppercase tracking-widest ${
isBrand ? "text-[#C9A2FF]" : "text-[#5E5866]"
}`}
>
{eyebrow}
</motion.p>
<div className="mt-8 flex w-full max-w-[300px] flex-col items-center">
{items.map((item, i) => (
<div key={item.label} className="flex w-full flex-col items-center">
<motion.div
variants={nodeVariant}
whileHover={reduce ? undefined : { y: -4, scale: 1.02 }}
transition={{ duration: 0.25, ease: EASE }}
className={`flex w-full items-center gap-4 rounded-2xl border px-5 py-4 ${
isBrand
? "border-white/10 bg-white/[0.06] hover:border-[#C9A2FF]/40 hover:bg-white/[0.1]"
: "border-[#6330D6]/10 bg-white shadow-[0_8px_22px_rgba(22,0,29,0.04)]"
} transition-colors duration-300`}
>
<div
className={`flex h-11 w-11 shrink-0 items-center justify-center rounded-xl ${
isBrand ? "bg-white/10 text-[#C9A2FF]" : "bg-[#6330D6]/[0.08] text-[#6330D6]"
}`}
>
<MaterialIcon icon={item.icon} size={22} />
</div>
<p
className={`font-display font-black leading-tight ${
isBrand ? "text-white" : "text-[#16001D]"
} text-[15px] md:text-base`}
>
{item.label}
</p>
</motion.div>
{/* Animated connector + travelling pulse */}
{i < items.length - 1 && (
<div className="relative my-1.5 h-8 w-0.5">
<motion.span
variants={connectorVariant}
className={`absolute inset-0 origin-top rounded-full ${
isBrand ? "bg-[#C9A2FF]/40" : "bg-[#6330D6]/25"
}`}
/>
{!reduce && (
<motion.span
aria-hidden="true"
className={`absolute left-1/2 top-0 h-2 w-2 -translate-x-1/2 rounded-full ${
isBrand
? "bg-[#C9A2FF] shadow-[0_0_10px_2px_rgba(201,162,255,0.6)]"
: "bg-[#7C3AED] shadow-[0_0_10px_2px_rgba(124,58,237,0.5)]"
}`}
animate={{ y: [0, 26], opacity: [0, 1, 0] }}
transition={{
repeat: Infinity,
duration: 1.8,
ease: "easeInOut",
delay: i * 0.3,
}}
/>
)}
</div>
)}
</div>
))}
</div>
</motion.div>
);
}
export function BeforeAfterFlow() {
const reduce = useReducedMotion();
return (
<section className="section section-soft w-full border-b border-[#6330D6]/10 overflow-hidden">
<div className="page-container">
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={viewport}
className="text-center max-w-[720px] mx-auto"
>
<motion.span
variants={fadeUp}
className="inline-flex items-center rounded-full bg-[#6330D6]/[0.08] px-3.5 py-1 text-xs font-black uppercase tracking-widest text-[#6330D6]"
>
Before vs After
</motion.span>
<motion.h2
variants={fadeUp}
className="font-display font-black text-[#16001D] mt-4 tracking-tight"
style={{ fontSize: "clamp(30px, 3vw, 48px)", lineHeight: 1.1 }}
>
The same store, reimagined
</motion.h2>
</motion.div>
<div className="relative mt-14 grid gap-6 md:grid-cols-2 md:items-start md:gap-8">
<FlowColumn
eyebrow="Traditional Commerce"
items={TRADITIONAL}
tone="muted"
reduce={reduce}
/>
{/* Center VS badge (desktop) */}
<motion.div
aria-hidden="true"
initial={{ opacity: 0, scale: 0.6 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true, amount: 0.5 }}
transition={{ duration: 0.5, delay: 0.25, ease: EASE }}
className="pointer-events-none absolute left-1/2 top-1/2 z-20 hidden h-14 w-14 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border border-[#6330D6]/15 bg-white text-xs font-black uppercase tracking-widest text-[#6330D6] shadow-[0_10px_30px_rgba(99,48,214,0.22)] md:flex"
>
{!reduce && (
<motion.span
className="absolute inset-0 rounded-full border-2 border-[#7C3AED]"
animate={{ scale: [1, 1.45], opacity: [0.5, 0] }}
transition={{ repeat: Infinity, duration: 2.2, ease: "easeOut" }}
/>
)}
VS
</motion.div>
<FlowColumn
eyebrow="Nearle AI Commerce"
items={NEARLE}
tone="brand"
reduce={reduce}
/>
</div>
</div>
</section>
);
}