Files
zeekayandHanzo Dev f11062f432 fix(analytics): theme-aware hero ground and surfaces to fix light-mode contrast
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]>
2026-10-25 09:02:26 -07:00

175 lines
7.6 KiB
TypeScript

'use client'
import React from "react";
import { motion } from "@/components/motion";
import { LineChart } from "lucide-react";
import { Title as Heading } from '@hanzo/ui/marketing'
import { Lattice, Sub, ink, tint } from '../kit'
import { Box, Text, XStack, YStack } from '@hanzo/ui'
import { phrase } from '@hanzo/i18n'
import { useI18n } from '@hanzo/i18n/react'
/** Each insight's heading, and its line with the figure as an argument. */
const INSIGHTS = [
{ head: phrase('Traffic Anomaly'), line: phrase('Unusual spike detected at {time}'), values: { time: '14:32 UTC' } },
{ head: phrase('Conversion Opportunity'), line: phrase('Checkout abandonment {share} higher on mobile'), values: { share: '23%' } },
{ head: phrase('Performance Alert'), line: phrase('API latency increased by {delay}'), values: { delay: '150ms' } },
]
/** A pane of the demo: a hairline on the sunken ground. */
function Pane({ children }: { children: React.ReactNode }) {
return (
<Box rounded="var(--radius-lg)" borderWidth={1} borderColor="var(--neutral-800)" bg="$sunken" p={20}>
{children}
</Box>
)
}
const AIAnalysisDemo = () => {
const { t } = useI18n()
return (
<motion.div initial={{ opacity: 0, y: 40 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.7 }}>
<Box
position="relative"
overflow="hidden"
p={24}
rounded={16}
borderWidth={1}
borderColor="var(--neutral-800)"
boxShadow="var(--shadow-xl)"
$platform-web={{ background: 'linear-gradient(to right, var(--muted), color-mix(in oklab, var(--muted) 50%, transparent))' }}
>
<Lattice size={24} opacity={0.02} />
<Box position="relative" z="var(--z-raised)">
<XStack items="center" mb="$5" gap="$3">
<LineChart size={24} color="var(--foreground)" />
<Heading render="h3" quiet>
{t('AI Analysis in Action')}
</Heading>
</XStack>
{/* One column on a phone, three from `$md` — the fixed three never
collapse and the wide chart runs off the side. */}
<Box display="grid" gridTemplateColumns="minmax(0,1fr)" gap="$5" $md={{ gridTemplateColumns: 'repeat(3, minmax(0,1fr))' }}>
<Box $md={{ gridColumn: 'span 2' }} minW={0}>
<Pane>
<Sub mb={16}>{t('Traffic Anomaly Detection')}</Sub>
<Box position="relative" height={240}>
{/* Base line chart */}
<svg width="100%" height="100%" viewBox="0 0 300 100" preserveAspectRatio="none">
<motion.path
d="M0,80 C25,70 50,65 75,60 C100,55 125,50 150,55 C175,60 200,80 225,50 C250,20 275,35 300,30"
fill="none"
stroke="var(--primary)"
strokeWidth="2"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 2 }}
/>
{/* Anomaly area */}
<motion.path
d="M200,80 C206,65 212,40 218,20 C224,30 230,45 236,50"
fill="none"
stroke="var(--muted-foreground)"
strokeWidth="3"
strokeDasharray="2,2"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.3, delay: 1.8 }}
/>
{/* Anomaly circle */}
<motion.circle
cx="218"
cy="20"
r="5"
fill="var(--muted-foreground)"
initial={{ opacity: 0, scale: 0 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.3, delay: 2 }}
/>
</svg>
{/* Anomaly detection popup */}
<Box position="absolute" t={16} r={32}>
<motion.div
initial={{ opacity: 0, y: -10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.3, delay: 2.2 }}
>
<YStack rounded={4} borderWidth={1} borderColor="var(--border)" py={8} px={12} $platform-web={{ backgroundColor: tint(10) }}>
<Text display="block" mb={4} fontSize="$1" lineHeight="1.25rem" fontWeight="700" color="var(--foreground)">
{t('Anomaly Detected')}
</Text>
<Text display="block" fontSize="$1" lineHeight="1rem" color="var(--foreground)">
{t('Unusual traffic spike')}
</Text>
<Text display="block" fontSize="$1" lineHeight="1rem" color="var(--foreground)">
{t('Confidence: {n}', { n: '98.7%' })}
</Text>
</YStack>
</motion.div>
</Box>
</Box>
</Pane>
</Box>
<YStack gap="$4">
<Pane>
<Sub mb={12}>{t('AI Insights')}</Sub>
<motion.div initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 2.4 }}>
<YStack gap={12}>
{INSIGHTS.map(({ head, line, values }) => (
<Box key={head}>
<Text display="block" fontSize="$2" lineHeight="1.25rem" fontWeight="500" $platform-web={{ color: ink(70) }}>
{t(head)}
</Text>
<Text display="block" fontSize="$2" lineHeight="1.25rem" color="var(--muted-foreground)">
{t(line, values)}
</Text>
</Box>
))}
</YStack>
</motion.div>
</Pane>
<motion.div initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: 2.6 }}>
<Box rounded={12} borderWidth={1} borderColor="var(--border)" p={20} $platform-web={{ backgroundColor: tint(10) }}>
<Sub mb={8}>{t('AI Recommendation')}</Sub>
<Text render="p" display="block" fontSize="$2" lineHeight="1.15rem" $platform-web={{ color: ink(80) }}>
{t('Based on the traffic pattern analysis, we recommend scaling your infrastructure in the US-West region for the next {n} hours.', { n: 3 })}
</Text>
<Text
render={<button type="button" />}
mt={12}
py={4}
px={12}
borderWidth={0}
rounded={4}
bg="var(--primary)"
fontSize="$2"
lineHeight="1.15rem"
color="var(--primary-foreground)"
cursor="pointer"
$platform-web={{ display: 'inline-block' }}
>
{t('Apply Recommendation')}
</Text>
</Box>
</motion.div>
</YStack>
</Box>
</Box>
</Box>
</motion.div>
);
};
export default AIAnalysisDemo;