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 { 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 = [
{
step: "01",
@@ -14,6 +108,7 @@ const STEPS = [
badge: "Voice & Text AI",
iconBg: "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",
@@ -23,6 +118,7 @@ const STEPS = [
badge: "Real-Time Stock",
iconBg: "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",
@@ -32,6 +128,7 @@ const STEPS = [
badge: "1-Tap Fulfillment",
iconBg: "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
id="ai-commerce"
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 */}
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
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
variants={fadeUp}
@@ -90,14 +187,14 @@ export function AICommerceIntro() {
<motion.h2
id="ai-commerce-title"
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>
</motion.h2>
<motion.p
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.
</motion.p>
@@ -109,55 +206,55 @@ export function AICommerceIntro() {
initial="hidden"
whileInView="visible"
viewport={viewport}
className="mx-auto max-w-[1100px] mb-14"
className="w-full mb-12 lg:mb-16"
>
<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)"
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="inline-flex items-center gap-2 rounded-xl bg-[#7C3AED]/10 px-3 py-1 text-xs font-black text-[#7C3AED] mb-4">
<MaterialIcon icon="auto_awesome" size={16} />
<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={18} />
<span>Conversational Commerce</span>
</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
</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.
</p>
</div>
<div className="lg:col-span-5 flex flex-col gap-3">
<div className="flex items-center gap-3.5 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 shadow-sm">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary">
<MaterialIcon icon="mic" size={20} />
<div className="lg:col-span-5 flex flex-col gap-3.5">
<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-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary/10 text-primary">
<MaterialIcon icon="mic" size={22} />
</div>
<div>
<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 className="flex items-center gap-3.5 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 shadow-sm">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[#EC4899]/10 text-[#EC4899]">
<MaterialIcon icon="travel_explore" size={20} />
<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-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#EC4899]/10 text-[#EC4899]">
<MaterialIcon icon="travel_explore" size={22} />
</div>
<div>
<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 className="flex items-center gap-3.5 rounded-2xl border border-[#6330D6]/10 bg-white/90 p-4 shadow-sm">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-[#10B981]/10 text-[#10B981]">
<MaterialIcon icon="local_shipping" size={20} />
<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-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#10B981]/10 text-[#10B981]">
<MaterialIcon icon="local_shipping" size={22} />
</div>
<div>
<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>
@@ -165,13 +262,13 @@ export function AICommerceIntro() {
</Spotlight>
</motion.div>
{/* 3 Step Cards Grid */}
{/* 3 Step Cards Grid with Right-Aligned Animated SVGs */}
<motion.div
variants={staggerCards}
initial="hidden"
whileInView="visible"
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) => (
<motion.div
@@ -179,12 +276,13 @@ export function AICommerceIntro() {
variants={cardReveal}
whileHover={reduce ? undefined : { y: -6, scale: 1.02 }}
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-center justify-between mb-6">
<span className={`flex h-12 w-12 items-center justify-center rounded-2xl border ${step.iconBg}`}>
<MaterialIcon icon={step.icon} size={24} />
<div className="flex items-start justify-between gap-4">
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between mb-5">
<span className={`flex h-11 w-11 items-center justify-center rounded-2xl border ${step.iconBg}`}>
<MaterialIcon icon={step.icon} size={22} />
</span>
<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">
{step.step}
@@ -195,7 +293,7 @@ export function AICommerceIntro() {
{step.badge}
</div>
<h3 className="font-display text-2xl font-black tracking-tight text-[#16001D] mb-3">
<h3 className="font-display text-2xl lg:text-3xl font-black tracking-tight text-[#16001D] mb-3">
{step.title}
</h3>
@@ -203,6 +301,11 @@ export function AICommerceIntro() {
{step.desc}
</p>
</div>
<div className="shrink-0 flex items-center justify-center self-center pt-2">
{step.renderSVG(!!reduce)}
</div>
</div>
</motion.div>
))}
</motion.div>
@@ -213,22 +316,22 @@ export function AICommerceIntro() {
initial="hidden"
whileInView="visible"
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) => (
<motion.div
key={item.title}
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}`}>
<MaterialIcon icon={item.icon} size={22} />
<div className={`flex h-12 w-12 shrink-0 items-center justify-center rounded-xl ${item.iconColor}`}>
<MaterialIcon icon={item.icon} size={24} />
</div>
<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}
</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}
</p>
</div>
@@ -239,5 +342,3 @@ export function AICommerceIntro() {
</section>
);
}

View File

@@ -1,32 +1,161 @@
"use client";
import Link from "next/link";
import { motion } from "framer-motion";
import { motion, useReducedMotion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import BorderGlow from "@/components/ui/BorderGlow";
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 = [
{
title: "Neighbourhood Trust",
desc: "Every order is fulfilled by a real local shop your street already knows — not an anonymous warehouse.",
icon: "handshake",
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",
desc: "ProductLLM turns a shelf into a live, shoppable catalog in minutes — so any store can go online without the busywork.",
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",
desc: "Billing and inventory keep working even when the network drops. Built for how neighbourhood stores really run.",
icon: "wifi_off",
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() {
const reduce = useReducedMotion();
return (
<section className="section bg-white w-full overflow-hidden">
<div className="page-container relative">
@@ -60,19 +189,50 @@ export function WhyAICommerceWins() {
<motion.div
key={card.title}
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"
whileHover={{ y: -4, scale: 1.02, transition: { duration: 0.22, ease: [0.22, 1, 0.36, 1] } }}
className="h-full"
whileHover={reduce ? undefined : { y: -6, transition: { duration: 0.22, ease: [0.22, 1, 0.36, 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
className="h-full"
backgroundColor="#ffffff"
borderRadius={24}
glowColor={card.glowColor}
colors={card.colors}
edgeSensitivity={15}
glowRadius={50}
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>
<p className="font-display text-xl font-black text-brand-dark mb-3">
<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}
</p>
</h3>
<p className="text-sm md:text-base text-on-surface-variant leading-relaxed">
{card.desc}
</p>
</div>
</BorderGlow>
</motion.div>
))}
</motion.div>

View File

@@ -176,12 +176,12 @@ const BorderGlow: React.FC<BorderGlowProps> = ({
onPointerMove={handlePointerMove}
onPointerEnter={() => setIsHovered(true)}
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={{
background: backgroundColor,
borderRadius: `${borderRadius}px`,
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 */}