"use client"; import { Fragment, useRef, type MouseEvent } from "react"; import Image from "next/image"; import Link from "next/link"; import { motion, useReducedMotion, useMotionValue, useSpring, useTransform } from "framer-motion"; import { MaterialIcon } from "@/components/ui/MaterialIcon"; import { MagneticButton } from "@/components/ui/MagneticButton"; import { HeroLogo } from "@/components/heroes/HeroLogo"; import { viewport } from "@/lib/animations"; const EASE = [0.22, 1, 0.36, 1] as [number, number, number, number]; // power4.out approximation, matches HeroLogo's entrance ease const EASE_LOGO = [0.16, 1, 0.3, 1] as [number, number, number, number]; // 1. Verified Store SVG function VerifiedStoreSVG({ reduce = false }: { reduce?: boolean }) { return (
); } // 2. Neighborhood Pin SVG function NeighborhoodPinSVG({ reduce = false }: { reduce?: boolean }) { return (
); } // 3. Happy Customers SVG function HappyCustomerSVG({ reduce = false }: { reduce?: boolean }) { return (
); } // 4. Trusted Star SVG function TrustedStarSVG({ reduce = false }: { reduce?: boolean }) { return (
); } const PROOF_ITEMS = [ { label: "Verified", text: "Local Stores", dotColor: "bg-[#6D28D9]", renderSVG: (reduce: boolean) => , }, { label: "Neighborhood", text: "Coverage", dotColor: "bg-[#A855F7]", renderSVG: (reduce: boolean) => , }, { label: "Growing", text: "Happy Customers", dotColor: "bg-[#EC4899]", renderSVG: (reduce: boolean) => , }, { label: "Trusted", text: "Local Platform", dotColor: "bg-[#F59E0B]", renderSVG: (reduce: boolean) => , }, ]; // Shared cascade timing: logo settles at ~1.2s, then badge → heading → description const badgeVariant = { hidden: { opacity: 0, y: 25 }, visible: { opacity: 1, y: 0, transition: { duration: 0.6, delay: 0.9, ease: EASE } }, }; const lineStagger = { hidden: {}, visible: { transition: { staggerChildren: 0.13, delayChildren: 1.0 } }, }; const lineReveal = { hidden: { y: 60, opacity: 0, filter: "blur(8px)" }, visible: { y: 0, opacity: 1, filter: "blur(0px)", transition: { duration: 0.9, ease: EASE_LOGO } }, }; const descVariant = { hidden: { opacity: 0, y: 30 }, visible: { opacity: 1, y: 0, transition: { duration: 0.6, delay: 1.4, ease: EASE } }, }; const ctaVariant = { hidden: { opacity: 0, y: 22 }, visible: { opacity: 1, y: 0, transition: { duration: 0.55, delay: 1.6, 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 shouldReduce = useReducedMotion(); const sectionRef = useRef(null); // Subtle mouse parallax: background drifts 5px toward the cursor const mouseX = useMotionValue(0); const mouseY = useMotionValue(0); const springX = useSpring(mouseX, { stiffness: 60, damping: 20, mass: 0.5 }); const springY = useSpring(mouseY, { stiffness: 60, damping: 20, mass: 0.5 }); const bgParallaxX = useTransform(springX, [-0.5, 0.5], [-5, 5]); const bgParallaxY = useTransform(springY, [-0.5, 0.5], [-5, 5]); function handleMouseMove(e: MouseEvent) { if (shouldReduce) return; const rect = e.currentTarget.getBoundingClientRect(); mouseX.set((e.clientX - rect.left) / rect.width - 0.5); mouseY.set((e.clientY - rect.top) / rect.height - 0.5); } function handleMouseLeave() { mouseX.set(0); mouseY.set(0); } return (
{/* `compact` logo spacing: this hero now carries CTAs and the evolution strip inside it, the same reason BrandsHero uses it. */}
{/* Badge */} Welcome to Nearle {/* H1: line-by-line reveal. Two lines, with the closing word highlighted in the Nearle purple. */} Everything Local. Everything Near. {/* Description */}

Discover verified local stores, exclusive offers, fast delivery, and a better way to shop within your neighbourhood.

{/* CTAs */} Discover Nearby Stores See How Nearle Works
{/* ─── BOTTOM PROOF STATS ROW ─── */}
{/* Floating Particle Glow Dots on Hover */}
{PROOF_ITEMS.map((item, idx) => ( {/* Corner Hover Glowing Dot on Item */}
{item.renderSVG(!!shouldReduce)}
{item.label}
{item.text}
{idx < PROOF_ITEMS.length - 1 && (
); }