Files
route-commerce/src/components/landing/HeroSection.tsx
T

1057 lines
45 KiB
TypeScript

"use client";
import React, { useEffect, useRef, useState } from "react";
import Link from "next/link";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
// Register GSAP plugins
if (typeof window !== "undefined") {
gsap.registerPlugin(ScrollTrigger);
}
// ─────────────────────────────────────────────────────────────────────────────
// CINEMATIC HERO SECTION - APPLE-STYLE SCROLL-DRIVEN ANIMATIONS
// ─────────────────────────────────────────────────────────────────────────────
function scrollToContent() {
const nextSection = document.getElementById("story");
if (nextSection) {
nextSection.scrollIntoView({ behavior: "smooth" });
}
}
export default function HeroSection() {
const containerRef = useRef<HTMLDivElement>(null);
const heroRef = useRef<HTMLElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const [mounted, setMounted] = useState(false);
const [scrollProgress, setScrollProgress] = useState(0);
useEffect(() => {
const timer = setTimeout(() => setMounted(true), 100);
return () => clearTimeout(timer);
}, []);
// ─── GSAP SCROLL ANIMATIONS ────────────────────────────────────────────────
// Resilient: respects reduced-motion, safe GSAP target checks, reliable counter impl, proper scope
useEffect(() => {
if (!mounted || !heroRef.current || !contentRef.current) return;
const prefersReducedMotion =
typeof window !== "undefined" &&
window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
// For reduced motion: show final states immediately (no scroll-driven anims to avoid jank/warnings)
if (prefersReducedMotion) {
const counterEls = document.querySelectorAll<Element>(".counter-animate");
counterEls.forEach((el) => {
const targetStr = el.getAttribute("data-target") || "0";
const target = parseInt(targetStr, 10);
el.textContent = target.toLocaleString();
});
// Still allow basic scroll progress bar if wanted, but skip all ScrollTrigger/GSAP heavy
return;
}
const ctx = gsap.context(() => {
// Scroll progress tracking
ScrollTrigger.create({
trigger: heroRef.current,
start: "top top",
end: "bottom top",
scrub: true,
onUpdate: (self) => {
setScrollProgress(self.progress);
},
});
// Hero content pin - fade out on scroll
gsap.to(contentRef.current, {
y: -120,
opacity: 0,
scale: 0.95,
ease: "power2.inOut",
scrollTrigger: {
trigger: heroRef.current,
start: "top top",
end: "40% top",
scrub: 1.2,
},
});
// Hero visual scale + parallax
gsap.to(".hero-visual", {
scale: 1.25,
y: 80,
ease: "power2.inOut",
scrollTrigger: {
trigger: heroRef.current,
start: "top top",
end: "50% top",
scrub: 1.5,
},
});
// Staggered entrance for content elements
const contentElements = gsap.utils.toArray<Element>(".hero-reveal");
contentElements.forEach((el, i) => {
gsap.fromTo(
el,
{ opacity: 0, y: 50, scale: 0.95 },
{
opacity: 1,
y: 0,
scale: 1,
duration: 1.2,
ease: "power3.out",
delay: i * 0.15,
scrollTrigger: {
trigger: el,
start: "top 90%",
toggleActions: "play none none reverse",
},
}
);
});
// Parallax floating elements
const floatElements = gsap.utils.toArray<Element>(".parallax-float");
floatElements.forEach((el, i) => {
gsap.to(el, {
y: -100 - i * 20,
scrollTrigger: {
trigger: el,
start: "top bottom",
end: "bottom top",
scrub: 1,
},
});
});
// Scroll indicator fade on scroll
gsap.to(".scroll-indicator", {
y: 20,
opacity: 0,
scrollTrigger: {
trigger: heroRef.current,
start: "10% top",
end: "30% top",
scrub: 1,
},
});
// Staggered text reveal on scroll for other sections
gsap.utils.toArray<Element>(".reveal-text").forEach((el) => {
gsap.fromTo(
el,
{ opacity: 0, y: 60, skewY: 3 },
{
opacity: 1,
y: 0,
skewY: 0,
duration: 1.2,
ease: "power3.out",
scrollTrigger: {
trigger: el,
start: "top 85%",
end: "top 50%",
toggleActions: "play none none reverse",
},
}
);
});
// Scale reveal for cards
gsap.utils.toArray<Element>(".reveal-scale").forEach((el, i) => {
gsap.fromTo(
el,
{ opacity: 0, scale: 0.85, y: 50 },
{
opacity: 1,
scale: 1,
y: 0,
duration: 0.8,
delay: i * 0.1,
ease: "back.out(1.4)",
scrollTrigger: {
trigger: el,
start: "top 85%",
toggleActions: "play none none reverse",
},
}
);
});
// Counter animations - fixed: use reliable proxy object (avoids textContent tween pitfalls that left counters at 0)
// + always scoped + fallback path (though reduced handled above) + !gsap guard per requirements
const counterElements = gsap.utils.toArray<Element>(".counter-animate");
const hasGSAP = typeof gsap !== "undefined" && typeof gsap.to === "function";
counterElements.forEach((el) => {
const targetStr = el.getAttribute("data-target") || "0";
const target = parseInt(targetStr, 10);
el.textContent = "0"; // ensure start
if (hasGSAP && gsap) {
const obj = { val: 0 };
gsap.to(obj, {
val: target,
duration: 2.5,
ease: "power2.out",
scrollTrigger: {
trigger: el,
start: "top 85%",
toggleActions: "play none none none",
},
onUpdate: () => {
if (el.textContent !== null) {
el.textContent = Math.floor(obj.val).toLocaleString();
}
},
});
} else {
// pure fallback using IO + rAF (IntersectionObserver + requestAnimationFrame) if no GSAP
const io = new IntersectionObserver((entries) => {
if (entries[0]?.isIntersecting) {
io.disconnect();
const startTime = Date.now();
const dur = 2000;
const tick = () => {
const p = Math.min((Date.now() - startTime) / dur, 1);
const eased = 1 - Math.pow(1 - p, 3);
const val = Math.floor(target * eased);
el.textContent = val.toLocaleString();
if (p < 1) requestAnimationFrame(tick);
else el.textContent = target.toLocaleString();
};
tick();
}
}, { threshold: 0.3 });
io.observe(el);
}
});
}, containerRef);
return () => ctx.revert();
}, [mounted]);
return (
<>
{/* ─── SCROLL PROGRESS BAR ──────────────────────────────────────────── */}
<div className="fixed top-0 left-0 right-0 h-1 z-[1000] bg-white/10">
<div
className="h-full bg-gradient-to-r from-amber-500 via-amber-400 to-sage-500"
style={{
transform: `scaleX(${scrollProgress})`,
transformOrigin: "left",
transition: "transform 0.1s ease-out",
}}
/>
</div>
{/* ─── HERO SECTION - SCROLL-DRIVEN PIN ───────────────────────────── */}
{/* containerRef wraps animated sections for proper GSAP context scope (fixes "Invalid scope" + missing targets) */}
<div ref={containerRef}>
<section
ref={heroRef}
className="hero-section relative min-h-screen flex items-center overflow-hidden"
style={{
background: "linear-gradient(180deg, #faf8f5 0%, #fef7f0 50%, #fdf6f0 100%)",
}}
>
{/* ─── PARALLAX BACKGROUND LAYERS ─────────────────────────────────────── */}
<div className="parallax-bg-layer absolute inset-0 pointer-events-none">
{/* Gradient orbs */}
<div className="parallax-float absolute top-20 right-20 w-80 h-80 rounded-full opacity-30 blur-3xl"
style={{
background: "radial-gradient(circle, rgba(201, 122, 62, 0.4) 0%, transparent 70%)",
animation: "hs-float-slow 900ms ease-in-out infinite",
}} />
<div className="parallax-float absolute bottom-40 left-10 w-96 h-96 rounded-full opacity-25 blur-3xl"
style={{
background: "radial-gradient(circle, rgba(107, 185, 165, 0.4) 0%, transparent 70%)",
animation: "hs-float-slow-delayed 950ms ease-in-out infinite",
}} />
{/* Grid pattern */}
<div
className="absolute inset-0 opacity-[0.03]"
style={{
backgroundImage: `
linear-gradient(#4a7c94 1px, transparent 1px),
linear-gradient(90deg, #4a7c94 1px, transparent 1px)
`,
backgroundSize: "80px 80px",
}}
/>
</div>
{/* ─── HERO CONTENT ────────────────────────────────────────────────────── */}
<div
ref={contentRef}
className="container mx-auto px-4 sm:px-6 lg:px-16 py-16 sm:py-24 lg:py-32 relative z-10"
>
<div className="grid lg:grid-cols-2 gap-8 lg:gap-16 items-center min-h-[70vh] lg:min-h-[80vh]">
{/* Left Column - Animated Content */}
<div className="space-y-6 sm:space-y-8 lg:space-y-10 order-2 lg:order-1">
{/* Animated Badge */}
<div className="hero-reveal hero-badge inline-flex items-center gap-2 sm:gap-3 px-3 sm:px-6 py-2 sm:py-3 rounded-full border text-xs sm:text-sm" style={{
borderColor: "#c97a3e",
background: "rgba(201, 122, 62, 0.08)",
}}>
<span className="w-2 h-2 sm:w-3 sm:h-3 rounded-full animate-pulse-glow" style={{ backgroundColor: "#c97a3e" }} />
<span className="text-xs sm:text-sm font-semibold tracking-wide" style={{ color: "#c97a3e" }}>
Farm-Fresh Delivery Platform
</span>
</div>
{/* Main Headline - Apple-style reveal */}
<div className="hero-reveal hero-title space-y-2">
<h1
className="text-5xl sm:text-6xl md:text-7xl lg:text-8xl xl:text-9xl font-bold leading-[0.9] tracking-tighter"
style={{
fontFamily: "var(--font-fraunces)",
color: "#1a1a1a",
}}
>
<span className="block text-gradient-sage">From Field</span>
<span className="block mt-2 text-gradient-sage">To Fresh</span>
</h1>
</div>
{/* Subtext */}
<p
className="hero-reveal hero-subtitle text-lg sm:text-xl md:text-2xl max-w-xl leading-relaxed"
style={{
fontFamily: "var(--font-manrope)",
color: "#4a6d56",
fontWeight: 400,
}}
>
We connect produce farms with reliable distribution networks,
ensuring your harvest reaches markets faster and fresher than ever.
</p>
{/* CTAs */}
<div className="hero-reveal hero-cta flex flex-col sm:flex-row flex-wrap gap-4 sm:gap-5">
<Link
href="/login"
className="group relative inline-flex items-center justify-center gap-3 px-6 sm:px-8 py-4 sm:py-5 rounded-2xl font-semibold text-base sm:text-lg overflow-hidden"
style={{
background: "linear-gradient(135deg, #1a4d2e 0%, #166534 100%)",
color: "white",
boxShadow: "0 8px 32px rgba(26, 77, 46, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.15)",
transition: "transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = "translateY(-3px)";
e.currentTarget.style.boxShadow = "0 12px 40px rgba(26, 77, 46, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.2)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = "translateY(0)";
e.currentTarget.style.boxShadow = "0 8px 32px rgba(26, 77, 46, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.15)";
}}
>
<span>Start Your Route</span>
<svg className="w-5 h-5 transition-transform group-hover:translate-x-1" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
<Link
href="/brands"
className="group inline-flex items-center justify-center gap-3 px-6 sm:px-8 py-4 sm:py-5 rounded-2xl font-semibold text-base sm:text-lg border-2"
style={{
color: "#1a4d2e",
borderColor: "#1a4d2e",
background: "rgba(26, 77, 46, 0.03)",
transition: "background 0.3s ease, color 0.3s ease",
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = "#1a4d2e";
e.currentTarget.style.color = "white";
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = "rgba(26, 77, 46, 0.03)";
e.currentTarget.style.color = "#1a4d2e";
}}
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<span>Browse Farms</span>
</Link>
</div>
{/* Stats */}
<div className="hero-reveal hero-stats flex flex-wrap gap-6 sm:gap-8 pt-6 sm:pt-8">
{[
{ stat: "500+", label: "Farm Brands" },
{ stat: "98%", label: "On-Time" },
{ stat: "50K+", label: "Deliveries" },
].map((item) => (
<div key={item.label} className="text-center">
<div
className="text-2xl sm:text-3xl lg:text-4xl font-bold"
style={{
fontFamily: "var(--font-fraunces)",
color: "#1a4d2e",
}}
>
{item.stat}
</div>
<div className="text-xs sm:text-sm font-medium" style={{ color: "#6b8f71" }}>
{item.label}
</div>
</div>
))}
</div>
</div>
{/* Right Column - Hero Visual */}
<div className="hero-visual relative lg:h-[500px] xl:h-[700px]">
{/* SVG Route Map Visual */}
<svg
viewBox="0 0 500 500"
className="w-full h-full"
style={{ maxHeight: "500px", maxWidth: "100%", filter: "drop-shadow(0 20px 40px rgba(0,0,0,0.08))" }}
>
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(107, 143, 113, 0.15)" strokeWidth="1" />
</pattern>
<linearGradient id="routeGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#c97a3e" />
<stop offset="50%" stopColor="#1a4d2e" />
<stop offset="100%" stopColor="#6b8f71" />
</linearGradient>
<filter id="glow">
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
<feMerge>
<feMergeNode in="coloredBlur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<rect x="40" y="40" width="420" height="420" rx="24" fill="rgba(107, 143, 113, 0.06)" stroke="rgba(107, 143, 113, 0.2)" strokeWidth="1" />
<rect x="40" y="40" width="420" height="420" rx="24" fill="url(#grid)" />
{/* Animated Route Path */}
<path
d="M 80 400 Q 140 360, 180 280 T 260 180 Q 320 120, 400 80"
fill="none"
stroke="url(#routeGradient)"
strokeWidth="6"
strokeLinecap="round"
filter="url(#glow)"
className="route-path-animate"
style={{
strokeDasharray: "1000",
strokeDashoffset: "1000",
animation: "hs-draw-route 950ms ease-out forwards 0.5s",
}}
/>
{/* Secondary Route */}
<path
d="M 80 400 Q 200 440, 320 380 T 440 280"
fill="none"
stroke="#6b8f71"
strokeWidth="3"
strokeLinecap="round"
opacity="0.5"
strokeDasharray="600"
strokeDashoffset="600"
style={{ animation: "hs-draw-route 950ms ease-out forwards 1s" }}
/>
{/* Farm Location - Animated Pulse */}
<g className="parallax-float">
<circle cx="80" cy="400" r="35" fill="rgba(26, 77, 46, 0.1)" className="animate-ping" style={{ animationDuration: "950ms" }} />
<circle cx="80" cy="400" r="28" fill="rgba(26, 77, 46, 0.15)" />
<circle cx="80" cy="400" r="20" fill="#1a4d2e" />
<path d="M 80 390 L 80 410 M 72 398 L 80 390 L 88 398" stroke="#faf8f5" strokeWidth="2.5" strokeLinecap="round" />
</g>
{/* Hub Location */}
<g className="parallax-float">
<circle cx="260" cy="180" r="40" fill="rgba(201, 122, 62, 0.1)" className="animate-ping" style={{ animationDuration: "950ms" }} />
<circle cx="260" cy="180" r="32" fill="rgba(201, 122, 62, 0.15)" />
<circle cx="260" cy="180" r="22" fill="#c97a3e" />
<rect x="250" y="173" width="20" height="14" rx="3" fill="#faf8f5" />
</g>
{/* Stop Points */}
{[
{ x: 180, y: 280 },
{ x: 360, y: 120 },
{ x: 420, y: 90 },
].map((stop, i) => (
<g key={i} className="parallax-float">
<circle cx={stop.x} cy={stop.y} r="18" fill="rgba(250, 248, 245, 0.9)" stroke="#6b8f71" strokeWidth="2" />
<circle cx={stop.x} cy={stop.y} r="10" fill="#6b8f71" />
</g>
))}
{/* Decorative Trees */}
{[[60, 100], [440, 360], [50, 440], [450, 180]].map((coords, i) => (
<circle
key={i}
cx={coords[0]}
cy={coords[1]}
r="10"
fill="rgba(107, 143, 113, 0.3)"
className="parallax-float"
/>
))}
{/* Compass */}
<g transform="translate(420, 420)">
<circle cx="0" cy="0" r="30" fill="rgba(26, 77, 46, 0.08)" stroke="#1a4d2e" strokeWidth="1.5" />
<path d="M 0 -22 L 5 0 L 0 6 L -5 0 Z" fill="#c97a3e" />
<path d="M 0 22 L 5 0 L 0 -6 L -5 0 Z" fill="#6b8f71" />
<text x="0" y="-30" textAnchor="middle" fill="#1a4d2e" fontSize="12" fontWeight="700">N</text>
</g>
</svg>
{/* Floating Cards */}
<div className="absolute top-4 sm:top-12 -left-2 sm:-left-8 parallax-float hidden sm:block" style={{ animation: "float 950ms ease-in-out infinite" }}>
<div
className="rounded-2xl p-4 sm:p-5 shadow-2xl border"
style={{
background: "linear-gradient(135deg, rgba(201, 122, 62, 0.95) 0%, rgba(247, 129, 130, 0.9) 100%)",
borderColor: "rgba(247, 129, 130, 0.5)",
boxShadow: "0 12px 40px rgba(232, 168, 124, 0.4), 0 0 30px rgba(247, 129, 130, 0.2)",
}}
>
<div className="text-2xl sm:text-3xl font-bold text-white">2.4T</div>
<div className="text-xs sm:text-sm text-white/90">Miles Saved</div>
</div>
</div>
<div className="absolute bottom-16 sm:bottom-24 -right-2 sm:-right-4 parallax-float hidden sm:block" style={{ animation: "float-delayed 950ms ease-in-out infinite" }}>
<div
className="rounded-2xl p-4 sm:p-5 shadow-2xl border"
style={{
background: "linear-gradient(135deg, rgba(107, 185, 165, 0.95) 0%, rgba(133, 210, 197, 0.9) 100%)",
borderColor: "rgba(133, 210, 197, 0.5)",
boxShadow: "0 12px 40px rgba(107, 185, 165, 0.4), 0 0 30px rgba(133, 210, 197, 0.2)",
}}
>
<div className="text-2xl sm:text-3xl font-bold text-white">12hrs</div>
<div className="text-xs sm:text-sm text-white/90">Avg Delivery</div>
</div>
</div>
<div className="absolute top-1/3 sm:top-1/2 -right-4 sm:-right-12 parallax-float hidden sm:block" style={{ animation: "float 950ms ease-in-out infinite 1s" }}>
<div
className="rounded-2xl p-3 sm:p-4 shadow-xl border flex items-center gap-2 sm:gap-3"
style={{
background: "linear-gradient(135deg, rgba(245, 199, 126, 0.95) 0%, rgba(201, 162, 80, 0.9) 100%)",
borderColor: "rgba(245, 199, 126, 0.6)",
boxShadow: "0 8px 32px rgba(245, 199, 126, 0.4)",
}}
>
<span className="w-3 h-3 sm:w-4 sm:h-4 rounded-full bg-white animate-pulse" />
<span className="text-xs sm:text-sm font-bold text-white/90">Live Tracking</span>
</div>
</div>
</div>
</div>
</div>
{/* ─── SCROLL INDICATOR ────────────────────────────────────────────────── */}
<button
type="button"
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") scrollToContent(); }}
className="scroll-indicator absolute bottom-12 left-1/2 -translate-x-1/2 flex flex-col items-center gap-4 cursor-pointer bg-transparent border-0 p-0"
onClick={scrollToContent}
>
<span className="text-xs font-semibold tracking-[0.2em] uppercase" style={{ color: "#6b8f71" }}>
Scroll to explore
</span>
<div className="relative w-8 h-14 rounded-full border-2" style={{ borderColor: "#6b8f71" }}>
<div
className="absolute top-2 left-1/2 -translate-x-1/2 w-2 h-4 rounded-full"
style={{
backgroundColor: "#1a4d2e",
animation: "hs-scroll-indicator 950ms ease-in-out infinite",
}}
/>
</div>
</button>
{/* Decorative leaves */}
<div className="parallax-float absolute bottom-32 left-16 opacity-15" style={{ animationDelay: "0.5s" }}>
<svg className="w-20 h-20" viewBox="0 0 60 60" fill="none">
<path d="M30 5 C45 15, 50 35, 30 55 C10 35, 15 15, 30 5" fill="#6b8f71" />
<path d="M30 5 L30 55" stroke="#1a4d2e" strokeWidth="2" />
</svg>
</div>
<div className="parallax-float absolute top-40 right-24 opacity-12" style={{ animationDelay: "1s" }}>
<svg className="w-16 h-16" viewBox="0 0 40 40" fill="none">
<path d="M20 3 C30 10, 33 23, 20 37 C7 23, 10 10, 20 3" fill="#c97a3e" />
<path d="M20 3 L20 37" stroke="#1a4d2e" strokeWidth="1.5" />
</svg>
</div>
</section>
{/* ─── STORY SECTION - SCROLL-DRIVEN REVEAL ──────────────────────────── */}
<section id="story" className="relative" style={{ background: "#1a1a1a" }}>
{/* Reduced from 300vh to avoid "long blank scroll regions" complaint while keeping cinematic sticky scroll effect */}
<div className="relative" style={{ minHeight: "140vh" }}>
{/* Sticky Container */}
<div
className="sticky top-0 h-screen flex items-center justify-center overflow-hidden"
>
{/* Background gradient */}
<div className="absolute inset-0" style={{
background: "radial-gradient(ellipse 80% 60% at 50% 50%, rgba(26, 77, 46, 0.15) 0%, #1a1a1a 70%)",
}} />
<div className="container mx-auto px-8 text-center relative z-10 max-w-4xl">
<div className="mb-8 reveal-scale">
<span
className="inline-block text-xs font-bold tracking-[0.3em] uppercase"
style={{ color: "#c97a3e", fontFamily: "var(--font-manrope)" }}
>
The Challenge
</span>
</div>
<div className="reveal-scale" style={{ animationDelay: "0.1s" }}>
<h2
className="text-4xl sm:text-5xl lg:text-6xl font-bold leading-tight mb-8"
style={{
fontFamily: "var(--font-fraunces)",
color: "#faf8f5",
}}
>
Fresh produce should not
<br />
<span style={{ color: "#c97a3e" }}>sit in transit for days</span>
</h2>
</div>
<div className="reveal-scale" style={{ animationDelay: "0.2s" }}>
<p
className="text-xl max-w-2xl mx-auto"
style={{
color: "#86868b",
fontFamily: "var(--font-manrope)",
lineHeight: 1.7,
}}
>
Traditional distribution means your harvest loses freshness, quality, and value before reaching customers.
</p>
</div>
</div>
</div>
</div>
</section>
{/* ─── FEATURES SECTION - PROGRESSIVE REVEAL ───────────────────────────── */}
<section
id="features"
className="relative py-32 overflow-hidden"
style={{ background: "linear-gradient(180deg, #faf8f5 0%, #fef7f0 100%)" }}
>
{/* Parallax background elements */}
<div className="parallax-bg absolute inset-0 pointer-events-none">
<div className="parallax-float absolute top-0 right-0 w-[50%] h-full opacity-10" style={{ animationDelay: "0.3s" }}>
<div className="absolute top-20 right-20 w-80 h-80 rounded-full" style={{
background: "radial-gradient(circle, rgba(26, 77, 46, 0.3) 0%, transparent 70%)",
filter: "blur(8px)",
}} />
</div>
</div>
<div className="container mx-auto px-8 relative z-10">
{/* Section Header */}
<div className="reveal-scale text-center mb-20">
<span
className="inline-block text-xs font-bold tracking-[0.2em] uppercase mb-6"
style={{ color: "#6b8f71", fontFamily: "var(--font-manrope)" }}
>
Platform Features
</span>
<h2
className="text-5xl sm:text-6xl lg:text-7xl font-bold leading-tight"
style={{
fontFamily: "var(--font-fraunces)",
color: "#1a4d2e",
}}
>
Everything You Need
</h2>
</div>
{/* Feature Cards Grid */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{[
{
icon: (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M12 2L2 7l10 5 10-5-10-5z" />
<path d="M2 17l10 5 10-5" />
<path d="M2 12l10 5 10-5" />
</svg>
),
title: "Product Catalogs",
description: "Showcase seasonal produce with rich media galleries and real-time availability updates.",
color: "#1a4d2e",
},
{
icon: (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<circle cx="12" cy="12" r="10" />
<polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76" />
</svg>
),
title: "Route Planning",
description: "Optimize delivery routes with intelligent mapping and real-time scheduling.",
color: "#c97a3e",
},
{
icon: (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M9 11l3 3L22 4" />
<path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11" />
</svg>
),
title: "Order Management",
description: "Track orders from placement through delivery with automated status updates.",
color: "#7a5c9e",
},
{
icon: (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" />
</svg>
),
title: "Wholesale Portal",
description: "Give buyers a dedicated space to browse pricing and place bulk orders.",
color: "#5c8a8f",
},
{
icon: (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
<polyline points="22,6 12,13 2,6" />
</svg>
),
title: "Harvest Reach",
description: "Send email and SMS campaigns about seasonal availability and new harvests.",
color: "#c97a3e",
},
{
icon: (
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<line x1="18" y1="20" x2="18" y2="10" />
<line x1="12" y1="20" x2="12" y2="4" />
<line x1="6" y1="20" x2="6" y2="14" />
</svg>
),
title: "Smart Analytics",
description: "Uncover sales trends and operational insights with real-time dashboards.",
color: "#6b8f71",
},
].map((feature) => (
<div
key={feature.title}
className="reveal-scale group relative p-8 rounded-3xl transition-all duration-500 cursor-pointer"
style={{
background: "linear-gradient(145deg, rgba(255,255,255,0.9) 0%, rgba(250,248,245,0.95) 100%)",
border: "1px solid rgba(26, 77, 46, 0.08)",
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.04)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = "translateY(-8px) scale(1.02)";
e.currentTarget.style.boxShadow = "0 20px 60px rgba(26, 77, 46, 0.12)";
e.currentTarget.style.borderColor = feature.color + "40";
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = "translateY(0) scale(1)";
e.currentTarget.style.boxShadow = "0 8px 32px rgba(0, 0, 0, 0.04)";
e.currentTarget.style.borderColor = "rgba(26, 77, 46, 0.08)";
}}
>
{/* Icon */}
<div
className="w-16 h-16 rounded-2xl flex items-center justify-center mb-6 transition-transform group-hover:scale-110"
style={{
background: `linear-gradient(135deg, ${feature.color}15 0%, ${feature.color}08 100%)`,
color: feature.color,
border: `1px solid ${feature.color}20`,
}}
>
{feature.icon}
</div>
{/* Content */}
<h3
className="text-2xl font-bold mb-4"
style={{
fontFamily: "var(--font-fraunces)",
color: "#1a4d2e",
}}
>
{feature.title}
</h3>
<p
className="text-base leading-relaxed"
style={{
color: "#555",
fontFamily: "var(--font-manrope)",
}}
>
{feature.description}
</p>
{/* Hover Arrow */}
<div
className="absolute bottom-8 right-8 w-10 h-10 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-300"
style={{
background: feature.color,
transform: "translateX(-10px)",
}}
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2">
<path d="M5 12h14M12 5l7 7-7 7" />
</svg>
</div>
</div>
))}
</div>
</div>
</section>
{/* ─── STATS SECTION - SCROLL-TRIGGERED COUNTERS ──────────────────────── */}
<section
id="stats"
className="relative py-32 overflow-hidden"
style={{ background: "#1a4d2e" }}
>
{/* Parallax decorative elements */}
<div className="parallax-bg absolute inset-0 pointer-events-none opacity-[0.03]" style={{
backgroundImage: "radial-gradient(circle at 25% 25%, white 1px, transparent 1px), radial-gradient(circle at 75% 75%, white 1px, transparent 1px)",
backgroundSize: "48px 48px",
}} />
<div className="absolute top-0 left-0 right-0 h-1" style={{
background: "linear-gradient(to right, transparent, #c97a3e, transparent)",
}} />
<div className="container mx-auto px-8 relative z-10">
<div className="text-center mb-16 reveal-scale">
<span
className="inline-block text-xs font-bold tracking-[0.2em] uppercase"
style={{ color: "#c97a3e", fontFamily: "var(--font-manrope)" }}
>
Our Impact
</span>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-12">
{[
{ value: 500, suffix: "+", label: "Produce Brands" },
{ value: 50000, suffix: "+", label: "Orders Delivered" },
{ value: 98, suffix: "%", label: "On-Time Delivery" },
{ value: 2000000, prefix: "$", suffix: "+", label: "Weekly Sales" },
].map((stat, i) => (
<div key={stat.label} className="reveal-scale text-center" style={{ animationDelay: `${i * 0.1}s` }}>
<div
className="text-5xl sm:text-6xl lg:text-7xl font-bold mb-4"
style={{
fontFamily: "var(--font-fraunces)",
color: "#faf8f5",
lineHeight: 1,
}}
>
{stat.prefix && <span style={{ color: "#c97a3e" }}>{stat.prefix}</span>}
<span className="counter-animate" data-target={stat.value}>0</span>
<span style={{ color: "#c97a3e" }}>{stat.suffix}</span>
</div>
<p
className="text-base font-medium"
style={{ color: "#6b8f71", fontFamily: "var(--font-manrope)" }}
>
{stat.label}
</p>
</div>
))}
</div>
</div>
</section>
{/* ─── CTA SECTION - FINAL REVEAL ─────────────────────────────────────── */}
{/* id="reviews" provides anchor for nav "Reviews" link (matches nav expectations; section contains final messaging) */}
<section
id="reviews"
className="relative py-40 overflow-hidden"
style={{ background: "linear-gradient(180deg, #faf8f5 0%, #f5f2ed 100%)" }}
>
{/* Parallax decorative orbs */}
<div className="absolute inset-0 pointer-events-none">
<div className="parallax-float absolute top-1/4 left-1/4 w-96 h-96 rounded-full opacity-10" style={{
background: "radial-gradient(circle, rgba(26, 77, 46, 0.3) 0%, transparent 70%)",
filter: "blur(8px)",
animationDelay: "0.2s",
}} />
<div className="parallax-float absolute bottom-1/4 right-1/4 w-80 h-80 rounded-full opacity-10" style={{
background: "radial-gradient(circle, rgba(201, 122, 62, 0.3) 0%, transparent 70%)",
filter: "blur(8px)",
animationDelay: "0.4s",
}} />
</div>
<div className="container mx-auto px-8 relative z-10">
<div className="max-w-3xl mx-auto text-center">
{/* Label */}
<div className="reveal-scale mb-8">
<span
className="reveal-text inline-block text-xs font-bold tracking-[0.2em] uppercase px-5 py-2 rounded-full"
style={{
color: "#c97a3e",
background: "rgba(201, 122, 62, 0.1)",
fontFamily: "var(--font-manrope)",
}}
>
Get Started Today
</span>
</div>
{/* Headline */}
<div className="reveal-scale" style={{ animationDelay: "0.1s" }}>
<h2
className="text-5xl sm:text-6xl lg:text-7xl font-bold leading-tight mb-8"
style={{
fontFamily: "var(--font-fraunces)",
color: "#1a4d2e",
}}
>
Ready to Transform<br />
Your Distribution?
</h2>
</div>
{/* Subtext */}
<div className="reveal-scale" style={{ animationDelay: "0.2s" }}>
<p
className="text-xl mb-12 max-w-xl mx-auto"
style={{
color: "#555",
fontFamily: "var(--font-manrope)",
}}
>
Join hundreds of farms already using Route Commerce to deliver fresher produce faster.
</p>
</div>
{/* CTAs */}
<div className="reveal-scale flex flex-wrap items-center justify-center gap-5" style={{ animationDelay: "0.3s" }}>
<Link
href="/login"
className="group relative inline-flex items-center gap-3 px-10 py-6 rounded-2xl font-semibold text-xl overflow-hidden"
style={{
background: "linear-gradient(135deg, #1a4d2e 0%, #166534 100%)",
color: "white",
boxShadow: "0 8px 32px rgba(26, 77, 46, 0.35)",
}}
>
<span>Start Free Trial</span>
<svg className="w-6 h-6 transition-transform group-hover:translate-x-1" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</Link>
<Link
href="/contact"
className="group inline-flex items-center gap-3 px-10 py-6 rounded-2xl font-semibold text-xl border-2"
style={{
color: "#1a4d2e",
borderColor: "#1a4d2e",
background: "rgba(26, 77, 46, 0.03)",
}}
>
<span>Contact Sales</span>
</Link>
</div>
</div>
</div>
</section>
</div>
{/* /containerRef for GSAP scope */}
{/* (duplicate inline footer removed -- LandingPageWrapper <Footer /> now provides the only footer; eliminates repeated content + trims scroll height) */}
{/* ─── GLOBAL STYLES ────────────────────────────────────────────────────── */}
<style dangerouslySetInnerHTML={{ __html: `
@keyframes hs-float {
0%, 100% { transform: translate(0, 0); }
33% { transform: translate(15px, -20px); }
66% { transform: translate(-10px, 10px); }
}
@keyframes hs-float-delayed {
0%, 100% { transform: translate(0, 0); }
33% { transform: translate(-20px, 15px); }
66% { transform: translate(10px, -10px); }
}
@keyframes hs-float-slow {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(25px, -30px) scale(1.02); }
}
@keyframes hs-float-slow-delayed {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(-30px, 20px) scale(1.03); }
}
@media (prefers-reduced-motion: reduce) {
.parallax-float,
.parallax-float *,
[class*="animate-"] {
animation: none !important;
transition: none !important;
}
}
@keyframes hs-draw-route {
to { stroke-dashoffset: 0; }
}
@keyframes hs-scroll-indicator {
0%, 100% { transform: translateY(0); opacity: 1; }
50% { transform: translateY(6px); opacity: 0.5; }
}
.text-gradient-sage {
background: linear-gradient(135deg, #1a4d2e 0%, #6b8f71 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.parallax-float {
will-change: transform;
}
.scroll-progress-container {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.scroll-progress-bar {
height: 3px;
background: linear-gradient(to right, #c97a3e, #6b8f71);
}
`}} />
</>
);
}