animation fixed in section

This commit is contained in:
2026-06-29 16:52:46 +05:30
parent 9449accea6
commit 114b49570b
36 changed files with 2308 additions and 1301 deletions

225
AGENTS.md
View File

@@ -1,13 +1,216 @@
# Nearle Project Guide
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
# STRICT PROJECT-WIDE EXECUTION RULES (TOKEN SAVER)
Whenever you are assigned ANY task (bug fix, new feature, UI update, backend logic, etc.), you MUST follow these constraints strictly to prevent infinite loops and save compute resources:
## Project
1. For frontend UI, layout, responsive, styling, or screenshot-reference tasks, you MAY start the Next.js local development server using `npm run dev`.
2. For those frontend visual tasks, you MAY open and inspect `http://localhost:3000` locally.
3. For those frontend visual tasks, you MAY capture local screenshots for before/after comparison.
4. `npm run build` verifies compilation only. Do not claim a visual issue is fixed based only on build success.
5. For visual tasks, inspect localhost before reporting completion.
6. Provide the exact code fix, logic, or file modification directly based on your analysis of the provided files.
7. For frontend UI, layout, responsive, styling, and screenshot-reference tasks, the agent must inspect localhost before reporting completion. Screenshots are required only for major layout changes, responsive/mobile changes, hero/footer changes, or when explicitly requested by the user. For small isolated CSS spacing fixes, screenshots are optional. The human developer gives final approval only.
Nearle is a premium hyperlocal commerce platform connecting
- Customers
- Local Merchants
- Delivery Partners
- Brands
The experience should feel:
- Premium
- Modern
- Minimal
- Community-first
- Human
- Trustworthy
Avoid generic SaaS designs.
---
# Tech Stack
- Next.js 16 App Router
- React
- TypeScript
- Tailwind CSS
- MaterialIcon component
IMPORTANT
This project uses the latest Next.js version.
Before changing framework APIs, read
node_modules/next/dist/docs/
Follow the latest conventions.
---
# Design System
Preserve the existing Nearle identity.
Never change unless explicitly requested:
- Brand colors
- Typography
- Border radius
- Layout hierarchy
- Section order
- Existing content
- Existing illustrations
Always extend the current design instead of redesigning it.
---
# UI Rules
Always match existing Nearle pages.
Reuse:
- Hero layouts
- CTA buttons
- Card styles
- Section spacing
- Grid system
- Shadows
- Glass effects
- Gradients
Avoid Bootstrap-style layouts.
---
# Motion Rules
Use:
- Framer Motion
- CSS transitions
Avoid:
- Heavy GSAP
- Layout animations
- Flashy effects
Animation style:
- Apple
- Linear
- Stripe
- Vercel
Use only:
- opacity
- transform
- scale
- translate
Respect prefers-reduced-motion.
---
# Code Rules
Never rewrite files unnecessarily.
Only modify required code.
Preserve formatting.
Do not rename components unless necessary.
Reuse existing components before creating new ones.
Create reusable components when repeated more than twice.
---
# Performance
Maintain 60 FPS.
Avoid unnecessary re-renders.
Avoid layout shift.
Prefer GPU-friendly animations.
Optimize images.
---
# SEO
Every new page should include:
- Metadata
- Title
- Description
- OpenGraph
- Semantic HTML
---
# Content Rules
Write content specifically for Nearle.
Tone:
- Friendly
- Premium
- Community-first
- Local
- Trustworthy
Avoid AI-sounding or generic marketing copy.
Prefer real examples involving:
- neighbourhood stores
- customers
- delivery partners
- local commerce
- digital catalogues
- QR ordering
---
# Execution Rules
Frontend Tasks
- You MAY run npm run dev.
- Inspect localhost before reporting visual fixes.
- Use screenshots only for major UI changes.
Build
- npm run build only verifies compilation.
- Never claim visual fixes based only on build success.
---
# Workflow
Before editing
1. Read the target file.
2. Understand existing implementation.
3. Modify only the required code.
4. Avoid unrelated refactoring.
5. Keep code production ready.
6. Return concise summaries.
---
# Response Rules
Do not explain obvious code.
Do not rewrite unchanged code.
Do not introduce unnecessary abstractions.
Focus only on the requested task.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 954 KiB

After

Width:  |  Height:  |  Size: 755 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 KiB

After

Width:  |  Height:  |  Size: 509 KiB

View File

@@ -3,8 +3,9 @@ import Image from "next/image";
import Link from "next/link";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import { AboutHero } from "@/components/heroes/AboutHero";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -48,93 +49,27 @@ const LOCAL_COUNTERS = [
{ count: "Growing", label: "Neighbourhood Reach", icon: "location_city" }
];
const HERO_STATS = [
{ title: "2km Coverage", icon: "explore", desc: "Hyperlocal radius" },
{ title: "Verified Local Stores", icon: "store", desc: "Growing local commerce" },
{ title: "Happy Customers", icon: "mood", desc: "Neighbourhood community" },
{ title: "Coimbatore & Beyond", icon: "location_city", desc: "Growing neighbourhood reach" }
];
export default function AboutPage() {
const collageCards = [
{ text: "Local First", icon: "store" },
{ text: "Trusted Network", icon: "verified" },
{ text: "Connected Communities", icon: "groups" }
];
return (
<PremiumPageShell>
{/* ─── Hero Section ─── */}
<section className="w-full flex flex-col pt-6 md:pt-10 select-none bg-gradient-to-br from-white via-[#FEFCFF] to-[#F4ECFA]">
{/* TWO-COLUMN GRID */}
<div className="border-b border-[#6330D6]/10">
<div className="grid grid-cols-1 lg:grid-cols-[45%_55%] items-center gap-10 lg:gap-6 px-6 md:px-12 lg:px-16 pb-12 max-w-[1600px] mx-auto">
{/* Left Column: Copy & Actions */}
<div className="flex flex-col text-left max-w-xl">
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit">
About Nearle
</span>
<h1 className="font-display text-4xl sm:text-5xl lg:text-[48px] xl:text-[54px] font-black text-[#16001D] leading-[1.1] tracking-tight">
Built for Every
<br />
<span className="text-[#6330D6]">Neighbourhood.</span>
</h1>
<p className="text-sm md:text-base text-[#5E5866] font-bold mt-6 leading-relaxed">
Nearle is India&apos;s first hyper-local managed Neighbourhood living platform which brings all the amenities and requirements of residents to their fingertips.
</p>
<div className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto">
<a
href="#story-section"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
>
Our Story
</a>
<Link
href="/contact"
className="border-2 border-[#6330D6]/20 hover:border-[#6330D6]/40 text-[#6330D6] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-all duration-200 hover:bg-[#6330D6]/5 active:scale-[0.98]"
>
Join Our Journey
</Link>
</div>
</div>
{/* Right Column: Visual Collage */}
<div className="w-full">
<HeroVisualCollage
centerImage="/nearlenewlogo.png"
leftImage="/Nearle Business.png"
rightImage="/nearle people.png"
glassCards={collageCards}
isPhoneCenter={false}
/>
</div>
</div>
</div>
{/* BOTTOM HERO STATS */}
<div className="bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 max-w-[1600px] mx-auto">
<FeatureStatsRow items={HERO_STATS} />
</div>
</div>
</section>
<AboutHero />
{/* ─── Intro Description Block ─── */}
<section id="story-section" className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-white border-b border-[#6330D6]/10">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Our Platform
</span>
<h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-4">
Nearle Neighbourhood Commerce
</h2>
<p className="mx-auto max-w-3xl text-sm md:text-base text-[#5E5866] leading-relaxed font-semibold">
We are redefining modern urban living by providing never before convenience. By linking local physical stores to an integrated digital framework, Nearle boosts merchant sales and delivers products instantly, fostering closer, cleaner, and faster local economies.
</p>
</section>
<AnimatedSection>
<section id="story-section" className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-white border-b border-[#6330D6]/10">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Our Platform
</span>
<h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-4">
Nearle Neighbourhood Commerce
</h2>
<p className="mx-auto max-w-3xl text-sm md:text-base text-[#5E5866] leading-relaxed font-semibold">
We are redefining modern urban living by providing never before convenience. By linking local physical stores to an integrated digital framework, Nearle boosts merchant sales and delivers products instantly, fostering closer, cleaner, and faster local economies.
</p>
</section>
</AnimatedSection>
{/* ─── Strategy Cards (Mission, Vision, Why) ─── */}
<section
@@ -145,12 +80,12 @@ export default function AboutPage() {
className="mx-auto w-[calc(100%-48px)] md:w-[calc(100%-64px)] xl:w-[calc(100%-96px)]"
style={{ maxWidth: 1800 }}
>
<div
<AnimatedGrid
className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3"
style={{ gap: "clamp(24px, 2.5vw, 36px)" }}
>
{STRATEGY_CARDS.map((card) => (
<div
<AnimatedCard
key={card.title}
className="bg-white border border-[#6330D6]/[0.10] shadow-[0_4px_28px_rgba(99,48,214,0.07)] flex flex-col overflow-hidden min-h-[480px] md:min-h-[520px] xl:min-h-[540px] 2xl:min-h-[560px]"
style={{ borderRadius: 32 }}
@@ -196,17 +131,17 @@ export default function AboutPage() {
className={card.imgFit}
/>
</div>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</section>
{/* ─── Global Stats counters ─── */}
<section className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-white border-b border-[#6330D6]/10">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
<AnimatedGrid className="grid grid-cols-1 md:grid-cols-3 gap-8">
{LOCAL_COUNTERS.map((c) => (
<div key={c.label} className="flex flex-col items-center text-center">
<AnimatedCard key={c.label} className="flex flex-col items-center text-center">
<div className="w-12 h-12 rounded-full bg-[#6330D6]/5 text-[#6330D6] flex items-center justify-center mb-4">
<MaterialIcon icon={c.icon} size={22} />
</div>
@@ -214,41 +149,43 @@ export default function AboutPage() {
<p className="text-[10px] font-bold text-[#5E5866] mt-1.5 uppercase tracking-wider">
{c.label}
</p>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</section>
{/* ─── Brand Building Banner ─── */}
<section className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-[#FAF7FD] mb-2 md:mb-3 xl:mb-4">
<div className="max-w-4xl mx-auto">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
Don&apos;t miss the opportunity
</span>
<h2 className="font-display text-3xl font-black text-[#16001D] mt-4 mb-4">
Build Your Brand with Nearle
</h2>
<p className="text-xs md:text-sm text-[#5E5866] max-w-2xl mx-auto mb-8 leading-relaxed font-semibold">
Delight your neighborhood with a personalized online experience. Unlock revenue opportunities and local marketing strategies that will take your business further.
</p>
<div className="flex flex-wrap justify-center gap-4">
<Link
href="/businesses"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm hover:shadow-lg transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="bolt" size={18} />
<span>Register Merchant</span>
</Link>
<Link
href="/people"
className="bg-white hover:bg-white/95 border border-[#6330D6]/10 text-[#16001D] font-black px-8 py-3.5 rounded-xl text-sm transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="download" size={18} />
<span>Explore Nearle</span>
</Link>
<AnimatedSection>
<section className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-[#FAF7FD] mb-2 md:mb-3 xl:mb-4">
<div className="max-w-4xl mx-auto">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
Don&apos;t miss the opportunity
</span>
<h2 className="font-display text-3xl font-black text-[#16001D] mt-4 mb-4">
Build Your Brand with Nearle
</h2>
<p className="text-xs md:text-sm text-[#5E5866] max-w-2xl mx-auto mb-8 leading-relaxed font-semibold">
Delight your neighborhood with a personalized online experience. Unlock revenue opportunities and local marketing strategies that will take your business further.
</p>
<div className="flex flex-wrap justify-center gap-4">
<Link
href="/businesses"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm hover:shadow-lg transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="bolt" size={18} />
<span>Register Merchant</span>
</Link>
<Link
href="/people"
className="bg-white hover:bg-white/95 border border-[#6330D6]/10 text-[#16001D] font-black px-8 py-3.5 rounded-xl text-sm transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="download" size={18} />
<span>Explore Nearle</span>
</Link>
</div>
</div>
</div>
</section>
</section>
</AnimatedSection>
</PremiumPageShell>
);
}

View File

@@ -5,6 +5,9 @@ import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import { FAQAccordion } from "@/components/ui/FAQAccordion";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import { CTABanner } from "@/components/ui/CTABanner";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -215,21 +218,23 @@ export default function BrandsPage() {
{/* ─── What We Offer ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
What Nearle Offers Brands
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
The Full Stack of Neighbourhood Commerce
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
From your first store listing to city-wide distribution Nearle builds the infrastructure so you can focus on your product.
</p>
</div>
<AnimatedSection>
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
What Nearle Offers Brands
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
The Full Stack of Neighbourhood Commerce
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
From your first store listing to city-wide distribution Nearle builds the infrastructure so you can focus on your product.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<AnimatedGrid className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{WHAT_WE_OFFER.map((item) => (
<div
<AnimatedCard
key={item.title}
className="rounded-[28px] border border-[#6330D6]/10 bg-[#FAF7FD] p-7 flex flex-col gap-4 hover:shadow-[0_10px_32px_rgba(22,0,29,0.09)] transition-shadow duration-300"
>
@@ -245,30 +250,32 @@ export default function BrandsPage() {
<h3 className="text-base font-black text-[#16001D] leading-tight mb-2">{item.title}</h3>
<p className="text-sm text-[#5E5866] leading-relaxed">{item.desc}</p>
</div>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</section>
{/* ─── Industries ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-10">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
Industries We Serve
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
Your Category Has a Place on Nearle
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Nearle&apos;s neighbourhood store network stocks a wide range of everyday product categories. If customers buy it daily, it belongs here.
</p>
</div>
<AnimatedSection>
<div className="text-center mb-10">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
Industries We Serve
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
Your Category Has a Place on Nearle
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Nearle&apos;s neighbourhood store network stocks a wide range of everyday product categories. If customers buy it daily, it belongs here.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-3 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-9 gap-4">
<AnimatedGrid className="grid grid-cols-3 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-9 gap-4">
{INDUSTRIES.map((industry) => (
<div
<AnimatedCard
key={industry.label}
className="flex flex-col items-center gap-2.5 p-4 rounded-2xl bg-white border border-[#6330D6]/8 hover:border-[#6330D6]/20 hover:shadow-md transition-all duration-200 cursor-default"
>
@@ -276,30 +283,32 @@ export default function BrandsPage() {
<MaterialIcon icon={industry.icon} size={20} />
</div>
<span className="text-[10px] font-black text-[#16001D] text-center leading-tight">{industry.label}</span>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</section>
{/* ─── Partnership Process ─── */}
<section id="partnership" className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
How It Works
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
The Partnership Process
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
From first conversation to city-wide brand presence in four structured steps.
</p>
</div>
<AnimatedSection>
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
How It Works
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
The Partnership Process
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
From first conversation to city-wide brand presence in four structured steps.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
<AnimatedGrid className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
{PARTNERSHIP_STEPS.map((step) => (
<article
<AnimatedCard
key={step.num}
className="relative flex flex-col rounded-[28px] border border-[#6330D6]/10 bg-[#FAF7FD] p-7 shadow-[0_8px_32px_rgba(22,0,29,0.05)]"
>
@@ -313,9 +322,9 @@ export default function BrandsPage() {
{step.title}
</h3>
<p className="text-sm text-[#5E5866] leading-relaxed">{step.desc}</p>
</article>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</section>
@@ -323,24 +332,26 @@ export default function BrandsPage() {
<section className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-[360px_1fr] gap-10 lg:gap-16 items-start">
<div className="lg:sticky lg:top-28">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
FAQs
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Brand Partner Questions
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6">
What brand managers usually ask before starting their Nearle partnership.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 text-sm font-black text-[#6330D6] hover:text-[#5225b8] transition-colors"
>
<span>Talk to our sales team</span>
<MaterialIcon icon="arrow_forward" size={16} />
</Link>
</div>
<AnimatedSection>
<div className="lg:sticky lg:top-28">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
FAQs
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Brand Partner Questions
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6">
What brand managers usually ask before starting their Nearle partnership.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 text-sm font-black text-[#6330D6] hover:text-[#5225b8] transition-colors"
>
<span>Talk to our sales team</span>
<MaterialIcon icon="arrow_forward" size={16} />
</Link>
</div>
</AnimatedSection>
<FAQAccordion items={FAQ_ITEMS} />
</div>
@@ -348,43 +359,17 @@ export default function BrandsPage() {
</section>
{/* ─── CTA Banner ─── */}
<section className="bg-white px-2 pt-2 pb-2 md:px-3 md:pt-3 md:pb-3 lg:px-4 lg:pt-4 lg:pb-4 xl:px-5 xl:pt-4 xl:pb-4 2xl:px-6 2xl:pb-5">
<div className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#1A0322] via-[#2B0040] to-[#0A000D] text-white">
<div className="px-6 py-12 md:px-10 md:py-14 lg:px-14 xl:py-12 text-center relative">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_30%,rgba(99,48,214,0.2),transparent_50%)] pointer-events-none" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_20%_70%,rgba(0,109,55,0.12),transparent_45%)] pointer-events-none" />
<div className="relative z-10 max-w-2xl mx-auto">
<span className="inline-flex items-center gap-2 px-3 py-1 rounded-full border border-white/15 text-white/70 text-xs font-black uppercase tracking-widest mb-6">
<MaterialIcon icon="hub" size={14} />
Brand Partnership
</span>
<h2 className="font-display text-3xl md:text-4xl xl:text-5xl font-black mb-4 tracking-tight leading-tight">
Ready to Grow in
<br />Every Neighbourhood?
</h2>
<p className="text-sm md:text-base text-white/70 mb-8 leading-relaxed max-w-lg mx-auto">
Let&apos;s talk about what a Nearle brand partnership looks like for your products. Our sales team will design a custom plan for your brand.
</p>
<div className="flex flex-wrap justify-center gap-3.5">
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm hover:shadow-xl transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="forum" size={18} />
<span>Talk to Sales</span>
</Link>
<Link
href="/contact"
className="bg-white/10 hover:bg-white/15 border border-white/20 text-white font-black px-8 py-3.5 rounded-xl text-sm transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="mail" size={18} />
<span>Send an Enquiry</span>
</Link>
</div>
</div>
</div>
</div>
</section>
<CTABanner
badge={{ icon: "hub", label: "Brand Partnership" }}
heading={<>Ready to Grow in<br />Every Neighbourhood?</>}
body="Let's talk about what a Nearle brand partnership looks like for your products. Our sales team will design a custom plan for your brand."
primary={{ href: "/contact", icon: "forum", label: "Talk to Sales" }}
secondary={{ href: "/contact", icon: "mail", label: "Send an Enquiry" }}
decorations={[
"circle at 70% 30%, rgba(99,48,214,0.2), transparent 50%",
"circle at 20% 70%, rgba(0,109,55,0.12), transparent 45%",
]}
/>
</PremiumPageShell>
);
}

View File

@@ -3,9 +3,11 @@ import Image from "next/image";
import Link from "next/link";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import { BusinessFeaturesGrid } from "@/components/businesses/BusinessFeaturesGrid";
import { BusinessHero } from "@/components/heroes/BusinessHero";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import { CTABanner } from "@/components/ui/CTABanner";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -15,12 +17,6 @@ export const metadata: Metadata = {
};
const HERO_BENEFITS = [
{ title: "More Visibility Across Nearle", icon: "visibility", desc: "Get discovered by locals" },
{ title: "Easy Order Management", icon: "assignment", desc: "Track and fulfill cleanly" },
{ title: "Grow Repeat Customers", icon: "trending_up", desc: "Build neighborhood loyalty" },
{ title: "Local Delivery Support", icon: "local_shipping", desc: "Deliver fast & verified" }
];
function PhoneMapVisual() {
return (
@@ -96,102 +92,42 @@ function PhoneMapVisual() {
}
export default function BusinessesPage() {
const collageCards = [
{ text: "Nearby Customers", icon: "people" },
{ text: "Orders & Offers", icon: "local_offer" },
{ text: "Local Delivery Support", icon: "local_shipping" }
];
return (
<PremiumPageShell>
{/* ─── Hero Section ─── */}
<section className="w-full flex flex-col pt-6 md:pt-10 select-none bg-gradient-to-br from-white via-[#FEFCFF] to-[#F4ECFA]">
{/* TWO-COLUMN GRID */}
<div className="border-b border-[#6330D6]/10">
<div className="grid grid-cols-1 lg:grid-cols-[45%_55%] items-center gap-10 lg:gap-6 px-6 md:px-12 lg:px-16 pb-12 max-w-[1600px] mx-auto">
{/* Left Column: Copy & Actions */}
<div className="flex flex-col text-left max-w-xl">
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit">
Nearle for Business
</span>
<h1 className="font-display text-4xl sm:text-5xl lg:text-[48px] xl:text-[54px] font-black text-[#16001D] leading-[1.1] tracking-tight">
Grow Your Local Business,
<br />
<span className="text-[#6330D6]">Neighbourhood by Neighbourhood.</span>
</h1>
<p className="text-sm md:text-base text-[#5E5866] font-bold mt-6 leading-relaxed">
Get discovered by nearby customers, manage orders, and build repeat business from one simple platform.
</p>
<div className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto">
{/* TODO: Replace with real merchant onboarding URL once confirmed */}
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
>
List Your Store
</Link>
<Link
href="/contact"
className="border-2 border-[#6330D6]/20 hover:border-[#6330D6]/40 text-[#6330D6] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-all duration-200 hover:bg-[#6330D6]/5 active:scale-[0.98]"
>
Explore Solutions
</Link>
</div>
</div>
{/* Right Column: Visual Collage */}
<div className="w-full">
<HeroVisualCollage
centerImage="/nearlenewlogo.png"
leftImage="/images/cat-daily-essentials.png"
rightImage="/Nearle Business.png"
glassCards={collageCards}
isPhoneCenter={false}
/>
</div>
</div>
</div>
{/* BOTTOM HERO BENEFITS */}
<div className="bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 max-w-[1600px] mx-auto">
<FeatureStatsRow items={HERO_BENEFITS} />
</div>
</div>
</section>
<BusinessHero />
{/* ─── Intro Highlight Banner ─── */}
<section className="py-7 px-6 md:py-9 md:px-12 lg:px-16 xl:px-20 xl:py-8 2xl:px-24 flex flex-col lg:flex-row justify-between items-start lg:items-center gap-6 border-b border-[#6330D6]/10 bg-white">
<div className="max-w-2xl text-left">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3 py-1 rounded-full">
Together, we grow
</span>
<h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-3 leading-tight">
Get set in minutes and delight the Neighborhood
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed">
Nearle is built to help businesses like <strong className="text-[#6330D6]">Yours</strong> establish a local online presence and empower neighborhood customers to discover and interact with you directly.
</p>
</div>
<div className="flex gap-4 shrink-0">
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm shadow-md active:scale-95 transition-all flex items-center gap-2"
>
<MaterialIcon icon="chat" size={18} />
<span>Talk to an Expert</span>
</Link>
</div>
</section>
<AnimatedSection>
<section className="py-7 px-6 md:py-9 md:px-12 lg:px-16 xl:px-20 xl:py-8 2xl:px-24 flex flex-col lg:flex-row justify-between items-start lg:items-center gap-6 border-b border-[#6330D6]/10 bg-white">
<div className="max-w-2xl text-left">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3 py-1 rounded-full">
Together, we grow
</span>
<h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-3 leading-tight">
Get set in minutes and delight the Neighborhood
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed">
Nearle is built to help businesses like <strong className="text-[#6330D6]">Yours</strong> establish a local online presence and empower neighborhood customers to discover and interact with you directly.
</p>
</div>
<div className="flex gap-4 shrink-0">
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm shadow-md active:scale-95 transition-all flex items-center gap-2"
>
<MaterialIcon icon="chat" size={18} />
<span>Talk to an Expert</span>
</Link>
</div>
</section>
</AnimatedSection>
{/* ─── Build Your Neighborhood Brand ─── */}
<section className="py-10 md:py-14 lg:py-16 xl:py-12 border-b border-[#6330D6]/10 bg-white overflow-hidden">
<div className="w-[calc(100%-32px)] lg:w-[calc(100%-64px)] max-w-[1800px] mx-auto">
{/* Header */}
<AnimatedSection>
<div className="text-center mb-10 lg:mb-12 xl:mb-10">
<span className="inline-flex items-center rounded-full bg-[#6330D6]/5 px-4 py-1.5 text-xs font-black uppercase tracking-widest text-[#6330D6]">
How it works
@@ -203,11 +139,12 @@ export default function BusinessesPage() {
Three simple milestones to scale your retail reach from offline streets straight into neighborhood homes.
</p>
</div>
</AnimatedSection>
{/* Cards grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-7">
<AnimatedGrid className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-7">
{/* Card 1 - Get Online */}
<article className="relative flex min-h-[360px] md:min-h-[390px] lg:min-h-[420px] flex-col overflow-hidden rounded-[32px] border border-[rgba(99,48,214,0.12)] bg-white p-7 md:p-8 shadow-[0_18px_45px_rgba(22,0,29,0.08)]">
<AnimatedCard className="relative flex min-h-[360px] md:min-h-[390px] lg:min-h-[420px] flex-col overflow-hidden rounded-[32px] border border-[rgba(99,48,214,0.12)] bg-white p-7 md:p-8 shadow-[0_18px_45px_rgba(22,0,29,0.08)]">
<span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-[#6330D6]/[0.07] select-none">
01
</span>
@@ -233,10 +170,10 @@ export default function BusinessesPage() {
/>
</div>
</div>
</article>
</AnimatedCard>
{/* Card 2 - Get Noticed */}
<article className="relative flex min-h-[360px] md:min-h-[390px] lg:min-h-[420px] flex-col overflow-hidden rounded-[32px] border border-[rgba(99,48,214,0.12)] bg-white p-7 md:p-8 shadow-[0_18px_45px_rgba(22,0,29,0.08)]">
<AnimatedCard className="relative flex min-h-[360px] md:min-h-[390px] lg:min-h-[420px] flex-col overflow-hidden rounded-[32px] border border-[rgba(99,48,214,0.12)] bg-white p-7 md:p-8 shadow-[0_18px_45px_rgba(22,0,29,0.08)]">
<span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-[#6330D6]/[0.07] select-none">
02
</span>
@@ -254,10 +191,10 @@ export default function BusinessesPage() {
<div className="relative z-0 mt-auto h-[270px] lg:h-[290px] flex items-center justify-center">
<PhoneMapVisual />
</div>
</article>
</AnimatedCard>
{/* Card 3 - Get Simplified */}
<article className="relative flex min-h-[360px] md:min-h-[390px] lg:min-h-[420px] flex-col overflow-hidden rounded-[32px] border border-[rgba(99,48,214,0.12)] bg-white p-7 md:p-8 shadow-[0_18px_45px_rgba(22,0,29,0.08)]">
<AnimatedCard className="relative flex min-h-[360px] md:min-h-[390px] lg:min-h-[420px] flex-col overflow-hidden rounded-[32px] border border-[rgba(99,48,214,0.12)] bg-white p-7 md:p-8 shadow-[0_18px_45px_rgba(22,0,29,0.08)]">
<span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-[#6330D6]/[0.07] select-none">
03
</span>
@@ -290,8 +227,8 @@ export default function BusinessesPage() {
/>
</div>
</div>
</article>
</div>
</AnimatedCard>
</AnimatedGrid>
</div>
</section>
@@ -299,38 +236,15 @@ export default function BusinessesPage() {
<BusinessFeaturesGrid />
{/* ─── Join/Download Call to Action ─── */}
<section className="bg-white px-2 pt-2 pb-2 md:px-3 md:pt-3 md:pb-3 lg:px-4 lg:pt-4 lg:pb-4 xl:px-5 xl:pt-4 xl:pb-4 2xl:px-6 2xl:pb-5">
<div className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#1A0322] to-[#0A000D] text-white">
<div className="px-6 py-10 md:px-10 md:py-12 lg:px-14 lg:py-14 xl:py-12 text-center relative">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_20%,rgba(99,48,214,0.15),transparent_50%)] pointer-events-none" />
<div className="relative z-10 max-w-2xl mx-auto">
<h2 className="font-display text-3xl md:text-4xl font-black mb-4 tracking-tight">
Nearle is Essential for Business Growth
</h2>
<p className="text-xs md:text-sm text-white/70 mb-8 leading-relaxed max-w-lg mx-auto">
Leverage the full potential of your neighborhood. Stand above the rest, increase customers, and scale your brand today.
</p>
<div className="flex flex-wrap justify-center gap-3.5">
{/* TODO: Replace with real merchant onboarding URL once confirmed */}
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm hover:shadow-xl transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="bolt" size={18} />
<span>Register Interest</span>
</Link>
<Link
href="/contact"
className="bg-white/10 hover:bg-white/15 border border-white/20 text-white font-black px-8 py-3.5 rounded-xl text-sm transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="phone" size={18} />
<span>Contact Experts</span>
</Link>
</div>
</div>
</div>
</div>
</section>
<CTABanner
heading="Nearle is Essential for Business Growth"
body="Leverage the full potential of your neighborhood. Stand above the rest, increase customers, and scale your brand today."
primary={{ href: "/contact", icon: "bolt", label: "Register Interest" }}
secondary={{ href: "/contact", icon: "phone", label: "Contact Experts" }}
decorations={[
"circle at 70% 20%, rgba(99,48,214,0.15), transparent 50%",
]}
/>
</PremiumPageShell>
);
}

View File

@@ -1,7 +1,8 @@
import React from "react";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { ContactForm } from "@/components/contact/ContactForm";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { ContactHero } from "@/components/heroes/ContactHero";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -13,95 +14,10 @@ export default function ContactPage() {
return (
<PremiumPageShell>
{/* ─── Contact Section ─── */}
<section className="relative border-b border-[#6330D6]/10 px-4 py-10 md:px-6 md:py-12 lg:px-8 lg:py-14 xl:px-10 xl:py-16 2xl:px-12 2xl:py-20">
{/* Subtle Map Grid/Location decoration behind left content */}
<div className="absolute left-[5%] top-[10%] w-[400px] h-[300px] opacity-[0.04] select-none pointer-events-none -z-10">
<svg className="w-full h-full text-[#6330D6]" stroke="currentColor" fill="none" strokeWidth="1.5" viewBox="0 0 400 300">
<path d="M0,50 L400,50 M0,130 L400,130 M0,210 L400,210 M80,0 L80,300 M200,0 L200,300 M320,0 L320,300" />
<path d="M 80,130 C 120,90 160,170 200,130 C 240,90 280,170 320,130" strokeDasharray="3,3" />
<circle cx="200" cy="130" r="4" fill="currentColor" />
<circle cx="200" cy="130" r="14" strokeWidth="1" />
<circle cx="80" cy="50" r="3" fill="currentColor" />
<circle cx="320" cy="210" r="3" fill="currentColor" />
</svg>
</div>
{/* <div className="w-full mx-auto relative z-10"> */}
<div className="relative z-10 mx-auto w-full max-w-[1520px]">
<div className="grid grid-cols-1 items-start gap-10 lg:grid-cols-[1.2fr_0.8fr] lg:gap-12 xl:gap-16">
{/* Left Column — Contact Info */}
<div className="w-full max-w-[760px] py-6 md:py-8 lg:py-10 relative z-10 text-[#16001D]">
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit">
Contact Us
</span>
<h1 className="font-display text-4xl font-black leading-[1.05] text-[#16001D] md:text-5xl lg:text-[58px] xl:text-[64px] 2xl:text-[70px]">
Were Here to <br />
<span className="text-[#6330D6]">Help You.</span>
</h1>
<p className="mt-6 max-w-[620px] text-sm font-bold leading-8 text-[#5E5866] md:text-base xl:text-lg">
Whether you are a customer, local store owner, or delivery partner, our team is here to help you get started with Nearle.
</p>
{/* Info Rows */}
<div className="mt-8 border-t border-[#6330D6]/10">
{/* Call Us */}
<div className="flex gap-4 py-4.5 border-b border-[#6330D6]/10 items-center">
<div className="w-10 h-10 rounded-full bg-[#6330D6]/10 text-[#6330D6] flex items-center justify-center shrink-0">
<MaterialIcon icon="phone" size={18} />
</div>
<div className="text-left">
<h4 className="text-[#16001D] text-sm font-black">Call Us</h4>
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">+91 909 206 8666</p>
</div>
</div>
{/* Email Us */}
<div className="flex gap-4 py-4.5 border-b border-[#6330D6]/10 items-center">
<div className="w-10 h-10 rounded-full bg-[#6330D6]/10 text-[#6330D6] flex items-center justify-center shrink-0">
<MaterialIcon icon="mail" size={18} />
</div>
<div className="text-left">
<h4 className="text-[#16001D] text-sm font-black">Email Us</h4>
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">nearle@care.in</p>
</div>
</div>
{/* Visit Us */}
<div className="flex gap-4 py-4.5 border-b border-[#6330D6]/10 items-center">
<div className="w-10 h-10 rounded-full bg-[#6330D6]/10 text-[#6330D6] flex items-center justify-center shrink-0">
<MaterialIcon icon="location_on" size={18} />
</div>
<div className="text-left">
<h4 className="text-[#16001D] text-sm font-black">Address </h4>
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">424, DB Rd, R.S. Puram, Coimbatore, TN 641002</p>
</div>
</div>
{/* Helpdesk */}
{/* <div className="flex gap-4 py-4.5 border-b border-[#6330D6]/10 items-center">
<div className="w-10 h-10 rounded-full bg-[#6330D6]/10 text-[#6330D6] flex items-center justify-center shrink-0">
<MaterialIcon icon="support_agent" size={18} />
</div>
<div className="text-left">
<h4 className="text-[#16001D] text-sm font-black">Helpdesk</h4>
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">24/7 Phone Helpdesk Active</p>
</div>
</div> */}
</div>
</div>
{/* Right Column: Compact Form Card */}
<div className="w-full max-w-[660px] justify-self-end pt-10 lg:pt-14 xl:pt-26">
<ContactForm />
</div>
</div>
</div>
</section>
<ContactHero />
{/* ─── Map Section ─── */}
<AnimatedSection>
<section className="bg-[#FAF7FD] px-4 py-8 md:px-6 lg:px-8 xl:px-10 2xl:px-12">
<div className="mx-auto w-full ">
<div className="relative h-[420px] overflow-hidden rounded-2xl border border-[#6330D6]/10 shadow-[0_4px_24px_rgba(99,48,214,0.02)]">
@@ -129,6 +45,7 @@ export default function ContactPage() {
</div>
</div>
</section>
</AnimatedSection>
</PremiumPageShell>
);
}

View File

@@ -5,6 +5,10 @@ import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import { FAQAccordion } from "@/components/ui/FAQAccordion";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import { AnimatedTimeline } from "@/components/delivery/AnimatedTimeline";
import { CTABanner } from "@/components/ui/CTABanner";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -252,21 +256,23 @@ export default function DeliveryPartnersPage() {
{/* ─── Benefits Grid ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Partner Benefits
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
More Than Just Delivery
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Nearle is designed around your life not the other way around.
</p>
</div>
<AnimatedSection>
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Partner Benefits
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
More Than Just Delivery
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Nearle is designed around your life not the other way around.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
<AnimatedGrid className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
{BENEFITS.map((benefit) => (
<div
<AnimatedCard
key={benefit.title}
className="rounded-2xl border border-[#6330D6]/10 bg-[#FAF7FD] p-6 flex flex-col gap-4 hover:shadow-[0_8px_28px_rgba(22,0,29,0.08)] transition-shadow duration-300"
>
@@ -277,9 +283,9 @@ export default function DeliveryPartnersPage() {
<h3 className="text-base font-black text-[#16001D] leading-tight">{benefit.title}</h3>
<p className="mt-2 text-sm text-[#5E5866] leading-relaxed">{benefit.desc}</p>
</div>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</section>
@@ -287,28 +293,30 @@ export default function DeliveryPartnersPage() {
<section className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-[1.1fr_1fr] gap-10 lg:gap-16 items-center">
<div>
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
To Get Started
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
What You Need to Join
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-8 max-w-lg">
No experience needed. If you have a vehicle, a smartphone, and a desire to earn you&apos;re more than qualified.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-7 py-3.5 rounded-xl text-sm shadow-md transition-all duration-200 active:scale-95"
>
<MaterialIcon icon="how_to_reg" size={18} />
<span>Apply Now</span>
</Link>
</div>
<AnimatedSection>
<div>
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
To Get Started
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
What You Need to Join
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-8 max-w-lg">
No experience needed. If you have a vehicle, a smartphone, and a desire to earn you&apos;re more than qualified.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-7 py-3.5 rounded-xl text-sm shadow-md transition-all duration-200 active:scale-95"
>
<MaterialIcon icon="how_to_reg" size={18} />
<span>Apply Now</span>
</Link>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<AnimatedGrid className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{REQUIREMENTS.map((req) => (
<div key={req.title} className="flex flex-col gap-3 p-5 rounded-2xl bg-white border border-[#6330D6]/10 shadow-[0_4px_18px_rgba(22,0,29,0.05)]">
<AnimatedCard key={req.title} className="flex flex-col gap-3 p-5 rounded-2xl bg-white border border-[#6330D6]/10 shadow-[0_4px_18px_rgba(22,0,29,0.05)]">
<div className="w-11 h-11 rounded-2xl bg-[#6330D6]/8 text-[#6330D6] flex items-center justify-center">
<MaterialIcon icon={req.icon} size={22} />
</div>
@@ -316,9 +324,9 @@ export default function DeliveryPartnersPage() {
<h4 className="text-sm font-black text-[#16001D]">{req.title}</h4>
<p className="mt-1 text-xs text-[#5E5866] leading-relaxed">{req.desc}</p>
</div>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</div>
</section>
@@ -326,41 +334,21 @@ export default function DeliveryPartnersPage() {
{/* ─── How Delivery Works (Timeline) ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Your Journey
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
From Sign-Up to First Earnings
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Most delivery partners are out earning within 3 days of applying.
</p>
</div>
<div className="relative max-w-3xl mx-auto">
{/* Vertical line */}
<div className="absolute left-[22px] md:left-[26px] top-8 bottom-8 w-0.5 bg-gradient-to-b from-[#6330D6]/30 via-[#6330D6]/20 to-transparent hidden sm:block" />
<div className="space-y-6">
{TIMELINE.map((item, index) => (
<div key={item.step} className="relative flex gap-5 sm:gap-7">
<div className="flex-shrink-0 w-11 h-11 md:w-12 md:h-12 rounded-full bg-[#6330D6] text-white flex items-center justify-center text-xs font-black shadow-[0_8px_20px_rgba(99,48,214,0.25)] relative z-10">
{String(index + 1).padStart(2, "0")}
</div>
<div className="flex-1 pb-2 pt-1">
<div className="flex items-center gap-3 mb-1.5">
<h3 className="text-base font-black text-[#16001D] leading-tight">{item.title}</h3>
<span className="px-2.5 py-0.5 rounded-full bg-[#6330D6]/8 text-[#6330D6] text-[10px] font-black uppercase tracking-wide shrink-0">
{item.time}
</span>
</div>
<p className="text-sm text-[#5E5866] leading-relaxed">{item.desc}</p>
</div>
</div>
))}
<AnimatedSection>
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Your Journey
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
From Sign-Up to First Earnings
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Most delivery partners are out earning within 3 days of applying.
</p>
</div>
</div>
</AnimatedSection>
<AnimatedTimeline items={TIMELINE} />
</div>
</section>
@@ -416,24 +404,26 @@ export default function DeliveryPartnersPage() {
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-[360px_1fr] gap-10 lg:gap-16 items-start">
<div className="lg:sticky lg:top-28">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
FAQs
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Common Questions
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6">
Everything you want to know before joining the Nearle delivery network.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 text-sm font-black text-[#6330D6] hover:text-[#5225b8] transition-colors"
>
<span>Contact support</span>
<MaterialIcon icon="arrow_forward" size={16} />
</Link>
</div>
<AnimatedSection>
<div className="lg:sticky lg:top-28">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
FAQs
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Common Questions
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6">
Everything you want to know before joining the Nearle delivery network.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 text-sm font-black text-[#6330D6] hover:text-[#5225b8] transition-colors"
>
<span>Contact support</span>
<MaterialIcon icon="arrow_forward" size={16} />
</Link>
</div>
</AnimatedSection>
<FAQAccordion items={FAQ_ITEMS} />
</div>
@@ -441,43 +431,17 @@ export default function DeliveryPartnersPage() {
</section>
{/* ─── CTA Banner ─── */}
<section className="bg-white px-2 pt-2 pb-2 md:px-3 md:pt-3 md:pb-3 lg:px-4 lg:pt-4 lg:pb-4 xl:px-5 xl:pt-4 xl:pb-4 2xl:px-6 2xl:pb-5">
<div className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#1A0322] via-[#2B0040] to-[#0A000D] text-white">
<div className="px-6 py-12 md:px-10 md:py-14 lg:px-14 xl:py-12 text-center relative">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_30%,rgba(0,109,55,0.15),transparent_50%)] pointer-events-none" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_70%,rgba(99,48,214,0.12),transparent_45%)] pointer-events-none" />
<div className="relative z-10 max-w-2xl mx-auto">
<span className="inline-flex items-center gap-2 px-3 py-1 rounded-full border border-white/15 text-white/70 text-xs font-black uppercase tracking-widest mb-6">
<MaterialIcon icon="electric_scooter" size={14} />
Start Earning Today
</span>
<h2 className="font-display text-3xl md:text-4xl xl:text-5xl font-black mb-4 tracking-tight leading-tight">
Join the Nearle
<br />Delivery Network
</h2>
<p className="text-sm md:text-base text-white/70 mb-8 leading-relaxed max-w-lg mx-auto">
Delivery partners in Coimbatore are already earning with Nearle. Sign up today and start delivering in your neighbourhood within days.
</p>
<div className="flex flex-wrap justify-center gap-3.5">
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm hover:shadow-xl transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="how_to_reg" size={18} />
<span>Apply Now</span>
</Link>
<Link
href="/contact"
className="bg-white/10 hover:bg-white/15 border border-white/20 text-white font-black px-8 py-3.5 rounded-xl text-sm transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="phone" size={18} />
<span>Talk to Us</span>
</Link>
</div>
</div>
</div>
</div>
</section>
<CTABanner
badge={{ icon: "electric_scooter", label: "Start Earning Today" }}
heading={<>Join the Nearle<br />Delivery Network</>}
body="Delivery partners in Coimbatore are already earning with Nearle. Sign up today and start delivering in your neighbourhood within days."
primary={{ href: "/contact", icon: "how_to_reg", label: "Apply Now" }}
secondary={{ href: "/contact", icon: "phone", label: "Talk to Us" }}
decorations={[
"circle at 30% 30%, rgba(0,109,55,0.15), transparent 50%",
"circle at 70% 70%, rgba(99,48,214,0.12), transparent 45%",
]}
/>
</PremiumPageShell>
);
}

View File

@@ -1,5 +1,8 @@
import type { Metadata, Viewport } from "next";
import "./globals.css";
import { MotionProvider } from "@/components/layout/MotionProvider";
import { PremiumNavbar } from "@/components/layout/PremiumNavbar";
import { PremiumFooter } from "@/components/layout/PremiumFooter";
const jost = { variable: "font-display" };
const jostBody = { variable: "font-body" };
@@ -70,6 +73,7 @@ export default function RootLayout({
<html
lang="en"
className={`${jost.variable} ${jostBody.variable}`}
data-scroll-behavior="smooth"
suppressHydrationWarning
>
<head>
@@ -86,7 +90,15 @@ export default function RootLayout({
/>
</head>
<body className="bg-[#E8DCF2] text-on-surface font-body antialiased" suppressHydrationWarning>
{children}
<MotionProvider>
<div className="min-h-screen bg-white font-body text-[#16001D] antialiased flex flex-col">
<PremiumNavbar />
<main className="w-full overflow-x-hidden bg-white">
{children}
</main>
<PremiumFooter />
</div>
</MotionProvider>
</body>
</html>
);

View File

@@ -5,6 +5,9 @@ import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import { FAQAccordion } from "@/components/ui/FAQAccordion";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import { CTABanner } from "@/components/ui/CTABanner";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -226,21 +229,23 @@ export default function MerchantsPage() {
{/* ─── Merchant Benefits Grid ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
What You Get
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
Everything Your Store Needs to Grow
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Nearle gives your neighbourhood store the same tools that big online retailers have without the complexity.
</p>
</div>
<AnimatedSection>
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
What You Get
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
Everything Your Store Needs to Grow
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
Nearle gives your neighbourhood store the same tools that big online retailers have without the complexity.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
<AnimatedGrid className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5">
{BENEFITS.map((benefit) => (
<div
<AnimatedCard
key={benefit.title}
className="rounded-2xl border border-[#6330D6]/10 bg-[#FAF7FD] p-5 flex flex-col gap-3 hover:shadow-[0_8px_28px_rgba(22,0,29,0.08)] transition-shadow duration-300"
>
@@ -251,30 +256,32 @@ export default function MerchantsPage() {
<h3 className="text-base font-black text-[#16001D] leading-tight">{benefit.title}</h3>
<p className="mt-1.5 text-xs md:text-sm text-[#5E5866] leading-relaxed">{benefit.desc}</p>
</div>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</section>
{/* ─── How It Works ─── */}
<section id="how-it-works" className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
Simple Steps
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
Up and Running in 24 Hours
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
From registration to your first order, we&apos;ve made the process as simple as possible.
</p>
</div>
<AnimatedSection>
<div className="text-center mb-10 md:mb-12">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
Simple Steps
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
Up and Running in 24 Hours
</h2>
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
From registration to your first order, we&apos;ve made the process as simple as possible.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
<AnimatedGrid className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
{HOW_STEPS.map((step) => (
<article
<AnimatedCard
key={step.num}
className="relative flex flex-col rounded-[28px] border border-[#6330D6]/10 bg-white p-7 shadow-[0_8px_32px_rgba(22,0,29,0.06)]"
>
@@ -288,9 +295,9 @@ export default function MerchantsPage() {
{step.title}
</h3>
<p className="text-sm text-[#5E5866] leading-relaxed">{step.desc}</p>
</article>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</section>
@@ -298,37 +305,39 @@ export default function MerchantsPage() {
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10 overflow-hidden">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-[1fr_1.1fr] gap-10 lg:gap-16 items-start">
<div>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Why Nearle
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Your Neighbourhood&apos;s
<br />
<span className="text-[#6330D6]">Best Business Partner</span>
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-8 max-w-lg">
Nearle isn&apos;t built like a big delivery platform that squeezes margins. We&apos;re built for neighbourhood stores like yours where community and trust come first.
</p>
<AnimatedSection>
<div>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Why Nearle
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Your Neighbourhood&apos;s
<br />
<span className="text-[#6330D6]">Best Business Partner</span>
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-8 max-w-lg">
Nearle isn&apos;t built like a big delivery platform that squeezes margins. We&apos;re built for neighbourhood stores like yours where community and trust come first.
</p>
{/* Stats row */}
<div className="grid grid-cols-3 gap-5">
{[
{ stat: "2km", label: "Hyperlocal Coverage" },
{ stat: "24h", label: "Time to Go Live" },
{ stat: "Fair", label: "Commission Model" },
].map((s) => (
<div key={s.label} className="text-center p-4 rounded-2xl bg-[#6330D6]/5 border border-[#6330D6]/8">
<div className="text-2xl md:text-3xl font-display font-black text-[#6330D6]">{s.stat}</div>
<div className="text-[10px] md:text-xs font-bold text-[#5E5866] mt-1 leading-tight">{s.label}</div>
</div>
))}
{/* Stats row */}
<div className="grid grid-cols-3 gap-5">
{[
{ stat: "2km", label: "Hyperlocal Coverage" },
{ stat: "24h", label: "Time to Go Live" },
{ stat: "Fair", label: "Commission Model" },
].map((s) => (
<div key={s.label} className="text-center p-4 rounded-2xl bg-[#6330D6]/5 border border-[#6330D6]/8">
<div className="text-2xl md:text-3xl font-display font-black text-[#6330D6]">{s.stat}</div>
<div className="text-[10px] md:text-xs font-bold text-[#5E5866] mt-1 leading-tight">{s.label}</div>
</div>
))}
</div>
</div>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<AnimatedGrid className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{WHY_REASONS.map((reason) => (
<div key={reason.title} className="flex gap-3.5 p-4 rounded-2xl border border-[#6330D6]/8 bg-[#FAF7FD]">
<AnimatedCard key={reason.title} className="flex gap-3.5 p-4 rounded-2xl border border-[#6330D6]/8 bg-[#FAF7FD]">
<div className="w-10 h-10 shrink-0 rounded-full bg-[#6330D6]/10 text-[#6330D6] flex items-center justify-center">
<MaterialIcon icon={reason.icon} size={20} />
</div>
@@ -336,9 +345,9 @@ export default function MerchantsPage() {
<h4 className="text-sm font-black text-[#16001D] leading-tight">{reason.title}</h4>
<p className="mt-1 text-xs text-[#5E5866] leading-relaxed">{reason.desc}</p>
</div>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</div>
</div>
</section>
@@ -346,53 +355,55 @@ export default function MerchantsPage() {
{/* ─── Success Story ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-8">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Merchant Story
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 tracking-tight">
Real Results from Real Stores
</h2>
</div>
<AnimatedSection>
<div className="text-center mb-8">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Merchant Story
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 tracking-tight">
Real Results from Real Stores
</h2>
</div>
<div className="relative overflow-hidden rounded-[2rem] bg-[linear-gradient(135deg,#ffffff_0%,#F4ECFA_100%)] border border-[#6330D6]/10 shadow-[0_18px_50px_rgba(22,0,29,0.08)] p-8 md:p-10 lg:p-12">
<div className="pointer-events-none absolute -right-20 -top-20 h-64 w-64 rounded-full bg-[#6330D6]/5 blur-3xl" />
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-[1fr_1.5fr] gap-8 lg:gap-12 items-center">
<div>
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/10 text-secondary text-xs font-black uppercase tracking-wider mb-5">
<MaterialIcon icon="store" size={14} />
Success Story
</div>
<blockquote className="text-xl md:text-2xl font-black text-[#16001D] leading-snug mb-6">
&ldquo;In three months, my daily orders doubled. Customers who used to call now just scan the QR and order. Even my evening rush feels calmer.&rdquo;
</blockquote>
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-full bg-[#6330D6]/10 flex items-center justify-center">
<MaterialIcon icon="person" size={22} className="text-[#6330D6]" />
<div className="relative overflow-hidden rounded-[2rem] bg-[linear-gradient(135deg,#ffffff_0%,#F4ECFA_100%)] border border-[#6330D6]/10 shadow-[0_18px_50px_rgba(22,0,29,0.08)] p-8 md:p-10 lg:p-12">
<div className="pointer-events-none absolute -right-20 -top-20 h-64 w-64 rounded-full bg-[#6330D6]/5 blur-3xl" />
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-[1fr_1.5fr] gap-8 lg:gap-12 items-center">
<div>
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/10 text-secondary text-xs font-black uppercase tracking-wider mb-5">
<MaterialIcon icon="store" size={14} />
Success Story
</div>
<div>
<div className="text-sm font-black text-[#16001D]">Ramesh Kumar</div>
<div className="text-xs text-[#5E5866] font-semibold">R.K. Provision Store, R.S. Puram, Coimbatore</div>
<blockquote className="text-xl md:text-2xl font-black text-[#16001D] leading-snug mb-6">
&ldquo;In three months, my daily orders doubled. Customers who used to call now just scan the QR and order. Even my evening rush feels calmer.&rdquo;
</blockquote>
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-full bg-[#6330D6]/10 flex items-center justify-center">
<MaterialIcon icon="person" size={22} className="text-[#6330D6]" />
</div>
<div>
<div className="text-sm font-black text-[#16001D]">Ramesh Kumar</div>
<div className="text-xs text-[#5E5866] font-semibold">R.K. Provision Store, R.S. Puram, Coimbatore</div>
</div>
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
{[
{ value: "2.1×", label: "Daily Orders Growth", icon: "trending_up", color: "text-[#006D37]" },
{ value: "340+", label: "Repeat Customers Built", icon: "people", color: "text-[#6330D6]" },
{ value: "3 hrs", label: "Saved per Day on Calls", icon: "schedule", color: "text-[#006D37]" },
{ value: "4.9 ★", label: "Average Store Rating", icon: "star", color: "text-amber-500" },
].map((m) => (
<div key={m.label} className="rounded-2xl bg-white border border-[#6330D6]/8 p-5 text-center shadow-sm">
<MaterialIcon icon={m.icon} size={22} className={`${m.color} mx-auto`} />
<div className={`text-2xl font-display font-black mt-2 ${m.color}`}>{m.value}</div>
<div className="text-xs text-[#5E5866] font-semibold mt-1 leading-tight">{m.label}</div>
</div>
))}
<AnimatedGrid className="grid grid-cols-2 gap-4">
{[
{ value: "2.1×", label: "Daily Orders Growth", icon: "trending_up", color: "text-[#006D37]" },
{ value: "340+", label: "Repeat Customers Built", icon: "people", color: "text-[#6330D6]" },
{ value: "3 hrs", label: "Saved per Day on Calls", icon: "schedule", color: "text-[#006D37]" },
{ value: "4.9 ★", label: "Average Store Rating", icon: "star", color: "text-amber-500" },
].map((m) => (
<AnimatedCard key={m.label} className="rounded-2xl bg-white border border-[#6330D6]/8 p-5 text-center shadow-sm">
<MaterialIcon icon={m.icon} size={22} className={`${m.color} mx-auto`} />
<div className={`text-2xl font-display font-black mt-2 ${m.color}`}>{m.value}</div>
<div className="text-xs text-[#5E5866] font-semibold mt-1 leading-tight">{m.label}</div>
</AnimatedCard>
))}
</AnimatedGrid>
</div>
</div>
</div>
</AnimatedSection>
</div>
</section>
@@ -400,24 +411,26 @@ export default function MerchantsPage() {
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-[360px_1fr] gap-10 lg:gap-16 items-start">
<div className="lg:sticky lg:top-28">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
FAQs
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Questions Merchants Ask
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6">
Everything you need to know before joining Nearle. Still have questions? Our team is a call away.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 text-sm font-black text-[#6330D6] hover:text-[#5225b8] transition-colors"
>
<span>Talk to our team</span>
<MaterialIcon icon="arrow_forward" size={16} />
</Link>
</div>
<AnimatedSection>
<div className="lg:sticky lg:top-28">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
FAQs
</span>
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
Questions Merchants Ask
</h2>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6">
Everything you need to know before joining Nearle. Still have questions? Our team is a call away.
</p>
<Link
href="/contact"
className="inline-flex items-center gap-2 text-sm font-black text-[#6330D6] hover:text-[#5225b8] transition-colors"
>
<span>Talk to our team</span>
<MaterialIcon icon="arrow_forward" size={16} />
</Link>
</div>
</AnimatedSection>
<FAQAccordion items={FAQ_ITEMS} />
</div>
@@ -425,42 +438,18 @@ export default function MerchantsPage() {
</section>
{/* ─── CTA Banner ─── */}
<section className="bg-white px-2 pt-2 pb-2 md:px-3 md:pt-3 md:pb-3 lg:px-4 lg:pt-4 lg:pb-4 xl:px-5 xl:pt-4 xl:pb-4 2xl:px-6 2xl:pb-5">
<div className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#1A0322] via-[#2B0040] to-[#0A000D] text-white">
<div className="px-6 py-12 md:px-10 md:py-14 lg:px-14 xl:py-12 text-center relative">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_20%,rgba(99,48,214,0.18),transparent_50%)] pointer-events-none" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_80%,rgba(0,109,55,0.10),transparent_45%)] pointer-events-none" />
<div className="relative z-10 max-w-2xl mx-auto">
<span className="inline-flex items-center gap-2 px-3 py-1 rounded-full border border-white/15 text-white/70 text-xs font-black uppercase tracking-widest mb-6">
<MaterialIcon icon="storefront" size={14} />
Ready to Grow?
</span>
<h2 className="font-display text-3xl md:text-4xl xl:text-5xl font-black mb-4 tracking-tight leading-tight">
Become a Merchant Partner
</h2>
<p className="text-sm md:text-base text-white/70 mb-8 leading-relaxed max-w-lg mx-auto">
Join neighbourhood stores across Coimbatore already selling online with Nearle. Registration takes under 10 minutes.
</p>
<div className="flex flex-wrap justify-center gap-3.5">
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm hover:shadow-xl transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="bolt" size={18} />
<span>Register Your Store</span>
</Link>
<Link
href="/contact"
className="bg-white/10 hover:bg-white/15 border border-white/20 text-white font-black px-8 py-3.5 rounded-xl text-sm transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon="phone" size={18} />
<span>Talk to Our Team</span>
</Link>
</div>
</div>
</div>
</div>
</section>
<CTABanner
badge={{ icon: "storefront", label: "Ready to Grow?" }}
heading="Become a Merchant Partner"
body="Join neighbourhood stores across Coimbatore already selling online with Nearle. Registration takes under 10 minutes."
primary={{ href: "/contact", icon: "bolt", label: "Register Your Store" }}
secondary={{ href: "/contact", icon: "phone", label: "Talk to Our Team" }}
decorations={[
"circle at 70% 20%, rgba(99,48,214,0.18), transparent 50%",
"circle at 30% 80%, rgba(0,109,55,0.10), transparent 45%",
]}
/>
</PremiumPageShell>
);
}

View File

@@ -3,8 +3,9 @@ import Image from "next/image";
import Link from "next/link";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import { PeopleHero } from "@/components/heroes/PeopleHero";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import type { Metadata } from "next";
export const metadata: Metadata = {
@@ -137,83 +138,15 @@ const APP_BENEFITS = [
}
];
const HERO_STATS = [
{ title: "Verified Local Stores", icon: "store", desc: "Growing local commerce" },
{ title: "2km Neighbourhood", icon: "location_on", desc: "Hyperlocal service radius" },
{ title: "Happy Customers", icon: "mood", desc: "Growing neighbourhood community" },
{ title: "Trusted Platform", icon: "star", desc: "Loved by local shoppers" }
];
export default function PeoplePage() {
const collageCards = [
{ text: "Discover Nearby", icon: "explore" },
{ text: "Save Favourites", icon: "favorite" },
{ text: "Order Anytime", icon: "shopping_bag" }
];
return (
<PremiumPageShell>
{/* ─── Hero Section ─── */}
<section className="w-full flex flex-col pt-6 md:pt-10 select-none bg-gradient-to-br from-white via-[#FEFCFF] to-[#F4ECFA]">
{/* TWO-COLUMN GRID */}
<div className="border-b border-[#6330D6]/10">
<div className="grid grid-cols-1 lg:grid-cols-[45%_55%] items-center gap-10 lg:gap-6 px-6 md:px-12 lg:px-16 pb-12 max-w-[1600px] mx-auto">
{/* Left Column: Copy & Actions */}
<div className="flex flex-col text-left max-w-xl">
<span className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit">
Nearle for People
</span>
<h1 className="font-display text-4xl sm:text-5xl lg:text-[48px] xl:text-[54px] font-black text-[#16001D] leading-[1.1] tracking-tight">
Your Neighbourhood,
<br />
Now in Your <span className="text-[#6330D6]">Pocket.</span>
</h1>
<p className="text-sm md:text-base text-[#5E5866] font-bold mt-6 leading-relaxed">
Discover verified local stores, save your favorite merchants, and order everyday items with superfast local fulfillment.
</p>
<div className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto">
<a
href="#download-section"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
>
Get the App
</a>
<Link
href="/"
className="border-2 border-[#6330D6]/20 hover:border-[#6330D6]/40 text-[#6330D6] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-all duration-200 hover:bg-[#6330D6]/5 active:scale-[0.98]"
>
Explore Stores
</Link>
</div>
</div>
{/* Right Column: Visual Collage */}
<div className="w-full">
<HeroVisualCollage
centerImage="/images/app-mockup.png"
leftImage="/images/cat-hot-food.png"
rightImage="/images/cat-health-wellness.png"
glassCards={collageCards}
isPhoneCenter={true}
/>
</div>
</div>
</div>
{/* BOTTOM HERO STATS */}
<div className="bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 max-w-[1600px] mx-auto">
<FeatureStatsRow items={HERO_STATS} />
</div>
</div>
</section>
<PeopleHero />
{/* ─── Bento Grid of Offerings ─── */}
<section className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-[#FAF7FD] border-b border-[#6330D6]/10">
<AnimatedSection>
<section className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="mb-10 xl:mb-8">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
What we offer
@@ -250,25 +183,28 @@ export default function PeoplePage() {
</article>
))}
</div>
</section>
</section>
</AnimatedSection>
{/* ─── Localized Stats Block ─── */}
<section className="py-10 md:py-14 xl:py-10 px-6 md:px-12 lg:px-16 xl:px-20 2xl:px-24 text-center bg-white border-b border-[#6330D6]/10">
<div className="mb-10 xl:mb-8">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Nearle Network
</span>
<h2 className="font-display text-2xl font-black text-[#16001D] mt-4">
The Neighbourhood App
</h2>
<p className="text-xs md:text-sm text-[#5E5866] mt-2">
Bringing local stores to your doorstep instantly.
</p>
</div>
<AnimatedSection>
<div className="mb-10 xl:mb-8">
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
Nearle Network
</span>
<h2 className="font-display text-2xl font-black text-[#16001D] mt-4">
The Neighbourhood App
</h2>
<p className="text-xs md:text-sm text-[#5E5866] mt-2">
Bringing local stores to your doorstep instantly.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-6">
<AnimatedGrid className="grid grid-cols-2 lg:grid-cols-4 gap-6">
{LOCAL_STATS.map((s) => (
<div
<AnimatedCard
key={s.label}
className="bg-white rounded-2xl p-6 border border-[#6330D6]/10 shadow-[0_4px_16px_rgba(99,48,214,0.01)] flex flex-col items-center text-center group hover:scale-[1.01] transition-transform duration-200"
>
@@ -279,12 +215,13 @@ export default function PeoplePage() {
<p className="text-[10px] font-bold text-[#5E5866] mt-1.5 uppercase tracking-wider">
{s.label}
</p>
</div>
</AnimatedCard>
))}
</div>
</AnimatedGrid>
</section>
{/* ─── Ready to Download Section ─── */}
<AnimatedSection>
<section className="bg-white px-2 pt-2 pb-2 md:px-3 md:pt-3 md:pb-3 lg:px-4 lg:pt-4 lg:pb-4 xl:px-5 xl:pt-4 xl:pb-4 2xl:px-6 2xl:pb-5">
<section id="download-section" className="relative w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#16001D] to-[#25002F] p-8 text-center text-white md:p-10 lg:p-12 xl:p-10">
{/* Subtle glow layers */}
@@ -531,6 +468,7 @@ export default function PeoplePage() {
</div>
</section>
</section>
</AnimatedSection>
</PremiumPageShell>
);
}

View File

@@ -1,7 +1,9 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { fadeUp, scaleIn, staggerContainer, staggerSlow, viewport } from "@/lib/animations";
// ─── Feature SVG Illustrations ───────────────────────────────────────────────
// Flat vector style · Nearle brand palette · no text inside SVGs
@@ -428,46 +430,50 @@ export function BusinessFeaturesGrid() {
>
{/* ── LEFT — clean text feature grid, no illustrations ── */}
<div>
{/* Badge */}
<span className="inline-flex items-center rounded-full bg-[#6330D6]/[0.08] px-3.5 py-1 text-xs font-black uppercase tracking-widest text-[#6330D6]">
Our features
</span>
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
{/* Badge + Heading */}
<motion.div variants={fadeUp}>
<span className="inline-flex items-center rounded-full bg-[#6330D6]/[0.08] px-3.5 py-1 text-xs font-black uppercase tracking-widest text-[#6330D6]">
Our features
</span>
<h2
className="font-display font-black text-[#16001D] mt-4"
style={{
fontSize: "clamp(36px, 3.4vw, 60px)",
lineHeight: 1.08,
letterSpacing: "-0.04em",
maxWidth: 780,
marginBottom: 0,
}}
>
Move Your Business Forward
</h2>
<p
className="text-[#5E5866]"
style={{ fontSize: 18, lineHeight: 1.65, maxWidth: 760, marginTop: 16, marginBottom: 36 }}
>
Be a part of a community of like-minded people. Brand your business on Nearle and reach out to your neighborhood with ease.
</p>
</motion.div>
{/* Heading */}
<h2
className="font-display font-black text-[#16001D] mt-4"
style={{
fontSize: "clamp(36px, 3.4vw, 60px)",
lineHeight: 1.08,
letterSpacing: "-0.04em",
maxWidth: 780,
marginBottom: 0,
}}
>
Move Your Business Forward
</h2>
{/* Description */}
<p
className="text-[#5E5866]"
style={{ fontSize: 18, lineHeight: 1.65, maxWidth: 760, marginTop: 16, marginBottom: 36 }}
>
Be a part of a community of like-minded people. Brand your business on Nearle and reach out to your neighborhood with ease.
</p>
{/* Feature list check + text only, no illustration columns */}
<div
{/* Feature list */}
<motion.div
className="grid grid-cols-1 md:grid-cols-2"
style={{ columnGap: 48, rowGap: 32 }}
variants={staggerSlow}
>
{FEATURES.map((feat) => (
<div
<motion.div
key={feat.title}
variants={fadeUp}
className="grid items-start"
style={{ gridTemplateColumns: "30px 1fr", columnGap: 16 }}
>
{/* Green check icon */}
<div
className="rounded-full bg-[#DCFCE7] flex items-center justify-center shrink-0"
style={{ width: 30, height: 30, marginTop: 3 }}
@@ -476,8 +482,6 @@ export function BusinessFeaturesGrid() {
<path d="M3.5 8.5L6.5 11.5L12.5 5" stroke="#16A34A" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
{/* Title + description */}
<div className="min-w-0">
<h4
className="font-display font-black text-[#16001D]"
@@ -489,30 +493,33 @@ export function BusinessFeaturesGrid() {
{feat.descr}
</p>
</div>
</div>
</motion.div>
))}
</div>
</div>
</motion.div>
</motion.div>
{/* ── RIGHT — 4 cards + illustration strip below ─────── */}
<div className="self-start flex flex-col" style={{ gap: 28 }}>
{/* 2×2 feature benefit cards */}
{/* ── RIGHT — 2×2 feature benefit cards ─────── */}
<motion.div
className="self-start flex flex-col"
style={{ gap: 28 }}
variants={staggerSlow}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
<div className="grid grid-cols-2" style={{ gap: 28 }}>
{CARDS.map((card) => {
const { CardIllus } = card;
return (
<div
<motion.div
key={card.title}
variants={scaleIn}
className={`relative flex flex-col overflow-hidden border border-[#6330D6]/10 shadow-[0_4px_28px_rgba(99,48,214,0.09)] ${card.bg}`}
style={{ borderRadius: 34, padding: 28, minHeight: 330 }}
>
{/* Top icon */}
<div className={`w-9 h-9 rounded-full flex items-center justify-center shrink-0 ${card.iconBg}`}>
<MaterialIcon icon={card.iconName} size={18} />
</div>
{/* Illustration */}
<div
className="flex-1 flex items-center justify-center overflow-hidden"
style={{ margin: "8px 0" }}
@@ -521,8 +528,6 @@ export function BusinessFeaturesGrid() {
<CardIllus />
</div>
</div>
{/* Bottom label */}
<div className="mt-auto shrink-0">
<p
className={`font-display font-black leading-none tracking-tight ${card.titleColor}`}
@@ -537,13 +542,11 @@ export function BusinessFeaturesGrid() {
{card.subtitle}
</p>
</div>
</div>
</motion.div>
);
})}
</div>
</div>
</motion.div>
</div>
</div>

View File

@@ -1,8 +1,27 @@
"use client";
import React, { useState } from "react";
import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
// Stagger form contents after the card slides in from right (card enters at delay 0.25, duration 0.65 → visible ~t=0.7s)
const formContentStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.1, delayChildren: 0.75 } },
};
const fieldVariant = {
hidden: { opacity: 0, y: 14 },
visible: { opacity: 1, y: 0, transition: { duration: 0.45, ease: EASE } },
};
const submitVariant = {
hidden: { opacity: 0, y: 10, scale: 0.98 },
visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.4, ease: EASE } },
};
type FormData = {
name: string;
email: string;
@@ -100,16 +119,21 @@ export function ContactForm() {
<div className="absolute top-0 right-0 w-32 h-32 bg-primary/5 rounded-full blur-3xl" />
<div className="absolute bottom-0 left-0 w-32 h-32 bg-secondary/5 rounded-full blur-2xl" />
<div className="relative z-10">
<span className="text-label-sm font-black text-primary uppercase tracking-widest">
<motion.div
className="relative z-10"
variants={formContentStagger}
initial="hidden"
animate="visible"
>
<motion.span variants={fieldVariant} className="text-label-sm font-black text-primary uppercase tracking-widest block">
Write to us
</span>
<h2 className="font-display text-2xl md:text-3xl font-black text-brand-dark mt-2 mb-8">
</motion.span>
<motion.h2 variants={fieldVariant} className="font-display text-2xl md:text-3xl font-black text-brand-dark mt-2 mb-8">
Send Message
</h2>
</motion.h2>
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<motion.div variants={fieldVariant} className="grid grid-cols-1 md:grid-cols-2 gap-5">
{/* Name field */}
<div className="flex flex-col">
<label htmlFor="field-name" className="text-label-sm text-on-surface-variant font-bold mb-2">
@@ -171,10 +195,10 @@ export function ContactForm() {
</span>
)}
</div>
</div>
</motion.div>
{/* Message field */}
<div className="flex flex-col">
<motion.div variants={fieldVariant} className="flex flex-col">
<label htmlFor="field-message" className="text-label-sm text-on-surface-variant font-bold mb-2">
Message
</label>
@@ -202,28 +226,30 @@ export function ContactForm() {
{errors.message}
</span>
)}
</div>
</motion.div>
{/* Submit button */}
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-[#6330D6] hover:bg-[#5225b8] disabled:bg-[#6330D6]/60 text-on-primary font-black px-8 py-4 rounded-xl text-label-md hover:shadow-lg transition-all duration-300 hover:-translate-y-0.5 active:translate-y-0 active:scale-[0.98] flex items-center justify-center gap-2 cursor-pointer"
>
{isSubmitting ? (
<>
<span className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
<span>Sending Message...</span>
</>
) : (
<>
<MaterialIcon icon="send" size={18} />
<span>Submit Message</span>
</>
)}
</button>
{/* Submit button — appears last */}
<motion.div variants={submitVariant}>
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-[#6330D6] hover:bg-[#5225b8] disabled:bg-[#6330D6]/60 text-on-primary font-black px-8 py-4 rounded-xl text-label-md hover:shadow-lg transition-all duration-300 hover:-translate-y-0.5 active:translate-y-0 active:scale-[0.98] flex items-center justify-center gap-2 cursor-pointer"
>
{isSubmitting ? (
<>
<span className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
<span>Sending Message...</span>
</>
) : (
<>
<MaterialIcon icon="send" size={18} />
<span>Submit Message</span>
</>
)}
</button>
</motion.div>
</form>
</div>
</motion.div>
</div>
{/* ─── Success Toast Notification ─── */}

View File

@@ -0,0 +1,71 @@
"use client";
import { motion } from "framer-motion";
type TimelineItem = {
step: string;
title: string;
desc: string;
time: string;
};
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const containerVariant = {
hidden: {},
visible: { transition: { staggerChildren: 0.1, delayChildren: 0.15 } },
};
const itemVariant = {
hidden: { opacity: 0, x: -24 },
visible: { opacity: 1, x: 0, transition: { duration: 0.55, ease: EASE } },
};
const connectorVariant = {
hidden: { scaleY: 0 },
visible: { scaleY: 1, transition: { duration: 0.9, ease: EASE } },
};
export function AnimatedTimeline({ items }: { items: TimelineItem[] }) {
return (
<div className="relative max-w-3xl mx-auto">
{/* Vertical connector line — animated scaleY from origin-top */}
<motion.div
className="absolute left-[22px] md:left-[26px] top-8 bottom-8 w-0.5 bg-gradient-to-b from-[#6330D6]/30 via-[#6330D6]/20 to-transparent hidden sm:block origin-top"
variants={connectorVariant}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.1 }}
/>
<motion.div
className="space-y-6"
variants={containerVariant}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.1 }}
>
{items.map((item, index) => (
<motion.div
key={item.step}
className="relative flex gap-5 sm:gap-7"
variants={itemVariant}
>
<div className="flex-shrink-0 w-11 h-11 md:w-12 md:h-12 rounded-full bg-[#6330D6] text-white flex items-center justify-center text-xs font-black shadow-[0_8px_20px_rgba(99,48,214,0.25)] relative z-10">
{String(index + 1).padStart(2, "0")}
</div>
<div className="flex-1 pb-2 pt-1">
<div className="flex items-center gap-3 mb-1.5">
<h3 className="text-base font-black text-[#16001D] leading-tight">{item.title}</h3>
<span className="px-2.5 py-0.5 rounded-full bg-[#6330D6]/8 text-[#6330D6] text-[10px] font-black uppercase tracking-wide shrink-0">
{item.time}
</span>
</div>
<p className="text-sm text-[#5E5866] leading-relaxed">{item.desc}</p>
</div>
</motion.div>
))}
</motion.div>
</div>
);
}

View File

@@ -0,0 +1,150 @@
"use client";
import Link from "next/link";
import { motion } from "framer-motion";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import type { Variants } from "framer-motion";
import {
fadeInSlow,
fadeUp,
staggerSlow,
viewport,
} from "@/lib/animations";
const HERO_STATS = [
{ title: "2km Coverage", icon: "explore", desc: "Hyperlocal radius" },
{ title: "Verified Local Stores", icon: "store", desc: "Growing local commerce" },
{ title: "Happy Customers", icon: "mood", desc: "Neighbourhood community" },
{ title: "Coimbatore & Beyond", icon: "location_city", desc: "Growing neighbourhood reach" },
];
const COLLAGE_CARDS = [
{ text: "Local First", icon: "store" },
{ text: "Trusted Network", icon: "verified" },
{ text: "Connected Communities", icon: "groups" },
];
// Choreography: deliberate, slow-stagger sequence — thoughtful and purposeful
// badge fades in (opacity only) → h1 sweeps from left → p fades up → CTAs fade up
// The collage scales up softly, reinforcing the "built carefully" feeling
const badgeVariant = fadeInSlow;
const titleVariant = {
hidden: { opacity: 0, x: -30 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
const descVariant = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
const ctaVariant = fadeUp;
const collageVariant = {
hidden: { opacity: 0, scale: 0.92 },
visible: {
opacity: 1,
scale: 1,
transition: { duration: 0.95, ease: [0.22, 1, 0.36, 1] as [number, number, number, number], delay: 0.2 },
},
};
// Trust-feeling card variant: no spring bounce, deliberate easing
const trustCardVariant: Variants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
export function AboutHero() {
return (
<section className="w-full flex flex-col pt-6 md:pt-10 select-none bg-gradient-to-br from-white via-[#FEFCFF] to-[#F4ECFA]">
<div className="border-b border-[#6330D6]/10">
<div className="grid grid-cols-1 lg:grid-cols-[45%_55%] items-center gap-10 lg:gap-6 px-6 md:px-12 lg:px-16 pb-12 max-w-[1600px] mx-auto">
{/* Left Column: slow, deliberate stagger — thoughtful pacing */}
<motion.div
className="flex flex-col text-left max-w-xl"
variants={staggerSlow}
initial="hidden"
animate="visible"
>
<motion.span
variants={badgeVariant}
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit"
>
About Nearle
</motion.span>
<motion.h1
variants={titleVariant}
className="font-display text-4xl sm:text-5xl lg:text-[48px] xl:text-[54px] font-black text-[#16001D] leading-[1.1] tracking-tight"
>
Built for Every
<br />
<span className="text-[#6330D6]">Neighbourhood.</span>
</motion.h1>
<motion.p
variants={descVariant}
className="text-sm md:text-base text-[#5E5866] font-bold mt-6 leading-relaxed"
>
Nearle is India&apos;s first hyper-local managed &ldquo;Neighbourhood living platform&rdquo; which brings all the amenities and requirements of residents to their fingertips.
</motion.p>
<motion.div
variants={ctaVariant}
className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto"
>
<a
href="#story-section"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
>
Our Story
</a>
<Link
href="/contact"
className="border-2 border-[#6330D6]/20 hover:border-[#6330D6]/40 text-[#6330D6] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-all duration-200 hover:bg-[#6330D6]/5 active:scale-[0.98]"
>
Join Our Journey
</Link>
</motion.div>
</motion.div>
{/* Right Column: soft scale-up — gentle and community-first */}
<motion.div
className="w-full"
variants={collageVariant}
initial="hidden"
animate="visible"
>
<HeroVisualCollage
centerImage="/nearlenewlogo.png"
leftImage="/Nearle Business.png"
rightImage="/nearle people.png"
glassCards={COLLAGE_CARDS}
isPhoneCenter={false}
cardVariant={trustCardVariant}
/>
</motion.div>
</div>
</div>
<div className="bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 max-w-[1600px] mx-auto">
<FeatureStatsRow items={HERO_STATS} />
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,123 @@
"use client";
import Link from "next/link";
import { motion } from "framer-motion";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import {
slideInLeftSubtle,
fadeUp,
fadeIn,
scaleIn,
staggerContainer,
viewport,
} from "@/lib/animations";
const HERO_BENEFITS = [
{ title: "More Visibility Across Nearle", icon: "visibility", desc: "Get discovered by locals" },
{ title: "Easy Order Management", icon: "assignment", desc: "Track and fulfill cleanly" },
{ title: "Grow Repeat Customers", icon: "trending_up", desc: "Build neighborhood loyalty" },
{ title: "Local Delivery Support", icon: "local_shipping", desc: "Deliver fast & verified" },
];
const COLLAGE_CARDS = [
{ text: "Nearby Customers", icon: "people" },
{ text: "Orders & Offers", icon: "local_offer" },
{ text: "Local Delivery Support", icon: "local_shipping" },
];
// Choreography: badge slides left → title fades up → description fades in (opacity only) → CTAs scale in
const badgeVariant = slideInLeftSubtle;
const titleVariant = {
hidden: { opacity: 0, y: 28 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.55, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
const descVariant = fadeIn;
const ctaVariant = scaleIn;
export function BusinessHero() {
return (
<section className="w-full flex flex-col pt-6 md:pt-10 select-none bg-gradient-to-br from-white via-[#FEFCFF] to-[#F4ECFA]">
<div className="border-b border-[#6330D6]/10">
<div className="grid grid-cols-1 lg:grid-cols-[45%_55%] items-center gap-10 lg:gap-6 px-6 md:px-12 lg:px-16 pb-12 max-w-[1600px] mx-auto">
{/* Left Column: animated with unique stagger */}
<motion.div
className="flex flex-col text-left max-w-xl"
variants={staggerContainer}
initial="hidden"
animate="visible"
>
<motion.span
variants={badgeVariant}
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit"
>
Nearle for Business
</motion.span>
<motion.h1
variants={titleVariant}
className="font-display text-4xl sm:text-5xl lg:text-[48px] xl:text-[54px] font-black text-[#16001D] leading-[1.1] tracking-tight"
>
Grow Your Local Business,
<br />
<span className="text-[#6330D6]">Neighbourhood by Neighbourhood.</span>
</motion.h1>
<motion.p
variants={descVariant}
className="text-sm md:text-base text-[#5E5866] font-bold mt-6 leading-relaxed"
>
Get discovered by nearby customers, manage orders, and build repeat business from one simple platform.
</motion.p>
<motion.div
variants={ctaVariant}
className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto"
>
<Link
href="/contact"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
>
List Your Store
</Link>
<Link
href="/contact"
className="border-2 border-[#6330D6]/20 hover:border-[#6330D6]/40 text-[#6330D6] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-all duration-200 hover:bg-[#6330D6]/5 active:scale-[0.98]"
>
Explore Solutions
</Link>
</motion.div>
</motion.div>
{/* Right Column: subtle fade-in — HeroVisualCollage internal animations choreograph each element */}
<motion.div
className="w-full"
variants={fadeIn}
initial="hidden"
animate="visible"
transition={{ delay: 0.2 }}
>
<HeroVisualCollage
centerImage="/nearlenewlogo.png"
leftImage="/images/cat-daily-essentials.png"
rightImage="/Nearle Business.png"
glassCards={COLLAGE_CARDS}
isPhoneCenter={false}
/>
</motion.div>
</div>
</div>
<div className="bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 max-w-[1600px] mx-auto">
<FeatureStatsRow items={HERO_BENEFITS} />
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,139 @@
"use client";
import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { ContactForm } from "@/components/contact/ContactForm";
import { fadeUp, fadeUpStrong, slideInRight, staggerContainer, viewport } from "@/lib/animations";
const CONTACT_INFO = [
{
icon: "phone",
label: "Call Us",
value: "+91 909 206 8666",
},
{
icon: "mail",
label: "Email Us",
value: "nearle@care.in",
},
{
icon: "location_on",
label: "Address",
value: "424, DB Rd, R.S. Puram, Coimbatore, TN 641002",
},
];
// Choreography: professional, clear sequence
// Left column — each info row staggers in from left; h1 has strong upward entry
// Form card slides in from right with slight delay
// Info rows use a tighter stagger to feel organised and systematic
const infoRowVariant = {
hidden: { opacity: 0, x: -18 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.45, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
const formVariant = {
hidden: { opacity: 0, x: 40 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.65, ease: [0.22, 1, 0.36, 1] as [number, number, number, number], delay: 0.25 },
},
};
const infoStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.08 } },
};
export function ContactHero() {
return (
<section className="relative border-b border-[#6330D6]/10 px-4 py-10 md:px-6 md:py-12 lg:px-8 lg:py-14 xl:px-10 xl:py-16 2xl:px-12 2xl:py-20">
{/* Decorative map grid — not animated so it stays subtle */}
<div className="absolute left-[5%] top-[10%] w-[400px] h-[300px] opacity-[0.04] select-none pointer-events-none -z-10">
<svg className="w-full h-full text-[#6330D6]" stroke="currentColor" fill="none" strokeWidth="1.5" viewBox="0 0 400 300">
<path d="M0,50 L400,50 M0,130 L400,130 M0,210 L400,210 M80,0 L80,300 M200,0 L200,300 M320,0 L320,300" />
<path d="M 80,130 C 120,90 160,170 200,130 C 240,90 280,170 320,130" strokeDasharray="3,3" />
<circle cx="200" cy="130" r="4" fill="currentColor" />
<circle cx="200" cy="130" r="14" strokeWidth="1" />
<circle cx="80" cy="50" r="3" fill="currentColor" />
<circle cx="320" cy="210" r="3" fill="currentColor" />
</svg>
</div>
<div className="relative z-10 mx-auto w-full max-w-[1520px]">
<div className="grid grid-cols-1 items-start gap-10 lg:grid-cols-[1.2fr_0.8fr] lg:gap-12 xl:gap-16">
{/* Left Column — Contact Info: sequential stagger from top */}
<motion.div
className="w-full max-w-[760px] py-6 md:py-8 lg:py-10 relative z-10 text-[#16001D]"
variants={staggerContainer}
initial="hidden"
animate="visible"
>
<motion.span
variants={fadeUp}
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit"
>
Contact Us
</motion.span>
<motion.h1
variants={fadeUpStrong}
className="font-display text-4xl font-black leading-[1.05] text-[#16001D] md:text-5xl lg:text-[58px] xl:text-[64px] 2xl:text-[70px]"
>
We&apos;re Here to <br />
<span className="text-[#6330D6]">Help You.</span>
</motion.h1>
<motion.p
variants={fadeUp}
className="mt-6 max-w-[620px] text-sm font-bold leading-8 text-[#5E5866] md:text-base xl:text-lg"
>
Whether you are a customer, local store owner, or delivery partner, our team is here to help you get started with Nearle.
</motion.p>
{/* Info rows: each row staggers in from left */}
<motion.div
className="mt-8 border-t border-[#6330D6]/10"
variants={infoStagger}
initial="hidden"
animate="visible"
>
{CONTACT_INFO.map((item) => (
<motion.div
key={item.label}
variants={infoRowVariant}
className="flex gap-4 py-4.5 border-b border-[#6330D6]/10 items-center"
>
<div className="w-10 h-10 rounded-full bg-[#6330D6]/10 text-[#6330D6] flex items-center justify-center shrink-0">
<MaterialIcon icon={item.icon} size={18} />
</div>
<div className="text-left">
<h4 className="text-[#16001D] text-sm font-black">{item.label}</h4>
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">{item.value}</p>
</div>
</motion.div>
))}
</motion.div>
</motion.div>
{/* Right Column: form slides in from right */}
<motion.div
className="w-full max-w-[660px] justify-self-end pt-10 lg:pt-14 xl:pt-26"
variants={formVariant}
initial="hidden"
animate="visible"
>
<ContactForm />
</motion.div>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,117 @@
"use client";
import Link from "next/link";
import { motion } from "framer-motion";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import {
scaleInSpring,
fadeUpStrong,
fadeUp,
slideInLeftSubtle,
fadeIn,
staggerFast,
} from "@/lib/animations";
const HERO_STATS = [
{ title: "Verified Local Stores", icon: "store", desc: "Growing local commerce" },
{ title: "2km Neighbourhood", icon: "location_on", desc: "Hyperlocal service radius" },
{ title: "Happy Customers", icon: "mood", desc: "Growing neighbourhood community" },
{ title: "Trusted Platform", icon: "star", desc: "Loved by local shoppers" },
];
const COLLAGE_CARDS = [
{ text: "Discover Nearby", icon: "explore" },
{ text: "Save Favourites", icon: "favorite" },
{ text: "Order Anytime", icon: "shopping_bag" },
];
// Choreography: badge bounces in (spring) → h1 strong fade-up → p subtle up → CTAs slide from left
// Fast stagger (0.07) gives a snappy, energetic feel — phone/fast/mobile-first vibe
const badgeVariant = scaleInSpring;
const titleVariant = fadeUpStrong;
const descVariant = fadeUp;
const ctaVariant = slideInLeftSubtle;
export function PeopleHero() {
return (
<section className="w-full flex flex-col pt-6 md:pt-10 select-none bg-gradient-to-br from-white via-[#FEFCFF] to-[#F4ECFA]">
<div className="border-b border-[#6330D6]/10">
<div className="grid grid-cols-1 lg:grid-cols-[45%_55%] items-center gap-10 lg:gap-6 px-6 md:px-12 lg:px-16 pb-12 max-w-[1600px] mx-auto">
{/* Left Column: snappy fast-stagger sequence */}
<motion.div
className="flex flex-col text-left max-w-xl"
variants={staggerFast}
initial="hidden"
animate="visible"
>
<motion.span
variants={badgeVariant}
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6330D6]/10 text-[#6330D6] uppercase mb-6 w-fit"
>
Nearle for People
</motion.span>
<motion.h1
variants={titleVariant}
className="font-display text-4xl sm:text-5xl lg:text-[48px] xl:text-[54px] font-black text-[#16001D] leading-[1.1] tracking-tight"
>
Your Neighbourhood,
<br />
Now in Your <span className="text-[#6330D6]">Pocket.</span>
</motion.h1>
<motion.p
variants={descVariant}
className="text-sm md:text-base text-[#5E5866] font-bold mt-6 leading-relaxed"
>
Discover verified local stores, save your favorite merchants, and order everyday items with superfast local fulfillment.
</motion.p>
<motion.div
variants={ctaVariant}
className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto"
>
<a
href="#download-section"
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
>
Get the App
</a>
<Link
href="/"
className="border-2 border-[#6330D6]/20 hover:border-[#6330D6]/40 text-[#6330D6] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-all duration-200 hover:bg-[#6330D6]/5 active:scale-[0.98]"
>
Explore Stores
</Link>
</motion.div>
</motion.div>
{/* Right Column: subtle fade — HeroVisualCollage's internal animations handle phone, images, badges individually */}
<motion.div
className="w-full"
variants={fadeIn}
initial="hidden"
animate="visible"
transition={{ delay: 0.15 }}
>
<HeroVisualCollage
centerImage="/images/app-mockup.png"
leftImage="/images/cat-hot-food.png"
rightImage="/images/cat-health-wellness.png"
glassCards={COLLAGE_CARDS}
isPhoneCenter={true}
/>
</motion.div>
</div>
</div>
<div className="bg-[#FAF7FD] border-b border-[#6330D6]/10">
<div className="px-6 md:px-12 lg:px-16 max-w-[1600px] mx-auto">
<FeatureStatsRow items={HERO_STATS} />
</div>
</div>
</section>
);
}

View File

@@ -69,8 +69,16 @@ export function BusinessSection() {
<section className="section bg-white w-full overflow-hidden">
<div className="mx-auto w-full max-w-[2200px] px-2 md:px-4 lg:px-5 xl:px-6">
<div className="relative overflow-hidden rounded-[2rem] bg-[linear-gradient(135deg,#ffffff_0%,#fbf7ff_50%,#ede4f5_100%)] border border-primary/10 shadow-[0_24px_70px_rgba(22,0,29,0.08)] p-5 md:p-8 lg:p-10">
<div className="pointer-events-none absolute -left-16 top-24 h-64 w-64 rounded-full bg-primary/5 blur-3xl" />
<div className="pointer-events-none absolute -right-20 bottom-10 h-64 w-64 rounded-full bg-secondary/5 blur-3xl" />
<motion.div
className="pointer-events-none absolute -left-16 top-24 h-64 w-64 rounded-full bg-primary/5 blur-3xl"
animate={{ y: [0, -16, 0] }}
transition={{ duration: 13, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.div
className="pointer-events-none absolute -right-20 bottom-10 h-64 w-64 rounded-full bg-secondary/5 blur-3xl"
animate={{ y: [0, 14, 0] }}
transition={{ duration: 11, repeat: Infinity, ease: "easeInOut" }}
/>
<div className="relative grid gap-6 grid-cols-1 md:gap-7
xl:grid-cols-[420px_1fr] xl:gap-8 xl:items-start">

View File

@@ -2,14 +2,11 @@
import Link from "next/link";
import Image from "next/image";
import { motion } from "framer-motion";
import { motion, useReducedMotion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import {
fadeUp,
slideInRight,
staggerContainer,
viewport,
} from "@/lib/animations";
import { viewport } from "@/lib/animations";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const PROOF_ITEMS = [
{ label: "Verified", text: "Local Stores", icon: "store" },
@@ -18,16 +15,86 @@ const PROOF_ITEMS = [
{ label: "Trusted", text: "Local Platform", icon: "star" },
];
// Left column: badge → line1 mask-reveal → line2 mask-reveal → desc → primary CTA → secondary CTA
const heroStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.12, delayChildren: 0.05 } },
};
const badgeVariant = {
hidden: { opacity: 0, y: 10, scale: 0.95 },
visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.38, ease: EASE } },
};
// Lines stagger internally
const lineStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.09 } },
};
// True mask reveal: text slides up from below a clipping parent
const lineReveal = {
hidden: { y: "110%" },
visible: { y: 0, transition: { duration: 0.72, ease: EASE } },
};
const descVariant = {
hidden: { opacity: 0, y: 18 },
visible: { opacity: 1, y: 0, transition: { duration: 0.55, ease: EASE } },
};
const primaryCtaVariant = {
hidden: { opacity: 0, y: 14, scale: 0.97 },
visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.45, ease: EASE } },
};
const secondaryCtaVariant = {
hidden: { opacity: 0, x: -14 },
visible: { opacity: 1, x: 0, transition: { duration: 0.4, ease: EASE } },
};
// Right column: each element enters independently
const imgFromLeft = {
hidden: { opacity: 0, x: -36 },
visible: { opacity: 1, x: 0, transition: { duration: 0.65, ease: EASE } },
};
const centerCardReveal = {
hidden: { opacity: 0, scale: 0.86, y: 24 },
visible: { opacity: 1, scale: 1, y: 0, transition: { duration: 0.75, ease: EASE } },
};
const imgFromRight = {
hidden: { opacity: 0, x: 36 },
visible: { opacity: 1, x: 0, transition: { duration: 0.65, ease: EASE } },
};
const statStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.08 } },
};
const statItem = {
hidden: { opacity: 0, y: 14 },
visible: { opacity: 1, y: 0, transition: { duration: 0.45, ease: EASE } },
};
export function Hero() {
const reducedMotion = useReducedMotion();
return (
<section className="relative overflow-hidden bg-gradient-to-b from-[#FAF8FC] to-[#FFFFFF] select-none w-full">
{/* ─── DECORATIONS (BEHIND CONTENT) ─── */}
{/* 1. Large pale lavender partial circle (top-left) */}
<div className="hidden md:block absolute top-[-100px] left-[-100px] w-[350px] h-[350px] rounded-full border-[1.5px] border-[#6D28D9]/15 bg-[#6D28D9]/5 opacity-[0.55] z-0 pointer-events-none" />
<motion.div
className="hidden md:block absolute top-[-100px] left-[-100px] w-[350px] h-[350px] rounded-full border-[1.5px] border-[#6D28D9]/15 bg-[#6D28D9]/5 opacity-[0.55] z-0 pointer-events-none"
animate={{ y: [0, -14, 0] }}
transition={{ duration: 14, repeat: Infinity, ease: "easeInOut" }}
/>
{/* 2. Purple solid circle (center-right around collage) */}
<div className="hidden lg:block absolute right-[22%] top-[38%] w-[84px] h-[84px] rounded-full bg-[#6D28D9] z-0 shadow-[0_8px_32px_rgba(109,40,217,0.3)] pointer-events-none animate-pulse" />
<div className="hidden lg:block absolute right-[22%] top-[38%] w-[84px] h-[84px] rounded-full bg-[#6D28D9] z-0 shadow-[0_8px_32px_rgba(109,40,217,0.3)] pointer-events-none motion-safe:animate-pulse" />
{/* 3. Striped purple circle (lower-middle collage area) */}
<div
@@ -53,7 +120,11 @@ export function Hero() {
<div className="absolute bottom-[14%] right-[5%] text-[#6D28D9]/25 font-bold text-xl pointer-events-none select-none z-0"></div>
{/* 6. Large partial purple circle (bottom-right corner) */}
<div className="absolute bottom-[-100px] right-[-100px] w-[200px] h-[200px] rounded-full bg-[#7C3AED]/80 z-0 pointer-events-none" />
<motion.div
className="absolute bottom-[-100px] right-[-100px] w-[200px] h-[200px] rounded-full bg-[#7C3AED]/80 z-0 pointer-events-none"
animate={{ y: [0, 12, 0] }}
transition={{ duration: 12, repeat: Infinity, ease: "easeInOut" }}
/>
{/* ─── TWO-COLUMN CONTENT GRID ─── */}
<div className="page-container relative grid min-h-[650px] grid-cols-1 lg:grid-cols-[0.92fr_1.08fr] items-center py-16 md:py-20 lg:py-24 z-10 gap-12 lg:gap-6">
@@ -61,64 +132,84 @@ export function Hero() {
{/* LEFT COLUMN: Editorial Typography & Copy */}
<motion.div
className="flex flex-col text-left max-w-xl z-20 relative"
variants={staggerContainer}
variants={heroStagger}
initial="hidden"
animate="visible"
>
{/* Badge */}
<motion.span
variants={fadeUp}
variants={badgeVariant}
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-black tracking-widest bg-[#6D28D9]/10 text-[#6D28D9] uppercase mb-6 w-fit"
>
Welcome to Nearle
</motion.span>
<motion.h1
variants={fadeUp}
className="font-display text-4xl sm:text-5xl lg:text-[68px] xl:text-[76px] font-black text-[#16001D] leading-[0.95] tracking-[-0.05em]"
>
Everything Local.
<br />
Everything <span className="text-[#6D28D9]">Near.</span>
</motion.h1>
{/* H1: line-by-line mask reveal */}
<motion.div variants={lineStagger} className="font-display text-4xl sm:text-5xl lg:text-[68px] xl:text-[76px] font-black text-[#16001D] tracking-[-0.05em]">
<div className="overflow-hidden pb-1">
<motion.div variants={lineReveal} className="leading-[0.95]">
Everything Local.
</motion.div>
</div>
<div className="overflow-hidden pb-1">
<motion.div variants={lineReveal} className="leading-[0.95]">
Everything <span className="text-[#6D28D9]">Near.</span>
</motion.div>
</div>
</motion.div>
{/* Description */}
<motion.p
variants={fadeUp}
variants={descVariant}
className="max-w-[480px] text-base md:text-lg text-[#6D6172] leading-relaxed font-bold mt-6"
>
Discover verified local stores, exclusive offers, fast delivery, and a better way to shop within your neighbourhood.
</motion.p>
<motion.div
variants={fadeUp}
className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto"
>
<Link
href="/people"
className="bg-[#6D28D9] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
{/* CTAs: primary scales in, secondary slides from left */}
<div className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto">
<motion.div
variants={primaryCtaVariant}
whileHover={{ scale: 1.03, transition: { duration: 0.2, ease: EASE } }}
whileTap={{ scale: 0.97, transition: { duration: 0.1 } }}
>
Explore Stores
</Link>
<Link
href="/people#download-section"
className="border-2 border-[#6D28D9]/20 hover:border-[#6D28D9]/40 text-[#6D28D9] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-all duration-200 hover:bg-[#6D28D9]/5 active:scale-[0.98]"
<Link
href="/people"
className="bg-[#6D28D9] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-colors duration-200 block"
>
Explore Stores
</Link>
</motion.div>
<motion.div
variants={secondaryCtaVariant}
whileHover={{ scale: 1.03, transition: { duration: 0.2, ease: EASE } }}
whileTap={{ scale: 0.97, transition: { duration: 0.1 } }}
>
Get the App
</Link>
</motion.div>
<Link
href="/people#download-section"
className="border-2 border-[#6D28D9]/20 hover:border-[#6D28D9]/40 text-[#6D28D9] font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center transition-colors duration-200 hover:bg-[#6D28D9]/5 block"
>
Get the App
</Link>
</motion.div>
</div>
</motion.div>
{/* RIGHT COLUMN: Creative Overlapping Strips Collage */}
<motion.div
className="w-full z-20"
variants={slideInRight}
initial="hidden"
animate="visible"
>
{/* RIGHT COLUMN: Individual image + center card animations */}
<div className="w-full z-20">
{/* DESKTOP LAYOUT (lg and above) */}
<div className="hidden lg:block relative h-[500px] lg:h-[560px] w-full">
{/* left image: absolute left-[10%] top-[20%] w-[170px] h-[360px] */}
<div className="absolute left-[10%] top-[20%] w-[170px] h-[360px] rounded-[1.5rem] overflow-hidden shadow-lg border-2 border-white z-10 transition-transform duration-300 hover:scale-[1.02]">
{/* Left image: slides in from left */}
<motion.div
className="absolute left-[10%] top-[20%] w-[170px] h-[360px] rounded-[1.5rem] overflow-hidden shadow-lg border-2 border-white z-10"
variants={imgFromLeft}
initial="hidden"
animate="visible"
transition={{ delay: 0.3 }}
whileHover={{ scale: 1.02, transition: { duration: 0.25, ease: EASE } }}
>
<Image
src="/images/cat-daily-essentials.png"
alt="Local grocery products"
@@ -126,37 +217,63 @@ export function Hero() {
className="object-cover"
sizes="170px"
/>
</div>
</motion.div>
{/* middle image: central logo panel absolute left-[38%] top-[8%] */}
<div className="absolute left-[38%] top-[8%] z-20 transition-transform duration-300 hover:scale-[1.02]">
<div className="relative w-[180px] sm:w-[220px] h-[280px] sm:h-[340px] rounded-[24px] border-[5px] sm:border-[6px] border-white/95 shadow-[0_20px_40px_rgba(99,48,214,0.1)] bg-[#FAF8FC] overflow-hidden flex items-center justify-center p-6">
<Image
src="/nearlenewlogo.png"
alt="Central logo panel"
width={160}
height={160}
className="object-contain max-h-[140px] w-auto animate-[pulse_6s_infinite]"
priority
/>
</div>
</div>
{/* Center card: scales up from below, then gently bobs */}
<motion.div
className="absolute left-[38%] top-[8%] z-20"
variants={centerCardReveal}
initial="hidden"
animate="visible"
transition={{ delay: 0.15 }}
>
<motion.div
animate={!reducedMotion ? { y: [0, -10, 0] } : {}}
transition={{ delay: 0.9, duration: 4, repeat: Infinity, ease: "easeInOut" }}
whileHover={{ scale: 1.02, transition: { duration: 0.25, ease: EASE } }}
>
<div className="relative w-[180px] sm:w-[220px] h-[280px] sm:h-[340px] rounded-[24px] border-[5px] sm:border-[6px] border-white/95 shadow-[0_20px_40px_rgba(99,48,214,0.1)] bg-[#FAF8FC] overflow-hidden flex items-center justify-center p-6">
<Image
src="/nearlenewlogo.png"
alt="Central logo panel"
width={160}
height={160}
className="object-contain max-h-[140px] w-auto motion-safe:animate-[pulse_6s_infinite]"
priority
/>
</div>
</motion.div>
</motion.div>
{/* right image: absolute right-[2%] top-[18%] w-[180px] h-[390px] */}
<div className="absolute right-[2%] top-[18%] w-[180px] h-[390px] rounded-[1.5rem] overflow-hidden shadow-lg border-2 border-white z-10 transition-transform duration-300 hover:scale-[1.02]">
{/* Right image: slides in from right */}
<motion.div
className="absolute right-[2%] top-[18%] w-[180px] h-[390px] rounded-[1.5rem] overflow-hidden shadow-lg border-2 border-white z-10"
variants={imgFromRight}
initial="hidden"
animate="visible"
transition={{ delay: 0.42 }}
whileHover={{ scale: 1.02, transition: { duration: 0.25, ease: EASE } }}
>
<Image
src="/nearle people.png"
alt="Local customer shopping"
fill
priority
className="object-cover"
sizes="180px"
/>
</div>
</motion.div>
</div>
{/* MOBILE/TABLET COLLAGE (below lg) */}
<div className="lg:hidden flex items-center justify-center gap-2 mt-2 w-full relative h-[280px]">
<div className="relative w-[95px] h-[200px] rounded-2xl overflow-hidden border border-white shadow-md z-10">
<motion.div
className="relative w-[95px] h-[200px] rounded-2xl overflow-hidden border border-white shadow-md z-10"
variants={imgFromLeft}
initial="hidden"
animate="visible"
transition={{ delay: 0.28 }}
>
<Image
src="/images/cat-daily-essentials.png"
alt="Grocery products"
@@ -164,18 +281,37 @@ export function Hero() {
className="object-cover"
sizes="95px"
/>
</div>
<div className="relative w-[115px] h-[240px] rounded-2xl border-4 border-white shadow-lg bg-[#FAF8FC] overflow-hidden flex items-center justify-center p-3 -mx-5 z-20">
<Image
src="/nearlenewlogo.png"
alt="Central logo panel"
width={100}
height={100}
className="object-contain max-h-[90px] w-auto animate-[pulse_6s_infinite]"
priority
/>
</div>
<div className="relative w-[105px] h-[220px] rounded-2xl overflow-hidden border border-white shadow-md z-10">
</motion.div>
<motion.div
className="relative w-[115px] h-[240px] rounded-2xl border-4 border-white shadow-lg bg-[#FAF8FC] overflow-hidden flex items-center justify-center p-3 -mx-5 z-20"
variants={centerCardReveal}
initial="hidden"
animate="visible"
transition={{ delay: 0.15 }}
>
<motion.div
animate={!reducedMotion ? { y: [0, -6, 0] } : {}}
transition={{ delay: 0.85, duration: 3.5, repeat: Infinity, ease: "easeInOut" }}
>
<Image
src="/nearlenewlogo.png"
alt="Central logo panel"
width={100}
height={100}
className="object-contain max-h-[90px] w-auto motion-safe:animate-[pulse_6s_infinite]"
priority
/>
</motion.div>
</motion.div>
<motion.div
className="relative w-[105px] h-[220px] rounded-2xl overflow-hidden border border-white shadow-md z-10"
variants={imgFromRight}
initial="hidden"
animate="visible"
transition={{ delay: 0.36 }}
>
<Image
src="/nearle people.png"
alt="Customer"
@@ -183,10 +319,10 @@ export function Hero() {
className="object-cover"
sizes="105px"
/>
</div>
</motion.div>
</div>
</motion.div>
</div>
</div>
@@ -194,14 +330,13 @@ export function Hero() {
<div className="border-t border-[#EEE7F4]/80 bg-white">
<motion.div
className="page-container py-8 grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-4 relative z-20"
variants={staggerContainer}
variants={statStagger}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
{PROOF_ITEMS.map((item, idx) => (
<motion.div key={item.label} variants={fadeUp} className="flex items-center gap-4 text-left md:justify-center relative">
{/* Subtle vertical separator for desktop, except for the first item */}
<motion.div key={item.label} variants={statItem} className="flex items-center gap-4 text-left md:justify-center relative">
{idx > 0 && (
<div className="hidden md:block absolute left-0 top-1/2 -translate-y-1/2 w-[1px] h-10 bg-[#EEE7F4]" />
)}

View File

@@ -256,10 +256,14 @@ export function HowItWorks() {
<span className="font-black text-primary">Yours</span>{" "}
establish a local online presence and empower neighbourhood customers to discover and interact with you directly.
</p>
<button className="mt-8 inline-flex w-full items-center justify-center gap-2 rounded-full bg-primary px-8 py-4 text-base font-black text-white shadow-[0_16px_30px_rgba(101,20,154,0.22)] transition-transform duration-200 hover:-translate-y-0.5 sm:w-auto">
<motion.button
className="mt-8 inline-flex w-full items-center justify-center gap-2 rounded-full bg-primary px-8 py-4 text-base font-black text-white shadow-[0_16px_30px_rgba(101,20,154,0.22)] sm:w-auto"
whileHover={{ y: -2, scale: 1.02, transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] } }}
whileTap={{ scale: 0.97, transition: { duration: 0.1 } }}
>
<MaterialIcon icon="forum" size={20} />
Talk to an Expert
</button>
</motion.button>
</motion.div>
<motion.div

View File

@@ -6,7 +6,7 @@ import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { fadeUp, fadeIn, staggerContainer, viewport } from "@/lib/animations";
const MotionLink = motion(Link);
const MotionLink = motion.create(Link);
const CATEGORIES = [
{
@@ -106,6 +106,8 @@ export function ServiceCategories() {
href="/people"
variants={fadeIn}
className="bg-white rounded-[2rem] border border-outline-variant/10 shadow-lg overflow-hidden premium-card flex flex-col group h-full cursor-pointer"
whileHover={{ y: -4, scale: 1.02, transition: { duration: 0.22, ease: [0.22, 1, 0.36, 1] } }}
whileTap={{ scale: 0.98, transition: { duration: 0.1 } }}
>
{/* Image Container */}
<div className="relative aspect-[4/3] w-full overflow-hidden bg-surface-container/30">

View File

@@ -17,8 +17,16 @@ export function StoreQRSection() {
<div className="w-full">
<div id="download" className="w-full bg-gradient-to-br from-[#1A0322] to-[#0A000D] rounded-3xl md:rounded-[2rem] overflow-hidden text-white relative flex items-center py-12 md:py-16 px-6 md:px-12 lg:px-16 xl:px-20 border border-white/10 shadow-[0_20px_50px_rgba(20,0,30,0.22)]">
{/* Background ambient glowing details */}
<div className="absolute right-[-10%] top-[-10%] w-[50%] h-[50%] rounded-full bg-brand-accent/5 blur-[120px] pointer-events-none" />
<div className="absolute left-[-10%] bottom-[-10%] w-[50%] h-[50%] rounded-full bg-primary/10 blur-[120px] pointer-events-none" />
<motion.div
className="absolute right-[-10%] top-[-10%] w-[50%] h-[50%] rounded-full bg-brand-accent/5 blur-[120px] pointer-events-none"
animate={{ y: [0, -18, 0] }}
transition={{ duration: 14, repeat: Infinity, ease: "easeInOut" }}
/>
<motion.div
className="absolute left-[-10%] bottom-[-10%] w-[50%] h-[50%] rounded-full bg-primary/10 blur-[120px] pointer-events-none"
animate={{ y: [0, 16, 0] }}
transition={{ duration: 11, repeat: Infinity, ease: "easeInOut" }}
/>
<div className="w-full relative z-10">
@@ -124,7 +132,7 @@ export function StoreQRSection() {
</div>
<span className="inline-flex items-center gap-1.5 px-3.5 py-1.5 rounded-full text-[10px] font-black tracking-wider uppercase bg-primary/5 text-primary leading-none">
<span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" />
<span className="w-1.5 h-1.5 rounded-full bg-primary motion-safe:animate-pulse" />
Scan to Shop
</span>
</motion.div>
@@ -189,7 +197,7 @@ export function StoreQRSection() {
</div>
<span className="inline-flex items-center gap-1.5 px-3.5 py-1.5 rounded-full text-[10px] font-black tracking-wider uppercase bg-primary/5 text-primary">
<span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" />
<span className="w-1.5 h-1.5 rounded-full bg-primary motion-safe:animate-pulse" />
Scan to Shop
</span>
</motion.div>

View File

@@ -81,7 +81,7 @@ export function WhyNearle() {
</p>
</motion.div>
{/* Cards Grid — fadeIn preserves .premium-card:hover CSS transform */}
{/* Cards Grid */}
<motion.div
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 lg:gap-7"
variants={staggerContainer}
@@ -93,7 +93,9 @@ export function WhyNearle() {
<motion.div
key={card.title}
variants={fadeIn}
className="premium-card bg-white rounded-3xl p-5 md:p-6 border border-outline-variant/15 shadow-lg flex flex-col h-full hover:scale-[1.02] transition-transform duration-300"
className="premium-card bg-white rounded-3xl p-5 md:p-6 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] } }}
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}`}>
<MaterialIcon icon={card.icon} size={27} />

View File

@@ -1,5 +1,10 @@
"use client";
import Image from "next/image";
import { motion } from "framer-motion";
import { GlassInfoCard } from "../ui/GlassInfoCard";
import { scaleIn, staggerContainer, viewport } from "@/lib/animations";
import type { Variants } from "framer-motion";
type GlassCardItem = {
text: string;
@@ -12,6 +17,40 @@ type HeroVisualCollageProps = {
rightImage?: string;
glassCards: GlassCardItem[];
isPhoneCenter?: boolean;
cardVariant?: Variants;
};
const leftPanelVariant: Variants = {
hidden: { opacity: 0, x: -28 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
const rightPanelVariant: Variants = {
hidden: { opacity: 0, x: 28 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
const glasscardVariant: Variants = {
hidden: { opacity: 0, scale: 0.88, y: 8 },
visible: {
opacity: 1,
scale: 1,
y: 0,
transition: { type: "spring", stiffness: 260, damping: 22 },
},
};
const glassCardStagger: Variants = {
hidden: {},
visible: { transition: { staggerChildren: 0.1, delayChildren: 0.3 } },
};
export function HeroVisualCollage({
@@ -20,14 +59,14 @@ export function HeroVisualCollage({
rightImage,
glassCards,
isPhoneCenter = true,
cardVariant,
}: HeroVisualCollageProps) {
const activeCardVariant = cardVariant ?? glasscardVariant;
return (
<div className="relative w-full h-[380px] sm:h-[450px] lg:h-[500px] max-w-[540px] mx-auto flex items-center justify-center select-none">
{/* Decorative backgrounds */}
{/* 1. Soft purple circle */}
<div className="absolute w-[240px] h-[240px] rounded-full bg-[#7C3AED]/10 blur-[60px] -z-10" />
{/* 2. SVG Dot grid */}
<div className="absolute -top-4 -right-4 w-32 h-32 text-[#6330D6]/10 -z-10 opacity-20">
<svg className="w-full h-full" fill="currentColor">
<pattern id="collage-dots" x="0" y="0" width="16" height="16" patternUnits="userSpaceOnUse">
@@ -37,13 +76,19 @@ export function HeroVisualCollage({
</svg>
</div>
{/* 3. Small x marks */}
<div className="absolute top-12 left-6 text-[#6330D6]/15 font-display text-xl font-bold select-none pointer-events-none"></div>
<div className="absolute bottom-16 right-8 text-[#6330D6]/15 font-display text-xl font-bold select-none pointer-events-none"></div>
{/* LEFT IMAGE PANEL (Hidden or compact on small mobile) */}
{/* LEFT IMAGE PANEL */}
{leftImage && (
<div className="absolute left-2 sm:left-4 top-[15%] w-[100px] sm:w-[130px] h-[180px] sm:h-[220px] rounded-[18px] overflow-hidden border-2 border-white shadow-[0_8px_24px_rgba(0,0,0,0.06)] z-10 transition-transform hover:scale-105 duration-300">
<motion.div
className="absolute left-2 sm:left-4 top-[15%] w-[100px] sm:w-[130px] h-[180px] sm:h-[220px] rounded-[18px] overflow-hidden border-2 border-white shadow-[0_8px_24px_rgba(0,0,0,0.06)] z-10"
variants={leftPanelVariant}
initial="hidden"
whileInView="visible"
viewport={viewport}
whileHover={{ scale: 1.04, transition: { duration: 0.25, ease: [0.22, 1, 0.36, 1] } }}
>
<Image
src={leftImage}
alt="Collage left asset"
@@ -51,12 +96,19 @@ export function HeroVisualCollage({
className="object-cover"
sizes="(max-width: 640px) 100px, 130px"
/>
</div>
</motion.div>
)}
{/* RIGHT IMAGE PANEL */}
{rightImage && (
<div className="absolute right-2 sm:right-4 bottom-[15%] w-[100px] sm:w-[130px] h-[180px] sm:h-[220px] rounded-[18px] overflow-hidden border-2 border-white shadow-[0_8px_24px_rgba(0,0,0,0.06)] z-10 transition-transform hover:scale-105 duration-300">
<motion.div
className="absolute right-2 sm:right-4 bottom-[15%] w-[100px] sm:w-[130px] h-[180px] sm:h-[220px] rounded-[18px] overflow-hidden border-2 border-white shadow-[0_8px_24px_rgba(0,0,0,0.06)] z-10"
variants={rightPanelVariant}
initial="hidden"
whileInView="visible"
viewport={viewport}
whileHover={{ scale: 1.04, transition: { duration: 0.25, ease: [0.22, 1, 0.36, 1] } }}
>
<Image
src={rightImage}
alt="Collage right asset"
@@ -64,13 +116,18 @@ export function HeroVisualCollage({
className="object-cover"
sizes="(max-width: 640px) 100px, 130px"
/>
</div>
</motion.div>
)}
{/* CENTER COMPONENT */}
<div className="relative z-20">
<motion.div
className="relative z-20"
variants={scaleIn}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
{isPhoneCenter ? (
/* Phone Frame Container */
<div className="relative w-[180px] sm:w-[220px] h-[360px] sm:h-[440px] rounded-[36px] border-[5px] sm:border-[6px] border-white/95 shadow-[0_20px_50px_rgba(99,48,214,0.12)] bg-[#FAF8FC] overflow-hidden flex flex-col justify-center items-center">
<Image
src={centerImage}
@@ -82,56 +139,70 @@ export function HeroVisualCollage({
/>
</div>
) : (
/* Central Logo / Visual Panel */
<div className="relative w-[180px] sm:w-[220px] h-[280px] sm:h-[340px] rounded-[24px] border-[5px] sm:border-[6px] border-white/95 shadow-[0_20px_40px_rgba(99,48,214,0.1)] bg-[#FAF8FC] overflow-hidden flex items-center justify-center p-6">
<Image
src={centerImage}
alt="Central logo panel"
width={160}
height={160}
className="object-contain max-h-[140px] w-auto animate-[pulse_6s_infinite]"
className="object-contain max-h-[140px] w-auto motion-safe:animate-[pulse_6s_infinite]"
priority
/>
</div>
)}
</div>
</motion.div>
{/* FLOATING GLASS CARDS (Positioned Absolute, scaled down on mobile) */}
{/* FLOATING GLASS CARDS — stagger in after center */}
{glassCards.length > 0 && (
<div className="absolute inset-0 z-30 pointer-events-none">
{/* Card 1: Top Right */}
<motion.div
className="absolute inset-0 z-30 pointer-events-none"
variants={glassCardStagger}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
{glassCards[0] && (
<div className="absolute right-0 sm:right-[5%] top-[10%] pointer-events-auto hover:translate-y-[-2px] transition-transform duration-200">
<motion.div
variants={activeCardVariant}
className="absolute right-0 sm:right-[5%] top-[10%] pointer-events-auto"
whileHover={{ y: -2, transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] } }}
>
<GlassInfoCard
text={glassCards[0].text}
icon={glassCards[0].icon}
className="scale-90 sm:scale-100"
/>
</div>
</motion.div>
)}
{/* Card 2: Bottom Left */}
{glassCards[1] && (
<div className="absolute left-0 sm:left-[5%] bottom-[10%] pointer-events-auto hover:translate-y-[-2px] transition-transform duration-200">
<motion.div
variants={activeCardVariant}
className="absolute left-0 sm:left-[5%] bottom-[10%] pointer-events-auto"
whileHover={{ y: -2, transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] } }}
>
<GlassInfoCard
text={glassCards[1].text}
icon={glassCards[1].icon}
className="scale-90 sm:scale-100"
/>
</div>
</motion.div>
)}
{/* Card 3: Mid Left/Right */}
{glassCards[2] && (
<div className="absolute left-[5%] top-[45%] pointer-events-auto hover:translate-y-[-2px] transition-transform duration-200">
<motion.div
variants={activeCardVariant}
className="absolute left-[5%] top-[45%] pointer-events-auto"
whileHover={{ y: -2, transition: { duration: 0.2, ease: [0.22, 1, 0.36, 1] } }}
>
<GlassInfoCard
text={glassCards[2].text}
icon={glassCards[2].icon}
className="scale-90 sm:scale-100"
/>
</div>
</motion.div>
)}
</div>
</motion.div>
)}
</div>
);

View File

@@ -1,120 +0,0 @@
"use client";
import Link from "next/link";
import Image from "next/image";
import { useState } from "react";
import { usePathname } from "next/navigation";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
const NAV_LINKS = [
{ label: "Home", href: "/" },
{ label: "Nearle for Business", href: "/businesses" },
{ label: "Nearle for People", href: "/people" },
{ label: "About", href: "/about" },
{ label: "Contact", href: "/contact" },
];
export function Navbar() {
const [mobileOpen, setMobileOpen] = useState(false);
const pathname = usePathname();
return (
<div className="fixed top-0 left-0 right-0 z-50 w-full flex justify-center pointer-events-none">
<div className="w-full max-w-[1920px] px-4 md:px-6 lg:px-8 xl:px-10 2xl:px-12 pt-[18px] md:pt-[24px] pointer-events-auto relative">
<nav className="w-full h-[68px] lg:h-[76px] rounded-full bg-[rgba(61,45,143,0.88)] border border-white/20 shadow-sm backdrop-blur-md px-5 md:px-8 lg:px-10 flex items-center transition-all duration-300">
<div className="grid grid-cols-2 md:grid-cols-[1fr_auto_1fr] items-center w-full h-full">
{/* Left: Brand */}
<div className="flex items-center justify-start">
<Link
href="/"
className="flex items-center hover:scale-[1.02] transition-transform active:scale-95 duration-200"
>
<Image
src="/logo-light.png"
alt="Nearle"
width={400}
height={50}
className="h-8 lg:h-9 w-auto object-contain"
priority
/>
</Link>
</div>
{/* Center: Navigation Links */}
<div className="hidden md:flex items-center justify-center gap-6 lg:gap-8 xl:gap-10">
{NAV_LINKS.map((link) => {
const isActive = pathname === link.href;
return (
<Link
key={link.label}
href={link.href}
className={`relative py-2 text-[15px] font-semibold leading-5 transition-all duration-200 whitespace-nowrap ${
isActive ? "text-white" : "text-white/75 hover:text-white"
} group`}
>
{link.label}
<span
className={`absolute bottom-[-6px] left-1/2 -translate-x-1/2 h-0.5 bg-white rounded-full transition-all duration-300 ${
isActive
? "w-5 opacity-100"
: "w-0 opacity-0 group-hover:w-4 group-hover:opacity-60"
}`}
/>
</Link>
);
})}
</div>
{/* Right: Actions */}
<div className="flex items-center justify-end gap-3">
<Link
href="/#download"
className="hidden md:inline-flex items-center justify-center bg-white text-[rgba(61,45,143,0.88)] font-bold px-7 h-[46px] rounded-full text-[15px] hover:bg-white/90 hover:shadow-md hover:scale-[1.02] active:scale-95 transition-all duration-300 whitespace-nowrap"
>
Get App
</Link>
{/* Mobile menu toggle */}
<button
className="md:hidden flex items-center justify-center w-10 h-10 rounded-full hover:bg-white/10 text-white transition-all active:scale-90 cursor-pointer"
onClick={() => setMobileOpen(!mobileOpen)}
aria-label="Toggle menu"
>
<MaterialIcon icon={mobileOpen ? "close" : "menu"} size={22} />
</button>
</div>
</div>
</nav>
{/* Mobile Drawer */}
{mobileOpen && (
<div className="absolute top-[calc(100%+12px)] left-4 right-4 z-40 bg-[rgba(61,45,143,0.95)] border border-white/20 rounded-2xl shadow-[0_20px_40px_rgba(100,60,120,0.15)] backdrop-blur-md p-4 md:hidden">
<div className="flex flex-col gap-2">
{NAV_LINKS.map((link) => {
const isActive = pathname === link.href;
return (
<Link
key={link.label}
href={link.href}
className={`px-4 py-3 text-body-md font-bold rounded-xl transition-all duration-200 flex items-center justify-between ${
isActive
? "bg-white/10 text-white"
: "text-white/70 hover:text-white hover:bg-white/5"
}`}
onClick={() => setMobileOpen(false)}
>
{link.label}
{isActive && (
<span className="w-1.5 h-1.5 rounded-full bg-white animate-pulse" />
)}
</Link>
);
})}
</div>
</div>
)}
</div>
</div>
);
}

View File

@@ -3,13 +3,16 @@
import Link from "next/link";
import Image from "next/image";
import React from "react";
import { motion } from "framer-motion";
import { FaFacebookF, FaInstagram, FaLinkedinIn } from "react-icons/fa";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { fadeUp, fadeIn, staggerContainer, staggerSlow, viewport } from "@/lib/animations";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const COMPANY = [
{ label: "About Us", href: "/about" },
{ label: "Careers", href: "#" },
// { label: "Press & News", href: "#" },
{ label: "Help Center", href: "#" },
];
@@ -17,26 +20,87 @@ const PARTNERSHIPS = [
{ label: "Merchant Partner", href: "/merchants" },
{ label: "Delivery Partner", href: "/delivery-partners" },
{ label: "Direct to Customer Brands", href: "/brands" },
// { label: "Local Real Estate", href: "#" },
];
const LEGAL = [
{ label: "Privacy Policy", href: "/privacy" },
{ label: "Terms of Service", href: "/terms" },
{ label: "Cookie Policy", href: "/cookies" },
// { label: "Sitemap", href: "#" },
];
const socialButtonClass =
"flex h-10 w-10 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white transition-all duration-300 xl:h-[54px] xl:w-[54px]";
"flex h-10 w-10 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white transition-[background-color] duration-300 xl:h-[54px] xl:w-[54px]";
const linkColumnVariant = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: EASE },
},
};
const footerReveal = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.6, ease: EASE },
},
};
// Renders an animated link list used in all three footer link columns
function FooterLinkList({ links }: { links: { label: string; href: string }[] }) {
return (
<ul className="flex flex-col gap-4 xl:gap-5">
{links.map((link) => (
<li key={link.label}>
<motion.span
className="relative inline-block"
initial="rest"
whileHover="hover"
animate="rest"
>
<Link
href={link.href}
className="text-sm leading-normal text-white/70 transition-colors duration-200 hover:text-white md:text-base xl:text-[17px]"
>
{link.label}
</Link>
{/* Underline grows from left on hover */}
<motion.span
className="absolute -bottom-0.5 left-0 right-0 h-px rounded-full bg-white/50"
style={{ originX: 0 }}
variants={{ rest: { scaleX: 0 }, hover: { scaleX: 1 } }}
transition={{ duration: 0.2, ease: EASE }}
/>
</motion.span>
</li>
))}
</ul>
);
}
export function PremiumFooter() {
return (
<section className="bg-white px-2 pb-2 md:px-3 md:pb-3 lg:px-4 lg:pb-4 xl:px-5 xl:pb-4 2xl:px-6 2xl:pb-5">
<footer className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#16001D] to-[#25002F] text-white shadow-[0_-12px_40px_rgba(22,0,29,0.12)]">
{/* Footer reveal: the whole footer fades up as it enters the viewport */}
<motion.footer
className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#16001D] to-[#25002F] text-white shadow-[0_-12px_40px_rgba(22,0,29,0.12)]"
variants={footerReveal}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
<div className="flex flex-col px-6 pb-8 pt-10 md:px-8 md:pb-8 md:pt-12 xl:px-16 xl:pb-8 xl:pt-12 2xl:px-20 2xl:pt-10 2xl:pb-8">
<div className="grid w-full grid-cols-1 items-start gap-10 text-left md:grid-cols-2 md:gap-x-12 md:gap-y-12 lg:grid-cols-[1.6fr_1fr_1fr_1fr] lg:gap-10 xl:gap-14">
<div className="flex flex-col gap-5">
{/* Main grid: columns stagger in via parent footer variant propagation */}
<motion.div
className="grid w-full grid-cols-1 items-start gap-10 text-left md:grid-cols-2 md:gap-x-12 md:gap-y-12 lg:grid-cols-[1.6fr_1fr_1fr_1fr] lg:gap-10 xl:gap-14"
variants={staggerSlow}
>
{/* Brand column */}
<motion.div variants={fadeUp} className="flex flex-col gap-5">
<Link href="/" className="inline-block w-fit">
<Image
src="/logo-light.png"
@@ -52,100 +116,81 @@ export function PremiumFooter() {
communities and verified local businesses within a 2km radius.
</p>
{/* Social icons with scale spring on hover */}
<div className="mt-1 flex gap-3 xl:mt-2 xl:gap-4">
<a
<motion.a
href="https://www.facebook.com/nearledigital"
target="_blank"
rel="noopener noreferrer"
aria-label="Facebook"
className={`${socialButtonClass} hover:bg-[#1877F2]`}
whileHover={{ scale: 1.12 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
<FaFacebookF size={20} />
</a>
</motion.a>
<a
<motion.a
href="https://www.instagram.com/nearledigital/"
target="_blank"
rel="noopener noreferrer"
aria-label="Instagram"
className={`${socialButtonClass} hover:bg-[#E4405F]`}
whileHover={{ scale: 1.12 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
<FaInstagram size={21} />
</a>
</motion.a>
<a
<motion.a
href="https://www.linkedin.com/company/nearledigital/"
target="_blank"
rel="noopener noreferrer"
aria-label="LinkedIn"
className={`${socialButtonClass} hover:bg-[#0A66C2]`}
whileHover={{ scale: 1.12 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
<FaLinkedinIn size={20} />
</a>
</motion.a>
</div>
</div>
</motion.div>
<div className="flex flex-col">
{/* Company links */}
<motion.div variants={linkColumnVariant} className="flex flex-col">
<h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]">
Company
</h5>
<FooterLinkList links={COMPANY} />
</motion.div>
<ul className="flex flex-col gap-4 xl:gap-5">
{COMPANY.map((link) => (
<li key={link.label}>
<Link
href={link.href}
className="inline-block text-sm leading-normal text-white/70 transition-all hover:translate-x-0.5 hover:text-white md:text-base xl:text-[17px]"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
<div className="flex flex-col">
{/* Nearle links */}
<motion.div variants={linkColumnVariant} className="flex flex-col">
<h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]">
Nearle
</h5>
<FooterLinkList links={PARTNERSHIPS} />
</motion.div>
<ul className="flex flex-col gap-4 xl:gap-5">
{PARTNERSHIPS.map((link) => (
<li key={link.label}>
<Link
href={link.href}
className="inline-block text-sm leading-normal text-white/70 transition-all hover:translate-x-0.5 hover:text-white md:text-base xl:text-[17px]"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
<div className="flex flex-col">
{/* Support links */}
<motion.div variants={linkColumnVariant} className="flex flex-col">
<h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]">
Support
</h5>
<ul className="flex flex-col gap-4 xl:gap-5">
{LEGAL.map((link) => (
<li key={link.label}>
<Link
href={link.href}
className="inline-block text-sm leading-normal text-white/70 transition-all hover:translate-x-0.5 hover:text-white md:text-base xl:text-[17px]"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
</div>
<FooterLinkList links={LEGAL} />
</motion.div>
</motion.div>
<div className="my-8 w-full border-t border-white/10 xl:my-10" />
<div className="flex w-full flex-col items-center justify-between gap-8 md:flex-row xl:gap-12">
{/* Bottom row */}
<motion.div
className="flex w-full flex-col items-center justify-between gap-8 md:flex-row xl:gap-12"
variants={fadeUp}
>
<div className="flex min-h-[120px] w-full max-w-[560px] items-start gap-3 rounded-2xl border border-white/10 bg-white/5 p-4 md:w-auto md:p-5 xl:min-h-[130px] xl:gap-4 xl:p-6">
<div className="mt-0.5 flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-white/10 text-white xl:h-[58px] xl:w-[58px] xl:rounded-2xl">
<MaterialIcon icon="location_on" size={20} />
@@ -155,7 +200,6 @@ export function PremiumFooter() {
<h6 className="text-sm font-black text-white md:text-base xl:text-lg">
Nearle
</h6>
<p className="mt-0.5 text-xs leading-normal text-white/60 md:text-sm xl:mt-1 xl:text-base">
424, Diwan Bahadur Rd, Opp. Sri Krishna Sweets, R.S. Puram,
Coimbatore, TN 641002
@@ -181,11 +225,15 @@ export function PremiumFooter() {
</span>
</a>
</div>
</div>
</motion.div>
<div className="mb-4 mt-8 w-full border-t border-white/5 xl:mb-6 xl:mt-10" />
<div className="flex w-full flex-col items-center justify-between gap-4 text-[13px] text-white/40 md:flex-row md:text-[14px] xl:text-[15px]">
{/* Copyright bar */}
<motion.div
className="flex w-full flex-col items-center justify-between gap-4 text-[13px] text-white/40 md:flex-row md:text-[14px] xl:text-[15px]"
variants={fadeIn}
>
<p className="text-center md:text-left">
© {new Date().getFullYear()} Nearle. All rights reserved.
</p>
@@ -203,9 +251,9 @@ export function PremiumFooter() {
Help Center
</Link>
</div>
</div>
</motion.div>
</div>
</footer>
</motion.footer>
</section>
);
}

View File

@@ -2,10 +2,18 @@
import Link from "next/link";
import Image from "next/image";
import { useState } from "react";
import { useState, useEffect } from "react";
import { usePathname } from "next/navigation";
import {
motion,
AnimatePresence,
useScroll,
useMotionValueEvent,
} from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const NAV_LINKS = [
{ label: "Home", href: "/" },
{ label: "Nearle for Business", href: "/businesses" },
@@ -14,9 +22,44 @@ const NAV_LINKS = [
{ label: "Contact", href: "/contact" },
];
const mobileMenuVariants = {
hidden: { opacity: 0, height: 0 },
visible: {
opacity: 1,
height: "auto",
transition: { duration: 0.3, ease: EASE },
},
exit: {
opacity: 0,
height: 0,
transition: { duration: 0.2, ease: "easeIn" as const },
},
};
const mobileStagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.04, delayChildren: 0.06 } },
};
const mobileItemVariants = {
hidden: { opacity: 0, x: -10 },
visible: { opacity: 1, x: 0, transition: { duration: 0.22, ease: EASE } },
};
export function PremiumNavbar() {
const [mobileOpen, setMobileOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const pathname = usePathname();
const { scrollY } = useScroll();
useMotionValueEvent(scrollY, "change", (y) => {
setScrolled(y > 20);
});
// Close mobile menu whenever the route changes (navbar no longer remounts on navigation)
useEffect(() => {
setMobileOpen(false);
}, [pathname]);
const isActiveLink = (href: string) => {
if (href === "/") return pathname === "/";
@@ -24,10 +67,18 @@ export function PremiumNavbar() {
};
return (
// <header className="sticky top-0 z-50 w-full border-b border-white/10 bg-[#3D2D8F]/90 text-white backdrop-blur-xl">
<header className="sticky top-0 z-50 w-full bg-[#83429f] border-b border-white/10 text-white">
<motion.header
className={`sticky top-0 z-50 w-full text-white transition-[background-color,box-shadow,backdrop-filter] duration-300 ${
scrolled
? "bg-[#83429f]/82 backdrop-blur-lg shadow-[0_6px_32px_rgba(0,0,0,0.35)] border-b border-white/15"
: "bg-[#83429f] border-b border-white/10"
}`}
initial={{ opacity: 0, y: -8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: EASE }}
>
<div className="relative mx-auto flex h-16 w-full max-w-[1600px] items-center px-6 md:h-20 md:px-10 xl:px-16">
{/* Logo - Left */}
{/* Logo */}
<Link
href="/"
className="flex shrink-0 items-center transition-transform duration-200 hover:scale-[1.02] active:scale-95"
@@ -43,38 +94,55 @@ export function PremiumNavbar() {
/>
</Link>
{/* Desktop Navigation - Center */}
{/* Desktop Navigation */}
<nav className="absolute left-1/2 hidden -translate-x-1/2 items-center gap-6 lg:flex lg:gap-8 xl:gap-10">
{NAV_LINKS.map((link) => {
const isActive = isActiveLink(link.href);
return (
<Link
<motion.div
key={link.label}
href={link.href}
className={`relative whitespace-nowrap py-2 text-sm font-bold transition-colors duration-200 ${
isActive
? "text-white"
: "text-white/70 hover:text-white"
}`}
className="relative py-2"
initial="rest"
whileHover="hover"
animate="rest"
>
{link.label}
<span
className={`absolute bottom-0 left-1/2 h-0.5 -translate-x-1/2 rounded-full bg-white transition-all duration-300 ${
isActive ? "w-full" : "w-0"
<Link
href={link.href}
className={`whitespace-nowrap text-sm font-bold transition-colors duration-200 ${
isActive ? "text-white" : "text-white/70 hover:text-white"
}`}
/>
</Link>
>
{link.label}
</Link>
{/* Active underline: layoutId lets it slide between links on navigation */}
{isActive && (
<motion.span
layoutId="activeNavUnderline"
className="absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-white"
transition={{ duration: 0.3, ease: EASE }}
/>
)}
{/* Hover underline: grows from left on inactive links */}
{!isActive && (
<motion.span
className="absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-white/40"
style={{ originX: 0 }}
variants={{ rest: { scaleX: 0 }, hover: { scaleX: 1 } }}
transition={{ duration: 0.2, ease: EASE }}
/>
)}
</motion.div>
);
})}
</nav>
{/* Mobile Toggle - Right */}
{/* Mobile Toggle */}
<button
type="button"
className="ml-auto flex h-10 w-10 items-center justify-center rounded-full text-white transition-all duration-200 hover:bg-white/10 active:scale-90 lg:hidden"
onClick={() => setMobileOpen((value) => !value)}
onClick={() => setMobileOpen((v) => !v)}
aria-label={mobileOpen ? "Close menu" : "Open menu"}
aria-expanded={mobileOpen}
>
@@ -82,39 +150,48 @@ export function PremiumNavbar() {
</button>
</div>
{/* Mobile Menu */}
<div
className={`overflow-hidden border-t border-white/10 bg-[#30226F]/98 backdrop-blur-xl transition-all duration-300 lg:hidden ${
mobileOpen
? "max-h-[500px] opacity-100"
: "pointer-events-none max-h-0 opacity-0"
}`}
>
<nav className="mx-auto flex w-full max-w-[1600px] flex-col gap-1 px-6 py-5 md:px-10">
{NAV_LINKS.map((link) => {
const isActive = isActiveLink(link.href);
return (
<Link
key={link.label}
href={link.href}
onClick={() => setMobileOpen(false)}
className={`flex items-center justify-between rounded-xl px-4 py-3 text-sm font-bold transition-all duration-200 ${
isActive
? "bg-white/15 text-white"
: "text-white/70 hover:bg-white/10 hover:text-white"
}`}
>
{link.label}
{isActive && (
<span className="h-2 w-2 rounded-full bg-white" />
)}
</Link>
);
})}
</nav>
</div>
</header>
{/* Mobile Menu with enter/exit animation */}
<AnimatePresence>
{mobileOpen && (
<motion.div
key="mobile-menu"
variants={mobileMenuVariants}
initial="hidden"
animate="visible"
exit="exit"
className="overflow-hidden border-t border-white/10 bg-[#30226F]/98 backdrop-blur-xl lg:hidden"
>
<motion.nav
className="mx-auto flex w-full max-w-[1600px] flex-col gap-1 px-6 py-5 md:px-10"
variants={mobileStagger}
initial="hidden"
animate="visible"
>
{NAV_LINKS.map((link) => {
const isActive = isActiveLink(link.href);
return (
<motion.div key={link.label} variants={mobileItemVariants}>
<Link
href={link.href}
onClick={() => setMobileOpen(false)}
className={`flex items-center justify-between rounded-xl px-4 py-3 text-sm font-bold transition-all duration-200 ${
isActive
? "bg-white/15 text-white"
: "text-white/70 hover:bg-white/10 hover:text-white"
}`}
>
{link.label}
{isActive && (
<span className="h-2 w-2 rounded-full bg-white" />
)}
</Link>
</motion.div>
);
})}
</motion.nav>
</motion.div>
)}
</AnimatePresence>
</motion.header>
);
}

View File

@@ -1,27 +1,12 @@
import React from "react";
import { PremiumNavbar } from "./PremiumNavbar";
import { PremiumFooter } from "./PremiumFooter";
import { MotionProvider } from "./MotionProvider";
type PremiumPageShellProps = {
children: React.ReactNode;
className?: string;
};
export function PremiumPageShell({ children, className = "" }: PremiumPageShellProps) {
return (
<MotionProvider>
<div className={`min-h-screen bg-white font-body text-[#16001D] antialiased flex flex-col ${className}`}>
{/* Header/Navbar */}
<PremiumNavbar />
{/* Page Content */}
<main className="w-full overflow-x-hidden bg-white">
{children}
</main>
<PremiumFooter />
</div>
</MotionProvider>
);
// Navbar, footer, and MotionProvider are rendered in the root layout (app/layout.tsx)
// and persist across all navigations. This component is kept for call-site compatibility.
export function PremiumPageShell({ children }: PremiumPageShellProps) {
return <>{children}</>;
}

View File

@@ -0,0 +1,42 @@
"use client";
import { motion } from "framer-motion";
import type { ReactNode, CSSProperties } from "react";
import { cardReveal, staggerCards, viewport20 } from "@/lib/animations";
type Props = {
children: ReactNode;
className?: string;
style?: CSSProperties;
};
export function AnimatedGrid({ children, className, style }: Props) {
return (
<motion.div
className={className}
style={style}
variants={staggerCards}
initial="hidden"
whileInView="visible"
viewport={viewport20}
>
{children}
</motion.div>
);
}
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
export function AnimatedCard({ children, className, style }: Props) {
return (
<motion.div
className={className}
style={style}
variants={cardReveal}
whileHover={{ y: -4, transition: { duration: 0.22, ease: EASE } }}
whileTap={{ scale: 0.97, transition: { duration: 0.1 } }}
>
{children}
</motion.div>
);
}

View File

@@ -0,0 +1,50 @@
"use client";
import { motion } from "framer-motion";
import type { ReactNode } from "react";
import { fadeUp, staggerContainer, viewport } from "@/lib/animations";
import type { Variants } from "framer-motion";
const VARIANTS: Record<string, Variants> = {
fadeUp,
stagger: staggerContainer,
};
type AnimatedSectionProps = {
children: ReactNode;
className?: string;
variant?: "fadeUp" | "stagger";
delay?: number;
};
export function AnimatedSection({
children,
className = "",
variant = "fadeUp",
delay = 0,
}: AnimatedSectionProps) {
const chosen = VARIANTS[variant] ?? fadeUp;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const withDelay: Variants = {
hidden: chosen.hidden as any,
visible: {
...(chosen.visible as any),
transition: {
...((chosen.visible as any)?.transition ?? {}),
delay,
},
},
};
return (
<motion.div
className={className}
variants={withDelay}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
{children}
</motion.div>
);
}

View File

@@ -0,0 +1,102 @@
"use client";
import Link from "next/link";
import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const VP = { once: true, amount: 0.25 } as const;
const stagger = {
hidden: {},
visible: { transition: { staggerChildren: 0.13 } },
};
const textReveal = {
hidden: { opacity: 0, y: 18 },
visible: { opacity: 1, y: 0, transition: { duration: 0.52, ease: EASE } },
};
const buttonReveal = {
hidden: { opacity: 0, y: 12 },
visible: { opacity: 1, y: 0, transition: { duration: 0.42, ease: EASE } },
};
const decorReveal = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { duration: 1.1, delay: 0.38 } },
};
type CTABannerProps = {
badge?: { icon: string; label: string };
heading: React.ReactNode;
body: string;
primary: { href: string; icon: string; label: string };
secondary: { href: string; icon: string; label: string };
decorations?: string[];
};
export function CTABanner({ badge, heading, body, primary, secondary, decorations }: CTABannerProps) {
return (
<section className="bg-white px-2 pt-2 pb-2 md:px-3 md:pt-3 md:pb-3 lg:px-4 lg:pt-4 lg:pb-4 xl:px-5 xl:pt-4 xl:pb-4 2xl:px-6 2xl:pb-5">
<motion.div
className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#1A0322] via-[#2B0040] to-[#0A000D] text-white"
variants={stagger}
initial="hidden"
whileInView="visible"
viewport={VP}
>
<div className="px-6 py-12 md:px-10 md:py-14 lg:px-14 xl:py-12 text-center relative">
{decorations?.map((grad, i) => (
<motion.div
key={i}
className="absolute inset-0 pointer-events-none"
style={{ background: `radial-gradient(${grad})` }}
variants={decorReveal}
/>
))}
<div className="relative z-10 max-w-2xl mx-auto">
{badge && (
<motion.span
className="inline-flex items-center gap-2 px-3 py-1 rounded-full border border-white/15 text-white/70 text-xs font-black uppercase tracking-widest mb-6"
variants={textReveal}
>
<MaterialIcon icon={badge.icon} size={14} />
{badge.label}
</motion.span>
)}
<motion.h2
className="font-display text-3xl md:text-4xl xl:text-5xl font-black mb-4 tracking-tight leading-tight"
variants={textReveal}
>
{heading}
</motion.h2>
<motion.p
className="text-sm md:text-base text-white/70 mb-8 leading-relaxed max-w-lg mx-auto"
variants={textReveal}
>
{body}
</motion.p>
<motion.div className="flex flex-wrap justify-center gap-3.5" variants={buttonReveal}>
<Link
href={primary.href}
className="bg-[#6330D6] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm hover:shadow-xl transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon={primary.icon} size={18} />
<span>{primary.label}</span>
</Link>
<Link
href={secondary.href}
className="bg-white/10 hover:bg-white/15 border border-white/20 text-white font-black px-8 py-3.5 rounded-xl text-sm transition-all duration-200 active:scale-95 flex items-center gap-2"
>
<MaterialIcon icon={secondary.icon} size={18} />
<span>{secondary.label}</span>
</Link>
</motion.div>
</div>
</div>
</motion.div>
</section>
);
}

View File

@@ -1,8 +1,11 @@
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { MaterialIcon } from "./MaterialIcon";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
export type FAQItem = {
question: string;
answer: string;
@@ -31,25 +34,30 @@ export function FAQAccordion({ items }: FAQAccordionProps) {
<span className="text-sm md:text-base font-black text-[#16001D] leading-snug">
{item.question}
</span>
<div
className={`flex-shrink-0 w-8 h-8 rounded-full bg-[#6330D6]/8 flex items-center justify-center text-[#6330D6] transition-transform duration-300 ${
openIndex === index ? "rotate-180" : ""
}`}
<motion.div
className="flex-shrink-0 w-8 h-8 rounded-full bg-[#6330D6]/8 flex items-center justify-center text-[#6330D6]"
animate={{ rotate: openIndex === index ? 180 : 0 }}
transition={{ duration: 0.3, ease: EASE }}
>
<MaterialIcon icon="expand_more" size={20} />
</div>
</motion.div>
</button>
<div
className={`overflow-hidden transition-all duration-300 ease-in-out ${
openIndex === index
? "max-h-[500px] opacity-100"
: "max-h-0 opacity-0"
}`}
>
<p className="px-6 pb-5 text-sm md:text-base text-[#5E5866] leading-relaxed border-t border-[#6330D6]/5 pt-4">
{item.answer}
</p>
</div>
<AnimatePresence initial={false}>
{openIndex === index && (
<motion.div
key="content"
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: EASE }}
style={{ overflow: "hidden" }}
>
<p className="px-6 pb-5 text-sm md:text-base text-[#5E5866] leading-relaxed border-t border-[#6330D6]/5 pt-4">
{item.answer}
</p>
</motion.div>
)}
</AnimatePresence>
</div>
))}
</div>

View File

@@ -1,4 +1,8 @@
"use client";
import { motion } from "framer-motion";
import { MaterialIcon } from "./MaterialIcon";
import { fadeUp, staggerContainer, viewport } from "@/lib/animations";
type FeatureStatsItem = {
icon: string;
@@ -13,11 +17,18 @@ type FeatureStatsRowProps = {
export function FeatureStatsRow({ items, className = "" }: FeatureStatsRowProps) {
return (
<div className={`w-full py-6 md:py-8 ${className}`}>
<motion.div
className={`w-full py-6 md:py-8 ${className}`}
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
<div className="grid grid-cols-2 lg:flex lg:flex-row items-center justify-between gap-y-6 gap-x-4 lg:gap-0">
{items.map((item, index) => (
<div
<motion.div
key={item.title}
variants={fadeUp}
className={`flex items-center gap-3.5 lg:flex-1 px-3 lg:px-8 text-left ${
index < items.length - 1
? "lg:border-r lg:border-[#6330D6]/10"
@@ -37,9 +48,9 @@ export function FeatureStatsRow({ items, className = "" }: FeatureStatsRowProps)
</p>
)}
</div>
</div>
</motion.div>
))}
</div>
</div>
</motion.div>
);
}

View File

@@ -16,7 +16,7 @@ export function GlassInfoCard({ text, icon, className = "" }: GlassInfoCardProps
<MaterialIcon icon={icon} size={14} />
</div>
) : (
<span className="w-2.5 h-2.5 rounded-full bg-[#6330D6] shrink-0 animate-pulse" />
<span className="w-2.5 h-2.5 rounded-full bg-[#6330D6] shrink-0 motion-safe:animate-pulse" />
)}
<span className="text-sm font-extrabold text-[#16001D] whitespace-nowrap">
{text}

View File

@@ -5,7 +5,7 @@ export const fadeUp: Variants = {
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] },
transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
@@ -19,12 +19,38 @@ export const fadeIn: Variants = {
},
};
export const fadeInSlow: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { duration: 0.7, ease: "easeOut" },
},
};
export const fadeUpStrong: Variants = {
hidden: { opacity: 0, y: 36 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.65, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
export const scaleIn: Variants = {
hidden: { opacity: 0, scale: 0.95 },
visible: {
opacity: 1,
scale: 1,
transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] },
transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
export const scaleInSpring: Variants = {
hidden: { opacity: 0, scale: 0.88 },
visible: {
opacity: 1,
scale: 1,
transition: { type: "spring", stiffness: 280, damping: 20 },
},
};
@@ -33,7 +59,16 @@ export const slideInLeft: Variants = {
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] },
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
export const slideInLeftSubtle: Variants = {
hidden: { opacity: 0, x: -20 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
@@ -42,7 +77,7 @@ export const slideInRight: Variants = {
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] },
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
@@ -53,4 +88,55 @@ export const staggerContainer: Variants = {
},
};
export const staggerFast: Variants = {
hidden: {},
visible: {
transition: { staggerChildren: 0.07 },
},
};
export const staggerSlow: Variants = {
hidden: {},
visible: {
transition: { staggerChildren: 0.13 },
},
};
export const viewport = { once: true, margin: "-60px" } as const;
export const viewportEarly = { once: true, margin: "-20px" } as const;
export const viewport20 = { once: true, amount: 0.2 } as const;
export const revealUp: Variants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.65, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
export const cardReveal: Variants = {
hidden: { opacity: 0, y: 20, scale: 0.98 },
visible: {
opacity: 1,
y: 0,
scale: 1,
transition: { duration: 0.55, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
export const imageReveal: Variants = {
hidden: { opacity: 0, scale: 0.97 },
visible: {
opacity: 1,
scale: 1,
transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] },
},
};
export const staggerCards: Variants = {
hidden: {},
visible: {
transition: { staggerChildren: 0.08 },
},
};