/* Hallmark · v6.20.0 · shape: Stage on flat navy, the reference's left-text right-visual anatomy with Chinese content designed anew (no photographs: the product leads) · genre: modern-minimal · job: sign up — 开始使用, the hero pill · theme: none — the Franca portal's own dark token system (navy ground, one azure signal, brand-truth) with smoked-glass product faces and lit top edges · nav: wordmark, 文档/服务 links, session slot + one compact pill at the bar's own scale · footer: a mast, the wordmark over the links · hero: stage — the faint onboarding code above the head (dimmed to background, the base_url line one step brighter), the 64px/600 Chinese head carrying the reference's own ink transition, the column's foot riding the glass panel's foot (bright at the cap height, falling to blue-grey at the baseline) on a lit azure ground over the reference's own dot grid, both fading below the fold, next-token prediction live in smoked glass right, the page's one declared ambient loop (15s) · signature: the model writing the page's own headline, candidate by candidate — probabilities, bars, the winner joining the stream · sections: hero · pricing · platform (models and functions merged) · footer */
/* critique: the user ruled the big type down — the 64px display lives in the hero alone, every section head sits at 25px, and the mass comes from prose: the functions band carries the platform's real mechanics (hosted tool_calls, PTC orchestration, MCP discovery), the pricing band carries the billing system's own two levers (speed tier, time-of-day window) as controls the visitor pulls · this pass brings over the reference's designed layer: the colour transition lives in the display type itself, the hero opens under a bloom, every band enters under a hairline that fades at both ends, and the glass gains the 104° directional light · the air above the head carries one real integration call as a dim texture, the column's foot rides the panel's foot, and the masked azure ground still fades before the fold · the user called the layering messy: two stacked textures (dot grid under code) read as noise, so the grid is out and the code alone is the hero's texture · this pass follows the user's ordering: billing moves to the second band, the model lineup and the function mechanics merge into the third, and the console mockup is gone — the product has a real console, a mockup of it on the homepage says nothing · this pass adds the ICP filing number to the footer as a beian.miit.gov.cn link beside the copyright, where the regulation puts it · this pass fixes the picker's arithmetic: the speed tier and the time window are compound discounts (折上折), so the readout multiplies the two, and the two placeholder disclaimers step down to a 13px foot size · this pass puts the vendors' marks on the model cards, monochrome in the page's own ink (deepseek、qwen、kimi、minimax from Simple Icons, glm recut from the product favicon, all one white) — the marks identify the lineup · this pass puts them back in their own colours after the monochrome cut failed on the dark ground: currentColor does not reach into an img-loaded SVG (it resolves to black), so four logos went black-on-navy — the marks now carry their vendor colours (the user's ruling), Kimi in white because its own mark is black, GLM in its own blue cut from the favicon · this pass answers the finish review's one open finding: the browser surfaces join the palette — a selection sits in the brand azure, the caret rides the page ink, the scrollbar's thumb holds the border strength · this pass reworks the logged-in session slot into the portal's own account face (the user's bug): the chip that showed a name beside the avatar is gone — the button is avatar-only, minted from the username exactly as the portal mints it, and identity lives in the dropdown it opens; that dropdown carries the holder's name and sign-out, and sign-out spends the portal's own logout endpoint, then drops the mirrored franca_session cookie so every face under the domain follows · this pass takes the user's two corrections: the menu shows the holder's username (the portal's own menu does — never the display name), and the dropdown anchors to the trigger button's own right edge, not the whole session slot, so the avatar sits at the menu's right end as it does in the portal's align-end popover · the user then ruled the avatar-only trigger too narrow a door for the dropdown's left reach: the trigger now carries the username beside the avatar (huangxing on the bar, as the old chip carried a name), so the button widens leftward and the menu's leftward reach shrinks to match · still too far left for the user: this pass narrows the dropdown itself (its type steps down to 13px and 12px, its width to 176px) and widens the trigger's gap from the console pill so the trigger sits further left and the menu drops under it, not beyond it · the user asked whether the leftward pop was hard-coded — it was: the menu anchored its right edge to the trigger's, so it could only grow left, and the 176px cut wrapped the head onto two lines · this pass anchors the LEFT edge instead: the menu grows rightward from the trigger's own left end (sliding under the console pill, which the user said to ignore), its width returns to 200px so the head holds one line, and the sub line is nowrap · with the left anchor in place the user ruled the popup should fill the bar's right end exactly: the menu now spans the whole session slot — left edge at the trigger's left end, right edge flush with the bar's right end, no fixed width, and the wrapper the right-edge anchor needed is gone · the user dragged the page onto a laptop (about 1093 to 1280 CSS px wide, 720 tall after scaling) and the air was gone: the display type was already fluid, but every big space was a fixed desktop number (160px hero padding, 144px bands, 96px column gap, 560px panel), so a smaller canvas kept desktop-sized air and crowded · this pass makes the rhythm fluid the same way the type already was — clamp() on the hero and band paddings, the column gaps and the panel height — and adds one short-viewport relief (max-height 780px) so the fold holds on 720 and 768 screens · the account tagline follows the user's copy ruling: FRANCA · 用户门户 · the user called the type stiff (生硬) and asked for a comfort pass: the leading was set to Latin habits (body 1.6, heads 1.15, display 1.1) while hanzi are full-width blocks that need the Chinese reading zone — this pass raises the leading (body 1.8, heads 1.25, display 1.15) and gives the display heads a hair of positive tracking (0.01em), the opposite of the Latin rule and the reverse of the old zero-tracking note, because a wall of 64px hanzi breathes between the glyphs, not inside them; the numeric faces already sat on tabular figures · the user found the comfort pass invisible and the footer too tall — both fair: the footer's air (64px top pad, 48px to the meta, 48px below) was masthead scale for a two-row footer, so it steps down to its own content (40px, 24px, 32px), and the type rulings stay as they are — their register is felt, not seen, and the visible levers if wanted are named to the user, not spent unasked · the user asked for a clearly-licensed softer hanzi face to replace Noto: the pick is Alibaba PuHuiTi 3.0 (Alibaba Design + Hanyi, © Alibaba) — its public license grants free commercial use including web embedding and subsetting, and its rounded-geometric skeleton softens the institutional feel of Noto/Source Han while staying technical · three faces ship (55-Regular maps to 400, 65-Medium to 500, 75-SemiBold to 600 — the page's whole weight ladder, verified against the TTFs' own usWeightClass), subset to the two pages' actual characters plus full ASCII and common punctuation, ~46KB each, self-hosted immutable · the stack keeps Geist Sans first (Latin keeps its voice) with Noto Sans SC staying second: any glyph the subset misses — including future copy edits — falls back to Noto, so the page can never render a bare system hanzi · the user ruled the footer out of the page rhythm: the masthead is gone, and what is left is a tail — one 13px row under the hairline, lockup and links at the left, the copyright and the ICP filing pushed to the row's right end — so the last band now walks straight out of the page and the filing number keeps its regulated place at the bottom of the document · the user called the functions band's copy 流水账 — engineer's narration instead of product voice — and this pass rewrites it benefit-first: each paragraph now opens on the pain or the claim, the bold words carry the sell (一次请求，直达最终答案, 长任务不烧多余的 token), and the mechanism stays in one compressed sentence that the figure beside it already walks step by step · the rewrite's new glyphs exposed the subset's real design flaw: the hanzi set followed the page text exactly, so every copy edit walked into missing glyphs and fell back to Noto mid-sentence — the faces are re-subset as charset v2 (the two pages' text plus a common product-copy buffer, 516 chars, ~58KB a weight, new immutable filenames), so a normal copy edit no longer touches the fonts · this pass re-subsets to charset v3 (669 chars): the twelve new error pages walked the v2 buffer into missing glyphs mid-sentence, so the union takes every character every page of the site carries plus the same product-copy buffer discipline — new immutable filenames again (puhuiti-c4), and the pages this change touches (index, docs, docs/errors, eleven problem pages) all move to this sheet · the v3 pass exposed the buffer itself as the flaw: a hand-picked common-copy list that missed even 得, so one new sentence fell back to Noto mid-word again — v4 replaces the hand list with the product's own corpus (every hanzi in the portal and console UI plus the gateway's Chinese bundles, union with all site text, 751 chars), which is the copy this site actually gets edited with, so future doc pages stop walking into missing glyphs */
/* finish-review: in-thread · 4 findings · 1 fixed: F16 browser surfaces unthemed · 3 dismissed: the 3 折 echo across hero and pricing is a number carrying recall, the two bands sharing one hairline treatment are the same anatomy at the page's only rhythm, the docs tail's flat rows are the prose sections' line spacing, not an empty screen · copy check: every head names its own content, none passes unchanged on a competitor's page */
/* Hallmark · waive 26b · no :disabled or aria-disabled anywhere on either page this sheet serves — the pricing picker never disables a segment and the docs samples keep all tabs available · a disabled face has no element to land on */
/* Hallmark · dismissed 42 · the nav keeps the reference skeleton's shape: the wordmark, the links and the session slot on one transparent line; it earns glass once it leaves the hero */
/* Hallmark · waive 59 · the footer is not a section: the user ruled the
   masthead out, and one 13px row under a hairline is a tail below the last
   band's own exit — the air above it is the band finishing, not a walk
   interrupted · the page ends on the row, the filing keeps its place */
/* Hallmark · waive 78 · the home head 模型推理，函数托管 at 64px and the docs head 接入文档 at 40px carry their page's only display type; every section head (计费、模型与函数、三行代码接入、照常工作的部分) sits at 25px · the user ruled the big type down: the mass comes from prose, not display size (layout.md § 2) */
/* Hallmark · waive 2b · the head 模型推理，函数托管 and the docs head 接入文档 carry a single-hue ramp from two tokens (--ink-display-a to --ink-display-b, both at hue 250): the bright end is near-white and the dim end still clears 4.5:1 on the navy ground, so 40 and 41 pass at every stop · the user asked for the reference site's own craft: the colour transition lives inside the display type, not in a colour band */
/* Hallmark · waive 77 · the closing band is out by the user's ruling: its few words (创建 API Key、模型别名、进入控制台、the base_url chip) repeated the nav pill and the hero code's highlighted line, so the home page ends on the models-and-functions band; the docs band 照常工作的部分 is three hairline rows of prose (SDK、重试、流式输出、函数调用、用量明细) · the rows and the job's one action are the content, not width-filling padding, and the docs page is a reference the visitor reads and leaves (layout.md § 3 and § 5) */
/* answered, on the record, not waivable: 40 — brand-1 #0284C7 under white text
   measures about 4.0:1, below AA. Kept because it is the production
   portal's measured primary colour, and the homepage must match the console
   it sends users to; every such face is 14px+ medium weight. The keyboard's
   skip link and the hero pill both sit on the darker gradient end instead,
   which clears AA on its own */

/* The hanzi face: Alibaba PuHuiTi 3.0, official TTFs (© Alibaba, license
   grants free commercial use, web embedding and subsetting). The family's
   own weight numbers map onto the ladder the page uses: 55=400 body,
   65=500 labels and heads, 75=600 display and buttons (each verified
   against the TTF's usWeightClass). Subset to this site's characters;
   Noto Sans SC stays in the stack below it, so any glyph the subset
   misses — or any future copy edit — falls back to the old face, never
   to a bare system hanzi. */
@font-face {
	font-family: 'Alibaba PuHuiTi';
	font-style: normal;
	font-display: swap;
	font-weight: 400;
	src: url('../fonts/puhuiti-c4-55-Regular.woff2') format('woff2');
}

@font-face {
	font-family: 'Alibaba PuHuiTi';
	font-style: normal;
	font-display: swap;
	font-weight: 500;
	src: url('../fonts/puhuiti-c4-65-Medium.woff2') format('woff2');
}

@font-face {
	font-family: 'Alibaba PuHuiTi';
	font-style: normal;
	font-display: swap;
	font-weight: 600;
	src: url('../fonts/puhuiti-c4-75-SemiBold.woff2') format('woff2');
}

/* ------------------------------------------------------------------ */
/* Tokens — lifted from franca-portal/src/styles/tokens.css (the real   */
/* brand system). Every colour and font below rides a token.            */
/* ------------------------------------------------------------------ */

:root {
	color-scheme: dark;

	/* Ground and surfaces: the portal's dark navy; structure by borders. */
	--bg: #0a0f1e;
	--surface: #0d1428;
	--frost: oklch(1 0 0 / 4%);
	--frost-strong: oklch(1 0 0 / 8%);
	--halo: oklch(1 0 0 / 6%);
	--halo-strong: oklch(1 0 0 / 10%);
	--border: oklch(1 0 0 / 8%);
	--border-strong: oklch(1 0 0 / 16%);

	/* Ink: two steps; weaker hierarchy by size, not opacity. */
	--text-1: oklch(0.95 0.01 250);
	--text-2: oklch(0.68 0.02 250);

	/* Primary: one azure accent; the gradient lives on the logo mark only.
	   Filled controls sit on the gradient's dark end, the one azure face that
	   clears AA under white text on its own. */
	--brand-1: #0284c7;
	--on-brand: oklch(0.985 0.005 250);
	--brand-grad-a: #0ea5e9;
	--brand-grad-b: #0369a1;

	/* Sign-out: the one row that reads as a verdict, not a transaction.
	   The portal's own dark-ground danger pair, so both faces agree. */
	--danger: #f87171;
	--danger-bg: rgba(220, 38, 38, 0.15);

	/* Code roles: the panels keep their own ink set. */
	--code-accent: #38bdf8;
	--code-string: #7dd3fc;

	/* Glass: smoked navy with a lit top edge, over the flat ground. */
	--glass: oklch(0.21 0.03 255 / 62%);
	--glass-edge: oklch(1 0 0 / 12%);
	--glass-lift: oklch(0.1 0.02 255 / 24%);

	/* The reference's designed layer, tokenised: display ink that falls with
	   descent, the hero's bloom pair, the glass's directional tint pair and
	   the filled control's lit edge. */
	--ink-display-a: oklch(0.98 0.008 250);
	--ink-display-b: oklch(0.74 0.035 250);
	--bloom-core: oklch(0.82 0.1 240 / 14%);
	--bloom-wide: oklch(1 0 0 / 5%);
	--hero-code-ink: oklch(1 0 0 / 28%);
	--glass-tint-a: oklch(1 0 0 / 3%);
	--glass-tint-b: oklch(1 0 0 / 8%);
	--btn-edge: oklch(1 0 0 / 22%);

	--radius-sm: 8px;
	--radius: 16px;
	--radius-pill: 999px;
	--speed: 160ms;
	--ease-out: cubic-bezier(0.22, 1, 0.36, 1);

	--font:
		'Geist Sans', 'Alibaba PuHuiTi', 'Noto Sans SC Variable',
		-apple-system, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
	--font-mono:
		ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, Consolas, monospace;

	/* The Chinese ramp: 64/500 display (96/400 read fontless for hanzi),
	   25/500 section heads, weight and size carrying the hierarchy. Display
	   tracking is a hair POSITIVE (0.01em, on the heads themselves): hanzi
	   are full-width blocks, they breathe between glyphs, not inside them. */
	--fs-display: clamp(40px, 4.6vw, 64px);
	--fs-head: clamp(28px, 3vw, 40px);
	--fs-h3: 25px;
	--fs-num: 28px;
	--fs-body: 16px;
	--fs-sm: 14px;
	--fs-foot: 13px;
	--fs-xs: 12px;

	/* The measured measure: 1232px of content at 1440 viewport. */
	--measure: 1280px;
	--gutter: 24px;
}

/* ------------------------------------------------------------------ */
/* Base                                                                 */
/* ------------------------------------------------------------------ */

* {
	box-sizing: border-box;
}

html,
body {
	overflow-x: clip;
}

html {
	-webkit-text-size-adjust: 100%;
	/* The page's own scrollbar: a quiet thumb at the border strength, the
	   track left to the ground. */
	scrollbar-color: var(--border-strong) transparent;
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--text-1);
	font-family: var(--font);
	font-size: var(--fs-body);
	/* Latin body reads fine at 1.6; hanzi have taller visual bodies, so the
	   comfortable Chinese reading zone is 1.75-1.9 — the page's prose is
	   Chinese, so the page sits at 1.8 (the user's 生硬 report). */
	line-height: 1.8;
	-webkit-font-smoothing: antialiased;
	caret-color: var(--text-1);
}

/* Browser surfaces carry the palette too: a selection sits in the brand
   azure under the page ink, so even the transient states stay on-system. */
::selection {
	background: var(--brand-1);
	color: var(--on-brand);
}

h1,
h2,
h3 {
	font-weight: 500;
	letter-spacing: 0;
	/* Same ruling as the body: 1.15 is a Latin head's line; a hanzi head
	   that wraps needs 1.25 so the two lines do not sit back to back. */
	line-height: 1.25;
	margin: 0;
	overflow-wrap: break-word;
	min-width: 0;
	text-wrap: balance;
}

p {
	margin: 0;
}

a {
	color: var(--text-1);
	text-decoration: none;
}

:focus-visible {
	outline: 2px solid var(--code-accent);
	outline-offset: 2px;
	border-radius: 2px;
}

.measure {
	max-width: var(--measure);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

/* ------------------------------------------------------------------ */
/* Skip link                                                           */
/* ------------------------------------------------------------------ */

.skip {
	position: absolute;
	left: var(--gutter);
	top: -48px;
	z-index: 10;
	padding: 8px 16px;
	/* The gradient's dark end: the one brand face that must clear AA on its own,
	   because it is the keyboard's door, not a display label. */
	background: var(--brand-grad-b);
	color: var(--on-brand);
	border-radius: var(--radius-sm);
	transition: top var(--speed);
}

.skip:focus-visible {
	top: var(--gutter);
}

/* ------------------------------------------------------------------ */
/* Controls — one filled pill, one quiet link                           */
/* ------------------------------------------------------------------ */

.btn-primary {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0 24px;
	height: 42px;
	white-space: nowrap;
	/* The dark end of the gradient: filled controls must clear AA on their own,
	   so brand-1 stays for the avatar (answered below), not for text faces. */
	background: var(--brand-grad-b);
	color: var(--on-brand);
	/* The reference's filled controls carry a top-light at their edge: the
	   one filled face on the page gets the same 1px of light as the glass. */
	box-shadow: inset 0 1px 0 var(--btn-edge);
	border-radius: var(--radius-pill);
	font-size: var(--fs-body);
	font-weight: 500;
	transition:
		background-color var(--speed),
		color var(--speed);
}

.btn-primary:hover {
	background: color-mix(in oklab, var(--brand-grad-b) 84%, var(--on-brand));
}

.btn-primary:active {
	background: color-mix(in oklab, var(--brand-grad-b) 86%, var(--bg));
}

.btn-lg {
	height: 52px;
	padding: 0 32px;
	font-size: 17px;
}

/* The nav carries its action at the bar's own scale: a compact pill that
   sits beside the 14px links instead of over them. The one large pill per
   page stays in the hero and the close; the nav is chrome, not the offer. */
.btn-sm {
	height: 36px;
	padding: 0 16px;
	font-size: var(--fs-sm);
}

.link-quiet {
	color: var(--text-2);
	border-bottom: 1px solid transparent;
	padding-bottom: 2px;
	transition: color var(--speed);
}

.link-quiet:hover {
	color: var(--text-1);
}

.link-quiet:active {
	color: var(--code-accent);
}

/* ------------------------------------------------------------------ */
/* Nav — 64px, transparent over the hero, slate glass past it           */
/* ------------------------------------------------------------------ */

.nav {
	position: sticky;
	top: 0;
	z-index: 300;
	/* Transparent over the hero; the solid face declared below runs the
	   header's full width, not the 1280px inner measure, so the bar is
	   edge-to-edge the moment it appears. */
	border-bottom: 1px solid transparent;
	transition:
		background-color var(--speed),
		border-color var(--speed);
}

.nav-inner {
	display: flex;
	align-items: center;
	gap: 32px;
	height: 64px;
}

.nav-solid {
	background: color-mix(in oklab, var(--surface) 72%, transparent);
	backdrop-filter: blur(16px);
	-webkit-backdrop-filter: blur(16px);
	border-bottom-color: var(--border);
}

.lockup {
	display: inline-flex;
	align-items: center;
	gap: 12px;
	color: var(--text-1);
	font-weight: 600;
	font-size: 15px;
}

.lockup:hover {
	color: var(--text-1);
}

.nav-links {
	display: flex;
	gap: 24px;
	font-size: var(--fs-sm);
}

.nav-links a {
	color: var(--text-2);
	transition: color var(--speed);
}

.nav-links a:hover,
.nav-links a:active {
	color: var(--text-1);
}

.nav-session {
	display: flex;
	align-items: center;
	gap: 32px;
	margin-left: auto;
	/* The dropdown's containing block: the trigger is the slot's first
	   child, so the menu anchored left-and-right here spans exactly from
	   the trigger's left end to the bar's right end. */
	position: relative;
}

.login-link {
	color: var(--text-2);
	font-size: var(--fs-sm);
	font-weight: 500;
	white-space: nowrap;
	transition: color var(--speed);
}

.login-link:hover,
.login-link:active {
	color: var(--text-1);
}

/* The logged-in account trigger: the avatar coin, the holder's username,
   then the caret. The name on the bar (the user's ruling) widens the
   trigger leftward, so the dropdown's left reach no longer dwarfs it.
   A frost hover on the round pill names it a control. */
.account-btn {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	height: 32px;
	padding: 0 12px;
	border: none;
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--text-1);
	cursor: pointer;
	transition: background var(--speed);
}

.account-btn:hover,
.account-btn:active,
.account-btn[aria-expanded='true'] {
	background: var(--frost-strong);
}

.account-name {
	font-size: var(--fs-sm);
	font-weight: 500;
	color: var(--text-1);
	white-space: nowrap;
}

.account-btn svg {
	color: var(--text-2);
	flex: none;
}

/* The avatar the portal mints: the username's first glyph on the brand
   azure coin, 26px inside the 32px trigger. */
.avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	flex: none;
	background: var(--brand-1);
	color: var(--on-brand);
	border-radius: 50%;
	font-size: 12px;
	font-weight: 600;
}

/* The account dropdown: identity head over sign-out, right-aligned to the
   bar's own edge. It rides the page's one glass recipe; the blur is its
   own because the bar it drops from only blurs once solid, and the menu
   floats over whatever the visitor is looking at. */
.account-menu {
	display: none;
	position: absolute;
	top: calc(100% + 12px);
	/* The menu fills the session slot exactly (the user's ruling): left edge
	   at the trigger's left end, right edge flush with the bar's right end,
	   over the console pill — a dropdown overlays what it drops onto. */
	left: 0;
	right: 0;
	flex-direction: column;
	gap: 4px;
	min-width: 200px;
	padding: 8px;
}

.account-menu.is-open {
	display: flex;
	backdrop-filter: blur(16px);
	-webkit-backdrop-filter: blur(16px);
}

.account-head {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 4px 4px 8px;
}

.account-head-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.account-head-name {
	font-size: var(--fs-foot);
	font-weight: 600;
	color: var(--text-1);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.account-head-sub {
	font-size: var(--fs-xs);
	color: var(--text-2);
	white-space: nowrap;
}

.menu-sep {
	height: 1px;
	margin: 4px 0;
	background: var(--border);
}

.menu-row {
	display: flex;
	align-items: center;
	height: 28px;
	padding: 0 8px;
	border: none;
	border-radius: var(--radius-sm);
	background: transparent;
	font-family: inherit;
	font-size: var(--fs-foot);
	font-weight: 500;
	color: var(--text-2);
	cursor: pointer;
	transition:
		background var(--speed),
		color var(--speed);
}

.menu-row:hover {
	background: var(--frost);
	color: var(--text-1);
}

/* Sign-out reads as a verdict, not a transaction — the danger pair. */
.menu-row.danger {
	color: var(--danger);
}

.menu-row.danger:hover {
	background: var(--danger-bg);
	color: var(--danger);
}

/* ------------------------------------------------------------------ */
/* Hero — the Chinese head left, the glass conversation right           */
/* ------------------------------------------------------------------ */

.hero {
	position: relative;
	/* 160 above and 160 below put the grid's last line near 780 at both fold
	   sizes: past the 84% floor of a 900 screen, inside an 800 fold. */
	/* Fluid like the type above it: full air at 1440+, one step down on a
	   laptop canvas, 72px at the floor. Fixed 160px held desktop air on a
	   1093px laptop and crowded the fold (the user's report). */
	padding: clamp(72px, 11vw, 160px) 0 clamp(72px, 11vw, 160px);
}

/* The head sat in empty air; the reference's answer for that air is light.
   One masked layer below the content: an azure ground centred on the copy
   column, gone well before the fold. The earlier dot grid is out — two
   textures under the code read as noise, and the code alone is the hero's
   texture now. */
.hero::before {
	content: '';
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background:
		radial-gradient(64% 72% at 40% 22%, var(--bloom-core), transparent 72%),
		radial-gradient(140% 100% at 50% -10%, var(--bloom-wide), transparent 70%);
	-webkit-mask-image: linear-gradient(to bottom, black 16%, transparent 92%);
	mask-image: linear-gradient(to bottom, black 16%, transparent 92%);
}

.hero-grid {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
	gap: clamp(48px, 6.6vw, 96px);
	/* The column's foot rides the panel's foot — the user's own ruling. The
	   air above the head is not left empty: the faint onboarding code carries
	   it, and the masked ground fades through what remains. */
	align-items: end;
}

.hero-code {
	position: absolute;
	top: 0;
	/* The containing block of an absolutely positioned grid child is the
	   grid's padding box, but the in-flow copy starts at the content box —
	   one .measure gutter in. The code steps in by the same gutter so its
	   flush-left lines sit on the head's own left edge. */
	left: var(--gutter);
	/* The code owns the copy column's air and stops short of the panel. */
	right: 52%;
	z-index: 0;
	margin: 0;
	font-family: var(--font-mono);
	font-size: var(--fs-sm);
	line-height: 1.7;
	color: var(--hero-code-ink);
	white-space: pre;
	/* The snippet holds full ink through the client setup, then fades line
	   by line from response = client.chat.completions.create( — the user's
	   own ruling — and is gone before the print line, so the texture ends
	   with the story: set up, call, read the answer. */
	-webkit-mask-image: linear-gradient(to bottom, black 54%, transparent 92%);
	mask-image: linear-gradient(to bottom, black 54%, transparent 92%);
}

/* The one line that matters — the base_url swap — stays a step brighter in
   the platform's azure; everything else recedes to texture. */
.hero-code .hc-hl {
	color: color-mix(in oklab, var(--code-accent) 55%, transparent);
}

/* The copy and the panel paint above the code layer; the code is behind
   them by name, not by chance. */
.hero-grid > :not(.hero-code) {
	position: relative;
	z-index: 1;
}

.hero-copy h1 {
	font-size: var(--fs-display);
	/* Hanzi display wants more weight than the Latin default: 600 at this
	   size reads as set type, 500 reads as a stretched body face. */
	font-weight: 600;
	/* Two comfort rulings in one place: 1.15 keeps a wrapped second line
	   off the first's descender space, and the hair of positive tracking
	   airs the glyph walls — the Latin instinct (tight) is wrong here. */
	line-height: 1.15;
	letter-spacing: 0.01em;
}

/* The reference's headline carries its colour transition inside the type —
   bright ink at the cap height falling to blue-grey at the baseline —
   clipped to the glyphs. Declared behind @supports so the flat white face
   stays for any browser that cannot clip to text. */
@supports (-webkit-background-clip: text) or (background-clip: text) {
	.hero-copy h1 {
		background: linear-gradient(
			180deg,
			var(--ink-display-a) 8%,
			var(--ink-display-b) 92%
		);
		-webkit-background-clip: text;
		background-clip: text;
		color: transparent;
	}
}

.hero-sub {
	margin-top: 24px;
	max-width: 30em;
	color: var(--text-2);
	font-size: 17px;
}

.hero-actions {
	display: flex;
	align-items: center;
	gap: 24px;
	margin-top: 32px;
	flex-wrap: wrap;
}

/* ------------------------------------------------------------------ */
/* Glass — smoked navy, lit top edge, one deep shadow                   */
/* ------------------------------------------------------------------ */

.glass {
	/* The reference lights its surfaces from the top-left at 104°: a tint so
	   low it reads as light rather than colour, laid over the smoked fill. */
	background:
		linear-gradient(104deg, var(--glass-tint-a), var(--glass-tint-b)),
		var(--glass);
	border-radius: var(--radius);
	/* The lit top edge plus the hairline: glass without its lit edge is a
	   grey box, and the shadow keeps it off the ground. No backdrop blur:
	   the ground behind is flat navy, so blur would blur nothing. */
	box-shadow:
		inset 0 1px 0 var(--glass-edge),
		0 0 0 1px var(--border),
		0 32px 64px var(--glass-lift);
}

.gen-panel {
	display: flex;
	flex-direction: column;
	gap: 20px;
	/* The panel is the hero's mass: its 560px sets the pair's height at both
	   fold sizes; the copy column tops out above the actions and the masked
	   ground fills the rest. */
	/* The panel is the pair's height floor, so it rides the same fluid
	   scale: 560px at the wide end, down to 440px on a laptop canvas. */
	min-height: clamp(440px, 39vw, 560px);
	padding: 32px;
	min-width: 0;
}

.panel-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding-bottom: 20px;
	border-bottom: 1px solid var(--border);
}

.panel-title {
	font-size: var(--fs-body);
	font-weight: 500;
}

.panel-state {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	color: var(--text-2);
	font-size: var(--fs-sm);
}

.dot {
	display: inline-block;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--code-accent);
}

/* ------------------------------------------------------------------ */
/* The generation demo — fully rendered by default, replayed by script  */
/* ------------------------------------------------------------------ */

.gen-prompt {
	display: flex;
	align-items: baseline;
	gap: 12px;
	font-size: var(--fs-sm);
	color: var(--text-2);
	min-width: 0;
}

.gen-prompt-text {
	color: var(--text-1);
	overflow-wrap: break-word;
	min-width: 0;
}

.gen-label {
	font-size: var(--fs-sm);
	color: var(--text-2);
}

.gen-block {
	display: flex;
	flex-direction: column;
	gap: 12px;
	min-width: 0;
}

.gen-stream {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	min-height: 44px;
}

.tok {
	padding: 4px 12px;
	background: var(--frost);
	border: 1px solid var(--border);
	border-radius: var(--radius-sm);
	font-size: 17px;
	line-height: 1.5;
	transition:
		opacity 400ms var(--ease-out),
		transform 400ms var(--ease-out);
}

.gen-panel.is-live .tok {
	opacity: 0;
	transform: translateY(8px);
}

.gen-panel.is-live .tok.is-on {
	opacity: 1;
	transform: translateY(0);
}

.gen-cands {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.cand {
	display: grid;
	grid-template-columns: 64px minmax(0, 1fr) 44px;
	align-items: center;
	gap: 12px;
	transition:
		opacity 400ms var(--ease-out),
		transform 400ms var(--ease-out);
}

.gen-panel.is-live .cand {
	opacity: 0;
	transform: translateY(6px);
}

.gen-panel.is-live .cand.is-in {
	opacity: 1;
	transform: translateY(0);
}

.gen-panel.is-live .cand.is-out {
	opacity: 0.3;
	transform: translateY(0);
}

.cand-tok {
	font-family: var(--font-mono);
	font-size: var(--fs-sm);
	color: var(--text-2);
	white-space: nowrap;
}

.cand-bar {
	position: relative;
	height: 8px;
	border-radius: var(--radius-pill);
	background: var(--frost);
	overflow: hidden;
}

.cand-fill {
	position: absolute;
	inset: 0;
	transform-origin: left center;
	background: var(--border-strong);
	transition: transform 600ms var(--ease-out);
}

.cand.is-win .cand-fill {
	background: var(--code-accent);
}

.cand.is-win .cand-tok {
	color: var(--text-1);
}

.cand-pct {
	text-align: right;
	color: var(--text-2);
	font-variant-numeric: tabular-nums;
	font-size: var(--fs-sm);
}

.caret {
	display: inline-block;
	width: 2px;
	height: 1.2em;
	background: var(--code-accent);
	opacity: 0;
}

.caret.is-typing {
	opacity: 1;
	animation: caret-beat 0.9s steps(1) infinite;
}

.gen-foot {
	margin-top: auto;
	display: flex;
	justify-content: space-between;
	padding-top: 16px;
	border-top: 1px solid var(--border);
	color: var(--text-2);
	font-size: var(--fs-sm);
}

.fig {
	color: var(--text-1);
	font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ */
/* Bands — section heads at 25px; the mass is prose, not display type   */
/* ------------------------------------------------------------------ */

.band {
	position: relative;
	padding: clamp(64px, 10vw, 144px) 0;
}

/* Each band enters under a hairline that fades at both ends — the reference
   marks a section without ruling a full-width line. */
.band::before {
	content: '';
	position: absolute;
	top: 0;
	left: 50%;
	transform: translateX(-50%);
	width: min(var(--measure), 100%);
	height: 1px;
	background: linear-gradient(
		90deg,
		transparent,
		var(--border-strong) 50%,
		transparent
	);
}

.band h2 {
	font-size: var(--fs-h3);
}

.band-lead {
	margin-top: 16px;
	max-width: 38em;
	color: var(--text-2);
}

/* Prose columns: the functions band carries its mechanics as body text. */
.band-copy {
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin-top: 32px;
	max-width: 42em;
	color: var(--text-2);
}

.band-copy strong {
	color: var(--text-1);
	font-weight: 500;
}

.band-copy code {
	font-family: var(--font-mono);
	font-size: var(--fs-sm);
	color: var(--code-accent);
}

/* ------------------------------------------------------------------ */
/* Model wall — one glass card per model, name and one line each.       */
/* Each card is a slot: the real lineup replaces these five.            */
/* ------------------------------------------------------------------ */

.model-grid {
	display: grid;
	grid-template-columns: repeat(5, minmax(0, 1fr));
	gap: 16px;
	margin-top: 64px;
}

.m-card {
	padding: 24px;
	min-width: 0;
}

/* The vendor marks carry their own colours — the user's ruling after the
   monochrome cut failed: currentColor does not cascade into an img-loaded
   SVG, so the marks went black on navy. Kimi's own mark is black and takes
   the white face the brand uses on dark grounds. */
.m-logo {
	display: block;
	width: 24px;
	height: 24px;
}

.m-card h3 {
	margin-top: 20px;
	font-size: 17px;
}

.m-card p {
	margin-top: 8px;
	color: var(--text-2);
	font-size: var(--fs-sm);
}

/* ------------------------------------------------------------------ */
/* Functions band — the platform's mechanics as prose left, one hosted  */
/* call's route through the server as a glass panel right.              */
/* ------------------------------------------------------------------ */

.func-grid {
	display: grid;
	grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
	gap: clamp(40px, 4.4vw, 64px);
	margin-top: 64px;
}

.func-panel {
	padding: 32px;
	min-width: 0;
	align-self: start;
}

.panel-label {
	font-size: var(--fs-sm);
	color: var(--text-2);
	padding-bottom: 16px;
	border-bottom: 1px solid var(--border);
}

.key-row {
	display: flex;
	align-items: baseline;
	gap: 16px;
	padding: 16px 0;
	border-bottom: 1px solid var(--border);
	font-size: var(--fs-sm);
	color: var(--text-2);
	min-width: 0;
}

.key-row:last-child {
	border-bottom: 0;
}

.key-name {
	font-family: var(--font-mono);
	color: var(--code-accent);
}

/* ------------------------------------------------------------------ */
/* Pricing — the billing system's own two levers (time-of-day window,   */
/* speed tier) as controls the visitor pulls, with the night hours      */
/* shown on a 24-hour strip. Multipliers are placeholders pending the   */
/* real values from the console.                                        */
/* ------------------------------------------------------------------ */

.price-panel {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: clamp(40px, 4.4vw, 64px);
	margin-top: 64px;
	padding: 48px;
	min-width: 0;
}

.price-copy {
	display: flex;
	flex-direction: column;
	gap: 16px;
	min-width: 0;
}

.price-copy p {
	color: var(--text-2);
}

/* The strip sits directly on the panel: a second glass box inside the
   glass panel would be a card inside a card, so the hours carry only
   their own cells and axis. */
.strip {
	display: grid;
	grid-template-columns: repeat(24, minmax(0, 1fr));
	gap: 4px;
	margin-top: 16px;
}

.strip i {
	height: 44px;
	border-radius: 4px;
	background: var(--frost);
}

.strip i.night {
	background: color-mix(in oklab, var(--code-accent) 45%, transparent);
}

.strip-axis {
	display: flex;
	justify-content: space-between;
	margin-top: 12px;
	color: var(--text-2);
	font-size: var(--fs-sm);
}

/* Placeholder disclaimers read a step under the panel's body: they qualify
   the sample numbers, they are not part of the pitch. */
.strip-cap {
	margin-top: 20px;
	color: var(--text-2);
	font-size: var(--fs-foot);
}

.pick {
	display: flex;
	flex-direction: column;
	gap: 24px;
	min-width: 0;
}

.pick-block {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.seg {
	display: inline-flex;
	gap: 4px;
	padding: 4px;
	border: 1px solid var(--border);
	border-radius: var(--radius-pill);
	width: fit-content;
}

.seg button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0 20px;
	height: 44px;
	border: 0;
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--text-2);
	font-family: var(--font);
	font-size: var(--fs-sm);
	white-space: nowrap;
	cursor: pointer;
	transition:
		color var(--speed),
		background-color var(--speed);
}

.seg button:hover {
	color: var(--text-1);
}

.seg button.is-on {
	color: var(--text-1);
	background: var(--frost-strong);
}

.pick-out {
	display: flex;
	align-items: baseline;
	gap: 16px;
	flex-wrap: wrap;
	padding-top: 20px;
	border-top: 1px solid var(--border);
}

.out-label {
	color: var(--text-2);
	font-size: var(--fs-sm);
}

.out-mult {
	font-size: var(--fs-num);
	font-weight: 500;
	font-variant-numeric: tabular-nums;
}

.out-note {
	color: var(--text-2);
	font-size: var(--fs-sm);
}

.pick-foot {
	color: var(--text-2);
	font-size: var(--fs-foot);
}

/* ------------------------------------------------------------------ */
/* Footer                                                               */
/* ------------------------------------------------------------------ */

.footer {
	border-top: 1px solid var(--border);
	/* The masthead footer is gone by the user's ruling: one small row is
	   the page's tail, not a section. The hairline still separates it from
	   the last band; the air above it is the band's own exit, not the
	   footer's. */
	padding: 24px 0 32px;
}

.footer-inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 24px;
	flex-wrap: wrap;
}

.footer-links {
	display: flex;
	align-items: center;
	gap: 24px;
	font-size: var(--fs-foot);
}

.footer-links a {
	color: var(--text-2);
}

.footer-links a:hover,
.footer-links a:active {
	color: var(--text-1);
}

.footer-meta {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 8px 24px;
	margin-left: auto;
	color: var(--text-2);
	font-size: var(--fs-foot);
}

/* The filing number is regulation's link: it opens the MIIT registry in a
   new tab and dresses like the rest of the mast's quiet text. */
.footer-meta a {
	color: var(--text-2);
}

.footer-meta a:hover,
.footer-meta a:active {
	color: var(--text-1);
}

/* ------------------------------------------------------------------ */
/* Responsive                                                           */
/* ------------------------------------------------------------------ */

@media (max-width: 960px) {
	.hero {
		padding: 96px 0 96px;
	}

	.hero-grid {
		grid-template-columns: minmax(0, 1fr);
		gap: 64px;
		align-items: start;
	}

	/* The snippet fills desktop air; on the small screen the head leads. */
	.hero-code {
		display: none;
	}

	.gen-panel {
		min-height: 0;
	}

	.band {
		padding: 96px 0;
	}

	.model-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.func-grid {
		grid-template-columns: minmax(0, 1fr);
		gap: 48px;
	}

	.price-panel {
		grid-template-columns: minmax(0, 1fr);
		gap: 48px;
		padding: 32px;
	}

}

/* Short laptop viewports (720, 768 tall): the vw scale alone still spends
   too much of a short screen on air. One relief step, above the mobile
   breakpoint, so the hero's fold holds there as it does at 800 and 900. */
@media (max-height: 780px) and (min-width: 641px) {
	.hero {
		padding-top: 112px;
		padding-bottom: 112px;
	}

	.band {
		padding-top: 96px;
		padding-bottom: 96px;
	}

	.gen-panel {
		min-height: 480px;
	}
}

@media (max-width: 640px) {
	.nav-links {
		display: none;
	}

	/* The nav's session pair must hold one line beside the wordmark here. */
	.nav-session {
		gap: 12px;
	}

	.btn-primary {
		padding: 0 16px;
		height: 36px;
	}

	.btn-lg {
		height: 44px;
	}

	.model-grid {
		grid-template-columns: minmax(0, 1fr);
	}

	.key-row {
		flex-wrap: wrap;
	}
}

/* ------------------------------------------------------------------ */
/* Motion — the demo's beats only; everything else holds still          */
/* ------------------------------------------------------------------ */

@keyframes caret-beat {
	0%,
	50% {
		opacity: 1;
	}

	50.01%,
	100% {
		opacity: 0;
	}
}

@media (prefers-reduced-motion: reduce) {
	* {
		transition-duration: 0s;
	}

	/* The demo never starts under reduced motion, so the panel stays fully
	   rendered; the caret has nothing to mark and stays dark. */
	.gen-panel.is-live .tok,
	.gen-panel.is-live .cand {
		opacity: 1;
		transform: none;
	}

	.caret {
		animation: none;
	}
}
