mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
The /enterprise card claimed SOC 2 Type II, HIPAA, ISO 27001 and a 99.99% uptime SLA; it now names the deployments, unlimited members, dedicated compute and 24/7 engineering support, and /contact-sales shares it. `generate-og.mjs <name>` redraws the named cards alone. /contact-sales keeps its submit off until the page runs, so the export's form never sends answers as an address, and takes any it finds out of the address. The team size select draws its own chevron at 56px in WebKit, the hidden field is read off the form and counted as sales_trapped, and an invalid field keeps its error edge in focus. /enterprise qualifies the database claim with Hanzo Base, records the audit status as /trust does, links the Privacy Policy for data use, and the site's sales contact point is [email protected] at /contact-sales. Co-authored-by: Hanzo Dev <[email protected]>
96 lines
4.4 KiB
TypeScript
96 lines
4.4 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Where Enterprise runs, as one picture: four stops on one track, from the
|
|
* fastest start (Hanzo's cloud) to the most control (air-gapped). The stops
|
|
* are the Enterprise plan's own list (GET /v1/billing/plans, `enterprise`:
|
|
* "Run Hanzo in your cloud, on your servers or fully offline"); the line under
|
|
* each comes from /trust (one database file per organization) and lib/data/solutions
|
|
* `INSIDE` (models served on machines you control).
|
|
*
|
|
* Below `$lg` the track runs down the left edge and the stops stack beside it;
|
|
* from `$lg` it runs across the top and the four stand side by side.
|
|
*/
|
|
|
|
import type { LucideIcon } from 'lucide-react'
|
|
import { Cloud, Network, Server, ShieldCheck } from 'lucide-react'
|
|
import { Text, View, XStack, YStack } from '@hanzo/ui'
|
|
import { CARD } from './kit'
|
|
|
|
type Stop = { icon: LucideIcon; name: string; where: string; line: string }
|
|
|
|
export const STOPS: readonly Stop[] = [
|
|
{ icon: Cloud, name: 'Hanzo Cloud', where: 'We run it for you', line: "Start today. On Hanzo Base, your organization's data is its own database." },
|
|
{ icon: Network, name: 'Your cloud', where: 'Inside your VPC', line: 'Hanzo runs in your own cloud account, on your network.' },
|
|
{ icon: Server, name: 'Your servers', where: 'On premises', line: 'On hardware in your data center, beside the systems you already run.' },
|
|
{ icon: ShieldCheck, name: 'Air-gapped', where: 'Fully offline', line: 'Sealed inside your network, with models served on machines you control.' },
|
|
]
|
|
|
|
/** The track's own ink: the hairline one rung up, so it reads on either ground. */
|
|
const RAIL = 'var(--border-strong)'
|
|
|
|
export default function Deploy() {
|
|
return (
|
|
<YStack {...CARD} p={24} $sm={{ p: 32 }} gap={28} data-slot="deploy">
|
|
{/* The legend reads across, so it stands only where the track does. */}
|
|
<XStack display="none" $lg={{ display: 'flex' }} items="center" gap={12} aria-hidden>
|
|
<Text fontSize="$2" color="var(--muted-foreground)" shrink={0}>
|
|
Fastest start
|
|
</Text>
|
|
<View
|
|
grow={1}
|
|
height={2}
|
|
rounded="var(--radius-full)"
|
|
backgroundImage="linear-gradient(to right, var(--border), var(--foreground))"
|
|
/>
|
|
<Text fontSize="$2" color="$ink" shrink={0}>
|
|
Most control
|
|
</Text>
|
|
</XStack>
|
|
<View render="ol" display="grid" gridTemplateColumns="minmax(0, 1fr)" m={0} p={0} $lg={{ gridTemplateColumns: 'repeat(4, minmax(0, 1fr))' }} $platform-web={{ listStyle: 'none' }}>
|
|
{STOPS.map((s, i) => (
|
|
<Stop key={s.name} stop={s} first={i === 0} last={i === STOPS.length - 1} />
|
|
))}
|
|
</View>
|
|
</YStack>
|
|
)
|
|
}
|
|
|
|
function Stop({ stop: { icon: Icon, name, where, line }, first, last }: { stop: Stop; first: boolean; last: boolean }) {
|
|
return (
|
|
<XStack render="li" gap={16} minW={0} $lg={{ flexDirection: 'column', items: 'center', gap: 20 }}>
|
|
{/* The track: down the left edge on a phone, across the top from $lg. */}
|
|
<YStack items="center" width={44} shrink={0} $lg={{ flexDirection: 'row', width: '100%' }}>
|
|
<View display="none" $lg={{ display: 'flex', grow: 1, height: 1, bg: first ? 'transparent' : RAIL }} />
|
|
<Text
|
|
display="inline-flex"
|
|
items="center"
|
|
justify="center"
|
|
width={44}
|
|
height={44}
|
|
shrink={0}
|
|
rounded="var(--radius-full)"
|
|
borderWidth={1}
|
|
borderColor={last ? 'var(--foreground)' : 'var(--border-strong)'}
|
|
bg={last ? 'var(--foreground)' : 'var(--background)'}
|
|
color={last ? 'var(--background)' : 'var(--foreground)'}
|
|
>
|
|
<Icon width={20} height={20} aria-hidden />
|
|
</Text>
|
|
<View grow={1} width={1} minH={24} bg={last ? 'transparent' : RAIL} $lg={{ height: 1, width: 'auto', minH: 0, bg: last ? 'transparent' : RAIL }} />
|
|
</YStack>
|
|
<YStack gap={6} minW={0} grow={1} shrink={1} pb={last ? 0 : 28} $lg={{ pb: 0, px: 12, items: 'center' }}>
|
|
<Text render="h3" fontSize="$5" lineHeight="1.75rem" fontWeight="600" color="$ink" $lg={{ text: 'center' }}>
|
|
{name}
|
|
</Text>
|
|
<Text fontSize="$2" color="var(--muted-foreground)" $lg={{ text: 'center' }}>
|
|
{where}
|
|
</Text>
|
|
<Text render="p" fontSize="$3" lineHeight="1.5rem" color="$ink" mt={4} $lg={{ text: 'center' }}>
|
|
{line}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
)
|
|
}
|