Files
SnapOtter/apps/landing/src/pages/enterprise.astro
T
SnapOtter 8403222d08 feat(landing): migrate from Next.js to Astro 5
Zero client JS, 165 static pages, self-hosted fonts, Otter Orange
design system. Includes tool SEO data for all 157 tools, enterprise
page, and updated e2e landing tests.
2026-06-14 16:50:20 +08:00

429 lines
24 KiB
Plaintext

---
import { TOOLS } from "@snapotter/shared";
import Footer from "@/components/Footer.astro";
import JsonLd from "@/components/JsonLd.astro";
import Navbar from "@/components/Navbar.astro";
import Base from "@/layouts/Base.astro";
const toolCount = TOOLS.length;
const breadcrumbJsonLd = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: [
{ "@type": "ListItem", position: 1, name: "Home", item: "https://snapotter.com" },
{
"@type": "ListItem",
position: 2,
name: "Enterprise",
item: "https://snapotter.com/enterprise",
},
],
};
const productJsonLd = {
"@context": "https://schema.org",
"@type": "Product",
name: "SnapOtter Enterprise",
description: `Enterprise-grade self-hosted file processing suite with ${toolCount} tools, SAML SSO, audit logging, per-tool permissions, and multi-tenancy.`,
brand: { "@type": "Organization", name: "SnapOtter" },
url: "https://snapotter.com/enterprise",
offers: {
"@type": "Offer",
priceCurrency: "USD",
availability: "https://schema.org/InStock",
url: "https://snapotter.com/contact",
},
applicationCategory: "UtilitiesApplication",
operatingSystem: "Docker, Linux, Kubernetes",
};
const whySelfHosted = [
{
title: "Compliance by Architecture",
description:
"GDPR, HIPAA, CCPA. Your files never leave your network. No data processing agreements needed with third parties.",
icon: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
},
{
title: "Zero External Dependencies",
description:
"No API keys, no cloud services, no internet required. Works in air-gapped environments and classified networks.",
icon: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
},
{
title: "Total Cost Control",
description:
"No per-user pricing, no per-file charges. Deploy once, use unlimited. Your hardware, your capacity.",
icon: '<line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>',
},
];
const identityProviders = ["Okta", "Azure AD", "Google Workspace", "Any OIDC Provider"];
const permissionMatrix = {
roles: ["Admin", "Editor", "User"],
features: ["All Tools", "Pipelines", "API Keys", "User Mgmt", "Audit Log"],
access: [
[true, true, true, true, true],
[true, true, true, false, false],
[true, false, false, false, false],
],
};
const storageProviders = [
{ name: "AWS S3", color: "text-[#FF9900]" },
{ name: "MinIO", color: "text-[#C72C48]" },
{ name: "Cloudflare R2", color: "text-[#F6821F]" },
{ name: "Local Storage", color: "text-primary" },
];
const deploymentTargets = [
{
name: "Docker",
icon: '<path d="M13.98 11.08h2.12a.19.19 0 0 0 .19-.19V9.01a.19.19 0 0 0-.19-.19h-2.12a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m-2.95-5.43h2.12a.19.19 0 0 0 .19-.19V3.58a.19.19 0 0 0-.19-.19h-2.12a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m0 2.71h2.12a.19.19 0 0 0 .19-.19V6.29a.19.19 0 0 0-.19-.19h-2.12a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m-2.93 0h2.12a.19.19 0 0 0 .19-.19V6.29a.19.19 0 0 0-.19-.19H8.1a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m-2.96 0h2.12a.19.19 0 0 0 .19-.19V6.29a.19.19 0 0 0-.19-.19H5.14a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m5.89 2.72h2.12a.19.19 0 0 0 .19-.19V9.01a.19.19 0 0 0-.19-.19h-2.12a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m-2.93 0h2.12a.19.19 0 0 0 .19-.19V9.01a.19.19 0 0 0-.19-.19H8.1a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m-2.96 0h2.12a.19.19 0 0 0 .19-.19V9.01a.19.19 0 0 0-.19-.19H5.14a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19m-2.92 0h2.12a.19.19 0 0 0 .19-.19V9.01a.19.19 0 0 0-.19-.19H2.22a.19.19 0 0 0-.19.19v1.88c0 .1.09.19.19.19M24 11.52c-.48-.41-1.59-.55-2.44-.38-.11-.79-.56-1.47-1.09-2.04l-.22-.2-.21.2c-.44.42-.7.99-.79 1.59a2.36 2.36 0 0 0 .33 1.72 3.46 3.46 0 0 1-1.62.4H.57a.56.56 0 0 0-.57.56c.02 2.19.36 4.37 1.37 6.3 1.1 2.08 2.83 3.44 5.28 4.14A17.5 17.5 0 0 0 10 24c2.19 0 4.45-.34 6.38-1.45 1.57-.9 2.83-2.27 3.68-3.93.57.04 1.81.06 3.04-.71.06-.04.14-.11.2-.17l.1-.17-.4-.25z"/>',
},
{
name: "Kubernetes",
icon: '<path d="M10.204 14.35l.007.01-.999 2.413a5.17 5.17 0 0 1-2.075-2.597l2.578-.437.004.005a.44.44 0 0 1 .484.606zm-.833-2.129a.44.44 0 0 0 .173-.756l.002-.011L7.585 9.7a5.17 5.17 0 0 0-.238 3.24l1.938-1.04.086.32zm1.926-1.621a.44.44 0 0 0 .752-.19h.01l.693-2.555a5.17 5.17 0 0 0-2.81 1.395l1.355 1.35zm1.474.639a.44.44 0 0 0-.064.776l-.003.01 1.956 1.753a5.17 5.17 0 0 0 .237-3.24l-2.127.7zm-.635 1.965a.44.44 0 0 0-.753.19h-.01l-.692 2.554a5.17 5.17 0 0 0 2.81-1.396l-1.355-1.348zM12 5.5a6.5 6.5 0 1 1 0 13 6.5 6.5 0 0 1 0-13zm0-2a8.5 8.5 0 1 0 0 17 8.5 8.5 0 0 0 0-17z"/>',
},
{
name: "AWS",
icon: '<path d="M6.763 10.036c0 .296.032.535.088.71.064.176.144.368.256.576.04.063.056.127.056.183 0 .08-.048.16-.152.24l-.503.335a.383.383 0 0 1-.208.072c-.08 0-.16-.04-.239-.112a2.47 2.47 0 0 1-.287-.375 6.18 6.18 0 0 1-.248-.471c-.622.734-1.405 1.101-2.347 1.101-.67 0-1.205-.191-1.596-.574-.391-.384-.59-.894-.59-1.533 0-.678.239-1.23.726-1.644.487-.415 1.133-.623 1.955-.623.272 0 .551.024.846.064.296.04.6.104.918.176v-.583c0-.607-.127-1.03-.375-1.277-.255-.248-.686-.367-1.3-.367-.28 0-.568.032-.863.104-.296.072-.583.16-.862.272a2.287 2.287 0 0 1-.28.104.488.488 0 0 1-.127.024c-.112 0-.168-.08-.168-.247v-.391c0-.128.016-.224.056-.28a.597.597 0 0 1 .224-.167c.279-.144.614-.264 1.005-.36a4.84 4.84 0 0 1 1.246-.151c.95 0 1.644.216 2.091.647.44.43.662 1.085.662 1.963v2.586zm-3.24 1.214c.263 0 .534-.048.822-.144.287-.096.543-.271.758-.51.128-.152.224-.32.272-.512.047-.191.08-.423.08-.694v-.335a6.66 6.66 0 0 0-.735-.136 6.02 6.02 0 0 0-.75-.048c-.535 0-.926.104-1.19.32-.263.215-.39.518-.39.917 0 .375.095.655.295.846.191.2.47.296.838.296zm6.41.862c-.144 0-.24-.024-.304-.08-.064-.048-.12-.16-.168-.311L7.586 5.55a1.398 1.398 0 0 1-.072-.32c0-.128.064-.2.191-.2h.783c.151 0 .255.025.31.08.065.048.113.16.16.312l1.342 5.284 1.245-5.284c.04-.16.088-.264.151-.312a.549.549 0 0 1 .32-.08h.638c.152 0 .256.025.32.08.063.048.12.16.151.312l1.261 5.348 1.381-5.348c.048-.16.104-.264.16-.312a.52.52 0 0 1 .311-.08h.743c.128 0 .2.064.2.2 0 .04-.009.08-.017.128a1.137 1.137 0 0 1-.056.2l-1.923 6.17c-.048.16-.104.264-.168.312a.52.52 0 0 1-.303.08h-.687c-.151 0-.255-.024-.32-.08-.063-.056-.119-.16-.15-.32l-1.238-5.148-1.23 5.14c-.04.16-.087.264-.15.32-.065.056-.177.08-.32.08zm10.256.215c-.415 0-.83-.048-1.229-.143-.399-.096-.71-.2-.918-.32-.128-.071-.215-.151-.247-.223a.563.563 0 0 1-.048-.224v-.407c0-.167.064-.247.183-.247.048 0 .096.008.144.024.048.016.12.048.2.08.271.12.566.215.878.279.319.064.63.096.95.096.502 0 .894-.088 1.165-.264a.86.86 0 0 0 .415-.758.777.777 0 0 0-.215-.559c-.144-.151-.415-.287-.806-.399l-1.157-.36c-.583-.183-1.013-.455-1.277-.815a1.903 1.903 0 0 1-.4-1.157c0-.335.073-.63.216-.886.144-.255.336-.479.575-.654.24-.184.51-.32.83-.415A3.64 3.64 0 0 1 18.2 6.2c.176 0 .359.008.535.032.183.024.35.056.518.088.16.04.312.08.455.127.144.048.256.096.336.144a.69.69 0 0 1 .24.2.43.43 0 0 1 .071.263v.375c0 .168-.064.256-.184.256a.83.83 0 0 1-.303-.096 3.652 3.652 0 0 0-1.532-.311c-.455 0-.815.071-1.062.223-.248.152-.375.383-.375.695 0 .224.08.416.24.567.16.152.455.304.878.44l1.134.358c.574.184.99.44 1.237.767.247.327.367.702.367 1.117 0 .343-.072.655-.207.926-.144.272-.336.511-.583.703-.248.2-.543.343-.886.447-.36.111-.742.167-1.142.167z"/><path d="M21.698 16.793c-2.812 2.08-6.89 3.187-10.399 3.187-4.92 0-9.35-1.82-12.7-4.843-.263-.24-.032-.567.287-.383 3.616 2.104 8.09 3.372 12.708 3.372 3.117 0 6.538-.647 9.693-1.987.471-.208.87.311.41.654z"/><path d="M22.874 15.416c-.36-.463-2.38-.223-3.29-.112-.272.032-.312-.208-.063-.383 1.605-1.133 4.245-.806 4.55-.423.304.391-.08 3.1-1.589 4.39-.232.2-.455.096-.35-.167.343-.855 1.102-2.75.742-3.305z"/>',
},
{
name: "Azure",
icon: '<path d="M5.483 21.3H14.3L8.544 8.947l5.963-6.407H6.626L.008 15.29zm3.694-2.092l7.913-9.153 2.422 5.724-10.335 3.43z"/>',
},
{
name: "Linux",
icon: '<path d="M12.504 0c-.155 0-.315.008-.48.021-4.226.333-3.105 4.807-3.17 6.298-.076 1.092-.3 1.953-1.05 3.02-.885 1.051-2.127 2.75-2.716 4.521-.278.832-.41 1.684-.287 2.489a.424.424 0 0 0-.11.135c-.26.268-.45.6-.663.839-.199.199-.485.267-.797.4-.313.136-.658.269-.864.68-.09.189-.136.394-.132.602 0 .199.027.4.055.536.058.399.116.728.04.97-.249.68-.28 1.145-.106 1.484.174.334.535.47.94.601.81.2 1.91.135 2.774.6.926.466 1.866.67 2.616.47.526-.116.97-.464 1.208-.946.587-.003 1.23-.269 2.26-.334.699-.058 1.574.267 2.577.2.025.134.063.198.114.333l.003.003c.391.778 1.113 1.368 1.884 1.43.868.065 1.157-.45 1.354-.962.088-.233.167-.468.157-.707.003-.07-.003-.14-.004-.208.138-.071.233-.14.36-.252.126-.113.243-.276.32-.415.158-.263.245-.525.206-.8-.04-.26-.132-.48-.263-.695a3.3 3.3 0 0 1-.12-.198c-.048-.097-.085-.155-.085-.155a1.047 1.047 0 0 0-.32-.397 1.58 1.58 0 0 0-.402-.22c-.168-.067-.371-.128-.576-.18a7.09 7.09 0 0 0-.207-.04c-.02-.005-.04-.01-.057-.014-.159-.037-.32-.068-.385-.073-.238-.017-.398.008-.553.066-.116.043-.236.114-.378.236-.158-.19-.248-.358-.36-.597-.112-.24-.226-.515-.408-.814-.182-.304-.408-.593-.706-.799-.295-.21-.637-.334-.974-.379a3.294 3.294 0 0 0-1.002.007c.118-.607.098-1.252-.142-1.873-.237-.616-.624-1.178-1.1-1.654-.476-.474-1.005-.875-1.482-1.243-.717-.552-1.32-1.06-1.55-1.675-.115-.305-.14-.63-.058-.958.082-.327.268-.647.572-.985l.005-.003c.394-.408.666-.75.864-1.05.196-.3.32-.56.386-.805.133-.484.196-.965.165-1.44-.029-.475-.137-.95-.308-1.43-.341-.961-.828-1.934-1.094-2.893-.266-.954-.292-1.87.155-2.694.242-.47.597-.85 1.036-1.126.44-.278.963-.456 1.556-.508.1-.008.2-.013.3-.013z"/>',
},
{
name: "Air-Gapped",
icon: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
},
];
const comparisonRows = [
{
label: "Data Privacy",
cloud: "Files uploaded to third-party servers",
self: "Files never leave your network",
},
{
label: "Compliance",
cloud: "Requires DPAs, vendor assessments",
self: "Compliant by architecture",
},
{
label: "Cost Model",
cloud: "Per-file or per-API-call pricing",
self: "Unlimited usage, fixed infrastructure cost",
},
{
label: "Internet Required",
cloud: "Always online",
self: "Works fully offline, air-gapped",
},
{
label: "Vendor Lock-in",
cloud: "Proprietary APIs, migration pain",
self: "Open source, standard formats, full API",
},
{
label: "AI Models",
cloud: "External API calls (OpenAI, etc.)",
self: "15 local models, no API keys",
},
{
label: "Customization",
cloud: "Limited to vendor offerings",
self: "Full source code access",
},
];
const checkSvg =
'<svg class="h-4 w-4 shrink-0 text-success" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';
const xSvg =
'<svg class="h-4 w-4 shrink-0 text-danger/60" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
---
<Base
title="Enterprise | SnapOtter"
description="Enterprise-grade self-hosted file processing. SAML SSO, audit logging, per-tool permissions, multi-tenancy. Deploy on your infrastructure."
canonical="https://snapotter.com/enterprise"
>
<Fragment slot="head">
<JsonLd data={[breadcrumbJsonLd, productJsonLd]} />
</Fragment>
<Navbar />
<main id="main">
<!-- ─── HERO (dark) ─── -->
<section class="section-dark bg-noise relative px-6 pt-32 pb-20 md:pt-44 md:pb-28">
<div class="relative mx-auto max-w-4xl text-center">
<!-- Breadcrumb -->
<nav class="animate-fade-up mb-8 text-sm text-dark-muted" aria-label="Breadcrumb">
<a href="/" class="transition-colors hover:text-dark-fg">Home</a>
<span class="mx-2">/</span>
<span class="text-dark-fg">Enterprise</span>
</nav>
<h1 class="animate-fade-up font-display text-4xl font-extrabold tracking-tight text-dark-fg sm:text-5xl md:text-6xl">
Enterprise-grade file processing.<br class="hidden sm:block" />
<span class="text-primary-light">On your infrastructure.</span>
</h1>
<p class="animate-fade-up mx-auto mt-6 max-w-2xl text-lg text-dark-muted md:text-xl" style="animation-delay: 0.1s;">
SAML SSO. Audit logging. Per-tool permissions. Multi-tenancy. All self-hosted, all open source at its core.
</p>
<div class="animate-fade-up mt-10 flex flex-col items-center justify-center gap-3 sm:flex-row" style="animation-delay: 0.2s;">
<a href="/contact" class="btn-primary rounded-xl px-8 py-3.5 text-base font-semibold">
Book a Demo
</a>
<a href="/#pricing" class="rounded-xl border border-dark-border px-8 py-3.5 text-base font-semibold text-dark-fg transition-colors hover:border-primary hover:bg-primary/10">
View Pricing
</a>
</div>
</div>
</section>
<!-- ─── WHY SELF-HOSTED ─── -->
<section class="px-6 py-20 md:py-28">
<div class="reveal mx-auto mb-12 max-w-3xl text-center md:mb-16">
<h2 class="font-display text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl">
Why enterprises choose self-hosted.
</h2>
<p class="mt-4 text-lg text-muted md:text-xl">
Keep full control over your data, your costs, and your compliance posture.
</p>
</div>
<div class="mx-auto grid max-w-5xl gap-6 sm:grid-cols-3">
{whySelfHosted.map((card, i) => (
<div class:list={["reveal card-hover rounded-xl border border-border bg-surface p-6", `reveal-delay-${i + 1}`]}>
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
<svg class="h-5 w-5 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" set:html={card.icon} />
</div>
<h3 class="mt-4 font-display text-lg font-bold">{card.title}</h3>
<p class="mt-2 text-sm leading-relaxed text-muted">{card.description}</p>
</div>
))}
</div>
</section>
<!-- ─── ENTERPRISE FEATURES (alternating rows) ─── -->
<section class="px-6 py-20 md:py-28">
<div class="reveal mx-auto mb-12 max-w-3xl text-center md:mb-16">
<h2 class="font-display text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl">
Every feature your team needs.
</h2>
<p class="mt-4 text-lg text-muted md:text-xl">
Security, compliance, and deployment capabilities built for enterprise requirements.
</p>
</div>
<div class="mx-auto max-w-6xl space-y-20 md:space-y-28">
<!-- Row 1: Identity & Access — text left, visual right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-1">
<h3 class="font-display text-2xl font-bold tracking-tight sm:text-3xl">
Single sign-on for your entire team
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
SAML 2.0 and OIDC integration with any identity provider. Okta, Azure AD, Google Workspace, and more. SCIM provisioning for automated user lifecycle management. Enforce MFA across your organization.
</p>
</div>
<div class="md:order-2">
<div class="rounded-xl border border-border bg-surface p-6">
<span class="mb-4 block text-xs font-semibold uppercase tracking-wider text-primary">Supported Providers</span>
<div class="flex flex-wrap gap-2.5">
{identityProviders.map((provider) => (
<span class="inline-flex items-center gap-1.5 rounded-full border border-primary/20 bg-primary-subtle px-3.5 py-1.5 text-sm font-medium text-primary-dark">
<svg class="h-3.5 w-3.5 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
{provider}
</span>
))}
</div>
<div class="mt-4 flex items-center gap-2 rounded-lg bg-primary-subtle/50 px-3 py-2">
<svg class="h-4 w-4 shrink-0 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
<span class="text-xs text-primary-dark">MFA enforced organization-wide</span>
</div>
</div>
</div>
</div>
<!-- Row 2: Permissions & Audit — visual left, text right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-2">
<h3 class="font-display text-2xl font-bold tracking-tight sm:text-3xl">
Granular control. Complete visibility.
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
Role-based access control with admin, editor, user, and custom roles. Per-tool permissions let you restrict which tools each team can access. Every action is logged with full audit trails exportable for SOC 2, ISO 27001, and compliance reporting.
</p>
</div>
<div class="md:order-1">
<div class="rounded-xl border border-border bg-surface p-6 overflow-x-auto">
<span class="mb-4 block text-xs font-semibold uppercase tracking-wider text-primary">Permission Matrix</span>
<table class="w-full text-xs">
<thead>
<tr>
<th class="pb-2 text-start font-semibold text-muted">Role</th>
{permissionMatrix.features.map((f) => (
<th class="pb-2 text-center font-semibold text-muted">{f}</th>
))}
</tr>
</thead>
<tbody>
{permissionMatrix.roles.map((role, ri) => (
<tr class="border-t border-border">
<td class="py-2 font-medium">{role}</td>
{permissionMatrix.access[ri].map((allowed) => (
<td class="py-2 text-center">
{allowed
? <Fragment set:html={checkSvg.replace('class="', 'class="mx-auto ')} />
: <Fragment set:html={xSvg.replace('class="', 'class="mx-auto ')} />
}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
<!-- Row 3: Storage & Infrastructure — text left, visual right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-1">
<h3 class="font-display text-2xl font-bold tracking-tight sm:text-3xl">
Your storage. Your rules.
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
Use local storage or connect any S3-compatible backend: AWS S3, MinIO, Cloudflare R2, Google Cloud Storage. Multi-tenancy isolates teams with separate workspaces, configurations, and storage paths.
</p>
</div>
<div class="md:order-2">
<div class="grid grid-cols-2 gap-3">
{storageProviders.map((provider) => (
<div class="card-hover flex flex-col items-center gap-2.5 rounded-xl border border-border bg-surface p-5">
<svg class:list={["h-8 w-8", provider.color]} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
</svg>
<span class="text-xs font-semibold">{provider.name}</span>
</div>
))}
</div>
</div>
</div>
<!-- Row 4: Deployment — visual left, text right -->
<div class="reveal grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center">
<div class="md:order-2">
<h3 class="font-display text-2xl font-bold tracking-tight sm:text-3xl">
Deploy anywhere. Scale as needed.
</h3>
<p class="mt-4 text-base leading-relaxed text-muted">
Single Docker container bundles everything: PostgreSQL, Redis, FFmpeg, LibreOffice, AI models. Deploy on Kubernetes with Helm charts, bare metal, or cloud VMs. ARM and x86 architectures. Air-gapped network support.
</p>
</div>
<div class="md:order-1">
<div class="grid grid-cols-3 gap-3">
{deploymentTargets.map((target) => (
<div class="card-hover flex flex-col items-center gap-2 rounded-xl border border-border bg-surface p-4">
{target.name === "Air-Gapped" ? (
<svg class="h-8 w-8 text-primary" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" set:html={target.icon} />
) : (
<svg class="h-8 w-8 text-foreground" viewBox="0 0 24 24" fill="currentColor" set:html={target.icon} />
)}
<span class="text-xs font-medium">{target.name}</span>
</div>
))}
</div>
</div>
</div>
</div>
</section>
<!-- ─── COMPARISON TABLE ─── -->
<section class="bg-background-alt px-6 py-20 md:py-28">
<div class="reveal mx-auto mb-12 max-w-3xl text-center md:mb-16">
<h2 class="font-display text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl">
SnapOtter vs. Cloud Processing
</h2>
<p class="mt-4 text-lg text-muted md:text-xl">
See why teams choose self-hosted file processing over cloud alternatives.
</p>
</div>
<div class="reveal mx-auto max-w-4xl overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr>
<th class="pb-4 text-start text-sm font-semibold text-muted"></th>
<th class="pb-4 text-start text-sm font-semibold text-muted">Cloud Processing</th>
<th class="pb-4 text-start text-sm font-semibold">
<span class="text-primary">SnapOtter</span> <span class="text-muted font-normal">(Self-Hosted)</span>
</th>
</tr>
</thead>
<tbody>
{comparisonRows.map((row) => (
<tr class="border-t border-border">
<td class="py-4 pe-6 font-medium">{row.label}</td>
<td class="py-4 pe-6">
<div class="flex items-start gap-2">
<Fragment set:html={xSvg} />
<span class="text-muted">{row.cloud}</span>
</div>
</td>
<td class="py-4">
<div class="flex items-start gap-2">
<Fragment set:html={checkSvg} />
<span>{row.self}</span>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
<!-- ─── CTA (dark) ─── -->
<section class="section-dark bg-noise relative px-6 py-20 md:py-28">
<div class="reveal relative mx-auto max-w-3xl text-center">
<h2 class="font-display text-3xl font-bold tracking-tight text-dark-fg sm:text-4xl md:text-5xl">
Ready to deploy?
</h2>
<p class="mt-4 text-lg text-dark-muted md:text-xl">
Talk to our team about enterprise licensing, deployment assistance, and custom integrations.
</p>
<div class="mt-10">
<a href="/contact" class="btn-primary inline-block rounded-xl px-10 py-4 text-base font-semibold">
Book a Demo
</a>
</div>
<p class="mt-6 text-sm text-dark-muted">
Or email us at <a href="mailto:contact@snapotter.com" class="font-medium text-primary-light hover:underline">contact@snapotter.com</a>
</p>
</div>
</section>
</main>
<Footer />
</Base>