update animation pages
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 954 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 657 KiB |
43
package-lock.json
generated
43
package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "nearle-new",
|
"name": "nearle-new",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.42.0",
|
||||||
"jsdom": "^29.1.1",
|
"jsdom": "^29.1.1",
|
||||||
"next": "16.2.9",
|
"next": "16.2.9",
|
||||||
"puppeteer-core": "^24.43.1",
|
"puppeteer-core": "^24.43.1",
|
||||||
@@ -4306,6 +4307,33 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.42.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.0.tgz",
|
||||||
|
"integrity": "sha512-wp7EJnfWaaEScVygKv3e20udoRz+LbtxScsuTkakAxfXmt+ReC6WyPW2nINRAGvd+hG9odwcjBLyOTPjH5pBRA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.42.0",
|
||||||
|
"motion-utils": "^12.39.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
@@ -5811,6 +5839,21 @@
|
|||||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.42.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.0.tgz",
|
||||||
|
"integrity": "sha512-M63h4n8R+quJdNhBwuLlgxM+OLYa9+I/T2pzDRboB9fLXRdbou+Gw7Zury+SkpaCyACP1JHSjHgZ1EgTkBr30w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.39.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
|
||||||
|
"integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.42.0",
|
||||||
"jsdom": "^29.1.1",
|
"jsdom": "^29.1.1",
|
||||||
"next": "16.2.9",
|
"next": "16.2.9",
|
||||||
"puppeteer-core": "^24.43.1",
|
"puppeteer-core": "^24.43.1",
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ export default function BrandsPage() {
|
|||||||
Your Category Has a Place on Nearle
|
Your Category Has a Place on Nearle
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
|
<p className="text-sm md:text-base text-[#5E5866] max-w-2xl mx-auto leading-relaxed">
|
||||||
Nearle's neighbourhood store network stocks a wide range of everyday product categories. If customers buy it daily, it belongs here.
|
Nearle's neighbourhood store network stocks a wide range of everyday product categories. If customers buy it daily, it belongs here.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default function ContactPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<h4 className="text-[#16001D] text-sm font-black">Email Us</h4>
|
<h4 className="text-[#16001D] text-sm font-black">Email Us</h4>
|
||||||
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">support@nearle.in • partners@nearle.in</p>
|
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">nearle@care.in</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -74,13 +74,13 @@ export default function ContactPage() {
|
|||||||
<MaterialIcon icon="location_on" size={18} />
|
<MaterialIcon icon="location_on" size={18} />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<h4 className="text-[#16001D] text-sm font-black">Visit Us</h4>
|
<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>
|
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">424, DB Rd, R.S. Puram, Coimbatore, TN 641002</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Helpdesk */}
|
{/* Helpdesk */}
|
||||||
<div className="flex gap-4 py-4.5 border-b border-[#6330D6]/10 items-center">
|
{/* <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">
|
<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} />
|
<MaterialIcon icon="support_agent" size={18} />
|
||||||
</div>
|
</div>
|
||||||
@@ -88,7 +88,7 @@ export default function ContactPage() {
|
|||||||
<h4 className="text-[#16001D] text-sm font-black">Helpdesk</h4>
|
<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>
|
<p className="text-[#5E5866] text-sm font-semibold mt-0.5">24/7 Phone Helpdesk Active</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ export default function DeliveryPartnersPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ─── Earnings Illustration ─── */}
|
{/* ─── Earnings Illustration ─── */}
|
||||||
<section id="earnings" className="py-10 md:py-14 xl:py-12 bg-[#FAF7FD] border-b border-[#6330D6]/10">
|
{/* <section id="earnings" 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">
|
<div className="text-center mb-10 md:mb-12">
|
||||||
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
|
<span className="text-xs text-secondary uppercase tracking-widest font-black bg-secondary/5 px-3.5 py-1 rounded-full">
|
||||||
@@ -410,7 +410,7 @@ export default function DeliveryPartnersPage() {
|
|||||||
Estimates include base delivery fees only. Peak-hour surge pricing, performance bonuses, and festive incentives are additional. Earnings vary by zone.
|
Estimates include base delivery fees only. Peak-hour surge pricing, performance bonuses, and festive incentives are additional. Earnings vary by zone.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section> */}
|
||||||
|
|
||||||
{/* ─── FAQ ─── */}
|
{/* ─── FAQ ─── */}
|
||||||
<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">
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export default function RootLayout({
|
|||||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
|
{/* eslint-disable-next-line @next/next/no-page-custom-font */}
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=Jost:wght@400;600;700;800&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Jost:wght@400;600;700;800&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
||||||
|
import { fadeUp, fadeIn, scaleIn, staggerContainer, viewport } from "@/lib/animations";
|
||||||
|
|
||||||
const FEATURES = [
|
const FEATURES = [
|
||||||
{
|
{
|
||||||
@@ -68,17 +72,17 @@ export function BusinessSection() {
|
|||||||
<div className="pointer-events-none absolute -left-16 top-24 h-64 w-64 rounded-full bg-primary/5 blur-3xl" />
|
<div className="pointer-events-none absolute -left-16 top-24 h-64 w-64 rounded-full bg-primary/5 blur-3xl" />
|
||||||
<div className="pointer-events-none absolute -right-20 bottom-10 h-64 w-64 rounded-full bg-secondary/5 blur-3xl" />
|
<div className="pointer-events-none absolute -right-20 bottom-10 h-64 w-64 rounded-full bg-secondary/5 blur-3xl" />
|
||||||
|
|
||||||
{/*
|
|
||||||
* Grid layout:
|
|
||||||
* mobile (<768px) : 1 col — text → cards (1/row) → success story [via CSS order]
|
|
||||||
* tablet (768–1279px) : 1 col — text → success story → cards 2×2 [via CSS order]
|
|
||||||
* desktop (≥1280px) : 2 col — [left: text + success story] | [right: cards 2×2]
|
|
||||||
*/}
|
|
||||||
<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">
|
||||||
|
|
||||||
{/* ── 1. Left text content (always col-1 row-1) ── */}
|
{/* ── 1. Left text content ── */}
|
||||||
<div className="min-w-0 xl:col-start-1 xl:row-start-1">
|
<motion.div
|
||||||
|
className="min-w-0 xl:col-start-1 xl:row-start-1"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{/* Badge */}
|
{/* Badge */}
|
||||||
<div className="flex w-fit items-center gap-2 rounded-full border border-secondary/20 bg-secondary/5 px-3 py-1.5">
|
<div className="flex w-fit items-center gap-2 rounded-full border border-secondary/20 bg-secondary/5 px-3 py-1.5">
|
||||||
<div className="flex h-5 w-5 items-center justify-center rounded-full bg-secondary/15">
|
<div className="flex h-5 w-5 items-center justify-center rounded-full bg-secondary/15">
|
||||||
@@ -133,20 +137,23 @@ export function BusinessSection() {
|
|||||||
<MaterialIcon icon="arrow_forward" size={18} />
|
<MaterialIcon icon="arrow_forward" size={18} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* ── 2. Feature cards — 2×2 grid (right col on desktop) ──
|
{/* ── 2. Feature cards ── */}
|
||||||
mobile : order-2 → appears after text, before success story
|
<motion.div
|
||||||
tablet : order-3 → appears after text + success story
|
className="min-w-0
|
||||||
desktop : explicit col-2, row-span-2
|
|
||||||
*/}
|
|
||||||
<div className="min-w-0
|
|
||||||
order-2 md:order-3
|
order-2 md:order-3
|
||||||
xl:order-none xl:col-start-2 xl:row-start-1 xl:row-span-2
|
xl:order-none xl:col-start-2 xl:row-start-1 xl:row-span-2
|
||||||
grid grid-cols-1 gap-4 md:grid-cols-2">
|
grid grid-cols-1 gap-4 md:grid-cols-2"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{FEATURES.map((feature) => (
|
{FEATURES.map((feature) => (
|
||||||
<article
|
<motion.article
|
||||||
key={feature.title}
|
key={feature.title}
|
||||||
|
variants={fadeUp}
|
||||||
className="merchant-feature-card flex min-w-0 flex-col overflow-hidden rounded-[1.5rem] border border-primary/10 bg-white shadow-[0_4px_24px_rgba(22,0,29,0.07)]"
|
className="merchant-feature-card flex min-w-0 flex-col overflow-hidden rounded-[1.5rem] border border-primary/10 bg-white shadow-[0_4px_24px_rgba(22,0,29,0.07)]"
|
||||||
>
|
>
|
||||||
<div className="p-5">
|
<div className="p-5">
|
||||||
@@ -174,20 +181,22 @@ export function BusinessSection() {
|
|||||||
className="block h-auto w-full object-cover"
|
className="block h-auto w-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</motion.article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* ── 3. Success story ──
|
{/* ── 3. Success story ── */}
|
||||||
mobile : order-3 → appears after feature cards
|
<motion.div
|
||||||
tablet : order-2 → appears after text, before cards
|
className="min-w-0
|
||||||
desktop : explicit col-1, row-2 (below text in left column)
|
|
||||||
*/}
|
|
||||||
<div className="min-w-0
|
|
||||||
order-3 md:order-2
|
order-3 md:order-2
|
||||||
xl:order-none xl:col-start-1 xl:row-start-2
|
xl:order-none xl:col-start-1 xl:row-start-2
|
||||||
overflow-hidden rounded-[1.5rem] border border-primary/10 bg-white/90 shadow-[0_4px_24px_rgba(22,0,29,0.07)]
|
overflow-hidden rounded-[1.5rem] border border-primary/10 bg-white/90 shadow-[0_4px_24px_rgba(22,0,29,0.07)]
|
||||||
grid grid-cols-[100px_minmax(0,1fr)]">
|
grid grid-cols-[100px_minmax(0,1fr)]"
|
||||||
|
variants={scaleIn}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<div className="relative min-h-[128px] overflow-hidden bg-brand-accent-light">
|
<div className="relative min-h-[128px] overflow-hidden bg-brand-accent-light">
|
||||||
<Image
|
<Image
|
||||||
src="/images/nearle-merchant/success-owner.webp"
|
src="/images/nearle-merchant/success-owner.webp"
|
||||||
@@ -208,14 +217,20 @@ export function BusinessSection() {
|
|||||||
Nearle helped us reach more customers in our area and manage orders effortlessly.
|
Nearle helped us reach more customers in our area and manage orders effortlessly.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Benefits strip ── */}
|
{/* ── Benefits strip ── */}
|
||||||
<div className="relative mt-6 overflow-hidden rounded-[1.5rem] border border-primary/10 bg-white/90 shadow-[0_4px_24px_rgba(22,0,29,0.06)]">
|
<motion.div
|
||||||
|
className="relative mt-6 overflow-hidden rounded-[1.5rem] border border-primary/10 bg-white/90 shadow-[0_4px_24px_rgba(22,0,29,0.06)]"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 divide-y sm:divide-y-0 sm:divide-x divide-primary/8 xl:divide-x xl:divide-y-0">
|
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 divide-y sm:divide-y-0 sm:divide-x divide-primary/8 xl:divide-x xl:divide-y-0">
|
||||||
{BENEFITS.map((b) => (
|
{BENEFITS.map((b) => (
|
||||||
<div key={b.title} className="flex items-start gap-3 p-5">
|
<motion.div key={b.title} variants={fadeIn} className="flex items-start gap-3 p-5">
|
||||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-secondary/10 text-secondary">
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-secondary/10 text-secondary">
|
||||||
<MaterialIcon icon={b.icon} size={20} />
|
<MaterialIcon icon={b.icon} size={20} />
|
||||||
</div>
|
</div>
|
||||||
@@ -223,10 +238,10 @@ export function BusinessSection() {
|
|||||||
<div className="text-sm font-black text-brand-dark">{b.title}</div>
|
<div className="text-sm font-black text-brand-dark">{b.title}</div>
|
||||||
<div className="mt-0.5 text-xs leading-relaxed text-on-surface-variant">{b.desc}</div>
|
<div className="mt-0.5 text-xs leading-relaxed text-on-surface-variant">{b.desc}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
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 { MaterialIcon } from "@/components/ui/MaterialIcon";
|
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
||||||
|
import {
|
||||||
|
fadeUp,
|
||||||
|
slideInRight,
|
||||||
|
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" },
|
||||||
@@ -50,22 +59,39 @@ export function Hero() {
|
|||||||
<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">
|
||||||
|
|
||||||
{/* LEFT COLUMN: Editorial Typography & Copy */}
|
{/* LEFT COLUMN: Editorial Typography & Copy */}
|
||||||
<div className="flex flex-col text-left max-w-xl z-20 relative">
|
<motion.div
|
||||||
<span 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="flex flex-col text-left max-w-xl z-20 relative"
|
||||||
|
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-[#6D28D9]/10 text-[#6D28D9] uppercase mb-6 w-fit"
|
||||||
|
>
|
||||||
Welcome to Nearle
|
Welcome to Nearle
|
||||||
</span>
|
</motion.span>
|
||||||
|
|
||||||
<h1 className="font-display text-4xl sm:text-5xl lg:text-[68px] xl:text-[76px] font-black text-[#16001D] leading-[0.95] tracking-[-0.05em]">
|
<motion.h1
|
||||||
|
variants={fadeUp}
|
||||||
|
className="font-display text-4xl sm:text-5xl lg:text-[68px] xl:text-[76px] font-black text-[#16001D] leading-[0.95] tracking-[-0.05em]"
|
||||||
|
>
|
||||||
Everything Local.
|
Everything Local.
|
||||||
<br />
|
<br />
|
||||||
Everything <span className="text-[#6D28D9]">Near.</span>
|
Everything <span className="text-[#6D28D9]">Near.</span>
|
||||||
</h1>
|
</motion.h1>
|
||||||
|
|
||||||
<p className="max-w-[480px] text-base md:text-lg text-[#6D6172] leading-relaxed font-bold mt-6">
|
<motion.p
|
||||||
|
variants={fadeUp}
|
||||||
|
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.
|
||||||
</p>
|
</motion.p>
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto">
|
<motion.div
|
||||||
|
variants={fadeUp}
|
||||||
|
className="flex flex-col sm:flex-row gap-3.5 mt-8 w-full sm:w-auto"
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
href="/people"
|
href="/people"
|
||||||
className="bg-[#6D28D9] hover:bg-[#5225b8] text-white font-black px-8 py-3.5 rounded-xl text-sm md:text-base text-center shadow-md hover:shadow-lg transition-all duration-200 active:scale-[0.98]"
|
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]"
|
||||||
@@ -78,11 +104,16 @@ export function Hero() {
|
|||||||
>
|
>
|
||||||
Get the App
|
Get the App
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* RIGHT COLUMN: Creative Overlapping Strips Collage */}
|
{/* RIGHT COLUMN: Creative Overlapping Strips Collage */}
|
||||||
<div className="w-full z-20">
|
<motion.div
|
||||||
|
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">
|
||||||
@@ -155,15 +186,21 @@ export function Hero() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ─── BOTTOM PROOF STATS ROW ─── */}
|
{/* ─── BOTTOM PROOF STATS ROW ─── */}
|
||||||
<div className="border-t border-[#EEE7F4]/80 bg-white">
|
<div className="border-t border-[#EEE7F4]/80 bg-white">
|
||||||
<div className="page-container py-8 grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-4 relative z-20">
|
<motion.div
|
||||||
|
className="page-container py-8 grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-4 relative z-20"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{PROOF_ITEMS.map((item, idx) => (
|
{PROOF_ITEMS.map((item, idx) => (
|
||||||
<div key={item.label} className="flex items-center gap-4 text-left md:justify-center relative">
|
<motion.div key={item.label} variants={fadeUp} className="flex items-center gap-4 text-left md:justify-center relative">
|
||||||
{/* Subtle vertical separator for desktop, except for the first item */}
|
{/* 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]" />
|
||||||
@@ -179,9 +216,9 @@ export function Hero() {
|
|||||||
{item.text}
|
{item.text}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
||||||
|
import {
|
||||||
|
fadeUp,
|
||||||
|
scaleIn,
|
||||||
|
staggerContainer,
|
||||||
|
slideInLeft,
|
||||||
|
slideInRight,
|
||||||
|
viewport,
|
||||||
|
} from "@/lib/animations";
|
||||||
|
|
||||||
const STEPS = [
|
const STEPS = [
|
||||||
{
|
{
|
||||||
@@ -174,7 +185,6 @@ function PhoneMapVisual() {
|
|||||||
|
|
||||||
function StepVisual({ type }: { type: string }) {
|
function StepVisual({ type }: { type: string }) {
|
||||||
if (type === "store") {
|
if (type === "store") {
|
||||||
// Inset box gives the storefront image breathing room on all sides
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute inset-x-4 bottom-0 top-2">
|
<div className="absolute inset-x-4 bottom-0 top-2">
|
||||||
<div className="relative h-full w-full">
|
<div className="relative h-full w-full">
|
||||||
@@ -198,7 +208,6 @@ function StepVisual({ type }: { type: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bag: chips on the left, image on the right – keep bag from overflowing right edge
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full items-end justify-end gap-2 px-2">
|
<div className="flex h-full items-end justify-end gap-2 px-2">
|
||||||
<div className="mb-4 hidden shrink-0 space-y-2 sm:block">
|
<div className="mb-4 hidden shrink-0 space-y-2 sm:block">
|
||||||
@@ -227,7 +236,13 @@ export function HowItWorks() {
|
|||||||
{/* HERO AREA */}
|
{/* HERO AREA */}
|
||||||
<div className="mx-auto w-[calc(100%-96px)] max-w-[1720px]">
|
<div className="mx-auto w-[calc(100%-96px)] max-w-[1720px]">
|
||||||
<div className="grid min-w-0 grid-cols-1 items-center gap-10 lg:grid-cols-[38fr_62fr]">
|
<div className="grid min-w-0 grid-cols-1 items-center gap-10 lg:grid-cols-[38fr_62fr]">
|
||||||
<div className="min-w-0">
|
<motion.div
|
||||||
|
className="min-w-0"
|
||||||
|
variants={slideInLeft}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<span className="inline-flex items-center gap-2 rounded-full border border-secondary/15 bg-secondary/5 px-4 py-2 text-xs font-black uppercase tracking-widest text-secondary">
|
<span className="inline-flex items-center gap-2 rounded-full border border-secondary/15 bg-secondary/5 px-4 py-2 text-xs font-black uppercase tracking-widest text-secondary">
|
||||||
<MaterialIcon icon="eco" size={16} />
|
<MaterialIcon icon="eco" size={16} />
|
||||||
Together, We Grow
|
Together, We Grow
|
||||||
@@ -245,9 +260,15 @@ export function HowItWorks() {
|
|||||||
<MaterialIcon icon="forum" size={20} />
|
<MaterialIcon icon="forum" size={20} />
|
||||||
Talk to an Expert
|
Talk to an Expert
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="relative flex min-h-[320px] justify-center md:min-h-[440px] xl:min-h-[400px]">
|
<motion.div
|
||||||
|
className="relative flex min-h-[320px] justify-center md:min-h-[440px] xl:min-h-[400px]"
|
||||||
|
variants={scaleIn}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<div className="relative w-full max-w-[680px]">
|
<div className="relative w-full max-w-[680px]">
|
||||||
<div className="relative h-[280px] overflow-hidden rounded-[2rem] bg-brand-accent-light shadow-[0_26px_70px_rgba(22,0,29,0.16)] md:h-[440px] xl:h-[400px]">
|
<div className="relative h-[280px] overflow-hidden rounded-[2rem] bg-brand-accent-light shadow-[0_26px_70px_rgba(22,0,29,0.16)] md:h-[440px] xl:h-[400px]">
|
||||||
<Image
|
<Image
|
||||||
@@ -263,7 +284,7 @@ export function HowItWorks() {
|
|||||||
<StorePhone />
|
<StorePhone />
|
||||||
<GrowthPill />
|
<GrowthPill />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -274,7 +295,13 @@ export function HowItWorks() {
|
|||||||
|
|
||||||
{/* CARDS SECTION */}
|
{/* CARDS SECTION */}
|
||||||
<div className="mx-auto w-full max-w-[2200px] px-6 lg:px-10 xl:px-12">
|
<div className="mx-auto w-full max-w-[2200px] px-6 lg:px-10 xl:px-12">
|
||||||
<div className="mt-10 text-center md:mt-14 xl:mt-10">
|
<motion.div
|
||||||
|
className="mt-10 text-center md:mt-14 xl:mt-10"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<span className="inline-flex rounded-full bg-primary/8 px-4 py-2 text-xs font-black uppercase tracking-widest text-primary">
|
<span className="inline-flex rounded-full bg-primary/8 px-4 py-2 text-xs font-black uppercase tracking-widest text-primary">
|
||||||
How It Works
|
How It Works
|
||||||
</span>
|
</span>
|
||||||
@@ -284,12 +311,19 @@ export function HowItWorks() {
|
|||||||
<p className="mx-auto mt-4 max-w-2xl text-base leading-relaxed text-on-surface-variant md:text-lg">
|
<p className="mx-auto mt-4 max-w-2xl text-base leading-relaxed text-on-surface-variant md:text-lg">
|
||||||
Three simple milestones to scale your retail reach from offline streets straight into neighbourhood homes.
|
Three simple milestones to scale your retail reach from offline streets straight into neighbourhood homes.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="mt-10 grid min-w-0 grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
<motion.div
|
||||||
|
className="mt-10 grid min-w-0 grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{STEPS.map((step) => (
|
{STEPS.map((step) => (
|
||||||
<article
|
<motion.article
|
||||||
key={step.num}
|
key={step.num}
|
||||||
|
variants={fadeUp}
|
||||||
className="relative flex min-h-[380px] min-w-0 flex-col overflow-hidden rounded-3xl border border-primary/10 bg-white p-7 shadow-[0_18px_45px_rgba(22,0,29,0.08)] md:min-h-[420px] lg:min-h-[460px] xl:min-h-[420px] lg:p-8"
|
className="relative flex min-h-[380px] min-w-0 flex-col overflow-hidden rounded-3xl border border-primary/10 bg-white p-7 shadow-[0_18px_45px_rgba(22,0,29,0.08)] md:min-h-[420px] lg:min-h-[460px] xl:min-h-[420px] lg:p-8"
|
||||||
>
|
>
|
||||||
<span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-primary/[0.07]">
|
<span className="absolute right-5 top-4 font-display text-6xl font-black leading-none text-primary/[0.07]">
|
||||||
@@ -309,9 +343,9 @@ export function HowItWorks() {
|
|||||||
<div className={`relative z-0 mt-auto w-full overflow-hidden rounded-[1.5rem] bg-brand-accent-light/60 ${step.visual === "phone" ? "h-64 lg:h-72" : "h-48 lg:h-52"}`}>
|
<div className={`relative z-0 mt-auto w-full overflow-hidden rounded-[1.5rem] bg-brand-accent-light/60 ${step.visual === "phone" ? "h-64 lg:h-72" : "h-48 lg:h-52"}`}>
|
||||||
<StepVisual type={step.visual} />
|
<StepVisual type={step.visual} />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</motion.article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
||||||
|
import { fadeUp, slideInLeft, staggerContainer, viewport } from "@/lib/animations";
|
||||||
|
|
||||||
const FEATURES = [
|
const FEATURES = [
|
||||||
{
|
{
|
||||||
@@ -37,7 +41,13 @@ export function HyperlocalFeatures() {
|
|||||||
<div className="grid grid-cols-1 lg:grid-cols-[0.95fr_1.05fr] gap-8 lg:gap-10 items-center">
|
<div className="grid grid-cols-1 lg:grid-cols-[0.95fr_1.05fr] gap-8 lg:gap-10 items-center">
|
||||||
|
|
||||||
{/* Left Column: Visual Storytelling */}
|
{/* Left Column: Visual Storytelling */}
|
||||||
<div className="w-full max-w-[880px] justify-self-center relative rounded-[2rem] border border-outline-variant/25 bg-[#F8F3FC] z-10 overflow-hidden aspect-[10/11] lg:h-[580px] shadow-[0_24px_80px_rgba(101,20,154,0.10)]">
|
<motion.div
|
||||||
|
className="w-full max-w-[880px] justify-self-center relative rounded-[2rem] border border-outline-variant/25 bg-[#F8F3FC] z-10 overflow-hidden aspect-[10/11] lg:h-[580px] shadow-[0_24px_80px_rgba(101,20,154,0.10)]"
|
||||||
|
variants={slideInLeft}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_52%_42%,rgba(124,58,237,0.12),transparent_34%),linear-gradient(145deg,rgba(255,255,255,0.94),rgba(243,232,246,0.62))]" />
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_52%_42%,rgba(124,58,237,0.12),transparent_34%),linear-gradient(145deg,rgba(255,255,255,0.94),rgba(243,232,246,0.62))]" />
|
||||||
<div className="absolute inset-6 rounded-[1.5rem] overflow-hidden bg-white/55 ring-1 ring-white/80 shadow-[inset_0_1px_0_rgba(255,255,255,0.9)]">
|
<div className="absolute inset-6 rounded-[1.5rem] overflow-hidden bg-white/55 ring-1 ring-white/80 shadow-[inset_0_1px_0_rgba(255,255,255,0.9)]">
|
||||||
<svg
|
<svg
|
||||||
@@ -135,31 +145,54 @@ export function HyperlocalFeatures() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Right Column: Feature Panel directly on page background */}
|
{/* Right Column: Feature Panel directly on page background */}
|
||||||
<div className="w-full py-6 md:py-8 lg:py-10 pl-4 md:pl-8 lg:pl-12 xl:pl-44 relative z-10 text-[#16001D]">
|
<div className="w-full py-6 md:py-8 lg:py-10 pl-4 md:pl-8 lg:pl-12 xl:pl-44 relative z-10 text-[#16001D]">
|
||||||
<span className="text-xs text-primary uppercase tracking-widest font-black bg-primary/10 px-3.5 py-1.5 rounded-full border border-primary/20">
|
<motion.span
|
||||||
|
className="text-xs text-primary uppercase tracking-widest font-black bg-primary/10 px-3.5 py-1.5 rounded-full border border-primary/20"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
Community & Trust
|
Community & Trust
|
||||||
</span>
|
</motion.span>
|
||||||
|
|
||||||
<h3 className="text-4xl md:text-5xl lg:text-[44px] font-display font-black text-brand-dark mt-4 mb-6 leading-tight tracking-tight">
|
<motion.h3
|
||||||
|
className="text-4xl md:text-5xl lg:text-[44px] font-display font-black text-brand-dark mt-4 mb-6 leading-tight tracking-tight"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
Built Around Your<br />
|
Built Around Your<br />
|
||||||
<span className="text-primary font-black">Neighbourhood</span>
|
<span className="text-primary font-black">Neighbourhood</span>
|
||||||
</h3>
|
</motion.h3>
|
||||||
|
|
||||||
<p className="text-body-lg text-on-surface-variant mb-10 max-w-lg leading-relaxed">
|
<motion.p
|
||||||
|
className="text-body-lg text-on-surface-variant mb-10 max-w-lg leading-relaxed"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
Nearle brings the convenience of an app to the stores you already know and trust, making it easy to support local shops.
|
Nearle brings the convenience of an app to the stores you already know and trust, making it easy to support local shops.
|
||||||
</p>
|
</motion.p>
|
||||||
|
|
||||||
{/* Features List with vertical connector line */}
|
{/* Features List with vertical connector line */}
|
||||||
<div className="relative space-y-8">
|
<motion.div
|
||||||
|
className="relative space-y-8"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{/* Vertical connector line */}
|
{/* Vertical connector line */}
|
||||||
<div className="absolute left-6 top-6 bottom-6 w-0.5 bg-primary/20 -translate-x-1/2 z-0" />
|
<div className="absolute left-6 top-6 bottom-6 w-0.5 bg-primary/20 -translate-x-1/2 z-0" />
|
||||||
|
|
||||||
{FEATURES.map((item) => (
|
{FEATURES.map((item) => (
|
||||||
<div key={item.title} className="flex gap-5 group relative z-10">
|
<motion.div key={item.title} variants={fadeUp} className="flex gap-5 group relative z-10">
|
||||||
|
|
||||||
{/* Icon circle */}
|
{/* Icon circle */}
|
||||||
<div className="flex-shrink-0 w-12 h-12 rounded-full bg-primary text-white flex items-center justify-center shadow-md relative z-10 group-hover:scale-105 transition-transform duration-300">
|
<div className="flex-shrink-0 w-12 h-12 rounded-full bg-primary text-white flex items-center justify-center shadow-md relative z-10 group-hover:scale-105 transition-transform duration-300">
|
||||||
@@ -178,15 +211,21 @@ export function HyperlocalFeatures() {
|
|||||||
{item.desc}
|
{item.desc}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Divider Line above CTA Area */}
|
{/* Divider Line above CTA Area */}
|
||||||
<hr className="border-t border-[#EEE7F4] my-6 relative z-10" />
|
<hr className="border-t border-[#EEE7F4] my-6 relative z-10" />
|
||||||
|
|
||||||
{/* Onboarding Call to Action */}
|
{/* Onboarding Call to Action */}
|
||||||
<div className="mt-2 flex flex-wrap gap-6 items-center relative z-10">
|
<motion.div
|
||||||
|
className="mt-2 flex flex-wrap gap-6 items-center relative z-10"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<Link
|
<Link
|
||||||
href="/#download"
|
href="/#download"
|
||||||
className="bg-primary hover:bg-primary-container text-white font-black px-8 py-3.5 rounded-full text-sm md:text-base hover:shadow-lg hover:-translate-y-0.5 active:translate-y-0 active:scale-95 transition-all duration-200"
|
className="bg-primary hover:bg-primary-container text-white font-black px-8 py-3.5 rounded-full text-sm md:text-base hover:shadow-lg hover:-translate-y-0.5 active:translate-y-0 active:scale-95 transition-all duration-200"
|
||||||
@@ -204,7 +243,7 @@ export function HyperlocalFeatures() {
|
|||||||
className="transition-transform group-hover:translate-x-1"
|
className="transition-transform group-hover:translate-x-1"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
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";
|
||||||
|
|
||||||
|
const MotionLink = motion(Link);
|
||||||
|
|
||||||
const CATEGORIES = [
|
const CATEGORIES = [
|
||||||
{
|
{
|
||||||
@@ -58,7 +64,13 @@ export function ServiceCategories() {
|
|||||||
<section className="section bg-white w-full">
|
<section className="section bg-white w-full">
|
||||||
<div className="page-container">
|
<div className="page-container">
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className="flex flex-col md:flex-row justify-between items-start md:items-end mb-10 md:mb-12 xl:mb-10 gap-6">
|
<motion.div
|
||||||
|
className="flex flex-col md:flex-row justify-between items-start md:items-end mb-10 md:mb-12 xl:mb-10 gap-6"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<span className="text-xs text-primary uppercase tracking-widest font-black bg-primary/10 px-3 py-1.5 rounded-full">
|
<span className="text-xs text-primary uppercase tracking-widest font-black bg-primary/10 px-3 py-1.5 rounded-full">
|
||||||
Neighbourhood Offerings
|
Neighbourhood Offerings
|
||||||
@@ -77,14 +89,22 @@ export function ServiceCategories() {
|
|||||||
View All Categories
|
View All Categories
|
||||||
<MaterialIcon icon="arrow_forward" size={18} className="transition-transform group-hover:translate-x-1" />
|
<MaterialIcon icon="arrow_forward" size={18} className="transition-transform group-hover:translate-x-1" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Categories Premium Grid */}
|
{/* Categories Premium Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
{/* fadeIn (opacity only) preserves .premium-card:hover CSS transform */}
|
||||||
|
<motion.div
|
||||||
|
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{CATEGORIES.map((cat) => (
|
{CATEGORIES.map((cat) => (
|
||||||
<Link
|
<MotionLink
|
||||||
key={cat.name}
|
key={cat.name}
|
||||||
href="/people"
|
href="/people"
|
||||||
|
variants={fadeIn}
|
||||||
className="bg-white rounded-[2rem] border border-outline-variant/10 shadow-lg overflow-hidden premium-card flex flex-col group h-full cursor-pointer"
|
className="bg-white rounded-[2rem] border border-outline-variant/10 shadow-lg overflow-hidden premium-card flex flex-col group h-full cursor-pointer"
|
||||||
>
|
>
|
||||||
{/* Image Container */}
|
{/* Image Container */}
|
||||||
@@ -132,11 +152,10 @@ export function ServiceCategories() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</MotionLink>
|
||||||
))}
|
))}
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
import { MaterialIcon } from "@/components/ui/MaterialIcon";
|
||||||
|
import { fadeUp, scaleIn, slideInRight, staggerContainer, viewport } from "@/lib/animations";
|
||||||
|
|
||||||
const FLOW_STEPS = [
|
const FLOW_STEPS = [
|
||||||
{ label: "Scan QR", icon: "qr_code_scanner" },
|
{ label: "Scan QR", icon: "qr_code_scanner" },
|
||||||
@@ -22,7 +26,13 @@ export function StoreQRSection() {
|
|||||||
<div className="hidden lg:grid lg:grid-cols-12 gap-12 xl:gap-16 items-center">
|
<div className="hidden lg:grid lg:grid-cols-12 gap-12 xl:gap-16 items-center">
|
||||||
|
|
||||||
{/* Left Column: Heading, flow steps, download buttons */}
|
{/* Left Column: Heading, flow steps, download buttons */}
|
||||||
<div className="lg:col-span-6">
|
<motion.div
|
||||||
|
className="lg:col-span-6"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<span className="text-xs text-brand-accent uppercase tracking-widest font-black bg-brand-accent/10 px-3.5 py-1.5 rounded-full border border-brand-accent/20">
|
<span className="text-xs text-brand-accent uppercase tracking-widest font-black bg-brand-accent/10 px-3.5 py-1.5 rounded-full border border-brand-accent/20">
|
||||||
Instant Connections & App
|
Instant Connections & App
|
||||||
</span>
|
</span>
|
||||||
@@ -83,12 +93,21 @@ export function StoreQRSection() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Right Column: Visual components (QR Sticker & Phone Mockup) */}
|
{/* Right Column: Visual components (QR Sticker & Phone Mockup) */}
|
||||||
<div className="lg:col-span-6 flex items-center justify-center gap-8 relative select-none">
|
<motion.div
|
||||||
|
className="lg:col-span-6 flex items-center justify-center gap-8 relative select-none"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{/* Small supporting visual: QR Sticker (Secondary) */}
|
{/* Small supporting visual: QR Sticker (Secondary) */}
|
||||||
<div className="relative w-[210px] shrink-0 aspect-[9/11.5] bg-white rounded-[2rem] p-6 text-brand-dark flex flex-col justify-between items-center shadow-2xl border-2 border-white/20">
|
<motion.div
|
||||||
|
variants={scaleIn}
|
||||||
|
className="relative w-[210px] shrink-0 aspect-[9/11.5] bg-white rounded-[2rem] p-6 text-brand-dark flex flex-col justify-between items-center shadow-2xl border-2 border-white/20"
|
||||||
|
>
|
||||||
<div className="text-center w-full">
|
<div className="text-center w-full">
|
||||||
<p className="text-[10px] font-black text-primary tracking-wider uppercase">Order Online</p>
|
<p className="text-[10px] font-black text-primary tracking-wider uppercase">Order Online</p>
|
||||||
<h4 className="font-display font-black text-brand-dark text-base mt-0.5 leading-none">Store QR</h4>
|
<h4 className="font-display font-black text-brand-dark text-base mt-0.5 leading-none">Store QR</h4>
|
||||||
@@ -108,10 +127,13 @@ export function StoreQRSection() {
|
|||||||
<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 animate-pulse" />
|
||||||
Scan to Shop
|
Scan to Shop
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Primary large visual: Phone Mockup (Significantly Larger) */}
|
{/* Primary large visual: Phone Mockup (Significantly Larger) */}
|
||||||
<div className="relative w-[290px] shrink-0 aspect-[9/18.5] bg-[#120217] rounded-[2.75rem] border-[7px] border-brand-dark-surface shadow-2xl overflow-hidden ring-4 ring-outline-variant/10">
|
<motion.div
|
||||||
|
variants={slideInRight}
|
||||||
|
className="relative w-[290px] shrink-0 aspect-[9/18.5] bg-[#120217] rounded-[2.75rem] border-[7px] border-brand-dark-surface shadow-2xl overflow-hidden ring-4 ring-outline-variant/10"
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/mobile image.jpeg"
|
src="/mobile image.jpeg"
|
||||||
alt="Nearle mobile app interface"
|
alt="Nearle mobile app interface"
|
||||||
@@ -119,16 +141,19 @@ export function StoreQRSection() {
|
|||||||
height={760}
|
height={760}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ─── MOBILE & TABLET LAYOUT (below lg) ─── */}
|
{/* ─── MOBILE & TABLET LAYOUT (below lg) ─── */}
|
||||||
<div className="lg:hidden flex flex-col gap-12 text-center">
|
<div className="lg:hidden flex flex-col gap-12 text-center">
|
||||||
{/* 1. Heading and description */}
|
{/* 1. Heading and description */}
|
||||||
<div>
|
<motion.div
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<span className="text-xs text-brand-accent uppercase tracking-widest font-black bg-brand-accent/10 px-3.5 py-1.5 rounded-full border border-brand-accent/20 inline-block">
|
<span className="text-xs text-brand-accent uppercase tracking-widest font-black bg-brand-accent/10 px-3.5 py-1.5 rounded-full border border-brand-accent/20 inline-block">
|
||||||
Instant Connections & App
|
Instant Connections & App
|
||||||
</span>
|
</span>
|
||||||
@@ -138,10 +163,16 @@ export function StoreQRSection() {
|
|||||||
<p className="text-body-md text-white/70 max-w-md mx-auto leading-relaxed">
|
<p className="text-body-md text-white/70 max-w-md mx-auto leading-relaxed">
|
||||||
Scan Nearle QRs to instantly save local stores, and order from them anytime in one convenient app.
|
Scan Nearle QRs to instantly save local stores, and order from them anytime in one convenient app.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* 2. QR Sticker */}
|
{/* 2. QR Sticker */}
|
||||||
<div className="mx-auto w-[240px] aspect-[9/11.5] bg-white rounded-[2rem] p-6 text-brand-dark flex flex-col justify-between items-center shadow-xl border-2 border-white/20 select-none">
|
<motion.div
|
||||||
|
className="mx-auto w-[240px] aspect-[9/11.5] bg-white rounded-[2rem] p-6 text-brand-dark flex flex-col justify-between items-center shadow-xl border-2 border-white/20 select-none"
|
||||||
|
variants={scaleIn}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<div className="text-center w-full">
|
<div className="text-center w-full">
|
||||||
<p className="text-[10px] font-black text-primary tracking-wider uppercase">Order Online</p>
|
<p className="text-[10px] font-black text-primary tracking-wider uppercase">Order Online</p>
|
||||||
<h4 className="font-display font-black text-brand-dark text-base mt-0.5">Store QR</h4>
|
<h4 className="font-display font-black text-brand-dark text-base mt-0.5">Store QR</h4>
|
||||||
@@ -161,7 +192,7 @@ export function StoreQRSection() {
|
|||||||
<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 animate-pulse" />
|
||||||
Scan to Shop
|
Scan to Shop
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* 3. Scan → Save → Order steps */}
|
{/* 3. Scan → Save → Order steps */}
|
||||||
<div className="flex items-center justify-center gap-4 max-w-sm mx-auto select-none">
|
<div className="flex items-center justify-center gap-4 max-w-sm mx-auto select-none">
|
||||||
@@ -183,7 +214,13 @@ export function StoreQRSection() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 4. Phone Mockup */}
|
{/* 4. Phone Mockup */}
|
||||||
<div className="mx-auto w-[240px] aspect-[9/18.5] bg-[#120217] rounded-[2.5rem] border-[5px] border-brand-dark-surface shadow-xl relative overflow-hidden select-none">
|
<motion.div
|
||||||
|
className="mx-auto w-[240px] aspect-[9/18.5] bg-[#120217] rounded-[2.5rem] border-[5px] border-brand-dark-surface shadow-xl relative overflow-hidden select-none"
|
||||||
|
variants={slideInRight}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/mobile image.jpeg"
|
src="/mobile image.jpeg"
|
||||||
alt="Nearle mobile app interface"
|
alt="Nearle mobile app interface"
|
||||||
@@ -191,7 +228,7 @@ export function StoreQRSection() {
|
|||||||
height={760}
|
height={760}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* 5. App store buttons — TODO: replace with real URLs once app is live */}
|
{/* 5. App store buttons — TODO: replace with real URLs once app is live */}
|
||||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
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";
|
||||||
|
|
||||||
const CARDS = [
|
const CARDS = [
|
||||||
{
|
{
|
||||||
@@ -41,7 +45,13 @@ export function WhyNearle() {
|
|||||||
<section className="section section-soft w-full overflow-hidden">
|
<section className="section section-soft w-full overflow-hidden">
|
||||||
<div className="page-container relative">
|
<div className="page-container relative">
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className="relative text-center mb-8 md:mb-10">
|
<motion.div
|
||||||
|
className="relative text-center mb-8 md:mb-10"
|
||||||
|
variants={fadeUp}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
<div className="pointer-events-none absolute -left-2 top-0 hidden h-36 w-36 overflow-hidden rounded-[2rem] bg-primary/5 shadow-[0_18px_45px_rgba(101,20,154,0.12)] md:block lg:-left-4 lg:h-44 lg:w-44">
|
<div className="pointer-events-none absolute -left-2 top-0 hidden h-36 w-36 overflow-hidden rounded-[2rem] bg-primary/5 shadow-[0_18px_45px_rgba(101,20,154,0.12)] md:block lg:-left-4 lg:h-44 lg:w-44">
|
||||||
<Image
|
<Image
|
||||||
src="/images/nearle-redesign/heading-shopper.webp"
|
src="/images/nearle-redesign/heading-shopper.webp"
|
||||||
@@ -69,13 +79,20 @@ export function WhyNearle() {
|
|||||||
<p className="text-body-lg text-on-surface-variant mt-2.5 max-w-xl mx-auto leading-relaxed">
|
<p className="text-body-lg text-on-surface-variant mt-2.5 max-w-xl mx-auto leading-relaxed">
|
||||||
Nearle brings the convenience of an app to the stores you already know and trust.
|
Nearle brings the convenience of an app to the stores you already know and trust.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Cards Grid */}
|
{/* Cards Grid — fadeIn preserves .premium-card:hover CSS transform */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 lg:gap-7">
|
<motion.div
|
||||||
|
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-5 lg:gap-7"
|
||||||
|
variants={staggerContainer}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={viewport}
|
||||||
|
>
|
||||||
{CARDS.map((card) => (
|
{CARDS.map((card) => (
|
||||||
<div
|
<motion.div
|
||||||
key={card.title}
|
key={card.title}
|
||||||
|
variants={fadeIn}
|
||||||
className="premium-card bg-white rounded-3xl p-5 md:p-6 border border-outline-variant/15 shadow-lg flex flex-col h-full hover:scale-[1.02] transition-transform duration-300"
|
className="premium-card bg-white rounded-3xl p-5 md:p-6 border border-outline-variant/15 shadow-lg flex flex-col h-full hover:scale-[1.02] transition-transform duration-300"
|
||||||
>
|
>
|
||||||
<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}`}>
|
||||||
@@ -96,11 +113,10 @@ export function WhyNearle() {
|
|||||||
className="object-cover"
|
className="object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
8
src/components/layout/MotionProvider.tsx
Normal file
8
src/components/layout/MotionProvider.tsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { MotionConfig } from "framer-motion";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export function MotionProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
return <MotionConfig reducedMotion="user">{children}</MotionConfig>;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { PremiumNavbar } from "./PremiumNavbar";
|
import { PremiumNavbar } from "./PremiumNavbar";
|
||||||
import { PremiumFooter } from "./PremiumFooter";
|
import { PremiumFooter } from "./PremiumFooter";
|
||||||
|
import { MotionProvider } from "./MotionProvider";
|
||||||
|
|
||||||
type PremiumPageShellProps = {
|
type PremiumPageShellProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -9,6 +10,7 @@ type PremiumPageShellProps = {
|
|||||||
|
|
||||||
export function PremiumPageShell({ children, className = "" }: PremiumPageShellProps) {
|
export function PremiumPageShell({ children, className = "" }: PremiumPageShellProps) {
|
||||||
return (
|
return (
|
||||||
|
<MotionProvider>
|
||||||
<div className={`min-h-screen bg-white font-body text-[#16001D] antialiased flex flex-col ${className}`}>
|
<div className={`min-h-screen bg-white font-body text-[#16001D] antialiased flex flex-col ${className}`}>
|
||||||
{/* Header/Navbar */}
|
{/* Header/Navbar */}
|
||||||
<PremiumNavbar />
|
<PremiumNavbar />
|
||||||
@@ -20,5 +22,6 @@ export function PremiumPageShell({ children, className = "" }: PremiumPageShellP
|
|||||||
|
|
||||||
<PremiumFooter />
|
<PremiumFooter />
|
||||||
</div>
|
</div>
|
||||||
|
</MotionProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
56
src/lib/animations/index.ts
Normal file
56
src/lib/animations/index.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import type { Variants } from "framer-motion";
|
||||||
|
|
||||||
|
export const fadeUp: Variants = {
|
||||||
|
hidden: { opacity: 0, y: 24 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: { duration: 0.5, ease: [0.22, 1, 0.36, 1] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use for elements with CSS transform-based hover effects (e.g. .premium-card:hover)
|
||||||
|
// to avoid inline-style override of CSS hover transforms.
|
||||||
|
export const fadeIn: Variants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.45, ease: "easeOut" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
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] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const slideInLeft: Variants = {
|
||||||
|
hidden: { opacity: 0, x: -40 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const slideInRight: Variants = {
|
||||||
|
hidden: { opacity: 0, x: 40 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
transition: { duration: 0.6, ease: [0.22, 1, 0.36, 1] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const staggerContainer: Variants = {
|
||||||
|
hidden: {},
|
||||||
|
visible: {
|
||||||
|
transition: { staggerChildren: 0.09 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const viewport = { once: true, margin: "-60px" } as const;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
const puppeteer = require('puppeteer-core');
|
const puppeteer = require('puppeteer-core');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user