Files

327 lines
11 KiB
TypeScript

'use client'
import React from "react";
import { motion } from "@/components/motion";
import Link from "next/link";
import {
Shield,
Zap,
Database,
Key,
HardDrive,
Activity,
Webhook,
Terminal,
Server,
Lock,
Users,
FileJson,
Clock,
ChevronRight
} from "lucide-react";
import { View, YStack, XStack } from '@hanzo/ui'
import { Grid } from '@hanzo/ui/grid'
import { Words } from '@/components/marketing/band'
import { Line } from '@/components/type'
import { Action, Chip, Lede, Title, Leaf } from '@hanzo/ui/marketing'
/** What a card's name comes up to on approach: full ink. */
const LIFT = { color: 'var(--text-primary)' } as const
interface Service {
name: string;
description: string;
icon: React.ElementType;
href?: string;
}
const CORE_SERVICES: Service[] = [
{
name: "Auth & IAM",
description: "Authentication, SSO, OAuth, JWT, and sovereign user management out of the box",
icon: Shield,
href: "/iam",
},
{
name: "Serverless Functions",
description: "Serverless functions with automatic scaling and edge deployment",
icon: Zap,
href: "/functions",
},
{
name: "Database & Datastore",
description: "High-performance SQL, vector search, and real-time subscriptions",
icon: Database,
href: "/datastore",
},
{
name: "Object Storage",
description: "S3-compatible distributed object storage with global CDN integration",
icon: HardDrive,
href: "/s3",
},
{
name: "Realtime Sync",
description: "WebSockets, presence, and live state replication for collaborative apps",
icon: Activity,
href: "/realtime",
},
{
name: "Secrets & KMS",
description: "Hardware-backed secret management, envelope encryption, and key rotation",
icon: Key,
href: "/kms",
},
];
const ADDITIONAL_SERVICES: Service[] = [
{
name: "API Gateway",
description: "Rate limiting, auth verification, and intelligent request routing",
icon: Server,
href: "/gateway",
},
{
name: "Background Tasks",
description: "Scheduled cron jobs and durable task queues with retry logic",
icon: Clock,
href: "/tasks",
},
{
name: "PubSub & Webhooks",
description: "Event-driven messaging and real-time webhook delivery",
icon: Webhook,
href: "/pubsub",
},
{
name: "Observability & O11y",
description: "OpenTelemetry traces, metrics, structured logs, and audit trails",
icon: Users,
href: "/o11y",
},
{
name: "Edge Network",
description: "Global edge compute, smart routing, and low-latency cache",
icon: FileJson,
href: "/edge",
},
{
name: "Vector & Search",
description: "Hybrid keyword and dense vector search with sub-millisecond retrieval",
icon: Lock,
href: "/products/memory",
},
];
/** The band's own ground: the page fading into the next section. */
const FADE = 'linear-gradient(to bottom, var(--background), color-mix(in oklab, var(--neutral-900) 30%, transparent))'
/** A group's heading over its grid. */
const GROUP = { fontSize: '$4', lineHeight: 'calc(1.75 / 1.125)', weight: '600', color: 'var(--foreground)', mb: 24 } as const
/** A line of the CLI transcript, and the prompt that opens a command. */
function Out({ mt, children }: { mt?: number; children: React.ReactNode }) {
return <Words render="div" mt={mt} color="var(--muted-foreground)">{children}</Words>
}
const PROMPT = <Words color="var(--foreground)">$</Words>
/** The eyebrow badge — `Chip`'s outline with this page's tinted fill. */
const BADGE = { self: "center", mb: 16, px: 12, py: 4, fontSize: "$1", lineHeight: "calc(1 / 0.75)", fontWeight: "500", bg: "color-mix(in srgb, var(--primary) 15%, transparent)", color: "var(--primary)" } as const
/**
* A core service tile. `Leaf` is the card that IS a link — its edge lifts on
* approach, which is the one thing a style attribute cannot say. Padding and
* both edges are its declared knobs, so a caller states three custom properties
* rather than a `group`/`group-hover:` pair.
*/
const leaf = (href?: string) =>
({
p: 24,
borderColor: 'var(--border)',
hoverStyle: { borderColor: href ? 'var(--white-30)' : 'var(--border)' },
rounded: 'var(--radius-xl)',
bg: 'color-mix(in oklab, var(--secondary) 50%, transparent)',
}) as const
const PaaSCapabilities = () => {
return (
<YStack
render="section"
py={96}
px={16}
$md={{ px: 32 }}
background={FADE}
>
<YStack width="100%" maxW={1152} mx="auto">
{/* Section header */}
<YStack
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />}
mb={64}
$platform-web={{ textAlign: 'center' }}
>
<Chip {...BADGE}>
<Terminal width={14} height={14} />
Platform-as-a-Service
</Chip>
<Title quiet mb={16}>
The services an app needs anyway
</Title>
<Lede maxW={672} mx="auto" mt="$3.5">
Platform ships your code. These are the pieces it lands next to.
Deploy each one beside your app, or reach it on Hanzo Cloud.
</Lede>
</YStack>
{/* Core services */}
<YStack mb={48}>
<Words render={<motion.h3 initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />} {...GROUP}>
Core Services
</Words>
<Grid columns={{ min: 300, max: 3 }} gap={16}>
{CORE_SERVICES.map((service, index) => {
const Icon = service.icon;
const content = (
<Leaf
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: index * 0.05 }} />}
{...leaf(service.href)}
>
<XStack items="flex-start" gap={16}>
<XStack
width={40}
height={40}
items="center"
justify="center"
shrink={0}
rounded="var(--radius-lg)"
background="color-mix(in srgb, var(--primary) 15%, transparent)"
>
<Icon width={20} height={20} />
</XStack>
<YStack flex={1} minW={0}>
<Line render="h4" weight="500" display="block" marginBottom={4} $group-hover={LIFT}>{service.name}</Line>
<Line render="p" size="sm" tone="muted" display="block">{service.description}</Line>
</YStack>
</XStack>
</Leaf>
);
return service.href ? (
<Link key={service.name} href={service.href || "#"}>
{content}
</Link>
) : (
<div key={service.name}>{content}</div>
);
})}
</Grid>
</YStack>
{/* Additional services */}
<YStack mb={48}>
<Words render={<motion.h3 initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />} {...GROUP}>
Additional Services
</Words>
<Grid columns={{ min: 300, max: 3 }} gap={16}>
{ADDITIONAL_SERVICES.map((service, index) => {
const Icon = service.icon;
const cardContent = (
<Leaf
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: index * 0.05 }} />}
{...leaf(service.href)}
>
<XStack items="center" gap={12}>
<Icon width={16} height={16} color="var(--primary)" />
<div>
<Line size="sm" weight="500" $group-hover={LIFT}>{service.name}</Line>
<Line size="xs" tone="muted" display="block" marginTop={2}>{service.description}</Line>
</div>
</XStack>
</Leaf>
);
return service.href ? (
<Link key={service.name} href={service.href}>
{cardContent}
</Link>
) : (
<div key={service.name}>{cardContent}</div>
);
})}
</Grid>
</YStack>
{/* CLI showcase */}
<View
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />}
display="block"
rounded="var(--radius-xl)"
borderWidth={1}
borderColor="var(--border)"
background="color-mix(in oklab, var(--secondary) 80%, transparent)"
overflow="hidden"
>
<XStack items="center" gap={12} px={16} py={12} borderBottomWidth={1} borderColor="var(--border)">
<XStack gap={6}>
<YStack width={12} height={12} rounded="var(--radius-full)" bg="$raised" />
<YStack width={12} height={12} rounded="var(--radius-full)" bg="$raised" />
<YStack width={12} height={12} rounded="var(--radius-full)" bg="$raised" />
</XStack>
<Line size="xs" tone="muted" fontFamily="$mono">hanzo platform</Line>
</XStack>
<Words render="div" display="flex" flexDirection="column" p={24} fontFamily="$mono" fontSize="$2" lineHeight="calc(1.25 / 0.875)">
<YStack gap={12}>
<Out>{PROMPT} hanzo init my-app</Out>
<Out>✓ Created project configuration</Out>
<Out>✓ Initialized Auth, Database, Storage</Out>
<Out>✓ Generated TypeScript types</Out>
<Out mt={16}>{PROMPT} hanzo dev</Out>
<Out>Starting local development server...</Out>
<XStack items="center" gap={8}>
<YStack width={8} height={8} rounded="var(--radius-full)" bg="var(--primary)" />
<Line color="var(--foreground)">
Platform running at http://localhost:8000
</Line>
</XStack>
<Out mt={16}>{PROMPT} hanzo serve cloud</Out>
<Out>Deploying to production...</Out>
<XStack items="center" gap={8}>
<YStack width={8} height={8} rounded="var(--radius-full)" bg="var(--primary)" />
<Line color="var(--foreground)">
Live at https://my-app.hanzo.app
</Line>
</XStack>
</YStack>
</Words>
</View>
{/* Bottom CTA */}
<XStack
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />}
flexWrap="wrap"
justify="center"
gap={16}
mt={48}
>
<Action
href="https://docs.hanzo.ai/docs/cli/platform"
target="_blank"
rel="noreferrer noopener"
fill
>
Read the docs
<ChevronRight width={16} height={16} />
</Action>
<Action
href="/cloud"
>
Explore Cloud
<ChevronRight width={16} height={16} />
</Action>
</XStack>
</YStack>
</YStack>
);
};
export default PaaSCapabilities;