Files
zeekay 44626a40c3 chrome: the bar is glass on every page, and the beta strip is blade steel
The bar stood in the page's flow, clear at rest, so a first screen whose
ground is a picture or a light started below it, and the bar read as a
black band over the html's #000 ground: above the creative models'
paintings, the kit pages' glow and the lit product heroes. It is glass now
at rest and scrolled ($background/72 under the chrome blur; the scroll
listener is gone), and such a first screen spreads `under(pt)` from
components/shell/dock.ts: up by the bar's height, the same back on its top
padding, so its ground starts at the top of the window behind the glass and
no word moves. On Splash (the six creative models and Zen), PageHero, /edge,
/identity, /functions, /playground, /operative, /realtime and /enso. On
/beta the site's bar at rest keeps the glass and paints the focused pages'
fixed glow on it (components/shell/focus.tsx), so the light runs on
unbroken. 72% is kept: over Zen's painting the nav's ink holds 5.1:1 at .72
and falls to 3.7:1 at .6.

The beta strip is polished curved steel in CSS gradients: a faint top line,
a ridge 5px down, the flat dark under the words, a temper line and the
bright 1px edge at the bottom; one `/` light at 70 degrees, resting
mid-strip, moved by transform, brighter where it crosses the ridge and the
edge than under the words. The 1px/3px grain that read as stripes is gone,
and so is the root script that set --beta-tilt on <html> every pointer
frame on every page, restyling the whole document and ignoring reduced
motion. White words hold 5.4:1 against the brightest steel under them,
where the live strip measured 4.35:1 at 1440 and 3.45:1 on a phone.

Gates: e2e/gates/glass.spec.ts and e2e/gates/steel.spec.ts. The composer
gate reads its rest once the theme's colours have eased in: read straight
after the input appeared, the pill's border was a value on its way to its
token (rgba(137…), rgba(175…) on two CI runs) and failed the check that the
border keeps one value through focus.

Claude-Session: https://claude.ai/code/session_01BLLMyBFSvAfCt7ZGqYuiEz
2026-10-22 12:02:12 -07:00

292 lines
13 KiB
TypeScript

'use client'
/**
* /edge — the on-device runtime, and when to reach for it.
*
* NO TAILWIND. The four section frames are `components/marketing/band`'s `Band`
* and `Head` — the rule, the rhythm, the gutter, the measure and the centred
* head this page used to write out by hand at each of them — the roles carry
* the type and the controls (`Display`, `Lede`, `Chip`, `Action`, `Line`), and
* everything left is gui props over @hanzo/design's tokens. The three hero
* tiles, the six capability cards and the four use cases
* are DATA; they were the same markup written thirteen times.
*
* Three things the swap moves, each deliberate, each the shared shape winning
* over this page's own copy of it:
*
* - the section headings take `Title quiet` and stop stating
* `text-3xl font-bold`, so they step 3xl -> 4xl at 640 and are CENTRED. They
* were `ChromeText` inside a `text-center` block, and ChromeText's own
* `flex flex-col items-start` wrapper shrink-wrapped each one to the LEFT
* while the paragraph under it centred — the page has been rendering a left
* heading over a centred lede.
* - the closing rule takes `Band`'s `--neutral-900` hairline rather than this
* page's `--neutral-800`, and its gutter steps at 640/1024 like every other
* band on the site rather than staying at 16 everywhere.
* - `Action` is the one button shape at its two weights, so `px-8 py-6
* text-lg` and `px-6 py-3 rounded-md` become the same control.
*
* `Lattice` is @hanzo/ui's ruled backdrop, placed by its own props.
*/
import React from 'react';
import { motion } from "@/components/motion";
import { Zap, Globe, Network, Server, Cloud, Code, Shield } from "lucide-react";
import { ProductFooter } from "@/components/products/ProductFooter"
import { AgentPrompt } from '@/components/product/AgentPrompt'
import { Related } from '@/components/product/Related'
import { Box, View, XStack, YStack } from '@hanzo/ui'
import { Grid } from '@hanzo/ui/grid'
import { Band, Head } from '@/components/marketing/band'
import { product } from '@/lib/product'
import { DOCS } from '@/components/home/nav-data'
import { Action, Chip, Display, Lattice, Lede, Line, type LineProps } from '@hanzo/ui/marketing'
import { under } from '@/components/shell/dock'
/** What the two shared blocks say about this product: the audited command, the
* route the catalog proves it by, and the docs page or null. The table has no
* edge command and docs publishes no page for it, so each block is silent on
* both rather than guessing. */
const PRODUCT = product('edge')
/** How narrow a column may get before a grid drops one, per shape. */
const TRIO = { min: 260, max: 3 } as const
const DECK = { min: 304, max: 3 } as const
const DUO = { min: 320, max: 2 } as const
/** The three inks this page draws body copy and marks in. */
const SOFT = 'color-mix(in oklab, var(--foreground) 80%, transparent)'
const DIM = 'color-mix(in oklab, var(--foreground) 70%, transparent)'
const WASH = 'color-mix(in oklab, var(--primary) 10%, transparent)'
/** A `color-mix()` ink is not a value gui's colour props type, so a frame's
* wash rides `$platform-web`, which gui still writes as a class. */
const CARD = { p: 24, rounded: 'var(--radius-xl)', borderWidth: 1, borderColor: 'var(--border)', '$platform-web': { backgroundColor: WASH } } as const
const PANEL = {
p: 32,
rounded: 'var(--radius-xl)',
borderWidth: 1,
borderColor: 'var(--border)',
'$platform-web': { backgroundColor: 'color-mix(in oklab, var(--primary) 5%, transparent)' },
} as const
/** A card's heading, at the ink a heading takes from the base sheet. */
const Heading = (p: LineProps) => <Line render="h3" display="block" fontWeight="700" color="var(--text-primary)" {...p} />
/** A paragraph in the page's soft ink. */
const Body = (p: LineProps) => <Line render="p" display="block" color={SOFT} {...p} />
/** The pair of controls in the hero, filled and outlined. */
const CENTER = { justify: "center" } as const
const OUTLINE = { justify: "center", borderColor: "var(--border)" } as const
/** The three tiles under the hero — where the runtime runs, and on what terms. */
const PLACES = [
{ icon: Globe, title: 'Where it runs', body: 'macOS on Apple Silicon and Intel, Linux on x86 and ARM, and modern browsers through WebAssembly' },
{ icon: Zap, title: 'No network at all', body: 'Weights on disk, tokens out of local memory. There is no request, so there is nothing to be slow' },
{ icon: Network, title: 'Fetched once', body: 'Name a Hugging Face repo and it downloads and caches the weights. After that, offline' },
] as const
const FEATURES = [
{ icon: Code, title: 'A server on localhost', body: 'A local inference endpoint on a port you pick. Point a client at it and the rest of your code does not change.' },
{ icon: Cloud, title: 'Quantized as a first language', body: 'GGUF is the native format — Q4_K, Q5_K, Q8_0. A 4B model at Q4_K_M is about 2.5GB on disk, which is what makes a phone a plausible place to run one.' },
{ icon: Server, title: 'It uses what it finds', body: 'Metal on Apple Silicon, CUDA where there is an NVIDIA card, AVX2 or AVX-512 on CPU. Detected at startup rather than configured by you.' },
{ icon: Globe, title: 'Zen models sized for devices', body: 'zen3-nano at 600M for embedded work and zen-eco at 4B for phones and tablets, both published pre-quantized.' },
{ icon: Network, title: 'Token by token', body: 'Streaming over server-sent events from the local server, or a callback from the Rust API, so a UI starts drawing before generation finishes.' },
{ icon: Shield, title: 'The prompt never leaves', body: 'Nothing is uploaded and there is no key to leak. The prompt and the output stay in the process that made them, which is the whole reason to run a model here.' },
] as const
const USE_CASES = [
{
title: 'Apps that work with no signal',
body: 'The model is a file on disk, so the feature does not stop existing when the connection does.',
points: [
'A desktop app that drafts and summarizes without a round trip',
'Field tools on a laptop, on a site with no coverage',
'Anything that has to keep working on a plane',
],
},
{
title: 'Text that cannot leave the building',
body: 'Some prompts are a legal problem the moment they cross a network boundary. This is the answer to those.',
points: [
'Clinical and legal text processed on the workstation that holds it',
'On-premise deployments with no egress at all',
'Personal data handled on the device it belongs to',
],
},
{
title: 'Inside the browser tab',
body: 'The WebAssembly build runs the model in the page, with nothing behind it — so a visitor costs you bandwidth once and compute never.',
points: [
'A demo that does not bill per visitor',
'An assistant inside a web app you already ship',
'Classification done client-side, before anything is uploaded',
],
},
{
title: 'Small hardware',
body: 'ARM64 Linux is a production target. Cortex-A class boards are experimental, and the 600M model is the one that fits them.',
points: [
'A 600M model on a single-board computer',
'An ARM server doing batch work on its own',
'Kiosks and appliances with no account to sign in to',
],
},
] as const
const Edge = () => {
return (
<Box minH="100vh" $platform-web={{ color: 'var(--foreground)' }}>
{/* Hero Section */}
<Box render="section" position="relative" {...under(80)} pb={80} $lg={{ pt: under(128).pt, pb: 128 }}>
<Box
position="absolute"
t={0}
r={0}
b={0}
l={0}
backgroundImage="linear-gradient(to bottom in oklab, color-mix(in oklab, var(--pure-white) 20%, transparent), transparent)"
/>
<Box
position="relative"
z="var(--z-raised)"
width="100%"
maxW={1280}
mx="auto"
px={16}
$sm={{ px: 24 }}
$lg={{ px: 32 }}
>
<Box width="100%" maxW={768} mx="auto" mb={64} $platform-web={{ textAlign: 'center' }}>
<Chip
mb={16}
px={16}
py={4}
borderColor="var(--border)"
bg="color-mix(in oklab, var(--primary) 5%, transparent)"
>
<Line fontWeight="500" color={DIM}>On-device inference</Line>
</Chip>
<Display
mb={24}
>
Hanzo Edge
</Display>
<Lede mb={32}>
Run a model on the machine in front of you. Edge is a Rust runtime that loads a quantized model and generates tokens locally — on a laptop, an ARM board, or a browser tab. Nothing is sent anywhere, because there is nothing to send it to.
</Lede>
<YStack gap={16} justify="center" $sm={{ flexDirection: 'row' }}>
<Action href="https://github.com/hanzoai/edge" fill {...CENTER}>
View on GitHub
</Action>
<Action href={DOCS} {...OUTLINE}>
View Documentation
</Action>
</YStack>
</Box>
{/* Hero Visual */}
<Box position="relative" overflow="hidden" p={32} rounded="var(--radius-xl)" borderWidth={1} borderColor="var(--border)" $platform-web={{ backgroundColor: WASH }}>
<Lattice
position="absolute"
inset={0}
maskImage="linear-gradient(to bottom, white, transparent)"
/>
<Grid columns={TRIO} gap={24}>
{PLACES.map(({ icon: Icon, title, body }) => (
<YStack
key={title}
items="center"
p={16}
rounded="var(--radius-lg)"
borderWidth={1}
borderColor="var(--border)"
$platform-web={{ backgroundColor: 'color-mix(in oklab, var(--black) 30%, transparent)' }}
>
<View mb={16}>
<Icon width={40} height={40} color={DIM} />
</View>
<Heading mb={8} fontSize="$4" lineHeight="1.5556" fontWeight="600">
{title}
</Heading>
<Body text="center">{body}</Body>
</YStack>
))}
</Grid>
</Box>
</Box>
</Box>
{PRODUCT && <AgentPrompt product={PRODUCT} />}
{/* Features */}
<Band rule={false} pad="$11" measure={1280}>
<Head
title="What the runtime does"
lede="One Rust binary, a core crate to embed, a WASM build for the browser, and a local server for everything else"
measure={768}
/>
<Grid columns={DECK} gap={32}>
{FEATURES.map(({ icon: Icon, title, body }, index) => (
<Box
key={title}
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: index * 0.1 }} viewport={{ once: true }} />}
{...CARD}
>
<Box mb={16}>
<Icon width={40} height={40} color={DIM} />
</Box>
<Heading mb={8} fontSize="$6" lineHeight="1.4">
{title}
</Heading>
<Body>{body}</Body>
</Box>
))}
</Grid>
</Band>
{/* Use Cases */}
<Band
rule={false}
pad="$11"
measure={1280}
ground="linear-gradient(to bottom in oklab, var(--background), color-mix(in oklab, var(--pure-white) 10%, transparent))"
>
<Head
title="When to reach for it"
lede="Use Edge when the data cannot leave, the network cannot be relied on, or a per-call bill is the wrong shape. Use Hanzo Engine when you need full precision or many users at once."
measure={768}
/>
<Grid columns={DUO} gap={32}>
{USE_CASES.map(({ title, body, points }) => (
<Box key={title} {...PANEL}>
<Heading mb={16} fontSize="$7" lineHeight="1.3333">
{title}
</Heading>
<Body mb={16}>{body}</Body>
<YStack render="ul" gap={8}>
{points.map((point) => (
<XStack key={point} render="li" items="flex-start">
<Line mr={8} color={DIM}>•</Line>
<Line color={SOFT}>{point}</Line>
</XStack>
))}
</YStack>
</Box>
))}
</Grid>
</Band>
{PRODUCT && <Related product={PRODUCT} />}
<ProductFooter slug="edge" name="Edge" />
</Box>
);
};
export default Edge;