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

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

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

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

142 lines
5.0 KiB
TypeScript

import type { ReactNode } from 'react'
import { ArrowRight } from 'lucide-react'
import { Text, View, XStack, YStack } from '@/components/ui'
import { Action } from '@hanzo/ui/marketing'
import { under } from '@/components/shell/dock'
/**
* A model's first screen: its painting (public/images/models/<slug>.webp)
* across the whole of it, dark at the foot so the words read, and on it what
* kind of model it is, its name as the release says it ("Zen 6", "Koma 1"), its
* line, a sentence, and two ways on. The picture is an illustration, said in
* the corner. Every model page opens this way: the creative models
* (components/creative/CreativeLanding) and Zen.
*
* The painting is dark in both themes, so the words and the two controls keep
* their dark-ground look in the light theme too: a white fill and dark glass,
* never ink on the picture.
*
* The painting starts at the top of the window, behind the bar's glass
* (`under`), and fills the first screen: a black band over it was the bar
* standing on nothing but the page's ground.
*/
const ON_PICTURE = {
fill: { bg: 'var(--pure-white)', color: 'var(--pure-black)', hoverStyle: { bg: 'var(--neutral-100)', color: 'var(--pure-black)' } },
glass: {
bg: 'color-mix(in srgb, var(--neutral-900) 65%, transparent)',
borderColor: 'var(--white-14)',
color: 'var(--pure-white)',
hoverStyle: { bg: 'color-mix(in srgb, var(--neutral-800) 80%, transparent)', borderColor: 'var(--white-22)', color: 'var(--pure-white)' },
},
} as const
type Way = { label: string; href: string }
export function Splash({
slug,
kind,
icon,
name,
line,
lede,
primary,
secondary,
video,
}: {
slug: string
/** What kind of model it is, a word or two: "Video", "Open models". */
kind: string
icon?: ReactNode
/** The release's name, the page's heading. */
name: string
line: string
lede: string
primary: Way
secondary: Way
video?: string
}) {
const activeVideo = video ?? (slug === 'koma' ? '/videos/koma-demo.mp4' : undefined)
return (
<View
render="section"
aria-label={`${name}, ${kind.toLowerCase()}`}
position="relative"
overflow="hidden"
width="100%"
minH="100dvh"
{...under(0)}
bg="var(--pure-black)"
justify="flex-end"
>
{activeVideo ? (
<View
render={<video autoPlay muted loop playsInline poster={`/images/models/${slug}.webp`} />}
position="absolute"
t={0}
l={0}
width="100%"
height="100%"
filter="brightness(0.92) contrast(1.05)"
$platform-web={{ objectFit: 'cover' }}
>
<source src={activeVideo.replace('.mp4', '.webm')} type="video/webm" />
<source src={activeVideo} type="video/mp4" />
</View>
) : (
<View
render={<img src={`/images/models/${slug}.webp`} alt="" aria-hidden fetchPriority="high" />}
position="absolute"
t={0}
l={0}
width="100%"
height="100%"
$platform-web={{ objectFit: 'cover' }}
/>
)}
<View
aria-hidden
position="absolute"
t={0}
l={0}
r={0}
b={0}
pointerEvents="none"
$platform-web={{
background:
'linear-gradient(to top, color-mix(in srgb, var(--pure-black) 92%, transparent) 0%, color-mix(in srgb, var(--pure-black) 55%, transparent) 42%, transparent 74%), linear-gradient(to right, color-mix(in srgb, var(--pure-black) 55%, transparent) 0%, transparent 55%)',
}}
/>
<YStack position="relative" width="100%" maxW={1240} mx="auto" px="var(--page-gutter)" pb={72} pt={160} gap={16} $md={{ pb: 96 }}>
<XStack items="center" gap={8}>
{icon}
<Text fontFamily="$mono" fontSize="$2" color="var(--white-80)">
{kind}
</Text>
</XStack>
<Text render="h1" fontWeight="600" color="var(--pure-white)" $platform-web={{ fontSize: 'clamp(64px, 11vw, 152px)', lineHeight: '0.92', letterSpacing: '-0.04em' }}>
{name}
</Text>
<Text render="p" fontWeight="500" color="var(--pure-white)" maxW={820} $platform-web={{ fontSize: 'clamp(26px, 3.4vw, 44px)', lineHeight: '1.1', letterSpacing: '-0.02em' }}>
{line}
</Text>
<Text render="p" color="var(--white-80)" maxW={620} $platform-web={{ fontSize: 'clamp(17px, 1.5vw, 21px)', lineHeight: '1.5' }}>
{lede}
</Text>
<XStack gap="$3" flexWrap="wrap" mt={8}>
<Action href={primary.href} fill $theme-light={ON_PICTURE.fill}>
{primary.label}
<ArrowRight width={16} height={16} />
</Action>
<Action href={secondary.href} $theme-light={ON_PICTURE.glass}>
{secondary.label}
</Action>
</XStack>
</YStack>
<Text position="absolute" r={20} b={16} fontFamily="$mono" fontSize="$1" color="var(--white-80)" opacity={0.7}>
{activeVideo ? '4K UHD · 60 FPS · Hanzo Studio' : 'Illustration'}
</Text>
</View>
)
}