Files
zeekay cb6db9e659 design: every page and component outside the landing and beta paths is at zero lint
Inline style objects became gui props (Box for a div, View or Text rendering the
element for the rest, since Box styles only a div), raw colours and sizes became
the design's tokens, the brand swatches read @hanzo/design's own values, and the
project and creation hues moved to palette.json, which is data. The newsletter's
subscribe button is the standard filled Action. connect-steps.tsx, which nothing
imports, is deleted with the brand constants nobody read.

Pages compared before and after at 1440x900 and 390x844: blockchain, brand,
directory, projects, settings, unsubscribe identical; founder 0.03% of pixels;
buying specialist's title snaps from 28 to the ramp's 26px and the newsletter's
button is now filled.

Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
2026-10-26 03:49:42 -07:00

271 lines
10 KiB
TypeScript

'use client'
/**
* /platform — every layer of the cloud, on one page.
*
* The order is a descent. The layers come first — the mount index, the four
* ways in, an agent's tool dispatch, the audit record, the trace — and then the
* deploy plane a reader puts their own code on: what it does, where it runs,
* and the licence it runs under. The first six live in components/home/ and this
* page is their only mount, so the copy in them is this page's copy.
*
* ONE `<h1>`, and `PageHero` draws it. Every section below is an `<h2>` of its
* own, so the outline is one level deep from the heading to the footer, and the
* heading is the words `layout.tsx` gives the tab and the search result.
*
* NO TAILWIND. The two sections this file draws itself are
* `components/marketing/band`'s `Band` and `Head` for the frame,
* `@hanzo/ui/grid`'s `Grid` for the card rows, and @hanzo/ui's marketing components
* (`Title`, `Lede`, `Action`, `More`, `Leaf`) for the type and the controls.
*/
import React from "react";
import PaaSCapabilities from "@/components/platform/PaaSCapabilities";
import CallToAction from "@/components/platform/CallToAction";
import ZenQuoteSection from "@/components/zen/ZenQuoteSection";
import { OSSRevenueBanner } from "@/components/oss/OSSRevenueBanner";
import { motion } from "@/components/motion";
import { Cloud, Server, Lock, Code2, Network, Terminal, ArrowRight } from "lucide-react"
import { Github } from "@/components/brand"
import { ProductFooter } from "@/components/products/ProductFooter"
import { Box, Text, View, XStack, YStack } from '@hanzo/ui'
import { Grid } from '@hanzo/ui/grid'
import { Band, Head } from '@/components/marketing/band'
import { Measure } from '@/components/home/band'
import { PageHero } from '@/components/marketing/page-kit'
import SystemArchitecture from '@/components/home/SystemArchitecture'
import { Ways } from '@/components/home/Ways'
import AgentRuntime from '@/components/home/AgentRuntime'
import Security from '@/components/home/Security'
import Observability from '@/components/home/Observability'
import { Together } from '@/components/home/Together'
import { Action, More, Title } from '@hanzo/ui/marketing'
/** The inks these two sections draw in. */
const INK = 'var(--foreground)'
const DIM = 'var(--muted-foreground)'
/** `--text-sm` at its leading, in the muted ink. */
const SMALL = { fontSize: '$2', lineHeight: '1.1607rem', color: DIM } as const
/** The three places it runs, and the one thing each card asks you to do next.
* `outside` is what makes the link an `<a>` with a `rel` rather than a `Link`. */
const deployments = [
{
icon: Server,
title: "Self-Hosted",
blurb: "One command on a server you already pay for. The whole platform, on hardware you can walk up to.",
checks: ["Runs anywhere Docker runs", "Scales out across a Swarm cluster", "Drives a Kubernetes cluster you own"],
href: "https://github.com/hanzoai",
cta: "View on GitHub",
outside: true,
},
{
icon: Cloud,
title: "Hanzo Cloud",
blurb: "The same platform, run for you. Sign in and deploy.",
checks: ["The machines are already running", "Deploy on push, or only on a tag", "Metered and billed per organization"],
href: "/cloud",
cta: "Explore Cloud",
outside: false,
},
{
icon: Network,
title: "Hybrid",
blurb: "Run the control plane on your own server. It deploys to the servers and clusters you already have.",
checks: ["Servers stay where they are", "One console over all of them", "Per-app target: this host, Swarm, or Kubernetes"],
href: "/contact-sales",
cta: "Contact Sales",
outside: false,
},
];
const DeploymentOptions = () => {
return (
<Band rule={false} pad="$11" measure={1152}>
<Head
title="Where it runs is your call"
lede="Same platform, same console, same API. You pick the machines."
/>
<Grid columns={{ min: 300, max: 3 }} gap={24}>
{deployments.map((d, i) => {
const Icon = d.icon;
return (
<motion.div
key={d.title}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 + i * 0.1 }}
>
{/* A hairline tile whose edge answers a pointer. */}
<Box
height="100%"
p={24}
borderWidth={1}
borderColor="var(--border)"
rounded="var(--radius-xl)"
hoverStyle={{ borderColor: 'var(--white-30)' }}
$platform-web={{ backgroundColor: 'color-mix(in oklab, var(--secondary) 50%, transparent)' }}
>
{/* The 48px disc a deployment's mark sits on. */}
<XStack
width={48}
height={48}
mb={16}
items="center"
justify="center"
rounded="var(--radius-lg)"
$platform-web={{ backgroundColor: 'color-mix(in srgb, var(--primary) 15%, transparent)' }}
>
<Icon width={24} height={24} color={INK} />
</XStack>
<Text render="h3" display="block" mb={8} fontSize="$6" lineHeight="1.4875rem" fontWeight="700" color={INK}>
{d.title}
</Text>
<Text render="p" display="block" mb={16} {...SMALL}>
{d.blurb}
</Text>
<View render="ul" display="grid" gap={8} my={13} pl={40}>
{d.checks.map((c) => (
<XStack key={c} render="li" items="center" gap={8}>
<Text {...SMALL} color={INK}>
✓
</Text>
<Text {...SMALL}>{c}</Text>
</XStack>
))}
</View>
{d.outside ? (
<More href={d.href} target="_blank" rel="noreferrer noopener" mt={24}>
<Github width={16} height={16} />
{d.cta}
</More>
) : (
<More href={d.href} mt={24}>
{d.cta}
<ArrowRight width={16} height={16} />
</More>
)}
</Box>
</motion.div>
);
})}
</Grid>
</Band>
);
};
const pillars = [
{
icon: Code2,
title: "Apache-2.0",
body: "Use it commercially. Change it. The upstream attribution travels with it.",
},
{
icon: Lock,
title: "It is your server",
body: "Builds, images, databases and backups sit on machines you chose, in a region you picked, under credentials you hold.",
},
{
icon: Terminal,
title: "Read the code",
body: "The code behind every deploy is right there, and so is the OpenAPI document the console itself calls.",
},
];
const OpenSourceBenefits = () => {
return (
<Band rule={false} pad="$11" measure={1152}>
<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }}>
<Box
p={32}
rounded="var(--radius-xl)"
borderWidth={1}
borderColor="var(--border)"
$platform-web={{ backgroundColor: 'color-mix(in oklab, var(--neutral-900) 60%, transparent)' }}
>
<XStack items="center" gap={16} mb={32}>
<Github width={32} height={32} color={INK} />
{/* A flex child's floor is its longest word unless it is told
otherwise, and this one carries a 21px heading. */}
<Box minW={0}>
<Title quiet>Open source, all of it</Title>
<Text render="p" display="block" fontSize="$3" lineHeight="1.25rem" color={DIM}>
Apache-2.0. Forked from Dokploy, and it says so in the NOTICE.
</Text>
</Box>
</XStack>
<Grid columns={{ min: 260, max: 3 }} gap={32}>
{pillars.map((p) => {
const Icon = p.icon;
return (
<YStack key={p.title} gap={16}>
<Icon width={40} height={40} color={INK} />
<Text render="h4" display="block" fontSize="$4" lineHeight="1.4583rem" fontWeight="700" color={INK}>
{p.title}
</Text>
<Text render="p" display="block" {...SMALL}>
{p.body}
</Text>
</YStack>
);
})}
</Grid>
<Box mt={32} pt={32} borderTopWidth={1} borderColor="var(--border)" $platform-web={{ textAlign: 'center' }}>
<Action href="https://github.com/hanzoai" target="_blank" rel="noreferrer noopener">
<Github width={16} height={16} />
Star on GitHub
</Action>
</Box>
</Box>
</motion.div>
</Band>
);
};
const Platform = () => {
return (
// ONE measure down the route. The apex bands take their width from
// `Measure` and the deploy-plane bands below them state 1152, so the page
// hands the apex bands the same number: one column width under one
// heading, and page-kit's 896 stays the prose measure it is.
<Measure.Provider value={1152}>
<Box overflowX="hidden">
<PageHero
eyebrow="Platform"
title="One platform. Every layer."
lede="Models, agents, the record they leave, and the machines underneath. One API for all of it."
/>
{/* ── The layers ───────────────────────────────────────────────── */}
<SystemArchitecture />
<Ways />
<AgentRuntime />
<Security />
<Observability />
<Together />
{/* ── The deploy plane: your own code, on top of them ──────────── */}
<PaaSCapabilities />
<ZenQuoteSection
quote="The wise engineer owns their tools, lest their tools own them."
attribution="Principle 14"
/>
<DeploymentOptions />
<OpenSourceBenefits />
<OSSRevenueBanner />
<CallToAction />
<ProductFooter slug="platform" name="Platform" />
</Box>
</Measure.Provider>
);
};
export default Platform;