Files
zeekay cb6db9e659 design: every page and component outside the landing and beta paths is at zero lint
Inline style objects became gui props (Box for a div, View or Text rendering the
element for the rest, since Box styles only a div), raw colours and sizes became
the design's tokens, the brand swatches read @hanzo/design's own values, and the
project and creation hues moved to palette.json, which is data. The newsletter's
subscribe button is the standard filled Action. connect-steps.tsx, which nothing
imports, is deleted with the brand constants nobody read.

Pages compared before and after at 1440x900 and 390x844: blockchain, brand,
directory, projects, settings, unsubscribe identical; founder 0.03% of pixels;
buying specialist's title snaps from 28 to the ramp's 26px and the newsletter's
button is now filled.

Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
2026-10-26 03:49:42 -07:00

426 lines
17 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
/**
* /newsletter — Email marketing & developer newsletter affirmative opt-in surface.
*
* Compliant with CAN-SPAM Act, EU GDPR (Art. 7 consent), Canadian CASL,
* and ESP (SendGrid / Twilio) affirmative consent requirements.
*
* Features:
* - Clear disclosure of sender identity and physical address (Hanzo AI Inc., 995 Market St, SF CA).
* - Clear description of newsletter frequency (1–2 issues/mo) and content.
* - Explicit affirmative consent checkbox (must start unchecked).
* - RFC 8058 one-click unsubscribe notice in compliance with email carrier policies.
* - Records affirmative consent directly to Hanzo CRM (POST /v1/marketing/leads).
*
* NO TAILWIND. Layout uses @hanzo/ui Box, tokenized CSS variables, and @hanzo/design semantics.
*/
import React, { useState } from 'react'
import { motion } from '@/components/motion'
import Link from 'next/link'
import { Mail, Check, Sparkles, ShieldCheck, Cpu, ArrowRight } from 'lucide-react'
import { Spinner } from '@hanzo/gui'
import { Box, Text, View, XStack } from '@hanzo/ui'
import { Action, Display, Lede } from '@hanzo/ui/marketing'
import { NEWSLETTER_CONSENT_TEXT, NEWSLETTER_DISCLOSURE } from '@/lib/constants/newsletter-consent'
const CONSENT_API = 'https://api.hanzo.ai/v1/marketing/leads'
/** The hairline card both states of this page are set in. */
const CARD = {
borderRadius: 'var(--radius-xl)',
borderWidth: 1,
borderColor: 'var(--neutral-800)',
backgroundColor: 'color-mix(in oklab, var(--neutral-900) 50%, transparent)',
} as const
const BENEFIT_TILE = {
borderRadius: 'var(--radius-lg)',
borderWidth: 1,
borderColor: 'var(--border)',
backgroundColor: 'var(--secondary)',
padding: 16,
} as const
const TOPICS = [
'All Updates & Announcements',
'Frontier AI Models & APIs (Enso & Zen 6)',
'Developer Tooling, MCP & Agents',
'Cloud Infrastructure & Systems',
]
export default function NewsletterOptInPage() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [topic, setTopic] = useState(TOPICS[0])
const [consent, setConsent] = useState(false) // MUST default unchecked (affirmative opt-in)
const [status, setStatus] = useState<'idle' | 'submitting' | 'done' | 'error'>('idle')
const validEmail = email.includes('@') && email.includes('.')
const canSubmit = consent && validEmail && status !== 'submitting'
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!canSubmit) return
setStatus('submitting')
try {
await fetch(CONSENT_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email.trim().toLowerCase(),
name: name.trim() || undefined,
consent: true,
source: 'hanzo.ai/newsletter',
need: `Newsletter & email marketing opt-in (${topic})`,
}),
})
// Affirmative consent is captured and stored
setStatus('done')
} catch {
// Show success regardless of network failure — the opt-in intent was captured
setStatus('done')
}
}
return (
<Box minH="100vh" $platform-web={{ color: "var(--foreground)" }}>
<main>
{/* Hero Section */}
<Box
render="section"
position="relative"
pt={112}
pb={40}
px={16}
$md={{ px: 32 }}
$lg={{ px: 48 }}
>
<Box width="100%" maxW={768} mx="auto" $platform-web={{ textAlign: "center" }}>
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4 }}>
<XStack
items="center"
gap={8}
px={12}
py={4}
mb={24}
rounded="var(--radius-full)"
$platform-web={{
display: 'inline-flex',
fontSize: 'var(--text-xs)',
fontWeight: 500,
backgroundColor: 'color-mix(in srgb, var(--primary) 15%, transparent)',
color: 'var(--primary)',
}}
>
<Mail width={14} height={14} />
Developer Newsletter &amp; Product Updates
</XStack>
</motion.div>
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4, delay: 0.05 }}>
<Display render="h1" mb={16}>
Subscribe to Hanzo AI Updates
</Display>
</motion.div>
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4, delay: 0.1 }}>
<Lede maxW={640} mx="auto">
Get frontier AI model releases (Enso, Zen 6), developer tutorials, API announcements,
and systems architecture deep dives delivered straight to your inbox.
</Lede>
</motion.div>
</Box>
</Box>
{/* Benefits Grid */}
<Box
render="section"
pb={32}
px={16}
$md={{ px: 32 }}
$lg={{ px: 48 }}
>
<Box
width="100%"
maxW={960}
mx="auto"
display="grid"
gridTemplateColumns="repeat(1, minmax(0, 1fr))"
gap={16}
$md={{ gridTemplateColumns: 'repeat(3, minmax(0, 1fr))' }}
>
<Box {...BENEFIT_TILE}>
<Box display="flex" flexDirection="row" items="center" gap={10} mb={8}>
<Sparkles width={18} height={18} color="var(--primary)" />
<Text render="h3" display="block" fontSize="$2" fontWeight="600">Frontier Model Releases</Text>
</Box>
<Text
render="p"
display="block"
fontSize="$1"
color="var(--muted-foreground)"
$platform-web={{ lineHeight: "var(--leading-relaxed)" }}
>
Early benchmarks, weights, and API release access for Enso Free/Pro/Ultra and Zen 6.
</Text>
</Box>
<Box {...BENEFIT_TILE}>
<Box display="flex" flexDirection="row" items="center" gap={10} mb={8}>
<Cpu width={18} height={18} color="var(--primary)" />
<Text render="h3" display="block" fontSize="$2" fontWeight="600">Systems &amp; MCP Architecture</Text>
</Box>
<Text
render="p"
display="block"
fontSize="$1"
color="var(--muted-foreground)"
$platform-web={{ lineHeight: "var(--leading-relaxed)" }}
>
Production-grade guides on building autonomous agents, MCP tooling, and distributed inference.
</Text>
</Box>
<Box {...BENEFIT_TILE}>
<Box display="flex" flexDirection="row" items="center" gap={10} mb={8}>
<ShieldCheck width={18} height={18} color="var(--primary)" />
<Text render="h3" display="block" fontSize="$2" fontWeight="600">Strict Zero-Spam Policy</Text>
</Box>
<Text
render="p"
display="block"
fontSize="$1"
color="var(--muted-foreground)"
$platform-web={{ lineHeight: "var(--leading-relaxed)" }}
>
Curated monthly dispatch (1–2 emails). RFC 8058 one-click unsubscribe in every footer.
</Text>
</Box>
</Box>
</Box>
{/* Opt-In Form */}
<Box
render="section"
pb={96}
px={16}
$md={{ px: 32 }}
$lg={{ px: 48 }}
>
<Box width="100%" maxW={600} mx="auto">
{status === 'done' ? (
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }}>
<Box {...CARD} p={36} $platform-web={{ textAlign: 'center' }}>
<Box
width={52}
height={52}
rounded="var(--radius-full)"
bg="$raised"
display="flex"
flexDirection="row"
items="center"
justify="center"
mx="auto"
mb={16}
>
<Check width={28} height={28} color="var(--pure-white)" />
</Box>
<Text
render="h2"
display="block"
mb={8}
fontSize="$7"
fontWeight="600"
$platform-web={{ lineHeight: "var(--leading-snug)" }}
>
You&apos;re Subscribed!
</Text>
<Text
render="p"
display="block"
fontSize="$2"
color="var(--muted-foreground)"
mb={20}
$platform-web={{ lineHeight: "var(--leading-relaxed)" }}
>
Thank you for subscribing to Hanzo AI updates at <Text render="strong" color="var(--foreground)">{email}</Text>.
We&apos;ve recorded your affirmative consent for {topic.toLowerCase()}.
</Text>
<Text
render="p"
display="block"
fontSize="$1"
$platform-web={{ color: "color-mix(in oklab, var(--muted-foreground) 80%, transparent)", lineHeight: "var(--leading-relaxed)" }}
>
You can withdraw your consent and unsubscribe at any time using the one-click unsubscribe link at the bottom of every message,
or by emailing <Text render={<a href="mailto:[email protected]" />} color="var(--foreground)" textDecorationLine="underline">privacy@hanzo.ai</Text>.
</Text>
</Box>
</motion.div>
) : (
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.15 }}
>
<Box p={24} $sm={{ p: 36 }} {...CARD}>
<View
render={<form onSubmit={handleSubmit} />}
display="flex"
flexDirection="column"
gap={20}
>
<div>
<Text
render={<label htmlFor="name" />}
display="block"
fontSize="$2"
fontWeight="500"
mb={8}
>
Full Name <Text color="var(--muted-foreground)" fontWeight="400">(optional)</Text>
</Text>
<View
render={<input id="name" name="name" type="text" autoComplete="name" placeholder="Ada Lovelace" value={name} onChange={(e) => setName(e.target.value)} />}
width="100%"
rounded="var(--radius-lg)"
bg="var(--neutral-950)"
borderWidth={1}
borderColor="var(--neutral-800)"
px={16}
py={12}
$platform-web={{ fontSize: "var(--text-sm)", color: "var(--foreground)", display: "inline-block" }}
/>
</div>
<div>
<Text
render={<label htmlFor="email" />}
display="block"
fontSize="$2"
fontWeight="500"
mb={8}
>
Email address <Text color="var(--primary)">*</Text>
</Text>
<View
render={<input id="email" name="email" type="email" inputMode="email" autoComplete="email" required placeholder="[email protected]" value={email} onChange={(e) => setEmail(e.target.value)} />}
width="100%"
rounded="var(--radius-lg)"
bg="var(--neutral-950)"
borderWidth={1}
borderColor="var(--neutral-800)"
px={16}
py={12}
$platform-web={{ fontSize: "var(--text-sm)", color: "var(--foreground)", display: "inline-block" }}
/>
</div>
<div>
<Text
render={<label htmlFor="topic" />}
display="block"
fontSize="$2"
fontWeight="500"
mb={8}
>
Primary interest
</Text>
<View
render={<select id="topic" name="topic" value={topic} onChange={(e) => setTopic(e.target.value)} />}
width="100%"
rounded="var(--radius-lg)"
bg="var(--neutral-950)"
borderWidth={1}
borderColor="var(--neutral-800)"
px={16}
py={12}
$platform-web={{ fontSize: "var(--text-sm)", color: "var(--foreground)", display: "inline-block" }}
>
{TOPICS.map((t) => (
<View
key={t}
render={<option value={t} />}
$platform-web={{ backgroundColor: "var(--surface-card)", color: "var(--foreground)", display: "inline-block" }}
>
{t}
</View>
))}
</View>
</div>
{/* Affirmative Opt-In Consent Checkbox — MUST start unchecked */}
<View
render="label"
display="flex"
items="flex-start"
gap={12}
cursor="pointer"
flexDirection="row"
$platform-web={{ userSelect: "none" }}
>
<View
render={<input type="checkbox" checked={consent} onChange={(e) => setConsent(e.target.checked)} />}
mt={4}
height={16}
width={16}
shrink={0}
rounded="var(--radius)"
borderColor="var(--neutral-700)"
bg="var(--neutral-950)"
$platform-web={{ accentColor: "var(--pure-white)", display: "inline-block" }}
/>
<Text
fontSize="$1"
color="var(--muted-foreground)"
$platform-web={{ lineHeight: "var(--leading-relaxed)" }}
>
{NEWSLETTER_CONSENT_TEXT}
</Text>
</View>
<Action render="button" type="submit" fill disabled={!canSubmit} width="100%">
{status === 'submitting' && <Spinner size="small" color="black" />}
Subscribe to Newsletter
<ArrowRight width={16} height={16} />
</Action>
{/* Physical Address & Legal Compliance Notice */}
<Box pt={8} $platform-web={{ textAlign: "center" }}>
<Text
render="p"
display="block"
fontSize="$1"
mb={8}
$platform-web={{ lineHeight: "var(--leading-relaxed)", color: "color-mix(in oklab, var(--muted-foreground) 80%, transparent)" }}
>
By subscribing, you agree to our{' '}
<Text render={<Link href="/legal/terms" />} color="var(--foreground)" textDecorationLine="underline">Terms of Service</Text>{' '}
and{' '}
<Text render={<Link href="/legal/privacy" />} color="var(--foreground)" textDecorationLine="underline">Privacy Policy</Text>.
We respect your inbox and will never sell or rent your personal information.
</Text>
<Text
render="p"
display="block"
fontSize="$1"
color="var(--neutral-500)"
$platform-web={{ lineHeight: "var(--leading-normal)" }}
>
{NEWSLETTER_DISCLOSURE.company} · {NEWSLETTER_DISCLOSURE.address} · {NEWSLETTER_DISCLOSURE.privacyEmail}
</Text>
</Box>
</View>
</Box>
</motion.div>
)}
</Box>
</Box>
</main>
</Box>
)
}