All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m20s
Translation quality & format preservation: - Word: merge adjacent same-format runs into one unit (sentence-level coherence like inline-tag handling); translate comments/balloons; dedupe textbox collection (was translated twice); RTL no longer overrides center/justify alignment; CJK/Arabic font hints (eastAsia/cs) - PPTX: chart translations now actually reach the output file (ChartPart.blob is read-only — rewrite chart XML in the saved ZIP); CJK typeface hints (a:ea) - Excel: sheet renames no longer break references — rewrite cell formulas (3D/quoted), defined names, data validations, cond. formats - PDF: bold/italic honored (hebo/heit/hebi); table cells never merge; unchanged blocks left untouched (typography preserved, fixes duplicate hyperlinks); attempted/changed stats + route gate now cover PDF; CJK font paths; scanned PDFs via Mistral OCR (detection + admin settings) Features: - formality param (formal/informal) + automatic regional-variant prompts - output_mode=bilingual docx (source above translation) - per-user translation memory on Redis (falls back to LRU), context-hashed - QA report + 0-100 confidence score in job status; L0 on by default - OpenAI-compatible providers: whole chunk in ONE numbered-JSON request (~15x fewer calls) with per-item fallback; base prompt always present (custom prompt no longer replaces translation instructions) Infra & marketing alignment: - plan-based engine gating + vision gating (closes paid-engine leak); /providers/available filtered per plan; 107 languages exposed - zh-CN/zh-TW validation fixed; libmagic disabled on Windows (native crash) - admin: Mistral OCR settings + engine status dashboard; httpx<0.28 pin (TestClient breakage); Prometheus test fixture fixed - marketing docs aligned with code (PDF+OCR, retention, engines, pricing) - security: .env.ionos/.env.production/provider_settings.json removed Tests: 1173 passed / 0 failed (6 network tests deselected: free Google endpoint temporarily blocked from this machine)
249 lines
7.8 KiB
TypeScript
249 lines
7.8 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { Check, Crown, Zap, Building2, Sparkles, ArrowRight } from "lucide-react"
|
|
import Link from "next/link"
|
|
import { track } from "@vercel/analytics"
|
|
import { Button } from "@/components/ui/button"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
type BillingCycle = "monthly" | "yearly"
|
|
|
|
interface Plan {
|
|
id: string
|
|
name: string
|
|
description: string
|
|
monthly: number
|
|
yearly: number
|
|
features: string[]
|
|
cta: string
|
|
badge?: string
|
|
icon: typeof Check
|
|
featured?: boolean
|
|
}
|
|
|
|
const PLANS: Plan[] = [
|
|
{
|
|
id: "free",
|
|
name: "Free",
|
|
description: "Test the full workflow on real documents.",
|
|
monthly: 0,
|
|
yearly: 0,
|
|
features: [
|
|
"2 documents / month",
|
|
"Up to 10 pages per document",
|
|
"Up to 5 MB file size",
|
|
"Google engine",
|
|
"Watermarked output",
|
|
],
|
|
cta: "Start Free",
|
|
icon: Zap,
|
|
},
|
|
{
|
|
id: "starter",
|
|
name: "Starter",
|
|
description: "For freelancers and light professional use.",
|
|
monthly: 9,
|
|
yearly: 7.2,
|
|
features: [
|
|
"50 documents / month",
|
|
"Up to 50 pages per document",
|
|
"Up to 10 MB file size",
|
|
"Google + DeepL engines",
|
|
"No watermark",
|
|
"Priority email support",
|
|
],
|
|
cta: "Choose Starter",
|
|
icon: Sparkles,
|
|
},
|
|
{
|
|
id: "pro",
|
|
name: "Pro",
|
|
description: "For teams that translate regularly.",
|
|
monthly: 19,
|
|
yearly: 15.2,
|
|
features: [
|
|
"200 documents / month",
|
|
"Up to 200 pages per document",
|
|
"Up to 25 MB file size",
|
|
"All classic engines (Google Cloud, DeepL, OpenRouter)",
|
|
"AI translation (context-aware LLM)",
|
|
"Priority processing",
|
|
"Custom glossaries",
|
|
"No watermark",
|
|
],
|
|
cta: "Choose Pro",
|
|
badge: "Most Popular",
|
|
icon: Crown,
|
|
featured: true,
|
|
},
|
|
{
|
|
id: "business",
|
|
name: "Business",
|
|
description: "For agencies and international operations.",
|
|
monthly: 49,
|
|
yearly: 39.2,
|
|
features: [
|
|
"1,000 documents / month",
|
|
"Up to 500 pages per document",
|
|
"Up to 50 MB file size",
|
|
"All engines incl. premium LLMs (Claude, OpenAI)",
|
|
"API access (10,000 calls / month)",
|
|
"5 team seats",
|
|
"Priority processing",
|
|
"Dedicated support",
|
|
],
|
|
cta: "Choose Business",
|
|
icon: Building2,
|
|
},
|
|
]
|
|
|
|
export function PricingSection() {
|
|
const [cycle, setCycle] = useState<BillingCycle>("monthly")
|
|
|
|
return (
|
|
<section id="pricing" className="flex flex-col items-center gap-8 px-6 py-16 md:py-24">
|
|
<div className="flex flex-col items-center gap-3 text-center">
|
|
<h2 className="text-balance text-3xl font-bold tracking-tight text-foreground md:text-4xl">
|
|
Simple pricing that scales with you
|
|
</h2>
|
|
<p className="max-w-xl text-pretty text-sm leading-relaxed text-muted-foreground md:text-base">
|
|
Start free, upgrade when you need more volume, faster engines or API
|
|
access. Cancel anytime.
|
|
</p>
|
|
|
|
<div className="mt-2 flex items-center rounded-lg border border-border bg-muted p-1">
|
|
<button
|
|
type="button"
|
|
className={cn(
|
|
"rounded-md px-4 py-1.5 text-sm font-medium transition-all",
|
|
cycle === "monthly"
|
|
? "bg-card text-foreground shadow-sm"
|
|
: "text-muted-foreground hover:text-foreground"
|
|
)}
|
|
onClick={() => setCycle("monthly")}
|
|
>
|
|
Monthly
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={cn(
|
|
"rounded-md px-4 py-1.5 text-sm font-medium transition-all",
|
|
cycle === "yearly"
|
|
? "bg-card text-foreground shadow-sm"
|
|
: "text-muted-foreground hover:text-foreground"
|
|
)}
|
|
onClick={() => setCycle("yearly")}
|
|
>
|
|
Yearly
|
|
<span className="ml-1.5 rounded-full bg-success/15 px-1.5 py-0.5 text-[10px] font-semibold text-success">
|
|
-20%
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid w-full max-w-6xl grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4">
|
|
{PLANS.map((plan) => (
|
|
<div
|
|
key={plan.id}
|
|
className={cn(
|
|
"flex flex-col rounded-xl border bg-card p-6 shadow-sm transition-shadow hover:shadow-md",
|
|
plan.featured
|
|
? "border-primary shadow-lg ring-1 ring-primary/20"
|
|
: "border-border"
|
|
)}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<div
|
|
className={cn(
|
|
"flex size-8 items-center justify-center rounded-lg",
|
|
plan.featured ? "bg-primary" : "bg-secondary"
|
|
)}
|
|
>
|
|
<plan.icon
|
|
className={cn(
|
|
"size-4",
|
|
plan.featured ? "text-primary-foreground" : "text-foreground"
|
|
)}
|
|
/>
|
|
</div>
|
|
<span className="text-base font-semibold text-foreground">
|
|
{plan.name}
|
|
</span>
|
|
</div>
|
|
{plan.badge && (
|
|
<span className="rounded-full bg-primary px-2.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary-foreground">
|
|
{plan.badge}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<p className="mt-3 text-xs leading-relaxed text-muted-foreground">
|
|
{plan.description}
|
|
</p>
|
|
|
|
<div className="mt-4 flex items-baseline gap-1">
|
|
<span className="text-4xl font-bold tracking-tight text-foreground">
|
|
{cycle === "yearly" && plan.monthly > 0
|
|
? `€${plan.yearly.toFixed(2).replace(/\.00$/, "")}`
|
|
: plan.monthly === 0
|
|
? "€0"
|
|
: `€${plan.monthly}`}
|
|
</span>
|
|
<span className="text-sm text-muted-foreground">
|
|
{cycle === "yearly" && plan.monthly > 0
|
|
? "/mo billed yearly"
|
|
: plan.monthly === 0
|
|
? "forever"
|
|
: "/mo"}
|
|
</span>
|
|
</div>
|
|
|
|
<ul className="mt-5 flex flex-1 flex-col gap-2.5">
|
|
{plan.features.map((feature) => (
|
|
<li key={feature} className="flex items-start gap-2 text-xs text-foreground/90">
|
|
<Check className="mt-0.5 size-3.5 shrink-0 text-success" />
|
|
<span>{feature}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<Button
|
|
asChild
|
|
variant={plan.featured ? "default" : "outline"}
|
|
size="sm"
|
|
className="mt-6 w-full"
|
|
>
|
|
<Link
|
|
href="/dashboard"
|
|
onClick={() => track("pricing_cta_click", { plan: plan.id })}
|
|
>
|
|
{plan.cta}
|
|
<ArrowRight className="size-3.5" />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex w-full max-w-6xl flex-col items-center justify-between gap-3 rounded-xl border border-border bg-muted/40 px-6 py-4 text-center sm:flex-row sm:text-left">
|
|
<div>
|
|
<p className="text-sm font-semibold text-foreground">
|
|
Enterprise: custom volume, custom LLM routing, SSO and dedicated infrastructure.
|
|
</p>
|
|
<p className="mt-0.5 text-xs text-muted-foreground">
|
|
Volume pricing, custom SLAs and on-prem options.
|
|
</p>
|
|
</div>
|
|
<Button asChild variant="outline" size="sm">
|
|
<a href="mailto:sales@wordly.art?subject=Enterprise%20inquiry">
|
|
Contact Sales
|
|
</a>
|
|
</Button>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|