update add animation

This commit is contained in:
2026-07-24 15:58:49 +05:30
parent 4a0adea793
commit 9b72a28b2f
3 changed files with 331 additions and 70 deletions

View File

@@ -5,6 +5,100 @@ import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { Spotlight } from "@/components/ui/Spotlight"; import { Spotlight } from "@/components/ui/Spotlight";
import { fadeUp, staggerContainer, staggerCards, cardReveal, viewport } from "@/lib/animations"; import { fadeUp, staggerContainer, staggerCards, cardReveal, viewport } from "@/lib/animations";
// 1. Natural Intent SVG (Sound Wave & AI Sparkle)
function NaturalIntentSVG({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center p-2">
<motion.div
className="absolute h-16 w-16 rounded-full bg-[#7C3AED]/15 blur-lg pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.2, 0.9], opacity: [0.2, 0.5, 0.2] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" className="relative z-10 w-20 h-20 filter drop-shadow-md">
<motion.rect x="18" y="30" width="5" height="20" rx="2.5" fill="#7C3AED"
animate={reduce ? {} : { height: [12, 34, 12], y: [4, -7, 4] }}
transition={{ duration: 1.4, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.rect x="28" y="20" width="5" height="40" rx="2.5" fill="#C084FC"
animate={reduce ? {} : { height: [40, 16, 40], y: [0, 12, 0] }}
transition={{ duration: 1.6, delay: 0.2, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.rect x="38" y="14" width="5" height="52" rx="2.5" fill="#7C3AED"
animate={reduce ? {} : { height: [24, 52, 24], y: [14, 0, 14] }}
transition={{ duration: 1.8, delay: 0.4, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.rect x="48" y="24" width="5" height="32" rx="2.5" fill="#A855F7"
animate={reduce ? {} : { height: [32, 14, 32], y: [0, 9, 0] }}
transition={{ duration: 1.5, delay: 0.1, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.rect x="58" y="32" width="5" height="16" rx="2.5" fill="#C084FC"
animate={reduce ? {} : { height: [10, 26, 10], y: [3, -5, 3] }}
transition={{ duration: 1.3, delay: 0.3, repeat: Infinity, ease: "easeInOut" }}
/>
</svg>
</div>
);
}
// 2. Local Discovery SVG (Radar Beam & Shop Pin)
function LocalDiscoverySVG({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center p-2">
<motion.div
className="absolute h-16 w-16 rounded-full bg-emerald-500/15 blur-lg pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.2, 0.9], opacity: [0.2, 0.5, 0.2] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" className="relative z-10 w-20 h-20 filter drop-shadow-md">
<motion.circle cx="40" cy="40" r="28" stroke="#10B981" strokeWidth="2" strokeDasharray="4 4"
animate={reduce ? {} : { scale: [0.85, 1.2, 0.85], opacity: [0.3, 0.8, 0.3] }}
transition={{ duration: 2.8, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path d="M 40 18 C 30 18 22 26 22 36 C 22 50 40 64 40 64 C 40 64 58 50 58 36 C 58 26 50 18 40 18 Z"
fill="url(#disc-grad)" stroke="#34D399" strokeWidth="2"
animate={reduce ? {} : { y: [0, -4, 0] }}
transition={{ duration: 2.5, repeat: Infinity, ease: "easeInOut" }}
/>
<circle cx="40" cy="35" r="6" fill="#047857" />
<circle cx="40" cy="35" r="3" fill="#FFFFFF" />
<defs>
<linearGradient id="disc-grad" x1="22" y1="18" x2="58" y2="64" gradientUnits="userSpaceOnUse">
<stop stopColor="#059669" stopOpacity="0.8" />
<stop offset="1" stopColor="#34D399" stopOpacity="0.4" />
</linearGradient>
</defs>
</svg>
</div>
);
}
// 3. Instant Fulfillment SVG (Delivery Box & Speed Trail)
function InstantFulfillmentSVG({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center p-2">
<motion.div
className="absolute h-16 w-16 rounded-full bg-[#EC4899]/15 blur-lg pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.2, 0.9], opacity: [0.2, 0.5, 0.2] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" className="relative z-10 w-20 h-20 filter drop-shadow-md">
<motion.path d="M 12 28 L 26 28 M 8 40 L 22 40 M 14 52 L 28 52" stroke="#EC4899" strokeWidth="3" strokeLinecap="round"
animate={reduce ? {} : { opacity: [0.3, 1, 0.3], x: [-3, 3, -3] }}
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path d="M 32 26 L 56 16 L 68 26 L 44 36 Z" fill="#F472B6" />
<motion.path d="M 32 26 L 44 36 L 44 60 L 32 50 Z" fill="#DB2777" />
<motion.path d="M 44 36 L 68 26 L 68 50 L 44 60 Z" fill="#BE185D" />
<motion.circle cx="58" cy="48" r="10" fill="#10B981"
animate={reduce ? {} : { scale: [0.9, 1.15, 0.9] }}
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
/>
<path d="M 53 48 L 57 52 L 64 45" stroke="#FFFFFF" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
);
}
const STEPS = [ const STEPS = [
{ {
step: "01", step: "01",
@@ -14,6 +108,7 @@ const STEPS = [
badge: "Voice & Text AI", badge: "Voice & Text AI",
iconBg: "bg-primary/10 text-primary border-primary/20", iconBg: "bg-primary/10 text-primary border-primary/20",
badgeStyle: "bg-primary/10 text-primary border-primary/20", badgeStyle: "bg-primary/10 text-primary border-primary/20",
renderSVG: (reduce: boolean) => <NaturalIntentSVG reduce={reduce} />,
}, },
{ {
step: "02", step: "02",
@@ -23,6 +118,7 @@ const STEPS = [
badge: "Real-Time Stock", badge: "Real-Time Stock",
iconBg: "bg-secondary/10 text-secondary border-secondary/20", iconBg: "bg-secondary/10 text-secondary border-secondary/20",
badgeStyle: "bg-secondary/10 text-secondary border-secondary/20", badgeStyle: "bg-secondary/10 text-secondary border-secondary/20",
renderSVG: (reduce: boolean) => <LocalDiscoverySVG reduce={reduce} />,
}, },
{ {
step: "03", step: "03",
@@ -32,6 +128,7 @@ const STEPS = [
badge: "1-Tap Fulfillment", badge: "1-Tap Fulfillment",
iconBg: "bg-[#7C3AED]/10 text-[#7C3AED] border-[#7C3AED]/20", iconBg: "bg-[#7C3AED]/10 text-[#7C3AED] border-[#7C3AED]/20",
badgeStyle: "bg-[#7C3AED]/10 text-[#7C3AED] border-[#7C3AED]/20", badgeStyle: "bg-[#7C3AED]/10 text-[#7C3AED] border-[#7C3AED]/20",
renderSVG: (reduce: boolean) => <InstantFulfillmentSVG reduce={reduce} />,
}, },
]; ];
@@ -69,16 +166,16 @@ export function AICommerceIntro() {
<section <section
id="ai-commerce" id="ai-commerce"
aria-labelledby="ai-commerce-title" aria-labelledby="ai-commerce-title"
className="section section-soft w-full overflow-hidden border-y border-[#6330D6]/10" className="section section-soft w-full overflow-hidden border-y border-[#6330D6]/10 px-4 sm:px-8 py-12 md:py-16"
> >
<div className="page-container relative z-10"> <div className="mx-auto max-w-[1760px] relative z-10">
{/* Section Header */} {/* Section Header */}
<motion.div <motion.div
variants={staggerContainer} variants={staggerContainer}
initial="hidden" initial="hidden"
whileInView="visible" whileInView="visible"
viewport={viewport} viewport={viewport}
className="mx-auto max-w-[780px] text-center mb-12 md:mb-16" className="mx-auto max-w-[960px] text-center mb-12 md:mb-16"
> >
<motion.span <motion.span
variants={fadeUp} variants={fadeUp}
@@ -90,14 +187,14 @@ export function AICommerceIntro() {
<motion.h2 <motion.h2
id="ai-commerce-title" id="ai-commerce-title"
variants={fadeUp} variants={fadeUp}
className="mt-4 font-display font-black tracking-tight text-[#16001D] text-4xl md:text-5xl lg:text-[54px] leading-tight" className="mt-4 font-display font-black tracking-tight text-[#16001D] text-4xl md:text-5xl lg:text-[56px] leading-tight"
> >
Neighbourhood Shopping, <span className="text-[#7C3AED]">Reimagined by AI</span> Neighbourhood Shopping, <span className="text-[#7C3AED]">Reimagined by AI</span>
</motion.h2> </motion.h2>
<motion.p <motion.p
variants={fadeUp} variants={fadeUp}
className="mx-auto mt-4 max-w-[640px] text-base md:text-lg leading-relaxed text-[#5E5866]" className="mx-auto mt-4 max-w-[760px] text-base md:text-lg leading-relaxed text-[#5E5866]"
> >
Instead of searching across multiple apps and comparing items manually, simply tell Nearle what you need. AI handles discovery, stock checking, and ordering all in one conversation. Instead of searching across multiple apps and comparing items manually, simply tell Nearle what you need. AI handles discovery, stock checking, and ordering all in one conversation.
</motion.p> </motion.p>
@@ -109,55 +206,55 @@ export function AICommerceIntro() {
initial="hidden" initial="hidden"
whileInView="visible" whileInView="visible"
viewport={viewport} viewport={viewport}
className="mx-auto max-w-[1100px] mb-14" className="w-full mb-12 lg:mb-16"
> >
<Spotlight <Spotlight
className="overflow-hidden rounded-[2.5rem] border border-[#6330D6]/15 bg-[linear-gradient(135deg,#ffffff_0%,#fbf7ff_55%,#f1e8fb_100%)] p-8 md:p-12 shadow-[0_16px_44px_-16px_rgba(99,48,214,0.2)]" className="overflow-hidden rounded-[2.5rem] border border-[#6330D6]/15 bg-[linear-gradient(135deg,#ffffff_0%,#fbf7ff_55%,#f1e8fb_100%)] p-8 md:p-12 lg:p-14 shadow-[0_16px_44px_-16px_rgba(99,48,214,0.2)]"
color="rgba(168,85,247,0.14)" color="rgba(168,85,247,0.14)"
size={500} size={600}
> >
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-center"> <div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 items-center">
<div className="lg:col-span-7"> <div className="lg:col-span-7">
<div className="inline-flex items-center gap-2 rounded-xl bg-[#7C3AED]/10 px-3 py-1 text-xs font-black text-[#7C3AED] mb-4"> <div className="inline-flex items-center gap-2 rounded-xl bg-[#7C3AED]/10 px-3.5 py-1.5 text-xs font-black text-[#7C3AED] mb-5">
<MaterialIcon icon="auto_awesome" size={16} /> <MaterialIcon icon="auto_awesome" size={18} />
<span>Conversational Commerce</span> <span>Conversational Commerce</span>
</div> </div>
<h3 className="font-display text-2xl md:text-3xl font-black text-[#16001D] leading-tight mb-4"> <h3 className="font-display text-2xl md:text-3xl lg:text-4xl font-black text-[#16001D] leading-tight mb-4">
Talk to Nearle AI like you speak to your local shopkeeper Talk to Nearle AI like you speak to your local shopkeeper
</h3> </h3>
<p className="text-base md:text-lg text-[#5E5866] leading-relaxed"> <p className="text-base md:text-lg text-[#5E5866] leading-relaxed max-w-2xl">
Nearle connects natural consumer intent directly to verified local merchant inventory. You ask, AI discovers nearby availability, confirms with the merchant, and dispatches delivery. Nearle connects natural consumer intent directly to verified local merchant inventory. You ask, AI discovers nearby availability, confirms with the merchant, and dispatches delivery.
</p> </p>
</div> </div>
<div className="lg:col-span-5 flex flex-col gap-3"> <div className="lg:col-span-5 flex flex-col gap-3.5">
<div className="flex items-center gap-3.5 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 shadow-sm"> <div className="flex items-center gap-4 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 lg:p-5 shadow-sm">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary"> <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary">
<MaterialIcon icon="mic" size={20} /> <MaterialIcon icon="mic" size={22} />
</div> </div>
<div> <div>
<p className="text-xs font-black text-primary uppercase tracking-wider">Step 1 Intent</p> <p className="text-xs font-black text-primary uppercase tracking-wider">Step 1 Intent</p>
<p className="text-sm font-bold text-[#16001D]">Natural Voice & Text Query</p> <p className="text-sm md:text-base font-bold text-[#16001D]">Natural Voice & Text Query</p>
</div> </div>
</div> </div>
<div className="flex items-center gap-3.5 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 shadow-sm"> <div className="flex items-center gap-4 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 lg:p-5 shadow-sm">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[#EC4899]/10 text-[#EC4899]"> <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#EC4899]/10 text-[#EC4899]">
<MaterialIcon icon="travel_explore" size={20} /> <MaterialIcon icon="travel_explore" size={22} />
</div> </div>
<div> <div>
<p className="text-xs font-black text-[#EC4899] uppercase tracking-wider">Step 2 Match</p> <p className="text-xs font-black text-[#EC4899] uppercase tracking-wider">Step 2 Match</p>
<p className="text-sm font-bold text-[#16001D]">Live Local Shop Scan</p> <p className="text-sm md:text-base font-bold text-[#16001D]">Live Local Shop Scan</p>
</div> </div>
</div> </div>
<div className="flex items-center gap-3.5 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 shadow-sm"> <div className="flex items-center gap-4 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 lg:p-5 shadow-sm">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[#10B981]/10 text-[#10B981]"> <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#10B981]/10 text-[#10B981]">
<MaterialIcon icon="local_shipping" size={20} /> <MaterialIcon icon="local_shipping" size={22} />
</div> </div>
<div> <div>
<p className="text-xs font-black text-[#10B981] uppercase tracking-wider">Step 3 Deliver</p> <p className="text-xs font-black text-[#10B981] uppercase tracking-wider">Step 3 Deliver</p>
<p className="text-sm font-bold text-[#16001D]">Shopkeeper Pack & Rider Delivery</p> <p className="text-sm md:text-base font-bold text-[#16001D]">Shopkeeper Pack & Rider Delivery</p>
</div> </div>
</div> </div>
</div> </div>
@@ -165,13 +262,13 @@ export function AICommerceIntro() {
</Spotlight> </Spotlight>
</motion.div> </motion.div>
{/* 3 Step Cards Grid */} {/* 3 Step Cards Grid with Right-Aligned Animated SVGs */}
<motion.div <motion.div
variants={staggerCards} variants={staggerCards}
initial="hidden" initial="hidden"
whileInView="visible" whileInView="visible"
viewport={viewport} viewport={viewport}
className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8 mx-auto max-w-[1100px]" className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8 w-full"
> >
{STEPS.map((step) => ( {STEPS.map((step) => (
<motion.div <motion.div
@@ -179,29 +276,35 @@ export function AICommerceIntro() {
variants={cardReveal} variants={cardReveal}
whileHover={reduce ? undefined : { y: -6, scale: 1.02 }} whileHover={reduce ? undefined : { y: -6, scale: 1.02 }}
transition={{ type: "spring", stiffness: 260, damping: 22 }} transition={{ type: "spring", stiffness: 260, damping: 22 }}
className="flex flex-col justify-between rounded-[2rem] border border-[#16001D]/8 bg-white p-7 md:p-8 shadow-[0_10px_30px_rgba(22,0,29,0.05)] transition-all duration-300 hover:border-[#7C3AED]/30 hover:shadow-[0_20px_40px_-15px_rgba(99,48,214,0.15)] h-full" className="flex flex-col justify-between rounded-[2rem] border border-[#16001D]/8 bg-white p-7 md:p-9 lg:p-10 shadow-[0_10px_30px_rgba(22,0,29,0.05)] transition-all duration-300 hover:border-[#7C3AED]/30 hover:shadow-[0_20px_40px_-15px_rgba(99,48,214,0.15)] h-full"
> >
<div> <div className="flex items-start justify-between gap-4">
<div className="flex items-center justify-between mb-6"> <div className="flex-1 min-w-0">
<span className={`flex h-12 w-12 items-center justify-center rounded-2xl border ${step.iconBg}`}> <div className="flex items-center justify-between mb-5">
<MaterialIcon icon={step.icon} size={24} /> <span className={`flex h-11 w-11 items-center justify-center rounded-2xl border ${step.iconBg}`}>
</span> <MaterialIcon icon={step.icon} size={22} />
<span className="font-display text-xs font-black tracking-widest text-[#7C3AED] uppercase bg-[#7C3AED]/5 border border-[#7C3AED]/15 px-3 py-1 rounded-full"> </span>
{step.step} <span className="font-display text-xs font-black tracking-widest text-[#7C3AED] uppercase bg-[#7C3AED]/5 border border-[#7C3AED]/15 px-3 py-1 rounded-full">
</span> {step.step}
</span>
</div>
<div className={`inline-block rounded-md border px-2.5 py-1 text-xs font-black mb-3 ${step.badgeStyle}`}>
{step.badge}
</div>
<h3 className="font-display text-2xl lg:text-3xl font-black tracking-tight text-[#16001D] mb-3">
{step.title}
</h3>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed">
{step.desc}
</p>
</div> </div>
<div className={`inline-block rounded-md border px-2.5 py-1 text-xs font-black mb-3 ${step.badgeStyle}`}> <div className="shrink-0 flex items-center justify-center self-center pt-2">
{step.badge} {step.renderSVG(!!reduce)}
</div> </div>
<h3 className="font-display text-2xl font-black tracking-tight text-[#16001D] mb-3">
{step.title}
</h3>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed">
{step.desc}
</p>
</div> </div>
</motion.div> </motion.div>
))} ))}
@@ -213,22 +316,22 @@ export function AICommerceIntro() {
initial="hidden" initial="hidden"
whileInView="visible" whileInView="visible"
viewport={viewport} viewport={viewport}
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-5 mt-12 mx-auto max-w-[1100px]" className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 lg:gap-6 mt-12 lg:mt-16 w-full"
> >
{HIGHLIGHTS.map((item) => ( {HIGHLIGHTS.map((item) => (
<motion.div <motion.div
key={item.title} key={item.title}
variants={fadeUp} variants={fadeUp}
className="flex items-center gap-4 rounded-2xl border border-[#16001D]/6 bg-white p-4 md:p-5 shadow-sm transition-all hover:border-[#7C3AED]/25 hover:shadow-md" className="flex items-center gap-4.5 rounded-2xl border border-[#16001D]/6 bg-white p-5 md:p-6 shadow-sm transition-all hover:border-[#7C3AED]/25 hover:shadow-md"
> >
<div className={`flex h-11 w-11 shrink-0 items-center justify-center rounded-xl ${item.iconColor}`}> <div className={`flex h-12 w-12 shrink-0 items-center justify-center rounded-xl ${item.iconColor}`}>
<MaterialIcon icon={item.icon} size={22} /> <MaterialIcon icon={item.icon} size={24} />
</div> </div>
<div className="min-w-0"> <div className="min-w-0">
<p className="truncate text-sm font-black text-[#16001D]"> <p className="truncate text-base font-black text-[#16001D]">
{item.title} {item.title}
</p> </p>
<p className="truncate text-xs text-[#5E5866] font-medium"> <p className="truncate text-xs md:text-sm text-[#5E5866] font-medium mt-0.5">
{item.sub} {item.sub}
</p> </p>
</div> </div>
@@ -239,5 +342,3 @@ export function AICommerceIntro() {
</section> </section>
); );
} }

View File

@@ -1,32 +1,161 @@
"use client"; "use client";
import Link from "next/link"; import Link from "next/link";
import { motion } from "framer-motion"; import { motion, useReducedMotion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import BorderGlow from "@/components/ui/BorderGlow";
import { fadeUp, fadeIn, staggerContainer, viewport } from "@/lib/animations"; import { fadeUp, fadeIn, staggerContainer, viewport } from "@/lib/animations";
// 1. Trust Card Graphic (Animated Shield & Checkmark)
function TrustCardGraphic({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center p-1">
<motion.div
className="absolute h-14 w-14 rounded-full bg-[#7C3AED]/15 blur-lg pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.25, 0.9], opacity: [0.2, 0.5, 0.2] }}
transition={{ duration: 3.2, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" className="relative z-10 w-16 h-16 filter drop-shadow-md">
<motion.path
d="M 35 8 L 56 16 C 56 36 46 53 35 62 C 24 53 14 36 14 16 L 35 8 Z"
fill="url(#trust-card-grad)"
stroke="#C084FC"
strokeWidth="2"
animate={reduce ? {} : { y: [0, -3, 0] }}
transition={{ duration: 2.8, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path
d="M 26 34 L 32 40 L 45 26"
stroke="#FFFFFF"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
animate={reduce ? {} : { opacity: [0.8, 1, 0.8] }}
transition={{ duration: 2.2, repeat: Infinity, ease: "easeInOut" }}
/>
<defs>
<linearGradient id="trust-card-grad" x1="14" y1="8" x2="56" y2="62" gradientUnits="userSpaceOnUse">
<stop stopColor="#6330D6" stopOpacity="0.8" />
<stop offset="1" stopColor="#A855F7" stopOpacity="0.4" />
</linearGradient>
</defs>
</svg>
</div>
);
}
// 2. AI Catalog Graphic (Animated Sparkle Cluster)
function AICatalogGraphic({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center p-1">
<motion.div
className="absolute h-14 w-14 rounded-full bg-emerald-500/15 blur-lg pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.25, 0.9], opacity: [0.2, 0.5, 0.2] }}
transition={{ duration: 3.5, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" className="relative z-10 w-16 h-16 filter drop-shadow-md">
<motion.path
d="M 28 6 C 28 17 17 28 6 28 C 17 28 28 39 28 50 C 28 39 39 28 50 28 C 39 28 28 17 28 6 Z"
fill="#34D399"
style={{ transformOrigin: "28px 28px" }}
animate={reduce ? {} : { scale: [1, 1.1, 1], rotate: [0, 4, 0, -4, 0] }}
transition={{ duration: 3.2, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path
d="M 52 10 C 52 15 48 19 43 19 C 48 19 52 23 52 28 C 52 23 56 19 61 19 C 56 19 52 15 52 10 Z"
fill="#818CF8"
style={{ transformOrigin: "52px 19px" }}
animate={reduce ? {} : { scale: [0.85, 1.2, 0.85], y: [0, -3, 0] }}
transition={{ duration: 2.6, delay: 0.3, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path
d="M 48 46 C 48 50 44 54 40 54 C 44 54 48 58 48 62 C 48 58 52 54 56 54 C 52 54 48 50 48 46 Z"
fill="#F5D0FE"
style={{ transformOrigin: "48px 54px" }}
animate={reduce ? {} : { scale: [1.15, 0.8, 1.15] }}
transition={{ duration: 2.8, delay: 0.8, repeat: Infinity, ease: "easeInOut" }}
/>
</svg>
</div>
);
}
// 3. Offline Commerce Graphic (Animated Wifi Signal Waves)
function OfflineCommerceGraphic({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center p-1">
<motion.div
className="absolute h-14 w-14 rounded-full bg-[#EC4899]/15 blur-lg pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.25, 0.9], opacity: [0.2, 0.5, 0.2] }}
transition={{ duration: 3.2, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" className="relative z-10 w-16 h-16 filter drop-shadow-md">
<motion.path
d="M 12 24 C 26 12 44 12 58 24"
stroke="#F472B6"
strokeWidth="3.5"
strokeLinecap="round"
animate={reduce ? {} : { opacity: [0.4, 1, 0.4] }}
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path
d="M 20 34 C 29 25 41 25 50 34"
stroke="#C084FC"
strokeWidth="3.5"
strokeLinecap="round"
animate={reduce ? {} : { opacity: [0.6, 1, 0.6] }}
transition={{ duration: 2, delay: 0.3, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path
d="M 28 44 C 32 39 38 39 42 44"
stroke="#818CF8"
strokeWidth="3.5"
strokeLinecap="round"
animate={reduce ? {} : { opacity: [0.8, 1, 0.8] }}
transition={{ duration: 2, delay: 0.6, repeat: Infinity, ease: "easeInOut" }}
/>
<circle cx="35" cy="54" r="4" fill="#E879F9" />
</svg>
</div>
);
}
const CARDS = [ const CARDS = [
{ {
title: "Neighbourhood Trust", title: "Neighbourhood Trust",
desc: "Every order is fulfilled by a real local shop your street already knows — not an anonymous warehouse.", desc: "Every order is fulfilled by a real local shop your street already knows — not an anonymous warehouse.",
icon: "handshake", icon: "handshake",
color: "bg-primary/5 text-primary border-primary/10", color: "bg-primary/5 text-primary border-primary/10",
glowColor: "268 85 65",
dotColor: "bg-[#7C3AED]",
colors: ["#c084fc", "#f472b6", "#38bdf8"],
renderSVG: (reduce: boolean) => <TrustCardGraphic reduce={reduce} />,
}, },
{ {
title: "AI-Powered Catalog", title: "AI-Powered Catalog",
desc: "ProductLLM turns a shelf into a live, shoppable catalog in minutes — so any store can go online without the busywork.", desc: "ProductLLM turns a shelf into a live, shoppable catalog in minutes — so any store can go online without the busywork.",
icon: "auto_awesome", icon: "auto_awesome",
color: "bg-secondary/5 text-secondary border-secondary/10", color: "bg-emerald-500/10 text-emerald-600 border-emerald-500/20",
glowColor: "160 84 45",
dotColor: "bg-emerald-400",
colors: ["#34d399", "#818cf8", "#c084fc"],
renderSVG: (reduce: boolean) => <AICatalogGraphic reduce={reduce} />,
}, },
{ {
title: "Offline-First Commerce", title: "Offline-First Commerce",
desc: "Billing and inventory keep working even when the network drops. Built for how neighbourhood stores really run.", desc: "Billing and inventory keep working even when the network drops. Built for how neighbourhood stores really run.",
icon: "wifi_off", icon: "wifi_off",
color: "bg-[#de96f9]/10 text-primary border-[#de96f9]/20", color: "bg-[#de96f9]/10 text-primary border-[#de96f9]/20",
glowColor: "280 85 65",
dotColor: "bg-[#EC4899]",
colors: ["#f472b6", "#c084fc", "#38bdf8"],
renderSVG: (reduce: boolean) => <OfflineCommerceGraphic reduce={reduce} />,
}, },
]; ];
export function WhyAICommerceWins() { export function WhyAICommerceWins() {
const reduce = useReducedMotion();
return ( return (
<section className="section bg-white w-full overflow-hidden"> <section className="section bg-white w-full overflow-hidden">
<div className="page-container relative"> <div className="page-container relative">
@@ -60,19 +189,50 @@ export function WhyAICommerceWins() {
<motion.div <motion.div
key={card.title} key={card.title}
variants={fadeIn} variants={fadeIn}
className="premium-card bg-white rounded-3xl p-6 md:p-7 border border-outline-variant/15 shadow-lg flex flex-col h-full" className="h-full"
whileHover={{ y: -4, scale: 1.02, transition: { duration: 0.22, ease: [0.22, 1, 0.36, 1] } }} whileHover={reduce ? undefined : { y: -6, transition: { duration: 0.22, ease: [0.22, 1, 0.36, 1] } }}
whileTap={{ scale: 0.98, transition: { duration: 0.1 } }} whileTap={{ scale: 0.98, transition: { duration: 0.1 } }}
> >
<div className={`w-14 h-14 rounded-2xl flex items-center justify-center border mb-5 shrink-0 ${card.color}`}> <BorderGlow
<MaterialIcon icon={card.icon} size={27} /> className="h-full"
</div> backgroundColor="#ffffff"
<p className="font-display text-xl font-black text-brand-dark mb-3"> borderRadius={24}
{card.title} glowColor={card.glowColor}
</p> colors={card.colors}
<p className="text-sm md:text-base text-on-surface-variant leading-relaxed"> edgeSensitivity={15}
{card.desc} glowRadius={50}
</p> glowIntensity={2.2}
coneSpread={40}
fillOpacity={0.75}
animated={true}
>
<div className="p-6 md:p-8 flex flex-col h-full bg-white rounded-[24px] relative group overflow-hidden">
{/* Corner Hover Glowing Dot */}
<div className="absolute top-5 right-5 z-20 flex items-center gap-2 opacity-40 transition-opacity duration-300 group-hover:opacity-100">
<span className="relative flex h-2.5 w-2.5">
<span className={`absolute inline-flex h-full w-full animate-ping rounded-full ${card.dotColor} opacity-75`} />
<span className={`relative inline-flex h-2.5 w-2.5 rounded-full ${card.dotColor} shadow-[0_0_8px_currentColor]`} />
</span>
</div>
<div className="flex items-start justify-between gap-4 mb-5">
<div className={`w-14 h-14 rounded-2xl flex items-center justify-center border shrink-0 ${card.color} transition-transform duration-300 group-hover:scale-110 group-hover:rotate-3`}>
<MaterialIcon icon={card.icon} size={27} />
</div>
<div className="shrink-0 flex items-center justify-center self-center transition-transform duration-300 group-hover:scale-110">
{card.renderSVG(!!reduce)}
</div>
</div>
<h3 className="font-display text-xl font-black text-brand-dark mb-3">
{card.title}
</h3>
<p className="text-sm md:text-base text-on-surface-variant leading-relaxed">
{card.desc}
</p>
</div>
</BorderGlow>
</motion.div> </motion.div>
))} ))}
</motion.div> </motion.div>

View File

@@ -176,12 +176,12 @@ const BorderGlow: React.FC<BorderGlowProps> = ({
onPointerMove={handlePointerMove} onPointerMove={handlePointerMove}
onPointerEnter={() => setIsHovered(true)} onPointerEnter={() => setIsHovered(true)}
onPointerLeave={() => setIsHovered(false)} onPointerLeave={() => setIsHovered(false)}
className={`relative grid isolate border border-white/15 ${className}`} className={`relative grid isolate border border-black/10 dark:border-white/15 ${className}`}
style={{ style={{
background: backgroundColor, background: backgroundColor,
borderRadius: `${borderRadius}px`, borderRadius: `${borderRadius}px`,
transform: 'translate3d(0, 0, 0.01px)', transform: 'translate3d(0, 0, 0.01px)',
boxShadow: 'rgba(0,0,0,0.1) 0 1px 2px, rgba(0,0,0,0.1) 0 2px 4px, rgba(0,0,0,0.1) 0 4px 8px, rgba(0,0,0,0.1) 0 8px 16px, rgba(0,0,0,0.1) 0 16px 32px, rgba(0,0,0,0.1) 0 32px 64px', boxShadow: 'rgba(0,0,0,0.06) 0 1px 3px, rgba(0,0,0,0.08) 0 4px 12px',
}} }}
> >
{/* mesh gradient border */} {/* mesh gradient border */}