Files
zeekay 6a1df53fb3 home: the agent's own way in, the backers and the open source, kept on the focused landing
The focused landing keeps its seven parts and adds back three: Backing under the
hero (the fold gate holds "Backed by" at the first scroll), the open-source
section with this build's counts, and a band for agents: sign up with a wallet,
a key from POST /v1/account/keys, a top-up address from
POST /v1/billing/crypto/deposit, no human. The Platform menu and its footer
column lead with "Agents: get an API key in one call" again. The description
says the agent path and the track record; the home cards are redrawn for the new
title and every card takes address v5.
2026-10-26 15:51:00 -07:00

452 lines
17 KiB
TypeScript

/**
* Where the site goes: the hosts it links out to, and the one nav tree.
*
* The hosts come first, named once so no page hard-codes a URL. The tree comes
* last (`MENUS`, `LOGIN`, `GET_KEY`, `TRY`, `LEGAL`). The bar and the phone
* sheet draw `MENUS`. The footer draws `FOOT`. Company stays exported for the
* palette's old shape and is not a door in the bar. A page the sitemap offers
* is linked from the footer, because a menu's rows are not in the HTML until
* it opens.
*/
import type { GlyphName } from '@hanzogui/shell'
import { phrase } from '@hanzo/i18n'
import { LOGIN as SIGNIN, signUp } from '@hanzo/app/lib/destination'
import { dev, GET } from '@hanzo/app/lib/host'
import { NEXT, NOW } from '@/lib/zen'
// The app IS this site: `/` signed in, and it signs in here, at /login and
// /signup, never on another host.
//
// APP is what hanzo.app is now: the page you get the Hanzo App from — the
// download, the CLI, or the app in the browser. A link that means "get the
// product" and a link that means "open it" are different promises, and one
// constant serving both is how a download button quietly becomes a second
// front door.
export const APP = GET
export const SH = 'https://hanzo.sh'
export const CLOUD = 'https://cloud.hanzo.ai'
export const CONSOLE = 'https://platform.hanzo.ai'
/**
* Where a reader gets the one key everything is billed against. The console
* signs a reader in or up on the way, and a key costs nothing to hold, so this
* is where every "free" call to action ends: /signup ends at /chat, whose
* workspace is the paid plan. `/api-keys` is the address the console's own
* bundle links to; platform.hanzo.ai answers 200 with identical HTML for any
* path, so read the console's resolver before changing it.
*/
export const KEYS = `${CONSOLE}/api-keys`
/** Where a reader sets up Claude Code, Codex, Cursor or Antigravity to run through Hanzo:
* install, sign in, `hanzo code <tool>`. The platform page, not a second copy of it here. */
export const CODING = `${CONSOLE}/accelerate`
/** Hanzo Dev, the builder: the door that signs a stranger in and opens the
* app in Dev. hanzo.build forwards here. */
export const BUILD = dev()
/** Where a new person starts: this site's sign-up, then the app at `/`. Try
* Hanzo and the fold's Get started are both this one door. */
export { signUp }
export const STATUS = 'https://status.hanzo.ai'
export const GITHUB = 'https://github.com/hanzoai'
export const DOCS = 'https://docs.hanzo.ai'
export const BLOG = 'https://blog.hanzo.ai'
export const AGENCY = 'https://hanzo.agency'
export const STUDIO = 'https://studio.hanzo.ai'
export const UNIVERSITY = 'https://hanzo.university'
/** A call with the team, booked: Hanzo AI's page at Cal.com, where a visitor
* picks the 15- or 30-minute call. */
export const BOOKING = 'https://cal.com/hanzo'
/** The 30-minute intro itself. An event page, unlike the profile, takes the
* `name`, `email` and `notes` the sales form fills in. */
export const INTRO = `${BOOKING}/30min`
/** The ONE way into the chat, and it is no longer a hand-off.
*
* It used to forward the prompt to hanzo.chat, because this site did not
* implement a chat. It does: /chat on this origin IS the product. So the text
* travels as `?q=` on a same-origin navigation and the reader never leaves.
*
* `hz_ref=site` is gone with the crossing it existed for: hanzo.ai and
* hanzo.chat were separate origins, so a logged-out visitor had a DIFFERENT
* anonymousId on each and no per-person funnel could span them — the param
* carried the source surface so the far side could stamp its own event. One
* origin has one identity and the funnel joins itself.
*
* `submit=true` is gone for a stronger reason: it asked the far side to SEND
* on arrival, and /chat deliberately refuses to. A link is not consent to
* spend somebody's credits, so the text is TYPED INTO the composer and a
* human presses send. Passing a flag that the destination is right to ignore
* is a promise this code should stop making. */
/** The chat's address, WITH ITS TRAILING SLASH, named once.
*
* /chat is a mounted single-page app, not a Next route, and its index declares
* `<base href="/chat/">`. So the slash is not cosmetic and it is not optional:
* fetched at `/chat` the shell still answers 200, the base resolves against
* `/chat` as a FILE, the router matches nothing, and the app renders an empty
* body — 1016 bytes against 24990, no console error, no failed request. A
* blank page that reports success is the worst shape a bug can take, and it
* was on this site's primary call to action.
*
* hanzo.chat redirects to `/chat/` and therefore always worked; every link on
* this site said `/chat` and therefore never did. That asymmetry is why nobody
* caught it: the door people tested was not the door people clicked.
*
* IT IS `/chat`, WITHOUT THE SLASH — the trailing form was the fix for that and
* it moved the failure rather than removing it. `next.config.mjs` sets
* `trailingSlash: false`, so the export writes `out/chat.html` and no
* `out/chat/index.html`: a static host answers `/chat` with the page and
* `/chat/` with a 404. Every submitted question went to the second one.
*
* Measured on both planes. The export: `/chat` 200, `/chat/` 404. Production:
* `/chat?q=hi` 301s to `/chat/?q=hi` — the query rides the redirect, so the
* slashless form reaches the same room there with the question intact. One
* spelling that works in the export, in dev, and in production.
*
* One constant because there were nine spellings of this address and the tenth
* would have been wrong too. */
export const CHAT = '/chat'
/** The door for an AI agent, said once: the Platform menu and its footer column both lead with it. */
export const AGENTS: Link = { label: phrase('Agents: get an API key in one call'), href: '/agents' }
export function goToChat(prompt = '') {
const q = prompt.trim()
window.location.href = q ? `${CHAT}?q=${encodeURIComponent(q)}` : CHAT
}
// ── The nav tree ─────────────────────────────────────────────────────────────
//
// Shaped after openai.com's sitemap, entry for entry where Hanzo has the page:
// Company, Research, Products, Solutions (their Business), Developers, Learn
// (their Support and More), and Terms & Policies at the foot. A label with no
// page on this site has no row; a row is a page that answers.
//
// Every label, title and hint is English marked with `phrase` (@hanzo/i18n), so
// the extractor finds it, and drawn through `t` in the reader's language. A name
// standing alone (Hanzo Dev, Enso 1, Koma, GitHub) is not marked and is drawn as
// written, and so are the blog's article titles, which are English articles.
/** A destination. `hint` is a short second line, used only where a row needs one. */
export interface Link {
label: string
href: string
hint?: string
}
/** A titled column. A `lead` column sets its links in display type, first. */
export interface Group {
title: string
links: Link[]
lead?: boolean
}
/**
* A top-level entry: its label and mark, the page it stands for, and the
* columns it opens. `glyph` names a mark from the shell's one set. An entry
* with no columns opens nothing: it is a link to its page.
*/
export interface Menu {
id: string
label: string
href: string
glyph: GlyphName
groups: Group[]
}
/** A link leaves the site when it names a host. It says so with ↗ and a new tab. */
export const away = (href: string): boolean => /^https?:\/\//.test(href)
/** The company, opened from the wordmark. */
export const COMPANY: Menu = {
id: 'company',
label: phrase('Company'),
href: '/about',
glyph: 'user',
groups: [
{
title: phrase('Explore Company'),
lead: true,
links: [
{ label: phrase('About Us'), href: '/about' },
{ label: phrase('Reviews'), href: '/reviews' },
{ label: phrase('Careers'), href: '/careers' },
{ label: phrase('News'), href: '/press' },
],
},
{
title: phrase('Resources'),
links: [
{ label: phrase('Brand Guidelines'), href: '/brand' },
{ label: phrase('Philosophy'), href: '/philosophy' },
{ label: phrase('Investors'), href: '/investors' },
{ label: phrase('Contact'), href: '/contact' },
],
},
],
}
/** The bar, left to right after the wordmark.
*
* Six doors: Models, Dev, Platform, Pricing, Docs, Enterprise. Enso, Kai and
* Zen live under Models, with the creative models and the two research rows.
* The platform's products open under Platform. An entry with no columns is a
* link. Company and Learn are footer columns, not doors in the bar.
*/
export const MENUS: Menu[] = [
{
id: 'models',
label: phrase('Models'),
href: '/models',
glyph: 'pulse',
groups: [
{
title: phrase('Models'),
lead: true,
links: [
{ label: 'Enso', href: '/enso' },
{ label: 'Kai', href: '/kai' },
{ label: NOW.name, href: '/zen' },
{ label: phrase('All models'), href: '/models' },
{ label: phrase('Benchmarks'), href: '/benchmarks' },
],
},
{
title: phrase('Creative AI'),
links: [
{ label: 'Koma', href: '/koma' },
{ label: 'Iro', href: '/iro' },
{ label: 'Mori', href: '/mori' },
{ label: 'Oto', href: '/oto' },
{ label: 'Koe', href: '/koe' },
{ label: 'Uta', href: '/uta' },
],
},
{
// The catalog has no "Research preview" yet. The group uses the
// translated word, and the two rows are the preview generations.
title: phrase('Research'),
links: [
{ label: NEXT.name, href: NEXT.request },
{ label: 'Zen 6 Max', href: '/zen/zen6-max' },
],
},
],
},
{ id: 'dev', label: phrase('Dev'), href: BUILD, glyph: 'code', groups: [] },
{
id: 'platform',
label: phrase('Platform'),
href: '/cloud',
glyph: 'blocks',
groups: [
{
title: phrase('Platform'),
lead: true,
links: [
AGENTS,
{ label: phrase('Gateway'), href: '/gateway' },
{ label: 'MCP', href: '/mcp' },
{ label: 'Base', href: '/base' },
{ label: phrase('Search'), href: '/search' },
{ label: 'Studio', href: '/studio' },
{ label: phrase('Machines'), href: '/machines' },
{ label: phrase('Observability'), href: '/o11y' },
{ label: phrase('Insights'), href: '/insights' },
{ label: phrase('Commerce'), href: '/commerce' },
{ label: phrase('Datastore'), href: '/datastore' },
{ label: phrase('Memory'), href: '/products/memory' },
],
},
{
title: phrase('Apps'),
links: [
{ label: phrase('Chat'), href: CHAT },
{ label: 'Hanzo App', href: APP },
{ label: 'Hanzo Bot', href: '/bot' },
{ label: 'Console', href: CONSOLE },
],
},
],
},
{ id: 'pricing', label: phrase('Pricing'), href: '/pricing', glyph: 'card', groups: [] },
{ id: 'docs', label: phrase('Docs'), href: DOCS, glyph: 'book', groups: [] },
{ id: 'enterprise', label: phrase('Enterprise'), href: '/enterprise', glyph: 'puzzle', groups: [] },
]
/** Log in goes straight to this site's sign-in. */
export const LOGIN: Link = { label: phrase('Log in'), href: SIGNIN }
/** The filled control in the bar: the one key everything is billed against. */
export const GET_KEY: Link = { label: phrase('Get API Key'), href: KEYS }
/** Try Hanzo stays the door a solution page offers. The bar's control is GET_KEY. */
export const TRY: Link = { label: phrase('Try Hanzo'), href: '/signup' }
/** What the same control says to a reader who is signed in: the app, at `/`. */
export const OPEN: Link = { label: phrase('Open Hanzo'), href: '/' }
/** Terms & Policies. The documents live here so the export keeps them; the
* footer shows the short line below, and /legal indexes the rest. */
export const LEGAL: Link[] = [
{ label: phrase('Terms of use'), href: '/terms' },
{ label: phrase('Privacy policy'), href: '/privacy' },
{ label: phrase('Usage policy'), href: '/aup' },
{ label: phrase('Cookie policy'), href: '/cookies' },
{ label: phrase('Privacy choices'), href: '#choices' },
{ label: phrase('Other policies'), href: '/legal' },
]
/**
* The footer. The bar is six doors; a page the sitemap offers still needs a
* link in the HTML, and a menu's rows are not in the HTML until it opens.
* Solutions keeps four. The index stays on those pages, not in this chrome.
*/
export const FOOT: Menu[] = [
{
id: 'models-foot',
label: phrase('Models'),
href: '/models',
glyph: 'pulse',
groups: [
{
title: phrase('Models'),
lead: true,
links: [
{ label: phrase('All models'), href: '/models' },
{ label: 'Enso', href: '/enso' },
{ label: 'Kai', href: '/kai' },
{ label: NOW.name, href: '/zen' },
{ label: phrase('Benchmarks'), href: '/benchmarks' },
{ label: phrase('Creative AI'), href: '/studio' },
{ label: NEXT.name, href: NEXT.request },
{ label: 'Zen 6 Max', href: '/zen/zen6-max' },
],
},
],
},
{
id: 'platform-foot',
label: phrase('Platform'),
href: '/cloud',
glyph: 'blocks',
groups: [
{
title: phrase('Platform'),
lead: true,
links: [
AGENTS,
{ label: phrase('Gateway'), href: '/gateway' },
{ label: 'MCP', href: '/mcp' },
{ label: 'Base', href: '/base' },
{ label: phrase('Search'), href: '/search' },
{ label: 'Studio', href: '/studio' },
{ label: phrase('Machines'), href: '/machines' },
{ label: phrase('Observability'), href: '/o11y' },
{ label: phrase('Insights'), href: '/insights' },
{ label: phrase('Commerce'), href: '/commerce' },
{ label: phrase('Datastore'), href: '/datastore' },
{ label: phrase('Memory'), href: '/products/memory' },
],
},
],
},
{
id: 'developers-foot',
label: phrase('Developers'),
href: DOCS,
glyph: 'code',
groups: [
{
title: phrase('Developers'),
lead: true,
links: [
{ label: phrase('Dev'), href: BUILD },
{ label: phrase('Download'), href: '/download' },
{ label: phrase('Chat'), href: CHAT },
{ label: 'Hanzo App', href: APP },
{ label: 'Hanzo Bot', href: '/bot' },
{ label: 'Console', href: CONSOLE },
{ label: 'Gen', href: '/gen' },
{ label: phrase('CLI'), href: '/cli' },
{ label: phrase('SDKs'), href: '/sdks' },
{ label: phrase('Documentation'), href: DOCS },
{ label: 'GitHub', href: GITHUB },
{ label: phrase('Status'), href: STATUS },
{ label: phrase('Open source'), href: '/open-source' },
{ label: 'Forge', href: 'https://forge.hanzo.ai' },
{ label: phrase('Security'), href: '/security' },
{ label: phrase('Compare'), href: '/compare' },
{ label: phrase('Startups'), href: '/startups' },
],
},
],
},
{
id: 'solutions-foot',
label: phrase('Solutions'),
href: '/solutions/engineering',
glyph: 'puzzle',
groups: [
{
title: phrase('Solutions'),
lead: true,
links: [
{ label: phrase('Engineering'), href: '/solutions/engineering' },
{ label: phrase('Agents'), href: '/solutions/agents' },
{ label: phrase('Research'), href: '/solutions/research' },
{ label: phrase('Data'), href: '/solutions/data' },
],
},
],
},
{
id: 'learn-foot',
label: phrase('Learn'),
href: '/learn',
glyph: 'book',
groups: [
{
title: phrase('Learn'),
lead: true,
links: [
{ label: phrase('Blog'), href: '/blog' },
{ label: phrase('Guides'), href: `${DOCS}/docs/guides/` },
{ label: phrase('Cookbook'), href: `${DOCS}/docs/guides/cookbook/` },
{ label: phrase('Help center'), href: '/support' },
{ label: phrase('Customer stories'), href: '/customers' },
],
},
],
},
{
id: 'company-foot',
label: phrase('Company'),
href: '/about',
glyph: 'user',
groups: [
{
title: phrase('Company'),
lead: true,
links: [
{ label: phrase('About'), href: '/about' },
{ label: phrase('Careers'), href: '/careers' },
{ label: phrase('News'), href: '/press' },
{ label: phrase('Brand'), href: '/brand' },
{ label: phrase('Philosophy'), href: '/philosophy' },
{ label: phrase('Investors'), href: '/investors' },
{ label: phrase('Contact'), href: '/contact' },
],
},
],
},
]
/** The line under the columns. /legal holds the policies that are not named here. */
export const FOOT_LINE: Link[] = [
{ label: phrase('Terms'), href: '/terms' },
{ label: phrase('Privacy'), href: '/privacy' },
{ label: phrase('Usage policy'), href: '/aup' },
{ label: phrase('Privacy choices'), href: '#choices' },
]