feat: homelab deployment - NPM + IONOS DNS + monitoring + NAS backup

- Restructured docker-compose for Nginx Proxy Manager (no custom nginx)
- Added domain wordly.art configuration
- Added Prometheus + Grafana monitoring stack with pre-configured dashboards
- Added PostgreSQL backup script to NAS (daily/weekly/monthly rotation)
- Added alert rules for backend, system, and Docker metrics
- Updated deployment guide for NPM + IONOS DNS homelab setup
- Added marketing plan document
- PDF translator and watermark support
- Enhanced middleware, routes, and translator modules

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 11:43:28 +02:00
parent 16ac7ca2b9
commit ce8e150a61
110 changed files with 6935 additions and 4301 deletions

View File

@@ -1,86 +0,0 @@
import {
Globe2,
FileText,
Zap,
Shield,
Brain,
Server
} from "lucide-react"
const features = [
{
icon: Globe2,
title: "100+ Languages",
description: "Translate between any language pair with high accuracy",
color: "text-blue-400",
},
{
icon: FileText,
title: "Preserve Formatting",
description: "All styles, fonts, colors, tables, and charts remain intact",
color: "text-green-400",
},
{
icon: Zap,
title: "Lightning Fast",
description: "Batch processing translates entire documents in seconds",
color: "text-amber-400",
},
{
icon: Shield,
title: "Secure & Private",
description: "Your documents are encrypted and never stored permanently",
color: "text-purple-400",
},
{
icon: Brain,
title: "AI-Powered",
description: "Advanced neural translation for natural, context-aware results",
color: "text-teal-400",
},
{
icon: Server,
title: "Enterprise Ready",
description: "API access, team management, and dedicated support",
color: "text-orange-400",
},
]
export function FeaturesSection() {
return (
<section className="py-16 px-6">
<div className="max-w-5xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-2xl font-bold text-foreground mb-3">
Everything You Need for Document Translation
</h2>
<p className="text-muted-foreground max-w-2xl mx-auto">
Professional-grade translation with enterprise features, available to everyone.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{features.map((feature) => {
const Icon = feature.icon
return (
<div
key={feature.title}
className="flex flex-col items-center text-center p-6 rounded-xl border border-border bg-card/50 hover:bg-card transition-colors"
>
<div className={`mb-4 ${feature.color}`}>
<Icon className="size-8" />
</div>
<h3 className="text-base font-semibold text-foreground mb-2">
{feature.title}
</h3>
<p className="text-sm text-muted-foreground">
{feature.description}
</p>
</div>
)
})}
</div>
</div>
</section>
)
}

View File

@@ -1,46 +0,0 @@
import Link from "next/link"
import { FileSpreadsheet, FileText, Presentation } from "lucide-react"
import { Button } from "@/components/ui/button"
export function HeroSection() {
return (
<section className="flex flex-col items-center gap-6 px-6 pt-16 pb-8 text-center md:pt-24 md:pb-12">
<div className="flex items-center gap-2 rounded-full border border-border bg-card px-4 py-1.5 text-xs font-medium text-muted-foreground shadow-sm">
<span className="inline-block size-1.5 rounded-full bg-green-500" />
Now with Pro LLM Engine
</div>
<h1 className="max-w-2xl text-balance text-4xl font-bold leading-tight tracking-tight text-foreground md:text-5xl lg:text-6xl">
Translate Office Documents. Keep the Format Perfect.
</h1>
<p className="max-w-xl text-pretty text-base leading-relaxed text-muted-foreground md:text-lg">
Upload your Excel, Word, or PowerPoint files and get accurate translations with zero formatting loss.
</p>
<div className="flex items-center gap-4 pt-2">
<Button asChild size="lg">
<Link href="/auth/register">Try Free</Link>
</Button>
<Button variant="outline" asChild size="lg">
<Link href="/pricing">View Pricing</Link>
</Button>
</div>
<div className="flex items-center gap-6 pt-4">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<FileSpreadsheet className="size-4 text-green-500" />
<span>.xlsx</span>
</div>
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<FileText className="size-4 text-blue-500" />
<span>.docx</span>
</div>
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Presentation className="size-4 text-orange-500" />
<span>.pptx</span>
</div>
</div>
</section>
)
}

View File

@@ -76,7 +76,7 @@ export function HeroWordComparison() {
</div>
<div className="relative p-2 sm:p-4 bg-[#1e1e1e]">
<p className="mb-3 text-center text-xs text-slate-400 sm:text-left sm:px-1">
<p className="mb-3 text-center text-xs text-slate-400 sm:text-start sm:px-1">
{t("landing.heroDoc.caption")}
</p>
<div className="flex rounded-lg overflow-hidden border border-[#3c3c3c] shadow-inner min-h-[240px] sm:min-h-[300px]">
@@ -86,7 +86,7 @@ export function HeroWordComparison() {
{t("landing.heroDoc.badgeOriginal")}
</span>
</div>
<div className="flex-1 p-3 sm:p-4 overflow-hidden bg-white text-left">
<div className="flex-1 p-3 sm:p-4 overflow-hidden bg-white text-start">
<article
className="text-[11px] sm:text-xs leading-relaxed text-[#242424]"
style={{
@@ -108,7 +108,7 @@ export function HeroWordComparison() {
{t("landing.heroDoc.badgeTranslated")}
</span>
</div>
<div className="flex-1 p-3 sm:p-4 overflow-hidden bg-white text-left">
<div className="flex-1 p-3 sm:p-4 overflow-hidden bg-white text-start">
<article
className="text-[11px] sm:text-xs leading-relaxed text-[#242424]"
style={{

View File

@@ -1,226 +1,355 @@
"use client";
import { useState } from "react";
import { useState, useEffect } from "react";
import Link from "next/link";
import { useTranslation } from "@/lib/i18n";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { LanguageSwitcher } from "@/components/ui/language-switcher";
import { ThemeToggle } from "@/components/ui/theme-toggle";
import { HeroWordComparison } from "@/components/landing/hero-word-comparison";
import { useI18n } from "@/lib/i18n";
import { API_BASE } from "@/lib/config";
import {
Table2,
FileText,
Table2,
Presentation,
Bot,
Lock,
Zap,
FileSpreadsheet,
Check,
PlayCircle,
ShieldCheck,
Clock,
Languages,
Zap,
Sparkles,
ArrowRight,
BadgeCheck,
Eye,
LayoutGrid,
Bot,
Brain,
BookOpen,
Upload,
ArrowLeftRight,
} from "lucide-react";
export function LandingPage() {
const { t } = useTranslation();
const [isYearly, setIsYearly] = useState(false);
const { t } = useI18n();
const [prices, setPrices] = useState({
starter: { monthly: 9, yearly: 86.40 },
pro: { monthly: 19, yearly: 182.40 },
business: { monthly: 49, yearly: 470.40 },
});
useEffect(() => {
fetch(`${API_BASE}/api/v1/auth/plans`, { cache: "no-store" })
.then((r) => (r.ok ? r.json() : null))
.then((json) => {
if (!json) return;
const d = json.data ?? json;
const plans = Array.isArray(d.plans) ? d.plans : [];
const map: Record<string, { monthly: number; yearly: number }> = {};
for (const p of plans) {
if (p.id && typeof p.price_monthly === "number") {
map[p.id] = {
monthly: p.price_monthly,
yearly: typeof p.price_yearly === "number" ? p.price_yearly : p.price_monthly * 12,
};
}
}
if (map.starter || map.pro || map.business) {
setPrices((prev) => ({
starter: map.starter || prev.starter,
pro: map.pro || prev.pro,
business: map.business || prev.business,
}));
}
})
.catch(() => {});
}, []);
return (
<div className="min-h-screen bg-background text-foreground font-sans">
{/* Header */}
{/* ── Header ── */}
<header className="sticky top-0 z-50 w-full border-b border-border bg-background/80 backdrop-blur-md">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
<div className="flex items-center gap-4">
<Link href="/" className="flex items-center gap-3">
<div className="flex items-center justify-center rounded-lg bg-primary/10 p-1.5">
<Languages className="h-5 w-5 text-primary" />
</div>
<span className="text-foreground text-lg font-bold tracking-tight">
Office Translator
</span>
</div>
</Link>
<nav className="hidden md:flex items-center gap-8">
<a
className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors"
href="#features"
>
{t("nav.features")}
<a className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors" href="#why">
{t("landing.nav.whyUs")}
</a>
<a
className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors"
href="#pricing"
>
{t("nav.pricing")}
<a className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors" href="#formats">
{t("landing.nav.formats")}
</a>
<a
className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors"
href="#enterprise"
>
{t("nav.enterprise")}
<a className="text-muted-foreground hover:text-foreground text-sm font-medium transition-colors" href="#pricing">
{t("landing.nav.pricing")}
</a>
</nav>
<div className="flex items-center gap-2">
<LanguageSwitcher variant="button" />
<LanguageSwitcher variant="select" />
<ThemeToggle />
<a
className="hidden sm:inline-flex h-9 items-center justify-center rounded-lg px-4 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
href="/auth/login"
>
{t("common.login")}
{t("landing.nav.login")}
</a>
<Button
asChild
className="bg-primary text-primary-foreground font-bold hover:bg-primary/90"
>
<Link href="/auth/register">{t("common.signup")}</Link>
<Button asChild className="bg-primary text-primary-foreground font-bold hover:bg-primary/90">
<Link href="/auth/register">{t("landing.nav.startFree")}</Link>
</Button>
</div>
</div>
</header>
<main>
{/* Hero Section */}
<section className="relative pt-20 pb-32 overflow-hidden">
{/* ── Hero ── */}
<section className="relative pt-20 pb-28 overflow-hidden">
<div className="absolute inset-0 -z-10 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-primary/10 via-background to-background" />
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex flex-col lg:flex-row items-center gap-12 lg:gap-20">
{/* Hero Text */}
<div className="flex-1 text-center lg:text-left space-y-8">
<h1 className="text-4xl font-extrabold tracking-tight text-foreground sm:text-5xl lg:text-6xl xl:text-7xl">
{t("hero.title")}
<br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-emerald-400">
{t("hero.titleHighlight")}
</span>
</h1>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 text-center">
{/* Badge */}
<div className="inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/5 px-4 py-1.5 text-sm font-medium text-primary mb-8">
<Sparkles className="h-4 w-4" />
{t("landing.hero.badge")}
</div>
<p className="mx-auto lg:mx-0 max-w-2xl text-lg text-muted-foreground">
{t("hero.subtitle")}
</p>
<h1 className="text-4xl font-extrabold tracking-tight text-foreground sm:text-5xl lg:text-6xl xl:text-7xl mx-auto max-w-5xl">
{t("landing.hero.title1")}
<br />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-emerald-400">
{t("landing.hero.title2")}
</span>
</h1>
<div className="flex flex-col sm:flex-row items-center justify-center lg:justify-start gap-4">
<Button
asChild
size="lg"
className="bg-primary text-primary-foreground font-bold hover:bg-primary/90"
>
<Link href="/auth/register">{t("hero.cta")}</Link>
</Button>
<Button
variant="outline"
size="lg"
className="gap-2"
>
<PlayCircle className="h-5 w-5" />
{t("hero.demoCta")}
</Button>
</div>
<p className="mt-8 mx-auto max-w-2xl text-lg text-muted-foreground">
{t("landing.hero.subtitle")}
</p>
{/* Trust Badges */}
<div className="pt-8 flex flex-wrap justify-center lg:justify-start gap-6">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<ShieldCheck className="h-5 w-5 text-primary" />
<span>{t("hero.badge1")}</span>
</div>
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Clock className="h-5 w-5 text-primary" />
<span>{t("hero.badge2")}</span>
</div>
</div>
<div className="mt-10 flex flex-col sm:flex-row items-center justify-center gap-4">
<Button
asChild
size="lg"
className="bg-primary text-primary-foreground font-bold hover:bg-primary/90 text-base px-8"
>
<Link href="/auth/register">
{t("landing.hero.cta")}
<ArrowRight className="ms-2 h-5 w-5" />
</Link>
</Button>
<Button variant="outline" size="lg" className="text-base px-8" asChild>
<Link href="#pricing">{t("landing.hero.seePlans")}</Link>
</Button>
</div>
{/* Trust Badges */}
<div className="mt-12 flex flex-wrap justify-center gap-8 text-sm text-muted-foreground">
<div className="flex items-center gap-2">
<ShieldCheck className="h-5 w-5 text-primary" />
<span>{t("landing.trust.filesDeleted")}</span>
</div>
{/* Hero Visual — Word EN | FR + illustrations */}
<div className="flex-1 w-full max-w-lg lg:max-w-none relative">
<HeroWordComparison />
<div className="flex items-center gap-2">
<BadgeCheck className="h-5 w-5 text-primary" />
<span>{t("landing.trust.noBait")}</span>
</div>
<div className="flex items-center gap-2">
<Eye className="h-5 w-5 text-primary" />
<span>{t("landing.trust.preview")}</span>
</div>
</div>
{/* Format pills */}
<div className="mt-10 flex flex-wrap justify-center gap-3">
{[".DOCX", ".XLSX", ".PPTX", ".PDF"].map((fmt) => (
<span
key={fmt}
className="inline-flex items-center rounded-full border border-border bg-card px-4 py-2 text-sm font-mono font-semibold text-foreground"
>
{fmt}
</span>
))}
</div>
</div>
</section>
{/* Features Grid */}
<section className="py-24 bg-background relative" id="features">
{/* ── How it works ── */}
<section className="py-24 bg-background">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-4">
{t("features.title")}
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-4">
{t("landing.howItWorks.title")}
</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
{t("features.subtitle")}
{t("landing.howItWorks.subtitle")}
</p>
</div>
<div className="flex flex-wrap justify-center gap-6">
{/* Feature 1 - Excel */}
<div className="w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)] group relative rounded-2xl border border-border bg-card p-8 hover:border-primary/50 transition-colors">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
<Table2 className="h-6 w-6" />
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-4xl mx-auto">
{[
{
step: "1",
icon: Upload,
title: t("landing.howItWorks.step1.title"),
desc: t("landing.howItWorks.step1.desc"),
color: "text-blue-500",
},
{
step: "2",
icon: ArrowLeftRight,
title: t("landing.howItWorks.step2.title"),
desc: t("landing.howItWorks.step2.desc"),
color: "text-primary",
},
{
step: "3",
icon: Check,
title: t("landing.howItWorks.step3.title"),
desc: t("landing.howItWorks.step3.desc"),
color: "text-emerald-500",
},
].map((s, i) => (
<div key={i} className="flex flex-col items-center text-center">
<div className={`flex size-16 items-center justify-center rounded-2xl bg-muted/50 mb-4 ${s.color}`}>
<s.icon className="size-7" />
</div>
<h3 className="text-lg font-bold mb-2">{s.title}</h3>
<p className="text-sm text-muted-foreground max-w-xs">{s.desc}</p>
</div>
<h3 className="text-xl font-bold text-foreground mb-2">
{t("features.excel.title")}
</h3>
<p className="text-muted-foreground">{t("features.excel.description")}</p>
</div>
))}
</div>
</div>
</section>
{/* Feature 2 - Word */}
<div className="w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)] group relative rounded-2xl border border-border bg-card p-8 hover:border-primary/50 transition-colors">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
<FileText className="h-6 w-6" />
</div>
<h3 className="text-xl font-bold text-foreground mb-2">
{t("features.word.title")}
</h3>
<p className="text-muted-foreground">{t("features.word.description")}</p>
{/* ── AI Translation Engine ── */}
<section className="py-24 bg-muted/30" id="ai-engine">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<div className="inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/5 px-4 py-1.5 text-sm font-medium text-primary mb-6">
<Brain className="h-4 w-4" />
{t("landing.ai.badge")}
</div>
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-4">
{t("landing.ai.title")}
</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
{t("landing.ai.subtitle")}
</p>
</div>
{/* Feature 3 - PowerPoint */}
<div className="w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)] group relative rounded-2xl border border-border bg-card p-8 hover:border-primary/50 transition-colors">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
<Presentation className="h-6 w-6" />
{/* Three pillars */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-5xl mx-auto mb-16">
{[
{ icon: Zap, title: t("landing.ai.context.title"), desc: t("landing.ai.context.desc"), color: "text-blue-500" },
{ icon: BookOpen, title: t("landing.ai.glossary.title"), desc: t("landing.ai.glossary.desc"), color: "text-primary" },
{ icon: Eye, title: t("landing.ai.vision.title"), desc: t("landing.ai.vision.desc"), color: "text-emerald-500" },
].map((feature, i) => (
<div key={i} className="group relative rounded-2xl border border-border bg-card p-8 hover:border-primary/50 transition-colors">
<div className={`mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-muted/50 ${feature.color}`}>
<feature.icon className="h-6 w-6" />
</div>
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
<p className="text-muted-foreground text-sm">{feature.desc}</p>
</div>
<h3 className="text-xl font-bold text-foreground mb-2">
{t("features.powerpoint.title")}
</h3>
<p className="text-muted-foreground">
{t("features.powerpoint.description")}
</p>
</div>
))}
</div>
{/* Feature 4 - AI */}
<div className="w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)] group relative rounded-2xl border border-border bg-card p-8 hover:border-primary/50 transition-colors">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
<Bot className="h-6 w-6" />
{/* Comparison */}
<div className="max-w-3xl mx-auto rounded-2xl border border-border bg-card p-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">{t("landing.ai.comparison.source")}</p>
<p className="text-sm text-foreground font-medium italic">&ldquo;{t("landing.ai.comparison.sourceText")}&rdquo;</p>
</div>
<h3 className="text-xl font-bold text-foreground mb-2">
{t("features.ai.title")}
</h3>
<p className="text-muted-foreground">{t("features.ai.description")}</p>
</div>
{/* Feature 5 - Speed */}
<div className="w-full md:w-[calc(50%-12px)] lg:w-[calc(33.333%-16px)] group relative rounded-2xl border border-border bg-card p-8 hover:border-primary/50 transition-colors">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
<Zap className="h-6 w-6" />
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">{t("landing.ai.comparison.google")}</p>
<p className="text-sm text-destructive/80">&ldquo;{t("landing.ai.comparison.googleText")}&rdquo;</p>
</div>
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-wider text-primary">{t("landing.ai.comparison.ai")}</p>
<p className="text-sm text-emerald-600 font-medium">&ldquo;{t("landing.ai.comparison.aiText")}&rdquo;</p>
</div>
<h3 className="text-xl font-bold text-foreground mb-2">
{t("features.speed.title")}
</h3>
<p className="text-muted-foreground">{t("features.speed.description")}</p>
</div>
</div>
</div>
</section>
{/* Pricing Section */}
<section className="py-24 bg-muted/30" id="pricing">
{/* ── Why Us — Format Preservation ── */}
<section className="py-24 bg-muted/30" id="why">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-4">
{t("landing.why.title")}
</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
{t("landing.why.subtitle")}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{[
{
icon: LayoutGrid,
title: t("landing.why.smartart.title"),
desc: t("landing.why.smartart.desc"),
},
{
icon: FileText,
title: t("landing.why.toc.title"),
desc: t("landing.why.toc.desc"),
},
{
icon: Table2,
title: t("landing.why.charts.title"),
desc: t("landing.why.charts.desc"),
},
{
icon: Presentation,
title: t("landing.why.shapes.title"),
desc: t("landing.why.shapes.desc"),
},
{
icon: FileSpreadsheet,
title: t("landing.why.headers.title"),
desc: t("landing.why.headers.desc"),
},
{
icon: Zap,
title: t("landing.why.languages.title"),
desc: t("landing.why.languages.desc"),
},
].map((feature, i) => (
<div
key={i}
className="group relative rounded-2xl border border-border bg-card p-8 hover:border-primary/50 transition-colors"
>
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
<feature.icon className="h-6 w-6" />
</div>
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
<p className="text-muted-foreground">{feature.desc}</p>
</div>
))}
</div>
</div>
</section>
{/* ── Honest Pricing Comparison ── */}
<section className="py-24 bg-background" id="pricing">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center mb-10">
<h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-4">
{t("pricing.title")}
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-4">
{t("landing.pricing.title")}
</h2>
<p className="text-lg text-muted-foreground">{t("pricing.subtitle")}</p>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
{t("landing.pricing.subtitle")}
</p>
<div className="flex justify-center mt-8">
<div className="inline-flex items-center gap-3 bg-card border border-border rounded-full p-1.5">
@@ -228,21 +357,24 @@ export function LandingPage() {
onClick={() => setIsYearly(false)}
className={cn(
"px-5 py-2 rounded-full text-sm font-medium transition-all",
!isYearly ? "bg-primary text-primary-foreground shadow" : "text-muted-foreground hover:text-foreground"
!isYearly ? "bg-primary text-primary-foreground shadow" : "text-muted-foreground"
)}
>
{t("common.monthly")}
{t("landing.pricing.monthly")}
</button>
<button
onClick={() => setIsYearly(true)}
className={cn(
"px-5 py-2 rounded-full text-sm font-medium transition-all flex items-center gap-2",
isYearly ? "bg-primary text-primary-foreground shadow" : "text-muted-foreground hover:text-foreground"
isYearly ? "bg-primary text-primary-foreground shadow" : "text-muted-foreground"
)}
>
{t("common.yearly")}
<span className={cn("text-xs px-1.5 py-0.5 rounded-full font-bold", isYearly ? "bg-background text-primary" : "bg-emerald-500/20 text-emerald-600 dark:text-emerald-400")}>
{t("common.save20")}
{t("landing.pricing.yearly")}
<span className={cn(
"text-xs px-1.5 py-0.5 rounded-full font-bold",
isYearly ? "bg-background text-primary" : "bg-emerald-500/20 text-emerald-600"
)}>
-20%
</span>
</button>
</div>
@@ -250,140 +382,218 @@ export function LandingPage() {
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto">
{/* Starter Plan */}
{/* Starter */}
<div className="rounded-2xl border border-border bg-card p-8 flex flex-col">
<h3 className="text-xl font-semibold text-foreground">
{t("pricing.starter.name")}
</h3>
<p className="mt-4 text-muted-foreground text-sm">
{t("pricing.starter.description")}
<h3 className="text-xl font-semibold">{t("landing.pricing.starter.title")}</h3>
<p className="mt-2 text-muted-foreground text-sm">
{t("landing.pricing.starter.desc")}
</p>
<div className="mt-6 mb-2">
<span className="text-4xl font-bold text-foreground">
{isYearly ? t("pricing.starter.priceYearly") : t("pricing.starter.priceMonthly")}
<span className="text-4xl font-bold">
{isYearly ? prices.starter.yearly / 12 : prices.starter.monthly}
</span>
<span className="text-muted-foreground">/{t("common.month")}</span>
<span className="text-muted-foreground">{t("landing.pricing.perMonth")}</span>
</div>
<div className="h-6 mb-4">
{isYearly && (
<span className="text-sm font-medium text-emerald-600 dark:text-emerald-400">
{t("pricing.starter.billedYearly")}
</span>
)}
</div>
<ul className="space-y-4 mb-8 flex-1">
{[0, 1, 2, 3].map((i) => (
<li key={i} className="flex items-center text-muted-foreground text-sm">
<Check className="h-4 w-4 text-primary mr-2 shrink-0" />
{t(`pricing.starter.features.${i}`)}
{isYearly && (
<span className="text-sm text-emerald-600 mb-4">{prices.starter.yearly} {t("landing.pricing.billedYearly")}</span>
)}
<ul className="space-y-3 mb-8 flex-1 mt-4">
{[
t("landing.pricing.starter.f1"),
t("landing.pricing.starter.f2"),
t("landing.pricing.starter.f3"),
t("landing.pricing.starter.f4"),
].map((f, i) => (
<li key={i} className="flex items-center text-sm text-muted-foreground">
<Check className="h-4 w-4 text-primary me-2 shrink-0" />{f}
</li>
))}
</ul>
<Button asChild variant="outline">
<Link href="/pricing?plan=starter">{t("common.signup")}</Link>
<Button asChild variant="outline" className="w-full">
<Link href="/pricing?plan=starter">{t("landing.pricing.starter.cta")}</Link>
</Button>
</div>
{/* Pro Plan */}
<div className="relative rounded-2xl border border-primary bg-card p-8 flex flex-col shadow-lg shadow-primary/10 scale-105 z-10">
<div className="absolute -top-4 left-1/2 -translate-x-1/2 rounded-full bg-warning px-3 py-1 text-xs font-bold text-warning-foreground uppercase tracking-wide">
{t("common.popular")}
{/* Pro — Popular */}
<div className="relative rounded-2xl border-2 border-primary bg-card p-8 flex flex-col shadow-lg shadow-primary/10 scale-105 z-10">
<div className="absolute -top-4 left-1/2 -translate-x-1/2 rounded-full bg-primary px-4 py-1 text-xs font-bold text-primary-foreground uppercase tracking-wide">
{t("landing.pricing.pro.badge")}
</div>
<h3 className="text-xl font-semibold text-foreground">
{t("pricing.pro.name")}
</h3>
<p className="mt-4 text-muted-foreground text-sm">
{t("pricing.pro.description")}
<h3 className="text-xl font-semibold">{t("landing.pricing.pro.title")}</h3>
<p className="mt-2 text-muted-foreground text-sm">
{t("landing.pricing.pro.desc")}
</p>
<div className="mt-6 mb-2">
<span className="text-4xl font-bold text-foreground">
{isYearly ? t("pricing.pro.priceYearly") : t("pricing.pro.priceMonthly")}
<span className="text-4xl font-bold">
{isYearly ? prices.pro.yearly / 12 : prices.pro.monthly}
</span>
<span className="text-muted-foreground">/{t("common.month")}</span>
<span className="text-muted-foreground">{t("landing.pricing.perMonth")}</span>
</div>
<div className="h-6 mb-4">
{isYearly && (
<span className="text-sm font-medium text-emerald-600 dark:text-emerald-400">
{t("pricing.pro.billedYearly")}
</span>
)}
</div>
<ul className="space-y-4 mb-8 flex-1">
{[0, 1, 2, 3].map((i) => (
<li key={i} className="flex items-center text-foreground text-sm">
<Check className="h-4 w-4 text-primary mr-2 shrink-0" />
{t(`pricing.pro.features.${i}`)}
{isYearly && (
<span className="text-sm text-emerald-600 mb-4">{prices.pro.yearly} {t("landing.pricing.billedYearly")}</span>
)}
<ul className="space-y-3 mb-8 flex-1 mt-4">
{[
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"),
].map((f, i) => (
<li key={i} className="flex items-center text-sm text-foreground font-medium">
<Check className="h-4 w-4 text-primary me-2 shrink-0" />{f}
</li>
))}
</ul>
<Button
asChild
className="bg-primary text-primary-foreground font-bold hover:bg-primary/90"
>
<Link href="/pricing?plan=pro">{t("common.tryPro")}</Link>
<Button asChild className="w-full bg-primary text-primary-foreground font-bold hover:bg-primary/90">
<Link href="/pricing?plan=pro">{t("landing.pricing.pro.cta")}</Link>
</Button>
</div>
{/* Business Plan */}
{/* Business */}
<div className="rounded-2xl border border-border bg-card p-8 flex flex-col">
<h3 className="text-xl font-semibold text-foreground">
{t("pricing.business.name")}
</h3>
<p className="mt-4 text-muted-foreground text-sm">
{t("pricing.business.description")}
<h3 className="text-xl font-semibold">{t("landing.pricing.business.title")}</h3>
<p className="mt-2 text-muted-foreground text-sm">
{t("landing.pricing.business.desc")}
</p>
<div className="mt-6 mb-2">
<span className="text-4xl font-bold text-foreground">
{isYearly ? t("pricing.business.priceYearly") : t("pricing.business.priceMonthly")}
<span className="text-4xl font-bold">
{isYearly ? prices.business.yearly / 12 : prices.business.monthly}
</span>
<span className="text-muted-foreground">/{t("common.month")}</span>
<span className="text-muted-foreground">{t("landing.pricing.perMonth")}</span>
</div>
<div className="h-6 mb-4">
{isYearly && (
<span className="text-sm font-medium text-emerald-600 dark:text-emerald-400">
{t("pricing.business.billedYearly")}
</span>
)}
</div>
<ul className="space-y-4 mb-8 flex-1">
{[0, 1, 2, 3].map((i) => (
<li key={i} className="flex items-center text-muted-foreground text-sm">
<Check className="h-4 w-4 text-primary mr-2 shrink-0" />
{t(`pricing.business.features.${i}`)}
{isYearly && (
<span className="text-sm text-emerald-600 mb-4">{prices.business.yearly} {t("landing.pricing.billedYearly")}</span>
)}
<ul className="space-y-3 mb-8 flex-1 mt-4">
{[
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"),
].map((f, i) => (
<li key={i} className="flex items-center text-sm text-muted-foreground">
<Check className="h-4 w-4 text-primary me-2 shrink-0" />{f}
</li>
))}
</ul>
<Button asChild variant="outline">
<Link href="/pricing?plan=business">{t("common.tryPro")}</Link>
<Button asChild variant="outline" className="w-full">
<Link href="/pricing?plan=business">{t("landing.pricing.business.cta")}</Link>
</Button>
</div>
</div>
{/* Honest pricing callout */}
<div className="mt-12 text-center">
<div className="inline-flex items-center gap-2 rounded-full border border-emerald-500/30 bg-emerald-500/5 px-5 py-2.5 text-sm text-emerald-700 dark:text-emerald-400">
<BadgeCheck className="h-5 w-5" />
<span className="font-medium">{t("landing.pricing.honest")}</span>
</div>
</div>
</div>
</section>
{/* CTA Section */}
{/* ── Format Support ── */}
<section className="py-24 bg-muted/30" id="formats">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl mb-4">
{t("landing.formats.title")}
</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
{t("landing.formats.subtitle")}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-4xl mx-auto">
{[
{
format: t("landing.formats.word"),
items: [
t("landing.formats.word.f1"),
t("landing.formats.word.f2"),
t("landing.formats.word.f3"),
t("landing.formats.word.f4"),
t("landing.formats.word.f5"),
t("landing.formats.word.f6"),
t("landing.formats.word.f7"),
],
},
{
format: t("landing.formats.excel"),
items: [
t("landing.formats.excel.f1"),
t("landing.formats.excel.f2"),
t("landing.formats.excel.f3"),
t("landing.formats.excel.f4"),
t("landing.formats.excel.f5"),
],
},
{
format: t("landing.formats.powerpoint"),
items: [
t("landing.formats.powerpoint.f1"),
t("landing.formats.powerpoint.f2"),
t("landing.formats.powerpoint.f3"),
t("landing.formats.powerpoint.f4"),
t("landing.formats.powerpoint.f5"),
],
},
{
format: t("landing.formats.pdf"),
items: [
t("landing.formats.pdf.f1"),
t("landing.formats.pdf.f2"),
t("landing.formats.pdf.f3"),
t("landing.formats.pdf.f4"),
t("landing.formats.pdf.f5"),
],
},
].map((fmt, i) => (
<div key={i} className="rounded-2xl border border-border bg-card p-8">
<h3 className="text-xl font-bold mb-4">{fmt.format}</h3>
<ul className="space-y-2">
{fmt.items.map((item, j) => (
<li key={j} className="flex items-center text-sm text-muted-foreground">
<Check className="h-4 w-4 text-primary me-2 shrink-0" />{item}
</li>
))}
</ul>
</div>
))}
</div>
</div>
</section>
{/* ── CTA ── */}
<section className="relative py-20 px-4 bg-background">
<div className="absolute inset-0 bg-gradient-to-t from-primary/5 to-transparent pointer-events-none" />
<div className="mx-auto max-w-4xl rounded-3xl bg-card border border-border p-12 text-center shadow-2xl overflow-hidden relative">
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-primary to-transparent opacity-50" />
<h2 className="text-3xl font-bold text-foreground mb-6">
{t("cta.title")}
<h2 className="text-3xl font-bold mb-6">
{t("landing.cta.title")}
</h2>
<p className="text-muted-foreground mb-8 max-w-2xl mx-auto">
{t("cta.subtitle")}
{t("landing.cta.subtitle")}
</p>
<Button
asChild
size="lg"
className="bg-primary text-primary-foreground font-bold hover:bg-primary/90"
className="bg-primary text-primary-foreground font-bold hover:bg-primary/90 text-base px-8"
>
<Link href="/auth/register">{t("cta.button")}</Link>
<Link href="/auth/register">
{t("landing.cta.button")}
<ArrowRight className="ms-2 h-5 w-5" />
</Link>
</Button>
</div>
</section>
</main>
{/* Footer */}
{/* ── Footer ── */}
<footer className="border-t border-border bg-background py-12">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex flex-col md:flex-row justify-between items-center gap-6">
@@ -396,17 +606,13 @@ export function LandingPage() {
</span>
</div>
<div className="flex gap-6 text-sm text-muted-foreground">
<a className="hover:text-foreground transition-colors" href="#">
{t("footer.privacy")}
</a>
<a className="hover:text-foreground transition-colors" href="#">
{t("footer.terms")}
</a>
<a className="hover:text-foreground transition-colors" href="#">
{t("footer.contact")}
</a>
<a className="hover:text-foreground transition-colors" href="#">{t("landing.footer.privacy")}</a>
<a className="hover:text-foreground transition-colors" href="#">{t("landing.footer.terms")}</a>
<a className="hover:text-foreground transition-colors" href="#">{t("landing.footer.contact")}</a>
</div>
<div className="text-muted-foreground text-sm">
© {new Date().getFullYear()} Office Translator
</div>
<div className="text-muted-foreground text-sm">{t("footer.copyright")}</div>
</div>
</div>
</footer>