Files
contexta_fe/src/pages/LandingPage.tsx
2026-04-03 09:15:25 +00:00

1092 lines
60 KiB
TypeScript

import React, { useState, useEffect, useRef } from 'react'
import { Link } from 'react-router-dom'
import {
Sparkles, Bot, Shield, Zap, ArrowRight,
Check, MessageSquare, Upload, Play, ChevronRight, Star,
Cpu, Menu, X,
Users, CalendarDays, Megaphone, TrendingUp, Inbox,
} from 'lucide-react'
// ─── Translations ──────────────────────────────────────────────────────────────
const TRANSLATIONS = {
fr: {
nav: {
marketplace: 'Marché',
pricing: 'Tarifs',
features: 'Fonctionnalités',
signin: 'Connexion',
cta: 'Commencer gratuitement',
},
hero: {
badge: "La plateforme IA qui gère vos interactions clients",
h1a: 'Une plateforme pour chatter, réserver,',
h1b: 'et conclure des ventes',
p: "Créez des chatbots RAG à partir de vos documents. Ajoutez le chat en direct, la capture de leads, la prise de rendez-vous et les campagnes Telegram — tout en un seul endroit.",
cta1: 'Commencer gratuitement',
cta2: 'Explorer le marché',
sub: 'Sans carte bancaire · Gratuit pour toujours · Installation en 2 min',
badge1: 'Lead Capturé',
badge2: 'Rendez-vous Confirmé',
},
stats: {
chatbots: 'Chatbots Créés',
companies: 'Entreprises',
messages: 'Messages Traités',
uptime: 'Disponibilité',
},
features: {
badge: 'Fonctionnalités',
h2: 'Tout pour gérer vos relations clients',
p: "Du chatbot IA à l'agent en direct, pipeline de leads, rendez-vous et campagnes — une plateforme, zéro intégration.",
items: [
{ title: 'Chatbots RAG', desc: 'Importez des PDF, DOCX et CSV. Votre chatbot répond à partir de votre contenu avec des sources citées.' },
{ title: 'Boîte de réception en direct', desc: "Prenez en charge n'importe quelle conversation en temps réel. Répondez en tant qu'agent et suivez chaque interaction." },
{ title: 'CRM de leads', desc: "Le chatbot capture les leads automatiquement. Gérez le pipeline : Nouveau → Contacté → Qualifié → Conclu." },
{ title: 'Prise de rendez-vous', desc: "Définissez vos horaires et durées de créneaux. Le bot partage un lien de réservation et les clients choisissent en ligne." },
{ title: 'Campagnes Telegram', desc: "Diffusez des messages ciblés à tous vos abonnés Telegram en un clic. Suivez les taux de livraison." },
{ title: 'Analyse des lacunes', desc: "Voyez quelles questions le bot n'a pas su répondre. Ajoutez le contenu manquant et comblezles lacunes rapidement." },
],
},
howItWorks: {
badge: 'Comment ça marche',
h2: 'Des documents à une suite métier complète',
steps: [
{ title: 'Importer & Entraîner', desc: 'Déposez vos PDF, DOCX, CSV ou Excel. Le moteur RAG indexe votre contenu et votre chatbot est prêt en quelques minutes.' },
{ title: 'Déployer & Capturer', desc: 'Publiez sur votre site web ou Telegram. Le bot gère les conversations, capture les leads et partage votre lien de réservation automatiquement.' },
{ title: 'Gérer & Croître', desc: "Rejoignez les conversations en direct, travaillez votre pipeline de leads, confirmez les rendez-vous et diffusez des campagnes — depuis une seule boîte de réception." },
],
},
widget: {
badge: 'Fonctionnalité Agence',
h2: 'Une ligne de code.\nVotre chatbot, partout.',
p: "Collez un simple script et votre chatbot est en ligne — sur n'importe quel site, n'importe quel framework. Aucune dépendance, aucune étape de build. Compatible WordPress, Webflow, Shopify, HTML brut, et plus.",
bullets: [
"Intégrez dans n'importe quelle page HTML — aucun framework requis",
"Le widget se charge de façon asynchrone, zéro impact sur la vitesse",
"Stylé pour correspondre à votre marque",
"L'ID du chatbot garde vos données complètement isolées",
],
status: 'Widget chargé · 0 dépendances',
},
workflow: {
badge: 'Suite Complète',
h2: 'Tout en un seul endroit — fini les outils éparpillés',
p: "La plupart des entreprises utilisent un chatbot, un CRM, un outil de réservation et une app de diffusion séparément. Contexta remplace les quatre — construit autour de votre chatbot IA.",
bullets: [
"Prenez en charge n'importe quelle conversation en tant qu'agent",
'Faites avancer les leads dans votre pipeline en un clic',
'Les clients réservent des créneaux directement depuis le chat',
'Diffusez à tous vos abonnés Telegram en une fois',
],
inbox: { title: 'Boîte de réception', badge: "Agent en charge", msg: '"Bonjour, j\'ai besoin d\'aide avec ma commande #4821..."' },
leads: { title: 'Pipeline Leads', statuses: ['Nouveau', 'Contacté', 'Qualifié', 'Conclu'] },
booking: { title: 'Nouveau Rendez-vous', badge: 'Confirmé', detail: 'Alice Martin · Jeu 12 Juin · 10h00' },
campaign: { title: "Campagne Soldes d'Été", badge: 'Envoyé', detail: '482 / 500 livrés · Telegram' },
},
testimonials: {
badge: 'Témoignages',
h2: 'Aimé par les entreprises',
items: [
{ quote: "Notre chatbot prend désormais des rendez-vous directement depuis les conversations. Les clients choisissent un créneau et nous recevons une notification — plus d'emails aller-retour.", name: 'Sarah Chen', role: 'Propriétaire', company: 'Chen Physio Clinic' },
{ quote: "La boîte de réception en direct change tout. On laisse l'IA gérer 80% des questions et on intervient seulement quand c'est nécessaire. Notre temps de réponse a été divisé par deux.", name: 'Marcus Johnson', role: 'Responsable Support', company: 'MedAssist' },
{ quote: "Nous envoyons des campagnes Telegram à nos abonnés chaque semaine. Contexta a remplacé trois outils séparés — chatbot, CRM et diffusion — à une fraction du coût.", name: 'Elena Kowalski', role: 'Responsable Marketing', company: 'DataBridge' },
],
},
pricing: {
h2: 'Gratuit pour démarrer, évoluez à votre rythme',
p: "Chaque plan inclut le créateur de chatbots. Débloquez le chat en direct, la réservation et les campagnes quand vous êtes prêt.",
seeAll: 'Voir la comparaison complète',
plans: {
free: { name: 'Gratuit', features: ['1 chatbot publié', 'Modèle Llama 3.3 70B', '100 conversations/mois', 'Boîte de réception lecture seule', 'Leads en lecture seule'], cta: 'Commencer' },
starter: { name: 'Starter', features: ['3 chatbots publiés', 'Tous les modèles Fireworks AI', '1 500 conversations/mois', 'Chat en direct + réponses agent', 'CRM complet', 'Rendez-vous (1 chatbot)', '3 campagnes/mois · 500 destinataires', 'Canal Telegram'], cta: 'Démarrer Starter' },
business: { name: 'Business', popular: 'Populaire', features: ['10 chatbots publiés', 'GPT-4o · Claude · Gemini', '5 000 conversations/mois', 'Réservations illimitées', 'Campagnes illimitées · 5k destinataires', 'Analyse des lacunes', 'Supprimer le badge "Propulsé par"'], cta: 'Démarrer Business' },
agency: { name: 'Agence', features: ['Chatbots illimités', 'Tous les modèles', '20 000 conversations/mois', 'Destinataires illimités', 'Export de code', 'Prêt pour le white-label'], cta: 'Démarrer Agence' },
},
},
cta: {
h2: 'Votre suite IA, prête en quelques minutes',
p: "Des centaines d'entreprises utilisent Contexta pour automatiser le chat, capturer des leads, réserver des rendez-vous et lancer des campagnes.",
cta1: 'Commencer gratuitement',
cta2: 'Explorer le marché',
},
footer: { copyright: '© 2025 Contexta. Fait avec ❤️ pour les créateurs.' },
chat: [
{ role: 'user', text: 'Quelle est votre politique de retour ?' },
{ role: 'bot', text: "D'après vos documents, nous offrons une politique de retour de 30 jours pour tous les articles en état d'origine. Les remboursements sont traités sous 5 à 7 jours ouvrables." },
{ role: 'user', text: 'Puis-je retourner des articles soldés ?' },
{ role: 'bot', text: "Selon la section 4.2 de votre politique, les articles soldés peuvent être échangés sous 14 jours mais ne sont pas éligibles au remboursement. Souhaitez-vous en savoir plus ?" },
],
chatLabel: 'Support Bot',
chatOnline: 'En ligne',
chatPlaceholder: 'Écrire un message...',
},
en: {
nav: {
marketplace: 'Marketplace',
pricing: 'Pricing',
features: 'Features',
signin: 'Sign in',
cta: 'Get started free',
},
hero: {
badge: 'The AI platform that runs your customer interactions',
h1a: 'One platform to chat, book,',
h1b: 'and close deals',
p: 'Build RAG-powered chatbots from your documents. Then add live chat, lead capture, appointment booking, and Telegram campaigns — all in one place.',
cta1: 'Start for free',
cta2: 'Explore marketplace',
sub: 'No credit card required · Free forever · 2min setup',
badge1: 'Lead Captured',
badge2: 'Booking Confirmed',
},
stats: {
chatbots: 'Chatbots Created',
companies: 'Companies',
messages: 'Messages Processed',
uptime: 'Uptime',
},
features: {
badge: 'Features',
h2: 'Everything to run your customer relationships',
p: 'From AI chatbot to live agent, lead pipeline, appointments, and broadcast campaigns — one platform, zero integrations.',
items: [
{ title: 'RAG-Powered Chatbots', desc: 'Upload PDFs, DOCX, and CSV files. Your chatbot answers from your actual content with source citations.' },
{ title: 'Live Chat Inbox', desc: 'Take over any conversation in real time. Reply as an agent, resolve tickets, and track every interaction.' },
{ title: 'Lead CRM', desc: 'Chatbot captures leads automatically. You manage the pipeline: New → Contacted → Qualified → Closed.' },
{ title: 'Appointment Booking', desc: 'Set business hours and slot durations. The bot shares a booking link and customers pick a slot online.' },
{ title: 'Telegram Campaigns', desc: 'Broadcast targeted messages to all your Telegram subscribers in one click. Track delivery counts.' },
{ title: 'Knowledge Gap Insights', desc: "See which questions your bot couldn't answer. Add the missing content and close the gaps fast." },
],
},
howItWorks: {
badge: 'How It Works',
h2: 'From documents to full business suite',
steps: [
{ title: 'Upload & Train', desc: 'Drop your PDFs, DOCX, CSV, or Excel files. The RAG engine indexes your content and your chatbot is ready in minutes.' },
{ title: 'Deploy & Capture', desc: 'Publish to your website or Telegram. The bot handles conversations, captures leads, and shares your booking link automatically.' },
{ title: 'Manage & Grow', desc: 'Jump into live conversations, work your lead pipeline, confirm appointments, and broadcast campaigns — all from one inbox.' },
],
},
widget: {
badge: 'Agency Feature',
h2: 'One line of code.\nYour chatbot, anywhere.',
p: 'Paste a single script tag and your chatbot is live — on any website, any framework. No dependencies, no build step. Works with WordPress, Webflow, Shopify, raw HTML, anything.',
bullets: [
'Drop it into any HTML page — no framework needed',
'Widget loads asynchronously, zero impact on page speed',
'Styled to match your brand out of the box',
'Chatbot ID keeps your data completely isolated',
],
status: 'Chatbot widget loaded · 0 dependencies',
},
workflow: {
badge: 'Full Business Suite',
h2: 'Everything in one place — no juggling tools',
p: 'Most businesses use a chatbot, a CRM, a booking tool, and a broadcast app separately. Contexta replaces all four — built around your AI chatbot so everything stays connected.',
bullets: [
'Take over any conversation as a live agent',
'Move leads through your pipeline with one click',
'Customers book slots directly from chat',
'Broadcast to all Telegram subscribers at once',
],
inbox: { title: 'Live Inbox', badge: 'Agent handling', msg: '"Hi, I need help with my order #4821..."' },
leads: { title: 'Lead Pipeline', statuses: ['New', 'Contacted', 'Qualified', 'Closed'] },
booking: { title: 'New Booking', badge: 'Confirmed', detail: 'Alice Martin · Thu Jun 12 · 10:00 AM' },
campaign: { title: 'Summer Sale Campaign', badge: 'Sent', detail: '482 / 500 delivered · Telegram' },
},
testimonials: {
badge: 'Testimonials',
h2: 'Loved by businesses',
items: [
{ quote: 'Our chatbot now books appointments directly from conversations. Clients pick a slot and we get a notification — no back-and-forth emails anymore.', name: 'Sarah Chen', role: 'Owner', company: 'Chen Physio Clinic' },
{ quote: 'The live inbox is a game changer. We let the AI handle 80% of questions and jump in only when it matters. Our response time dropped by half.', name: 'Marcus Johnson', role: 'Head of Support', company: 'MedAssist' },
{ quote: 'We run Telegram campaigns to our subscribers every week. Contexta replaced three separate tools — chatbot, CRM, and broadcast — at a fraction of the cost.', name: 'Elena Kowalski', role: 'Marketing Lead', company: 'DataBridge' },
],
},
pricing: {
h2: 'Start free, scale as you grow',
p: "Every plan includes the chatbot builder. Unlock live chat, booking, and campaigns when you're ready.",
seeAll: 'See full feature comparison',
plans: {
free: { name: 'Free', features: ['1 published chatbot', 'Llama 3.3 70B model', '100 conversations/mo', 'Read-only inbox', 'View-only leads'], cta: 'Get started' },
starter: { name: 'Starter', features: ['3 published chatbots', 'All Fireworks AI models', '1,500 conversations/mo', 'Live chat + agent replies', 'Full lead CRM', 'Appointments (1 chatbot)', '3 campaigns/mo · 500 recipients', 'Telegram channel'], cta: 'Start Starter' },
business: { name: 'Business', popular: 'Popular', features: ['10 published chatbots', 'GPT-4o · Claude · Gemini', '5,000 conversations/mo', 'Unlimited booking chatbots', 'Unlimited campaigns · 5k recipients', 'Knowledge gap suggestions', 'Remove "Powered by" badge'], cta: 'Start Business' },
agency: { name: 'Agency', features: ['Unlimited chatbots', 'All models', '20,000 conversations/mo', 'Unlimited recipients', 'Code export', 'White-label ready'], cta: 'Start Agency' },
},
},
cta: {
h2: 'Your AI business suite, ready in minutes',
p: 'Join hundreds of businesses using Contexta to automate chat, capture leads, book appointments, and run campaigns.',
cta1: 'Get started for free',
cta2: 'Explore marketplace',
},
footer: { copyright: '© 2025 Contexta. Built with ❤️ for builders.' },
chat: [
{ role: 'user', text: 'What are your return policies?' },
{ role: 'bot', text: 'Based on your company documents, we offer a 30-day return policy for all items in original condition. Refunds are processed within 5-7 business days.' },
{ role: 'user', text: 'Can I return sale items?' },
{ role: 'bot', text: 'According to Section 4.2 of your policy, sale items can be exchanged within 14 days but are not eligible for refunds. Would you like to know more?' },
],
chatLabel: 'Support Bot',
chatOnline: 'Online',
chatPlaceholder: 'Type a message...',
},
}
type Lang = keyof typeof TRANSLATIONS
// ─── Intersection Observer Hook ────────────────────────────────────────────────
function useInView(options?: IntersectionObserverInit) {
const ref = useRef<HTMLDivElement>(null)
const [isInView, setIsInView] = useState(false)
useEffect(() => {
const el = ref.current
if (!el) return
const observer = new IntersectionObserver(
([entry]) => { if (entry.isIntersecting) setIsInView(true) },
{ threshold: 0.15, ...options }
)
observer.observe(el)
return () => observer.disconnect()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return { ref, isInView }
}
// ─── Animated Counter ──────────────────────────────────────────────────────────
const AnimatedCounter: React.FC<{ end: number; suffix?: string; label: string; isInView: boolean }> = ({ end, suffix = '', label, isInView }) => {
const [count, setCount] = useState(0)
useEffect(() => {
if (!isInView) return
let start = 0
const duration = 2000
const increment = end / (duration / 16)
const timer = setInterval(() => {
start += increment
if (start >= end) { setCount(end); clearInterval(timer) }
else setCount(Math.floor(start))
}, 16)
return () => clearInterval(timer)
}, [isInView, end])
return (
<div className="text-center">
<div className="text-4xl md:text-5xl font-extrabold text-gradient">
{count.toLocaleString()}{suffix}
</div>
<p className="text-sm text-gray-500 mt-2 font-medium">{label}</p>
</div>
)
}
// ─── Floating Chat Preview ─────────────────────────────────────────────────────
const FloatingChatPreview: React.FC<{
conversation: { role: string; text: string }[]
label: string
online: string
placeholder: string
}> = ({ conversation, label, online, placeholder }) => {
const [messages, setMessages] = useState<{ role: string; text: string }[]>([])
const [isTyping, setIsTyping] = useState(false)
const [step, setStep] = useState(0)
useEffect(() => {
setMessages([])
setStep(0)
}, [conversation])
useEffect(() => {
if (step >= conversation.length) {
const timeout = setTimeout(() => { setMessages([]); setStep(0) }, 4000)
return () => clearTimeout(timeout)
}
const timeout = setTimeout(() => {
if (conversation[step].role === 'bot') {
setIsTyping(true)
setTimeout(() => {
setIsTyping(false)
setMessages(prev => [...prev, conversation[step]])
setStep(s => s + 1)
}, 1500)
} else {
setMessages(prev => [...prev, conversation[step]])
setStep(s => s + 1)
}
}, step === 0 ? 1500 : 1000)
return () => clearTimeout(timeout)
}, [step, conversation])
return (
<div className="w-full max-w-sm bg-white rounded-2xl shadow-2xl shadow-primary-500/10 border border-gray-200/60 overflow-hidden">
<div className="bg-gradient-to-r from-primary-600 to-primary-700 px-4 py-3 flex items-center gap-3">
<div className="w-8 h-8 bg-white/20 rounded-full flex items-center justify-center">
<Bot className="w-4 h-4 text-white" />
</div>
<div>
<p className="text-white text-sm font-semibold">{label}</p>
<div className="flex items-center gap-1.5">
<span className="w-2 h-2 bg-green-400 rounded-full animate-pulse-soft" />
<span className="text-primary-200 text-xs">{online}</span>
</div>
</div>
</div>
<div className="h-64 p-4 space-y-3 overflow-hidden bg-gray-50/50">
{messages.map((msg, i) => (
<div
key={i}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'} animate-fade-in-up`}
style={{ animationDelay: '0ms' }}
>
<div className={`max-w-[85%] px-3.5 py-2.5 rounded-2xl text-sm leading-relaxed ${
msg.role === 'user'
? 'bg-primary-600 text-white rounded-br-md'
: 'bg-white text-gray-700 shadow-sm border border-gray-100 rounded-bl-md'
}`}>
{msg.text}
</div>
</div>
))}
{isTyping && (
<div className="flex justify-start animate-fade-in">
<div className="bg-white px-4 py-3 rounded-2xl rounded-bl-md shadow-sm border border-gray-100 flex gap-1.5">
<span className="w-2 h-2 bg-gray-400 rounded-full animate-typing" style={{ animationDelay: '0ms' }} />
<span className="w-2 h-2 bg-gray-400 rounded-full animate-typing" style={{ animationDelay: '200ms' }} />
<span className="w-2 h-2 bg-gray-400 rounded-full animate-typing" style={{ animationDelay: '400ms' }} />
</div>
</div>
)}
</div>
<div className="border-t border-gray-100 p-3 flex items-center gap-2 bg-white">
<div className="flex-1 bg-gray-100 rounded-full px-4 py-2 text-sm text-gray-400">
{placeholder}
</div>
<button className="w-8 h-8 bg-primary-600 rounded-full flex items-center justify-center hover:bg-primary-700 transition-colors">
<ArrowRight className="w-4 h-4 text-white" />
</button>
</div>
</div>
)
}
// ─── Feature Card ──────────────────────────────────────────────────────────────
const FeatureCard: React.FC<{
icon: React.ReactNode
title: string
desc: string
color: string
delay: number
isInView: boolean
}> = ({ icon, title, desc, color, delay, isInView }) => (
<div
className={`group relative bg-white rounded-2xl p-6 border border-gray-200/80 hover:border-primary-200
hover:shadow-xl hover:shadow-primary-500/5 transition-all duration-500 hover:-translate-y-1
${isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}
style={{ transitionDelay: `${delay}ms` }}
>
<div className="absolute inset-0 rounded-2xl bg-gradient-to-br from-primary-50/0 to-purple-50/0
group-hover:from-primary-50/50 group-hover:to-purple-50/30 transition-all duration-500" />
<div className="relative">
<div className={`w-12 h-12 rounded-xl flex items-center justify-center mb-4 ${color}
group-hover:scale-110 transition-transform duration-300`}>
{icon}
</div>
<h3 className="font-semibold text-gray-900 mb-2 group-hover:text-primary-700 transition-colors">{title}</h3>
<p className="text-sm text-gray-500 leading-relaxed">{desc}</p>
</div>
</div>
)
// ─── How It Works Step ─────────────────────────────────────────────────────────
const Step: React.FC<{
num: number
icon: React.ReactNode
title: string
desc: string
isInView: boolean
delay: number
}> = ({ num, icon, title, desc, isInView, delay }) => (
<div
className={`relative flex flex-col items-center text-center transition-all duration-700
${isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-12'}`}
style={{ transitionDelay: `${delay}ms` }}
>
<div className="relative mb-5">
<div className="w-16 h-16 bg-gradient-to-br from-primary-500 to-primary-700 rounded-2xl flex items-center justify-center
shadow-lg shadow-primary-500/25 rotate-3 hover:rotate-0 transition-transform duration-300">
{icon}
</div>
<div className="absolute -top-2 -right-2 w-7 h-7 bg-white rounded-full border-2 border-primary-500 flex items-center justify-center">
<span className="text-xs font-bold text-primary-600">{num}</span>
</div>
</div>
<h3 className="font-semibold text-gray-900 mb-2">{title}</h3>
<p className="text-sm text-gray-500 leading-relaxed max-w-xs">{desc}</p>
</div>
)
// ─── Testimonial Card ──────────────────────────────────────────────────────────
const TestimonialCard: React.FC<{
quote: string
name: string
role: string
company: string
isInView: boolean
delay: number
}> = ({ quote, name, role, company, isInView, delay }) => (
<div
className={`bg-white rounded-2xl p-6 border border-gray-200/80 shadow-sm hover:shadow-lg
transition-all duration-700 ${isInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}
style={{ transitionDelay: `${delay}ms` }}
>
<div className="flex gap-1 mb-4">
{[...Array(5)].map((_, i) => (
<Star key={i} className="w-4 h-4 text-yellow-400 fill-yellow-400" />
))}
</div>
<p className="text-gray-600 text-sm leading-relaxed mb-5 italic">"{quote}"</p>
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-gradient-to-br from-primary-400 to-primary-600 rounded-full flex items-center justify-center">
<span className="text-white text-sm font-bold">{name[0]}</span>
</div>
<div>
<p className="text-sm font-semibold text-gray-900">{name}</p>
<p className="text-xs text-gray-500">{role} · {company}</p>
</div>
</div>
</div>
)
// ═══════════════════════════════════════════════════════════════════════════════
// LANDING PAGE
// ═══════════════════════════════════════════════════════════════════════════════
export const LandingPage: React.FC = () => {
const [lang, setLang] = useState<Lang>('fr')
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const [scrolled, setScrolled] = useState(false)
const t = TRANSLATIONS[lang]
const { ref: featuresRef, isInView: featuresInView } = useInView()
const { ref: howItWorksRef, isInView: howItWorksInView } = useInView()
const { ref: statsRef, isInView: statsInView } = useInView()
const { ref: testimonialsRef, isInView: testimonialsInView } = useInView()
const { ref: pricingRef, isInView: pricingInView } = useInView()
const { ref: ctaRef, isInView: ctaInView } = useInView()
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 20)
window.addEventListener('scroll', handleScroll, { passive: true })
return () => window.removeEventListener('scroll', handleScroll)
}, [])
const FEATURE_ICONS = [
{ icon: <Bot className="w-6 h-6" />, color: 'bg-blue-100 text-blue-600' },
{ icon: <Inbox className="w-6 h-6" />, color: 'bg-sky-100 text-sky-600' },
{ icon: <Users className="w-6 h-6" />, color: 'bg-green-100 text-green-600' },
{ icon: <CalendarDays className="w-6 h-6" />, color: 'bg-orange-100 text-orange-600' },
{ icon: <Megaphone className="w-6 h-6" />, color: 'bg-purple-100 text-purple-600' },
{ icon: <TrendingUp className="w-6 h-6" />, color: 'bg-rose-100 text-rose-600' },
]
const LEAD_COLORS = ['bg-gray-100 text-gray-600', 'bg-blue-100 text-blue-600', 'bg-yellow-100 text-yellow-700', 'bg-green-100 text-green-700']
return (
<div className="min-h-screen bg-white overflow-x-hidden">
{/* ── Navigation ── */}
<nav className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
scrolled ? 'bg-white/90 backdrop-blur-xl shadow-sm border-b border-gray-100' : 'bg-transparent'
}`}>
<div className="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
<Link to="/" className="flex items-center gap-2.5 group">
<div className="w-9 h-9 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl flex items-center justify-center
shadow-lg shadow-primary-500/20 group-hover:shadow-primary-500/40 transition-shadow">
<Sparkles className="w-4.5 h-4.5 text-white" />
</div>
<span className="font-bold text-gray-900 text-lg tracking-tight">Contexta</span>
</Link>
{/* Desktop nav */}
<div className="hidden md:flex items-center gap-8">
<Link to="/marketplace" className="text-sm text-gray-600 hover:text-gray-900 transition-colors font-medium">{t.nav.marketplace}</Link>
<Link to="/pricing" className="text-sm text-gray-600 hover:text-gray-900 transition-colors font-medium">{t.nav.pricing}</Link>
<a href="#features" className="text-sm text-gray-600 hover:text-gray-900 transition-colors font-medium">{t.nav.features}</a>
</div>
<div className="hidden md:flex items-center gap-3">
{/* Language toggle */}
<div className="flex items-center border border-gray-200 rounded-lg overflow-hidden text-xs font-semibold">
<button
onClick={() => setLang('fr')}
className={`px-2.5 py-1.5 transition-colors ${lang === 'fr' ? 'bg-primary-600 text-white' : 'text-gray-500 hover:bg-gray-50'}`}
>
FR
</button>
<button
onClick={() => setLang('en')}
className={`px-2.5 py-1.5 transition-colors ${lang === 'en' ? 'bg-primary-600 text-white' : 'text-gray-500 hover:bg-gray-50'}`}
>
EN
</button>
</div>
<Link to="/login" className="text-sm text-gray-600 hover:text-gray-900 transition-colors font-medium px-4 py-2">
{t.nav.signin}
</Link>
<Link to="/signup" className="bg-primary-600 text-white text-sm px-5 py-2.5 rounded-xl hover:bg-primary-700
font-semibold transition-all shadow-md shadow-primary-500/20 hover:shadow-lg hover:shadow-primary-500/30 hover:-translate-y-0.5">
{t.nav.cta}
</Link>
</div>
{/* Mobile hamburger */}
<div className="md:hidden flex items-center gap-2">
{/* Mobile language toggle */}
<div className="flex items-center border border-gray-200 rounded-lg overflow-hidden text-xs font-semibold">
<button onClick={() => setLang('fr')} className={`px-2 py-1 transition-colors ${lang === 'fr' ? 'bg-primary-600 text-white' : 'text-gray-500'}`}>FR</button>
<button onClick={() => setLang('en')} className={`px-2 py-1 transition-colors ${lang === 'en' ? 'bg-primary-600 text-white' : 'text-gray-500'}`}>EN</button>
</div>
<button
className="p-2 rounded-lg hover:bg-gray-100 transition-colors"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
{mobileMenuOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
</div>
</div>
{/* Mobile menu */}
{mobileMenuOpen && (
<div className="md:hidden bg-white border-t border-gray-100 px-6 py-4 space-y-3 animate-fade-in-down shadow-lg">
<Link to="/marketplace" className="block text-sm text-gray-700 py-2 font-medium" onClick={() => setMobileMenuOpen(false)}>{t.nav.marketplace}</Link>
<Link to="/pricing" className="block text-sm text-gray-700 py-2 font-medium" onClick={() => setMobileMenuOpen(false)}>{t.nav.pricing}</Link>
<hr className="border-gray-100" />
<Link to="/login" className="block text-sm text-gray-700 py-2 font-medium" onClick={() => setMobileMenuOpen(false)}>{t.nav.signin}</Link>
<Link to="/signup" className="block bg-primary-600 text-white text-sm px-4 py-2.5 rounded-xl font-semibold text-center" onClick={() => setMobileMenuOpen(false)}>
{t.nav.cta}
</Link>
</div>
)}
</nav>
{/* ── Hero ── */}
<section className="relative pt-32 pb-20 md:pt-40 md:pb-28 overflow-hidden">
<div className="absolute inset-0 bg-grid opacity-50" />
<div className="absolute top-20 -left-40 w-80 h-80 bg-primary-200/30 rounded-full blur-3xl animate-float" />
<div className="absolute top-40 -right-40 w-96 h-96 bg-purple-200/20 rounded-full blur-3xl animate-float-delayed" />
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-full h-32 bg-gradient-to-t from-white to-transparent" />
<div className="relative max-w-6xl mx-auto px-6">
<div className="flex flex-col lg:flex-row items-center gap-12 lg:gap-16">
<div className="flex-1 text-center lg:text-left">
<div className="inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-sm px-4 py-1.5 rounded-full
mb-6 font-medium border border-primary-100 animate-fade-in-down">
<Zap className="w-3.5 h-3.5" />
{t.hero.badge}
<ChevronRight className="w-3.5 h-3.5" />
</div>
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold text-gray-900 mb-6 leading-tight animate-fade-in-up tracking-tight">
{t.hero.h1a}{' '}
<span className="text-gradient">{t.hero.h1b}</span>
</h1>
<p className="text-lg sm:text-xl text-gray-500 max-w-xl mx-auto lg:mx-0 mb-10 leading-relaxed opacity-0 animate-fade-in-up" style={{ animationDelay: '200ms' }}>
{t.hero.p}
</p>
<div className="flex flex-col sm:flex-row items-center justify-center lg:justify-start gap-4 opacity-0 animate-fade-in-up" style={{ animationDelay: '400ms' }}>
<Link to="/signup" className="bg-primary-600 text-white px-7 py-3.5 rounded-xl font-semibold
hover:bg-primary-700 transition-all flex items-center gap-2 shadow-lg shadow-primary-500/25
hover:shadow-xl hover:shadow-primary-500/30 hover:-translate-y-0.5 w-full sm:w-auto justify-center text-base">
{t.hero.cta1} <ArrowRight className="w-4 h-4" />
</Link>
<Link to="/marketplace" className="border border-gray-300 text-gray-700 px-7 py-3.5 rounded-xl font-semibold
hover:bg-gray-50 hover:border-gray-400 transition-all w-full sm:w-auto text-center text-base">
{t.hero.cta2}
</Link>
</div>
<p className="text-sm text-gray-400 mt-5 opacity-0 animate-fade-in" style={{ animationDelay: '600ms' }}>
{t.hero.sub}
</p>
</div>
<div className="flex-shrink-0 opacity-0 animate-fade-in-up" style={{ animationDelay: '500ms' }}>
<div className="relative">
<div className="absolute -inset-4 bg-gradient-to-r from-primary-500/20 to-purple-500/20 rounded-3xl blur-2xl" />
<div className="relative animate-float">
<FloatingChatPreview
conversation={t.chat}
label={t.chatLabel}
online={t.chatOnline}
placeholder={t.chatPlaceholder}
/>
</div>
<div className="absolute -left-12 top-12 bg-white rounded-xl px-3 py-2 shadow-lg border border-gray-100
flex items-center gap-2 animate-bounce-subtle hidden lg:flex">
<div className="w-6 h-6 bg-green-100 rounded-full flex items-center justify-center">
<Check className="w-3.5 h-3.5 text-green-600" />
</div>
<span className="text-xs font-semibold text-gray-700">{t.hero.badge1}</span>
</div>
<div className="absolute -right-10 bottom-20 bg-white rounded-xl px-3 py-2 shadow-lg border border-gray-100
flex items-center gap-2 animate-bounce-subtle hidden lg:flex" style={{ animationDelay: '1s' }}>
<div className="w-6 h-6 bg-purple-100 rounded-full flex items-center justify-center">
<CalendarDays className="w-3.5 h-3.5 text-purple-600" />
</div>
<span className="text-xs font-semibold text-gray-700">{t.hero.badge2}</span>
</div>
</div>
</div>
</div>
</div>
</section>
{/* ── Stats ── */}
<section ref={statsRef} className="py-16 border-t border-b border-gray-100 bg-gray-50/50">
<div className="max-w-5xl mx-auto px-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
<AnimatedCounter end={2500} suffix="+" label={t.stats.chatbots} isInView={statsInView} />
<AnimatedCounter end={500} suffix="+" label={t.stats.companies} isInView={statsInView} />
<AnimatedCounter end={10} suffix="M+" label={t.stats.messages} isInView={statsInView} />
<AnimatedCounter end={99} suffix="%" label={t.stats.uptime} isInView={statsInView} />
</div>
</div>
</section>
{/* ── Features ── */}
<section id="features" ref={featuresRef} className="py-20 md:py-28">
<div className="max-w-6xl mx-auto px-6">
<div className="text-center mb-16">
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${featuresInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}>
{t.features.badge}
</div>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${featuresInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.features.h2}
</h2>
<p className={`text-gray-500 max-w-lg mx-auto transition-all duration-700 delay-100
${featuresInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.features.p}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
{t.features.items.map((f, i) => (
<FeatureCard
key={i}
icon={FEATURE_ICONS[i].icon}
color={FEATURE_ICONS[i].color}
title={f.title}
desc={f.desc}
delay={i * 100}
isInView={featuresInView}
/>
))}
</div>
</div>
</section>
{/* ── How It Works ── */}
<section ref={howItWorksRef} className="py-20 md:py-28 bg-gray-50/70 relative">
<div className="absolute inset-0 bg-dots" />
<div className="relative max-w-5xl mx-auto px-6">
<div className="text-center mb-16">
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${howItWorksInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`}>
{t.howItWorks.badge}
</div>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${howItWorksInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.howItWorks.h2}
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-8">
<div className="hidden md:block absolute top-[13.5rem] left-[22%] right-[22%] h-0.5 bg-gradient-to-r from-primary-200 via-primary-400 to-primary-200 opacity-40" />
{[
{ icon: <Upload className="w-7 h-7 text-white" /> },
{ icon: <Play className="w-7 h-7 text-white" /> },
{ icon: <Cpu className="w-7 h-7 text-white" /> },
].map((s, i) => (
<Step
key={i}
num={i + 1}
icon={s.icon}
title={t.howItWorks.steps[i].title}
desc={t.howItWorks.steps[i].desc}
isInView={howItWorksInView}
delay={i * 200}
/>
))}
</div>
</div>
</section>
{/* ── Widget Embed Highlight ── */}
<section className="py-20 md:py-28 overflow-hidden bg-gray-50/50">
<div className="max-w-6xl mx-auto px-6">
<div className="flex flex-col lg:flex-row items-center gap-12 lg:gap-16">
<div className="flex-1">
<div className="inline-flex items-center gap-2 bg-purple-50 text-purple-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider">
<MessageSquare className="w-3.5 h-3.5" />
{t.widget.badge}
</div>
<h2 className="text-3xl md:text-4xl font-extrabold text-gray-900 mb-5 tracking-tight whitespace-pre-line">
{t.widget.h2}
</h2>
<p className="text-gray-500 mb-8 leading-relaxed">{t.widget.p}</p>
<div className="space-y-3.5">
{t.widget.bullets.map(text => (
<div key={text} className="flex items-center gap-3">
<div className="w-5 h-5 rounded-full bg-green-100 flex items-center justify-center shrink-0">
<Check className="w-3 h-3 text-green-600" />
</div>
<span className="text-sm text-gray-700">{text}</span>
</div>
))}
</div>
</div>
<div className="flex-1 w-full max-w-lg opacity-0 animate-slide-in-left" style={{ animationDelay: '200ms' }}>
<div className="bg-gray-900 rounded-2xl overflow-hidden shadow-2xl shadow-gray-900/20">
<div className="flex items-center gap-2 px-4 py-3 bg-gray-800/80 border-b border-gray-700/50">
<span className="w-3 h-3 rounded-full bg-red-500/80" />
<span className="w-3 h-3 rounded-full bg-yellow-500/80" />
<span className="w-3 h-3 rounded-full bg-green-500/80" />
<span className="text-gray-500 text-xs ml-2 font-mono">index.html</span>
</div>
<pre className="p-6 text-sm font-mono leading-relaxed overflow-x-auto">
<code>
<span className="text-gray-500">{`<!-- Add before </body> -->`}</span>
{'\n'}
<span className="text-blue-400">{`<script`}</span>
{'\n'}
{' '}
<span className="text-green-400">{'src'}</span>
<span className="text-gray-300">{'='}</span>
<span className="text-yellow-300">{`"https://app.contexta.ai/widget.js"`}</span>
{'\n'}
{' '}
<span className="text-green-400">{'data-chatbot'}</span>
<span className="text-gray-300">{'='}</span>
<span className="text-yellow-300">{`"your-chatbot-id"`}</span>
{'\n'}
<span className="text-blue-400">{'>'}</span>
<span className="text-blue-400">{'</script>'}</span>
</code>
</pre>
<div className="px-6 pb-5">
<div className="bg-gray-800/60 rounded-xl px-4 py-3 flex items-center gap-3">
<div className="w-2 h-2 rounded-full bg-green-400 animate-pulse shrink-0" />
<span className="text-xs text-gray-400 font-mono">{t.widget.status}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{/* ── Business Workflow Highlight ── */}
<section className="py-20 md:py-28 overflow-hidden">
<div className="max-w-6xl mx-auto px-6">
<div className="flex flex-col lg:flex-row items-center gap-12 lg:gap-16">
<div className="flex-1 w-full max-w-lg opacity-0 animate-slide-in-left" style={{ animationDelay: '200ms' }}>
<div className="space-y-3">
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 flex items-start gap-3">
<div className="w-9 h-9 bg-sky-100 rounded-xl flex items-center justify-center shrink-0">
<Inbox className="w-4.5 h-4.5 text-sky-600" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between mb-1">
<p className="text-sm font-semibold text-gray-900">{t.workflow.inbox.title}</p>
<span className="text-xs bg-orange-100 text-orange-600 font-medium px-2 py-0.5 rounded-full">{t.workflow.inbox.badge}</span>
</div>
<p className="text-xs text-gray-500 truncate">{t.workflow.inbox.msg}</p>
</div>
</div>
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 flex items-start gap-3">
<div className="w-9 h-9 bg-green-100 rounded-xl flex items-center justify-center shrink-0">
<Users className="w-4.5 h-4.5 text-green-600" />
</div>
<div className="flex-1">
<p className="text-sm font-semibold text-gray-900 mb-2">{t.workflow.leads.title}</p>
<div className="flex gap-2">
{t.workflow.leads.statuses.map((label, i) => (
<div key={label} className={`flex-1 rounded-lg px-2 py-1.5 text-center ${LEAD_COLORS[i]}`}>
<p className="text-xs font-bold">{[12, 7, 4, 9][i]}</p>
<p className="text-xs leading-tight">{label}</p>
</div>
))}
</div>
</div>
</div>
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 flex items-start gap-3">
<div className="w-9 h-9 bg-orange-100 rounded-xl flex items-center justify-center shrink-0">
<CalendarDays className="w-4.5 h-4.5 text-orange-600" />
</div>
<div className="flex-1">
<div className="flex items-center justify-between mb-1">
<p className="text-sm font-semibold text-gray-900">{t.workflow.booking.title}</p>
<span className="text-xs bg-green-100 text-green-600 font-medium px-2 py-0.5 rounded-full">{t.workflow.booking.badge}</span>
</div>
<p className="text-xs text-gray-500">{t.workflow.booking.detail}</p>
</div>
</div>
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm p-4 flex items-start gap-3">
<div className="w-9 h-9 bg-purple-100 rounded-xl flex items-center justify-center shrink-0">
<Megaphone className="w-4.5 h-4.5 text-purple-600" />
</div>
<div className="flex-1">
<div className="flex items-center justify-between mb-1">
<p className="text-sm font-semibold text-gray-900">{t.workflow.campaign.title}</p>
<span className="text-xs bg-primary-100 text-primary-600 font-medium px-2 py-0.5 rounded-full">{t.workflow.campaign.badge}</span>
</div>
<p className="text-xs text-gray-500">{t.workflow.campaign.detail}</p>
</div>
</div>
</div>
</div>
<div className="flex-1">
<div className="inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider">
<Sparkles className="w-3.5 h-3.5" />
{t.workflow.badge}
</div>
<h2 className="text-3xl md:text-4xl font-extrabold text-gray-900 mb-5 tracking-tight">
{t.workflow.h2}
</h2>
<p className="text-gray-500 mb-8 leading-relaxed">{t.workflow.p}</p>
<div className="space-y-4">
{[
{ icon: <Inbox className="w-4 h-4" /> },
{ icon: <Users className="w-4 h-4" /> },
{ icon: <CalendarDays className="w-4 h-4" /> },
{ icon: <Megaphone className="w-4 h-4" /> },
].map(({ icon }, i) => (
<div key={i} className="flex items-center gap-3 group">
<div className="w-8 h-8 bg-primary-50 rounded-lg flex items-center justify-center text-primary-600
group-hover:bg-primary-100 transition-colors flex-shrink-0">
{icon}
</div>
<span className="text-sm text-gray-700 font-medium">{t.workflow.bullets[i]}</span>
</div>
))}
</div>
</div>
</div>
</div>
</section>
{/* ── Testimonials ── */}
<section ref={testimonialsRef} className="py-20 md:py-28 bg-gray-50/70 relative">
<div className="absolute inset-0 bg-grid opacity-30" />
<div className="relative max-w-6xl mx-auto px-6">
<div className="text-center mb-16">
<div className={`inline-flex items-center gap-2 bg-primary-50 text-primary-700 text-xs px-3 py-1 rounded-full
mb-4 font-semibold uppercase tracking-wider transition-all duration-500
${testimonialsInView ? 'opacity-100' : 'opacity-0'}`}>
{t.testimonials.badge}
</div>
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${testimonialsInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.testimonials.h2}
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{t.testimonials.items.map((item, i) => (
<TestimonialCard
key={i}
quote={item.quote}
name={item.name}
role={item.role}
company={item.company}
isInView={testimonialsInView}
delay={i * 150}
/>
))}
</div>
</div>
</section>
{/* ── Pricing Teaser ── */}
<section ref={pricingRef} className="py-20 md:py-28 bg-gray-50/50">
<div className="max-w-5xl mx-auto px-6">
<div className="text-center mb-12">
<h2 className={`text-3xl md:text-4xl font-extrabold text-gray-900 mb-4 tracking-tight transition-all duration-700
${pricingInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.pricing.h2}
</h2>
<p className={`text-gray-500 transition-all duration-700 delay-100
${pricingInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.pricing.p}
</p>
</div>
<div className={`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-5 transition-all duration-700 delay-200
${pricingInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{/* Free */}
<div className="bg-white rounded-2xl border border-gray-200 p-6 flex flex-col">
<div className="mb-5">
<p className="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-1">{t.pricing.plans.free.name}</p>
<div className="flex items-baseline gap-1">
<span className="text-3xl font-extrabold text-gray-900">$0</span>
<span className="text-sm text-gray-400">/mo</span>
</div>
</div>
<ul className="space-y-2.5 flex-1 mb-6">
{t.pricing.plans.free.features.map(f => (
<li key={f} className="flex items-center gap-2 text-sm text-gray-600">
<Check className="w-3.5 h-3.5 text-green-500 shrink-0" />{f}
</li>
))}
</ul>
<Link to="/signup" className="block text-center text-sm font-semibold py-2.5 rounded-xl border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors">
{t.pricing.plans.free.cta}
</Link>
</div>
{/* Starter */}
<div className="bg-white rounded-2xl border border-gray-200 p-6 flex flex-col">
<div className="mb-5">
<p className="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-1">{t.pricing.plans.starter.name}</p>
<div className="flex items-baseline gap-1">
<span className="text-3xl font-extrabold text-gray-900">$19</span>
<span className="text-sm text-gray-400">/mo</span>
</div>
</div>
<ul className="space-y-2.5 flex-1 mb-6">
{t.pricing.plans.starter.features.map(f => (
<li key={f} className="flex items-center gap-2 text-sm text-gray-600">
<Check className="w-3.5 h-3.5 text-green-500 shrink-0" />{f}
</li>
))}
</ul>
<Link to="/signup" className="block text-center text-sm font-semibold py-2.5 rounded-xl border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors">
{t.pricing.plans.starter.cta}
</Link>
</div>
{/* Business */}
<div className="bg-primary-600 rounded-2xl p-6 flex flex-col relative overflow-hidden shadow-xl shadow-primary-500/20">
<div className="absolute top-4 right-4 bg-white/20 text-white text-xs font-bold px-2.5 py-1 rounded-full">{t.pricing.plans.business.popular}</div>
<div className="mb-5">
<p className="text-sm font-semibold text-primary-200 uppercase tracking-wide mb-1">{t.pricing.plans.business.name}</p>
<div className="flex items-baseline gap-1">
<span className="text-3xl font-extrabold text-white">$49</span>
<span className="text-sm text-primary-300">/mo</span>
</div>
</div>
<ul className="space-y-2.5 flex-1 mb-6">
{t.pricing.plans.business.features.map(f => (
<li key={f} className="flex items-center gap-2 text-sm text-white/90">
<Check className="w-3.5 h-3.5 text-primary-200 shrink-0" />{f}
</li>
))}
</ul>
<Link to="/signup" className="block text-center text-sm font-semibold py-2.5 rounded-xl bg-white text-primary-700 hover:bg-primary-50 transition-colors">
{t.pricing.plans.business.cta}
</Link>
</div>
{/* Agency */}
<div className="bg-white rounded-2xl border border-gray-200 p-6 flex flex-col">
<div className="mb-5">
<p className="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-1">{t.pricing.plans.agency.name}</p>
<div className="flex items-baseline gap-1">
<span className="text-3xl font-extrabold text-gray-900">$99</span>
<span className="text-sm text-gray-400">/mo</span>
</div>
</div>
<ul className="space-y-2.5 flex-1 mb-6">
{t.pricing.plans.agency.features.map(f => (
<li key={f} className="flex items-center gap-2 text-sm text-gray-600">
<Check className="w-3.5 h-3.5 text-green-500 shrink-0" />{f}
</li>
))}
</ul>
<Link to="/signup" className="block text-center text-sm font-semibold py-2.5 rounded-xl border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors">
{t.pricing.plans.agency.cta}
</Link>
</div>
</div>
<div className={`text-center mt-8 transition-all duration-700 delay-300 ${pricingInView ? 'opacity-100' : 'opacity-0'}`}>
<Link to="/pricing" className="text-primary-600 font-semibold hover:text-primary-700 text-sm inline-flex items-center gap-1.5 hover:gap-2.5 transition-all">
{t.pricing.seeAll} <ArrowRight className="w-4 h-4" />
</Link>
</div>
</div>
</section>
{/* ── CTA ── */}
<section ref={ctaRef} className="relative py-20 md:py-24 overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-primary-600 via-primary-700 to-indigo-800" />
<div className="absolute inset-0 bg-grid opacity-10" />
<div className="absolute top-10 left-10 w-40 h-40 bg-white/5 rounded-full blur-2xl animate-float" />
<div className="absolute bottom-10 right-10 w-60 h-60 bg-white/5 rounded-full blur-2xl animate-float-delayed" />
<div className="relative max-w-2xl mx-auto px-6 text-center">
<h2 className={`text-3xl md:text-4xl font-extrabold text-white mb-5 tracking-tight transition-all duration-700
${ctaInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.cta.h2}
</h2>
<p className={`text-primary-100 mb-10 text-lg transition-all duration-700 delay-100
${ctaInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
{t.cta.p}
</p>
<div className={`flex flex-col sm:flex-row items-center justify-center gap-4 transition-all duration-700 delay-200
${ctaInView ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-8'}`}>
<Link to="/signup" className="bg-white text-primary-700 px-8 py-3.5 rounded-xl font-semibold
hover:bg-primary-50 transition-all inline-flex items-center gap-2 shadow-lg shadow-black/10
hover:-translate-y-0.5 w-full sm:w-auto justify-center text-base">
{t.cta.cta1} <ArrowRight className="w-4 h-4" />
</Link>
<Link to="/marketplace" className="border border-white/30 text-white px-8 py-3.5 rounded-xl font-semibold
hover:bg-white/10 transition-all w-full sm:w-auto text-center text-base">
{t.cta.cta2}
</Link>
</div>
</div>
</section>
{/* ── Footer ── */}
<footer className="border-t border-gray-100 py-12 bg-white">
<div className="max-w-6xl mx-auto px-6">
<div className="flex flex-col md:flex-row items-center justify-between gap-6">
<div className="flex items-center gap-2.5">
<div className="w-7 h-7 bg-gradient-to-br from-primary-500 to-primary-700 rounded-lg flex items-center justify-center">
<Sparkles className="w-3.5 h-3.5 text-white" />
</div>
<span className="font-bold text-gray-900 tracking-tight">Contexta</span>
</div>
<div className="flex items-center gap-6 text-sm text-gray-500">
<Link to="/marketplace" className="hover:text-gray-700 transition-colors">{t.nav.marketplace}</Link>
<Link to="/pricing" className="hover:text-gray-700 transition-colors">{t.nav.pricing}</Link>
<a href="#features" className="hover:text-gray-700 transition-colors">{t.nav.features}</a>
</div>
<p className="text-sm text-gray-400">{t.footer.copyright}</p>
</div>
</div>
</footer>
</div>
)
}