xpeditis2.0/apps/frontend/app/[locale]/page.tsx
David ec0173483a
All checks were successful
Dev CI / Backend — Lint (push) Successful in 10m23s
Dev CI / Backend — Unit Tests (push) Successful in 10m17s
Dev CI / Frontend — Lint & Type-check (push) Successful in 11m3s
Dev CI / Frontend — Unit Tests (push) Successful in 10m33s
Dev CI / Notify Failure (push) Has been skipped
fix language
2026-04-21 18:04:02 +02:00

1031 lines
42 KiB
TypeScript

'use client';
import { useRef, useState, useEffect } from 'react';
import { Link } from '@/i18n/navigation';
import Image from 'next/image';
import { motion, useInView, useScroll, useTransform } from 'framer-motion';
import {
Ship,
Shield,
Zap,
BarChart3,
Package,
Clock,
CheckCircle2,
ArrowRight,
Search,
Anchor,
Container,
FileText,
LayoutDashboard,
Bell,
BookOpen,
Users,
Check,
X,
} from 'lucide-react';
import { useTranslations, useLocale } from 'next-intl';
import { useAuth } from '@/lib/context/auth-context';
import { LandingHeader, LandingFooter } from '@/components/layout';
function AnimatedCounter({
end,
suffix = '',
prefix = '',
decimals = 0,
isActive,
duration = 2,
}: {
end: number;
suffix?: string;
prefix?: string;
decimals?: number;
isActive: boolean;
duration?: number;
}) {
const [count, setCount] = useState(0);
useEffect(() => {
if (!isActive) return;
let startTime: number | undefined;
const animate = (timestamp: number) => {
if (!startTime) startTime = timestamp;
const progress = Math.min((timestamp - startTime) / (duration * 1000), 1);
const eased = 1 - Math.pow(1 - progress, 3);
setCount(eased * end);
if (progress < 1) requestAnimationFrame(animate);
else setCount(end);
};
requestAnimationFrame(animate);
}, [end, duration, isActive]);
const display = decimals > 0 ? count.toFixed(decimals) : Math.floor(count).toString();
return <>{prefix}{display}{suffix}</>;
}
export default function LandingPage() {
const t = useTranslations('landing');
const tCommon = useTranslations('common');
const locale = useLocale();
const { user, isAuthenticated } = useAuth();
const heroRef = useRef(null);
const featuresRef = useRef(null);
const statsRef = useRef(null);
const pricingRef = useRef(null);
const testimonialsRef = useRef(null);
const ctaRef = useRef(null);
const howRef = useRef(null);
const isHeroInView = useInView(heroRef, { once: true });
const isFeaturesInView = useInView(featuresRef, { once: true });
const isStatsInView = useInView(statsRef, { once: true, amount: 0.3 });
const isPricingInView = useInView(pricingRef, { once: true });
const isTestimonialsInView = useInView(testimonialsRef, { once: true });
const isCtaInView = useInView(ctaRef, { once: true });
const isHowInView = useInView(howRef, { once: true, amount: 0.2 });
const [billingYearly, setBillingYearly] = useState(false);
const { scrollYProgress } = useScroll();
const backgroundY = useTransform(scrollYProgress, [0, 1], ['0%', '50%']);
const features = [
{
icon: BarChart3,
title: t('features.dashboard.title'),
description: t('features.dashboard.description'),
color: 'from-blue-500 to-cyan-500',
link: '/dashboard',
},
{
icon: Package,
title: t('features.bookings.title'),
description: t('features.bookings.description'),
color: 'from-purple-500 to-pink-500',
link: '/dashboard/bookings',
},
{
icon: FileText,
title: t('features.documents.title'),
description: t('features.documents.description'),
color: 'from-orange-500 to-red-500',
link: '/dashboard/documents',
},
{
icon: Search,
title: t('features.tracking.title'),
description: t('features.tracking.description'),
color: 'from-green-500 to-emerald-500',
link: '/dashboard/track-trace',
},
{
icon: BookOpen,
title: t('features.wiki.title'),
description: t('features.wiki.description'),
color: 'from-yellow-500 to-orange-500',
link: '/dashboard/wiki',
},
{
icon: Bell,
title: t('features.notifications.title'),
description: t('features.notifications.description'),
color: 'from-indigo-500 to-purple-500',
link: '/dashboard',
},
];
const stats = [
{ end: 50, prefix: '', suffix: '+', decimals: 0, label: t('stats.carriers'), icon: Ship },
{ end: 10, prefix: '', suffix: 'K+', decimals: 0, label: t('stats.ports'), icon: Anchor },
{ end: 2, prefix: '<', suffix: 's', decimals: 0, label: t('stats.responseTime'), icon: Zap },
{ end: 99.5, prefix: '', suffix: '%', decimals: 1, label: t('stats.availability'), icon: CheckCircle2 },
];
const planFeaturesByKey: Record<string, Array<{ key: string; included: boolean }>> = {
bronze: [
{ key: 'lclBooking', included: true },
{ key: 'tracking', included: true },
{ key: 'dashboard', included: false },
{ key: 'wiki', included: false },
{ key: 'userManagement', included: false },
{ key: 'csvExport', included: false },
{ key: 'apiAccess', included: false },
{ key: 'kam', included: false },
],
silver: [
{ key: 'lclBooking', included: true },
{ key: 'tracking', included: true },
{ key: 'dashboardAdvanced', included: true },
{ key: 'wikiFull', included: true },
{ key: 'userManagement', included: true },
{ key: 'csvExport', included: true },
{ key: 'apiAccess', included: false },
{ key: 'kam', included: false },
],
gold: [
{ key: 'lclBooking', included: true },
{ key: 'tracking', included: true },
{ key: 'dashboardAdvanced', included: true },
{ key: 'wikiFull', included: true },
{ key: 'userManagement', included: true },
{ key: 'csvExport', included: true },
{ key: 'apiFull', included: true },
{ key: 'kam', included: false },
],
platinium: [
{ key: 'lclBooking', included: true },
{ key: 'tracking', included: true },
{ key: 'dashboardAdvanced', included: true },
{ key: 'wikiFull', included: true },
{ key: 'userManagement', included: true },
{ key: 'csvExport', included: true },
{ key: 'apiFull', included: true },
{ key: 'kamCustom', included: true },
],
};
const pricingPlans = [
{
key: 'bronze',
badge: null,
monthlyPrice: 0,
yearlyPrice: 0,
yearlyMonthly: 0,
commission: '5%',
ctaLink: '/register',
highlighted: false,
accentColor: 'from-amber-600 to-yellow-500',
textAccent: 'text-amber-700',
badgeBg: 'bg-amber-100 text-amber-800',
},
{
key: 'silver',
badge: 'popular',
monthlyPrice: 249,
yearlyPrice: 2739,
yearlyMonthly: 228,
commission: '3%',
ctaLink: '/register',
highlighted: true,
accentColor: 'from-slate-400 to-slate-500',
textAccent: 'text-slate-600',
badgeBg: 'bg-slate-100 text-slate-700',
},
{
key: 'gold',
badge: null,
monthlyPrice: 899,
yearlyPrice: 9889,
yearlyMonthly: 824,
commission: '2%',
ctaLink: '/register',
highlighted: false,
accentColor: 'from-yellow-400 to-amber-400',
textAccent: 'text-amber-600',
badgeBg: 'bg-yellow-50 text-amber-700',
},
{
key: 'platinium',
badge: 'custom',
monthlyPrice: null,
yearlyPrice: null,
yearlyMonthly: null,
commission: '1%',
ctaLink: '/contact',
highlighted: false,
accentColor: 'from-brand-navy to-brand-turquoise',
textAccent: 'text-brand-turquoise',
badgeBg: 'bg-brand-navy/10 text-brand-navy',
},
] as const;
const testimonials = (t.raw('testimonials.items') as Array<{
quote: string;
author: string;
role: string;
company: string;
}>) ?? [];
const containerVariants = {
hidden: { opacity: 0, y: 50 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
staggerChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5 },
},
};
const numberFormat = new Intl.NumberFormat(locale === 'fr' ? 'fr-FR' : 'en-US');
return (
<div className="min-h-screen bg-white">
<LandingHeader transparentOnTop={true} />
{/* Hero Section */}
<section ref={heroRef} className="relative min-h-screen flex items-center overflow-hidden">
<motion.div style={{ y: backgroundY }} className="absolute inset-0 z-0">
<video
autoPlay
loop
muted
playsInline
className="absolute inset-0 w-full h-full object-cover"
>
<source
src="https://assets.mixkit.co/videos/36264/36264-720.mp4"
type="video/mp4"
/>
<div
className="absolute inset-0"
style={{
backgroundImage: 'url(/assets/images/background-section-1-landingpage.png)',
backgroundSize: 'cover',
backgroundPosition: 'center',
}}
/>
</video>
<div className="absolute inset-0 bg-brand-navy/65" />
<div className="absolute inset-0 bg-gradient-to-br from-brand-navy/70 via-brand-navy/50 to-brand-turquoise/20" />
</motion.div>
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isHeroInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8 }}
className="text-center"
>
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={isHeroInView ? { scale: 1, opacity: 1 } : {}}
transition={{ duration: 0.6, delay: 0.2 }}
className="inline-flex items-center space-x-2 bg-white/10 backdrop-blur-sm px-3 py-1.5 sm:px-4 sm:py-2 rounded-full mb-6 sm:mb-8 border border-white/20"
>
<Ship className="w-4 h-4 sm:w-5 sm:h-5 text-brand-turquoise flex-shrink-0" />
<span className="text-white/90 text-xs sm:text-sm font-medium">
{t('hero.badge')}
</span>
</motion.div>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={isHeroInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.3 }}
className="text-3xl sm:text-5xl lg:text-7xl font-bold text-white mb-4 sm:mb-6 leading-tight"
>
{t('hero.titleLine1')}
<br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-brand-turquoise to-brand-green">
{t('hero.titleLine2')}
</span>
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isHeroInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.4 }}
className="text-base sm:text-xl lg:text-2xl text-white/80 mb-8 sm:mb-12 max-w-3xl mx-auto leading-relaxed px-2"
>
{t('hero.subtitle')}
</motion.p>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isHeroInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.5 }}
className="flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-6 mb-12 px-4 sm:px-0"
>
{isAuthenticated && user ? (
<Link
href="/dashboard"
target="_blank"
rel="noopener noreferrer"
className="group px-6 sm:px-8 py-3 sm:py-4 bg-brand-turquoise text-white rounded-lg hover:bg-brand-turquoise/90 transition-all hover:shadow-2xl hover:scale-105 font-semibold text-base sm:text-lg w-full sm:w-auto flex items-center justify-center space-x-2"
>
<span>{t('hero.ctaAuthenticated')}</span>
<LayoutDashboard className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
) : (
<>
<Link
href="/register"
target="_blank"
rel="noopener noreferrer"
className="group px-6 sm:px-8 py-3 sm:py-4 bg-brand-turquoise text-white rounded-lg hover:bg-brand-turquoise/90 transition-all hover:shadow-2xl hover:scale-105 font-semibold text-base sm:text-lg w-full sm:w-auto flex items-center justify-center space-x-2"
>
<span>{t('hero.ctaRegister')}</span>
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
<Link
href="/login"
target="_blank"
rel="noopener noreferrer"
className="px-6 sm:px-8 py-3 sm:py-4 bg-white text-brand-navy rounded-lg hover:bg-gray-50 transition-all hover:shadow-xl font-semibold text-base sm:text-lg w-full sm:w-auto text-center"
>
{t('hero.ctaDemo')}
</Link>
</>
)}
</motion.div>
</motion.div>
</div>
<div className="absolute bottom-0 left-0 right-0">
<svg className="w-full h-24 lg:h-32" viewBox="0 0 1440 120" preserveAspectRatio="none">
<motion.path
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 2, ease: 'easeInOut' }}
d="M0,60 C240,90 480,30 720,60 C960,90 1200,30 1440,60 L1440,120 L0,120 Z"
fill="white"
opacity="0.8"
/>
<motion.path
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 2.5, ease: 'easeInOut' }}
d="M0,80 C240,50 480,110 720,80 C960,50 1200,110 1440,80 L1440,120 L0,120 Z"
fill="white"
/>
</svg>
</div>
</section>
{/* Stats Section */}
<section ref={statsRef} className="py-10 sm:py-16 bg-gray-50">
<motion.div
variants={containerVariants}
initial="hidden"
animate={isStatsInView ? 'visible' : 'hidden'}
className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"
>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8">
{stats.map((stat, index) => {
const IconComponent = stat.icon;
return (
<motion.div
key={index}
variants={itemVariants}
className="text-center group cursor-pointer"
>
<div className="flex justify-center mb-3">
<div className="p-3 bg-brand-turquoise/10 rounded-full group-hover:bg-brand-turquoise/20 transition-colors">
<IconComponent className="w-8 h-8 text-brand-turquoise" />
</div>
</div>
<motion.div
initial={{ scale: 0 }}
animate={isStatsInView ? { scale: 1 } : {}}
transition={{ duration: 0.5, delay: index * 0.1 }}
className="text-3xl sm:text-5xl lg:text-6xl font-bold text-brand-navy mb-2 tabular-nums"
>
<AnimatedCounter
end={stat.end}
prefix={stat.prefix}
suffix={stat.suffix}
decimals={stat.decimals}
isActive={isStatsInView}
duration={2.2}
/>
</motion.div>
<div className="text-gray-600 font-medium">{stat.label}</div>
</motion.div>
);
})}
</div>
</motion.div>
</section>
{/* Features Section */}
<section ref={featuresRef} id="features" className="py-12 sm:py-20 lg:py-32">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isFeaturesInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8 }}
className="text-center mb-10 sm:mb-16"
>
<h2 className="text-2xl sm:text-4xl lg:text-5xl font-bold text-brand-navy mb-3 sm:mb-4">
{t('features.title')}
</h2>
<p className="text-base sm:text-xl text-gray-600 max-w-2xl mx-auto">
{t('features.subtitle')}
</p>
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
animate={isFeaturesInView ? 'visible' : 'hidden'}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
>
{features.map((feature, index) => {
const IconComponent = feature.icon;
return (
<motion.div
key={index}
variants={itemVariants}
whileHover={{ y: -8 }}
className="h-full"
>
<Link
href={feature.link}
target="_blank"
rel="noopener noreferrer"
className="group flex flex-col h-full bg-white p-8 rounded-2xl shadow-lg hover:shadow-2xl transition-all border border-gray-100 hover:border-brand-turquoise/30 relative overflow-hidden"
>
<div
className={`absolute top-0 left-0 right-0 h-1 bg-gradient-to-r ${feature.color} opacity-40 group-hover:opacity-100 transition-opacity duration-300`}
/>
<div
className={`w-14 h-14 rounded-xl bg-gradient-to-br ${feature.color} flex items-center justify-center mb-5 group-hover:scale-110 transition-transform flex-shrink-0`}
>
<IconComponent className="w-7 h-7 text-white" />
</div>
<h3 className="text-xl font-bold text-brand-navy mb-3 group-hover:text-brand-turquoise transition-colors">
{feature.title}
</h3>
<p className="text-gray-600 leading-relaxed flex-1">{feature.description}</p>
<div className="mt-5 pt-4 border-t border-gray-100 flex items-center justify-between">
<span className="text-brand-turquoise font-semibold text-sm">
{tCommon('discover')}
</span>
<div className="w-8 h-8 rounded-full bg-brand-turquoise/10 group-hover:bg-brand-turquoise/20 flex items-center justify-center transition-colors">
<ArrowRight className="w-4 h-4 text-brand-turquoise group-hover:translate-x-0.5 transition-transform" />
</div>
</div>
</Link>
</motion.div>
);
})}
</motion.div>
</div>
</section>
{/* Partner Logos Section */}
<section className="py-16 bg-white">
<div className="max-w-7xl mx-auto px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="text-center mb-12"
>
<h3 className="text-2xl font-bold text-brand-navy mb-2">
{t('partners.title')}
</h3>
<p className="text-gray-600">{t('partners.subtitle')}</p>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: 0.2 }}
className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-8 items-center"
>
{[
'ECU Line 2.png',
'ICL 1.png',
'NVO Consolidation 1.png',
'TCC LOG 1.png',
'VANGUARD 1.png',
'image 1.png',
].map((logo, index) => (
<motion.div
key={index}
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
whileHover={{ scale: 1.1 }}
className="flex items-center justify-center p-4 grayscale hover:grayscale-0 transition-all cursor-pointer"
>
<Image
src={`/assets/logos/partner/${logo}`}
alt={`Partner ${index + 1}`}
width={120}
height={60}
className="object-contain h-12 w-auto"
/>
</motion.div>
))}
</motion.div>
</div>
</section>
{/* Pricing Section */}
<section
ref={pricingRef}
id="pricing"
className="py-12 sm:py-20 lg:py-32 bg-gradient-to-b from-white to-gray-50"
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isPricingInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8 }}
className="text-center mb-8 sm:mb-12"
>
<span className="inline-block bg-brand-turquoise/10 text-brand-turquoise text-sm font-semibold px-4 py-1.5 rounded-full mb-4 uppercase tracking-wide">
{t('pricing.badge')}
</span>
<h2 className="text-2xl sm:text-4xl lg:text-5xl font-bold text-brand-navy mb-3 sm:mb-4">
{t('pricing.title')}
</h2>
<p className="text-base sm:text-xl text-gray-600 max-w-2xl mx-auto">
{t('pricing.subtitle')}
</p>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={isPricingInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2 }}
className="flex items-center justify-center gap-4 mb-12"
>
<span className={`text-sm font-medium ${!billingYearly ? 'text-brand-navy' : 'text-gray-400'}`}>
{t('pricing.monthly')}
</span>
<button
onClick={() => setBillingYearly(v => !v)}
className={`relative inline-flex h-7 w-14 items-center rounded-full transition-colors focus:outline-none ${
billingYearly ? 'bg-brand-turquoise' : 'bg-gray-300'
}`}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white shadow transition-transform ${
billingYearly ? 'translate-x-8' : 'translate-x-1'
}`}
/>
</button>
<span className={`text-sm font-medium ${billingYearly ? 'text-brand-navy' : 'text-gray-400'}`}>
{t('pricing.yearly')}
</span>
{billingYearly && (
<span className="bg-brand-green/10 text-brand-green text-xs font-bold px-2.5 py-1 rounded-full">
{t('pricing.yearlySaving')}
</span>
)}
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
animate={isPricingInView ? 'visible' : 'hidden'}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 items-stretch"
>
{pricingPlans.map((plan) => {
const planName = t(`pricing.plans.${plan.key}.name` as any);
const planDescription = t(`pricing.plans.${plan.key}.description` as any);
const planUsers = t(`pricing.plans.${plan.key}.users` as any);
const planShipments = t(`pricing.plans.${plan.key}.shipments` as any);
const planCta = t(`pricing.plans.${plan.key}.cta` as any);
const planFeatures = planFeaturesByKey[plan.key] ?? [];
return (
<motion.div
key={plan.key}
variants={itemVariants}
whileHover={{ y: -6 }}
className={`relative flex flex-col rounded-2xl transition-all overflow-hidden ${
plan.highlighted
? 'bg-brand-navy shadow-2xl ring-2 ring-brand-turquoise'
: 'bg-white shadow-lg border border-gray-100 hover:shadow-xl hover:border-brand-turquoise/30'
}`}
>
<div className={`h-1.5 w-full bg-gradient-to-r ${plan.accentColor}`} />
{plan.badge === 'popular' && (
<div className="absolute top-4 right-4">
<span className="bg-brand-turquoise text-white text-xs font-bold px-2.5 py-1 rounded-full">
{t('pricing.popularBadge')}
</span>
</div>
)}
{plan.badge === 'custom' && (
<div className="absolute top-4 right-4">
<span className="bg-gradient-to-r from-brand-navy to-brand-turquoise text-white text-xs font-bold px-2.5 py-1 rounded-full">
{t('pricing.customBadge')}
</span>
</div>
)}
<div className="flex flex-col flex-1 p-6">
<div className="mb-4">
<div className={`inline-flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider px-2.5 py-1 rounded-full mb-3 ${plan.highlighted ? 'bg-white/10 text-white/70' : plan.badgeBg}`}>
<div className={`w-1.5 h-1.5 rounded-full bg-gradient-to-r ${plan.accentColor}`} />
{planName}
</div>
<h3 className={`text-xl font-bold mb-1 ${plan.highlighted ? 'text-white' : 'text-brand-navy'}`}>
{planDescription}
</h3>
</div>
<div className="mb-6">
{plan.monthlyPrice === null ? (
<div>
<span className={`text-3xl font-bold ${plan.highlighted ? 'text-white' : 'text-brand-navy'}`}>
{t('pricing.custom')}
</span>
<p className={`text-sm mt-1 ${plan.highlighted ? 'text-white/60' : 'text-gray-500'}`}>
{t('pricing.customSubtitle')}
</p>
</div>
) : plan.monthlyPrice === 0 ? (
<div>
<span className={`text-4xl font-bold ${plan.highlighted ? 'text-white' : 'text-brand-navy'}`}>
{t('pricing.free')}
</span>
<p className={`text-sm mt-1 ${plan.highlighted ? 'text-white/60' : 'text-gray-500'}`}>
{t('pricing.freeSubtitle')}
</p>
</div>
) : (
<div>
<div className="flex items-end gap-1">
<span className={`text-4xl font-bold ${plan.highlighted ? 'text-white' : 'text-brand-navy'}`}>
{billingYearly ? plan.yearlyMonthly : plan.monthlyPrice}
</span>
<span className={`text-sm pb-1.5 ${plan.highlighted ? 'text-white/60' : 'text-gray-500'}`}>
{t('pricing.perMonth')}
</span>
</div>
{billingYearly ? (
<p className={`text-xs mt-1 ${plan.highlighted ? 'text-white/60' : 'text-gray-500'}`}>
{t('pricing.billedYearly', {
price: numberFormat.format(plan.yearlyPrice ?? 0),
})}
</p>
) : (
<p className="text-xs mt-1 text-brand-turquoise">
{t('pricing.saveWithYearly')}
</p>
)}
</div>
)}
</div>
<div className={`rounded-xl p-3 mb-5 space-y-2 ${plan.highlighted ? 'bg-white/10' : 'bg-gray-50'}`}>
<div className="flex items-center gap-2">
<Users className="w-3.5 h-3.5 flex-shrink-0 text-brand-turquoise" />
<span className={`text-xs font-medium ${plan.highlighted ? 'text-white/80' : 'text-gray-700'}`}>{planUsers}</span>
</div>
<div className="flex items-center gap-2">
<Ship className="w-3.5 h-3.5 flex-shrink-0 text-brand-turquoise" />
<span className={`text-xs font-medium ${plan.highlighted ? 'text-white/80' : 'text-gray-700'}`}>{planShipments}</span>
</div>
<div className="flex items-center gap-2">
<BarChart3 className="w-3.5 h-3.5 flex-shrink-0 text-brand-turquoise" />
<span className={`text-xs font-medium ${plan.highlighted ? 'text-white/80' : 'text-gray-700'}`}>
{t('pricing.commission', { rate: plan.commission })}
</span>
</div>
</div>
<ul className="space-y-2.5 mb-6 flex-1">
{planFeatures.map((feature, featureIndex) => (
<li key={featureIndex} className="flex items-start gap-2.5">
{feature.included ? (
<Check className={`w-4 h-4 flex-shrink-0 mt-0.5 ${plan.highlighted ? 'text-brand-turquoise' : 'text-brand-green'}`} />
) : (
<X className={`w-4 h-4 flex-shrink-0 mt-0.5 ${plan.highlighted ? 'text-white/20' : 'text-gray-300'}`} />
)}
<span className={`text-sm ${
feature.included
? plan.highlighted ? 'text-white/90' : 'text-gray-700'
: plan.highlighted ? 'text-white/30' : 'text-gray-400'
}`}>
{t(`pricing.features.${feature.key}` as any)}
</span>
</li>
))}
</ul>
<Link
href={plan.ctaLink}
className={`block w-full text-center py-3 px-6 rounded-xl font-semibold text-sm transition-all ${
plan.highlighted
? 'bg-brand-turquoise text-white hover:bg-brand-turquoise/90 shadow-lg shadow-brand-turquoise/30 hover:shadow-xl'
: plan.key === 'bronze'
? 'bg-gray-100 text-brand-navy hover:bg-gray-200'
: 'bg-brand-navy text-white hover:bg-brand-navy/90 shadow-md hover:shadow-lg'
}`}
>
{planCta}
</Link>
</div>
</motion.div>
);
})}
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isPricingInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.5 }}
className="mt-12 text-center space-y-2"
>
<p className="text-gray-600 text-sm">
{t('pricing.noCommitment')}
</p>
<p className="text-sm text-gray-500">
{t('pricing.questions')}{' '}
<Link href="/contact" className="text-brand-turquoise font-medium hover:underline">
{t('pricing.contactSales')}
</Link>
</p>
</motion.div>
</div>
</section>
{/* How It Works Section */}
<section
ref={howRef}
className="py-20 lg:py-32 bg-gradient-to-br from-brand-navy to-brand-navy/95 relative overflow-hidden"
>
<div className="absolute inset-0 opacity-10 pointer-events-none">
<div className="absolute top-10 left-10 w-72 h-72 bg-brand-turquoise rounded-full blur-3xl" />
<div className="absolute bottom-10 right-10 w-72 h-72 bg-brand-green rounded-full blur-3xl" />
</div>
<div className="max-w-7xl mx-auto px-6 lg:px-8 relative z-10">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isHowInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8 }}
className="text-center mb-16"
>
<h2 className="text-4xl lg:text-5xl font-bold text-white mb-4">{t('howItWorks.title')}</h2>
<p className="text-xl text-white/80 max-w-2xl mx-auto">
{t('howItWorks.subtitle')}
</p>
</motion.div>
<div className="relative">
<div className="hidden lg:block absolute top-10 left-[12.5%] right-[12.5%] h-px bg-white/15">
<motion.div
initial={{ scaleX: 0 }}
animate={isHowInView ? { scaleX: 1 } : {}}
transition={{ duration: 1.6, delay: 0.5, ease: 'easeInOut' }}
style={{ transformOrigin: 'left' }}
className="absolute inset-0 bg-brand-turquoise"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{[
{ stepKey: 'step1', step: '01', icon: Search },
{ stepKey: 'step2', step: '02', icon: BarChart3 },
{ stepKey: 'step3', step: '03', icon: CheckCircle2 },
{ stepKey: 'step4', step: '04', icon: Container },
].map((step, index) => {
const IconComponent = step.icon;
return (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
animate={isHowInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.3 + index * 0.15 }}
className="text-center relative z-10"
>
<div className="mb-5">
<div className="w-20 h-20 bg-brand-turquoise rounded-full flex items-center justify-center text-3xl font-bold text-white mx-auto shadow-xl ring-4 ring-brand-turquoise/20">
{step.step}
</div>
</div>
<div className="flex justify-center mb-3">
<div className="p-2 bg-white/10 rounded-lg">
<IconComponent className="w-5 h-5 text-brand-turquoise" />
</div>
</div>
<h3 className="text-xl font-bold text-white mb-2">
{t(`howItWorks.${step.stepKey}.title` as any)}
</h3>
<p className="text-white/70 text-sm leading-relaxed">
{t(`howItWorks.${step.stepKey}.description` as any)}
</p>
</motion.div>
);
})}
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isHowInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 1.4 }}
className="mt-16 text-center"
>
<Link
href="/register"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center space-x-2 px-8 py-4 bg-brand-turquoise text-white rounded-xl font-semibold text-lg hover:bg-brand-turquoise/90 transition-all hover:shadow-2xl hover:scale-105 group"
>
<span>{tCommon('tryNow')}</span>
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
<p className="mt-3 text-white/50 text-sm">
{t('howItWorks.ctaHint')}
</p>
</motion.div>
</div>
</section>
{/* Testimonials Section */}
<section
ref={testimonialsRef}
className="py-12 sm:py-20 lg:py-32 bg-gradient-to-br from-gray-50 to-white"
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isTestimonialsInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8 }}
className="text-center mb-10 sm:mb-16"
>
<h2 className="text-2xl sm:text-4xl lg:text-5xl font-bold text-brand-navy mb-3 sm:mb-4">
{t('testimonials.title')}
</h2>
<p className="text-base sm:text-xl text-gray-600 max-w-2xl mx-auto">
{t('testimonials.subtitle')}
</p>
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
animate={isTestimonialsInView ? 'visible' : 'hidden'}
className="grid grid-cols-1 md:grid-cols-3 gap-8"
>
{testimonials.map((testimonial, index) => (
<motion.div
key={index}
variants={itemVariants}
whileHover={{ y: -10 }}
className="bg-white p-8 rounded-2xl shadow-lg border border-gray-100"
>
<div className="flex mb-4">
{[...Array(5)].map((_, i) => (
<svg
key={i}
className="w-5 h-5 text-yellow-400 fill-current"
viewBox="0 0 20 20"
>
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" />
</svg>
))}
</div>
<p className="text-gray-700 mb-6 leading-relaxed italic">"{testimonial.quote}"</p>
<div className="border-t pt-4">
<div className="font-bold text-brand-navy">{testimonial.author}</div>
<div className="text-sm text-gray-600">
{testimonial.role} - {testimonial.company}
</div>
</div>
</motion.div>
))}
</motion.div>
</div>
</section>
{/* CTA Section */}
<section ref={ctaRef} className="py-12 sm:py-20 lg:py-32">
<motion.div
variants={containerVariants}
initial="hidden"
animate={isCtaInView ? 'visible' : 'hidden'}
className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center"
>
<motion.div variants={itemVariants}>
<h2 className="text-2xl sm:text-4xl lg:text-5xl font-bold text-brand-navy mb-4 sm:mb-6">
{t('cta.title')}
</h2>
<p className="text-base sm:text-xl text-gray-600 mb-8 sm:mb-10">
{t('cta.subtitle')}
</p>
</motion.div>
<motion.div
variants={itemVariants}
className="flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-6 px-2 sm:px-0"
>
{isAuthenticated && user ? (
<Link
href="/dashboard"
target="_blank"
rel="noopener noreferrer"
className="group px-6 sm:px-8 py-3 sm:py-4 bg-brand-turquoise text-white rounded-lg hover:bg-brand-turquoise/90 transition-all hover:shadow-2xl hover:scale-105 font-semibold text-base sm:text-lg w-full sm:w-auto flex items-center justify-center space-x-2"
>
<span>{t('cta.ctaAuthenticated')}</span>
<LayoutDashboard className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
) : (
<>
<Link
href="/register"
target="_blank"
rel="noopener noreferrer"
className="group px-6 sm:px-8 py-3 sm:py-4 bg-brand-turquoise text-white rounded-lg hover:bg-brand-turquoise/90 transition-all hover:shadow-2xl hover:scale-105 font-semibold text-base sm:text-lg w-full sm:w-auto flex items-center justify-center space-x-2"
>
<span>{t('cta.ctaRegister')}</span>
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
<Link
href="/login"
target="_blank"
rel="noopener noreferrer"
className="px-6 sm:px-8 py-3 sm:py-4 bg-brand-navy text-white rounded-lg hover:bg-brand-navy/90 transition-all hover:shadow-xl font-semibold text-base sm:text-lg w-full sm:w-auto text-center"
>
{t('cta.ctaLogin')}
</Link>
</>
)}
</motion.div>
<motion.div
variants={itemVariants}
className="flex flex-wrap items-center justify-center gap-4 sm:gap-6 mt-8 text-sm text-gray-500"
>
<div className="flex items-center space-x-2">
<CheckCircle2 className="w-4 h-4 text-brand-green" />
<span>{t('cta.features.noCard')}</span>
</div>
<div className="flex items-center space-x-2">
<Clock className="w-4 h-4 text-brand-green" />
<span>{t('cta.features.quickSetup')}</span>
</div>
<div className="flex items-center space-x-2">
<Shield className="w-4 h-4 text-brand-green" />
<span>{t('cta.features.secure')}</span>
</div>
</motion.div>
</motion.div>
</section>
<LandingFooter />
</div>
);
}