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 = [
{
step: "01",
title: "Natural Intent",
desc: "Tell Nearle what you need in plain Tamil or English. AI understands local store products, brands, and quantities without manual searching.",
title: "Just Ask",
desc: "Tell Nearle what you need in your own words — in Tamil or English. No searching, no scrolling through endless lists.",
icon: "psychology",
badge: "Voice & Text AI",
badge: "Voice or Text",
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",
title: "Live Local Discovery",
desc: "AI instantly checks live stock and pricing at verified neighbourhood stores right around your street to find exact matches.",
title: "We Find It Nearby",
desc: "Nearle checks what's on the shelves at trusted shops around your street, so you get exactly what you asked for.",
icon: "storefront",
badge: "Real-Time Stock",
badge: "In Stock Nearby",
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",
title: "Instant Fulfillment",
desc: "Confirms your order, alerts the local shopkeeper to pack, and dispatches a Nearle rider for fast doorstep delivery — all in 1 chat.",
title: "Delivered to You",
desc: "Confirm your order and the shopkeeper packs it while a Nearle rider brings it to your door — all in one chat.",
icon: "bolt",
badge: "1-Tap Fulfillment",
badge: "To Your Door",
iconBg: "bg-[#7C3AED]/10 text-[#7C3AED] border-[#7C3AED]/20",
badgeStyle: "bg-[#7C3AED]/10 text-[#7C3AED] border-[#7C3AED]/20",
renderSVG: (reduce: boolean) => <InstantFulfillmentSVG reduce={reduce} />,
@@ -134,8 +134,8 @@ const STEPS = [
const HIGHLIGHTS = [
{
title: "Zero App Switching",
sub: "Everything in 1 conversation",
title: "No App Switching",
sub: "Everything in one chat",
icon: "forum",
iconColor: "bg-primary/10 text-primary",
},
@@ -146,14 +146,14 @@ const HIGHLIGHTS = [
iconColor: "bg-secondary/10 text-secondary",
},
{
title: "Live Inventory",
sub: "Real-time store stock",
title: "Real Availability",
sub: "See what's in stock nearby",
icon: "inventory_2",
iconColor: "bg-blue-600/10 text-blue-600",
},
{
title: "Fast Rider Delivery",
sub: "Direct doorstep fulfillment",
title: "Fast Local Delivery",
sub: "Straight to your door",
icon: "local_shipping",
iconColor: "bg-[#7C3AED]/10 text-[#7C3AED]",
},
@@ -181,22 +181,22 @@ export function AICommerceIntro() {
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]"
>
Nearle AI Engine
Powered by Nearle AI
</motion.span>
<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-[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.p
variants={fadeUp}
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.div>
@@ -217,13 +217,13 @@ export function AICommerceIntro() {
<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">
<MaterialIcon icon="auto_awesome" size={18} />
<span>Conversational Commerce</span>
<span>Just Say It</span>
</div>
<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>
<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>
</div>
@@ -233,8 +233,8 @@ export function AICommerceIntro() {
<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 md:text-base font-bold text-[#16001D]">Natural Voice & Text Query</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]">Tell us what you need</p>
</div>
</div>
@@ -243,8 +243,8 @@ export function AICommerceIntro() {
<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 md:text-base font-bold text-[#16001D]">Live Local Shop Scan</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]">We find it nearby</p>
</div>
</div>
@@ -254,7 +254,7 @@ export function AICommerceIntro() {
</div>
<div>
<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>

View File

@@ -8,8 +8,8 @@ import { fadeUp, fadeIn, scaleIn, staggerContainer, viewport } from "@/lib/anima
const FEATURES = [
{
title: "AI Catalogue",
desc: "Turn your shelf into a live online catalogue with ProductLLM — no manual data entry.",
title: "Digital Catalogue",
desc: "Turn your shelves into an online catalogue in minutes — no manual data entry.",
icon: "menu_book",
visual: "/images/stores/catalogue-ui.webp",
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
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 = [
{ label: "Verified", text: "Local Stores", icon: "store" },
{ label: "Neighborhood", text: "Coverage", icon: "location_on" },
{ label: "Growing", text: "Happy Customers", icon: "mood" },
{ label: "Trusted", text: "Local Platform", icon: "star" },
{
label: "Verified",
text: "Local Stores",
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
@@ -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"
>
<MaterialIcon icon="auto_awesome" size={18} />
<span>Explore AI Commerce</span>
<span>Discover Nearby Stores</span>
</Link>
</MagneticButton>
<MagneticButton>
@@ -204,24 +327,60 @@ export function Hero() {
<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">
<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}
initial="hidden"
whileInView="visible"
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) => (
<Fragment key={item.label}>
<motion.div
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]">
<MaterialIcon icon={item.icon} size={28} />
{/* Corner Hover Glowing Dot on Item */}
<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 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}
</div>
<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="absolute inline-flex h-full w-full rounded-full bg-[#7C3AED] opacity-60" />
</span>
Live AI Commerce demo
See Nearle in action
</p>
<Link
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 = [
{
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",
color: "bg-primary/5 text-primary border-primary/10",
glowColor: "268 85 65",
@@ -132,8 +137,8 @@ const CARDS = [
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.",
title: "Your Shops, On Your Phone",
desc: "The shops you love are now a tap away — browse their shelves and order in seconds, without leaving home.",
icon: "auto_awesome",
color: "bg-emerald-500/10 text-emerald-600 border-emerald-500/20",
glowColor: "160 84 45",
@@ -142,8 +147,8 @@ const CARDS = [
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.",
title: "Always Reliable",
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",
color: "bg-[#de96f9]/10 text-primary border-[#de96f9]/20",
glowColor: "280 85 65",
@@ -167,14 +172,14 @@ export function WhyAICommerceWins() {
viewport={viewport}
>
<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>
<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>
<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
neighbourhood stores online — without replacing them.
Nearle isn&rsquo;t another delivery app. We bring the shops on your street online — so
you can order from them in seconds, while they stay the ones serving you.
</p>
</motion.div>
@@ -207,6 +212,38 @@ export function WhyAICommerceWins() {
animated={true}
>
<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 */}
<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">

View File

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

View File

@@ -20,21 +20,284 @@ const TRADITIONAL = [
];
const NEARLE = [
{ icon: "psychology", text: "AI understands intent" },
{ icon: "storefront", text: "Merchant stays at the center" },
{ icon: "near_me", text: "Customers discover nearby stores naturally" },
{ icon: "handshake", text: "Technology empowers merchants instead of replacing them" },
{ icon: "psychology", text: "Just ask in your own words" },
{ icon: "storefront", text: "The shop you trust stays at the centre" },
{ icon: "near_me", text: "You discover the shops right around you" },
{ 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() {
const reduce = useReducedMotion();
return (
<section
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
variants={staggerContainer}
initial="hidden"
@@ -65,17 +328,22 @@ export function WhyNearleDifferent() {
</motion.p>
</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 */}
<motion.div
variants={slideInLeftSubtle}
initial="hidden"
whileInView="visible"
viewport={viewport}
whileHover={reduce ? undefined : { y: -6, scale: 1.02 }}
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">
<span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-[#F1EEF5] text-[#6D6172]">
<MaterialIcon icon="apartment" size={22} />
@@ -118,12 +386,17 @@ export function WhyNearleDifferent() {
{/* Nearle AI Commerce — highlighted */}
<motion.div
variants={slideInRight}
initial="hidden"
whileInView="visible"
viewport={viewport}
whileHover={reduce ? undefined : { y: -6, scale: 1.02 }}
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
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)"
@@ -131,7 +404,13 @@ export function WhyNearleDifferent() {
>
<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">
<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>
<div>
<p className="text-[10px] font-black uppercase tracking-[0.14em] text-[#7C3AED]">
@@ -167,7 +446,7 @@ export function WhyNearleDifferent() {
</motion.ul>
</Spotlight>
</motion.div>
</div>
</motion.div>
</div>
</section>
);