diff --git a/AGENTS.md b/AGENTS.md index 4d94105..b55974e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,13 +1,216 @@ +# Nearle Project Guide -# This is NOT the Next.js you know -This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. -# STRICT PROJECT-WIDE EXECUTION RULES (TOKEN SAVER) -Whenever you are assigned ANY task (bug fix, new feature, UI update, backend logic, etc.), you MUST follow these constraints strictly to prevent infinite loops and save compute resources: +## Project -1. For frontend UI, layout, responsive, styling, or screenshot-reference tasks, you MAY start the Next.js local development server using `npm run dev`. -2. For those frontend visual tasks, you MAY open and inspect `http://localhost:3000` locally. -3. For those frontend visual tasks, you MAY capture local screenshots for before/after comparison. -4. `npm run build` verifies compilation only. Do not claim a visual issue is fixed based only on build success. -5. For visual tasks, inspect localhost before reporting completion. -6. Provide the exact code fix, logic, or file modification directly based on your analysis of the provided files. -7. For frontend UI, layout, responsive, styling, and screenshot-reference tasks, the agent must inspect localhost before reporting completion. Screenshots are required only for major layout changes, responsive/mobile changes, hero/footer changes, or when explicitly requested by the user. For small isolated CSS spacing fixes, screenshots are optional. The human developer gives final approval only. \ No newline at end of file +Nearle is a premium hyperlocal commerce platform connecting + +- Customers +- Local Merchants +- Delivery Partners +- Brands + +The experience should feel: + +- Premium +- Modern +- Minimal +- Community-first +- Human +- Trustworthy + +Avoid generic SaaS designs. + +--- + +# Tech Stack + +- Next.js 16 App Router +- React +- TypeScript +- Tailwind CSS +- MaterialIcon component + +IMPORTANT + +This project uses the latest Next.js version. + +Before changing framework APIs, read + +node_modules/next/dist/docs/ + +Follow the latest conventions. + +--- + +# Design System + +Preserve the existing Nearle identity. + +Never change unless explicitly requested: + +- Brand colors +- Typography +- Border radius +- Layout hierarchy +- Section order +- Existing content +- Existing illustrations + +Always extend the current design instead of redesigning it. + +--- + +# UI Rules + +Always match existing Nearle pages. + +Reuse: + +- Hero layouts +- CTA buttons +- Card styles +- Section spacing +- Grid system +- Shadows +- Glass effects +- Gradients + +Avoid Bootstrap-style layouts. + +--- + +# Motion Rules + +Use: + +- Framer Motion +- CSS transitions + +Avoid: + +- Heavy GSAP +- Layout animations +- Flashy effects + +Animation style: + +- Apple +- Linear +- Stripe +- Vercel + +Use only: + +- opacity +- transform +- scale +- translate + +Respect prefers-reduced-motion. + +--- + +# Code Rules + +Never rewrite files unnecessarily. + +Only modify required code. + +Preserve formatting. + +Do not rename components unless necessary. + +Reuse existing components before creating new ones. + +Create reusable components when repeated more than twice. + +--- + +# Performance + +Maintain 60 FPS. + +Avoid unnecessary re-renders. + +Avoid layout shift. + +Prefer GPU-friendly animations. + +Optimize images. + +--- + +# SEO + +Every new page should include: + +- Metadata +- Title +- Description +- OpenGraph +- Semantic HTML + +--- + +# Content Rules + +Write content specifically for Nearle. + +Tone: + +- Friendly +- Premium +- Community-first +- Local +- Trustworthy + +Avoid AI-sounding or generic marketing copy. + +Prefer real examples involving: + +- neighbourhood stores +- customers +- delivery partners +- local commerce +- digital catalogues +- QR ordering + +--- + +# Execution Rules + +Frontend Tasks + +- You MAY run npm run dev. +- Inspect localhost before reporting visual fixes. +- Use screenshots only for major UI changes. + +Build + +- npm run build only verifies compilation. +- Never claim visual fixes based only on build success. + +--- + +# Workflow + +Before editing + +1. Read the target file. +2. Understand existing implementation. +3. Modify only the required code. +4. Avoid unrelated refactoring. +5. Keep code production ready. +6. Return concise summaries. + +--- + +# Response Rules + +Do not explain obvious code. + +Do not rewrite unchanged code. + +Do not introduce unnecessary abstractions. + +Focus only on the requested task. \ No newline at end of file diff --git a/desktop_screenshot.png b/desktop_screenshot.png index bce2f86..c639224 100644 Binary files a/desktop_screenshot.png and b/desktop_screenshot.png differ diff --git a/mobile_screenshot.png b/mobile_screenshot.png index a5891bd..b8088a5 100644 Binary files a/mobile_screenshot.png and b/mobile_screenshot.png differ diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index a3d2f1d..5f72ca7 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -3,8 +3,9 @@ import Image from "next/image"; import Link from "next/link"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; -import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; -import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; +import { AboutHero } from "@/components/heroes/AboutHero"; +import { AnimatedSection } from "@/components/motion/AnimatedSection"; +import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -48,93 +49,27 @@ const LOCAL_COUNTERS = [ { count: "Growing", label: "Neighbourhood Reach", icon: "location_city" } ]; -const HERO_STATS = [ - { title: "2km Coverage", icon: "explore", desc: "Hyperlocal radius" }, - { title: "Verified Local Stores", icon: "store", desc: "Growing local commerce" }, - { title: "Happy Customers", icon: "mood", desc: "Neighbourhood community" }, - { title: "Coimbatore & Beyond", icon: "location_city", desc: "Growing neighbourhood reach" } -]; export default function AboutPage() { - const collageCards = [ - { text: "Local First", icon: "store" }, - { text: "Trusted Network", icon: "verified" }, - { text: "Connected Communities", icon: "groups" } - ]; - return ( {/* ─── Hero Section ─── */} -
- {/* TWO-COLUMN GRID */} -
-
- - {/* Left Column: Copy & Actions */} -
- - About Nearle - - -

- Built for Every -
- Neighbourhood. -

- -

- Nearle is India's first hyper-local managed “Neighbourhood living platform” which brings all the amenities and requirements of residents to their fingertips. -

- -
- - Our Story - - - Join Our Journey - -
-
- - {/* Right Column: Visual Collage */} -
- -
-
-
- - {/* BOTTOM HERO STATS */} -
-
- -
-
-
+ {/* ─── Intro Description Block ─── */} -
- - Our Platform - -

- Nearle Neighbourhood Commerce -

-

- 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. -

-
+ +
+ + Our Platform + +

+ Nearle Neighbourhood Commerce +

+

+ 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. +

+
+
{/* ─── Strategy Cards (Mission, Vision, Why) ─── */}
-
{STRATEGY_CARDS.map((card) => ( -
-
+ ))} - +
{/* ─── Global Stats counters ─── */}
-
+ {LOCAL_COUNTERS.map((c) => ( -
+
@@ -214,41 +149,43 @@ export default function AboutPage() {

{c.label}

-
+ ))} -
+
{/* ─── Brand Building Banner ─── */} -
-
- - Don't miss the opportunity - -

- Build Your Brand with Nearle -

-

- Delight your neighborhood with a personalized online experience. Unlock revenue opportunities and local marketing strategies that will take your business further. -

-
- - - Register Merchant - - - - Explore Nearle - + +
+
+ + Don't miss the opportunity + +

+ Build Your Brand with Nearle +

+

+ Delight your neighborhood with a personalized online experience. Unlock revenue opportunities and local marketing strategies that will take your business further. +

+
+ + + Register Merchant + + + + Explore Nearle + +
-
-
+ +
); } diff --git a/src/app/brands/page.tsx b/src/app/brands/page.tsx index 2ae5722..3193f37 100644 --- a/src/app/brands/page.tsx +++ b/src/app/brands/page.tsx @@ -5,6 +5,9 @@ import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; import { FAQAccordion } from "@/components/ui/FAQAccordion"; +import { AnimatedSection } from "@/components/motion/AnimatedSection"; +import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid"; +import { CTABanner } from "@/components/ui/CTABanner"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -215,21 +218,23 @@ export default function BrandsPage() { {/* ─── What We Offer ─── */}
-
- - What Nearle Offers Brands - -

- The Full Stack of Neighbourhood Commerce -

-

- From your first store listing to city-wide distribution — Nearle builds the infrastructure so you can focus on your product. -

-
+ +
+ + What Nearle Offers Brands + +

+ The Full Stack of Neighbourhood Commerce +

+

+ From your first store listing to city-wide distribution — Nearle builds the infrastructure so you can focus on your product. +

+
+
-
+ {WHAT_WE_OFFER.map((item) => ( -
@@ -245,30 +250,32 @@ export default function BrandsPage() {

{item.title}

{item.desc}

-
+ ))} -
+
{/* ─── Industries ─── */}
-
- - Industries We Serve - -

- Your Category Has a Place on Nearle -

-

- Nearle's neighbourhood store network stocks a wide range of everyday product categories. If customers buy it daily, it belongs here. -

-
+ +
+ + Industries We Serve + +

+ Your Category Has a Place on Nearle +

+

+ Nearle's neighbourhood store network stocks a wide range of everyday product categories. If customers buy it daily, it belongs here. +

+
+
-
+ {INDUSTRIES.map((industry) => ( -
@@ -276,30 +283,32 @@ export default function BrandsPage() {
{industry.label} -
+ ))} -
+
{/* ─── Partnership Process ─── */}
-
- - How It Works - -

- The Partnership Process -

-

- From first conversation to city-wide brand presence in four structured steps. -

-
+ +
+ + How It Works + +

+ The Partnership Process +

+

+ From first conversation to city-wide brand presence in four structured steps. +

+
+
-
+ {PARTNERSHIP_STEPS.map((step) => ( -
@@ -313,9 +322,9 @@ export default function BrandsPage() { {step.title}

{step.desc}

-
+ ))} -
+
@@ -323,24 +332,26 @@ export default function BrandsPage() {
-
- - FAQs - -

- Brand Partner Questions -

-

- What brand managers usually ask before starting their Nearle partnership. -

- - Talk to our sales team - - -
+ +
+ + FAQs + +

+ Brand Partner Questions +

+

+ What brand managers usually ask before starting their Nearle partnership. +

+ + Talk to our sales team + + +
+
@@ -348,43 +359,17 @@ export default function BrandsPage() {
{/* ─── CTA Banner ─── */} -
-
-
-
-
-
- - - Brand Partnership - -

- Ready to Grow in -
Every Neighbourhood? -

-

- 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. -

-
- - - Talk to Sales - - - - Send an Enquiry - -
-
-
-
-
+ Ready to Grow in
Every Neighbourhood?} + body="Let's talk about what a Nearle brand partnership looks like for your products. Our sales team will design a custom plan for your brand." + primary={{ href: "/contact", icon: "forum", label: "Talk to Sales" }} + secondary={{ href: "/contact", icon: "mail", label: "Send an Enquiry" }} + decorations={[ + "circle at 70% 30%, rgba(99,48,214,0.2), transparent 50%", + "circle at 20% 70%, rgba(0,109,55,0.12), transparent 45%", + ]} + /> ); } diff --git a/src/app/businesses/page.tsx b/src/app/businesses/page.tsx index c6b36e5..83696f3 100644 --- a/src/app/businesses/page.tsx +++ b/src/app/businesses/page.tsx @@ -3,9 +3,11 @@ import Image from "next/image"; import Link from "next/link"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; -import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; -import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; import { BusinessFeaturesGrid } from "@/components/businesses/BusinessFeaturesGrid"; +import { BusinessHero } from "@/components/heroes/BusinessHero"; +import { AnimatedSection } from "@/components/motion/AnimatedSection"; +import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid"; +import { CTABanner } from "@/components/ui/CTABanner"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -15,12 +17,6 @@ export const metadata: Metadata = { }; -const HERO_BENEFITS = [ - { title: "More Visibility Across Nearle", icon: "visibility", desc: "Get discovered by locals" }, - { title: "Easy Order Management", icon: "assignment", desc: "Track and fulfill cleanly" }, - { title: "Grow Repeat Customers", icon: "trending_up", desc: "Build neighborhood loyalty" }, - { title: "Local Delivery Support", icon: "local_shipping", desc: "Deliver fast & verified" } -]; function PhoneMapVisual() { return ( @@ -96,102 +92,42 @@ function PhoneMapVisual() { } export default function BusinessesPage() { - const collageCards = [ - { text: "Nearby Customers", icon: "people" }, - { text: "Orders & Offers", icon: "local_offer" }, - { text: "Local Delivery Support", icon: "local_shipping" } - ]; - return ( {/* ─── Hero Section ─── */} -
- {/* TWO-COLUMN GRID */} -
-
- - {/* Left Column: Copy & Actions */} -
- - Nearle for Business - - -

- Grow Your Local Business, -
- Neighbourhood by Neighbourhood. -

- -

- Get discovered by nearby customers, manage orders, and build repeat business from one simple platform. -

- -
- {/* TODO: Replace with real merchant onboarding URL once confirmed */} - - List Your Store - - - Explore Solutions - -
-
- - {/* Right Column: Visual Collage */} -
- -
-
-
- - {/* BOTTOM HERO BENEFITS */} -
-
- -
-
-
+ {/* ─── Intro Highlight Banner ─── */} -
-
- - Together, we grow - -

- Get set in minutes and delight the Neighborhood -

-

- Nearle is built to help businesses like Yours establish a local online presence and empower neighborhood customers to discover and interact with you directly. -

-
-
- - - Talk to an Expert - -
-
+ +
+
+ + Together, we grow + +

+ Get set in minutes and delight the Neighborhood +

+

+ Nearle is built to help businesses like Yours establish a local online presence and empower neighborhood customers to discover and interact with you directly. +

+
+
+ + + Talk to an Expert + +
+
+
{/* ─── Build Your Neighborhood Brand ─── */}
{/* Header */} +
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.

+
{/* Cards grid */} -
+ {/* Card 1 - Get Online */} -
+ 01 @@ -233,10 +170,10 @@ export default function BusinessesPage() { />
- + {/* Card 2 - Get Noticed */} -
+ 02 @@ -254,10 +191,10 @@ export default function BusinessesPage() {
-
+ {/* Card 3 - Get Simplified */} -
+ 03 @@ -290,8 +227,8 @@ export default function BusinessesPage() { /> -
- + +
@@ -299,38 +236,15 @@ export default function BusinessesPage() { {/* ─── Join/Download Call to Action ─── */} -
-
-
-
-
-

- Nearle is Essential for Business Growth -

-

- Leverage the full potential of your neighborhood. Stand above the rest, increase customers, and scale your brand today. -

-
- {/* TODO: Replace with real merchant onboarding URL once confirmed */} - - - Register Interest - - - - Contact Experts - -
-
-
-
-
+
); } diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx index 6f66bfe..47d99fb 100644 --- a/src/app/contact/page.tsx +++ b/src/app/contact/page.tsx @@ -1,7 +1,8 @@ import React from "react"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; -import { ContactForm } from "@/components/contact/ContactForm"; import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; +import { ContactHero } from "@/components/heroes/ContactHero"; +import { AnimatedSection } from "@/components/motion/AnimatedSection"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -13,95 +14,10 @@ export default function ContactPage() { return ( {/* ─── Contact Section ─── */} -
- - {/* Subtle Map Grid/Location decoration behind left content */} -
- - - - - - - - -
- - {/*
*/} -
-
- - {/* Left Column — Contact Info */} -
- - Contact Us - -

- We’re Here to
- Help You. -

-

- Whether you are a customer, local store owner, or delivery partner, our team is here to help you get started with Nearle. -

- - {/* Info Rows */} -
- {/* Call Us */} -
-
- -
-
-

Call Us

-

+91 909 206 8666

-
-
- - {/* Email Us */} -
-
- -
-
-

Email Us

-

nearle@care.in

-
-
- - {/* Visit Us */} -
-
- -
-
-

Address

-

424, DB Rd, R.S. Puram, Coimbatore, TN 641002

-
-
- - {/* Helpdesk */} - {/*
-
- -
-
-

Helpdesk

-

24/7 Phone Helpdesk Active

-
-
*/} -
-
- - {/* Right Column: Compact Form Card */} -
- -
- -
-
-
+ {/* ─── Map Section ─── */} +
@@ -129,6 +45,7 @@ export default function ContactPage() {
+
); } diff --git a/src/app/delivery-partners/page.tsx b/src/app/delivery-partners/page.tsx index 400cd2b..3e4a329 100644 --- a/src/app/delivery-partners/page.tsx +++ b/src/app/delivery-partners/page.tsx @@ -5,6 +5,10 @@ import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; import { FAQAccordion } from "@/components/ui/FAQAccordion"; +import { AnimatedSection } from "@/components/motion/AnimatedSection"; +import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid"; +import { AnimatedTimeline } from "@/components/delivery/AnimatedTimeline"; +import { CTABanner } from "@/components/ui/CTABanner"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -252,21 +256,23 @@ export default function DeliveryPartnersPage() { {/* ─── Benefits Grid ─── */}
-
- - Partner Benefits - -

- More Than Just Delivery -

-

- Nearle is designed around your life — not the other way around. -

-
+ +
+ + Partner Benefits + +

+ More Than Just Delivery +

+

+ Nearle is designed around your life — not the other way around. +

+
+
-
+ {BENEFITS.map((benefit) => ( -
@@ -277,9 +283,9 @@ export default function DeliveryPartnersPage() {

{benefit.title}

{benefit.desc}

-
+ ))} -
+
@@ -287,28 +293,30 @@ export default function DeliveryPartnersPage() {
-
- - To Get Started - -

- What You Need to Join -

-

- No experience needed. If you have a vehicle, a smartphone, and a desire to earn — you're more than qualified. -

- - - Apply Now - -
+ +
+ + To Get Started + +

+ What You Need to Join +

+

+ No experience needed. If you have a vehicle, a smartphone, and a desire to earn — you're more than qualified. +

+ + + Apply Now + +
+
-
+ {REQUIREMENTS.map((req) => ( -
+
@@ -316,9 +324,9 @@ export default function DeliveryPartnersPage() {

{req.title}

{req.desc}

-
+ ))} -
+
@@ -326,41 +334,21 @@ export default function DeliveryPartnersPage() { {/* ─── How Delivery Works (Timeline) ─── */}
-
- - Your Journey - -

- From Sign-Up to First Earnings -

-

- Most delivery partners are out earning within 3 days of applying. -

-
- -
- {/* Vertical line */} -
- -
- {TIMELINE.map((item, index) => ( -
-
- {String(index + 1).padStart(2, "0")} -
-
-
-

{item.title}

- - {item.time} - -
-

{item.desc}

-
-
- ))} + +
+ + Your Journey + +

+ From Sign-Up to First Earnings +

+

+ Most delivery partners are out earning within 3 days of applying. +

-
+ + +
@@ -416,24 +404,26 @@ export default function DeliveryPartnersPage() {
-
- - FAQs - -

- Common Questions -

-

- Everything you want to know before joining the Nearle delivery network. -

- - Contact support - - -
+ +
+ + FAQs + +

+ Common Questions +

+

+ Everything you want to know before joining the Nearle delivery network. +

+ + Contact support + + +
+
@@ -441,43 +431,17 @@ export default function DeliveryPartnersPage() {
{/* ─── CTA Banner ─── */} -
-
-
-
-
-
- - - Start Earning Today - -

- Join the Nearle -
Delivery Network -

-

- Delivery partners in Coimbatore are already earning with Nearle. Sign up today and start delivering in your neighbourhood within days. -

-
- - - Apply Now - - - - Talk to Us - -
-
-
-
-
+ Join the Nearle
Delivery Network} + body="Delivery partners in Coimbatore are already earning with Nearle. Sign up today and start delivering in your neighbourhood within days." + primary={{ href: "/contact", icon: "how_to_reg", label: "Apply Now" }} + secondary={{ href: "/contact", icon: "phone", label: "Talk to Us" }} + decorations={[ + "circle at 30% 30%, rgba(0,109,55,0.15), transparent 50%", + "circle at 70% 70%, rgba(99,48,214,0.12), transparent 45%", + ]} + /> ); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index dc39cc4..9b711ff 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,8 @@ import type { Metadata, Viewport } from "next"; import "./globals.css"; +import { MotionProvider } from "@/components/layout/MotionProvider"; +import { PremiumNavbar } from "@/components/layout/PremiumNavbar"; +import { PremiumFooter } from "@/components/layout/PremiumFooter"; const jost = { variable: "font-display" }; const jostBody = { variable: "font-body" }; @@ -70,6 +73,7 @@ export default function RootLayout({ @@ -86,7 +90,15 @@ export default function RootLayout({ /> - {children} + +
+ +
+ {children} +
+ +
+
); diff --git a/src/app/merchants/page.tsx b/src/app/merchants/page.tsx index 2b814b7..56b9bcd 100644 --- a/src/app/merchants/page.tsx +++ b/src/app/merchants/page.tsx @@ -5,6 +5,9 @@ import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; import { FAQAccordion } from "@/components/ui/FAQAccordion"; +import { AnimatedSection } from "@/components/motion/AnimatedSection"; +import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid"; +import { CTABanner } from "@/components/ui/CTABanner"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -226,21 +229,23 @@ export default function MerchantsPage() { {/* ─── Merchant Benefits Grid ─── */}
-
- - What You Get - -

- Everything Your Store Needs to Grow -

-

- Nearle gives your neighbourhood store the same tools that big online retailers have — without the complexity. -

-
+ +
+ + What You Get + +

+ Everything Your Store Needs to Grow +

+

+ Nearle gives your neighbourhood store the same tools that big online retailers have — without the complexity. +

+
+
-
+ {BENEFITS.map((benefit) => ( -
@@ -251,30 +256,32 @@ export default function MerchantsPage() {

{benefit.title}

{benefit.desc}

-
+ ))} -
+
{/* ─── How It Works ─── */}
-
- - Simple Steps - -

- Up and Running in 24 Hours -

-

- From registration to your first order, we've made the process as simple as possible. -

-
+ +
+ + Simple Steps + +

+ Up and Running in 24 Hours +

+

+ From registration to your first order, we've made the process as simple as possible. +

+
+
-
+ {HOW_STEPS.map((step) => ( -
@@ -288,9 +295,9 @@ export default function MerchantsPage() { {step.title}

{step.desc}

-
+ ))} -
+
@@ -298,37 +305,39 @@ export default function MerchantsPage() {
-
- - Why Nearle - -

- Your Neighbourhood's -
- Best Business Partner -

-

- Nearle isn't built like a big delivery platform that squeezes margins. We're built for neighbourhood stores like yours — where community and trust come first. -

+ +
+ + Why Nearle + +

+ Your Neighbourhood's +
+ Best Business Partner +

+

+ Nearle isn't built like a big delivery platform that squeezes margins. We're built for neighbourhood stores like yours — where community and trust come first. +

- {/* Stats row */} -
- {[ - { stat: "2km", label: "Hyperlocal Coverage" }, - { stat: "24h", label: "Time to Go Live" }, - { stat: "Fair", label: "Commission Model" }, - ].map((s) => ( -
-
{s.stat}
-
{s.label}
-
- ))} + {/* Stats row */} +
+ {[ + { stat: "2km", label: "Hyperlocal Coverage" }, + { stat: "24h", label: "Time to Go Live" }, + { stat: "Fair", label: "Commission Model" }, + ].map((s) => ( +
+
{s.stat}
+
{s.label}
+
+ ))} +
-
+
-
+ {WHY_REASONS.map((reason) => ( -
+
@@ -336,9 +345,9 @@ export default function MerchantsPage() {

{reason.title}

{reason.desc}

-
+ ))} -
+
@@ -346,53 +355,55 @@ export default function MerchantsPage() { {/* ─── Success Story ─── */}
-
- - Merchant Story - -

- Real Results from Real Stores -

-
+ +
+ + Merchant Story + +

+ Real Results from Real Stores +

+
-
-
-
-
-
- - Success Story -
-
- “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.” -
-
-
- +
+
+
+
+
+ + Success Story
-
-
Ramesh Kumar
-
R.K. Provision Store, R.S. Puram, Coimbatore
+
+ “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.” +
+
+
+ +
+
+
Ramesh Kumar
+
R.K. Provision Store, R.S. Puram, Coimbatore
+
-
-
- {[ - { value: "2.1×", label: "Daily Orders Growth", icon: "trending_up", color: "text-[#006D37]" }, - { value: "340+", label: "Repeat Customers Built", icon: "people", color: "text-[#6330D6]" }, - { value: "3 hrs", label: "Saved per Day on Calls", icon: "schedule", color: "text-[#006D37]" }, - { value: "4.9 ★", label: "Average Store Rating", icon: "star", color: "text-amber-500" }, - ].map((m) => ( -
- -
{m.value}
-
{m.label}
-
- ))} + + {[ + { value: "2.1×", label: "Daily Orders Growth", icon: "trending_up", color: "text-[#006D37]" }, + { value: "340+", label: "Repeat Customers Built", icon: "people", color: "text-[#6330D6]" }, + { value: "3 hrs", label: "Saved per Day on Calls", icon: "schedule", color: "text-[#006D37]" }, + { value: "4.9 ★", label: "Average Store Rating", icon: "star", color: "text-amber-500" }, + ].map((m) => ( + + +
{m.value}
+
{m.label}
+
+ ))} +
-
+
@@ -400,24 +411,26 @@ export default function MerchantsPage() {
-
- - FAQs - -

- Questions Merchants Ask -

-

- Everything you need to know before joining Nearle. Still have questions? Our team is a call away. -

- - Talk to our team - - -
+ +
+ + FAQs + +

+ Questions Merchants Ask +

+

+ Everything you need to know before joining Nearle. Still have questions? Our team is a call away. +

+ + Talk to our team + + +
+
@@ -425,42 +438,18 @@ export default function MerchantsPage() {
{/* ─── CTA Banner ─── */} -
-
-
-
-
-
- - - Ready to Grow? - -

- Become a Merchant Partner -

-

- Join neighbourhood stores across Coimbatore already selling online with Nearle. Registration takes under 10 minutes. -

-
- - - Register Your Store - - - - Talk to Our Team - -
-
-
-
-
+ ); } + diff --git a/src/app/people/page.tsx b/src/app/people/page.tsx index 1418227..3ae0e49 100644 --- a/src/app/people/page.tsx +++ b/src/app/people/page.tsx @@ -3,8 +3,9 @@ import Image from "next/image"; import Link from "next/link"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { PremiumPageShell } from "@/components/layout/PremiumPageShell"; -import { HeroVisualCollage } from "@/components/layout/HeroVisualCollage"; -import { FeatureStatsRow } from "@/components/ui/FeatureStatsRow"; +import { PeopleHero } from "@/components/heroes/PeopleHero"; +import { AnimatedSection } from "@/components/motion/AnimatedSection"; +import { AnimatedGrid, AnimatedCard } from "@/components/motion/AnimatedGrid"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -137,83 +138,15 @@ const APP_BENEFITS = [ } ]; -const HERO_STATS = [ - { title: "Verified Local Stores", icon: "store", desc: "Growing local commerce" }, - { title: "2km Neighbourhood", icon: "location_on", desc: "Hyperlocal service radius" }, - { title: "Happy Customers", icon: "mood", desc: "Growing neighbourhood community" }, - { title: "Trusted Platform", icon: "star", desc: "Loved by local shoppers" } -]; - export default function PeoplePage() { - const collageCards = [ - { text: "Discover Nearby", icon: "explore" }, - { text: "Save Favourites", icon: "favorite" }, - { text: "Order Anytime", icon: "shopping_bag" } - ]; - return ( {/* ─── Hero Section ─── */} -
- {/* TWO-COLUMN GRID */} -
-
- - {/* Left Column: Copy & Actions */} -
- - Nearle for People - - -

- Your Neighbourhood, -
- Now in Your Pocket. -

- -

- Discover verified local stores, save your favorite merchants, and order everyday items with superfast local fulfillment. -

- -
- - Get the App - - - Explore Stores - -
-
- - {/* Right Column: Visual Collage */} -
- -
-
-
- - {/* BOTTOM HERO STATS */} -
-
- -
-
-
+ {/* ─── Bento Grid of Offerings ─── */} -
+ +
What we offer @@ -250,25 +183,28 @@ export default function PeoplePage() { ))}
-
+
+ {/* ─── Localized Stats Block ─── */}
-
- - Nearle Network - -

- The Neighbourhood App -

-

- Bringing local stores to your doorstep instantly. -

-
+ +
+ + Nearle Network + +

+ The Neighbourhood App +

+

+ Bringing local stores to your doorstep instantly. +

+
+
-
+ {LOCAL_STATS.map((s) => ( -
@@ -279,12 +215,13 @@ export default function PeoplePage() {

{s.label}

-
+ ))} -
+
{/* ─── Ready to Download Section ─── */} +
{/* Subtle glow layers */} @@ -531,6 +468,7 @@ export default function PeoplePage() {
+
); } diff --git a/src/components/businesses/BusinessFeaturesGrid.tsx b/src/components/businesses/BusinessFeaturesGrid.tsx index effcdd7..55acd5f 100644 --- a/src/components/businesses/BusinessFeaturesGrid.tsx +++ b/src/components/businesses/BusinessFeaturesGrid.tsx @@ -1,7 +1,9 @@ "use client"; import React from "react"; +import { motion } from "framer-motion"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; +import { fadeUp, scaleIn, staggerContainer, staggerSlow, viewport } from "@/lib/animations"; // ─── Feature SVG Illustrations ─────────────────────────────────────────────── // Flat vector style · Nearle brand palette · no text inside SVGs @@ -428,46 +430,50 @@ export function BusinessFeaturesGrid() { > {/* ── LEFT — clean text feature grid, no illustrations ── */} -
- {/* Badge */} - - Our features - + + {/* Badge + Heading */} + + + Our features + +

+ Move Your Business Forward +

+

+ Be a part of a community of like-minded people. Brand your business on Nearle and reach out to your neighborhood with ease. +

+
- {/* Heading */} -

- Move Your Business Forward -

- - {/* Description */} -

- Be a part of a community of like-minded people. Brand your business on Nearle and reach out to your neighborhood with ease. -

- - {/* Feature list – check + text only, no illustration columns */} -
{FEATURES.map((feat) => ( -
- {/* Green check icon */}
- - {/* Title + description */}

-
+ ))} -
-
+ + - {/* ── RIGHT — 4 cards + illustration strip below ─────── */} -
- - {/* 2×2 feature benefit cards */} + {/* ── RIGHT — 2×2 feature benefit cards ─────── */} +
{CARDS.map((card) => { const { CardIllus } = card; return ( -
- {/* Top icon */}
- - {/* Illustration */}
- - {/* Bottom label */}

-
+
); })}
- - - + diff --git a/src/components/contact/ContactForm.tsx b/src/components/contact/ContactForm.tsx index 7ea85f1..0b1e3fd 100644 --- a/src/components/contact/ContactForm.tsx +++ b/src/components/contact/ContactForm.tsx @@ -1,8 +1,27 @@ "use client"; import React, { useState } from "react"; +import { motion } from "framer-motion"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; +const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number]; + +// Stagger form contents after the card slides in from right (card enters at delay 0.25, duration 0.65 → visible ~t=0.7s) +const formContentStagger = { + hidden: {}, + visible: { transition: { staggerChildren: 0.1, delayChildren: 0.75 } }, +}; + +const fieldVariant = { + hidden: { opacity: 0, y: 14 }, + visible: { opacity: 1, y: 0, transition: { duration: 0.45, ease: EASE } }, +}; + +const submitVariant = { + hidden: { opacity: 0, y: 10, scale: 0.98 }, + visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.4, ease: EASE } }, +}; + type FormData = { name: string; email: string; @@ -100,16 +119,21 @@ export function ContactForm() {
-
- + + Write to us - -

+ + Send Message -

+
-
+ {/* Name field */}
-
+ {/* Message field */} -
+ @@ -202,28 +226,30 @@ export function ContactForm() { {errors.message} )} -
+ - {/* Submit button */} - + {/* Submit button — appears last */} + + +
-
+
{/* ─── Success Toast Notification ─── */} diff --git a/src/components/delivery/AnimatedTimeline.tsx b/src/components/delivery/AnimatedTimeline.tsx new file mode 100644 index 0000000..b997dfa --- /dev/null +++ b/src/components/delivery/AnimatedTimeline.tsx @@ -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 ( +
+ {/* Vertical connector line — animated scaleY from origin-top */} + + + + {items.map((item, index) => ( + +
+ {String(index + 1).padStart(2, "0")} +
+
+
+

{item.title}

+ + {item.time} + +
+

{item.desc}

+
+
+ ))} +
+
+ ); +} diff --git a/src/components/heroes/AboutHero.tsx b/src/components/heroes/AboutHero.tsx new file mode 100644 index 0000000..b3a2605 --- /dev/null +++ b/src/components/heroes/AboutHero.tsx @@ -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 ( +
+
+
+ + {/* Left Column: slow, deliberate stagger — thoughtful pacing */} + + + About Nearle + + + + Built for Every +
+ Neighbourhood. +
+ + + Nearle is India's first hyper-local managed “Neighbourhood living platform” which brings all the amenities and requirements of residents to their fingertips. + + + + + Our Story + + + Join Our Journey + + +
+ + {/* Right Column: soft scale-up — gentle and community-first */} + + + +
+
+ +
+
+ +
+
+
+ ); +} diff --git a/src/components/heroes/BusinessHero.tsx b/src/components/heroes/BusinessHero.tsx new file mode 100644 index 0000000..f1a7df1 --- /dev/null +++ b/src/components/heroes/BusinessHero.tsx @@ -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 ( +
+
+
+ + {/* Left Column: animated with unique stagger */} + + + Nearle for Business + + + + Grow Your Local Business, +
+ Neighbourhood by Neighbourhood. +
+ + + Get discovered by nearby customers, manage orders, and build repeat business from one simple platform. + + + + + List Your Store + + + Explore Solutions + + +
+ + {/* Right Column: subtle fade-in — HeroVisualCollage internal animations choreograph each element */} + + + +
+
+ +
+
+ +
+
+
+ ); +} diff --git a/src/components/heroes/ContactHero.tsx b/src/components/heroes/ContactHero.tsx new file mode 100644 index 0000000..2fcb989 --- /dev/null +++ b/src/components/heroes/ContactHero.tsx @@ -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 ( +
+ + {/* Decorative map grid — not animated so it stays subtle */} +
+ + + + + + + + +
+ +
+
+ + {/* Left Column — Contact Info: sequential stagger from top */} + + + Contact Us + + + + We're Here to
+ Help You. +
+ + + Whether you are a customer, local store owner, or delivery partner, our team is here to help you get started with Nearle. + + + {/* Info rows: each row staggers in from left */} + + {CONTACT_INFO.map((item) => ( + +
+ +
+
+

{item.label}

+

{item.value}

+
+
+ ))} +
+
+ + {/* Right Column: form slides in from right */} + + + +
+
+
+ ); +} diff --git a/src/components/heroes/PeopleHero.tsx b/src/components/heroes/PeopleHero.tsx new file mode 100644 index 0000000..dc82c86 --- /dev/null +++ b/src/components/heroes/PeopleHero.tsx @@ -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 ( +
+
+
+ + {/* Left Column: snappy fast-stagger sequence */} + + + Nearle for People + + + + Your Neighbourhood, +
+ Now in Your Pocket. +
+ + + Discover verified local stores, save your favorite merchants, and order everyday items with superfast local fulfillment. + + + + + Get the App + + + Explore Stores + + +
+ + {/* Right Column: subtle fade — HeroVisualCollage's internal animations handle phone, images, badges individually */} + + + +
+
+ +
+
+ +
+
+
+ ); +} diff --git a/src/components/home/BusinessSection.tsx b/src/components/home/BusinessSection.tsx index 40390f1..2a1db85 100644 --- a/src/components/home/BusinessSection.tsx +++ b/src/components/home/BusinessSection.tsx @@ -69,8 +69,16 @@ export function BusinessSection() {
-
-
+ +
diff --git a/src/components/home/Hero.tsx b/src/components/home/Hero.tsx index 9366122..bc7af07 100644 --- a/src/components/home/Hero.tsx +++ b/src/components/home/Hero.tsx @@ -2,14 +2,11 @@ import Link from "next/link"; import Image from "next/image"; -import { motion } from "framer-motion"; +import { motion, useReducedMotion } from "framer-motion"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; -import { - fadeUp, - slideInRight, - staggerContainer, - viewport, -} from "@/lib/animations"; +import { viewport } from "@/lib/animations"; + +const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number]; const PROOF_ITEMS = [ { label: "Verified", text: "Local Stores", icon: "store" }, @@ -18,16 +15,86 @@ const PROOF_ITEMS = [ { label: "Trusted", text: "Local Platform", icon: "star" }, ]; +// Left column: badge → line1 mask-reveal → line2 mask-reveal → desc → primary CTA → secondary CTA +const heroStagger = { + hidden: {}, + visible: { transition: { staggerChildren: 0.12, delayChildren: 0.05 } }, +}; + +const badgeVariant = { + hidden: { opacity: 0, y: 10, scale: 0.95 }, + visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.38, ease: EASE } }, +}; + +// Lines stagger internally +const lineStagger = { + hidden: {}, + visible: { transition: { staggerChildren: 0.09 } }, +}; + +// True mask reveal: text slides up from below a clipping parent +const lineReveal = { + hidden: { y: "110%" }, + visible: { y: 0, transition: { duration: 0.72, ease: EASE } }, +}; + +const descVariant = { + hidden: { opacity: 0, y: 18 }, + visible: { opacity: 1, y: 0, transition: { duration: 0.55, ease: EASE } }, +}; + +const primaryCtaVariant = { + hidden: { opacity: 0, y: 14, scale: 0.97 }, + visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.45, ease: EASE } }, +}; + +const secondaryCtaVariant = { + hidden: { opacity: 0, x: -14 }, + visible: { opacity: 1, x: 0, transition: { duration: 0.4, ease: EASE } }, +}; + +// Right column: each element enters independently +const imgFromLeft = { + hidden: { opacity: 0, x: -36 }, + visible: { opacity: 1, x: 0, transition: { duration: 0.65, ease: EASE } }, +}; + +const centerCardReveal = { + hidden: { opacity: 0, scale: 0.86, y: 24 }, + visible: { opacity: 1, scale: 1, y: 0, transition: { duration: 0.75, ease: EASE } }, +}; + +const imgFromRight = { + hidden: { opacity: 0, x: 36 }, + visible: { opacity: 1, x: 0, transition: { duration: 0.65, ease: EASE } }, +}; + +const statStagger = { + hidden: {}, + visible: { transition: { staggerChildren: 0.08 } }, +}; + +const statItem = { + hidden: { opacity: 0, y: 14 }, + visible: { opacity: 1, y: 0, transition: { duration: 0.45, ease: EASE } }, +}; + export function Hero() { + const reducedMotion = useReducedMotion(); + return (
{/* ─── DECORATIONS (BEHIND CONTENT) ─── */} {/* 1. Large pale lavender partial circle (top-left) */} -
+ {/* 2. Purple solid circle (center-right around collage) */} -
+
{/* 3. Striped purple circle (lower-middle collage area) */}
{/* 6. Large partial purple circle (bottom-right corner) */} -
+ {/* ─── TWO-COLUMN CONTENT GRID ─── */}
@@ -61,64 +132,84 @@ export function Hero() { {/* LEFT COLUMN: Editorial Typography & Copy */} + {/* Badge */} Welcome to Nearle - - Everything Local. -
- Everything Near. -
+ {/* H1: line-by-line mask reveal */} + +
+ + Everything Local. + +
+
+ + Everything Near. + +
+
+ {/* Description */} Discover verified local stores, exclusive offers, fast delivery, and a better way to shop within your neighbourhood. - - + - Explore Stores - - + Explore Stores + + + - Get the App - - + + Get the App + + +
- {/* RIGHT COLUMN: Creative Overlapping Strips Collage */} - + {/* RIGHT COLUMN: Individual image + center card animations */} +
{/* DESKTOP LAYOUT (lg and above) */}
- {/* left image: absolute left-[10%] top-[20%] w-[170px] h-[360px] */} -
+ + {/* Left image: slides in from left */} + Local grocery products -
+ - {/* middle image: central logo panel absolute left-[38%] top-[8%] */} -
-
- Central logo panel -
-
+ {/* Center card: scales up from below, then gently bobs */} + + +
+ Central logo panel +
+
+
- {/* right image: absolute right-[2%] top-[18%] w-[180px] h-[390px] */} -
+ {/* Right image: slides in from right */} + Local customer shopping -
+
{/* MOBILE/TABLET COLLAGE (below lg) */}
-
+ Grocery products -
-
- Central logo panel -
-
+ + + + + Central logo panel + + + + Customer -
+
- +
@@ -194,14 +330,13 @@ export function Hero() {
{PROOF_ITEMS.map((item, idx) => ( - - {/* Subtle vertical separator for desktop, except for the first item */} + {idx > 0 && (
)} diff --git a/src/components/home/HowItWorks.tsx b/src/components/home/HowItWorks.tsx index 175976e..8c2aafe 100644 --- a/src/components/home/HowItWorks.tsx +++ b/src/components/home/HowItWorks.tsx @@ -256,10 +256,14 @@ export function HowItWorks() { Yours{" "} establish a local online presence and empower neighbourhood customers to discover and interact with you directly.

- + {/* Image Container */}
diff --git a/src/components/home/StoreQRSection.tsx b/src/components/home/StoreQRSection.tsx index 0906b5f..7d1777f 100644 --- a/src/components/home/StoreQRSection.tsx +++ b/src/components/home/StoreQRSection.tsx @@ -17,8 +17,16 @@ export function StoreQRSection() {
{/* Background ambient glowing details */} -
-
+ +
@@ -124,7 +132,7 @@ export function StoreQRSection() {
- + Scan to Shop
@@ -189,7 +197,7 @@ export function StoreQRSection() {
- + Scan to Shop diff --git a/src/components/home/WhyNearle.tsx b/src/components/home/WhyNearle.tsx index 1b6327c..0f142e7 100644 --- a/src/components/home/WhyNearle.tsx +++ b/src/components/home/WhyNearle.tsx @@ -81,7 +81,7 @@ export function WhyNearle() {

- {/* Cards Grid — fadeIn preserves .premium-card:hover CSS transform */} + {/* Cards Grid */}
diff --git a/src/components/layout/HeroVisualCollage.tsx b/src/components/layout/HeroVisualCollage.tsx index 237b6f8..b6b3056 100644 --- a/src/components/layout/HeroVisualCollage.tsx +++ b/src/components/layout/HeroVisualCollage.tsx @@ -1,5 +1,10 @@ +"use client"; + import Image from "next/image"; +import { motion } from "framer-motion"; import { GlassInfoCard } from "../ui/GlassInfoCard"; +import { scaleIn, staggerContainer, viewport } from "@/lib/animations"; +import type { Variants } from "framer-motion"; type GlassCardItem = { text: string; @@ -12,6 +17,40 @@ type HeroVisualCollageProps = { rightImage?: string; glassCards: GlassCardItem[]; isPhoneCenter?: boolean; + cardVariant?: Variants; +}; + +const leftPanelVariant: Variants = { + hidden: { opacity: 0, x: -28 }, + visible: { + opacity: 1, + x: 0, + transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + +const rightPanelVariant: Variants = { + hidden: { opacity: 0, x: 28 }, + visible: { + opacity: 1, + x: 0, + transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + +const glasscardVariant: Variants = { + hidden: { opacity: 0, scale: 0.88, y: 8 }, + visible: { + opacity: 1, + scale: 1, + y: 0, + transition: { type: "spring", stiffness: 260, damping: 22 }, + }, +}; + +const glassCardStagger: Variants = { + hidden: {}, + visible: { transition: { staggerChildren: 0.1, delayChildren: 0.3 } }, }; export function HeroVisualCollage({ @@ -20,14 +59,14 @@ export function HeroVisualCollage({ rightImage, glassCards, isPhoneCenter = true, + cardVariant, }: HeroVisualCollageProps) { + const activeCardVariant = cardVariant ?? glasscardVariant; return (
{/* Decorative backgrounds */} - {/* 1. Soft purple circle */}
- {/* 2. SVG Dot grid */}
@@ -37,13 +76,19 @@ export function HeroVisualCollage({
- {/* 3. Small x marks */}
- {/* LEFT IMAGE PANEL (Hidden or compact on small mobile) */} + {/* LEFT IMAGE PANEL */} {leftImage && ( -
+ Collage left asset -
+ )} {/* RIGHT IMAGE PANEL */} {rightImage && ( -
+ Collage right asset -
+ )} {/* CENTER COMPONENT */} -
+ {isPhoneCenter ? ( - /* Phone Frame Container */
) : ( - /* Central Logo / Visual Panel */
Central logo panel
)} -
+ - {/* FLOATING GLASS CARDS (Positioned Absolute, scaled down on mobile) */} + {/* FLOATING GLASS CARDS — stagger in after center */} {glassCards.length > 0 && ( -
- {/* Card 1: Top Right */} + {glassCards[0] && ( -
+ -
+
)} - {/* Card 2: Bottom Left */} {glassCards[1] && ( -
+ -
+ )} - {/* Card 3: Mid Left/Right */} {glassCards[2] && ( -
+ -
+ )} -
+ )}
); diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx deleted file mode 100644 index 562b9c0..0000000 --- a/src/components/layout/Navbar.tsx +++ /dev/null @@ -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 ( -
-
- - - {/* Mobile Drawer */} - {mobileOpen && ( -
-
- {NAV_LINKS.map((link) => { - const isActive = pathname === link.href; - return ( - setMobileOpen(false)} - > - {link.label} - {isActive && ( - - )} - - ); - })} -
-
- )} -
-
- ); -} - diff --git a/src/components/layout/PremiumFooter.tsx b/src/components/layout/PremiumFooter.tsx index 4b69316..bf14b79 100644 --- a/src/components/layout/PremiumFooter.tsx +++ b/src/components/layout/PremiumFooter.tsx @@ -3,13 +3,16 @@ import Link from "next/link"; import Image from "next/image"; import React from "react"; +import { motion } from "framer-motion"; import { FaFacebookF, FaInstagram, FaLinkedinIn } from "react-icons/fa"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; +import { fadeUp, fadeIn, staggerContainer, staggerSlow, viewport } from "@/lib/animations"; + +const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number]; const COMPANY = [ { label: "About Us", href: "/about" }, { label: "Careers", href: "#" }, - // { label: "Press & News", href: "#" }, { label: "Help Center", href: "#" }, ]; @@ -17,26 +20,87 @@ const PARTNERSHIPS = [ { label: "Merchant Partner", href: "/merchants" }, { label: "Delivery Partner", href: "/delivery-partners" }, { label: "Direct to Customer Brands", href: "/brands" }, - // { label: "Local Real Estate", href: "#" }, ]; const LEGAL = [ { label: "Privacy Policy", href: "/privacy" }, { label: "Terms of Service", href: "/terms" }, { label: "Cookie Policy", href: "/cookies" }, - // { label: "Sitemap", href: "#" }, ]; const socialButtonClass = - "flex h-10 w-10 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white transition-all duration-300 xl:h-[54px] xl:w-[54px]"; + "flex h-10 w-10 items-center justify-center rounded-full border border-white/10 bg-white/5 text-white transition-[background-color] duration-300 xl:h-[54px] xl:w-[54px]"; + +const linkColumnVariant = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5, ease: EASE }, + }, +}; + +const footerReveal = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.6, ease: EASE }, + }, +}; + +// Renders an animated link list used in all three footer link columns +function FooterLinkList({ links }: { links: { label: string; href: string }[] }) { + return ( +
    + {links.map((link) => ( +
  • + + + {link.label} + + {/* Underline grows from left on hover */} + + +
  • + ))} +
+ ); +} export function PremiumFooter() { return (
-
+ {/* Footer reveal: the whole footer fades up as it enters the viewport */} +
-
-
+ + {/* Main grid: columns stagger in via parent footer variant propagation */} + + {/* Brand column */} + + {/* Social icons with scale spring on hover */}
- - + - - + - - +
-
+ -
+ {/* Company links */} +
Company
+ +
-
    - {COMPANY.map((link) => ( -
  • - - {link.label} - -
  • - ))} -
-
- -
+ {/* Nearle links */} +
Nearle
+ +
-
    - {PARTNERSHIPS.map((link) => ( -
  • - - {link.label} - -
  • - ))} -
-
- -
+ {/* Support links */} +
Support
- -
    - {LEGAL.map((link) => ( -
  • - - {link.label} - -
  • - ))} -
-
-
+ + +
-
+ {/* Bottom row */} +
@@ -155,7 +200,6 @@ export function PremiumFooter() {
Nearle
-

424, Diwan Bahadur Rd, Opp. Sri Krishna Sweets, R.S. Puram, Coimbatore, TN 641002 @@ -181,11 +225,15 @@ export function PremiumFooter() {

-
+
-
+ {/* Copyright bar */} +

© {new Date().getFullYear()} Nearle. All rights reserved.

@@ -203,9 +251,9 @@ export function PremiumFooter() { Help Center
-
+
-
+
); } diff --git a/src/components/layout/PremiumNavbar.tsx b/src/components/layout/PremiumNavbar.tsx index 5f21000..a36151f 100644 --- a/src/components/layout/PremiumNavbar.tsx +++ b/src/components/layout/PremiumNavbar.tsx @@ -2,10 +2,18 @@ import Link from "next/link"; import Image from "next/image"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { usePathname } from "next/navigation"; +import { + motion, + AnimatePresence, + useScroll, + useMotionValueEvent, +} from "framer-motion"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; +const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number]; + const NAV_LINKS = [ { label: "Home", href: "/" }, { label: "Nearle for Business", href: "/businesses" }, @@ -14,9 +22,44 @@ const NAV_LINKS = [ { label: "Contact", href: "/contact" }, ]; +const mobileMenuVariants = { + hidden: { opacity: 0, height: 0 }, + visible: { + opacity: 1, + height: "auto", + transition: { duration: 0.3, ease: EASE }, + }, + exit: { + opacity: 0, + height: 0, + transition: { duration: 0.2, ease: "easeIn" as const }, + }, +}; + +const mobileStagger = { + hidden: {}, + visible: { transition: { staggerChildren: 0.04, delayChildren: 0.06 } }, +}; + +const mobileItemVariants = { + hidden: { opacity: 0, x: -10 }, + visible: { opacity: 1, x: 0, transition: { duration: 0.22, ease: EASE } }, +}; + export function PremiumNavbar() { const [mobileOpen, setMobileOpen] = useState(false); + const [scrolled, setScrolled] = useState(false); const pathname = usePathname(); + const { scrollY } = useScroll(); + + useMotionValueEvent(scrollY, "change", (y) => { + setScrolled(y > 20); + }); + + // Close mobile menu whenever the route changes (navbar no longer remounts on navigation) + useEffect(() => { + setMobileOpen(false); + }, [pathname]); const isActiveLink = (href: string) => { if (href === "/") return pathname === "/"; @@ -24,10 +67,18 @@ export function PremiumNavbar() { }; return ( - //
-
+
- {/* Logo - Left */} + {/* Logo */} - {/* Desktop Navigation - Center */} + {/* Desktop Navigation */} - {/* Mobile Toggle - Right */} + {/* Mobile Toggle */}
- {/* Mobile Menu */} -
- -
-
+ {/* Mobile Menu with enter/exit animation */} + + {mobileOpen && ( + + + {NAV_LINKS.map((link) => { + const isActive = isActiveLink(link.href); + return ( + + setMobileOpen(false)} + className={`flex items-center justify-between rounded-xl px-4 py-3 text-sm font-bold transition-all duration-200 ${ + isActive + ? "bg-white/15 text-white" + : "text-white/70 hover:bg-white/10 hover:text-white" + }`} + > + {link.label} + {isActive && ( + + )} + + + ); + })} + + + )} + + ); -} \ No newline at end of file +} diff --git a/src/components/layout/PremiumPageShell.tsx b/src/components/layout/PremiumPageShell.tsx index 07214e5..2fc1d59 100644 --- a/src/components/layout/PremiumPageShell.tsx +++ b/src/components/layout/PremiumPageShell.tsx @@ -1,27 +1,12 @@ import React from "react"; -import { PremiumNavbar } from "./PremiumNavbar"; -import { PremiumFooter } from "./PremiumFooter"; -import { MotionProvider } from "./MotionProvider"; type PremiumPageShellProps = { children: React.ReactNode; className?: string; }; -export function PremiumPageShell({ children, className = "" }: PremiumPageShellProps) { - return ( - -
- {/* Header/Navbar */} - - - {/* Page Content */} -
- {children} -
- - -
-
- ); +// Navbar, footer, and MotionProvider are rendered in the root layout (app/layout.tsx) +// and persist across all navigations. This component is kept for call-site compatibility. +export function PremiumPageShell({ children }: PremiumPageShellProps) { + return <>{children}; } diff --git a/src/components/motion/AnimatedGrid.tsx b/src/components/motion/AnimatedGrid.tsx new file mode 100644 index 0000000..a2225e0 --- /dev/null +++ b/src/components/motion/AnimatedGrid.tsx @@ -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 ( + + {children} + + ); +} + +const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number]; + +export function AnimatedCard({ children, className, style }: Props) { + return ( + + {children} + + ); +} diff --git a/src/components/motion/AnimatedSection.tsx b/src/components/motion/AnimatedSection.tsx new file mode 100644 index 0000000..74f722e --- /dev/null +++ b/src/components/motion/AnimatedSection.tsx @@ -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 = { + 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 ( + + {children} + + ); +} diff --git a/src/components/ui/CTABanner.tsx b/src/components/ui/CTABanner.tsx new file mode 100644 index 0000000..94c6754 --- /dev/null +++ b/src/components/ui/CTABanner.tsx @@ -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 ( +
+ +
+ {decorations?.map((grad, i) => ( + + ))} + +
+ {badge && ( + + + {badge.label} + + )} + + + {heading} + + + + {body} + + + + + + {primary.label} + + + + {secondary.label} + + +
+
+
+
+ ); +} diff --git a/src/components/ui/FAQAccordion.tsx b/src/components/ui/FAQAccordion.tsx index b1c1f9b..43ab613 100644 --- a/src/components/ui/FAQAccordion.tsx +++ b/src/components/ui/FAQAccordion.tsx @@ -1,8 +1,11 @@ "use client"; import { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; import { MaterialIcon } from "./MaterialIcon"; +const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number]; + export type FAQItem = { question: string; answer: string; @@ -31,25 +34,30 @@ export function FAQAccordion({ items }: FAQAccordionProps) { {item.question} -
-
+ -
-

- {item.answer} -

-
+ + {openIndex === index && ( + +

+ {item.answer} +

+
+ )} +
))}
diff --git a/src/components/ui/FeatureStatsRow.tsx b/src/components/ui/FeatureStatsRow.tsx index 5d3d6ea..3368ef9 100644 --- a/src/components/ui/FeatureStatsRow.tsx +++ b/src/components/ui/FeatureStatsRow.tsx @@ -1,4 +1,8 @@ +"use client"; + +import { motion } from "framer-motion"; import { MaterialIcon } from "./MaterialIcon"; +import { fadeUp, staggerContainer, viewport } from "@/lib/animations"; type FeatureStatsItem = { icon: string; @@ -13,11 +17,18 @@ type FeatureStatsRowProps = { export function FeatureStatsRow({ items, className = "" }: FeatureStatsRowProps) { return ( -
+
{items.map((item, index) => ( -
)}
-
+
))}
-
+ ); } diff --git a/src/components/ui/GlassInfoCard.tsx b/src/components/ui/GlassInfoCard.tsx index 46f36f5..0e2f23a 100644 --- a/src/components/ui/GlassInfoCard.tsx +++ b/src/components/ui/GlassInfoCard.tsx @@ -16,7 +16,7 @@ export function GlassInfoCard({ text, icon, className = "" }: GlassInfoCardProps
) : ( - + )} {text} diff --git a/src/lib/animations/index.ts b/src/lib/animations/index.ts index e234312..bdf6435 100644 --- a/src/lib/animations/index.ts +++ b/src/lib/animations/index.ts @@ -5,7 +5,7 @@ export const fadeUp: Variants = { visible: { opacity: 1, y: 0, - transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] }, + transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, }, }; @@ -19,12 +19,38 @@ export const fadeIn: Variants = { }, }; +export const fadeInSlow: Variants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { duration: 0.7, ease: "easeOut" }, + }, +}; + +export const fadeUpStrong: Variants = { + hidden: { opacity: 0, y: 36 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.65, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + export const scaleIn: Variants = { hidden: { opacity: 0, scale: 0.95 }, visible: { opacity: 1, scale: 1, - transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] }, + transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + +export const scaleInSpring: Variants = { + hidden: { opacity: 0, scale: 0.88 }, + visible: { + opacity: 1, + scale: 1, + transition: { type: "spring", stiffness: 280, damping: 20 }, }, }; @@ -33,7 +59,16 @@ export const slideInLeft: Variants = { visible: { opacity: 1, x: 0, - transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] }, + transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + +export const slideInLeftSubtle: Variants = { + hidden: { opacity: 0, x: -20 }, + visible: { + opacity: 1, + x: 0, + transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, }, }; @@ -42,7 +77,7 @@ export const slideInRight: Variants = { visible: { opacity: 1, x: 0, - transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] }, + transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, }, }; @@ -53,4 +88,55 @@ export const staggerContainer: Variants = { }, }; +export const staggerFast: Variants = { + hidden: {}, + visible: { + transition: { staggerChildren: 0.07 }, + }, +}; + +export const staggerSlow: Variants = { + hidden: {}, + visible: { + transition: { staggerChildren: 0.13 }, + }, +}; + export const viewport = { once: true, margin: "-60px" } as const; +export const viewportEarly = { once: true, margin: "-20px" } as const; +export const viewport20 = { once: true, amount: 0.2 } as const; + +export const revealUp: Variants = { + hidden: { opacity: 0, y: 30 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.65, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + +export const cardReveal: Variants = { + hidden: { opacity: 0, y: 20, scale: 0.98 }, + visible: { + opacity: 1, + y: 0, + scale: 1, + transition: { duration: 0.55, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + +export const imageReveal: Variants = { + hidden: { opacity: 0, scale: 0.97 }, + visible: { + opacity: 1, + scale: 1, + transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] as [number, number, number, number] }, + }, +}; + +export const staggerCards: Variants = { + hidden: {}, + visible: { + transition: { staggerChildren: 0.08 }, + }, +};