feat(credits): solde IA unique (option M), packs Stripe et polish billing
Un pot de crédits global (BASIC 100 / PRO 1 000 / BUSINESS 4 000) remplace les seaux par fonction côté débit. Packs one-shot S/M/L, admin sans seaux legacy, usage multi-features, hints de coût, anti-flash thème et hydratation admin. Inclut aussi le pipeline slides renforcé et la page publique polishée.
This commit is contained in:
@@ -1,31 +1,72 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'motion/react'
|
||||
import { motion, AnimatePresence } from 'motion/react'
|
||||
import {
|
||||
BrainCircuit, Search, MessageSquare, Zap, Cpu, Workflow,
|
||||
Globe, Shield, ArrowRight, Sparkles, Layers, Activity,
|
||||
Box
|
||||
ArrowRight, Menu, X, Check, BrainCircuit,
|
||||
Network, GraduationCap, Bot, KeyRound, Globe, ChevronDown
|
||||
} from 'lucide-react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
import { useState } from 'react'
|
||||
import type { SupportedLanguage } from '@/lib/i18n/load-translations'
|
||||
import { useEffect, useRef, useState, type ReactNode } from 'react'
|
||||
|
||||
const ECHO_LINES = ['echo0', 'echo1', 'echo2'] as const
|
||||
|
||||
const LANDING_LANGS: { code: SupportedLanguage; labelKey: string }[] = [
|
||||
{ code: 'fr', labelKey: 'languages.fr' },
|
||||
{ code: 'en', labelKey: 'languages.en' },
|
||||
{ code: 'es', labelKey: 'languages.es' },
|
||||
{ code: 'de', labelKey: 'languages.de' },
|
||||
{ code: 'it', labelKey: 'languages.it' },
|
||||
{ code: 'pt', labelKey: 'languages.pt' },
|
||||
{ code: 'nl', labelKey: 'languages.nl' },
|
||||
{ code: 'pl', labelKey: 'languages.pl' },
|
||||
{ code: 'ru', labelKey: 'languages.ru' },
|
||||
{ code: 'zh', labelKey: 'languages.zh' },
|
||||
{ code: 'ja', labelKey: 'languages.ja' },
|
||||
{ code: 'ko', labelKey: 'languages.ko' },
|
||||
{ code: 'ar', labelKey: 'languages.ar' },
|
||||
{ code: 'fa', labelKey: 'languages.fa' },
|
||||
{ code: 'hi', labelKey: 'languages.hi' },
|
||||
]
|
||||
|
||||
export function LandingPage() {
|
||||
const { t } = useLanguage()
|
||||
const router = useRouter()
|
||||
const { t, language, setLanguage } = useLanguage()
|
||||
const [billingInterval, setBillingInterval] = useState<'monthly' | 'annual'>('monthly')
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const [langOpen, setLangOpen] = useState(false)
|
||||
const [echoIndex, setEchoIndex] = useState(0)
|
||||
const langRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const AGENTS = [
|
||||
{ key: 'scraper', icon: <Globe size={24} /> },
|
||||
{ key: 'researcher', icon: <Search size={24} /> },
|
||||
{ key: 'slideGen', icon: <Layers size={24} /> },
|
||||
{ key: 'monitor', icon: <Activity size={24} /> },
|
||||
{ key: 'diagramGen', icon: <Box size={24} /> },
|
||||
{ key: 'custom', icon: <Workflow size={24} /> },
|
||||
]
|
||||
useEffect(() => {
|
||||
if (!langOpen) return
|
||||
const onPointer = (e: MouseEvent) => {
|
||||
if (langRef.current && !langRef.current.contains(e.target as Node)) setLangOpen(false)
|
||||
}
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') setLangOpen(false)
|
||||
}
|
||||
document.addEventListener('mousedown', onPointer)
|
||||
document.addEventListener('keydown', onKey)
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', onPointer)
|
||||
document.removeEventListener('keydown', onKey)
|
||||
}
|
||||
}, [langOpen])
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setEchoIndex((i) => (i + 1) % ECHO_LINES.length), 3200)
|
||||
return () => clearInterval(id)
|
||||
}, [])
|
||||
|
||||
const BRAINSTORM_ITEMS = ['waveGeneration', 'collaboration', 'export']
|
||||
useEffect(() => {
|
||||
const root = document.querySelector<HTMLElement>('[data-public-scroll-root]')
|
||||
if (!root) return
|
||||
const prev = root.style.overflow
|
||||
if (menuOpen) root.style.overflow = 'hidden'
|
||||
else root.style.overflow = prev || ''
|
||||
return () => { root.style.overflow = prev }
|
||||
}, [menuOpen])
|
||||
|
||||
const PLANS = [
|
||||
{ key: 'basic', popular: false, price: t('landing.pricing.basicPrice'), period: '' },
|
||||
@@ -34,380 +75,556 @@ export function LandingPage() {
|
||||
{ key: 'enterprise', popular: false, price: billingInterval === 'monthly' ? '49,90€' : '39,90€', period: billingInterval === 'monthly' ? t('landing.pricing.perUser') : t('landing.pricing.perUserAnnual') },
|
||||
]
|
||||
|
||||
const TECH_TIERS = [
|
||||
{ key: 'tags', color: 'bg-brand-accent' },
|
||||
{ key: 'embeddings', color: 'bg-ochre' },
|
||||
{ key: 'chatRag', color: 'bg-ink' },
|
||||
const NAV = [
|
||||
{ href: '#product', label: t('landing.nav.secondBrain') },
|
||||
{ href: '#echo', label: t('landing.nav.echo') },
|
||||
{ href: '#agents', label: t('landing.nav.agents') },
|
||||
{ href: '#pricing', label: t('landing.nav.pricing') },
|
||||
]
|
||||
|
||||
const FOOTER_SECTIONS = ['product', 'community', 'legal'] as const
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-paper text-ink font-sans selection:bg-ochre/30 selection:text-ink">
|
||||
{/* Navigation */}
|
||||
<nav className="fixed top-0 left-0 right-0 z-[100] bg-paper/80 backdrop-blur-md border-b border-border px-4 sm:px-8 py-4 flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-ink flex items-center justify-center rounded-xl shadow-lg rotate-3 group hover:rotate-0 transition-transform cursor-pointer">
|
||||
<span className="text-paper font-serif text-2xl font-bold">M</span>
|
||||
<div className="min-h-screen bg-[#0B0A09] text-[#F4F1EA] font-[family-name:var(--font-manrope)] selection:bg-[#D4A373]/40 selection:text-white">
|
||||
{/* Nav */}
|
||||
<nav className="fixed top-0 left-0 right-0 z-[100] px-5 sm:px-8 py-4 flex items-center justify-between bg-[#0B0A09]/70 backdrop-blur-xl border-b border-white/[0.06]">
|
||||
<Link href="/" className="flex items-center gap-2.5 group">
|
||||
<div className="w-9 h-9 bg-[#F4F1EA] text-[#0B0A09] flex items-center justify-center rounded-lg transition-transform group-hover:scale-105">
|
||||
<span className="font-serif text-xl font-bold leading-none">M</span>
|
||||
</div>
|
||||
<span className="font-serif text-2xl font-medium tracking-tight">Memento</span>
|
||||
<span className="font-serif text-xl font-medium tracking-tight text-[#F4F1EA]">Memento</span>
|
||||
</Link>
|
||||
<div className="hidden lg:flex items-center gap-8">
|
||||
{NAV.map((l) => (
|
||||
<a key={l.href} href={l.href} className="text-[13px] text-white/55 hover:text-white transition-colors">
|
||||
{l.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 sm:gap-3">
|
||||
{/* Language switcher */}
|
||||
<div ref={langRef} className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLangOpen((o) => !o)}
|
||||
aria-expanded={langOpen}
|
||||
aria-haspopup="listbox"
|
||||
aria-label={t('landing.nav.language')}
|
||||
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-full border border-white/15 text-[12px] text-white/70 hover:text-white hover:border-white/30 transition-colors"
|
||||
>
|
||||
<Globe size={14} />
|
||||
<span className="uppercase font-semibold tracking-wide">{language}</span>
|
||||
<ChevronDown size={12} className={`opacity-60 transition-transform ${langOpen ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
<AnimatePresence>
|
||||
{langOpen && (
|
||||
<motion.ul
|
||||
role="listbox"
|
||||
initial={{ opacity: 0, y: 6 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 6 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
className="absolute end-0 mt-2 w-48 max-h-72 overflow-y-auto rounded-2xl border border-white/10 bg-[#141210] shadow-2xl py-1.5 z-[110]"
|
||||
>
|
||||
{LANDING_LANGS.map((lang) => (
|
||||
<li key={lang.code} role="option" aria-selected={language === lang.code}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setLanguage(lang.code)
|
||||
setLangOpen(false)
|
||||
}}
|
||||
className={`w-full text-start px-4 py-2.5 text-[13px] transition-colors ${
|
||||
language === lang.code
|
||||
? 'bg-[#D4A373]/15 text-[#D4A373]'
|
||||
: 'text-white/70 hover:bg-white/5 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{t(lang.labelKey)}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</motion.ul>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex items-center gap-10">
|
||||
<a href="#features" className="text-[11px] font-bold uppercase tracking-widest text-concrete hover:text-ink transition-colors">{t('landing.nav.features')}</a>
|
||||
<a href="#agents" className="text-[11px] font-bold uppercase tracking-widest text-concrete hover:text-ink transition-colors">{t('landing.nav.agents')}</a>
|
||||
<a href="#brainstorm" className="text-[11px] font-bold uppercase tracking-widest text-concrete hover:text-ink transition-colors">{t('landing.nav.brainstorm')}</a>
|
||||
<a href="#pricing" className="text-[11px] font-bold uppercase tracking-widest text-concrete hover:text-ink transition-colors">{t('landing.nav.pricing')}</a>
|
||||
<a href="#tech" className="text-[11px] font-bold uppercase tracking-widest text-concrete hover:text-ink transition-colors">{t('landing.nav.tech')}</a>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<button onClick={() => router.push('/login')} className="hidden md:block px-6 py-2.5 text-concrete hover:text-ink text-[11px] font-bold uppercase tracking-widest transition-colors">
|
||||
<Link href="/login" className="hidden sm:inline text-[13px] text-white/55 hover:text-white transition-colors px-2">
|
||||
{t('landing.nav.login')}
|
||||
</button>
|
||||
<button onClick={() => router.push('/register')} className="px-6 py-2.5 bg-ink text-paper rounded-full text-[11px] font-bold uppercase tracking-widest hover:opacity-90 transition-all flex items-center gap-2 group shadow-xl shadow-ink/10">
|
||||
</Link>
|
||||
<Link
|
||||
href="/register"
|
||||
className="hidden sm:inline-flex items-center gap-2 px-5 py-2.5 rounded-full bg-[#F4F1EA] text-[#0B0A09] text-[13px] font-semibold hover:bg-white transition-colors"
|
||||
>
|
||||
{t('landing.nav.cta')}
|
||||
<ArrowRight size={14} className="group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={menuOpen ? t('landing.nav.closeMenu') : t('landing.nav.openMenu')}
|
||||
onClick={() => setMenuOpen((o) => !o)}
|
||||
className="lg:hidden w-10 h-10 rounded-full border border-white/15 flex items-center justify-center text-white/80"
|
||||
>
|
||||
{menuOpen ? <X size={18} /> : <Menu size={18} />}
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Hero */}
|
||||
<section className="relative pt-40 pb-32 px-8 overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-[800px] h-[800px] bg-ochre/5 rounded-full blur-[120px] -translate-y-1/2 translate-x-1/4 -z-10" />
|
||||
<div className="absolute bottom-0 left-0 w-[600px] h-[600px] bg-brand-accent/5 rounded-full blur-[100px] translate-y-1/2 -translate-x-1/4 -z-10" />
|
||||
<AnimatePresence>
|
||||
{menuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-[99] bg-[#0B0A09] pt-24 px-8 lg:hidden"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
{NAV.map((l) => (
|
||||
<a
|
||||
key={l.href}
|
||||
href={l.href}
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="py-4 text-3xl font-serif border-b border-white/10"
|
||||
>
|
||||
{l.label}
|
||||
</a>
|
||||
))}
|
||||
<Link href="/register" onClick={() => setMenuOpen(false)} className="mt-10 py-4 rounded-2xl bg-[#F4F1EA] text-[#0B0A09] text-center font-semibold">
|
||||
{t('landing.nav.cta')}
|
||||
</Link>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<div className="max-w-6xl mx-auto text-center">
|
||||
<motion.div initial={{ opacity: 0, y: 30 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8, ease: [0.23, 1, 0.32, 1] }}>
|
||||
<div className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-ochre/10 border border-ochre/20 text-ochre text-[10px] font-bold uppercase tracking-[0.2em] mb-8">
|
||||
<Sparkles size={12} />
|
||||
{/* ── HERO ── */}
|
||||
<section className="relative min-h-[100dvh] flex flex-col justify-center pt-28 pb-16 px-5 sm:px-8 overflow-hidden">
|
||||
{/* Atmosphere — warm, not purple neon */}
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<div className="absolute -top-32 left-1/2 -translate-x-1/2 w-[900px] h-[500px] bg-[#D4A373]/15 blur-[120px] rounded-full" />
|
||||
<div className="absolute bottom-0 right-0 w-[500px] h-[400px] bg-[#A47148]/10 blur-[100px] rounded-full" />
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.07]"
|
||||
style={{
|
||||
backgroundImage: 'radial-gradient(circle at 1px 1px, #F4F1EA 1px, transparent 0)',
|
||||
backgroundSize: '28px 28px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 max-w-5xl mx-auto w-full text-center">
|
||||
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 rounded-full border border-[#D4A373]/35 bg-[#D4A373]/10 mb-7">
|
||||
<BrainCircuit size={14} className="text-[#D4A373]" />
|
||||
<span className="text-[12px] tracking-[0.18em] uppercase text-[#D4A373] font-semibold">
|
||||
{t('landing.hero.badge')}
|
||||
</div>
|
||||
<h1 className="text-6xl md:text-8xl font-serif font-medium tracking-tight text-ink mb-8 leading-[1.1]">
|
||||
{t('landing.hero.title1')} <br />
|
||||
<span className="italic">{t('landing.hero.title2')}</span>
|
||||
</h1>
|
||||
<p className="max-w-2xl mx-auto text-lg md:text-xl text-concrete font-light leading-relaxed mb-12">
|
||||
{t('landing.hero.subtitle')}
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<button onClick={() => router.push('/register')} className="px-10 py-5 bg-ink text-paper rounded-2xl text-sm font-bold uppercase tracking-[0.2em] hover:opacity-95 transition-all shadow-2xl shadow-ink/20 flex items-center gap-4 group">
|
||||
{t('landing.hero.cta')}
|
||||
<ArrowRight size={18} className="group-hover:translate-x-1 transition-transform" />
|
||||
</button>
|
||||
<a href="#features" className="px-10 py-5 border border-border rounded-2xl text-sm font-bold uppercase tracking-[0.2em] hover:bg-slate-50 transition-all">
|
||||
{t('landing.hero.secondary')}
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
<p className="text-[13px] tracking-[0.12em] uppercase text-white/40 mb-5 font-medium">
|
||||
{t('landing.hero.eyebrow')}
|
||||
</p>
|
||||
|
||||
{/* App Preview Mockup */}
|
||||
<motion.div initial={{ opacity: 0, y: 100 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1, delay: 0.2, ease: [0.23, 1, 0.32, 1] }} className="mt-24 relative">
|
||||
<div className="relative mx-auto max-w-5xl aspect-[16/10] bg-white rounded-[32px] shadow-[0_40px_100px_-20px_rgba(0,0,0,0.15)] border border-border p-4 overflow-hidden group">
|
||||
<Image src="/images/workspace-hero.jpg" alt="Memento Workspace" width={1200} height={750} className="w-full h-full object-cover rounded-2xl filter saturate-[0.8]" priority />
|
||||
<div className="absolute inset-0 bg-ink/10 group-hover:bg-ink/0 transition-colors duration-500" />
|
||||
<h1 className="font-serif text-[clamp(2.5rem,7.5vw,5.4rem)] font-medium tracking-tight leading-[1.05] text-[#F4F1EA] mb-6">
|
||||
{t('landing.hero.headline')}
|
||||
<br />
|
||||
<span className="italic text-[#D4A373]">{t('landing.hero.headlineAccent')}</span>
|
||||
</h1>
|
||||
|
||||
<div className="absolute top-10 right-10 w-64 bg-paper/90 backdrop-blur-xl border border-border p-6 rounded-2xl shadow-2xl">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-8 h-8 rounded-full bg-brand-accent/20 flex items-center justify-center text-brand-accent">
|
||||
<BrainCircuit size={16} />
|
||||
</div>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest">{t('landing.hero.memoryEcho')}</span>
|
||||
</div>
|
||||
<p className="text-xs font-serif italic text-ink/70">{t('landing.hero.memoryEchoText')}</p>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-10 left-10 w-72 bg-ink text-paper p-6 rounded-2xl shadow-2xl">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<Activity size={16} className="text-ochre" />
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-ochre">{t('landing.hero.brainstormLive')}</span>
|
||||
</div>
|
||||
<div className="flex items-center -space-x-2">
|
||||
{[1, 2, 3].map(i => (
|
||||
<div key={i} className="w-6 h-6 rounded-full border-2 border-ink bg-concrete text-[8px] flex items-center justify-center font-bold">JD</div>
|
||||
))}
|
||||
<span className="text-[10px] ml-4 text-paper/60">{t('landing.hero.ideasGenerated')}</span>
|
||||
<p className="max-w-xl mx-auto text-[17px] sm:text-lg text-white/55 leading-relaxed mb-10">
|
||||
{t('landing.hero.subtitle')}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<Link
|
||||
href="/register"
|
||||
className="group inline-flex items-center justify-center gap-3 px-9 py-4 rounded-full bg-[#F4F1EA] text-[#0B0A09] text-[15px] font-semibold hover:bg-white transition-all hover:scale-[1.02] shadow-[0_0_40px_-8px_rgba(212,163,115,0.45)]"
|
||||
>
|
||||
{t('landing.hero.cta')}
|
||||
<ArrowRight size={18} className="transition-transform group-hover:translate-x-0.5" />
|
||||
</Link>
|
||||
<p className="text-[12px] text-white/35">{t('landing.hero.ctaHint')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Product stage */}
|
||||
<div className="relative z-10 mt-14 sm:mt-20 max-w-5xl mx-auto w-full">
|
||||
<div className="relative rounded-[20px] sm:rounded-[28px] overflow-hidden border border-white/10 shadow-[0_40px_100px_-20px_rgba(0,0,0,0.7)]">
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-[#0B0A09] via-transparent to-transparent z-10 pointer-events-none" />
|
||||
<Image
|
||||
src="/images/workspace-hero.jpg"
|
||||
alt={t('landing.hero.imageAlt')}
|
||||
width={2000}
|
||||
height={1200}
|
||||
priority
|
||||
className="w-full h-auto object-cover aspect-[16/10] opacity-90"
|
||||
/>
|
||||
{/* Live Memory Echo chip */}
|
||||
<div className="absolute bottom-5 left-5 right-5 sm:bottom-8 sm:left-8 sm:right-auto sm:max-w-md z-20">
|
||||
<div className="rounded-2xl border border-white/15 bg-[#0B0A09]/85 backdrop-blur-xl p-4 sm:p-5 text-left shadow-2xl">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#D4A373] opacity-60" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-[#D4A373]" />
|
||||
</span>
|
||||
<span className="text-[11px] font-semibold uppercase tracking-[0.2em] text-[#D4A373]">
|
||||
{t('landing.hero.echoLive')}
|
||||
</span>
|
||||
</div>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.p
|
||||
key={ECHO_LINES[echoIndex]}
|
||||
initial={{ opacity: 0, y: 8 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -8 }}
|
||||
transition={{ duration: 0.35 }}
|
||||
className="text-sm sm:text-[15px] font-serif italic text-[#F4F1EA]/90 leading-relaxed"
|
||||
>
|
||||
{t(`landing.hero.${ECHO_LINES[echoIndex]}`)}
|
||||
</motion.p>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features */}
|
||||
<section id="features" className="py-32 px-8 bg-paper">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="mb-24 flex flex-col md:flex-row md:items-end justify-between gap-8">
|
||||
<div className="max-w-2xl">
|
||||
<span className="text-[11px] font-bold uppercase tracking-[0.3em] text-ochre mb-4 block">{t('landing.features.label')}</span>
|
||||
<h2 className="text-4xl md:text-5xl font-serif tracking-tight text-ink">{t('landing.features.title')} <br />{t('landing.features.title2')}</h2>
|
||||
</div>
|
||||
<div className="text-concrete font-light">{t('landing.features.desc')}</div>
|
||||
</div>
|
||||
{/* Trust strip */}
|
||||
<section className="px-5 sm:px-8 py-10 border-y border-white/[0.06]">
|
||||
<div className="max-w-5xl mx-auto flex flex-wrap items-center justify-center gap-x-10 gap-y-4 text-[12px] sm:text-[13px] text-white/40 tracking-wide">
|
||||
{['trust0', 'trust1', 'trust2', 'trust3'].map((k) => (
|
||||
<span key={k} className="flex items-center gap-2">
|
||||
<Check size={14} className="text-[#D4A373]" />
|
||||
{t(`landing.trust.${k}`)}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
|
||||
{['f1', 'f2', 'f3'].map((f, i) => {
|
||||
const icons = [
|
||||
<Search key="s" className="text-brand-accent" />,
|
||||
<MessageSquare key="m" className="text-ochre" />,
|
||||
<Zap key="z" className="text-ink" />
|
||||
]
|
||||
return (
|
||||
<div key={f} className="group">
|
||||
<div className="w-14 h-14 bg-slate-50 border border-border rounded-2xl flex items-center justify-center mb-8 group-hover:bg-ink group-hover:text-paper transition-all duration-300">
|
||||
{icons[i]}
|
||||
</div>
|
||||
<h3 className="text-xl font-serif font-medium mb-4">{t(`landing.features.${f}Title`)}</h3>
|
||||
<p className="text-sm text-concrete leading-relaxed font-light">{t(`landing.features.${f}Desc`)}</p>
|
||||
{/* Pain → Promise */}
|
||||
<section className="px-5 sm:px-8 py-24 sm:py-32">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<p className="text-[12px] uppercase tracking-[0.25em] text-[#D4A373] mb-5 font-medium">{t('landing.pain.label')}</p>
|
||||
<h2 className="font-serif text-3xl sm:text-5xl tracking-tight leading-[1.15] mb-6">
|
||||
{t('landing.pain.title')}
|
||||
</h2>
|
||||
<p className="text-lg text-white/50 leading-relaxed mb-8">{t('landing.pain.desc')}</p>
|
||||
<p className="text-base sm:text-lg font-serif italic text-[#D4A373]/90 leading-relaxed">
|
||||
{t('landing.pain.secondBrain')}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Product moments — large narrative blocks, not wireframe cards */}
|
||||
<section id="product" className="px-5 sm:px-8 pb-8">
|
||||
<div className="max-w-6xl mx-auto space-y-4">
|
||||
<ProductMoment
|
||||
id="echo"
|
||||
eyebrow={t('landing.moments.echo.eyebrow')}
|
||||
title={t('landing.moments.echo.title')}
|
||||
desc={t('landing.moments.echo.desc')}
|
||||
dark
|
||||
>
|
||||
<div className="space-y-3 p-2">
|
||||
{[0, 1, 2].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`rounded-xl border p-4 ${i === 0 ? 'border-[#D4A373]/40 bg-[#D4A373]/10' : 'border-white/10 bg-white/[0.03]'}`}
|
||||
>
|
||||
<p className="text-[11px] uppercase tracking-wider text-[#D4A373] mb-1">
|
||||
{t(`landing.moments.echo.card${i}Label`)}
|
||||
</p>
|
||||
<p className="text-sm text-white/75 font-serif italic leading-relaxed">
|
||||
{t(`landing.moments.echo.card${i}`)}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
))}
|
||||
</div>
|
||||
</ProductMoment>
|
||||
|
||||
<ProductMoment
|
||||
eyebrow={t('landing.moments.dashboard.eyebrow')}
|
||||
title={t('landing.moments.dashboard.title')}
|
||||
desc={t('landing.moments.dashboard.desc')}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-2 p-2">
|
||||
{['w0', 'w1', 'w2', 'w3'].map((w) => (
|
||||
<div key={w} className="rounded-xl bg-[#0B0A09]/40 border border-black/10 p-4 min-h-[88px]">
|
||||
<p className="text-[10px] uppercase tracking-wider text-[#A47148] mb-2">{t(`landing.moments.dashboard.${w}Label`)}</p>
|
||||
<p className="text-sm font-medium text-[#0B0A09]/80">{t(`landing.moments.dashboard.${w}`)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ProductMoment>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<ProductMoment
|
||||
compact
|
||||
eyebrow={t('landing.moments.insights.eyebrow')}
|
||||
title={t('landing.moments.insights.title')}
|
||||
desc={t('landing.moments.insights.desc')}
|
||||
dark
|
||||
>
|
||||
<div className="relative h-36 flex items-center justify-center">
|
||||
<Network className="text-[#D4A373]/80" size={48} strokeWidth={1.25} />
|
||||
<p className="absolute bottom-2 left-4 text-[11px] uppercase tracking-wider text-white/35">
|
||||
{t('landing.moments.insights.chip')}
|
||||
</p>
|
||||
</div>
|
||||
</ProductMoment>
|
||||
<ProductMoment
|
||||
compact
|
||||
eyebrow={t('landing.moments.revision.eyebrow')}
|
||||
title={t('landing.moments.revision.title')}
|
||||
desc={t('landing.moments.revision.desc')}
|
||||
>
|
||||
<div className="rounded-xl bg-white border border-black/5 p-5 mx-2 mb-2">
|
||||
<GraduationCap className="text-[#A47148] mb-3" size={22} />
|
||||
<p className="font-serif text-[#0B0A09] text-base mb-3">{t('landing.moments.revision.card')}</p>
|
||||
<div className="flex gap-2">
|
||||
<span className="flex-1 py-2 rounded-lg bg-[#0B0A09]/5 text-center text-[11px] font-semibold text-[#0B0A09]/50">?</span>
|
||||
<span className="flex-1 py-2 rounded-lg bg-[#0B0A09] text-center text-[11px] font-semibold text-[#F4F1EA]">SM-2</span>
|
||||
</div>
|
||||
</div>
|
||||
</ProductMoment>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How it works */}
|
||||
<section className="px-5 sm:px-8 py-28">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="font-serif text-3xl sm:text-4xl tracking-tight text-center mb-16">
|
||||
{t('landing.how.title')}
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-6">
|
||||
{['s0', 's1', 's2'].map((s, i) => (
|
||||
<div key={s} className="relative text-center md:text-left">
|
||||
<div className="text-[13px] font-semibold text-[#D4A373] mb-4 tracking-widest">0{i + 1}</div>
|
||||
<h3 className="font-serif text-xl mb-3">{t(`landing.how.${s}.title`)}</h3>
|
||||
<p className="text-sm text-white/45 leading-relaxed">{t(`landing.how.${s}.desc`)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Agents */}
|
||||
<section id="agents" className="py-32 px-8 bg-ink text-paper overflow-hidden relative">
|
||||
<div className="absolute top-0 right-0 w-[1000px] h-[1000px] bg-brand-accent/10 rounded-full blur-[150px] -translate-y-1/2 translate-x-1/2" />
|
||||
<div className="max-w-6xl mx-auto relative z-10">
|
||||
<div className="text-center mb-24">
|
||||
<span className="text-[11px] font-bold uppercase tracking-[0.3em] text-ochre mb-4 block">{t('landing.agents.label')}</span>
|
||||
<h2 className="text-4xl md:text-6xl font-serif tracking-tight mb-8">{t('landing.agents.title')}</h2>
|
||||
<p className="text-paper/60 max-w-xl mx-auto font-light">{t('landing.agents.desc')}</p>
|
||||
<section id="agents" className="px-5 sm:px-8 py-28 border-t border-white/[0.06]">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="max-w-2xl mb-14">
|
||||
<p className="text-[12px] uppercase tracking-[0.25em] text-[#D4A373] mb-4 font-medium">{t('landing.agents.label')}</p>
|
||||
<h2 className="font-serif text-3xl sm:text-5xl tracking-tight mb-5">{t('landing.agents.title')}</h2>
|
||||
<p className="text-white/50 text-lg leading-relaxed">{t('landing.agents.desc')}</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{AGENTS.map((agent, i) => (
|
||||
<div key={i} className="p-8 rounded-3xl bg-white/5 border border-white/10 hover:bg-white/10 transition-all cursor-default group">
|
||||
<div className="text-ochre mb-6 transition-transform group-hover:scale-110 duration-300">{agent.icon}</div>
|
||||
<h4 className="text-xl font-serif font-medium mb-4">{t(`landing.agents.${agent.key}.title`)}</h4>
|
||||
<p className="text-sm text-paper/50 leading-relaxed font-light">{t(`landing.agents.${agent.key}.desc`)}</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{(['scraper', 'researcher', 'slideGen', 'monitor', 'diagramGen', 'custom'] as const).map((key) => (
|
||||
<div key={key} className="p-6 rounded-2xl border border-white/[0.08] bg-white/[0.03] hover:bg-white/[0.06] transition-colors">
|
||||
<Bot size={18} className="text-[#D4A373] mb-4" />
|
||||
<h4 className="font-serif text-lg mb-2">{t(`landing.agents.${key}.title`)}</h4>
|
||||
<p className="text-sm text-white/40 leading-relaxed">{t(`landing.agents.${key}.desc`)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Brainstorm */}
|
||||
<section id="brainstorm" className="py-32 px-8 bg-paper">
|
||||
<div className="max-w-6xl mx-auto flex flex-col md:flex-row items-center gap-24">
|
||||
{/* BYOK */}
|
||||
<section className="px-5 sm:px-8 py-20">
|
||||
<div className="max-w-5xl mx-auto rounded-[28px] border border-white/10 bg-gradient-to-br from-[#1a1612] to-[#0B0A09] p-8 sm:p-12 flex flex-col md:flex-row gap-10 items-start">
|
||||
<div className="flex-1">
|
||||
<span className="text-[11px] font-bold uppercase tracking-[0.3em] text-ochre mb-4 block">{t('landing.brainstorm.label')}</span>
|
||||
<h2 className="text-4xl md:text-5xl font-serif tracking-tight text-ink mb-8 leading-tight">{t('landing.brainstorm.title')}</h2>
|
||||
<div className="space-y-8">
|
||||
{BRAINSTORM_ITEMS.map((item, i) => (
|
||||
<div key={item} className="flex gap-6">
|
||||
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-ochre/10 text-ochre flex items-center justify-center font-bold text-xs">{i + 1}</div>
|
||||
<div>
|
||||
<h5 className="font-bold text-sm mb-1">{t(`landing.brainstorm.${item}.title`)}</h5>
|
||||
<p className="text-sm text-concrete font-light leading-relaxed">{t(`landing.brainstorm.${item}.desc`)}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex items-center gap-2 text-[#D4A373] mb-4">
|
||||
<KeyRound size={18} />
|
||||
<span className="text-[12px] uppercase tracking-[0.2em] font-semibold">{t('landing.byok.label')}</span>
|
||||
</div>
|
||||
<h3 className="font-serif text-3xl tracking-tight mb-4">{t('landing.byok.title')}</h3>
|
||||
<p className="text-white/50 leading-relaxed">{t('landing.byok.desc')}</p>
|
||||
</div>
|
||||
<div className="flex-1 relative">
|
||||
<div className="w-[450px] h-[450px] border-2 border-dashed border-border rounded-full flex items-center justify-center relative">
|
||||
<div className="absolute top-0 right-1/2 translate-x-1/2 -translate-y-1/2 w-4 h-4 bg-ink rounded-full" />
|
||||
<div className="absolute bottom-0 right-1/2 translate-x-1/2 translate-y-1/2 w-4 h-4 bg-ochre rounded-full" />
|
||||
<div className="w-[300px] h-[300px] border-2 border-dashed border-border rounded-full flex items-center justify-center">
|
||||
<div className="w-[150px] h-[150px] border-2 border-dashed border-border rounded-full flex items-center justify-center">
|
||||
<div className="w-12 h-12 bg-ink rounded-xl shadow-2xl flex items-center justify-center text-paper font-serif text-xl">M</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-10 right-0 p-4 bg-white border border-border rounded-xl shadow-xl">
|
||||
<p className="text-[10px] font-bold text-ochre">{t('landing.brainstorm.disruptionLabel')}</p>
|
||||
<p className="text-xs font-serif italic text-ink">{t('landing.brainstorm.disruptionText')}</p>
|
||||
</div>
|
||||
<div className="absolute bottom-20 -left-10 p-4 bg-white border border-border rounded-xl shadow-xl">
|
||||
<p className="text-[10px] font-bold text-brand-accent">{t('landing.brainstorm.analogyLabel')}</p>
|
||||
<p className="text-xs font-serif italic text-ink">{t('landing.brainstorm.analogyText')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech */}
|
||||
<section id="tech" className="py-32 px-8 bg-slate-50 border-y border-border">
|
||||
<div className="max-w-6xl mx-auto text-center">
|
||||
<span className="text-[11px] font-bold uppercase tracking-[0.3em] text-ochre mb-4 block">{t('landing.tech.label')}</span>
|
||||
<h2 className="text-4xl font-serif tracking-tight mb-16">{t('landing.tech.title')}</h2>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-8 grayscale opacity-50 hover:grayscale-0 hover:opacity-100 transition-all duration-700">
|
||||
{['OpenAI', 'Google', 'Anthropic', 'DeepSeek', 'Mistral', 'Meta', 'Ollama', 'Groq', 'X.AI', 'Custom'].map((brand, i) => (
|
||||
<div key={i} className="flex flex-col items-center gap-3">
|
||||
<div className="w-12 h-12 bg-white rounded-xl border border-border flex items-center justify-center text-xs font-black tracking-tighter">
|
||||
{brand.slice(0, 2).toUpperCase()}
|
||||
</div>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest">{brand}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-24 max-w-2xl mx-auto p-1 bg-white rounded-3xl border border-border shadow-sm flex flex-col md:flex-row gap-0.5">
|
||||
{TECH_TIERS.map((tier, i) => (
|
||||
<div key={i} className="flex-1 p-6 text-left">
|
||||
<div className={`w-1.5 h-1.5 rounded-full ${tier.color} mb-4`} />
|
||||
<h6 className="text-[10px] font-bold uppercase tracking-widest text-concrete mb-2">{t(`landing.tech.${tier.key}.title`)}</h6>
|
||||
<p className="text-xs font-light text-concrete">{t(`landing.tech.${tier.key}.desc`)}</p>
|
||||
</div>
|
||||
))}
|
||||
<div className="w-full md:w-[320px] font-mono text-[11px] text-white/35 space-y-1.5 rounded-2xl border border-white/10 bg-black/40 p-5">
|
||||
<p className="text-[#D4A373]">{'{'}</p>
|
||||
<p className="pl-3">"provider": "anthropic",</p>
|
||||
<p className="pl-3">"model": "claude-sonnet",</p>
|
||||
<p className="pl-3 text-[#F4F1EA]/70">"apiKey": "sk-ant-…",</p>
|
||||
<p className="pl-3">"yours": true</p>
|
||||
<p className="text-[#D4A373]">{'}'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Pricing */}
|
||||
<section id="pricing" className="py-32 px-8 bg-paper">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<section id="pricing" className="px-5 sm:px-8 py-28 border-t border-white/[0.06]">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="text-center mb-12">
|
||||
<span className="text-[11px] font-bold uppercase tracking-[0.3em] text-ochre mb-4 block">{t('landing.pricing.label')}</span>
|
||||
<h2 className="text-4xl md:text-5xl font-serif tracking-tight text-ink mb-6">{t('landing.pricing.title')}</h2>
|
||||
<p className="text-concrete font-light max-w-xl mx-auto mb-12">{t('landing.pricing.desc')}</p>
|
||||
|
||||
<div className="flex items-center justify-center gap-10 mb-8">
|
||||
<button onClick={() => setBillingInterval('monthly')} className={`group relative py-2 px-1 transition-all ${billingInterval === 'monthly' ? 'text-ink' : 'text-concrete/40 hover:text-concrete'}`}>
|
||||
<span className="text-xs font-black uppercase tracking-[0.2em]">{t('landing.pricing.monthly')}</span>
|
||||
{billingInterval === 'monthly' && (
|
||||
<motion.div layoutId="interval-active" className="absolute -inset-x-1 -inset-y-0.5 border border-ochre/60" transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }} />
|
||||
)}
|
||||
<h2 className="font-serif text-3xl sm:text-5xl tracking-tight mb-4">{t('landing.pricing.title')}</h2>
|
||||
<p className="text-white/45 mb-8">{t('landing.pricing.desc')}</p>
|
||||
<div className="inline-flex p-1 rounded-full border border-white/10 bg-white/[0.03]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setBillingInterval('monthly')}
|
||||
className={`px-5 py-2 rounded-full text-[12px] font-semibold transition-all ${billingInterval === 'monthly' ? 'bg-[#F4F1EA] text-[#0B0A09]' : 'text-white/45'}`}
|
||||
>
|
||||
{t('landing.pricing.monthly')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setBillingInterval('annual')}
|
||||
className={`px-5 py-2 rounded-full text-[12px] font-semibold transition-all relative ${billingInterval === 'annual' ? 'bg-[#F4F1EA] text-[#0B0A09]' : 'text-white/45'}`}
|
||||
>
|
||||
{t('landing.pricing.annual')}
|
||||
<span className="absolute -top-3 -right-1 text-[10px] text-[#D4A373]">-20%</span>
|
||||
</button>
|
||||
<div className="relative">
|
||||
<button onClick={() => setBillingInterval('annual')} className={`group relative py-2 px-1 transition-all ${billingInterval === 'annual' ? 'text-ink' : 'text-concrete/40 hover:text-concrete'}`}>
|
||||
<span className="text-xs font-black uppercase tracking-[0.2em]">{t('landing.pricing.annual')}</span>
|
||||
{billingInterval === 'annual' && (
|
||||
<motion.div layoutId="interval-active" className="absolute -inset-x-1 -inset-y-0.5 border border-ochre/60" transition={{ type: 'spring', bounce: 0.2, duration: 0.6 }} />
|
||||
)}
|
||||
</button>
|
||||
<div className="absolute -top-6 left-1/2 -translate-x-1/2 whitespace-nowrap">
|
||||
<span className="text-[9px] font-bold text-ochre uppercase tracking-widest italic animate-pulse">(-20%)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 items-stretch">
|
||||
{PLANS.map((plan, i) => (
|
||||
<div key={plan.key} className={`relative p-8 rounded-[32px] border flex flex-col transition-all duration-300 hover:shadow-2xl hover:shadow-ink/5 ${plan.popular ? 'bg-ink text-paper border-ink ring-4 ring-ochre/20' : 'bg-white border-border text-ink'}`}>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-3">
|
||||
{PLANS.map((plan) => (
|
||||
<div
|
||||
key={plan.key}
|
||||
className={`rounded-2xl border p-6 flex flex-col ${
|
||||
plan.popular
|
||||
? 'border-[#D4A373]/50 bg-[#D4A373]/10'
|
||||
: 'border-white/[0.08] bg-white/[0.02]'
|
||||
}`}
|
||||
>
|
||||
{plan.popular && (
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2 px-4 py-1 bg-ochre text-ink text-[10px] font-bold uppercase tracking-widest rounded-full">
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-[#D4A373] mb-3">
|
||||
{t('landing.pricing.popular')}
|
||||
</div>
|
||||
</span>
|
||||
)}
|
||||
<div className="mb-8">
|
||||
<h4 className="text-[11px] font-bold uppercase tracking-widest mb-2 opacity-60">{t(`landing.pricing.${plan.key}.name`)}</h4>
|
||||
<div className="flex items-baseline gap-1 mb-4">
|
||||
<span className="text-4xl font-serif font-medium">{plan.price}</span>
|
||||
{plan.period && <span className="text-xs opacity-60">{plan.period}</span>}
|
||||
</div>
|
||||
<p className="text-sm font-light leading-relaxed opacity-80">{t(`landing.pricing.${plan.key}.desc`)}</p>
|
||||
<h4 className="text-[12px] uppercase tracking-widest text-white/40 mb-2">
|
||||
{t(`landing.pricing.${plan.key}.name`)}
|
||||
</h4>
|
||||
<div className="flex items-baseline gap-1 mb-2">
|
||||
<span className="text-3xl font-serif">{plan.price}</span>
|
||||
{plan.period && <span className="text-xs text-white/35">{plan.period}</span>}
|
||||
</div>
|
||||
<div className="flex-1 space-y-4 mb-10">
|
||||
{[0, 1, 2, 3, 4, 5].map(j => {
|
||||
<p className="text-sm text-white/45 mb-6">{t(`landing.pricing.${plan.key}.desc`)}</p>
|
||||
<ul className="space-y-2.5 mb-8 flex-1">
|
||||
{[0, 1, 2, 3, 4, 5].map((j) => {
|
||||
const feat = t(`landing.pricing.${plan.key}.feature${j}`)
|
||||
if (!feat || feat === `landing.pricing.${plan.key}.feature${j}`) return null
|
||||
if (!feat || feat.startsWith('landing.')) return null
|
||||
return (
|
||||
<div key={j} className="flex items-start gap-3">
|
||||
<div className={`mt-1 rounded-full p-0.5 ${plan.popular ? 'bg-ochre text-ink' : 'bg-brand-accent/10 text-brand-accent'}`}>
|
||||
<Shield size={10} fill="currentColor" />
|
||||
</div>
|
||||
<span className="text-xs font-light">{feat}</span>
|
||||
</div>
|
||||
<li key={j} className="flex gap-2 text-xs text-white/60">
|
||||
<Check size={12} className="text-[#D4A373] mt-0.5 shrink-0" />
|
||||
{feat}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<button onClick={() => router.push('/register')} className={`w-full py-4 rounded-2xl text-xs font-bold uppercase tracking-widest transition-all ${plan.popular ? 'bg-ochre text-ink hover:opacity-90' : 'bg-ink text-paper hover:bg-ink/90'}`}>
|
||||
</ul>
|
||||
<Link
|
||||
href="/register"
|
||||
className={`py-3 rounded-xl text-center text-[12px] font-semibold transition-colors ${
|
||||
plan.popular
|
||||
? 'bg-[#F4F1EA] text-[#0B0A09] hover:bg-white'
|
||||
: 'bg-white/10 text-white hover:bg-white/15'
|
||||
}`}
|
||||
>
|
||||
{t(`landing.pricing.${plan.key}.cta`)}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* BYOK */}
|
||||
<div className="mt-20 p-12 bg-slate-50 border border-border rounded-[40px] flex flex-col md:flex-row items-center gap-12">
|
||||
<div className="flex-1">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-brand-accent/10 text-brand-accent text-[9px] font-bold uppercase tracking-widest mb-6">
|
||||
<Cpu size={12} />
|
||||
{t('landing.byok.label')}
|
||||
</div>
|
||||
<h3 className="text-3xl font-serif font-medium mb-4">{t('landing.byok.title')}</h3>
|
||||
<p className="text-concrete font-light leading-relaxed mb-6">{t('landing.byok.desc')}</p>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="p-4 bg-white rounded-2xl border border-border">
|
||||
<h5 className="text-[10px] font-bold uppercase tracking-widest mb-2">{t('landing.byok.noLockin')}</h5>
|
||||
<p className="text-[10px] text-concrete font-light">{t('landing.byok.noLockinDesc')}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-white rounded-2xl border border-border">
|
||||
<h5 className="text-[10px] font-bold uppercase tracking-widest mb-2">{t('landing.byok.cost')}</h5>
|
||||
<p className="text-[10px] text-concrete font-light">{t('landing.byok.costDesc')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full md:w-[400px] bg-ink rounded-3xl p-8 relative overflow-hidden group">
|
||||
<div className="absolute inset-0 bg-brand-accent/10 blur-[50px] group-hover:bg-ochre/10 transition-colors" />
|
||||
<div className="relative z-10 font-mono text-[10px] text-paper/40 space-y-2">
|
||||
<p className="text-ochre">{"{"}</p>
|
||||
<p className="pl-4">"provider": "anthropic",</p>
|
||||
<p className="pl-4">"model": "claude-3-opus",</p>
|
||||
<p className="pl-4 border-l border-brand-accent/30 bg-brand-accent/5">"apiKey": "sk-ant-at03-..."</p>
|
||||
<p className="pl-4">"useSystemKey": false</p>
|
||||
<p className="text-ochre">{"}"}</p>
|
||||
</div>
|
||||
<div className="mt-8 flex items-center justify-between relative z-10">
|
||||
<span className="text-[10px] font-bold text-paper uppercase tracking-widest">{t('landing.byok.configLabel')}</span>
|
||||
<div className="flex gap-1">{[1, 2, 3].map(i => <div key={i} className="w-1 h-1 rounded-full bg-paper/20" />)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Final CTA */}
|
||||
<section className="py-40 px-8 text-center bg-paper relative overflow-hidden">
|
||||
<div className="max-w-3xl mx-auto relative z-10">
|
||||
<h2 className="text-5xl md:text-7xl font-serif tracking-tight mb-8 leading-tight">{t('landing.cta.title1')} <br /><span className="italic">{t('landing.cta.title2')}</span></h2>
|
||||
<p className="text-lg text-concrete font-light mb-12">{t('landing.cta.desc')}</p>
|
||||
<button onClick={() => router.push('/register')} className="px-16 py-6 bg-ink text-paper rounded-[32px] text-lg font-bold uppercase tracking-[0.2em] hover:scale-105 transition-all shadow-[0_30px_60px_-15px_rgba(0,0,0,0.3)]">
|
||||
<section className="relative px-5 sm:px-8 py-36 text-center overflow-hidden">
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[300px] bg-[#D4A373]/20 blur-[100px] rounded-full" />
|
||||
</div>
|
||||
<div className="relative z-10 max-w-2xl mx-auto">
|
||||
<BrainCircuit className="mx-auto text-[#D4A373] mb-6" size={32} strokeWidth={1.25} />
|
||||
<h2 className="font-serif text-4xl sm:text-6xl tracking-tight leading-tight mb-6">
|
||||
{t('landing.cta.title')}
|
||||
</h2>
|
||||
<p className="text-white/50 text-lg mb-10">{t('landing.cta.desc')}</p>
|
||||
<Link
|
||||
href="/register"
|
||||
className="inline-flex items-center gap-3 px-10 py-4 rounded-full bg-[#F4F1EA] text-[#0B0A09] text-[15px] font-semibold hover:bg-white transition-all hover:scale-[1.02]"
|
||||
>
|
||||
{t('landing.cta.button')}
|
||||
</button>
|
||||
<ArrowRight size={18} />
|
||||
</Link>
|
||||
<p className="mt-4 text-[12px] text-white/30">{t('landing.hero.ctaHint')}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="py-20 px-8 border-t border-border bg-paper">
|
||||
<div className="max-w-6xl mx-auto flex flex-col md:flex-row justify-between gap-12">
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 bg-ink flex items-center justify-center rounded-lg">
|
||||
<span className="text-paper font-serif text-lg font-bold">M</span>
|
||||
<footer className="px-5 sm:px-8 py-14 border-t border-white/[0.06]">
|
||||
<div className="max-w-6xl mx-auto flex flex-col md:flex-row justify-between gap-10">
|
||||
<div className="max-w-xs">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<div className="w-7 h-7 bg-[#F4F1EA] text-[#0B0A09] flex items-center justify-center rounded-md">
|
||||
<span className="font-serif font-bold text-sm">M</span>
|
||||
</div>
|
||||
<span className="font-serif text-xl font-medium tracking-tight">Memento</span>
|
||||
<span className="font-serif text-lg">Memento</span>
|
||||
</div>
|
||||
<p className="text-sm text-concrete font-light max-w-xs">{t('landing.footer.desc')}</p>
|
||||
<p className="text-sm text-white/35">{t('landing.footer.desc')}</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-16">
|
||||
{FOOTER_SECTIONS.map(section => (
|
||||
<div key={section} className="space-y-4">
|
||||
<h6 className="text-[10px] font-bold uppercase tracking-widest text-ink">{t(`landing.footer.${section}.title`)}</h6>
|
||||
<ul className="space-y-2 text-sm text-concrete font-light">
|
||||
{[0, 1, 2].map(j => {
|
||||
<div className="grid grid-cols-3 gap-10 text-sm">
|
||||
{(['product', 'community', 'legal'] as const).map((section) => (
|
||||
<div key={section}>
|
||||
<p className="text-[11px] uppercase tracking-widest text-white/40 mb-3">
|
||||
{t(`landing.footer.${section}.title`)}
|
||||
</p>
|
||||
<ul className="space-y-2 text-white/50">
|
||||
{[0, 1, 2].map((j) => {
|
||||
const label = t(`landing.footer.${section}.link${j}`)
|
||||
const href = t(`landing.footer.${section}.link${j}Href`)
|
||||
if (!label || label.startsWith('landing.')) return null
|
||||
return <li key={j}><a href={href} className="hover:text-ink">{label}</a></li>
|
||||
return (
|
||||
<li key={j}>
|
||||
<a href={href} className="hover:text-white transition-colors">{label}</a>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-w-6xl mx-auto mt-20 pt-10 border-t border-border flex flex-col md:flex-row justify-between items-center gap-4 text-[10px] uppercase font-bold tracking-widest text-concrete">
|
||||
<div>© 2026 MOMENTO LABS. ALL RIGHTS RESERVED.</div>
|
||||
<div className="flex gap-8"><span>DESIGNED BY ANTIGRAVITY</span></div>
|
||||
</div>
|
||||
<p className="max-w-6xl mx-auto mt-12 pt-8 border-t border-white/[0.06] text-[11px] text-white/25 tracking-wide">
|
||||
© 2026 Memento. {t('landing.footer.rights')}
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ProductMoment({
|
||||
id,
|
||||
eyebrow,
|
||||
title,
|
||||
desc,
|
||||
children,
|
||||
dark,
|
||||
compact,
|
||||
}: {
|
||||
id?: string
|
||||
eyebrow: string
|
||||
title: string
|
||||
desc: string
|
||||
children: ReactNode
|
||||
dark?: boolean
|
||||
compact?: boolean
|
||||
}) {
|
||||
return (
|
||||
<motion.div
|
||||
id={id}
|
||||
initial={{ opacity: 0, y: 28 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: '-60px' }}
|
||||
transition={{ duration: 0.55 }}
|
||||
className={`rounded-[24px] sm:rounded-[32px] border overflow-hidden ${
|
||||
dark
|
||||
? 'bg-[#141210] border-white/[0.08] text-[#F4F1EA]'
|
||||
: 'bg-[#EDE8DF] border-transparent text-[#0B0A09]'
|
||||
} ${compact ? '' : 'grid grid-cols-1 lg:grid-cols-2'}`}
|
||||
>
|
||||
<div className={`p-8 sm:p-10 flex flex-col justify-center ${compact ? 'pb-4' : ''}`}>
|
||||
<p className={`text-[11px] uppercase tracking-[0.22em] font-semibold mb-3 ${dark ? 'text-[#D4A373]' : 'text-[#A47148]'}`}>
|
||||
{eyebrow}
|
||||
</p>
|
||||
<h3 className="font-serif text-2xl sm:text-3xl tracking-tight mb-3 leading-tight">{title}</h3>
|
||||
<p className={`text-[15px] leading-relaxed ${dark ? 'text-white/50' : 'text-[#0B0A09]/55'}`}>{desc}</p>
|
||||
</div>
|
||||
<div className={`${compact ? 'px-4 pb-6' : 'p-6 sm:p-8'} flex items-center`}>
|
||||
<div className="w-full">{children}</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user