mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
176 lines
7.0 KiB
TypeScript
176 lines
7.0 KiB
TypeScript
'use client'
|
|
|
|
import React from "react";
|
|
import { motion } from "@/components/motion";
|
|
import { Activity, Users, Zap, Cpu, Server, Network } from "lucide-react";
|
|
import { View, YStack, XStack } from '@hanzo/ui'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { Line } from '@/components/type'
|
|
import { Lede, Title } from '@hanzo/ui/marketing'
|
|
|
|
/**
|
|
* The tile each claim sits in.
|
|
*
|
|
* A COLUMN, not a block: gui's `Text` is `display: inline`, so the heading and
|
|
* the paragraph under it would share a line unless each is a flex item.
|
|
*/
|
|
const CARD = {
|
|
p: 24,
|
|
rounded: 'var(--radius-xl)',
|
|
borderWidth: 1,
|
|
borderColor: 'var(--border)',
|
|
background: 'linear-gradient(to bottom right, color-mix(in oklab, var(--pure-white) 20%, transparent), transparent)',
|
|
} as const
|
|
|
|
/** The strip of three facts under them. */
|
|
const FACT = {
|
|
p: 16,
|
|
rounded: 'var(--radius-lg)',
|
|
borderWidth: 1,
|
|
borderColor: 'var(--neutral-800)',
|
|
background: 'color-mix(in oklab, var(--pure-black) 40%, transparent)',
|
|
} as const
|
|
|
|
/** A meter: the track, and the fill whose width is the only thing that varies. */
|
|
function Meter({ fill }: { fill: `${number}%` }) {
|
|
return (
|
|
<View width="100%" height={8} rounded="var(--radius-full)" overflow="hidden" bg="var(--neutral-800)">
|
|
<View width={fill} height="100%" rounded="var(--radius-full)" bg="var(--primary)" />
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const Scaling = () => {
|
|
return (
|
|
<YStack
|
|
render="section"
|
|
py={80}
|
|
px={16}
|
|
$sm={{ px: 24 }}
|
|
$lg={{ px: 32 }}
|
|
background="linear-gradient(to bottom, var(--background), color-mix(in oklab, var(--pure-white) 10%, transparent))"
|
|
>
|
|
<YStack width="100%" maxW={1280} mx="auto">
|
|
<View
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5 }} />}
|
|
display="block"
|
|
mb={64}
|
|
$platform-web={{ textAlign: 'center' }}
|
|
>
|
|
<Title quiet mb={16}>
|
|
What happens as it grows
|
|
</Title>
|
|
<Lede maxW={768} mx="auto" mt="$3.5">
|
|
The parts that scale, and the parts that are simply not in the way
|
|
</Lede>
|
|
</View>
|
|
|
|
<Grid columns={{ min: 300, max: 3 }} gap={32}>
|
|
<YStack
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 0.1 }} />}
|
|
{...CARD}
|
|
>
|
|
<View mb={16}>
|
|
<Activity width={32} height={32} color="var(--foreground)" />
|
|
</View>
|
|
<Line render="h3" size="xl" weight="600" marginBottom={8}>A change costs its audience</Line>
|
|
<Line render="p" tone="soft">
|
|
A record is written once and handed to the subscribers already
|
|
attached to it. The work is the number of people watching, not the
|
|
size of the collection they are watching.
|
|
</Line>
|
|
|
|
<YStack mt={24} pt={24} borderTopWidth={1} borderColor="var(--neutral-800)">
|
|
<XStack items="center" justify="space-between" mb={8}>
|
|
<Line size="sm" tone="muted">Cost of a write</Line>
|
|
<Line weight="500">Per subscriber</Line>
|
|
</XStack>
|
|
<Meter fill="100%" />
|
|
</YStack>
|
|
</YStack>
|
|
|
|
<YStack
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 0.2 }} />}
|
|
{...CARD}
|
|
>
|
|
<View mb={16}>
|
|
<Users width={32} height={32} color="var(--foreground)" />
|
|
</View>
|
|
<Line render="h3" size="xl" weight="600" marginBottom={8}>A stream is a response</Line>
|
|
<Line render="p" tone="soft">
|
|
Not a process, not a socket server beside the API — an HTTP
|
|
response left open. They are cheap to hold, and one that goes away
|
|
releases what it held without anything needing to be told.
|
|
</Line>
|
|
|
|
<YStack mt={24} pt={24} borderTopWidth={1} borderColor="var(--neutral-800)">
|
|
<XStack items="center" justify="space-between" mb={8}>
|
|
<Line size="sm" tone="muted">Per subscriber</Line>
|
|
<Line weight="500">One open response</Line>
|
|
</XStack>
|
|
<Meter fill="95%" />
|
|
</YStack>
|
|
</YStack>
|
|
|
|
<YStack
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 0.3 }} />}
|
|
{...CARD}
|
|
>
|
|
<View mb={16}>
|
|
<Network width={32} height={32} color="var(--foreground)" />
|
|
</View>
|
|
<Line render="h3" size="xl" weight="600" marginBottom={8}>Isolated by tenant</Line>
|
|
<Line render="p" tone="soft">
|
|
Every org's subscribers sit on that org's own broker, over that
|
|
org's own data. Adding a tenant adds a broker rather than widening
|
|
a shared one, so nobody's growth is anybody else's problem.
|
|
</Line>
|
|
|
|
<YStack mt={24} pt={24} borderTopWidth={1} borderColor="var(--neutral-800)">
|
|
<XStack items="center" justify="space-between" mb={8}>
|
|
<Line size="sm" tone="muted">Blast radius</Line>
|
|
<Line weight="500">One tenant</Line>
|
|
</XStack>
|
|
<Meter fill="90%" />
|
|
</YStack>
|
|
</YStack>
|
|
</Grid>
|
|
|
|
<View
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 0.4 }} />}
|
|
display="block"
|
|
mt={64}
|
|
>
|
|
<Grid columns={{ min: 220, max: 3 }} gap={24}>
|
|
<XStack items="center" gap={16} {...FACT}>
|
|
<Zap width={32} height={32} color="var(--foreground)" />
|
|
<YStack>
|
|
<Line render="h4" weight="500">No polling</Line>
|
|
<Line render="p" size="sm" tone="muted">The client asks once, not every second</Line>
|
|
</YStack>
|
|
</XStack>
|
|
|
|
<XStack items="center" gap={16} {...FACT}>
|
|
<Cpu width={32} height={32} color="var(--foreground)" />
|
|
<YStack>
|
|
<Line render="h4" weight="500">Reconnects itself</Line>
|
|
<Line render="p" size="sm" tone="muted">A dropped stream reopens with a fresh grant</Line>
|
|
</YStack>
|
|
</XStack>
|
|
|
|
<XStack items="center" gap={16} {...FACT}>
|
|
<Server width={32} height={32} color="var(--foreground)" />
|
|
<YStack>
|
|
<Line render="h4" weight="500">Nothing extra to run</Line>
|
|
<Line render="p" size="sm" tone="muted">The same binary that serves the API serves the stream</Line>
|
|
</YStack>
|
|
</XStack>
|
|
</Grid>
|
|
</View>
|
|
</YStack>
|
|
</YStack>
|
|
);
|
|
};
|
|
|
|
export default Scaling;
|