"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { FileText, Layers, MousePointer2, Download, Cpu, CheckCircle2, ArrowRight, Layout, PieChart, FileSpreadsheet, Presentation, MessageSquare, ShieldCheck, Menu, X, Languages, Monitor, Eye, } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; import { useI18n } from "@/lib/i18n"; import { HeroVisual } from "./hero-visual"; import { API_BASE } from "@/lib/config"; import { LanguageSwitcher } from "@/components/ui/language-switcher"; const Logo = () => (
W
Wordly.art
); const Navbar = () => { const [isOpen, setIsOpen] = useState(false); const { t } = useI18n(); return ( ); }; const Hero = () => { const { t } = useI18n(); return (
{t('landing.hero.tag')}

{t('landing.hero.titleLine1')}
{t('landing.hero.titleLine2')}

{t('landing.hero.description')}

{t('landing.hero.ctaMain')} {t('landing.hero.ctaSec')}

{t('landing.hero.visualCaption')}

); }; const Steps = () => { const { t } = useI18n(); const steps = [ { num: t('landing.steps.step1.num'), title: t('landing.steps.step1.title'), desc: t('landing.steps.step1.desc'), icon: }, { num: t('landing.steps.step2.num'), title: t('landing.steps.step2.title'), desc: t('landing.steps.step2.desc'), icon: }, { num: t('landing.steps.step3.num'), title: t('landing.steps.step3.title'), desc: t('landing.steps.step3.desc'), icon: }, ]; return (
{t('landing.steps.process')}

{t('landing.steps.title')}

{t('landing.steps.subtitle')}

{steps.map((st, i) => (
{st.icon}

{st.title}

{st.desc}

))}
); }; const FeaturesContext = () => { const { t } = useI18n(); return (
{t('landing.features.tag')}

{t('landing.features.title')}

{[ { icon: , title: t('landing.features.context.title'), desc: t('landing.features.context.desc') }, { icon: , title: t('landing.features.glossary.title'), desc: t('landing.features.glossary.desc') }, { icon: , title: t('landing.features.vision.title'), desc: t('landing.features.vision.desc') }, ].map((item, i) => (
{item.icon}

{item.title}

{item.desc}

))}
); }; const LayoutFeatures = () => { const { t } = useI18n(); // Deux vraies pages de document : même structure, deux langues. // Le terme en gras montre le respect de la terminologie (glossaire). const DocPage = ({ side }: { side: "source" | "target" }) => (
{side === "source" ? "FR — original" : "EN — traduit"}

{side === "source" ? t("landing.beforeAfter.sourceTitle") : t("landing.beforeAfter.targetTitle")}

{side === "source" ? t("landing.beforeAfter.sourcePara1") : t("landing.beforeAfter.targetPara1")}

{side === "source" ? ( <> {t("landing.beforeAfter.sourcePara2Before")}{" "} {t("landing.beforeAfter.sourceTerm")}{" "} {t("landing.beforeAfter.sourcePara2After")} ) : ( <> {t("landing.beforeAfter.targetPara2Before")}{" "} {t("landing.beforeAfter.targetTerm")}{" "} {t("landing.beforeAfter.targetPara2After")} )}

  • {side === "source" ? t("landing.beforeAfter.sourceItem1") : t("landing.beforeAfter.targetItem1")}
  • {side === "source" ? t("landing.beforeAfter.sourceItem2") : t("landing.beforeAfter.targetItem2")}
cahier-des-charges.docx 3 pages · 2 481 mots
); return (

{t("landing.layout.title")}
{t("landing.layout.title2")}

{t("landing.layout.subtitle")}

{/* sceau central : la promesse, entre les deux pages */}
{t("landing.beforeAfter.seal")}
{/* trois preuves précises, pas des promesses vagues */}
{[ { icon: , label: t("landing.beforeAfter.proof1") }, { icon: , label: t("landing.beforeAfter.proof2") }, { icon: , label: t("landing.beforeAfter.proof3") }, ].map((proof, i) => (
{proof.icon} {proof.label}
))}
); }; const FormatsSection = () => { const { t } = useI18n(); const formats = [ { id: ".DOCX", name: t('landing.formats.word.name'), icon: , items: [t('landing.formats.word.i1'), t('landing.formats.word.i2'), t('landing.formats.word.i3'), t('landing.formats.word.i4')] }, { id: ".XLSX", name: t('landing.formats.excel.name'), icon: , items: [t('landing.formats.excel.i1'), t('landing.formats.excel.i2'), t('landing.formats.excel.i3'), t('landing.formats.excel.i4')] }, { id: ".PPTX", name: t('landing.formats.pptx.name'), icon: , items: [t('landing.formats.pptx.i1'), t('landing.formats.pptx.i2'), t('landing.formats.pptx.i3'), t('landing.formats.pptx.i4')] }, { id: ".PDF", name: t('landing.formats.pdf.name'), icon: , items: [t('landing.formats.pdf.i1'), t('landing.formats.pdf.i2'), t('landing.formats.pdf.i3'), t('landing.formats.pdf.i4')] }, ]; return (
{t('landing.formats.pill')}

{t('landing.formats.title')} {t('landing.formats.title2')}

{t('landing.formats.subtitle')}

{formats.map((f, i) => (
{f.icon}
{f.id}

{f.name}

    {f.items.map((item, j) => (
  • {item}
  • ))}
))}
); }; const PricingSection = () => { const { t } = useI18n(); const [isAnnual, setIsAnnual] = useState(true); // Prices come from the same live endpoint as /pricing (admin-configurable); // the hardcoded values below are only a fallback while it loads or fails. const [livePrices, setLivePrices] = useState>({}); useEffect(() => { fetch(`${API_BASE}/api/v1/auth/plans`, { cache: 'no-store' }) .then(r => (r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))) .then(json => { const d = json.data ?? json; const map: Record = {}; for (const plan of d.plans ?? []) { if (typeof plan.price_monthly === 'number' && plan.price_monthly > 0) { map[plan.id] = { monthly: plan.price_monthly, annualMonthly: plan.price_yearly > 0 ? plan.price_yearly / 12 : plan.price_monthly, }; } } setLivePrices(map); }) .catch(() => { /* keep marketing defaults */ }); }, []); const priceFor = (id: string, fallbackMonthly: number, fallbackAnnual: number): string => { const live = livePrices[id]; const value = live ? (isAnnual ? live.annualMonthly : live.monthly) : (isAnnual ? fallbackAnnual : fallbackMonthly); return Number.isInteger(value) ? String(value) : value.toFixed(2); }; const plans = [ { id: 'starter', name: t('landing.pricing.starter.name'), desc: t('landing.pricing.starter.desc'), monthlyPrice: 9, annualPrice: 7, features: [t('landing.pricing.starter.f1'), t('landing.pricing.starter.f2'), t('landing.pricing.starter.f3'), t('landing.pricing.starter.f4')], cta: t('landing.pricing.starter.cta'), popular: false }, { id: 'pro', name: t('landing.pricing.pro.name'), desc: t('landing.pricing.pro.desc'), monthlyPrice: 19, annualPrice: 15, features: [t('landing.pricing.pro.f1'), t('landing.pricing.pro.f2'), t('landing.pricing.pro.f3'), t('landing.pricing.pro.f4'), t('landing.pricing.pro.f5'), t('landing.pricing.pro.f6')], cta: t('landing.pricing.pro.cta'), popular: true }, { id: 'business', name: t('landing.pricing.business.name'), desc: t('landing.pricing.business.desc'), monthlyPrice: 49, annualPrice: 39, features: [t('landing.pricing.business.f1'), t('landing.pricing.business.f2'), t('landing.pricing.business.f3'), t('landing.pricing.business.f4'), t('landing.pricing.business.f5'), t('landing.pricing.business.f6')], cta: t('landing.pricing.business.cta'), popular: false }, ]; return (

{t('landing.pricing.title')}

{t('landing.pricing.subtitle')}

{plans.map((p, i) => (

{p.name}

{p.desc}

{priceFor(p.id, p.monthlyPrice, p.annualPrice)} € / {t('landing.pricing.month')}
    {p.features.map((f, j) => (
  • {f}
  • ))}
{p.cta}
))}

{t('landing.pricing.footer')}

); }; const CTASection = () => { const { t } = useI18n(); return (

{t('landing.cta.title')}

{t('landing.cta.subtitle')}

{t('landing.cta.button')}
); }; const Footer = () => { const { t } = useI18n(); return (

{t('landing.footer.desc')}

{t('landing.footer.rights')}
wordly.art
); }; export default function LandingPage() { return (
); }