content order changes

This commit is contained in:
2026-07-24 17:04:09 +05:30
parent 9b72a28b2f
commit 74f53ec286
7 changed files with 543 additions and 68 deletions

View File

@@ -102,30 +102,30 @@ function InstantFulfillmentSVG({ reduce = false }: { reduce?: boolean }) {
const STEPS = [ const STEPS = [
{ {
step: "01", step: "01",
title: "Natural Intent", title: "Just Ask",
desc: "Tell Nearle what you need in plain Tamil or English. AI understands local store products, brands, and quantities without manual searching.", desc: "Tell Nearle what you need in your own words — in Tamil or English. No searching, no scrolling through endless lists.",
icon: "psychology", icon: "psychology",
badge: "Voice & Text AI", badge: "Voice or Text",
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} />, renderSVG: (reduce: boolean) => <NaturalIntentSVG reduce={reduce} />,
}, },
{ {
step: "02", step: "02",
title: "Live Local Discovery", title: "We Find It Nearby",
desc: "AI instantly checks live stock and pricing at verified neighbourhood stores right around your street to find exact matches.", desc: "Nearle checks what's on the shelves at trusted shops around your street, so you get exactly what you asked for.",
icon: "storefront", icon: "storefront",
badge: "Real-Time Stock", badge: "In Stock Nearby",
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} />, renderSVG: (reduce: boolean) => <LocalDiscoverySVG reduce={reduce} />,
}, },
{ {
step: "03", step: "03",
title: "Instant Fulfillment", title: "Delivered to You",
desc: "Confirms your order, alerts the local shopkeeper to pack, and dispatches a Nearle rider for fast doorstep delivery — all in 1 chat.", desc: "Confirm your order and the shopkeeper packs it while a Nearle rider brings it to your door — all in one chat.",
icon: "bolt", icon: "bolt",
badge: "1-Tap Fulfillment", badge: "To Your Door",
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} />, renderSVG: (reduce: boolean) => <InstantFulfillmentSVG reduce={reduce} />,
@@ -134,8 +134,8 @@ const STEPS = [
const HIGHLIGHTS = [ const HIGHLIGHTS = [
{ {
title: "Zero App Switching", title: "No App Switching",
sub: "Everything in 1 conversation", sub: "Everything in one chat",
icon: "forum", icon: "forum",
iconColor: "bg-primary/10 text-primary", iconColor: "bg-primary/10 text-primary",
}, },
@@ -146,14 +146,14 @@ const HIGHLIGHTS = [
iconColor: "bg-secondary/10 text-secondary", iconColor: "bg-secondary/10 text-secondary",
}, },
{ {
title: "Live Inventory", title: "Real Availability",
sub: "Real-time store stock", sub: "See what's in stock nearby",
icon: "inventory_2", icon: "inventory_2",
iconColor: "bg-blue-600/10 text-blue-600", iconColor: "bg-blue-600/10 text-blue-600",
}, },
{ {
title: "Fast Rider Delivery", title: "Fast Local Delivery",
sub: "Direct doorstep fulfillment", sub: "Straight to your door",
icon: "local_shipping", icon: "local_shipping",
iconColor: "bg-[#7C3AED]/10 text-[#7C3AED]", iconColor: "bg-[#7C3AED]/10 text-[#7C3AED]",
}, },
@@ -181,22 +181,22 @@ export function AICommerceIntro() {
variants={fadeUp} variants={fadeUp}
className="inline-flex items-center rounded-full bg-[#6330D6]/[0.08] px-3.5 py-1.5 text-xs font-black uppercase tracking-widest text-[#6330D6]" className="inline-flex items-center rounded-full bg-[#6330D6]/[0.08] px-3.5 py-1.5 text-xs font-black uppercase tracking-widest text-[#6330D6]"
> >
Nearle AI Engine Powered by Nearle AI
</motion.span> </motion.span>
<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-[56px] 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> Just Tell Us <span className="text-[#7C3AED]">What You Need</span>
</motion.h2> </motion.h2>
<motion.p <motion.p
variants={fadeUp} variants={fadeUp}
className="mx-auto mt-4 max-w-[760px] 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. No more hopping between apps or scrolling endless lists. Tell Nearle what you need, and we&rsquo;ll find it at a shop nearby, make sure it&rsquo;s in stock, and get it on its way all in one chat.
</motion.p> </motion.p>
</motion.div> </motion.div>
@@ -217,13 +217,13 @@ export function AICommerceIntro() {
<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.5 py-1.5 text-xs font-black text-[#7C3AED] mb-5"> <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} /> <MaterialIcon icon="auto_awesome" size={18} />
<span>Conversational Commerce</span> <span>Just Say It</span>
</div> </div>
<h3 className="font-display text-2xl md:text-3xl lg:text-4xl 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 Shop like you&rsquo;re talking to your local shopkeeper
</h3> </h3>
<p className="text-base md:text-lg text-[#5E5866] leading-relaxed max-w-2xl"> <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. Just say what you&rsquo;re after. Nearle finds it at a trusted shop nearby, confirms it with the owner, and sends it to your door.
</p> </p>
</div> </div>
@@ -233,8 +233,8 @@ export function AICommerceIntro() {
<MaterialIcon icon="mic" size={22} /> <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 Ask</p>
<p className="text-sm md:text-base font-bold text-[#16001D]">Natural Voice & Text Query</p> <p className="text-sm md:text-base font-bold text-[#16001D]">Tell us what you need</p>
</div> </div>
</div> </div>
@@ -243,8 +243,8 @@ export function AICommerceIntro() {
<MaterialIcon icon="travel_explore" size={22} /> <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 Find</p>
<p className="text-sm md:text-base font-bold text-[#16001D]">Live Local Shop Scan</p> <p className="text-sm md:text-base font-bold text-[#16001D]">We find it nearby</p>
</div> </div>
</div> </div>
@@ -254,7 +254,7 @@ export function AICommerceIntro() {
</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 md:text-base font-bold text-[#16001D]">Shopkeeper Pack & Rider Delivery</p> <p className="text-sm md:text-base font-bold text-[#16001D]">Packed by the shop, brought to you</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -8,8 +8,8 @@ import { fadeUp, fadeIn, scaleIn, staggerContainer, viewport } from "@/lib/anima
const FEATURES = [ const FEATURES = [
{ {
title: "AI Catalogue", title: "Digital Catalogue",
desc: "Turn your shelf into a live online catalogue with ProductLLM — no manual data entry.", desc: "Turn your shelves into an online catalogue in minutes — no manual data entry.",
icon: "menu_book", icon: "menu_book",
visual: "/images/stores/catalogue-ui.webp", visual: "/images/stores/catalogue-ui.webp",
alt: "Clean grocery catalogue interface preview", alt: "Clean grocery catalogue interface preview",

View File

@@ -13,11 +13,134 @@ const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
// power4.out approximation, matches HeroLogo's entrance ease // power4.out approximation, matches HeroLogo's entrance ease
const EASE_LOGO = [0.16, 1, 0.3, 1] as [number, number, number, number]; const EASE_LOGO = [0.16, 1, 0.3, 1] as [number, number, number, number];
// 1. Verified Store SVG
function VerifiedStoreSVG({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center">
<motion.div
className="absolute h-10 w-10 rounded-full bg-[#6D28D9]/15 blur-md pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.25, 0.9], opacity: [0.25, 0.6, 0.25] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" className="relative z-10 w-8 h-8 md:w-9 md:h-9">
<motion.path
d="M 8 16 L 36 16 L 33 8 L 11 8 Z"
fill="#A855F7"
animate={reduce ? {} : { y: [0, -2, 0] }}
transition={{ duration: 2.8, repeat: Infinity, ease: "easeInOut" }}
/>
<rect x="10" y="16" width="24" height="20" rx="2" fill="#6D28D9" />
<rect x="18" y="24" width="8" height="12" rx="1.5" fill="#E4D1FF" />
<motion.circle cx="32" cy="28" r="5" fill="#10B981"
animate={reduce ? {} : { scale: [0.85, 1.15, 0.85] }}
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
/>
<path d="M 29.5 28 L 31.5 30 L 34.5 26.5" stroke="#FFFFFF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
);
}
// 2. Neighborhood Pin SVG
function NeighborhoodPinSVG({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center">
<motion.div
className="absolute h-10 w-10 rounded-full bg-[#6D28D9]/15 blur-md pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.25, 0.9], opacity: [0.25, 0.6, 0.25] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" className="relative z-10 w-8 h-8 md:w-9 md:h-9">
<motion.circle cx="22" cy="30" r="11" stroke="#A855F7" strokeWidth="1.5" strokeDasharray="3 3"
animate={reduce ? {} : { scale: [0.8, 1.3, 0.8], opacity: [0.3, 0.8, 0.3] }}
transition={{ duration: 2.8, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.path
d="M 22 6 C 15 6 9 12 9 19 C 9 28 22 38 22 38 C 22 38 35 28 35 19 C 35 12 29 6 22 6 Z"
fill="#6D28D9"
animate={reduce ? {} : { y: [0, -3, 0] }}
transition={{ duration: 2.5, repeat: Infinity, ease: "easeInOut" }}
/>
<circle cx="22" cy="18" r="4" fill="#FFFFFF" />
</svg>
</div>
);
}
// 3. Happy Customers SVG
function HappyCustomerSVG({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center">
<motion.div
className="absolute h-10 w-10 rounded-full bg-[#EC4899]/15 blur-md pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.25, 0.9], opacity: [0.25, 0.6, 0.25] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" className="relative z-10 w-8 h-8 md:w-9 md:h-9">
<motion.circle cx="22" cy="22" r="16" fill="#F472B6"
animate={reduce ? {} : { scale: [1, 1.06, 1] }}
transition={{ duration: 2.6, repeat: Infinity, ease: "easeInOut" }}
/>
<circle cx="16" cy="18" r="2.5" fill="#FFFFFF" />
<circle cx="28" cy="18" r="2.5" fill="#FFFFFF" />
<motion.path d="M 15 26 C 18 31 26 31 29 26" stroke="#FFFFFF" strokeWidth="2.5" strokeLinecap="round"
animate={reduce ? {} : { opacity: [0.8, 1, 0.8] }}
transition={{ duration: 2.4, repeat: Infinity, ease: "easeInOut" }}
/>
</svg>
</div>
);
}
// 4. Trusted Star SVG
function TrustedStarSVG({ reduce = false }: { reduce?: boolean }) {
return (
<div className="relative flex items-center justify-center">
<motion.div
className="absolute h-10 w-10 rounded-full bg-[#F59E0B]/20 blur-md pointer-events-none"
animate={reduce ? {} : { scale: [0.9, 1.25, 0.9], opacity: [0.25, 0.6, 0.25] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" className="relative z-10 w-8 h-8 md:w-9 md:h-9">
<motion.path
d="M 22 4 L 27.5 15.5 L 40 17 L 31 26 L 33.5 38 L 22 32 L 10.5 38 L 13 26 L 4 17 L 16.5 15.5 Z"
fill="#F59E0B"
stroke="#FDE047"
strokeWidth="1.5"
style={{ transformOrigin: "22px 22px" }}
animate={reduce ? {} : { scale: [1, 1.1, 1], rotate: [0, 4, 0, -4, 0] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut" }}
/>
</svg>
</div>
);
}
const PROOF_ITEMS = [ const PROOF_ITEMS = [
{ label: "Verified", text: "Local Stores", icon: "store" }, {
{ label: "Neighborhood", text: "Coverage", icon: "location_on" }, label: "Verified",
{ label: "Growing", text: "Happy Customers", icon: "mood" }, text: "Local Stores",
{ label: "Trusted", text: "Local Platform", icon: "star" }, dotColor: "bg-[#6D28D9]",
renderSVG: (reduce: boolean) => <VerifiedStoreSVG reduce={reduce} />,
},
{
label: "Neighborhood",
text: "Coverage",
dotColor: "bg-[#A855F7]",
renderSVG: (reduce: boolean) => <NeighborhoodPinSVG reduce={reduce} />,
},
{
label: "Growing",
text: "Happy Customers",
dotColor: "bg-[#EC4899]",
renderSVG: (reduce: boolean) => <HappyCustomerSVG reduce={reduce} />,
},
{
label: "Trusted",
text: "Local Platform",
dotColor: "bg-[#F59E0B]",
renderSVG: (reduce: boolean) => <TrustedStarSVG reduce={reduce} />,
},
]; ];
// Shared cascade timing: logo settles at ~1.2s, then badge → heading → description // Shared cascade timing: logo settles at ~1.2s, then badge → heading → description
@@ -182,7 +305,7 @@ export function Hero() {
className="flex items-center gap-2 rounded-xl bg-[#6330D6] px-7 py-3.5 text-sm font-black text-white shadow-lg transition-all duration-200 hover:bg-[#5225b8] hover:shadow-xl active:scale-95 sm:px-8" className="flex items-center gap-2 rounded-xl bg-[#6330D6] px-7 py-3.5 text-sm font-black text-white shadow-lg transition-all duration-200 hover:bg-[#5225b8] hover:shadow-xl active:scale-95 sm:px-8"
> >
<MaterialIcon icon="auto_awesome" size={18} /> <MaterialIcon icon="auto_awesome" size={18} />
<span>Explore AI Commerce</span> <span>Discover Nearby Stores</span>
</Link> </Link>
</MagneticButton> </MagneticButton>
<MagneticButton> <MagneticButton>
@@ -204,24 +327,60 @@ export function Hero() {
<div className="border-t border-[#EEE7F4]/80 bg-white"> <div className="border-t border-[#EEE7F4]/80 bg-white">
<div className="px-6 sm:px-10 max-w-[1600px] mx-auto py-6 md:py-8 lg:py-10"> <div className="px-6 sm:px-10 max-w-[1600px] mx-auto py-6 md:py-8 lg:py-10">
<motion.div <motion.div
className="bg-white rounded-[32px] border border-[#E9D8FD] shadow-[0_10px_35px_rgba(124,58,237,0.08),0_2px_8px_rgba(124,58,237,0.05)] px-6 py-6 md:px-8 md:py-7 lg:px-12 lg:py-8" className="bg-white rounded-[32px] border border-[#E9D8FD] shadow-[0_10px_35px_rgba(124,58,237,0.08),0_2px_8px_rgba(124,58,237,0.05)] px-6 py-6 md:px-8 md:py-7 lg:px-12 lg:py-8 relative overflow-hidden group"
variants={statStagger} variants={statStagger}
initial="hidden" initial="hidden"
whileInView="visible" whileInView="visible"
viewport={viewport} viewport={viewport}
> >
<div className="flex flex-col gap-6 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-6 lg:flex lg:flex-row lg:items-center lg:justify-between lg:gap-0"> {/* Floating Particle Glow Dots on Hover */}
<div aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden z-0 opacity-0 transition-opacity duration-500 group-hover:opacity-100">
<motion.span
className="absolute h-1.5 w-1.5 rounded-full bg-[#6D28D9] shadow-[0_0_8px_#6D28D9]"
style={{ left: "12%", top: "35%" }}
animate={shouldReduce ? {} : { y: [0, -10, 0], opacity: [0.3, 0.9, 0.3] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.span
className="absolute h-1.5 w-1.5 rounded-full bg-[#A855F7] shadow-[0_0_8px_#A855F7]"
style={{ left: "38%", top: "60%" }}
animate={shouldReduce ? {} : { y: [0, -12, 0], opacity: [0.3, 0.9, 0.3] }}
transition={{ duration: 4.8, delay: 0.8, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.span
className="absolute h-1.5 w-1.5 rounded-full bg-[#EC4899] shadow-[0_0_8px_#EC4899]"
style={{ left: "62%", top: "25%" }}
animate={shouldReduce ? {} : { y: [0, -10, 0], opacity: [0.3, 0.9, 0.3] }}
transition={{ duration: 4.2, delay: 1.2, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.span
className="absolute h-1.5 w-1.5 rounded-full bg-[#F59E0B] shadow-[0_0_8px_#F59E0B]"
style={{ left: "85%", top: "55%" }}
animate={shouldReduce ? {} : { y: [0, -12, 0], opacity: [0.3, 0.9, 0.3] }}
transition={{ duration: 5, delay: 0.4, repeat: Infinity, ease: "easeInOut" }}
/>
</div>
<div className="relative z-10 flex flex-col gap-6 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-6 lg:flex lg:flex-row lg:items-center lg:justify-between lg:gap-0">
{PROOF_ITEMS.map((item, idx) => ( {PROOF_ITEMS.map((item, idx) => (
<Fragment key={item.label}> <Fragment key={item.label}>
<motion.div <motion.div
variants={statItem} variants={statItem}
className="flex items-center gap-[18px] group cursor-default transition-transform duration-[250ms] hover:-translate-y-1 lg:flex-1 lg:py-2" className="relative flex items-center gap-[18px] group/item cursor-default transition-transform duration-[250ms] hover:-translate-y-1 lg:flex-1 lg:py-2"
> >
<div className="w-16 h-16 rounded-full bg-[#F5EDFF] text-[#6D28D9] flex items-center justify-center shrink-0 transition-colors duration-[250ms] group-hover:bg-[#E4D1FF]"> {/* Corner Hover Glowing Dot on Item */}
<MaterialIcon icon={item.icon} size={28} /> <div className="absolute -top-1 right-2 z-20 flex items-center gap-2 opacity-0 transition-opacity duration-300 group-hover/item:opacity-100">
<span className="relative flex h-2 w-2">
<span className={`absolute inline-flex h-full w-full animate-ping rounded-full ${item.dotColor} opacity-75`} />
<span className={`relative inline-flex h-2 w-2 rounded-full ${item.dotColor} shadow-[0_0_6px_currentColor]`} />
</span>
</div>
<div className="w-16 h-16 rounded-full bg-[#F5EDFF] text-[#6D28D9] flex items-center justify-center shrink-0 transition-all duration-[250ms] group-hover/item:bg-[#E4D1FF] group-hover/item:scale-110">
{item.renderSVG(!!shouldReduce)}
</div> </div>
<div> <div>
<div className="text-2xl lg:text-[28px] font-black text-[#17051F] leading-none tracking-tight transition-colors duration-[250ms] group-hover:text-[#6D28D9]"> <div className="text-2xl lg:text-[28px] font-black text-[#17051F] leading-none tracking-tight transition-colors duration-[250ms] group-hover/item:text-[#6D28D9]">
{item.label} {item.label}
</div> </div>
<div className="text-sm lg:text-[15px] font-medium text-[#6B7280] mt-1.5"> <div className="text-sm lg:text-[15px] font-medium text-[#6B7280] mt-1.5">

View File

@@ -175,7 +175,7 @@ export function VoiceCommerceExperience() {
<span className="relative flex h-1.5 w-1.5"> <span className="relative flex h-1.5 w-1.5">
<span className="absolute inline-flex h-full w-full rounded-full bg-[#7C3AED] opacity-60" /> <span className="absolute inline-flex h-full w-full rounded-full bg-[#7C3AED] opacity-60" />
</span> </span>
Live AI Commerce demo See Nearle in action
</p> </p>
<Link <Link
href="/platform" href="/platform"

View File

@@ -120,10 +120,15 @@ function OfflineCommerceGraphic({ reduce = false }: { reduce?: boolean }) {
); );
} }
const CARD_MOTES = [
{ left: "20%", top: "35%", size: 4, duration: 4.5, delay: 0 },
{ left: "75%", top: "60%", size: 3, duration: 5.5, delay: 0.8 },
];
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 comes from a real shop on your street — the people you already know, not a faraway 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", glowColor: "268 85 65",
@@ -132,8 +137,8 @@ const CARDS = [
renderSVG: (reduce: boolean) => <TrustCardGraphic reduce={reduce} />, renderSVG: (reduce: boolean) => <TrustCardGraphic reduce={reduce} />,
}, },
{ {
title: "AI-Powered Catalog", title: "Your Shops, On Your Phone",
desc: "ProductLLM turns a shelf into a live, shoppable catalog in minutes — so any store can go online without the busywork.", desc: "The shops you love are now a tap away — browse their shelves and order in seconds, without leaving home.",
icon: "auto_awesome", icon: "auto_awesome",
color: "bg-emerald-500/10 text-emerald-600 border-emerald-500/20", color: "bg-emerald-500/10 text-emerald-600 border-emerald-500/20",
glowColor: "160 84 45", glowColor: "160 84 45",
@@ -142,8 +147,8 @@ const CARDS = [
renderSVG: (reduce: boolean) => <AICatalogGraphic reduce={reduce} />, renderSVG: (reduce: boolean) => <AICatalogGraphic reduce={reduce} />,
}, },
{ {
title: "Offline-First Commerce", title: "Always Reliable",
desc: "Billing and inventory keep working even when the network drops. Built for how neighbourhood stores really run.", desc: "Nearle keeps working the way local shops really do — steady even when the network drops, so your order always goes through.",
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", glowColor: "280 85 65",
@@ -167,14 +172,14 @@ export function WhyAICommerceWins() {
viewport={viewport} viewport={viewport}
> >
<span className="text-xs text-primary uppercase tracking-widest font-black bg-primary/5 px-3.5 py-1.5 rounded-full"> <span className="text-xs text-primary uppercase tracking-widest font-black bg-primary/5 px-3.5 py-1.5 rounded-full">
AI Commerce Shop Local
</span> </span>
<h2 className="text-4xl md:text-5xl lg:text-[54px] font-display font-black text-brand-dark mt-3 leading-tight tracking-tight"> <h2 className="text-4xl md:text-5xl lg:text-[54px] font-display font-black text-brand-dark mt-3 leading-tight tracking-tight">
Why AI Commerce <span className="text-primary font-normal">Wins</span> A Smarter Way to <span className="text-primary font-normal">Shop Local</span>
</h2> </h2>
<p className="text-body-lg text-on-surface-variant mt-2.5 max-w-xl mx-auto leading-relaxed"> <p className="text-body-lg text-on-surface-variant mt-2.5 max-w-xl mx-auto leading-relaxed">
Nearle isn&rsquo;t another delivery app. It&rsquo;s the AI layer that brings Nearle isn&rsquo;t another delivery app. We bring the shops on your street online so
neighbourhood stores online without replacing them. you can order from them in seconds, while they stay the ones serving you.
</p> </p>
</motion.div> </motion.div>
@@ -207,6 +212,38 @@ export function WhyAICommerceWins() {
animated={true} animated={true}
> >
<div className="p-6 md:p-8 flex flex-col h-full bg-white rounded-[24px] relative group overflow-hidden"> <div className="p-6 md:p-8 flex flex-col h-full bg-white rounded-[24px] relative group overflow-hidden">
{/* Floating Flying Particle Glow Dots (Motes) - Shown ONLY on Hover */}
<div aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden z-0 opacity-0 transition-opacity duration-500 group-hover:opacity-100">
{CARD_MOTES.map((mote, idx) => (
<motion.span
key={idx}
className={`absolute rounded-full ${card.dotColor} shadow-[0_0_8px_currentColor]`}
style={{
left: mote.left,
top: mote.top,
width: mote.size,
height: mote.size,
}}
animate={
reduce
? {}
: {
y: [0, -12, 0],
x: [0, 6, 0],
opacity: [0.3, 0.9, 0.3],
scale: [0.8, 1.2, 0.8],
}
}
transition={{
duration: mote.duration,
delay: mote.delay,
repeat: Infinity,
ease: "easeInOut",
}}
/>
))}
</div>
{/* Corner Hover Glowing Dot */} {/* 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"> <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="relative flex h-2.5 w-2.5">

View File

@@ -15,8 +15,8 @@ const CARDS = [
alt: "Friendly local grocery shop owner inside a kirana store", alt: "Friendly local grocery shop owner inside a kirana store",
}, },
{ {
title: "AI Assisted Shopping", title: "Find Things Faster",
desc: "Search naturally. Nearle AI understands what youre looking for and recommends relevant products.", desc: "Search in your own words and Nearle helps you find exactly what you need, in seconds.",
icon: "auto_awesome", icon: "auto_awesome",
color: "bg-[#de96f9]/10 text-primary border-[#de96f9]/20", color: "bg-[#de96f9]/10 text-primary border-[#de96f9]/20",
image: "/images/illustrations/easy-reordering.webp", image: "/images/illustrations/easy-reordering.webp",

View File

@@ -20,21 +20,284 @@ const TRADITIONAL = [
]; ];
const NEARLE = [ const NEARLE = [
{ icon: "psychology", text: "AI understands intent" }, { icon: "psychology", text: "Just ask in your own words" },
{ icon: "storefront", text: "Merchant stays at the center" }, { icon: "storefront", text: "The shop you trust stays at the centre" },
{ icon: "near_me", text: "Customers discover nearby stores naturally" }, { icon: "near_me", text: "You discover the shops right around you" },
{ icon: "handshake", text: "Technology empowers merchants instead of replacing them" }, { icon: "handshake", text: "Local shops grow — they never get replaced" },
]; ];
// The gentle S-curve the transformation flow travels. Stretched full-bleed via
// preserveAspectRatio="none", so exact geometry matters less than the direction:
// it always reads left (traditional) → right (Nearle).
const FLOW_PATH = "M0 168 C 300 96, 470 236, 640 168 S 960 96, 1200 152";
// ─────────────────────────────────────────────────────────────────────────
// Storytelling layer — everything below is decorative, pointer-events-none,
// low-opacity, and sits behind the cards. It reinforces one idea:
// Traditional (isolated, grey) → transformation beam → Nearle (connected, purple).
// ─────────────────────────────────────────────────────────────────────────
// Ambient depth: soft drifting blobs + a faint dot texture so the band never
// reads as flat.
function AmbientBackdrop({ reduce }: { reduce: boolean }) {
return (
<div className="absolute inset-0 overflow-hidden">
{/* faint dot texture for subtle depth */}
<svg className="absolute inset-0 h-full w-full opacity-70" aria-hidden="true">
<defs>
<pattern id="wnd-dots" width="28" height="28" patternUnits="userSpaceOnUse">
<circle cx="1.5" cy="1.5" r="1.5" fill="#7C3AED" fillOpacity="0.05" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#wnd-dots)" />
</svg>
{/* two slow gradient blobs, kept out toward the margins */}
<motion.div
className="absolute -left-32 top-[22%] h-72 w-72 rounded-full bg-[#C084FC]/[0.10] blur-[120px]"
animate={reduce ? undefined : { y: [0, -26, 0], opacity: [0.55, 1, 0.55] }}
transition={{ duration: 17, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.div
className="absolute -right-28 top-[34%] h-80 w-80 rounded-full bg-[#7C3AED]/[0.08] blur-[130px]"
animate={reduce ? undefined : { y: [0, 28, 0], opacity: [0.5, 0.9, 0.5] }}
transition={{ duration: 21, repeat: Infinity, ease: "easeInOut", delay: 1.6 }}
/>
{/* a few tiny floating particles */}
{[
{ c: "left-[12%] top-[30%]", d: 6, dur: 9 },
{ c: "left-[46%] top-[18%]", d: 5, dur: 11 },
{ c: "right-[18%] top-[62%]", d: 7, dur: 10 },
{ c: "right-[40%] top-[26%]", d: 4, dur: 13 },
].map((p, i) => (
<motion.span
key={i}
className={`absolute ${p.c} rounded-full bg-[#A855F7]/40`}
style={{ height: p.d, width: p.d }}
animate={reduce ? undefined : { y: [0, -14, 0], opacity: [0.2, 0.7, 0.2] }}
transition={{ duration: p.dur, repeat: Infinity, ease: "easeInOut", delay: i * 0.7 }}
/>
))}
</div>
);
}
// The centrepiece: a full-width flow line whose stroke shifts grey → purple,
// with a constant dotted drift plus a single brighter beam that sweeps slowly
// left → right — the transformation itself.
function TransformationFlow({ reduce }: { reduce: boolean }) {
return (
<div className="absolute inset-x-0 top-1/2 hidden h-[340px] -translate-y-1/2 md:block">
<svg
viewBox="0 0 1200 340"
preserveAspectRatio="none"
className="h-full w-full"
fill="none"
aria-hidden="true"
>
<defs>
<linearGradient id="wnd-flow" x1="0" y1="0" x2="1200" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stopColor="#B6ADC6" stopOpacity="0.45" />
<stop offset="0.5" stopColor="#9F7AE8" stopOpacity="0.6" />
<stop offset="1" stopColor="#A855F7" stopOpacity="0.85" />
</linearGradient>
<linearGradient id="wnd-beam" x1="0" y1="0" x2="1200" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stopColor="#7C3AED" stopOpacity="0" />
<stop offset="0.5" stopColor="#A855F7" stopOpacity="0.9" />
<stop offset="1" stopColor="#C084FC" stopOpacity="0" />
</linearGradient>
</defs>
{/* base line */}
<path d={FLOW_PATH} stroke="url(#wnd-flow)" strokeWidth="1.5" opacity="0.22" />
{/* constant drifting dots */}
<motion.path
d={FLOW_PATH}
stroke="url(#wnd-flow)"
strokeWidth="2.4"
strokeLinecap="round"
strokeDasharray="2 17"
opacity="0.5"
animate={reduce ? undefined : { strokeDashoffset: [0, -190] }}
transition={{ duration: 10, repeat: Infinity, ease: "linear" }}
/>
{/* single slow transformation beam sweeping left → right */}
{!reduce && (
<motion.path
d={FLOW_PATH}
stroke="url(#wnd-beam)"
strokeWidth="3.5"
strokeLinecap="round"
strokeDasharray="40 1200"
animate={{ strokeDashoffset: [0, -1240] }}
transition={{ duration: 11, repeat: Infinity, ease: "easeInOut" }}
/>
)}
</svg>
</div>
);
}
// Left gutter — the "before": a lone grey storefront and a few disconnected
// nodes. Isolation is the whole point.
function TraditionalCluster({ reduce }: { reduce: boolean }) {
return (
<motion.div
aria-hidden="true"
className="absolute left-4 top-1/2 hidden w-[150px] -translate-y-1/2 xl:block 2xl:left-10"
animate={reduce ? undefined : { y: [0, -10, 0] }}
transition={{ duration: 12, repeat: Infinity, ease: "easeInOut" }}
>
<div className="relative opacity-[0.55]">
<svg width="112" height="112" viewBox="0 0 80 80" fill="none">
{/* awning */}
<path d="M14 30 L66 30 L62 20 L18 20 Z" fill="#B4ABC4" fillOpacity="0.06" stroke="#B4ABC4" strokeWidth="1.6" />
<path
d="M14 30 L20 37 L26 30 L32 37 L38 30 L44 37 L50 30 L56 37 L62 30"
stroke="#B4ABC4"
strokeWidth="1.4"
fill="none"
/>
{/* body */}
<rect x="18" y="30" width="44" height="34" stroke="#B4ABC4" strokeWidth="1.6" fill="none" />
{/* door */}
<rect x="34" y="45" width="12" height="19" stroke="#B4ABC4" strokeWidth="1.3" fill="none" />
</svg>
{/* disconnected nodes — no lines between them */}
{[
{ c: "left-1 top-2", d: 6 },
{ c: "right-2 top-8", d: 5 },
{ c: "right-4 bottom-3", d: 7 },
].map((n, i) => (
<motion.span
key={i}
className={`absolute ${n.c} rounded-full bg-[#B4ABC4]/50`}
style={{ height: n.d, width: n.d }}
animate={reduce ? undefined : { opacity: [0.3, 0.6, 0.3] }}
transition={{ duration: 3.4 + i, repeat: Infinity, ease: "easeInOut" }}
/>
))}
</div>
</motion.div>
);
}
// Right gutter — the "after": a connected AI network with a pulsing core, a
// living location pin, and a sparkle. Connection is the whole point.
function NearleCluster({ reduce }: { reduce: boolean }) {
const nodes = [
{ x: 20, y: 26 },
{ x: 92, y: 30 },
{ x: 26, y: 90 },
{ x: 90, y: 88 },
{ x: 56, y: 16 },
];
return (
<motion.div
aria-hidden="true"
className="absolute right-4 top-1/2 hidden w-[160px] -translate-y-1/2 xl:block 2xl:right-10"
animate={reduce ? undefined : { y: [0, 12, 0] }}
transition={{ duration: 13, repeat: Infinity, ease: "easeInOut" }}
>
<div className="relative flex flex-col items-center gap-3 opacity-[0.7]">
{/* connected network */}
<svg width="128" height="128" viewBox="0 0 112 112" fill="none">
<g stroke="#A855F7" strokeWidth="1.3" strokeOpacity="0.5">
{nodes.map((n, i) => (
<line key={i} x1="56" y1="56" x2={n.x} y2={n.y} />
))}
</g>
{nodes.map((n, i) => (
<motion.circle
key={i}
cx={n.x}
cy={n.y}
r="3.6"
fill="#C084FC"
animate={reduce ? undefined : { opacity: [0.5, 1, 0.5] }}
transition={{ duration: 2.6 + i * 0.4, repeat: Infinity, ease: "easeInOut" }}
/>
))}
{/* expanding ring off the core */}
{!reduce && (
<motion.circle
cx="56"
cy="56"
r="8"
fill="none"
stroke="#A855F7"
strokeWidth="1.4"
animate={{ r: [8, 22], opacity: [0.6, 0] }}
transition={{ duration: 3.2, repeat: Infinity, ease: "easeOut" }}
/>
)}
{/* pulsing core */}
<motion.circle
cx="56"
cy="56"
r="7"
fill="#7C3AED"
animate={reduce ? undefined : { scale: [1, 1.16, 1], opacity: [0.85, 1, 0.85] }}
style={{ transformOrigin: "56px 56px" }}
transition={{ duration: 2.8, repeat: Infinity, ease: "easeInOut" }}
/>
</svg>
{/* living location pin */}
<div className="relative flex items-center justify-center text-[#7C3AED]">
{!reduce && (
<motion.span
className="absolute h-8 w-8 rounded-full bg-[#A855F7]/20"
animate={{ scale: [0.6, 1.5], opacity: [0.5, 0] }}
transition={{ duration: 2.6, repeat: Infinity, ease: "easeOut" }}
/>
)}
<MaterialIcon icon="location_on" size={26} />
</div>
{/* sparkle */}
<motion.div
className="absolute -right-1 top-1 text-[#C084FC]"
animate={reduce ? undefined : { scale: [0.8, 1.15, 0.8], opacity: [0.4, 1, 0.4], rotate: [0, 12, 0] }}
transition={{ duration: 3.4, repeat: Infinity, ease: "easeInOut" }}
>
<MaterialIcon icon="auto_awesome" size={16} />
</motion.div>
</div>
</motion.div>
);
}
export function WhyNearleDifferent() { export function WhyNearleDifferent() {
const reduce = useReducedMotion(); const reduce = useReducedMotion();
return ( return (
<section <section
aria-labelledby="why-different-title" aria-labelledby="why-different-title"
className="section section-soft w-full overflow-hidden border-y border-[#6330D6]/10" className="section section-soft relative w-full overflow-hidden border-y border-[#6330D6]/10"
> >
<div className="page-container"> {/* Decorative storytelling layer — revealed after the cards (delay), so the
content leads and the illustration follows. */}
<motion.div
aria-hidden="true"
className="pointer-events-none absolute inset-0 z-0"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={viewport}
transition={{ duration: 1.2, delay: 0.55, ease: "easeOut" }}
>
<AmbientBackdrop reduce={!!reduce} />
<TransformationFlow reduce={!!reduce} />
<TraditionalCluster reduce={!!reduce} />
<NearleCluster reduce={!!reduce} />
</motion.div>
<div className="page-container relative z-10">
<motion.div <motion.div
variants={staggerContainer} variants={staggerContainer}
initial="hidden" initial="hidden"
@@ -65,17 +328,22 @@ export function WhyNearleDifferent() {
</motion.p> </motion.p>
</motion.div> </motion.div>
<div className="mt-14 grid gap-5 lg:grid-cols-2 lg:gap-6"> {/* Sequential reveal: heading (above) → left card → right card, staggered
by the parent; rows stagger within each card. */}
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={viewport}
className="mt-14 grid gap-5 lg:grid-cols-2 lg:gap-6"
>
{/* Traditional commerce */} {/* Traditional commerce */}
<motion.div <motion.div
variants={slideInLeftSubtle} variants={slideInLeftSubtle}
initial="hidden"
whileInView="visible"
viewport={viewport}
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 }}
> >
<div className="flex h-full flex-col rounded-[32px] border border-[#16001D]/8 bg-white p-7 shadow-[0_10px_30px_rgba(22,0,29,0.05)] md:p-9"> <div className="flex h-full flex-col rounded-[32px] border border-[#16001D]/8 bg-white p-7 shadow-[0_10px_30px_rgba(22,0,29,0.05)] transition-shadow duration-300 hover:shadow-[0_18px_44px_rgba(22,0,29,0.09)] md:p-9">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[#F1EEF5] text-[#6D6172]"> <span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[#F1EEF5] text-[#6D6172]">
<MaterialIcon icon="apartment" size={22} /> <MaterialIcon icon="apartment" size={22} />
@@ -118,12 +386,17 @@ export function WhyNearleDifferent() {
{/* Nearle AI Commerce — highlighted */} {/* Nearle AI Commerce — highlighted */}
<motion.div <motion.div
variants={slideInRight} variants={slideInRight}
initial="hidden"
whileInView="visible"
viewport={viewport}
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="relative"
> >
{/* soft border glow behind the highlighted card */}
<motion.div
aria-hidden="true"
className="pointer-events-none absolute -inset-1 -z-10 rounded-[36px] bg-[#7C3AED]/10 blur-2xl"
animate={reduce ? undefined : { opacity: [0.4, 0.75, 0.4] }}
transition={{ duration: 4.5, repeat: Infinity, ease: "easeInOut" }}
/>
<Spotlight <Spotlight
className="flex h-full flex-col overflow-hidden rounded-[32px] border border-[#6330D6]/20 bg-[linear-gradient(135deg,#ffffff_0%,#fbf7ff_55%,#f1e8fb_100%)] p-7 shadow-[0_16px_44px_-16px_rgba(99,48,214,0.28)] md:p-9" className="flex h-full flex-col overflow-hidden rounded-[32px] border border-[#6330D6]/20 bg-[linear-gradient(135deg,#ffffff_0%,#fbf7ff_55%,#f1e8fb_100%)] p-7 shadow-[0_16px_44px_-16px_rgba(99,48,214,0.28)] md:p-9"
color="rgba(168,85,247,0.14)" color="rgba(168,85,247,0.14)"
@@ -131,7 +404,13 @@ export function WhyNearleDifferent() {
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[#7C3AED]/10 text-[#7C3AED] ring-1 ring-[#7C3AED]/15"> <span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[#7C3AED]/10 text-[#7C3AED] ring-1 ring-[#7C3AED]/15">
<MaterialIcon icon="auto_awesome" size={22} /> <motion.span
className="inline-flex"
animate={reduce ? undefined : { scale: [1, 1.12, 1], opacity: [0.85, 1, 0.85] }}
transition={{ duration: 2.6, repeat: Infinity, ease: "easeInOut" }}
>
<MaterialIcon icon="auto_awesome" size={22} />
</motion.span>
</span> </span>
<div> <div>
<p className="text-[10px] font-black uppercase tracking-[0.14em] text-[#7C3AED]"> <p className="text-[10px] font-black uppercase tracking-[0.14em] text-[#7C3AED]">
@@ -167,7 +446,7 @@ export function WhyNearleDifferent() {
</motion.ul> </motion.ul>
</Spotlight> </Spotlight>
</motion.div> </motion.div>
</div> </motion.div>
</div> </div>
</section> </section>
); );