mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useRef } from 'react'
|
|
import Link from 'next/link'
|
|
import { ArrowRight, Sparkles } from 'lucide-react'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
import { Text, XStack, type GuiElement } from '@hanzo/ui'
|
|
import { node, useStill } from './chrome'
|
|
|
|
/**
|
|
* The strip under the header that invites a reader into the beta (/beta).
|
|
* Polished blade steel, drawn in CSS gradients alone (`[data-slot="beta"]` in
|
|
* app/layout.tsx): the spine at the top with a faint line, the ridge a few
|
|
* pixels under it, the flat falling dark through the words and curving up into
|
|
* a pale temper line, and the bright edge along the bottom. One light crosses
|
|
* it, a steep `/` at 70°, resting in the middle and brighter where it crosses
|
|
* the ridge and the edge than under the words, which stay 4.5:1 or better at
|
|
* its brightest (e2e/gates/steel.spec.ts).
|
|
* The pointer sets `--tilt`, 0 at the window's left to 1 at its right, and the
|
|
* light follows it by a transform, on the compositor. Still under reduced motion.
|
|
*/
|
|
/** The strip's height: the first screen subtracts it from the window. */
|
|
export const STRIP = 40
|
|
|
|
export function Beta() {
|
|
const { t } = useI18n()
|
|
const strip = useRef<GuiElement>(null)
|
|
const still = useStill()
|
|
useEffect(() => {
|
|
const el = node(strip)
|
|
if (!el || still) return
|
|
let want = 0.5
|
|
let at = 0.5
|
|
let frame = 0
|
|
const step = () => {
|
|
at += (want - at) * 0.42
|
|
el.style.setProperty('--tilt', at.toFixed(4))
|
|
frame = Math.abs(want - at) > 0.0005 ? requestAnimationFrame(step) : 0
|
|
}
|
|
const move = (e: PointerEvent) => {
|
|
want = e.clientX / Math.max(1, window.innerWidth)
|
|
if (!frame) frame = requestAnimationFrame(step)
|
|
}
|
|
window.addEventListener('pointermove', move, { passive: true })
|
|
return () => {
|
|
window.removeEventListener('pointermove', move)
|
|
cancelAnimationFrame(frame)
|
|
}
|
|
}, [still])
|
|
return (
|
|
<XStack
|
|
ref={strip}
|
|
render={<Link href="/beta" />}
|
|
data-slot="beta"
|
|
items="center"
|
|
justify="center"
|
|
gap={8}
|
|
minH={STRIP}
|
|
px="var(--page-gutter)"
|
|
py={8}
|
|
position="relative"
|
|
overflow="hidden"
|
|
$platform-web={{ textDecoration: 'none' }}
|
|
>
|
|
<Sparkles size={14} strokeWidth={2.2} color="var(--pure-white)" aria-hidden />
|
|
<Text fontSize="$2" lineHeight="1.25rem" fontWeight="500" color="var(--pure-white)" text="center">
|
|
{t('Join the Hanzo beta and get 50% off any paid plan')}
|
|
</Text>
|
|
<ArrowRight size={14} color="var(--pure-white)" aria-hidden data-i18n-mirror />
|
|
</XStack>
|
|
)
|
|
}
|