mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-28 07:32:52 +00:00
The /analytics landing page hardcoded theme-independent colors (var(--pure-black), var(--pure-white), --neutral-*) into the hero ground, dashboard mock, code windows and section washes. In light mode that meant near-black ink on a pure-black hero and white chart lines on light surfaces — unreadable and harsh. Swap them for @hanzo/design's theme-aware tokens (--background, --muted, --secondary, --primary, --muted-foreground). Dark rendering is unchanged; light mode now resolves correctly. Verified with headless Chromium (hero bg/ink before vs after) and clean tsc --noEmit / design lint. Co-authored-by: Hanzo Dev <[email protected]>
96 lines
3.5 KiB
TypeScript
96 lines
3.5 KiB
TypeScript
'use client'
|
|
|
|
import React from "react";
|
|
import { motion } from "@/components/motion";
|
|
import { ArrowRight, LineChart } from "lucide-react";
|
|
import { Band, Head } from '@/components/marketing/band'
|
|
import { Action } from '@hanzo/ui/marketing'
|
|
import { Body, Disc, Tile, Title } from './kit'
|
|
import { Box, Text, XStack } from '@hanzo/ui'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { phrase } from '@hanzo/i18n'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
|
|
const GROUND = 'linear-gradient(to bottom, color-mix(in oklab, var(--muted) 30%, transparent), var(--background))'
|
|
|
|
const FEATURES = [
|
|
{
|
|
title: phrase("One tag"),
|
|
description: phrase("Paste it into the head of your site. Pageviews start arriving before you have decided what to name anything.")
|
|
},
|
|
{
|
|
title: phrase("Your data stays yours"),
|
|
description: phrase("The events live in your own Postgres, MySQL or MariaDB. Nothing is sampled, and nothing is held back from an export.")
|
|
},
|
|
{
|
|
title: phrase("Open source, MIT"),
|
|
description: phrase("Run it on a small server for as long as you like. The hosted version is the same code with the operations taken care of.")
|
|
}
|
|
]
|
|
|
|
/** A control that leans toward the pointer. */
|
|
const LEAN = { whileHover: { scale: 1.05 }, transition: { type: "spring", stiffness: 400, damping: 10 } } as const
|
|
|
|
const CallToAction = () => {
|
|
const { t } = useI18n()
|
|
return (
|
|
<Band rule={false} pad="$14" measure={1152} ground={GROUND}>
|
|
<Box position="relative" z="var(--z-raised)">
|
|
<Box mb="$5" $platform-web={{ textAlign: 'center' }}>
|
|
<Disc>
|
|
<LineChart size={40} />
|
|
</Disc>
|
|
</Box>
|
|
<Head
|
|
loud
|
|
title={t('Add the script')}
|
|
lede={t('One tag on the page and the dashboard fills in on its own. Host it yourself or let us run it — the software is the same either way.')}
|
|
>
|
|
<XStack flexWrap="wrap" justify="center" gap="$5">
|
|
<motion.div {...LEAN}>
|
|
<Action href="/login" fill>
|
|
{t('Try Hanzo')}
|
|
</Action>
|
|
</motion.div>
|
|
<motion.div {...LEAN}>
|
|
<Action href="/contact-sales">
|
|
{t('Request Demo')} <ArrowRight size={20} data-i18n-mirror />
|
|
</Action>
|
|
</motion.div>
|
|
</XStack>
|
|
</Head>
|
|
|
|
<Grid columns={{ min: 300, max: 3 }} gap={24}>
|
|
{FEATURES.map((feature, index) => (
|
|
<motion.div
|
|
key={feature.title}
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.5, delay: index * 0.1 }}
|
|
>
|
|
<Tile height="100%" p="$5" bg="$panel" hoverStyle={{ borderColor: 'var(--white-30)' }}>
|
|
<Title>{t(feature.title)}</Title>
|
|
<Body>{t(feature.description)}</Body>
|
|
</Tile>
|
|
</motion.div>
|
|
))}
|
|
</Grid>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.5, delay: 0.4 }}
|
|
>
|
|
<Text display="block" mt={64} text="center" fontSize="$2" lineHeight="1.15rem" color="var(--muted-foreground)">
|
|
{t('No credit card required. Free trial includes all premium features for {n} days.', { n: 14 })}
|
|
</Text>
|
|
</motion.div>
|
|
</Box>
|
|
</Band>
|
|
);
|
|
};
|
|
|
|
export default CallToAction;
|