Files
zeekayandHanzo Dev 2d8ddd097d /kai: theme inks for the figures, a phone-sized headline, plain board title
The distribution's track and runners-up, the switches and the surface read in
theme inks, so they hold on a light ground. The hero's sentence takes a size
that tracks the width on phones, keeping "don't generate text." on one line at
375. The harness board says what it is: Kai and Jev on Decision Index v2.
evidence_sufficient is evidence_enough, so the graph's label clears its value.


Co-authored-by: Hanzo Dev <[email protected]>
2026-10-09 04:27:02 -07:00

60 lines
2.2 KiB
TypeScript

'use client'
/**
* The hero's sentence, said in the order it is read: the condition, the thing
* not to do, and then the one word that is the page. Each line rises into place
* a beat after the last; the last word lands on a rule drawn under it.
*
* The words are in the markup from the first byte, a space after each line so
* the heading reads as one sentence to a crawler and a screen reader. The motion module grounds
* every start state, so a reader without script, or who declines motion, reads
* the whole sentence where it belongs.
*/
import { Text } from '@hanzo/ui'
import { motion } from '@/components/motion'
const RISE = { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 } }
const EASE = [0.22, 1, 0.36, 1] as const
/**
* On a phone the hero's size would break "don't generate text." after its first
* word; a size that tracks the width keeps each line of the sentence whole down
* to 375, and each line balances if it must wrap.
*/
const PHONE = { '$platform-web': { fontSize: 'clamp(2rem, 9vw, 2.75rem)', lineHeight: '1.1em', textWrap: 'balance' } } as const
export default function Headline({ lines, last }: { lines: readonly string[]; last: string }) {
const beat = 0.1 * lines.length
return (
<>
{lines.map((l, i) => (
<Text key={l} render={<motion.span {...RISE} transition={{ duration: 0.5, ease: EASE, delay: 0.1 * i }} />} display="block" $max-sm={PHONE}>
{l}{' '}
</Text>
))}
<Text
render={<motion.span {...RISE} transition={{ duration: 0.45, ease: EASE, delay: beat + 0.2 }} />}
position="relative"
mt="$2"
$platform-web={{ display: 'inline-block' }}
$max-sm={PHONE}
>
{last}
{/* The rule grows from the left under the word; undrawn, it is an ornament missing, not a word. */}
<Text
render={<motion.span aria-hidden initial={{ scaleX: 0 }} animate={{ scaleX: 1 }} transition={{ duration: 0.6, ease: EASE, delay: beat + 0.45 }} />}
position="absolute"
l={0}
r={0}
b={-3}
height={3}
bg="$ink"
$platform-web={{ display: 'block', transformOrigin: 'left' }}
/>
</Text>
</>
)
}