mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
CI/CD's test job has failed on main since Oct 16: hanzo-design-lint counted
154 raw colours, 159 style objects, 8 bare z-indexes and 2 all-caps labels
past hanzo-design.allow.json, almost all in the creative studio surface, the
creative model registry, /research-access and the product sections. The
allowance is untouched; the code now meets it.
- Colour is a token. Each creative model's accent is one of @hanzo/design's
art palette (tide, iris, ember); stems, faders and axes read the same art
and state tokens; red/amber/green window dots are --chrome-dot-*; every
black or white wash is --pure-black/--pure-white mixed to a share. The
unused accentRgb field is gone with its literals.
- Style is gui props. style={} objects became @hanzo/gui props; buttons and
links are gui stacks rendering the native element; /research-access's
fields are @hanzo/ui Input, Textarea and Picker. What props cannot say
(keyframes, a media element's fill and filter, an SVG overlay's place in
the stack) sits in the studio's existing style block, keyed by data-*.
- Stacking is the ladder: the studio overlays and the beta strip's layers
are var(--z-base) and calc() off it, the same order as before.
- "HANZO / ENSO" reads in sentence case, as the system's labels do, and the
creative toggles' 11px labels are the 12px step of the type scale.
The next gate in line, audit-model-counts, also failed on two counts typed
on Oct 16: /playground's description says "every model in the catalog"
again, and Enso's model card draws <Models /> instead of "100+".
No feature is removed: every studio keeps its controls, state and motion.
The visible changes are the accent hues and a few pixels of spacing.
126 lines
4.4 KiB
TypeScript
126 lines
4.4 KiB
TypeScript
'use client'
|
|
|
|
import type { ReactNode } from 'react'
|
|
import { ArrowRight, CheckCircle2, ShieldCheck, FileText, BookOpen, Code2, ExternalLink } from 'lucide-react'
|
|
import { Box, Text, XStack, YStack } from '@hanzo/ui'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { Action, Chip, Eyebrow, Lede, Title } from '@hanzo/ui/marketing'
|
|
import { Band, Card, Head } from '@/components/marketing/band'
|
|
import { Rise } from '@/components/marketing/rise'
|
|
|
|
export interface UseCaseLink {
|
|
label: string
|
|
href: string
|
|
}
|
|
|
|
export interface UseCaseItem {
|
|
tag: string
|
|
title: string
|
|
stack: string
|
|
description: string
|
|
whitepaper?: UseCaseLink
|
|
blogPost?: UseCaseLink
|
|
sourceCode?: UseCaseLink
|
|
actionLabel?: string
|
|
actionHref?: string
|
|
}
|
|
|
|
/** One "Read more" link: its icon, its label in its own ink, and the leaving-site mark. */
|
|
function ReadLink({ href, label, icon, ink }: UseCaseLink & { icon: ReactNode; ink: `var(--${string})` }) {
|
|
return (
|
|
<XStack render={<a href={href} target="_blank" rel="noreferrer" />} items="center" gap={4} $platform-web={{ color: ink, textDecoration: 'none' }}>
|
|
{icon}
|
|
<Text fontSize="$1" fontWeight="500" color={ink}>
|
|
{label}
|
|
</Text>
|
|
<ExternalLink size={10} opacity={0.7} />
|
|
</XStack>
|
|
)
|
|
}
|
|
|
|
export interface ProductUseCasesSectionProps {
|
|
id?: string
|
|
eyebrow?: string
|
|
title?: string
|
|
lede?: string
|
|
useCases: UseCaseItem[]
|
|
}
|
|
|
|
export function ProductUseCasesSection({
|
|
id = 'use-cases',
|
|
eyebrow = 'Enterprise Solutions',
|
|
title = 'Engineered for high-consequence enterprise workloads.',
|
|
lede = 'Battle-tested blueprints across massive catalog volumes, retail logistics, and mission-critical engineering pipelines.',
|
|
useCases,
|
|
}: ProductUseCasesSectionProps) {
|
|
return (
|
|
<Band id={id} measure={1180}>
|
|
<Head
|
|
eyebrow={eyebrow}
|
|
title={title}
|
|
lede={lede}
|
|
loud
|
|
/>
|
|
|
|
<Grid columns={{ min: 320, max: useCases.length > 2 ? 3 : 2 }} gap={16} mt="$6">
|
|
{useCases.map((uc, i) => (
|
|
<Rise key={uc.title} beat={i}>
|
|
<Card
|
|
display="flex"
|
|
flexDirection="column"
|
|
justify="space-between"
|
|
gap="$4"
|
|
p="$6"
|
|
height="100%"
|
|
borderColor="var(--border)"
|
|
bg="$panel"
|
|
hoverStyle={{ borderColor: 'var(--border-strong)' }}
|
|
>
|
|
<YStack gap="$3">
|
|
<XStack justify="space-between" items="center" flexWrap="wrap" gap="$2">
|
|
<Chip px={8} py={3} fontSize="$1" lineHeight="$1">
|
|
{uc.tag}
|
|
</Chip>
|
|
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
|
|
Stack: {uc.stack}
|
|
</Text>
|
|
</XStack>
|
|
|
|
<Text render="h3" fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink">
|
|
{uc.title}
|
|
</Text>
|
|
|
|
<Text fontSize="$2" lineHeight="$3" color="var(--text-secondary)">
|
|
{uc.description}
|
|
</Text>
|
|
|
|
{/* Grounding Resources: White Paper, Blog Post, Source Code */}
|
|
{(uc.whitepaper || uc.blogPost || uc.sourceCode) && (
|
|
<YStack gap="$2" pt="$2" borderTopWidth={1} borderColor="var(--border)">
|
|
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)" textTransform="uppercase">
|
|
Read more
|
|
</Text>
|
|
<XStack flexWrap="wrap" gap="$3" items="center">
|
|
{uc.whitepaper && <ReadLink {...uc.whitepaper} icon={<FileText size={13} />} ink="var(--primary)" />}
|
|
{uc.blogPost && <ReadLink {...uc.blogPost} icon={<BookOpen size={13} />} ink="var(--foreground)" />}
|
|
{uc.sourceCode && <ReadLink {...uc.sourceCode} icon={<Code2 size={13} />} ink="var(--muted-foreground)" />}
|
|
</XStack>
|
|
</YStack>
|
|
)}
|
|
</YStack>
|
|
|
|
{uc.actionHref && (
|
|
<XStack mt="auto" pt="$3">
|
|
<Action href={uc.actionHref} width="100%" justify="center">
|
|
{uc.actionLabel ?? 'View the blueprint'} <ArrowRight size={14} />
|
|
</Action>
|
|
</XStack>
|
|
)}
|
|
</Card>
|
|
</Rise>
|
|
))}
|
|
</Grid>
|
|
</Band>
|
|
)
|
|
}
|