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 ## Project
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:
1. For frontend UI, layout, responsive, styling, or screenshot-reference tasks, you MAY start the Next.js local development server using `npm run dev`. Nearle is a premium hyperlocal commerce platform connecting
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. - Customers
4. `npm run build` verifies compilation only. Do not claim a visual issue is fixed based only on build success. - Local Merchants
5. For visual tasks, inspect localhost before reporting completion. - Delivery Partners
6. Provide the exact code fix, logic, or file modification directly based on your analysis of the provided files. - Brands
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.
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 Link from "next/link";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; import { AboutHero } from "@/components/heroes/AboutHero";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import type { Metadata } from "next"; import type { Metadata } from "next";
export const metadata: Metadata = { export const metadata: Metadata = {
@@ -48,93 +49,27 @@ const LOCAL_COUNTERS = [
{ count: "Growing", label: "Neighbourhood Reach", icon: "location_city" } { 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() { export default function AboutPage() {
const collageCards = [
{ text: "Local First", icon: "store" },
{ text: "Trusted Network", icon: "verified" },
{ text: "Connected Communities", icon: "groups" }
];
return ( return (
<PremiumPageShell> <PremiumPageShell>
{/* ─── Hero Section ─── */} {/* ─── 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]"> <AboutHero />
{/* 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>
{/* ─── Intro Description Block ─── */} {/* ─── 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"> <AnimatedSection>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full"> <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">
Our Platform <span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
</span> Our Platform
<h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-4"> </span>
Nearle Neighbourhood Commerce <h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-4">
</h2> Nearle Neighbourhood Commerce
<p className="mx-auto max-w-3xl text-sm md:text-base text-[#5E5866] leading-relaxed font-semibold"> </h2>
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 className="mx-auto max-w-3xl text-sm md:text-base text-[#5E5866] leading-relaxed font-semibold">
</p> 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.
</section> </p>
</section>
</AnimatedSection>
{/* ─── Strategy Cards (Mission, Vision, Why) ─── */} {/* ─── Strategy Cards (Mission, Vision, Why) ─── */}
<section <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)]" className="mx-auto w-[calc(100%-48px)] md:w-[calc(100%-64px)] xl:w-[calc(100%-96px)]"
style={{ maxWidth: 1800 }} style={{ maxWidth: 1800 }}
> >
<div <AnimatedGrid
className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3"
style={{ gap: "clamp(24px, 2.5vw, 36px)" }} style={{ gap: "clamp(24px, 2.5vw, 36px)" }}
> >
{STRATEGY_CARDS.map((card) => ( {STRATEGY_CARDS.map((card) => (
<div <AnimatedCard
key={card.title} 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]" 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 }} style={{ borderRadius: 32 }}
@@ -196,17 +131,17 @@ export default function AboutPage() {
className={card.imgFit} className={card.imgFit}
/> />
</div> </div>
</div> </AnimatedCard>
))} ))}
</div> </AnimatedGrid>
</div> </div>
</section> </section>
{/* ─── Global Stats counters ─── */} {/* ─── 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"> <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) => ( {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"> <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} /> <MaterialIcon icon={c.icon} size={22} />
</div> </div>
@@ -214,41 +149,43 @@ export default function AboutPage() {
<p className="text-[10px] font-bold text-[#5E5866] mt-1.5 uppercase tracking-wider"> <p className="text-[10px] font-bold text-[#5E5866] mt-1.5 uppercase tracking-wider">
{c.label} {c.label}
</p> </p>
</div> </AnimatedCard>
))} ))}
</div> </AnimatedGrid>
</section> </section>
{/* ─── Brand Building Banner ─── */} {/* ─── 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"> <AnimatedSection>
<div className="max-w-4xl mx-auto"> <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">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full"> <div className="max-w-4xl mx-auto">
Don&apos;t miss the opportunity <span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
</span> Don&apos;t miss the opportunity
<h2 className="font-display text-3xl font-black text-[#16001D] mt-4 mb-4"> </span>
Build Your Brand with Nearle <h2 className="font-display text-3xl font-black text-[#16001D] mt-4 mb-4">
</h2> Build Your Brand with Nearle
<p className="text-xs md:text-sm text-[#5E5866] max-w-2xl mx-auto mb-8 leading-relaxed font-semibold"> </h2>
Delight your neighborhood with a personalized online experience. Unlock revenue opportunities and local marketing strategies that will take your business further. <p className="text-xs md:text-sm text-[#5E5866] max-w-2xl mx-auto mb-8 leading-relaxed font-semibold">
</p> Delight your neighborhood with a personalized online experience. Unlock revenue opportunities and local marketing strategies that will take your business further.
<div className="flex flex-wrap justify-center gap-4"> </p>
<Link <div className="flex flex-wrap justify-center gap-4">
href="/businesses" <Link
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" 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> <MaterialIcon icon="bolt" size={18} />
</Link> <span>Register Merchant</span>
<Link </Link>
href="/people" <Link
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" 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> <MaterialIcon icon="download" size={18} />
</Link> <span>Explore Nearle</span>
</Link>
</div>
</div> </div>
</div> </section>
</section> </AnimatedSection>
</PremiumPageShell> </PremiumPageShell>
); );
} }

View File

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

View File

@@ -3,9 +3,11 @@ import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; 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 { 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"; import type { Metadata } from "next";
export const metadata: Metadata = { 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() { function PhoneMapVisual() {
return ( return (
@@ -96,102 +92,42 @@ function PhoneMapVisual() {
} }
export default function BusinessesPage() { 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 ( return (
<PremiumPageShell> <PremiumPageShell>
{/* ─── Hero Section ─── */} {/* ─── 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]"> <BusinessHero />
{/* 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>
{/* ─── Intro Highlight Banner ─── */} {/* ─── 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"> <AnimatedSection>
<div className="max-w-2xl text-left"> <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">
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3 py-1 rounded-full"> <div className="max-w-2xl text-left">
Together, we grow <span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3 py-1 rounded-full">
</span> Together, we grow
<h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-3 leading-tight"> </span>
Get set in minutes and delight the Neighborhood <h2 className="font-display text-2xl md:text-3xl font-black text-[#16001D] mt-4 mb-3 leading-tight">
</h2> Get set in minutes and delight the Neighborhood
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed"> </h2>
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 className="text-sm md:text-base text-[#5E5866] leading-relaxed">
</p> 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.
</div> </p>
<div className="flex gap-4 shrink-0"> </div>
<Link <div className="flex gap-4 shrink-0">
href="/contact" <Link
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" 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> <MaterialIcon icon="chat" size={18} />
</Link> <span>Talk to an Expert</span>
</div> </Link>
</section> </div>
</section>
</AnimatedSection>
{/* ─── Build Your Neighborhood Brand ─── */} {/* ─── 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"> <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"> <div className="w-[calc(100%-32px)] lg:w-[calc(100%-64px)] max-w-[1800px] mx-auto">
{/* Header */} {/* Header */}
<AnimatedSection>
<div className="text-center mb-10 lg:mb-12 xl:mb-10"> <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]"> <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 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. Three simple milestones to scale your retail reach from offline streets straight into neighborhood homes.
</p> </p>
</div> </div>
</AnimatedSection>
{/* Cards grid */} {/* 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 */} {/* 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"> <span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-[#6330D6]/[0.07] select-none">
01 01
</span> </span>
@@ -233,10 +170,10 @@ export default function BusinessesPage() {
/> />
</div> </div>
</div> </div>
</article> </AnimatedCard>
{/* Card 2 - Get Noticed */} {/* 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"> <span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-[#6330D6]/[0.07] select-none">
02 02
</span> </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"> <div className="relative z-0 mt-auto h-[270px] lg:h-[290px] flex items-center justify-center">
<PhoneMapVisual /> <PhoneMapVisual />
</div> </div>
</article> </AnimatedCard>
{/* Card 3 - Get Simplified */} {/* 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"> <span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-[#6330D6]/[0.07] select-none">
03 03
</span> </span>
@@ -290,8 +227,8 @@ export default function BusinessesPage() {
/> />
</div> </div>
</div> </div>
</article> </AnimatedCard>
</div> </AnimatedGrid>
</div> </div>
</section> </section>
@@ -299,38 +236,15 @@ export default function BusinessesPage() {
<BusinessFeaturesGrid /> <BusinessFeaturesGrid />
{/* ─── Join/Download Call to Action ─── */} {/* ─── 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"> <CTABanner
<div className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#1A0322] to-[#0A000D] text-white"> heading="Nearle is Essential for Business Growth"
<div className="px-6 py-10 md:px-10 md:py-12 lg:px-14 lg:py-14 xl:py-12 text-center relative"> body="Leverage the full potential of your neighborhood. Stand above the rest, increase customers, and scale your brand today."
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_20%,rgba(99,48,214,0.15),transparent_50%)] pointer-events-none" /> primary={{ href: "/contact", icon: "bolt", label: "Register Interest" }}
<div className="relative z-10 max-w-2xl mx-auto"> secondary={{ href: "/contact", icon: "phone", label: "Contact Experts" }}
<h2 className="font-display text-3xl md:text-4xl font-black mb-4 tracking-tight"> decorations={[
Nearle is Essential for Business Growth "circle at 70% 20%, rgba(99,48,214,0.15), transparent 50%",
</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>
</PremiumPageShell> </PremiumPageShell>
); );
} }

View File

@@ -1,7 +1,8 @@
import React from "react"; import React from "react";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { ContactForm } from "@/components/contact/ContactForm";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { ContactHero } from "@/components/heroes/ContactHero";
import { AnimatedSection } from "@/components/motion/AnimatedSection";
import type { Metadata } from "next"; import type { Metadata } from "next";
export const metadata: Metadata = { export const metadata: Metadata = {
@@ -13,95 +14,10 @@ export default function ContactPage() {
return ( return (
<PremiumPageShell> <PremiumPageShell>
{/* ─── Contact Section ─── */} {/* ─── 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"> <ContactHero />
{/* 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>
{/* ─── Map Section ─── */} {/* ─── Map Section ─── */}
<AnimatedSection>
<section className="bg-[#FAF7FD] px-4 py-8 md:px-6 lg:px-8 xl:px-10 2xl:px-12"> <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="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)]"> <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>
</div> </div>
</section> </section>
</AnimatedSection>
</PremiumPageShell> </PremiumPageShell>
); );
} }

View File

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

View File

@@ -1,5 +1,8 @@
import type { Metadata, Viewport } from "next"; import type { Metadata, Viewport } from "next";
import "./globals.css"; 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 jost = { variable: "font-display" };
const jostBody = { variable: "font-body" }; const jostBody = { variable: "font-body" };
@@ -70,6 +73,7 @@ export default function RootLayout({
<html <html
lang="en" lang="en"
className={`${jost.variable} ${jostBody.variable}`} className={`${jost.variable} ${jostBody.variable}`}
data-scroll-behavior="smooth"
suppressHydrationWarning suppressHydrationWarning
> >
<head> <head>
@@ -86,7 +90,15 @@ export default function RootLayout({
/> />
</head> </head>
<body className="bg-[#E8DCF2] text-on-surface font-body antialiased" suppressHydrationWarning> <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> </body>
</html> </html>
); );

View File

@@ -5,6 +5,9 @@ import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow";
import { FAQAccordion } from "@/components/ui/FAQAccordion"; 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"; import type { Metadata } from "next";
export const metadata: Metadata = { export const metadata: Metadata = {
@@ -226,21 +229,23 @@ export default function MerchantsPage() {
{/* ─── Merchant Benefits Grid ─── */} {/* ─── Merchant Benefits Grid ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-white border-b border-[#6330D6]/10"> <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="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"> <AnimatedSection>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full"> <div className="text-center mb-10 md:mb-12">
What You Get <span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
</span> What You Get
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight"> </span>
Everything Your Store Needs to Grow <h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
</h2> Everything Your Store Needs to Grow
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed"> </h2>
Nearle gives your neighbourhood store the same tools that big online retailers have without the complexity. <p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
</p> Nearle gives your neighbourhood store the same tools that big online retailers have without the complexity.
</div> </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) => ( {BENEFITS.map((benefit) => (
<div <AnimatedCard
key={benefit.title} 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" 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> <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> <p className="mt-1.5 text-xs md:text-sm text-[#5E5866] leading-relaxed">{benefit.desc}</p>
</div> </div>
</div> </AnimatedCard>
))} ))}
</div> </AnimatedGrid>
</div> </div>
</section> </section>
{/* ─── How It Works ─── */} {/* ─── How It Works ─── */}
<section id="how-it-works" className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10"> <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="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"> <AnimatedSection>
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full"> <div className="text-center mb-10 md:mb-12">
Simple Steps <span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
</span> Simple Steps
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight"> </span>
Up and Running in 24 Hours <h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-3 tracking-tight">
</h2> Up and Running in 24 Hours
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed"> </h2>
From registration to your first order, we&apos;ve made the process as simple as possible. <p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
</p> From registration to your first order, we&apos;ve made the process as simple as possible.
</div> </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) => ( {HOW_STEPS.map((step) => (
<article <AnimatedCard
key={step.num} 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)]" 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} {step.title}
</h3> </h3>
<p className="text-sm text-[#5E5866] leading-relaxed">{step.desc}</p> <p className="text-sm text-[#5E5866] leading-relaxed">{step.desc}</p>
</article> </AnimatedCard>
))} ))}
</div> </AnimatedGrid>
</div> </div>
</section> </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"> <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="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 className="grid grid-cols-1 lg:grid-cols-[1fr_1.1fr] gap-10 lg:gap-16 items-start">
<div> <AnimatedSection>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full"> <div>
Why Nearle <span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
</span> Why Nearle
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight"> </span>
Your Neighbourhood&apos;s <h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
<br /> Your Neighbourhood&apos;s
<span className="text-[#6330D6]">Best Business Partner</span> <br />
</h2> <span className="text-[#6330D6]">Best Business Partner</span>
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-8 max-w-lg"> </h2>
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 className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-8 max-w-lg">
</p> 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 */} {/* Stats row */}
<div className="grid grid-cols-3 gap-5"> <div className="grid grid-cols-3 gap-5">
{[ {[
{ stat: "2km", label: "Hyperlocal Coverage" }, { stat: "2km", label: "Hyperlocal Coverage" },
{ stat: "24h", label: "Time to Go Live" }, { stat: "24h", label: "Time to Go Live" },
{ stat: "Fair", label: "Commission Model" }, { stat: "Fair", label: "Commission Model" },
].map((s) => ( ].map((s) => (
<div key={s.label} className="text-center p-4 rounded-2xl bg-[#6330D6]/5 border border-[#6330D6]/8"> <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-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 className="text-[10px] md:text-xs font-bold text-[#5E5866] mt-1 leading-tight">{s.label}</div>
</div> </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) => ( {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"> <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} /> <MaterialIcon icon={reason.icon} size={20} />
</div> </div>
@@ -336,9 +345,9 @@ export default function MerchantsPage() {
<h4 className="text-sm font-black text-[#16001D] leading-tight">{reason.title}</h4> <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> <p className="mt-1 text-xs text-[#5E5866] leading-relaxed">{reason.desc}</p>
</div> </div>
</div> </AnimatedCard>
))} ))}
</div> </AnimatedGrid>
</div> </div>
</div> </div>
</section> </section>
@@ -346,53 +355,55 @@ export default function MerchantsPage() {
{/* ─── Success Story ─── */} {/* ─── Success Story ─── */}
<section className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10"> <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="px-6 md:px-12 lg:px-16 xl:px-20 max-w-[1600px] mx-auto">
<div className="text-center mb-8"> <AnimatedSection>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full"> <div className="text-center mb-8">
Merchant Story <span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
</span> Merchant Story
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 tracking-tight"> </span>
Real Results from Real Stores <h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 tracking-tight">
</h2> Real Results from Real Stores
</div> </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="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="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 className="relative z-10 grid grid-cols-1 lg:grid-cols-[1fr_1.5fr] gap-8 lg:gap-12 items-center">
<div> <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"> <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} /> <MaterialIcon icon="store" size={14} />
Success Story 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> </div>
<div> <blockquote className="text-xl md:text-2xl font-black text-[#16001D] leading-snug mb-6">
<div className="text-sm font-black text-[#16001D]">Ramesh Kumar</div> &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;
<div className="text-xs text-[#5E5866] font-semibold">R.K. Provision Store, R.S. Puram, Coimbatore</div> </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>
</div>
<div className="grid grid-cols-2 gap-4"> <AnimatedGrid className="grid grid-cols-2 gap-4">
{[ {[
{ value: "2.1×", label: "Daily Orders Growth", icon: "trending_up", color: "text-[#006D37]" }, { 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: "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: "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" }, { value: "4.9 ★", label: "Average Store Rating", icon: "star", color: "text-amber-500" },
].map((m) => ( ].map((m) => (
<div key={m.label} className="rounded-2xl bg-white border border-[#6330D6]/8 p-5 text-center shadow-sm"> <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`} /> <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-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 className="text-xs text-[#5E5866] font-semibold mt-1 leading-tight">{m.label}</div>
</div> </AnimatedCard>
))} ))}
</AnimatedGrid>
</div> </div>
</div> </div>
</div> </AnimatedSection>
</div> </div>
</section> </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"> <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="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="grid grid-cols-1 lg:grid-cols-[360px_1fr] gap-10 lg:gap-16 items-start">
<div className="lg:sticky lg:top-28"> <AnimatedSection>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full"> <div className="lg:sticky lg:top-28">
FAQs <span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
</span> FAQs
<h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight"> </span>
Questions Merchants Ask <h2 className="font-display text-3xl md:text-4xl font-black text-[#16001D] mt-4 mb-4 tracking-tight leading-tight">
</h2> Questions Merchants Ask
<p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6"> </h2>
Everything you need to know before joining Nearle. Still have questions? Our team is a call away. <p className="text-sm md:text-base text-[#5E5866] leading-relaxed mb-6">
</p> Everything you need to know before joining Nearle. Still have questions? Our team is a call away.
<Link </p>
href="/contact" <Link
className="inline-flex items-center gap-2 text-sm font-black text-[#6330D6] hover:text-[#5225b8] transition-colors" 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} /> <span>Talk to our team</span>
</Link> <MaterialIcon icon="arrow_forward" size={16} />
</div> </Link>
</div>
</AnimatedSection>
<FAQAccordion items={FAQ_ITEMS} /> <FAQAccordion items={FAQ_ITEMS} />
</div> </div>
@@ -425,42 +438,18 @@ export default function MerchantsPage() {
</section> </section>
{/* ─── CTA Banner ─── */} {/* ─── 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"> <CTABanner
<div className="w-full overflow-hidden rounded-[28px] bg-gradient-to-br from-[#1A0322] via-[#2B0040] to-[#0A000D] text-white"> badge={{ icon: "storefront", label: "Ready to Grow?" }}
<div className="px-6 py-12 md:px-10 md:py-14 lg:px-14 xl:py-12 text-center relative"> heading="Become a Merchant Partner"
<div className="absolute inset-0 bg-[radial-gradient(circle_at_70%_20%,rgba(99,48,214,0.18),transparent_50%)] pointer-events-none" /> body="Join neighbourhood stores across Coimbatore already selling online with Nearle. Registration takes under 10 minutes."
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_80%,rgba(0,109,55,0.10),transparent_45%)] pointer-events-none" /> primary={{ href: "/contact", icon: "bolt", label: "Register Your Store" }}
<div className="relative z-10 max-w-2xl mx-auto"> secondary={{ href: "/contact", icon: "phone", label: "Talk to Our Team" }}
<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"> decorations={[
<MaterialIcon icon="storefront" size={14} /> "circle at 70% 20%, rgba(99,48,214,0.18), transparent 50%",
Ready to Grow? "circle at 30% 80%, rgba(0,109,55,0.10), transparent 45%",
</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>
</PremiumPageShell> </PremiumPageShell>
); );
} }

View File

@@ -3,8 +3,9 @@ import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; import { PremiumPageShell } from "@/components/layout/PremiumPageShell";
import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; import { PeopleHero } from "@/components/heroes/PeopleHero";
import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; import { AnimatedSection } from "@/components/motion/AnimatedSection";
import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid";
import type { Metadata } from "next"; import type { Metadata } from "next";
export const metadata: Metadata = { 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() { export default function PeoplePage() {
const collageCards = [
{ text: "Discover Nearby", icon: "explore" },
{ text: "Save Favourites", icon: "favorite" },
{ text: "Order Anytime", icon: "shopping_bag" }
];
return ( return (
<PremiumPageShell> <PremiumPageShell>
{/* ─── Hero Section ─── */} {/* ─── 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]"> <PeopleHero />
{/* 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>
{/* ─── Bento Grid of Offerings ─── */} {/* ─── 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"> <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"> <span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
What we offer What we offer
@@ -250,25 +183,28 @@ export default function PeoplePage() {
</article> </article>
))} ))}
</div> </div>
</section> </section>
</AnimatedSection>
{/* ─── Localized Stats Block ─── */} {/* ─── 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"> <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"> <AnimatedSection>
<span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full"> <div className="mb-10 xl:mb-8">
Nearle Network <span className="text-xs text-[#6330D6] uppercase tracking-widest font-black bg-[#6330D6]/5 px-3.5 py-1 rounded-full">
</span> Nearle Network
<h2 className="font-display text-2xl font-black text-[#16001D] mt-4"> </span>
The Neighbourhood App <h2 className="font-display text-2xl font-black text-[#16001D] mt-4">
</h2> The Neighbourhood App
<p className="text-xs md:text-sm text-[#5E5866] mt-2"> </h2>
Bringing local stores to your doorstep instantly. <p className="text-xs md:text-sm text-[#5E5866] mt-2">
</p> Bringing local stores to your doorstep instantly.
</div> </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) => ( {LOCAL_STATS.map((s) => (
<div <AnimatedCard
key={s.label} 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" 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"> <p className="text-[10px] font-bold text-[#5E5866] mt-1.5 uppercase tracking-wider">
{s.label} {s.label}
</p> </p>
</div> </AnimatedCard>
))} ))}
</div> </AnimatedGrid>
</section> </section>
{/* ─── Ready to Download 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 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"> <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 */} {/* Subtle glow layers */}
@@ -531,6 +468,7 @@ export default function PeoplePage() {
</div> </div>
</section> </section>
</section> </section>
</AnimatedSection>
</PremiumPageShell> </PremiumPageShell>
); );
} }

View File

@@ -1,7 +1,9 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { fadeUp, scaleIn, staggerContainer, staggerSlow, viewport } from "@/lib/animations";
// ─── Feature SVG Illustrations ─────────────────────────────────────────────── // ─── Feature SVG Illustrations ───────────────────────────────────────────────
// Flat vector style · Nearle brand palette · no text inside SVGs // Flat vector style · Nearle brand palette · no text inside SVGs
@@ -428,46 +430,50 @@ export function BusinessFeaturesGrid() {
> >
{/* ── LEFT — clean text feature grid, no illustrations ── */} {/* ── LEFT — clean text feature grid, no illustrations ── */}
<div> <motion.div
{/* Badge */} variants={staggerContainer}
<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]"> initial="hidden"
Our features whileInView="visible"
</span> 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 */} {/* Feature list */}
<h2 <motion.div
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
className="grid grid-cols-1 md:grid-cols-2" className="grid grid-cols-1 md:grid-cols-2"
style={{ columnGap: 48, rowGap: 32 }} style={{ columnGap: 48, rowGap: 32 }}
variants={staggerSlow}
> >
{FEATURES.map((feat) => ( {FEATURES.map((feat) => (
<div <motion.div
key={feat.title} key={feat.title}
variants={fadeUp}
className="grid items-start" className="grid items-start"
style={{ gridTemplateColumns: "30px 1fr", columnGap: 16 }} style={{ gridTemplateColumns: "30px 1fr", columnGap: 16 }}
> >
{/* Green check icon */}
<div <div
className="rounded-full bg-[#DCFCE7] flex items-center justify-center shrink-0" className="rounded-full bg-[#DCFCE7] flex items-center justify-center shrink-0"
style={{ width: 30, height: 30, marginTop: 3 }} 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"/> <path d="M3.5 8.5L6.5 11.5L12.5 5" stroke="#16A34A" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg> </svg>
</div> </div>
{/* Title + description */}
<div className="min-w-0"> <div className="min-w-0">
<h4 <h4
className="font-display font-black text-[#16001D]" className="font-display font-black text-[#16001D]"
@@ -489,30 +493,33 @@ export function BusinessFeaturesGrid() {
{feat.descr} {feat.descr}
</p> </p>
</div> </div>
</div> </motion.div>
))} ))}
</div> </motion.div>
</div> </motion.div>
{/* ── RIGHT — 4 cards + illustration strip below ─────── */} {/* ── RIGHT — 2×2 feature benefit cards ─────── */}
<div className="self-start flex flex-col" style={{ gap: 28 }}> <motion.div
className="self-start flex flex-col"
{/* 2×2 feature benefit cards */} style={{ gap: 28 }}
variants={staggerSlow}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
<div className="grid grid-cols-2" style={{ gap: 28 }}> <div className="grid grid-cols-2" style={{ gap: 28 }}>
{CARDS.map((card) => { {CARDS.map((card) => {
const { CardIllus } = card; const { CardIllus } = card;
return ( return (
<div <motion.div
key={card.title} 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}`} 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 }} 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}`}> <div className={`w-9 h-9 rounded-full flex items-center justify-center shrink-0 ${card.iconBg}`}>
<MaterialIcon icon={card.iconName} size={18} /> <MaterialIcon icon={card.iconName} size={18} />
</div> </div>
{/* Illustration */}
<div <div
className="flex-1 flex items-center justify-center overflow-hidden" className="flex-1 flex items-center justify-center overflow-hidden"
style={{ margin: "8px 0" }} style={{ margin: "8px 0" }}
@@ -521,8 +528,6 @@ export function BusinessFeaturesGrid() {
<CardIllus /> <CardIllus />
</div> </div>
</div> </div>
{/* Bottom label */}
<div className="mt-auto shrink-0"> <div className="mt-auto shrink-0">
<p <p
className={`font-display font-black leading-none tracking-tight ${card.titleColor}`} className={`font-display font-black leading-none tracking-tight ${card.titleColor}`}
@@ -537,13 +542,11 @@ export function BusinessFeaturesGrid() {
{card.subtitle} {card.subtitle}
</p> </p>
</div> </div>
</div> </motion.div>
); );
})} })}
</div> </div>
</motion.div>
</div>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,27 @@
"use client"; "use client";
import React, { useState } from "react"; import React, { useState } from "react";
import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; 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 = { type FormData = {
name: string; name: string;
email: 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 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="absolute bottom-0 left-0 w-32 h-32 bg-secondary/5 rounded-full blur-2xl" />
<div className="relative z-10"> <motion.div
<span className="text-label-sm font-black text-primary uppercase tracking-widest"> 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 Write to us
</span> </motion.span>
<h2 className="font-display text-2xl md:text-3xl font-black text-brand-dark mt-2 mb-8"> <motion.h2 variants={fieldVariant} className="font-display text-2xl md:text-3xl font-black text-brand-dark mt-2 mb-8">
Send Message Send Message
</h2> </motion.h2>
<form onSubmit={handleSubmit} className="flex flex-col gap-5"> <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 */} {/* Name field */}
<div className="flex flex-col"> <div className="flex flex-col">
<label htmlFor="field-name" className="text-label-sm text-on-surface-variant font-bold mb-2"> <label htmlFor="field-name" className="text-label-sm text-on-surface-variant font-bold mb-2">
@@ -171,10 +195,10 @@ export function ContactForm() {
</span> </span>
)} )}
</div> </div>
</div> </motion.div>
{/* Message field */} {/* 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"> <label htmlFor="field-message" className="text-label-sm text-on-surface-variant font-bold mb-2">
Message Message
</label> </label>
@@ -202,28 +226,30 @@ export function ContactForm() {
{errors.message} {errors.message}
</span> </span>
)} )}
</div> </motion.div>
{/* Submit button */} {/* Submit button — appears last */}
<button <motion.div variants={submitVariant}>
type="submit" <button
disabled={isSubmitting} type="submit"
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" 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 ? ( >
<> {isSubmitting ? (
<span className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" /> <>
<span>Sending Message...</span> <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> <MaterialIcon icon="send" size={18} />
</> <span>Submit Message</span>
)} </>
</button> )}
</button>
</motion.div>
</form> </form>
</div> </motion.div>
</div> </div>
{/* ─── Success Toast Notification ─── */} {/* ─── 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"> <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="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="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" /> <motion.div
<div className="pointer-events-none absolute -right-20 bottom-10 h-64 w-64 rounded-full bg-secondary/5 blur-3xl" /> 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 <div className="relative grid gap-6 grid-cols-1 md:gap-7
xl:grid-cols-[420px_1fr] xl:gap-8 xl:items-start"> xl:grid-cols-[420px_1fr] xl:gap-8 xl:items-start">

View File

@@ -2,14 +2,11 @@
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { motion } from "framer-motion"; import { motion, useReducedMotion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { import { viewport } from "@/lib/animations";
fadeUp,
slideInRight, const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
staggerContainer,
viewport,
} from "@/lib/animations";
const PROOF_ITEMS = [ const PROOF_ITEMS = [
{ label: "Verified", text: "Local Stores", icon: "store" }, { label: "Verified", text: "Local Stores", icon: "store" },
@@ -18,16 +15,86 @@ const PROOF_ITEMS = [
{ label: "Trusted", text: "Local Platform", icon: "star" }, { 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() { export function Hero() {
const reducedMotion = useReducedMotion();
return ( return (
<section className="relative overflow-hidden bg-gradient-to-b from-[#FAF8FC] to-[#FFFFFF] select-none w-full"> <section className="relative overflow-hidden bg-gradient-to-b from-[#FAF8FC] to-[#FFFFFF] select-none w-full">
{/* ─── DECORATIONS (BEHIND CONTENT) ─── */} {/* ─── DECORATIONS (BEHIND CONTENT) ─── */}
{/* 1. Large pale lavender partial circle (top-left) */} {/* 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) */} {/* 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) */} {/* 3. Striped purple circle (lower-middle collage area) */}
<div <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> <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) */} {/* 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 ─── */} {/* ─── 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"> <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 */} {/* LEFT COLUMN: Editorial Typography & Copy */}
<motion.div <motion.div
className="flex flex-col text-left max-w-xl z-20 relative" className="flex flex-col text-left max-w-xl z-20 relative"
variants={staggerContainer} variants={heroStagger}
initial="hidden" initial="hidden"
animate="visible" animate="visible"
> >
{/* Badge */}
<motion.span <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" 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 Welcome to Nearle
</motion.span> </motion.span>
<motion.h1 {/* H1: line-by-line mask reveal */}
variants={fadeUp} <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]">
className="font-display text-4xl sm:text-5xl lg:text-[68px] xl:text-[76px] font-black text-[#16001D] leading-[0.95] tracking-[-0.05em]" <div className="overflow-hidden pb-1">
> <motion.div variants={lineReveal} className="leading-[0.95]">
Everything Local. Everything Local.
<br /> </motion.div>
Everything <span className="text-[#6D28D9]">Near.</span> </div>
</motion.h1> <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 <motion.p
variants={fadeUp} variants={descVariant}
className="max-w-[480px] text-base md:text-lg text-[#6D6172] leading-relaxed font-bold mt-6" 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. Discover verified local stores, exclusive offers, fast delivery, and a better way to shop within your neighbourhood.
</motion.p> </motion.p>
<motion.div {/* CTAs: primary scales in, secondary slides from left */}
variants={fadeUp} <div className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto">
className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto" <motion.div
> variants={primaryCtaVariant}
<Link whileHover={{ scale: 1.03, transition: { duration: 0.2, ease: EASE } }}
href="/people" whileTap={{ scale: 0.97, transition: { duration: 0.1 } }}
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]"
> >
Explore Stores <Link
</Link> href="/people"
<Link 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"
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]" 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
</Link> href="/people#download-section"
</motion.div> 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> </motion.div>
{/* RIGHT COLUMN: Creative Overlapping Strips Collage */} {/* RIGHT COLUMN: Individual image + center card animations */}
<motion.div <div className="w-full z-20">
className="w-full z-20"
variants={slideInRight}
initial="hidden"
animate="visible"
>
{/* DESKTOP LAYOUT (lg and above) */} {/* DESKTOP LAYOUT (lg and above) */}
<div className="hidden lg:block relative h-[500px] lg:h-[560px] w-full"> <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 <Image
src="/images/cat-daily-essentials.png" src="/images/cat-daily-essentials.png"
alt="Local grocery products" alt="Local grocery products"
@@ -126,37 +217,63 @@ export function Hero() {
className="object-cover" className="object-cover"
sizes="170px" sizes="170px"
/> />
</div> </motion.div>
{/* middle image: central logo panel absolute left-[38%] top-[8%] */} {/* Center card: scales up from below, then gently bobs */}
<div className="absolute left-[38%] top-[8%] z-20 transition-transform duration-300 hover:scale-[1.02]"> <motion.div
<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"> className="absolute left-[38%] top-[8%] z-20"
<Image variants={centerCardReveal}
src="/nearlenewlogo.png" initial="hidden"
alt="Central logo panel" animate="visible"
width={160} transition={{ delay: 0.15 }}
height={160} >
className="object-contain max-h-[140px] w-auto animate-[pulse_6s_infinite]" <motion.div
priority animate={!reducedMotion ? { y: [0, -10, 0] } : {}}
/> transition={{ delay: 0.9, duration: 4, repeat: Infinity, ease: "easeInOut" }}
</div> whileHover={{ scale: 1.02, transition: { duration: 0.25, ease: EASE } }}
</div> >
<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] */} {/* Right image: slides in from right */}
<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]"> <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 <Image
src="/nearle people.png" src="/nearle people.png"
alt="Local customer shopping" alt="Local customer shopping"
fill fill
priority
className="object-cover" className="object-cover"
sizes="180px" sizes="180px"
/> />
</div> </motion.div>
</div> </div>
{/* MOBILE/TABLET COLLAGE (below lg) */} {/* 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="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 <Image
src="/images/cat-daily-essentials.png" src="/images/cat-daily-essentials.png"
alt="Grocery products" alt="Grocery products"
@@ -164,18 +281,37 @@ export function Hero() {
className="object-cover" className="object-cover"
sizes="95px" sizes="95px"
/> />
</div> </motion.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 <motion.div
src="/nearlenewlogo.png" 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"
alt="Central logo panel" variants={centerCardReveal}
width={100} initial="hidden"
height={100} animate="visible"
className="object-contain max-h-[90px] w-auto animate-[pulse_6s_infinite]" transition={{ delay: 0.15 }}
priority >
/> <motion.div
</div> animate={!reducedMotion ? { y: [0, -6, 0] } : {}}
<div className="relative w-[105px] h-[220px] rounded-2xl overflow-hidden border border-white shadow-md z-10"> 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 <Image
src="/nearle people.png" src="/nearle people.png"
alt="Customer" alt="Customer"
@@ -183,10 +319,10 @@ export function Hero() {
className="object-cover" className="object-cover"
sizes="105px" sizes="105px"
/> />
</div> </motion.div>
</div> </div>
</motion.div> </div>
</div> </div>
@@ -194,14 +330,13 @@ export function Hero() {
<div className="border-t border-[#EEE7F4]/80 bg-white"> <div className="border-t border-[#EEE7F4]/80 bg-white">
<motion.div <motion.div
className="page-container py-8 grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-4 relative z-20" 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" initial="hidden"
whileInView="visible" whileInView="visible"
viewport={viewport} viewport={viewport}
> >
{PROOF_ITEMS.map((item, idx) => ( {PROOF_ITEMS.map((item, idx) => (
<motion.div key={item.label} variants={fadeUp} className="flex items-center gap-4 text-left md:justify-center relative"> <motion.div key={item.label} variants={statItem} className="flex items-center gap-4 text-left md:justify-center relative">
{/* Subtle vertical separator for desktop, except for the first item */}
{idx > 0 && ( {idx > 0 && (
<div className="hidden md:block absolute left-0 top-1/2 -translate-y-1/2 w-[1px] h-10 bg-[#EEE7F4]" /> <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>{" "} <span className="font-black text-primary">Yours</span>{" "}
establish a local online presence and empower neighbourhood customers to discover and interact with you directly. establish a local online presence and empower neighbourhood customers to discover and interact with you directly.
</p> </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} /> <MaterialIcon icon="forum" size={20} />
Talk to an Expert Talk to an Expert
</button> </motion.button>
</motion.div> </motion.div>
<motion.div <motion.div

View File

@@ -6,7 +6,7 @@ import { motion } from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { fadeUp, fadeIn, staggerContainer, viewport } from "@/lib/animations"; import { fadeUp, fadeIn, staggerContainer, viewport } from "@/lib/animations";
const MotionLink = motion(Link); const MotionLink = motion.create(Link);
const CATEGORIES = [ const CATEGORIES = [
{ {
@@ -106,6 +106,8 @@ export function ServiceCategories() {
href="/people" href="/people"
variants={fadeIn} 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" 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 */} {/* Image Container */}
<div className="relative aspect-[4/3] w-full overflow-hidden bg-surface-container/30"> <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 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)]"> <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 */} {/* 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" /> <motion.div
<div className="absolute left-[-10%] bottom-[-10%] w-[50%] h-[50%] rounded-full bg-primary/10 blur-[120px] pointer-events-none" /> 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"> <div className="w-full relative z-10">
@@ -124,7 +132,7 @@ export function StoreQRSection() {
</div> </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="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 Scan to Shop
</span> </span>
</motion.div> </motion.div>
@@ -189,7 +197,7 @@ export function StoreQRSection() {
</div> </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="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 Scan to Shop
</span> </span>
</motion.div> </motion.div>

View File

@@ -81,7 +81,7 @@ export function WhyNearle() {
</p> </p>
</motion.div> </motion.div>
{/* Cards Grid — fadeIn preserves .premium-card:hover CSS transform */} {/* Cards Grid */}
<motion.div <motion.div
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 lg:gap-7" className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 lg:gap-7"
variants={staggerContainer} variants={staggerContainer}
@@ -93,7 +93,9 @@ export function WhyNearle() {
<motion.div <motion.div
key={card.title} key={card.title}
variants={fadeIn} 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}`}> <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} /> <MaterialIcon icon={card.icon} size={27} />

View File

@@ -1,5 +1,10 @@
"use client";
import Image from "next/image"; import Image from "next/image";
import { motion } from "framer-motion";
import { GlassInfoCard } from "../ui/GlassInfoCard"; import { GlassInfoCard } from "../ui/GlassInfoCard";
import { scaleIn, staggerContainer, viewport } from "@/lib/animations";
import type { Variants } from "framer-motion";
type GlassCardItem = { type GlassCardItem = {
text: string; text: string;
@@ -12,6 +17,40 @@ type HeroVisualCollageProps = {
rightImage?: string; rightImage?: string;
glassCards: GlassCardItem[]; glassCards: GlassCardItem[];
isPhoneCenter?: boolean; 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({ export function HeroVisualCollage({
@@ -20,14 +59,14 @@ export function HeroVisualCollage({
rightImage, rightImage,
glassCards, glassCards,
isPhoneCenter = true, isPhoneCenter = true,
cardVariant,
}: HeroVisualCollageProps) { }: HeroVisualCollageProps) {
const activeCardVariant = cardVariant ?? glasscardVariant;
return ( 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"> <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 */} {/* Decorative backgrounds */}
{/* 1. Soft purple circle */}
<div className="absolute w-[240px] h-[240px] rounded-full bg-[#7C3AED]/10 blur-[60px] -z-10" /> <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"> <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"> <svg className="w-full h-full" fill="currentColor">
<pattern id="collage-dots" x="0" y="0" width="16" height="16" patternUnits="userSpaceOnUse"> <pattern id="collage-dots" x="0" y="0" width="16" height="16" patternUnits="userSpaceOnUse">
@@ -37,13 +76,19 @@ export function HeroVisualCollage({
</svg> </svg>
</div> </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 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> <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 && ( {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 <Image
src={leftImage} src={leftImage}
alt="Collage left asset" alt="Collage left asset"
@@ -51,12 +96,19 @@ export function HeroVisualCollage({
className="object-cover" className="object-cover"
sizes="(max-width: 640px) 100px, 130px" sizes="(max-width: 640px) 100px, 130px"
/> />
</div> </motion.div>
)} )}
{/* RIGHT IMAGE PANEL */} {/* RIGHT IMAGE PANEL */}
{rightImage && ( {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 <Image
src={rightImage} src={rightImage}
alt="Collage right asset" alt="Collage right asset"
@@ -64,13 +116,18 @@ export function HeroVisualCollage({
className="object-cover" className="object-cover"
sizes="(max-width: 640px) 100px, 130px" sizes="(max-width: 640px) 100px, 130px"
/> />
</div> </motion.div>
)} )}
{/* CENTER COMPONENT */} {/* CENTER COMPONENT */}
<div className="relative z-20"> <motion.div
className="relative z-20"
variants={scaleIn}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
{isPhoneCenter ? ( {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"> <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 <Image
src={centerImage} src={centerImage}
@@ -82,56 +139,70 @@ export function HeroVisualCollage({
/> />
</div> </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"> <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 <Image
src={centerImage} src={centerImage}
alt="Central logo panel" alt="Central logo panel"
width={160} width={160}
height={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 priority
/> />
</div> </div>
)} )}
</div> </motion.div>
{/* FLOATING GLASS CARDS (Positioned Absolute, scaled down on mobile) */} {/* FLOATING GLASS CARDS — stagger in after center */}
{glassCards.length > 0 && ( {glassCards.length > 0 && (
<div className="absolute inset-0 z-30 pointer-events-none"> <motion.div
{/* Card 1: Top Right */} className="absolute inset-0 z-30 pointer-events-none"
variants={glassCardStagger}
initial="hidden"
whileInView="visible"
viewport={viewport}
>
{glassCards[0] && ( {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 <GlassInfoCard
text={glassCards[0].text} text={glassCards[0].text}
icon={glassCards[0].icon} icon={glassCards[0].icon}
className="scale-90 sm:scale-100" className="scale-90 sm:scale-100"
/> />
</div> </motion.div>
)} )}
{/* Card 2: Bottom Left */}
{glassCards[1] && ( {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 <GlassInfoCard
text={glassCards[1].text} text={glassCards[1].text}
icon={glassCards[1].icon} icon={glassCards[1].icon}
className="scale-90 sm:scale-100" className="scale-90 sm:scale-100"
/> />
</div> </motion.div>
)} )}
{/* Card 3: Mid Left/Right */}
{glassCards[2] && ( {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 <GlassInfoCard
text={glassCards[2].text} text={glassCards[2].text}
icon={glassCards[2].icon} icon={glassCards[2].icon}
className="scale-90 sm:scale-100" className="scale-90 sm:scale-100"
/> />
</div> </motion.div>
)} )}
</div> </motion.div>
)} )}
</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 Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import React from "react"; import React from "react";
import { motion } from "framer-motion";
import { FaFacebookF, FaInstagram, FaLinkedinIn } from "react-icons/fa"; import { FaFacebookF, FaInstagram, FaLinkedinIn } from "react-icons/fa";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; 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 = [ const COMPANY = [
{ label: "About Us", href: "/about" }, { label: "About Us", href: "/about" },
{ label: "Careers", href: "#" }, { label: "Careers", href: "#" },
// { label: "Press & News", href: "#" },
{ label: "Help Center", href: "#" }, { label: "Help Center", href: "#" },
]; ];
@@ -17,26 +20,87 @@ const PARTNERSHIPS = [
{ label: "Merchant Partner", href: "/merchants" }, { label: "Merchant Partner", href: "/merchants" },
{ label: "Delivery Partner", href: "/delivery-partners" }, { label: "Delivery Partner", href: "/delivery-partners" },
{ label: "Direct to Customer Brands", href: "/brands" }, { label: "Direct to Customer Brands", href: "/brands" },
// { label: "Local Real Estate", href: "#" },
]; ];
const LEGAL = [ const LEGAL = [
{ label: "Privacy Policy", href: "/privacy" }, { label: "Privacy Policy", href: "/privacy" },
{ label: "Terms of Service", href: "/terms" }, { label: "Terms of Service", href: "/terms" },
{ label: "Cookie Policy", href: "/cookies" }, { label: "Cookie Policy", href: "/cookies" },
// { label: "Sitemap", href: "#" },
]; ];
const socialButtonClass = 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() { export function PremiumFooter() {
return ( 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"> <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="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"> <Link href="/" className="inline-block w-fit">
<Image <Image
src="/logo-light.png" src="/logo-light.png"
@@ -52,100 +116,81 @@ export function PremiumFooter() {
communities and verified local businesses within a 2km radius. communities and verified local businesses within a 2km radius.
</p> </p>
{/* Social icons with scale spring on hover */}
<div className="mt-1 flex gap-3 xl:mt-2 xl:gap-4"> <div className="mt-1 flex gap-3 xl:mt-2 xl:gap-4">
<a <motion.a
href="https://www.facebook.com/nearledigital" href="https://www.facebook.com/nearledigital"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
aria-label="Facebook" aria-label="Facebook"
className={`${socialButtonClass} hover:bg-[#1877F2]`} className={`${socialButtonClass} hover:bg-[#1877F2]`}
whileHover={{ scale: 1.12 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
> >
<FaFacebookF size={20} /> <FaFacebookF size={20} />
</a> </motion.a>
<a <motion.a
href="https://www.instagram.com/nearledigital/" href="https://www.instagram.com/nearledigital/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
aria-label="Instagram" aria-label="Instagram"
className={`${socialButtonClass} hover:bg-[#E4405F]`} className={`${socialButtonClass} hover:bg-[#E4405F]`}
whileHover={{ scale: 1.12 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
> >
<FaInstagram size={21} /> <FaInstagram size={21} />
</a> </motion.a>
<a <motion.a
href="https://www.linkedin.com/company/nearledigital/" href="https://www.linkedin.com/company/nearledigital/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
aria-label="LinkedIn" aria-label="LinkedIn"
className={`${socialButtonClass} hover:bg-[#0A66C2]`} className={`${socialButtonClass} hover:bg-[#0A66C2]`}
whileHover={{ scale: 1.12 }}
whileTap={{ scale: 0.9 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
> >
<FaLinkedinIn size={20} /> <FaLinkedinIn size={20} />
</a> </motion.a>
</div> </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]"> <h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]">
Company Company
</h5> </h5>
<FooterLinkList links={COMPANY} />
</motion.div>
<ul className="flex flex-col gap-4 xl:gap-5"> {/* Nearle links */}
{COMPANY.map((link) => ( <motion.div variants={linkColumnVariant} className="flex flex-col">
<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">
<h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]"> <h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]">
Nearle Nearle
</h5> </h5>
<FooterLinkList links={PARTNERSHIPS} />
</motion.div>
<ul className="flex flex-col gap-4 xl:gap-5"> {/* Support links */}
{PARTNERSHIPS.map((link) => ( <motion.div variants={linkColumnVariant} className="flex flex-col">
<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">
<h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]"> <h5 className="mb-5 text-xs font-extrabold uppercase tracking-wider text-white/40 md:text-sm xl:mb-7 xl:text-[17px]">
Support Support
</h5> </h5>
<FooterLinkList links={LEGAL} />
<ul className="flex flex-col gap-4 xl:gap-5"> </motion.div>
{LEGAL.map((link) => ( </motion.div>
<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>
<div className="my-8 w-full border-t border-white/10 xl:my-10" /> <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="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"> <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} /> <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"> <h6 className="text-sm font-black text-white md:text-base xl:text-lg">
Nearle Nearle
</h6> </h6>
<p className="mt-0.5 text-xs leading-normal text-white/60 md:text-sm xl:mt-1 xl:text-base"> <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, 424, Diwan Bahadur Rd, Opp. Sri Krishna Sweets, R.S. Puram,
Coimbatore, TN 641002 Coimbatore, TN 641002
@@ -181,11 +225,15 @@ export function PremiumFooter() {
</span> </span>
</a> </a>
</div> </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="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"> <p className="text-center md:text-left">
© {new Date().getFullYear()} Nearle. All rights reserved. © {new Date().getFullYear()} Nearle. All rights reserved.
</p> </p>
@@ -203,9 +251,9 @@ export function PremiumFooter() {
Help Center Help Center
</Link> </Link>
</div> </div>
</div> </motion.div>
</div> </div>
</footer> </motion.footer>
</section> </section>
); );
} }

View File

@@ -2,10 +2,18 @@
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { useState } from "react"; import { useState, useEffect } from "react";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import {
motion,
AnimatePresence,
useScroll,
useMotionValueEvent,
} from "framer-motion";
import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MaterialIcon } from "@/components/ui/MaterialIcon";
const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number];
const NAV_LINKS = [ const NAV_LINKS = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
{ label: "Nearle for Business", href: "/businesses" }, { label: "Nearle for Business", href: "/businesses" },
@@ -14,9 +22,44 @@ const NAV_LINKS = [
{ label: "Contact", href: "/contact" }, { 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() { export function PremiumNavbar() {
const [mobileOpen, setMobileOpen] = useState(false); const [mobileOpen, setMobileOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const pathname = usePathname(); 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) => { const isActiveLink = (href: string) => {
if (href === "/") return pathname === "/"; if (href === "/") return pathname === "/";
@@ -24,10 +67,18 @@ export function PremiumNavbar() {
}; };
return ( return (
// <header className="sticky top-0 z-50 w-full border-b border-white/10 bg-[#3D2D8F]/90 text-white backdrop-blur-xl"> <motion.header
<header className="sticky top-0 z-50 w-full bg-[#83429f] border-b border-white/10 text-white"> 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"> <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 <Link
href="/" href="/"
className="flex shrink-0 items-center transition-transform duration-200 hover:scale-[1.02] active:scale-95" 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> </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 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) => { {NAV_LINKS.map((link) => {
const isActive = isActiveLink(link.href); const isActive = isActiveLink(link.href);
return ( return (
<Link <motion.div
key={link.label} key={link.label}
href={link.href} className="relative py-2"
className={`relative whitespace-nowrap py-2 text-sm font-bold transition-colors duration-200 ${ initial="rest"
isActive whileHover="hover"
? "text-white" animate="rest"
: "text-white/70 hover:text-white"
}`}
> >
{link.label} <Link
href={link.href}
<span className={`whitespace-nowrap text-sm font-bold transition-colors duration-200 ${
className={`absolute bottom-0 left-1/2 h-0.5 -translate-x-1/2 rounded-full bg-white transition-all duration-300 ${ isActive ? "text-white" : "text-white/70 hover:text-white"
isActive ? "w-full" : "w-0"
}`} }`}
/> >
</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> </nav>
{/* Mobile Toggle - Right */} {/* Mobile Toggle */}
<button <button
type="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" 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-label={mobileOpen ? "Close menu" : "Open menu"}
aria-expanded={mobileOpen} aria-expanded={mobileOpen}
> >
@@ -82,39 +150,48 @@ export function PremiumNavbar() {
</button> </button>
</div> </div>
{/* Mobile Menu */} {/* Mobile Menu with enter/exit animation */}
<div <AnimatePresence>
className={`overflow-hidden border-t border-white/10 bg-[#30226F]/98 backdrop-blur-xl transition-all duration-300 lg:hidden ${ {mobileOpen && (
mobileOpen <motion.div
? "max-h-[500px] opacity-100" key="mobile-menu"
: "pointer-events-none max-h-0 opacity-0" variants={mobileMenuVariants}
}`} initial="hidden"
> animate="visible"
<nav className="mx-auto flex w-full max-w-[1600px] flex-col gap-1 px-6 py-5 md:px-10"> exit="exit"
{NAV_LINKS.map((link) => { className="overflow-hidden border-t border-white/10 bg-[#30226F]/98 backdrop-blur-xl lg:hidden"
const isActive = isActiveLink(link.href); >
<motion.nav
return ( className="mx-auto flex w-full max-w-[1600px] flex-col gap-1 px-6 py-5 md:px-10"
<Link variants={mobileStagger}
key={link.label} initial="hidden"
href={link.href} animate="visible"
onClick={() => setMobileOpen(false)} >
className={`flex items-center justify-between rounded-xl px-4 py-3 text-sm font-bold transition-all duration-200 ${ {NAV_LINKS.map((link) => {
isActive const isActive = isActiveLink(link.href);
? "bg-white/15 text-white" return (
: "text-white/70 hover:bg-white/10 hover:text-white" <motion.div key={link.label} variants={mobileItemVariants}>
}`} <Link
> href={link.href}
{link.label} onClick={() => setMobileOpen(false)}
className={`flex items-center justify-between rounded-xl px-4 py-3 text-sm font-bold transition-all duration-200 ${
{isActive && ( isActive
<span className="h-2 w-2 rounded-full bg-white" /> ? "bg-white/15 text-white"
)} : "text-white/70 hover:bg-white/10 hover:text-white"
</Link> }`}
); >
})} {link.label}
</nav> {isActive && (
</div> <span className="h-2 w-2 rounded-full bg-white" />
</header> )}
</Link>
</motion.div>
);
})}
</motion.nav>
</motion.div>
)}
</AnimatePresence>
</motion.header>
); );
} }

View File

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

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

View File

@@ -1,4 +1,8 @@
"use client";
import { motion } from "framer-motion";
import { MaterialIcon } from "./MaterialIcon"; import { MaterialIcon } from "./MaterialIcon";
import { fadeUp, staggerContainer, viewport } from "@/lib/animations";
type FeatureStatsItem = { type FeatureStatsItem = {
icon: string; icon: string;
@@ -13,11 +17,18 @@ type FeatureStatsRowProps = {
export function FeatureStatsRow({ items, className = "" }: FeatureStatsRowProps) { export function FeatureStatsRow({ items, className = "" }: FeatureStatsRowProps) {
return ( 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"> <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) => ( {items.map((item, index) => (
<div <motion.div
key={item.title} key={item.title}
variants={fadeUp}
className={`flex items-center gap-3.5 lg:flex-1 px-3 lg:px-8 text-left ${ className={`flex items-center gap-3.5 lg:flex-1 px-3 lg:px-8 text-left ${
index < items.length - 1 index < items.length - 1
? "lg:border-r lg:border-[#6330D6]/10" ? "lg:border-r lg:border-[#6330D6]/10"
@@ -37,9 +48,9 @@ export function FeatureStatsRow({ items, className = "" }: FeatureStatsRowProps)
</p> </p>
)} )}
</div> </div>
</div> </motion.div>
))} ))}
</div> </div>
</div> </motion.div>
); );
} }

View File

@@ -16,7 +16,7 @@ export function GlassInfoCard({ text, icon, className = "" }: GlassInfoCardProps
<MaterialIcon icon={icon} size={14} /> <MaterialIcon icon={icon} size={14} />
</div> </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"> <span className="text-sm font-extrabold text-[#16001D] whitespace-nowrap">
{text} {text}

View File

@@ -5,7 +5,7 @@ export const fadeUp: Variants = {
visible: { visible: {
opacity: 1, opacity: 1,
y: 0, 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 = { export const scaleIn: Variants = {
hidden: { opacity: 0, scale: 0.95 }, hidden: { opacity: 0, scale: 0.95 },
visible: { visible: {
opacity: 1, opacity: 1,
scale: 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: { visible: {
opacity: 1, opacity: 1,
x: 0, 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: { visible: {
opacity: 1, opacity: 1,
x: 0, 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 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 },
},
};