mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
Every import, the catalog pin and the docs name @hanzo/app and github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and components/rooms.tsx is components/app.tsx.
954 lines
44 KiB
TypeScript
954 lines
44 KiB
TypeScript
/**
|
||
* /enso — Hanzo's agentic language model. One endpoint, and for each request a
|
||
* route: which lane (write, decide, act), which preset, which model.
|
||
*
|
||
* The thesis is the headline: the right intelligence, every time. The hero
|
||
* draws one request taking its route, and the page under it tells the whole
|
||
* story in short claims, each with the thing that backs it: the runtime's
|
||
* layers, the paths, what a call is weighed on, the measured tiers, one request
|
||
* end to end, the glue it replaces, the models it routes across, agents,
|
||
* decisions, the benchmarks (where Enso leads), infrastructure, the call
|
||
* itself, enterprise.
|
||
*
|
||
* Every figure is read. The lead, the chart and the tables are the research
|
||
* API's `enso.*` runs, read when the page opens (lib/enso, components/enso/Lead,
|
||
* components/enso/Chart, components/enso/Routed, components/benchmarks/Ladder);
|
||
* a preset's rate is the price list's
|
||
* (components/api/rate); the model count and the labs are the catalog
|
||
* snapshot, passed in. A preset card states no score: the runs measured
|
||
* enso-bench's router at a tag, not a preset the catalog sells. A sentence that
|
||
* compares Enso with the field is a condition on the runs, so a rerun changes
|
||
* the sentence rather than leaving it stale.
|
||
* Decide is a lane being built — Kai inside Enso is in progress — and it is drawn
|
||
* and said that way.
|
||
*
|
||
* A server component; the dot field (/'s, deferred: ./Opening), the
|
||
* hero figure, the turning verb, the walking lists, the reveals and the
|
||
* magnetic control are the client pieces it stacks.
|
||
*/
|
||
import { ArrowRight } from 'lucide-react'
|
||
import { Fragment, type ReactNode } from 'react'
|
||
import { Call, Pair, Price } from '@/components/api/rate'
|
||
import { ModelCardSection } from '@/components/product/ModelCardSection'
|
||
import { ProductArticlesSection, type ArticleItem } from '@/components/product/ProductArticlesSection'
|
||
import { ProductUseCasesSection, type UseCaseItem } from '@/components/product/ProductUseCasesSection'
|
||
import { AgentPrompt } from '@/components/product/AgentPrompt'
|
||
import { product } from '@/lib/product'
|
||
import { SiteFooter, SiteHeader } from '@/components/home/shell'
|
||
import { Label } from '@/components/home/label'
|
||
import { Siblings } from '@/components/family/Hero'
|
||
import Closing from '@/components/marketing/Closing'
|
||
import { Band, Card, Head } from '@/components/marketing/band'
|
||
import { Sample } from '@/components/models/kit'
|
||
import { Box, Text, View, XStack, YStack } from '@/components/ui'
|
||
import { Action, Chip, Display, Lede, More, Run } from '@hanzo/ui/marketing'
|
||
import { Faq } from '@/components/ui/faq'
|
||
import { Grid } from '@hanzo/ui/grid'
|
||
import { signUp } from '@hanzo/app/lib/destination'
|
||
import { modelPagePath } from '@hanzo/app/lib/path'
|
||
import { Read } from '@/components/family/Read'
|
||
import { CODE, PRESETS } from '@/lib/enso'
|
||
import { Models } from '@/components/api/count'
|
||
import { Ladder } from '@/components/benchmarks/Ladder'
|
||
import { Code } from '@/components/code'
|
||
import { Magnetic } from '@/components/marketing/magnetic'
|
||
import { Rise } from '@/components/marketing/rise'
|
||
import Runtime from './Runtime'
|
||
import { Chart } from './Chart'
|
||
import { Lead } from './Lead'
|
||
import { Opening } from './Opening'
|
||
import { Routed } from './Routed'
|
||
import { Requests, SecondPass, Walk } from './Story'
|
||
import { Words } from './Words'
|
||
import type { Lane, Scene, Step } from './scenes'
|
||
import { under } from '@/components/shell/dock'
|
||
|
||
const DOCS = 'https://docs.hanzo.ai/docs/models'
|
||
|
||
/* ---- the runtime: three lanes, and the requests that take them ---- */
|
||
|
||
/** Write and act are served; decide is the lane being built, drawn as such. */
|
||
const LANES: readonly Lane[] = [
|
||
{ id: 'write', verb: 'Write', who: 'Zen and the frontier’s models' },
|
||
{ id: 'decide', verb: 'Decide', who: 'Kai, the decision model', soon: 'In progress' },
|
||
{ id: 'act', verb: 'Act', who: 'Tools and agents' },
|
||
]
|
||
|
||
/**
|
||
* Five requests, each the kind of work its lane is for. The write lane's three
|
||
* are the presets' own jobs — volume, someone waiting, an expensive mistake —
|
||
* so the figure and the preset cards describe one product.
|
||
*/
|
||
const SCENES: readonly Scene[] = (
|
||
[
|
||
{ request: 'Review this change.', lane: 'write', pick: 'enso', why: 'Someone is waiting on the review.' },
|
||
{ request: 'Classify this ticket.', lane: 'write', pick: 'enso-flash', why: 'High volume, and a fast answer is enough.' },
|
||
{ request: 'File the bug and assign it.', lane: 'act', pick: 'tool call', why: 'The answer is an action, so it comes back as a tool call.' },
|
||
{ request: 'Should the deploy continue?', lane: 'decide', pick: 'Kai', why: 'A bounded choice with known options — Kai’s job once it ships inside Enso.' },
|
||
{ request: 'Audit this design.', lane: 'write', pick: 'enso-ultra', why: 'A wrong answer here is expensive.' },
|
||
] as const
|
||
).map((scene) => ({
|
||
...scene,
|
||
name: PRESETS.find((t) => t.id === scene.pick)?.name ?? (scene.pick === 'tool call' ? 'A tool call' : 'Kai · in progress'),
|
||
}))
|
||
|
||
/** The verbs the hero line turns through: the lanes, and the steps around them. */
|
||
const VERBS = ['write', 'decide', 'retrieve', 'act', 'route'] as const
|
||
|
||
/** What a call is weighed on. Policy is the fence, said under the three and drawn on the figure. */
|
||
const AXES = [
|
||
{ name: 'Quality', body: 'A call the cheap preset can answer does not spend the flagship.' },
|
||
{ name: 'Latency', body: 'When someone is waiting, the faster preset that can do the work comes first.' },
|
||
{ name: 'Cost', body: 'The dearer preset is for the call where being wrong is the expensive part.' },
|
||
] as const
|
||
|
||
/* ---- the story ---- */
|
||
|
||
/** The layers one request can touch. Enso is the router across them. */
|
||
const LAYERS = [
|
||
{ name: 'Models', body: 'Zen, and the frontier’s models, behind one endpoint.' },
|
||
{ name: 'Agents', body: 'Long-running agents in sandboxes, on a schedule or a trigger.' },
|
||
{ name: 'Tools', body: 'MCP tools your agents call, from search to a deploy.' },
|
||
{ name: 'Memory', body: 'Your files and knowledge, retrieved into the work.' },
|
||
{ name: 'Decisions', body: 'Kai answers a bounded choice with calibrated odds.', soon: 'Inside Enso: in progress' },
|
||
{ name: 'Data', body: 'Each organisation’s data in its own encrypted store.' },
|
||
{ name: 'Compute', body: 'Our cloud, or your own machine.' },
|
||
] as const
|
||
|
||
/** One request, start to answer. Every step is what Enso does today, except where it says. */
|
||
const RUN: readonly Step[] = [
|
||
{ title: 'Understand', body: 'Enso reads what the request is for — an answer, an action, or a choice.' },
|
||
{ title: 'Constrain', body: 'Your policy sets the bounds: the models you allow, and the budget on your key.' },
|
||
{ title: 'Route', body: 'The preset, and the model inside it, that should answer this call.' },
|
||
{ title: 'Act', body: 'When the answer is an action, it comes back as a tool call your agent runs.' },
|
||
{ title: 'Check', body: 'Ultra looks again when its first pass is unsure.' },
|
||
{ title: 'Learn', body: 'Scores from your traffic sharpen the next choice of the same kind.' },
|
||
]
|
||
|
||
/** What a team stitches together without a runtime. Each is a part of Hanzo's. */
|
||
const GLUE = [
|
||
'Model gateway',
|
||
'Agent runtime',
|
||
'Tools',
|
||
'Memory',
|
||
'Policy',
|
||
'Routing',
|
||
'Decisions',
|
||
'Observability',
|
||
'Compute',
|
||
] as const
|
||
|
||
/** What a route is chosen on. */
|
||
const FACTORS = [
|
||
{ name: 'Capability', body: 'Can this model do this work at all?' },
|
||
{ name: 'Latency', body: 'Is someone waiting on the answer?' },
|
||
{ name: 'Cost', body: 'What is a wrong answer worth here?' },
|
||
{ name: 'Policy', body: 'Which models is this call allowed to reach?' },
|
||
{ name: 'Context', body: 'How much does the call carry, and which window holds it?' },
|
||
] as const
|
||
|
||
/** An agent's loop. Every turn is a call, and every call is routed. */
|
||
const LOOP = ['Plan', 'Act', 'Observe', 'Decide', 'Continue'] as const
|
||
|
||
/** The path from an application to the metal, and what rides along it. */
|
||
const STACK = ['Your application', 'Enso', 'Models · Agents · Tools', 'Hanzo Compute'] as const
|
||
const ALONG = ['Routing', 'Observability', 'Policy', 'Authentication', 'Usage', 'Billing', 'Deployment'] as const
|
||
|
||
const LEARN: readonly Step[] = [
|
||
{ title: 'Rules first', body: 'A new kind of work has no scores yet, so the call follows the rules.' },
|
||
{ title: 'Then your scores', body: 'Scores from your traffic take over that kind of call.' },
|
||
{ title: 'Unsure, rules again', body: 'A score that is not confident gives the call back to the rules.' },
|
||
]
|
||
|
||
/** The three ways to route, one API for all of them. */
|
||
const WAYS = [
|
||
{ name: 'Static', body: 'Pin a model to a route or a tier. The same model answers, every time.' },
|
||
{ name: 'Policy', body: 'Set rules on cost, latency, capability, provider and data residency. Every call stays inside them.' },
|
||
{ name: 'Trained', body: 'Train a router on your own usage and evals, the way Enso is trained.' },
|
||
] as const
|
||
|
||
/** Ultra's two outcomes. Flash and Pro pick once; this is the flagship's extra pass. */
|
||
const PASS: readonly Step[] = [
|
||
{ title: 'Sure', body: 'The first pass holds, and that answer is the one that comes back.' },
|
||
{ title: 'Not sure', body: 'Ultra asks again and keeps the answer that holds.' },
|
||
]
|
||
|
||
/** Enterprise, as what is true of the platform today. */
|
||
const TRUST = [
|
||
{
|
||
question: 'Security',
|
||
answer: 'People sign in through Hanzo IAM with single sign-on. Secrets live in Hanzo KMS, never in code or config.',
|
||
},
|
||
{
|
||
question: 'Governance',
|
||
answer: 'The models you allow are a fence. Every preset, and every route inside it, stays within them.',
|
||
},
|
||
{
|
||
question: 'Control',
|
||
answer: 'Each key carries its own budget and rate, and shows when it was last used.',
|
||
},
|
||
{
|
||
question: 'Observability',
|
||
answer: 'Every call is metered and traced: the tokens, what they cost, and the span the call ran in.',
|
||
},
|
||
{
|
||
question: 'Reliability',
|
||
answer: 'When an upstream model fails, the call moves to the next one in its pool rather than failing with it.',
|
||
},
|
||
]
|
||
|
||
const CALL = `curl https://api.hanzo.ai/v1/chat/completions \\
|
||
-H "Authorization: Bearer $HANZO_API_KEY" \\
|
||
-H "Content-Type: application/json" \\
|
||
-d '{
|
||
"model": "enso",
|
||
"messages": [{ "role": "user", "content": "Plan the migration." }]
|
||
}'`
|
||
|
||
const PY = `import os
|
||
|
||
from openai import OpenAI
|
||
|
||
client = OpenAI(base_url="https://api.hanzo.ai/v1", api_key=os.environ["HANZO_API_KEY"])
|
||
|
||
answer = client.chat.completions.create(
|
||
model="enso", # or enso-flash, enso-ultra
|
||
messages=[{"role": "user", "content": "Plan the migration."}],
|
||
)`
|
||
|
||
const REST = SCENES.findIndex((s) => s.pick === (PRESETS.find((t) => t.featured)?.id ?? 'enso'))
|
||
|
||
/** A statement band: one line, set large, and the sentence that backs it. */
|
||
function Statement({ line, under }: { line: string; under: string }) {
|
||
return (
|
||
<Band measure={1024}>
|
||
<Rise items="center" gap="$4" $platform-web={{ textAlign: 'center' }}>
|
||
<Display render="h2" fontSize="$11" $lg={{ fontSize: 'var(--text-6xl)' }} $platform-web={{ textWrap: 'balance' }}>
|
||
{line}
|
||
</Display>
|
||
<Lede maxW={640}>{under}</Lede>
|
||
</Rise>
|
||
</Band>
|
||
)
|
||
}
|
||
|
||
/** A figure and its label, as the measured strip under the hero sets them. */
|
||
function Figure({ value, label, note, beat }: { value: ReactNode; label: string; note: string; beat: number }) {
|
||
return (
|
||
<Rise beat={beat} gap="$1" flex={1} minW={180}>
|
||
<Text fontSize="$10" lineHeight="$10" fontWeight="600" color="$ink" $platform-web={{ fontVariantNumeric: 'tabular-nums' }}>
|
||
{value}
|
||
</Text>
|
||
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="$ink" $platform-web={{ textTransform: 'uppercase', letterSpacing: '0.12em' }}>
|
||
{label}
|
||
</Text>
|
||
<Text fontSize="$2" lineHeight="$2" color="var(--text-secondary)">
|
||
{note}
|
||
</Text>
|
||
</Rise>
|
||
)
|
||
}
|
||
|
||
const JOBS = [
|
||
{ role: 'Coding and review', body: 'Pro is the default when someone is waiting on the change. The call is the endpoint you already speak.' },
|
||
{ role: 'A long, careful run', body: 'Ultra is the preset where a wrong answer costs more than the tokens.' },
|
||
{ role: 'Volume', body: 'Flash is the preset for a label, an extraction, or a short step, repeated.' },
|
||
{ role: 'A pool you bound', body: 'The choice stays inside the models you allow. That holds on every preset.' },
|
||
]
|
||
|
||
const QUESTIONS = [
|
||
{
|
||
question: 'What do I send?',
|
||
answer: 'Set model to enso, or to a preset id. One key, on /v1/chat/completions and on /v1/messages.',
|
||
},
|
||
{
|
||
question: 'What do the three presets change?',
|
||
answer: (
|
||
<>
|
||
The pool, and the rate.
|
||
{PRESETS.map((t) => (
|
||
<Fragment key={t.id}>
|
||
{' '}
|
||
{t.name} is <Price id={t.id} side="input" /> in and <Price id={t.id} side="output" /> out per million tokens.
|
||
</Fragment>
|
||
))}
|
||
</>
|
||
),
|
||
},
|
||
{
|
||
question: 'Can I see which model answered?',
|
||
answer: 'No. Enso is a closed family. You name the preset and the models you allow. The response is the answer.',
|
||
},
|
||
{
|
||
question: 'Does Enso route to Kai today?',
|
||
answer: 'Not yet. Kai is live on its own endpoint, /v1/decisions; routing a bounded choice to it from inside Enso is in progress.',
|
||
},
|
||
{
|
||
question: 'Where is the rest of the measurement?',
|
||
answer: 'The boards on this page are GPQA Diamond and LiveCodeBench, every row a research run read when the page opens. The GPQA write-up is on its own page, and the benchmarks page draws each board as accuracy against what the run cost at list prices.',
|
||
},
|
||
]
|
||
|
||
/** The headline, broken on purpose. Display owns the size; these are its lines. */
|
||
function Promise() {
|
||
return (
|
||
<>
|
||
The right intelligence.
|
||
<br />
|
||
Every time.
|
||
</>
|
||
)
|
||
}
|
||
|
||
/**
|
||
* The opening, drawn as /'s is: the same dot field drifting behind it, the
|
||
* same rungs of type and the same measure, the promise centred in its column
|
||
* over what Enso is, consensus mode, the turning verb and the two doors, with
|
||
* the figure beside it. The columns split at 1280, where each line of the
|
||
* promise holds on one line at the display's 52px; narrower, the figure sits
|
||
* under the copy, as /'s window does under its sign-up.
|
||
*/
|
||
function Hero() {
|
||
return (
|
||
<View render="section" aria-labelledby="enso-promise" position="relative" width="100%" overflow="hidden" {...under(0)} $platform-web={{ overflowX: 'clip' }}>
|
||
<Opening />
|
||
<View
|
||
position="relative"
|
||
display="grid"
|
||
gridTemplateColumns="minmax(0, 1fr)"
|
||
items="center"
|
||
width="100%"
|
||
maxW={1320}
|
||
mx="auto"
|
||
px="var(--page-gutter)"
|
||
pt="max(56px, min(12vh, 120px))"
|
||
pb="max(56px, min(10vh, 104px))"
|
||
gap="max(32px, min(5vw, 72px))"
|
||
$xl={{ gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)' }}
|
||
>
|
||
<YStack minW={0} gap="$4" items="center">
|
||
<Label center>Hanzo / Enso</Label>
|
||
<Display id="enso-promise" width="100%" text="center">
|
||
<Promise />
|
||
</Display>
|
||
<Lede text="center" maxW={460}>
|
||
Enso learns which AI model is best for your request, automatically balancing quality, speed and cost. If a provider fails, Enso can switch to another capable model.
|
||
</Lede>
|
||
<YStack
|
||
gap="$1"
|
||
items="center"
|
||
maxW={460}
|
||
px="$4"
|
||
py="$3"
|
||
borderWidth={1}
|
||
borderColor="var(--border)"
|
||
rounded={16}
|
||
bg="$panel"
|
||
$platform-web={{ borderRadius: '16px', textAlign: 'center' }}
|
||
>
|
||
<Text render="h2" fontSize="$3" lineHeight="$3" fontWeight="600" color="$ink">
|
||
Consensus mode
|
||
</Text>
|
||
<Text render="p" fontSize="$2" lineHeight="$2" color="var(--text-secondary)">
|
||
When accuracy matters more than cost, Enso brings multiple models together to independently evaluate, compare and verify their answers.
|
||
</Text>
|
||
</YStack>
|
||
<Words lead="Enso knows when to" verbs={VERBS} />
|
||
<XStack mt="$2" flexWrap="wrap" gap="$3" justify="center">
|
||
<Magnetic>
|
||
<Action href="https://platform.hanzo.ai/keys" fill>
|
||
Get API key <ArrowRight size={16} />
|
||
</Action>
|
||
</Magnetic>
|
||
<Action href="https://github.com/hanzoai/enso">Explore GitHub examples →</Action>
|
||
</XStack>
|
||
<More href="https://docs.hanzo.ai/docs/models/enso">Read the docs →</More>
|
||
</YStack>
|
||
<YStack minW={0}>
|
||
<Runtime lanes={LANES} scenes={SCENES} rest={Math.max(0, REST)} />
|
||
</YStack>
|
||
</View>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
export default function EnsoLanding({ models, labs }: { models: string; labs: number }) {
|
||
/* Rounded down, as the model count is: the catalog moves, and the claim should outlast a sync. */
|
||
const many = labs >= 20 ? `${Math.floor(labs / 10) * 10}+` : String(labs)
|
||
return (
|
||
<>
|
||
<SiteHeader />
|
||
|
||
<Box render="main" bg="$background" $platform-web={{ color: 'var(--foreground)' }}>
|
||
<Hero />
|
||
|
||
{/* What the runs say, first: three lines read from the research API when the page opens. */}
|
||
<Band id="lead" measure={1024}>
|
||
<Lead />
|
||
<XStack mt="$8" flexWrap="wrap" gap="$8" justify="space-between">
|
||
<Figure beat={0} value={models} label="Models" note={`From ${many} labs, behind one endpoint.`} />
|
||
<Figure beat={1} value="2" label="APIs it speaks" note="OpenAI-compatible and Anthropic-compatible, one key." />
|
||
</XStack>
|
||
</Band>
|
||
|
||
<Band measure={1024}>
|
||
<Head
|
||
eyebrow="One runtime"
|
||
title="One runtime. Every layer of AI."
|
||
lede="A request can touch a model, a tool, an agent, your memory, a decision and the machine it runs on. Enso is the runtime that decides which."
|
||
/>
|
||
<Grid columns={{ min: 220, max: 4 }} gap={12}>
|
||
{LAYERS.map((layer, n) => (
|
||
<Rise key={layer.name} beat={n}>
|
||
<Card display="flex" flexDirection="column" gap="$2" p="$5" height="100%" borderStyle={'soon' in layer ? 'dashed' : 'solid'}>
|
||
<XStack justify="space-between" items="baseline" gap="$2">
|
||
<Text render="h3" fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink">
|
||
{layer.name}
|
||
</Text>
|
||
</XStack>
|
||
<Text fontSize="$3" lineHeight="$3" color="var(--text-secondary)">
|
||
{layer.body}
|
||
</Text>
|
||
{'soon' in layer ? (
|
||
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
|
||
{layer.soon}
|
||
</Text>
|
||
) : null}
|
||
</Card>
|
||
</Rise>
|
||
))}
|
||
<Rise beat={LAYERS.length}>
|
||
<Card display="flex" flexDirection="column" justify="center" gap="$2" p="$5" height="100%" borderColor="var(--border-strong)" bg="$hover">
|
||
<Text render="h3" fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink">
|
||
Enso
|
||
</Text>
|
||
<Text fontSize="$3" lineHeight="$3" color="var(--text-secondary)">
|
||
The route between all of them, chosen per request.
|
||
</Text>
|
||
</Card>
|
||
</Rise>
|
||
</Grid>
|
||
</Band>
|
||
|
||
<Band measure={896}>
|
||
<Head
|
||
eyebrow="One request at a time"
|
||
title="Every request has a different best path."
|
||
lede="Same endpoint. A review, a label, an action and a choice each take the lane and the preset the work needs."
|
||
/>
|
||
<Requests scenes={SCENES} lanes={LANES} rest={Math.max(0, REST)} />
|
||
</Band>
|
||
|
||
<Statement
|
||
line="Spend intelligence where it matters."
|
||
under="The flagship is for the call where being wrong is expensive. Everything else gets the preset that is fast and enough."
|
||
/>
|
||
|
||
<Band measure={896}>
|
||
<Head eyebrow="The balance" title="Weighed the same way, every call." lede="Quality, latency and cost — inside the fence your policy draws." />
|
||
<Run rest="var(--muted-foreground)" flexDirection="row" flexWrap="wrap" justify="center" gap="$6">
|
||
{AXES.map((axis, i) => (
|
||
<XStack key={axis.name} data-beat={i}>
|
||
<Text fontSize="$6" lineHeight="$6" fontWeight="500" color="inherit">
|
||
{axis.name}
|
||
</Text>
|
||
</XStack>
|
||
))}
|
||
</Run>
|
||
<YStack mt="$8" borderTopWidth={1} borderColor="var(--border)">
|
||
{AXES.map((axis) => (
|
||
<YStack key={axis.name} gap="$2" py="$5" borderBottomWidth={1} borderColor="var(--border)">
|
||
<Text fontFamily="$mono" fontSize="$2" lineHeight="$2" color="$ink">
|
||
{axis.name}
|
||
</Text>
|
||
<Text fontSize="$4" lineHeight="$4" color="var(--text-secondary)">
|
||
{axis.body}
|
||
</Text>
|
||
</YStack>
|
||
))}
|
||
</YStack>
|
||
<Text display="block" mt="$5" fontSize="$3" lineHeight="$3" color="var(--text-secondary)">
|
||
Policy is the fence. The choice stays inside the models you allow.
|
||
</Text>
|
||
</Band>
|
||
|
||
<Band id="presets" measure={1024}>
|
||
<Head
|
||
eyebrow="Flash · Pro · Ultra"
|
||
title="Pick speed or depth per call."
|
||
lede="Name a preset when you already know. Name Enso when you want the choice. A preset is a pool and a rate; rates per million tokens, in → out."
|
||
/>
|
||
<Grid columns={{ min: 280, max: 3 }} gap={16}>
|
||
{PRESETS.map((t, n) => (
|
||
<Rise key={t.id} beat={n}>
|
||
<Card
|
||
display="flex"
|
||
flexDirection="column"
|
||
gap="$3"
|
||
p="$6"
|
||
height="100%"
|
||
borderColor={t.flagship ? 'var(--border-strong)' : 'var(--border)'}
|
||
bg={t.flagship ? '$hover' : '$panel'}
|
||
>
|
||
<YStack gap="$1">
|
||
<Text render="h3" fontSize="$6" lineHeight="$6" fontWeight="600" color="$ink">
|
||
{t.name}
|
||
</Text>
|
||
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
|
||
{t.featured ? 'Default · ' : ''}
|
||
{t.id}
|
||
</Text>
|
||
</YStack>
|
||
<Text py="$3" borderTopWidth={1} borderBottomWidth={1} borderColor="var(--border)" fontFamily="$mono" fontSize="$3" lineHeight="$3" color="$ink">
|
||
<Pair id={t.id} />
|
||
</Text>
|
||
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
|
||
<Call id={t.id} /> for 1,000 in and 1,000 out
|
||
</Text>
|
||
<Text fontSize="$3" lineHeight="$3" color="var(--text-secondary)">
|
||
{t.blurb}
|
||
</Text>
|
||
</Card>
|
||
</Rise>
|
||
))}
|
||
</Grid>
|
||
<XStack mt="$6" justify="center">
|
||
<More href="/pricing">Pricing →</More>
|
||
</XStack>
|
||
</Band>
|
||
|
||
<Band measure={768}>
|
||
<Head
|
||
eyebrow="End to end"
|
||
title="Watch one request run."
|
||
lede="Six steps between the request and the answer. You write none of them."
|
||
/>
|
||
<Walk id="enso-run" steps={RUN} />
|
||
</Band>
|
||
|
||
<Band measure={1024}>
|
||
<Head
|
||
eyebrow="The glue"
|
||
title="Stop stitching the AI stack together."
|
||
lede="A gateway here, an agent runtime there, a policy layer, a tracer, a GPU contract. Each is one more integration to keep alive. On Hanzo they are one runtime."
|
||
/>
|
||
<Run rest="var(--text-secondary)" flexDirection="row" flexWrap="wrap" justify="center" gap="$2" step={0.22}>
|
||
{GLUE.map((part, n) => (
|
||
<Chip key={part} data-beat={n} px={14} py={6} fontSize="$2" lineHeight="$2">
|
||
{part}
|
||
</Chip>
|
||
))}
|
||
</Run>
|
||
<YStack items="center" mt="$6" gap="$3">
|
||
<View width={1} height={40} bg="var(--border)" />
|
||
<Card px="$6" py="$4" borderColor="var(--border-strong)" bg="$hover">
|
||
<Text display="block" fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink" $platform-web={{ textAlign: 'center' }}>
|
||
Enso runtime
|
||
</Text>
|
||
<Text display="block" fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)" $platform-web={{ textAlign: 'center' }}>
|
||
one key · one bill · one trace
|
||
</Text>
|
||
</Card>
|
||
</YStack>
|
||
</Band>
|
||
|
||
<Band measure={1024}>
|
||
<Head
|
||
eyebrow="Models"
|
||
title="Every model you can name. One you don’t have to."
|
||
lede={`Enso picks among ${models} models from ${many} labs. It weighs five things.`}
|
||
/>
|
||
<Grid columns={{ min: 180, max: 5 }} gap={12}>
|
||
{FACTORS.map((f, n) => (
|
||
<Rise key={f.name} beat={n}>
|
||
<Card display="flex" flexDirection="column" gap="$2" p="$5" height="100%">
|
||
<Text render="h3" fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink">
|
||
{f.name}
|
||
</Text>
|
||
<Text fontSize="$3" lineHeight="$3" color="var(--text-secondary)">
|
||
{f.body}
|
||
</Text>
|
||
</Card>
|
||
</Rise>
|
||
))}
|
||
</Grid>
|
||
</Band>
|
||
|
||
<Band measure={896}>
|
||
<Head
|
||
eyebrow="Agents"
|
||
title="Agents inside a runtime, not a demo."
|
||
lede="An agent makes hundreds of calls to finish one task. Every turn is a call, and every call is routed: the plan gets depth, the routine step gets speed."
|
||
/>
|
||
<Run rest="var(--text-secondary)" flexDirection="row" flexWrap="wrap" justify="center" items="center" gap="$3" step={0.5}>
|
||
{LOOP.map((step, n) => (
|
||
<XStack key={step} data-beat={n} items="center" gap="$3">
|
||
<Text fontSize="$7" lineHeight="$7" fontWeight="600" color="inherit">
|
||
{step}
|
||
</Text>
|
||
{n < LOOP.length - 1 ? (
|
||
<Text aria-hidden fontSize="$5" lineHeight="$5" color="var(--muted-foreground)">
|
||
→
|
||
</Text>
|
||
) : null}
|
||
</XStack>
|
||
))}
|
||
</Run>
|
||
<Text display="block" mt="$6" fontSize="$3" lineHeight="$3" color="var(--text-secondary)" $platform-web={{ textAlign: 'center' }}>
|
||
Agents run in sandboxes, call tools over MCP, and keep their memory between runs.
|
||
</Text>
|
||
</Band>
|
||
|
||
<Band measure={896}>
|
||
<Head
|
||
eyebrow="Decisions"
|
||
title="Zen answers “what could we do?” Kai answers “what should we do?”"
|
||
lede="Kai takes the state, the evidence and the options, and returns a calibrated distribution over them — no prose to parse. Inside Enso, routing a bounded choice to Kai is in progress; Kai is live on its own today."
|
||
/>
|
||
<Grid columns={{ min: 260, max: 2 }} gap={16}>
|
||
<Card display="flex" flexDirection="column" gap="$2" p="$6">
|
||
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
|
||
Write · Zen
|
||
</Text>
|
||
<Text fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink">
|
||
Draft the migration plan.
|
||
</Text>
|
||
</Card>
|
||
<Card display="flex" flexDirection="column" gap="$2" p="$6" borderStyle="dashed">
|
||
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
|
||
Decide · Kai · in progress inside Enso
|
||
</Text>
|
||
<Text fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink">
|
||
Should the migration proceed?
|
||
</Text>
|
||
</Card>
|
||
</Grid>
|
||
<Text display="block" mt="$6" fontFamily="$mono" fontSize="$2" lineHeight="$2" color="var(--text-secondary)" $platform-web={{ textAlign: 'center' }}>
|
||
Kai today: POST /v1/decisions
|
||
</Text>
|
||
</Band>
|
||
|
||
<Band measure={768}>
|
||
<Head
|
||
eyebrow="Ultra"
|
||
title="Sure on the first pass. Checked when it is not."
|
||
lede="Flash and Pro pick once. Ultra answers once when the first pass holds, and looks again when it does not."
|
||
/>
|
||
<SecondPass steps={PASS} />
|
||
</Band>
|
||
|
||
<Band measure={768}>
|
||
<Head
|
||
eyebrow="Your traffic"
|
||
title="The same choice, getting sharper."
|
||
lede="A new workload follows the rules. Scores from your traffic take over, and the rules return when a score is unsure."
|
||
/>
|
||
<Walk id="enso-learn" steps={LEARN} />
|
||
<Text display="block" mt="$5" fontSize="$3" lineHeight="$3" color="var(--text-secondary)" $platform-web={{ textAlign: 'center' }}>
|
||
What is kept is the score, not the text of the prompt.
|
||
</Text>
|
||
</Band>
|
||
|
||
<Band id="routing" measure={1024}>
|
||
<Head eyebrow="Your routing" title="Route it your way." lede="Static, by policy, or trained on your own traffic. One API for all three." />
|
||
<Grid columns={{ min: 240, max: 3 }} gap={16}>
|
||
{WAYS.map((way, n) => (
|
||
<Rise key={way.name} beat={n}>
|
||
<Card display="flex" flexDirection="column" gap="$2" p="$6" height="100%">
|
||
<Text render="h3" fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink">
|
||
{way.name}
|
||
</Text>
|
||
<Text fontSize="$3" lineHeight="$3" color="var(--text-secondary)">
|
||
{way.body}
|
||
</Text>
|
||
</Card>
|
||
</Rise>
|
||
))}
|
||
</Grid>
|
||
<YStack mt="$6" items="center" gap="$2">
|
||
<Text display="block" fontFamily="$mono" fontSize="$3" lineHeight="$3" color="$ink" $platform-web={{ textAlign: 'center' }}>
|
||
{'"model": "enso", "consensus": true'}
|
||
</Text>
|
||
<Text display="block" fontSize="$3" lineHeight="$3" color="var(--text-secondary)" $platform-web={{ textAlign: 'center' }}>
|
||
Fixed policies are overrides in the console.
|
||
</Text>
|
||
</YStack>
|
||
</Band>
|
||
|
||
<Band id="benchmarks" measure={1024}>
|
||
<Head
|
||
eyebrow="Measured"
|
||
title="Accuracy, and what it costs."
|
||
lede="Enso beside single models from the labs, on the same questions: GPQA Diamond against what 1,000 questions cost, then LiveCodeBench."
|
||
/>
|
||
<YStack gap="$3">
|
||
<Text render="h3" fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink">
|
||
GPQA Diamond
|
||
</Text>
|
||
<Chart />
|
||
<Routed />
|
||
</YStack>
|
||
<YStack mt="$8" gap="$3">
|
||
<Text render="h3" fontSize="$5" lineHeight="$5" fontWeight="600" color="$ink">
|
||
LiveCodeBench
|
||
</Text>
|
||
<Ladder bench={CODE} />
|
||
</YStack>
|
||
<XStack mt="$5" flexWrap="wrap" justify="flex-end" gap="$3">
|
||
<More href="/benchmarks/gpqa">The GPQA page →</More>
|
||
<More href="/benchmarks#frontier">The frontier →</More>
|
||
</XStack>
|
||
</Band>
|
||
|
||
<Band measure={1024}>
|
||
<Head
|
||
eyebrow="Infrastructure"
|
||
title="From prototype to production without changing the architecture."
|
||
lede="The call you make on day one is the call you make at scale. What changes is what rides along it."
|
||
/>
|
||
<YStack items="center" gap="$2">
|
||
{STACK.map((layer, n) => (
|
||
<YStack key={layer} items="center" gap="$2" width="100%">
|
||
<Rise beat={n} width="100%" maxW={420}>
|
||
<Card px="$5" py="$4" borderColor={layer === 'Enso' ? 'var(--border-strong)' : 'var(--border)'} bg={layer === 'Enso' ? '$hover' : '$panel'}>
|
||
<Text display="block" fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink" $platform-web={{ textAlign: 'center' }}>
|
||
{layer}
|
||
</Text>
|
||
</Card>
|
||
</Rise>
|
||
{n < STACK.length - 1 ? <View width={1} height={20} bg="var(--border)" /> : null}
|
||
</YStack>
|
||
))}
|
||
</YStack>
|
||
<XStack mt="$8" flexWrap="wrap" justify="center" gap="$2">
|
||
{ALONG.map((part) => (
|
||
<Chip key={part} px={12} py={5} fontSize="$1" lineHeight="$1">
|
||
{part}
|
||
</Chip>
|
||
))}
|
||
</XStack>
|
||
</Band>
|
||
|
||
<Band measure={896}>
|
||
<Head
|
||
eyebrow="Drop-in"
|
||
title="Name Enso. It picks the model."
|
||
lede={`Same key on /v1/chat/completions and /v1/messages. Send enso, or name the preset. The SDK you already use works: point it at api.hanzo.ai.`}
|
||
/>
|
||
<YStack gap="$4">
|
||
<Sample lang="bash">
|
||
<Code lang="bash" code={CALL} />
|
||
</Sample>
|
||
<Sample lang="python">
|
||
<Code lang="python" code={PY} />
|
||
</Sample>
|
||
</YStack>
|
||
</Band>
|
||
|
||
<Band measure={1024}>
|
||
<Head eyebrow="Built for" title="What the presets are for." />
|
||
<Grid columns={{ min: 280, max: 2 }} gap={16}>
|
||
{JOBS.map((job, n) => (
|
||
<Rise key={job.role} beat={n}>
|
||
<Card display="flex" flexDirection="column" gap="$2" p="$5" height="100%">
|
||
<Text render="h3" fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink">
|
||
{job.role}
|
||
</Text>
|
||
<Text fontSize="$3" lineHeight="$3" color="var(--text-secondary)">
|
||
{job.body}
|
||
</Text>
|
||
</Card>
|
||
</Rise>
|
||
))}
|
||
</Grid>
|
||
</Band>
|
||
|
||
<Band measure={768}>
|
||
<Head eyebrow="Enterprise" title="Built for AI that actually runs businesses." lede="What holds today, said plainly." />
|
||
<Faq items={TRUST} />
|
||
</Band>
|
||
|
||
<Band measure={768}>
|
||
<Head eyebrow="Questions" title="Asked plainly." />
|
||
<Faq items={QUESTIONS} />
|
||
</Band>
|
||
|
||
<ModelCardSection
|
||
id="model-card"
|
||
badge="Enso · Model Router"
|
||
title="Microsecond model routing. Intelligent optimization across quality, latency, and cost."
|
||
description={
|
||
<>
|
||
Learned bilinear utility model evaluates each incoming prompt in 12 µs. Routes across <Models /> from frontier labs and Hanzo native weights behind one unified API.
|
||
</>
|
||
}
|
||
modelId="enso-auto"
|
||
endpoint="POST https://api.hanzo.ai/v1/chat/completions"
|
||
specs={[
|
||
{ label: 'Decision Objective', value: 'quality - λ·cost - μ·latency', mono: true },
|
||
{ label: 'Routing Latency', value: '300 ns rule-based · 12 µs learned policy', note: 'Measured in-process before inference' },
|
||
{ label: 'API Compatibility', value: 'OpenAI /v1/chat/completions & Anthropic /v1/messages' },
|
||
{ label: 'Managed SKUs', value: 'enso-auto · enso-flash · enso-ultra · enso-pro · enso-free', mono: true },
|
||
{ label: 'Context Capacity', value: 'Up to 1,000,000 tokens (model dependent)' },
|
||
{ label: 'Safety & Policy', value: 'Strict organization fences, budget caps, and model whitelist' },
|
||
{ label: 'Online Feedback', value: 'POST /v1/ai/feedback LinUCB contextual bandit updates' },
|
||
]}
|
||
sdkLinks={[
|
||
{ language: 'Python', href: 'https://github.com/hanzoai/enso/tree/main/examples/python', tag: 'examples/python' },
|
||
{ language: 'Go', href: 'https://github.com/hanzoai/enso/tree/main/examples/go', tag: 'examples/go' },
|
||
{ language: 'Rust', href: 'https://github.com/hanzoai/enso/tree/main/examples/rust', tag: 'examples/rust' },
|
||
{ language: 'TypeScript', href: 'https://github.com/hanzoai/enso/tree/main/examples', tag: 'examples' },
|
||
{ language: 'cURL', href: 'https://github.com/hanzoai/enso/tree/main/examples', tag: 'curl' },
|
||
]}
|
||
recipesLink={{
|
||
label: 'Modular Rust Routing Recipes Crate',
|
||
href: 'https://github.com/hanzoai/enso/tree/main/recipes',
|
||
}}
|
||
githubRepo="https://github.com/hanzoai/enso"
|
||
docsUrl="https://docs.hanzo.ai/docs/models/enso"
|
||
sampleCode={{
|
||
lang: 'bash',
|
||
code: `curl -X POST https://api.hanzo.ai/v1/chat/completions \\
|
||
-H "Authorization: Bearer $HANZO_API_KEY" \\
|
||
-H "Content-Type: application/json" \\
|
||
-H "X-Max-Latency-Ms: 800" \\
|
||
-d '{
|
||
"model": "auto",
|
||
"messages": [{"role": "user", "content": "Analyze and optimize this SQL query."}]
|
||
}'`,
|
||
}}
|
||
/>
|
||
|
||
<ProductUseCasesSection
|
||
eyebrow="Enterprise Solutions"
|
||
title="Battle-tested model routing blueprints across enterprise scale."
|
||
lede="Save up to 90% in LLM cloud compute costs while enforcing strict SLA latency caps and organization data fences."
|
||
useCases={[
|
||
{
|
||
tag: 'Cost and latency',
|
||
title: 'AI Inference Optimization & Workload Routing',
|
||
stack: 'Enso + Kai + Zen',
|
||
description: 'Learned bilinear utility model maps each prompt to the optimal model based on quality, latency, and token cost. Bypasses expensive frontier models when specialized open weights suffice.',
|
||
whitepaper: { label: 'Hanzo Router (Bilinear Utility)', href: 'https://papers.hanzo.ai/hanzo-router/' },
|
||
blogPost: { label: 'Route Prompts, Cut AI Costs 90%', href: 'https://hanzo.blog/blog/2026-07-07-hanzo-router-cut-ai-costs/' },
|
||
sourceCode: { label: 'recipes/src/routing.rs', href: 'https://github.com/hanzoai/enso/tree/main/recipes' },
|
||
actionLabel: 'Explore the routing recipe',
|
||
actionHref: 'https://github.com/hanzoai/enso/tree/main/recipes',
|
||
},
|
||
{
|
||
tag: 'Supply chain',
|
||
title: 'Distribution & EDI Exception Routing',
|
||
stack: 'Kai + Enso',
|
||
description: 'Parses ASN discrepancies, EDI 856 transmission mismatches, and missed carrier ETAs. Enso routes exceptions to automated disposition or escalation under strict SLA bounds.',
|
||
whitepaper: { label: 'HIP-0510 (Learned Router Spec)', href: 'https://hips.hanzo.ai/docs/hip-0510-enso-learned-router' },
|
||
blogPost: { label: 'Introducing Enso Intelligence', href: 'https://hanzo.blog/blog/introducing-enso/' },
|
||
sourceCode: { label: 'recipes/src/routing.rs', href: 'https://github.com/hanzoai/enso/tree/main/recipes' },
|
||
actionLabel: 'Explore the logistics recipe',
|
||
actionHref: 'https://github.com/hanzoai/enso/tree/main/recipes',
|
||
},
|
||
{
|
||
tag: 'Retail media',
|
||
title: 'Retail Media & Seller Campaign Ad Ops',
|
||
stack: 'Kai + Zen + Enso',
|
||
description: 'Evaluates seller promotional copy and sponsored banner creatives against trademark and safety guidelines. Enso routes real-time bidstream requests with deterministic latency.',
|
||
whitepaper: { label: 'HIP-1332 (Real-Time Policies)', href: 'https://hips.hanzo.ai/docs/hip-1332-kai-the-decision-model' },
|
||
blogPost: { label: 'The Router is the Registry', href: 'https://hanzo.blog/blog/2026-07-27-the-router-is-the-registry/' },
|
||
sourceCode: { label: 'recipes/src/routing.rs', href: 'https://github.com/hanzoai/enso/tree/main/recipes' },
|
||
actionLabel: 'Explore the ad ops recipe',
|
||
actionHref: 'https://github.com/hanzoai/enso/tree/main/recipes',
|
||
},
|
||
{
|
||
tag: 'Governance',
|
||
title: 'Audit, Evaluation & Enterprise Guardrails',
|
||
stack: 'Kai + Kata',
|
||
description: 'Guarantees strict enterprise boundary fences, automated verification, and false-alarm suppression across all routed AI requests in production.',
|
||
whitepaper: { label: 'HIP-1145 (Research Experiment Record)', href: 'https://hips.hanzo.ai/docs/hip-1145-research-experiment-record' },
|
||
blogPost: { label: 'Proof of AI: Deterministic Cloud', href: 'https://hanzo.blog/blog/proof-of-ai/' },
|
||
sourceCode: { label: 'recipes/src/routing.rs', href: 'https://github.com/hanzoai/enso/tree/main/recipes' },
|
||
actionLabel: 'Explore the audit recipe',
|
||
actionHref: 'https://github.com/hanzoai/enso/tree/main/recipes',
|
||
},
|
||
]}
|
||
/>
|
||
|
||
<ProductArticlesSection
|
||
eyebrow="Research & Architecture"
|
||
title="Routing theory, microsecond data planes, and learned utilities."
|
||
lede="Read engineering papers, latency benchmarks, and architectural designs from the Enso routing team."
|
||
articles={[
|
||
{
|
||
title: 'Introducing Enso — one API, routed to the best frontier intelligence',
|
||
href: 'https://hanzo.blog/blog/introducing-enso/',
|
||
date: '2026-07-17',
|
||
category: 'Architecture',
|
||
description: 'Enso routes each request to the model most likely to win it — and learns from your feedback as it goes. Microsecond routing, transparent metering, and a 1% fee that pays for itself.',
|
||
},
|
||
{
|
||
title: 'Route every prompt to the right model — and cut AI cloud costs by up to 90%',
|
||
href: 'https://hanzo.blog/blog/2026-07-07-hanzo-router-cut-ai-costs/',
|
||
date: '2026-07-07',
|
||
category: 'Economics',
|
||
description: 'Hanzo Router places each request on the cheapest model that can still serve it: local models for free, open weights that fit, or frontier cloud models when needed.',
|
||
},
|
||
{
|
||
title: 'The router is the registry',
|
||
href: 'https://hanzo.blog/blog/2026-07-27-the-router-is-the-registry/',
|
||
date: '2026-07-27',
|
||
category: 'Infrastructure',
|
||
description: 'Hanzo Cloud has no checked-in OpenAPI file. The live route table is the source, projected three ways, with bijection tests failing the build if any projection drifts.',
|
||
},
|
||
]}
|
||
/>
|
||
|
||
<Read
|
||
eyebrow="Read"
|
||
title="Models, benchmarks, research."
|
||
lede="The three presets, the runs that measure the router, and the writing."
|
||
groups={[
|
||
{
|
||
label: 'Models',
|
||
links: [
|
||
...PRESETS.map((t) => ({ href: modelPagePath(t.id), label: `${t.name} →` })),
|
||
{ href: '#presets', label: 'The three presets →' },
|
||
],
|
||
},
|
||
{
|
||
label: 'Benchmarks',
|
||
links: [
|
||
{ href: '#benchmarks', label: 'On this page →' },
|
||
{ href: '/benchmarks/gpqa', label: 'GPQA-Diamond →' },
|
||
{ href: '/models/enso', label: 'Enso, measured →' },
|
||
{ href: '/benchmarks', label: 'The suite →' },
|
||
],
|
||
},
|
||
{
|
||
label: 'Research',
|
||
links: [
|
||
{
|
||
href: 'https://papers.hanzo.ai/hanzo-router/',
|
||
label: 'Hanzo Router →',
|
||
note: 'The router paper. The live board is the one on this page.',
|
||
},
|
||
{
|
||
href: 'https://hips.hanzo.ai/docs/hip-0510-enso-learned-router',
|
||
label: 'HIP-0510 →',
|
||
note: 'The learned router.',
|
||
},
|
||
{ href: DOCS, label: 'Docs →' },
|
||
{ href: '/pricing', label: 'Pricing →' },
|
||
{ href: 'https://hips.hanzo.ai', label: 'HIPs →' },
|
||
{ href: '/research', label: 'Research →' },
|
||
],
|
||
},
|
||
]}
|
||
/>
|
||
<Siblings name="Enso" />
|
||
{product('enso') && <AgentPrompt product={product('enso')!} />}
|
||
<Closing title="Route every call through Enso." secondary={{ label: 'Explore routing recipes', href: 'https://github.com/hanzoai/enso/tree/main/recipes' }} />
|
||
</Box>
|
||
|
||
<SiteFooter />
|
||
</>
|
||
)
|
||
}
|