mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
Pages from /machines to /risk, the model pages, and the kits they are made of
(page-kit, ProductLanding, LayerPage, Stack, the models kit, band's Head) mark
authored copy where it is written and draw it in the reader's language. A server
page marks a literal phrase('…') or wraps it in <T>; a client page calls t. The
kits translate a string prop through useSay (components/marketing/say), so a
page passes phrase('…') and markup passes as written. An arrow that points
turns over right to left (data-i18n-mirror).
Rebased on main: the fold-based heroes (Fold, PageHero, MachinesHero, the
product and layer pages) keep main's structure and copy, with their label,
title and lede marked phrase. /platform is main's CloudLanding; the deleted
/platform/layers and the old referral components stay deleted.
Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
113 lines
4.9 KiB
TypeScript
113 lines
4.9 KiB
TypeScript
'use client'
|
||
|
||
/**
|
||
* What Hanzo costs.
|
||
*
|
||
* THE PLANS FIRST, THE METER ON THE API TAB. A reader buys a plan — Free, a
|
||
* personal rung, Team by the seat, or Enterprise by conversation — so the page
|
||
* opens on the plans as cards (Plans) and the questions. What a metered bill is
|
||
* made of (Tariff) and every model's rate belong to the API tab, and are drawn
|
||
* only while it is the tab on screen.
|
||
*
|
||
* NOTHING HERE IS TYPED, AND NOTHING IS BAKED. The plans come from commerce's
|
||
* plan authority (GET /v1/billing/plans), and the rate card, the model rates and
|
||
* the tool rates from the price list (GET /v1/pricing), each read when the page
|
||
* opens (lib/api) — so a price a SuperAdmin edits at admin.hanzo.ai is the price
|
||
* the next reader sees, and a backend that does not answer is said to be
|
||
* unreachable rather than stood in for. scripts/audit-price-literals.mjs fails
|
||
* the build on a typed copy: a number a reader sees is a number somebody could
|
||
* be charged.
|
||
*/
|
||
|
||
import dynamic from 'next/dynamic'
|
||
import { Suspense, useState } from 'react'
|
||
import { Box, XStack } from '@hanzo/ui'
|
||
import { More } from '@hanzo/ui/marketing'
|
||
import { useI18n } from '@hanzo/i18n/react'
|
||
import { Line } from '@/components/type'
|
||
import Plans, { type Audience } from '@/components/pricing/Plans'
|
||
import { PlansJsonLd } from '@/components/pricing/PlansJsonLd'
|
||
import Tariff from '@/components/pricing/Tariff'
|
||
import { Wide, Sub } from '@/components/pricing/kit'
|
||
import { Ground } from '@/components/marketing/fold'
|
||
|
||
// Below the plans and the card, so not on the path to a usable page: the model
|
||
// table and the questions. Both are still prerendered (the table as its loading
|
||
// line, the questions whole), so a crawler meets them in the HTML; their scripts
|
||
// load at low priority, and each sits in its own Suspense boundary so the rest
|
||
// of the page hydrates without waiting for them.
|
||
const APIPricing = dynamic(() => import('@/components/pricing/APIPricing'))
|
||
const PricingFAQ = dynamic(() => import('@/components/pricing/PricingFAQ'))
|
||
const Savings = dynamic(() => import('@/components/sales/Savings'))
|
||
|
||
// The view is stated by Plans, once per tab a reader looks at, with the plans it
|
||
// shows (lib/analytics/tags.ts track).
|
||
export default function PricingPage() {
|
||
// The tab Plans has on screen: the meter and the model rates are the API tab's.
|
||
const [tab, setTab] = useState<Audience | null>(null)
|
||
const { t } = useI18n()
|
||
return (
|
||
<>
|
||
{/* The first screen is the plans: their title, who they are for, the term
|
||
and the cards, on the shared ground (components/marketing/fold). */}
|
||
<Ground label="Pricing">
|
||
<Plans onTab={setTab} />
|
||
</Ground>
|
||
<PlansJsonLd />
|
||
|
||
{/* A BLOCK, not a stack. gui's YStack is a flex column, and a flex column
|
||
shrinks its children to the height it was given — so a section taller
|
||
than the viewport gets squashed rather than scrolled. Nothing here
|
||
needs flex. */}
|
||
<Box pb={48} px="$4">
|
||
{tab === 'api' ? (
|
||
<>
|
||
<Tariff />
|
||
<Wide marginTop={96}>
|
||
<Sub
|
||
title={t('Every model, at the price on your bill')}
|
||
lede={t('Model inference is metered per token in five disjoint tiers, at the tariff for the completion window the request asked for.')}
|
||
/>
|
||
<Suspense>
|
||
<APIPricing />
|
||
</Suspense>
|
||
</Wide>
|
||
</>
|
||
) : null}
|
||
|
||
{/* What the same tokens cost, priced three ways from the served rates (components/sales/Savings). */}
|
||
<Wide marginTop={96}>
|
||
<Sub
|
||
title={t('What your tokens cost')}
|
||
lede={t('Put in the models and the volume you run today. The page prices it at the providers’ list rates, on Hanzo Cloud, and on GPUs of your own, every rate read from the price list as you look.')}
|
||
/>
|
||
<Suspense>
|
||
<Savings />
|
||
</Suspense>
|
||
<XStack mt={24} flexWrap="wrap" columnGap={24} rowGap={8} justify="center">
|
||
<More href="/calculator">{t('The whole operating-cost model')} →</More>
|
||
<More href="https://docs.hanzo.ai/docs/pricing">{t('How pricing works')} →</More>
|
||
</XStack>
|
||
</Wide>
|
||
|
||
<Suspense>
|
||
<PricingFAQ />
|
||
</Suspense>
|
||
|
||
<Wide maxWidth={768} marginTop={48} marginBottom={32}>
|
||
<Line render="p" size="base" tone="muted" align="center">
|
||
{t('Need help sizing seats or configuring the right plan? {link}', {
|
||
link: <a href="/buying-specialist">{t('Talk to our AI Buying Specialist →')}</a>,
|
||
})}
|
||
</Line>
|
||
<Line render="p" size="sm" tone="muted" align="center" marginTop={8}>
|
||
{t('Need dedicated VPC clusters, custom fine-tuning, or high-volume enterprise SLAs? {link}', {
|
||
link: <a href="/enterprise">{t('Contact our engineering team →')}</a>,
|
||
})}
|
||
</Line>
|
||
</Wide>
|
||
</Box>
|
||
</>
|
||
)
|
||
}
|