update add animation
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
Reference in New Issue
Block a user