mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-28 07:09:50 +00:00
Replace hardcoded 3-tier pricing with dynamic fetch from pricing.hanzo.ai/v1/pricing/cloud/plans. Shows all 6 canonical PaaS compute tiers (Starter $6 through Enterprise $384) with real vCPU/RAM/disk specs and hourly rates. Static fallback ensures the page renders even when the API is unreachable.
643 lines
24 KiB
TypeScript
643 lines
24 KiB
TypeScript
import Link from 'next/link';
|
|
import { cn } from '@/lib/cn';
|
|
import { cva } from 'class-variance-authority';
|
|
import { fetchComputePlans, type ComputePlan } from '@/lib/pricing';
|
|
import {
|
|
DatabaseIcon,
|
|
ShieldCheckIcon,
|
|
ZapIcon,
|
|
CloudIcon,
|
|
HardDriveIcon,
|
|
CodeIcon,
|
|
TerminalIcon,
|
|
ServerIcon,
|
|
RefreshCwIcon,
|
|
LockIcon,
|
|
FolderIcon,
|
|
ArrowRightIcon,
|
|
CheckIcon,
|
|
MessageCircleIcon,
|
|
CpuIcon,
|
|
} from 'lucide-react';
|
|
|
|
const headingVariants = cva('font-medium tracking-tight', {
|
|
variants: {
|
|
variant: {
|
|
h1: 'text-4xl lg:text-5xl xl:text-6xl',
|
|
h2: 'text-3xl lg:text-4xl',
|
|
h3: 'text-xl lg:text-2xl',
|
|
},
|
|
},
|
|
});
|
|
|
|
const buttonVariants = cva(
|
|
'inline-flex justify-center px-5 py-3 rounded-full font-medium tracking-tight transition-colors',
|
|
{
|
|
variants: {
|
|
variant: {
|
|
primary: 'bg-fd-foreground text-fd-background hover:bg-fd-foreground/90',
|
|
secondary: 'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent',
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
variant: 'primary',
|
|
},
|
|
},
|
|
);
|
|
|
|
const cardVariants = cva('rounded-2xl text-sm p-6 bg-origin-border shadow-lg', {
|
|
variants: {
|
|
variant: {
|
|
secondary: 'bg-fd-muted text-fd-foreground',
|
|
default: 'border bg-fd-card',
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
variant: 'default',
|
|
},
|
|
});
|
|
|
|
function formatPrice(cents: number): string {
|
|
return cents % 1 === 0 ? `$${cents}` : `$${cents.toFixed(0)}`
|
|
}
|
|
|
|
function planSpecs(plan: ComputePlan): string {
|
|
return `${plan.vcpus} ${plan.cpuType} vCPU${plan.vcpus > 1 ? 's' : ''}, ${plan.memoryGB} GB RAM, ${plan.diskGB} GB disk`
|
|
}
|
|
|
|
export default async function Page() {
|
|
const plans = await fetchComputePlans()
|
|
|
|
return (
|
|
<main className="text-fd-foreground pt-4 pb-6 md:pb-12">
|
|
{/* Hero Section */}
|
|
<div className="relative flex min-h-[600px] h-[70vh] max-h-[900px] border rounded-2xl overflow-hidden mx-auto w-full max-w-[1400px]">
|
|
<div className="absolute inset-0 bg-gradient-to-br from-fd-muted/50 via-transparent to-fd-muted/30" />
|
|
<div className="flex flex-col z-2 px-4 size-full md:p-12 max-md:items-center max-md:text-center">
|
|
<div className="flex items-center gap-2 mt-12 text-xs font-medium rounded-full p-2 border w-fit">
|
|
<DatabaseIcon className="size-4" />
|
|
Open-Source Realtime Backend
|
|
</div>
|
|
<h1 className={cn(headingVariants({ variant: 'h1' }), 'my-8 leading-tight')}>
|
|
One Binary.
|
|
<br />
|
|
Auth, DB, API.
|
|
</h1>
|
|
<p className="text-lg text-fd-muted-foreground max-w-2xl mb-8">
|
|
Auth, database, realtime subscriptions, file storage, and server-side functions
|
|
compiled into a single Go binary. SQLite locally, PostgreSQL in production.
|
|
REST API auto-generated from your schema.
|
|
</p>
|
|
<div className="flex flex-row items-center justify-center gap-4 flex-wrap w-fit">
|
|
<Link href="/docs" className={cn(buttonVariants(), 'max-sm:text-sm')}>
|
|
Get Started
|
|
</Link>
|
|
<a
|
|
href="https://github.com/hanzoai/base"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
className={cn(buttonVariants({ variant: 'secondary' }), 'max-sm:text-sm')}
|
|
>
|
|
View on GitHub
|
|
</a>
|
|
</div>
|
|
|
|
{/* Terminal Preview */}
|
|
<div className="mt-12 w-full max-w-3xl mx-auto">
|
|
<div className="bg-fd-card border rounded-xl overflow-hidden shadow-2xl">
|
|
<div className="flex items-center gap-2 px-4 py-2 border-b bg-fd-muted/50">
|
|
<div className="flex gap-1.5">
|
|
<div className="size-3 rounded-full bg-red-500/80" />
|
|
<div className="size-3 rounded-full bg-yellow-500/80" />
|
|
<div className="size-3 rounded-full bg-green-500/80" />
|
|
</div>
|
|
<span className="text-xs text-fd-muted-foreground ml-2">terminal</span>
|
|
</div>
|
|
<pre className="p-4 text-sm overflow-x-auto font-mono">
|
|
<code className="text-fd-muted-foreground">{`$ `}<span className="text-fd-foreground">base serve</span>{`
|
|
> Server started at http://127.0.0.1:8090
|
|
- REST API: /api/
|
|
- Realtime: /api/realtime
|
|
- Admin UI: /_/
|
|
- Auth: /api/collections/users/auth-with-password
|
|
|
|
$ `}<span className="text-fd-foreground">curl localhost:8090/api/collections/tasks/records</span>{`
|
|
{
|
|
"items": [...],
|
|
"totalItems": 42,
|
|
"totalPages": 5
|
|
}`}</code>
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Features Grid */}
|
|
<div className="grid grid-cols-1 gap-10 mt-12 px-6 mx-auto w-full max-w-[1400px] md:px-12 lg:grid-cols-2">
|
|
<p className="text-2xl tracking-tight leading-snug font-light col-span-full md:text-3xl xl:text-4xl">
|
|
<span className="font-medium">Database, auth, files, realtime, and functions</span> in
|
|
a <span className="font-medium">~15 MB binary</span> with zero external dependencies.
|
|
</p>
|
|
|
|
{/* Database */}
|
|
<div className={cn(cardVariants())}>
|
|
<DatabaseIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-4' }))}>
|
|
Database
|
|
</h3>
|
|
<p className="text-fd-muted-foreground mb-4">
|
|
SQLite for local dev, PostgreSQL for production. REST API auto-generated
|
|
from your schema with filtering, sorting, pagination, and relation expansion.
|
|
</p>
|
|
<pre className="bg-fd-muted/30 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`// Create a record
|
|
const record = await base.collection('tasks').create({
|
|
title: 'Ship it',
|
|
status: 'active',
|
|
assignee: userId
|
|
});
|
|
|
|
// Query with filters
|
|
const tasks = await base.collection('tasks').getList(1, 20, {
|
|
filter: 'status = "active" && assignee = "' + userId + '"',
|
|
sort: '-created',
|
|
expand: 'assignee'
|
|
});`}
|
|
</pre>
|
|
</div>
|
|
|
|
{/* Realtime */}
|
|
<div className={cn(cardVariants({ variant: 'secondary' }))}>
|
|
<RefreshCwIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-4' }))}>
|
|
Realtime Subscriptions
|
|
</h3>
|
|
<p className="mb-4">
|
|
Subscribe to collection changes over SSE. Each create, update, or delete
|
|
event is broadcast with the full record payload. Client SDKs handle
|
|
reconnection and deduplication.
|
|
</p>
|
|
<pre className="bg-black/10 dark:bg-white/5 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`// Subscribe to all changes in a collection
|
|
base.collection('messages').subscribe('*', (e) => {
|
|
console.log(e.action); // 'create' | 'update' | 'delete'
|
|
console.log(e.record); // the changed record
|
|
});
|
|
|
|
// Subscribe to a single record by ID
|
|
base.collection('tasks').subscribe(recordId, (e) => {
|
|
updateUI(e.record);
|
|
});`}
|
|
</pre>
|
|
</div>
|
|
|
|
{/* Auth */}
|
|
<div className={cn(cardVariants())}>
|
|
<ShieldCheckIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-4' }))}>
|
|
Authentication
|
|
</h3>
|
|
<p className="text-fd-muted-foreground mb-4">
|
|
Built-in auth with email/password, OAuth2 providers, and Hanzo IAM integration.
|
|
Per-collection API rules define read/write/create/delete access using
|
|
filter expressions.
|
|
</p>
|
|
<div className="grid grid-cols-2 gap-4 text-sm">
|
|
<div className="p-3 rounded-lg border">
|
|
<p className="font-medium">Email/Password</p>
|
|
<p className="text-xs text-fd-muted-foreground">With email verification flow</p>
|
|
</div>
|
|
<div className="p-3 rounded-lg border">
|
|
<p className="font-medium">OAuth2</p>
|
|
<p className="text-xs text-fd-muted-foreground">Google, GitHub, Apple, 10+ others</p>
|
|
</div>
|
|
<div className="p-3 rounded-lg border">
|
|
<p className="font-medium">Hanzo IAM</p>
|
|
<p className="text-xs text-fd-muted-foreground">SSO via hanzo.id, SAML/OIDC</p>
|
|
</div>
|
|
<div className="p-3 rounded-lg border">
|
|
<p className="font-medium">API Rules</p>
|
|
<p className="text-xs text-fd-muted-foreground">Filter-based access per collection</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* File Storage */}
|
|
<div className={cn(cardVariants())}>
|
|
<FolderIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-4' }))}>
|
|
File Storage
|
|
</h3>
|
|
<p className="text-fd-muted-foreground mb-4">
|
|
Attach files to any collection record. Local filesystem in dev mode,
|
|
S3-compatible object storage in production. Generates thumbnails on read
|
|
via query parameters.
|
|
</p>
|
|
<pre className="bg-fd-muted/30 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`// Upload a file
|
|
const formData = new FormData();
|
|
formData.append('document', file);
|
|
formData.append('title', 'Report Q4');
|
|
|
|
const record = await base.collection('documents')
|
|
.create(formData);
|
|
|
|
// Get file URL (with optional thumb params)
|
|
const url = base.files.getURL(record, record.document);`}
|
|
</pre>
|
|
</div>
|
|
|
|
{/* Cloud Functions */}
|
|
<div className={cn(cardVariants({ variant: 'secondary' }), 'col-span-full')}>
|
|
<CloudIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-6' }))}>
|
|
Server-Side Functions
|
|
</h3>
|
|
<p className="mb-6 max-w-2xl">
|
|
JavaScript hooks execute before or after any API operation. Define
|
|
custom HTTP routes, cron jobs, and event-driven logic in the embedded
|
|
JSVM runtime. No separate function deployment.
|
|
</p>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<div>
|
|
<h4 className="font-medium mb-2">Hooks</h4>
|
|
<pre className="bg-black/10 dark:bg-white/5 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`onRecordCreate((e) => {
|
|
// Runs before record is saved
|
|
e.record.set('slug',
|
|
slugify(e.record.get('title'))
|
|
);
|
|
});`}
|
|
</pre>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium mb-2">Custom Routes</h4>
|
|
<pre className="bg-black/10 dark:bg-white/5 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`routerAdd("POST", "/webhook",
|
|
(e) => {
|
|
const body = e.requestBody();
|
|
// Process incoming payload
|
|
return e.json(200,
|
|
{ ok: true }
|
|
);
|
|
}
|
|
);`}
|
|
</pre>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium mb-2">Cron Jobs</h4>
|
|
<pre className="bg-black/10 dark:bg-white/5 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`cronAdd("daily cleanup",
|
|
"0 3 * * *",
|
|
() => {
|
|
// Runs at 03:00 UTC daily
|
|
const old = findRecords(
|
|
"logs", "created < -30d"
|
|
);
|
|
deleteRecords("logs", old);
|
|
}
|
|
);`}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* SDKs */}
|
|
<div className={cn(cardVariants(), 'col-span-full')}>
|
|
<CodeIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-6' }))}>
|
|
Client SDKs
|
|
</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<div>
|
|
<h4 className="font-medium mb-2">JavaScript / TypeScript</h4>
|
|
<pre className="bg-fd-muted/30 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`import { BaseClient } from '@hanzoai/base'
|
|
|
|
const base = new BaseClient('http://localhost:8090')
|
|
|
|
// Auth
|
|
await base.collection('users')
|
|
.authWithPassword(email, pass)
|
|
|
|
// CRUD with type safety
|
|
const tasks = await base.collection('tasks')
|
|
.getFullList<Task>({
|
|
sort: '-created',
|
|
expand: 'assignee'
|
|
})`}
|
|
</pre>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium mb-2">Go</h4>
|
|
<pre className="bg-fd-muted/30 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`import "github.com/hanzoai/base"
|
|
|
|
app := base.New()
|
|
|
|
app.OnRecordCreate("tasks").
|
|
BindFunc(func(e *core.RecordEvent) error {
|
|
// Custom logic
|
|
return e.Next()
|
|
})
|
|
|
|
app.Start()`}
|
|
</pre>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium mb-2">Dart / Flutter</h4>
|
|
<pre className="bg-fd-muted/30 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`import 'package:hanzo_base/hanzo_base.dart';
|
|
|
|
final base = BaseClient('http://localhost:8090');
|
|
|
|
// Realtime subscription
|
|
base.collection('messages')
|
|
.subscribe('*', (e) {
|
|
print(e.action);
|
|
print(e.record);
|
|
});`}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Architecture: Dev vs Prod */}
|
|
<div className={cn(cardVariants(), 'col-span-full')}>
|
|
<ServerIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-6' }))}>
|
|
Dev to Prod
|
|
</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
<div className="p-6 rounded-xl border">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<TerminalIcon className="size-5" />
|
|
<h4 className="font-medium text-lg">Local Development</h4>
|
|
</div>
|
|
<p className="text-fd-muted-foreground text-sm mb-4">
|
|
Single binary, no dependencies. Embedded SQLite, starts in under 1 second.
|
|
</p>
|
|
<pre className="bg-fd-muted/30 rounded-lg p-3 text-xs font-mono">
|
|
{`$ base serve --dev
|
|
# Auth, DB, REST API, Admin UI
|
|
# all running at localhost:8090`}
|
|
</pre>
|
|
</div>
|
|
<div className="p-6 rounded-xl border">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<CloudIcon className="size-5" />
|
|
<h4 className="font-medium text-lg">Production</h4>
|
|
</div>
|
|
<p className="text-fd-muted-foreground text-sm mb-4">
|
|
PostgreSQL backend, horizontal replicas, K8s-native. Same binary, different config.
|
|
</p>
|
|
<pre className="bg-fd-muted/30 rounded-lg p-3 text-xs font-mono">
|
|
{`# Kubernetes deployment
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
spec:
|
|
replicas: 3
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: base
|
|
image: ghcr.io/hanzoai/base`}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Native ZAP */}
|
|
<div className={cn(cardVariants())}>
|
|
<ZapIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-4' }))}>
|
|
ZAP Protocol
|
|
</h3>
|
|
<p className="text-fd-muted-foreground mb-4">
|
|
Zero-copy binary protocol for inter-service communication between Base
|
|
instances. Uses Cap{"'"}n Proto RPC instead of JSON serialization.
|
|
</p>
|
|
<ul className="space-y-2 text-sm text-fd-muted-foreground">
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
Cap{"'"}n Proto RPC for zero-copy message passing
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
Database, KV, and gateway transport layers
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
ML-KEM / ML-DSA post-quantum TLS
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
{/* Admin UI */}
|
|
<div className={cn(cardVariants())}>
|
|
<HardDriveIcon className="size-8 mb-4" />
|
|
<h3 className={cn(headingVariants({ variant: 'h3', className: 'mb-4' }))}>
|
|
Admin Dashboard
|
|
</h3>
|
|
<p className="text-fd-muted-foreground mb-4">
|
|
Served at <code className="text-xs bg-fd-muted px-1 py-0.5 rounded">/_/</code>.
|
|
Embedded in the binary. No separate deployment.
|
|
</p>
|
|
<ul className="space-y-2 text-sm text-fd-muted-foreground">
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
Visual schema editor for collections
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
Record browser with inline editing
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
User management and auth configuration
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
API rules editor
|
|
</li>
|
|
<li className="flex items-center gap-2">
|
|
<ArrowRightIcon className="size-3" />
|
|
Request logs and system metrics
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
{/* Quick Start */}
|
|
<div className={cn(cardVariants({ variant: 'secondary' }), 'col-span-full')}>
|
|
<h2 className={cn(headingVariants({ variant: 'h2', className: 'mb-6' }))}>
|
|
Quick Start
|
|
</h2>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<div>
|
|
<h4 className="font-medium mb-2">1. Install</h4>
|
|
<pre className="bg-black/10 dark:bg-white/5 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`# macOS
|
|
brew install hanzoai/tap/base
|
|
|
|
# Linux
|
|
curl -fsSL https://base.hanzo.ai/install | sh
|
|
|
|
# Docker
|
|
docker pull ghcr.io/hanzoai/base`}
|
|
</pre>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium mb-2">2. Run</h4>
|
|
<pre className="bg-black/10 dark:bg-white/5 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`# Start with dev mode
|
|
base serve --dev
|
|
|
|
# Admin UI: localhost:8090/_/
|
|
# API: localhost:8090/api/`}
|
|
</pre>
|
|
</div>
|
|
<div>
|
|
<h4 className="font-medium mb-2">3. Query</h4>
|
|
<pre className="bg-black/10 dark:bg-white/5 rounded-lg p-3 text-xs overflow-x-auto font-mono">
|
|
{`import { BaseClient } from '@hanzoai/base'
|
|
|
|
const base = new BaseClient(
|
|
'http://localhost:8090'
|
|
)
|
|
|
|
const records = await base
|
|
.collection('posts')
|
|
.getFullList()`}
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Pricing */}
|
|
<div className="col-span-full" id="pricing">
|
|
<h2 className={cn(headingVariants({ variant: 'h2', className: 'mb-2' }))}>
|
|
Pricing
|
|
</h2>
|
|
<p className="text-fd-muted-foreground mb-8">
|
|
Each Base instance runs on a dedicated compute node.
|
|
Pricing syncs from{' '}
|
|
<a href="https://pricing.hanzo.ai" className="underline underline-offset-4 hover:text-fd-foreground">
|
|
pricing.hanzo.ai
|
|
</a>
|
|
.
|
|
</p>
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
|
|
{plans.map((plan) => (
|
|
<div
|
|
key={plan.id}
|
|
className={cn(
|
|
'relative flex flex-col p-6 rounded-2xl border transition-colors',
|
|
plan.popular
|
|
? 'border-fd-foreground/40 bg-fd-card shadow-lg'
|
|
: 'border-fd-border bg-fd-card',
|
|
)}
|
|
>
|
|
{plan.popular && (
|
|
<span className="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-0.5 text-xs font-medium bg-fd-foreground text-fd-background rounded-full">
|
|
Popular
|
|
</span>
|
|
)}
|
|
{plan.freeTier && (
|
|
<span className="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-0.5 text-xs font-medium bg-green-600 text-white rounded-full">
|
|
Free Tier
|
|
</span>
|
|
)}
|
|
<h3 className="text-lg font-medium text-fd-foreground mb-1">{plan.name}</h3>
|
|
<div className="flex items-baseline gap-1 mb-1">
|
|
<span className="text-3xl font-medium text-fd-foreground">
|
|
{formatPrice(plan.priceMonthly)}
|
|
</span>
|
|
<span className="text-sm text-fd-muted-foreground">/mo</span>
|
|
</div>
|
|
<p className="text-xs text-fd-muted-foreground mb-1">
|
|
${plan.priceHourly.toFixed(3)}/hr
|
|
</p>
|
|
<div className="flex items-center gap-1.5 text-xs text-fd-muted-foreground mb-4">
|
|
<CpuIcon className="size-3" />
|
|
{planSpecs(plan)}
|
|
</div>
|
|
<ul className="flex flex-col gap-2 mb-6 flex-1">
|
|
<li className="flex items-start gap-2 text-sm text-fd-muted-foreground">
|
|
<CheckIcon className="size-4 shrink-0 mt-0.5 text-fd-foreground" />
|
|
{plan.transferTB} TB transfer
|
|
</li>
|
|
{plan.features.map((feat) => (
|
|
<li key={feat} className="flex items-start gap-2 text-sm text-fd-muted-foreground">
|
|
<CheckIcon className="size-4 shrink-0 mt-0.5 text-fd-foreground" />
|
|
{feat}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<Link
|
|
href="/docs"
|
|
className={cn(
|
|
'inline-flex items-center justify-center gap-2 px-5 py-3 rounded-full font-medium transition-colors text-center text-sm',
|
|
plan.popular
|
|
? 'bg-fd-foreground text-fd-background hover:bg-fd-foreground/90'
|
|
: 'border border-fd-border text-fd-foreground hover:bg-fd-accent',
|
|
)}
|
|
>
|
|
{plan.freeTier ? 'Start Free' : 'Get Started'}
|
|
<ArrowRightIcon className="size-4" />
|
|
</Link>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Custom / Beyond */}
|
|
<div className="flex flex-col sm:flex-row items-center justify-between gap-6 p-6 rounded-2xl border border-fd-border bg-fd-card">
|
|
<div>
|
|
<h3 className="text-lg font-medium text-fd-foreground mb-1">Need More?</h3>
|
|
<p className="text-sm text-fd-muted-foreground max-w-lg">
|
|
Custom clusters, bare-metal nodes, private networking, SLA guarantees,
|
|
and dedicated support. Runs on your infrastructure or ours.
|
|
</p>
|
|
</div>
|
|
<a
|
|
href="mailto:[email protected]"
|
|
className={cn(
|
|
buttonVariants({ variant: 'secondary' }),
|
|
'shrink-0 gap-2',
|
|
)}
|
|
>
|
|
<MessageCircleIcon className="size-4" />
|
|
Contact Sales
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{/* CTA */}
|
|
<div className={cn(cardVariants(), 'col-span-full text-center py-12')}>
|
|
<LockIcon className="size-8 mx-auto mb-4" />
|
|
<h2 className={cn(headingVariants({ variant: 'h2', className: 'mb-4' }))}>
|
|
Start Building
|
|
</h2>
|
|
<p className="text-fd-muted-foreground mb-8 max-w-2xl mx-auto">
|
|
Install the binary, define your collections, and ship.
|
|
Open-source under MIT. Backed by Hanzo AI.
|
|
</p>
|
|
<div className="flex flex-row items-center justify-center gap-4">
|
|
<Link href="/docs" className={cn(buttonVariants())}>
|
|
Read the Docs
|
|
</Link>
|
|
<a
|
|
href="https://github.com/hanzoai/base"
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
className={cn(buttonVariants({ variant: 'secondary' }))}
|
|
>
|
|
GitHub
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|