mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
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
519 lines
22 KiB
TypeScript
519 lines
22 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* /brand — the design system, shown rather than described.
|
|
*
|
|
* NO TAILWIND. The seven section frames are `components/marketing/band`'s
|
|
* `Band` and `Head`; @hanzo/ui's marketing components carry the type and the
|
|
* controls (`Display`, `Lede`, `Title`, `Chip`, `Action`, `Leaf`, `Line`);
|
|
* everything left is gui props over @hanzo/design's tokens. A `color-mix()`
|
|
* ink is not a value gui's colour props type, so a frame's tint rides
|
|
* `$platform-web`, which gui still writes as a class.
|
|
*
|
|
* THE THREE LOCAL COMPONENTS TOOK A `className` AND IT WAS THE PAYLOAD, which
|
|
* is the one shape a de-Tailwind pass cannot leave alone: `ColorSwatch` was
|
|
* handed `bg-[#171717]`, and `TypographyExample` a whole type ramp, so the
|
|
* utility string WAS the thing being demonstrated. Each now takes the VALUE it
|
|
* was demonstrating — a `fill` for the swatch, the `type` props for the
|
|
* specimen — and every call site passes one. A page about a design system may
|
|
* not be the last place still writing utilities to describe it.
|
|
*
|
|
* Two things the swap moves, both deliberate:
|
|
*
|
|
* - the section headings take `Title quiet`, so `text-2xl md:text-3xl`
|
|
* becomes the site's own section rung and steps at 640 rather than 768.
|
|
* - `Action` is the one button shape, so the four `Button` variants in the
|
|
* Components showcase render as the site's two weights plus `More` — which
|
|
* is what those four have BEEN on every other page since the roles landed.
|
|
*
|
|
* `rounded-3xl` on the CTA glows had no design token (`--radius-3xl` is
|
|
* Tailwind's alone) and neither did they need one: they are circles, so they
|
|
* take `--radius-full`, which is what `rounded-full` already said.
|
|
*/
|
|
|
|
|
|
import { colors } from '@hanzo/design'
|
|
import React from "react";
|
|
import { motion } from "@/components/motion";
|
|
import {
|
|
Palette,
|
|
Download,
|
|
Type,
|
|
Grid3X3,
|
|
Sparkles,
|
|
ArrowRight,
|
|
} from "lucide-react";
|
|
import { Box, XStack, YStack } from '@hanzo/ui'
|
|
import { phrase } from '@hanzo/i18n'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { Band, Head } from '@/components/marketing/band'
|
|
import { Action, Chip, Display, Lede, More, Title, Leaf, Lift, Line, type LineProps } from '@hanzo/ui/marketing'
|
|
|
|
/** How narrow a column may get before a grid drops one, per shape. */
|
|
const TRIO = { min: 280, max: 3 } as const
|
|
const RAMP = { min: 140, max: 6 } as const
|
|
const PAIR_UP = { min: 140, max: 4 } as const
|
|
|
|
/** The wash a mark's plate is drawn on, at the two strengths this page uses. */
|
|
const PLATE = 'color-mix(in srgb, var(--primary) 10%, transparent)'
|
|
const BADGE = 'color-mix(in srgb, var(--primary) 15%, transparent)'
|
|
const TILE = '$hover'
|
|
|
|
const CAPTION = { fontSize: '$1', lineHeight: '1.3333', color: 'var(--muted-foreground)' } as const
|
|
const LABEL = { fontSize: '$2', lineHeight: '1.4286', fontWeight: '500', color: 'var(--foreground)' } as const
|
|
|
|
/** A heading inside a section or a card, at the rung and weight it states. */
|
|
const Heading = (p: LineProps) => <Line render="h3" display="block" color="var(--foreground)" {...p} />
|
|
|
|
/** The card whose edge lifts on approach — a `Leaf`, at this page's one wash. */
|
|
const LEAF = { p: 24, borderColor: "var(--border)", bg: TILE, hoverStyle: { borderColor: "color-mix(in oklab, var(--pure-white) 30%, transparent)" } } as const
|
|
|
|
/** A mark's plate, in the page's one accent at 10%. */
|
|
const MARK = {
|
|
items: 'center',
|
|
justify: 'center',
|
|
width: 48,
|
|
height: 48,
|
|
mb: 16,
|
|
rounded: 'var(--radius-lg)',
|
|
'$platform-web': { backgroundColor: PLATE },
|
|
} as const
|
|
|
|
/**
|
|
* A colour, drawn as itself. `fill` is any CSS `background` — a hex, a token,
|
|
* or a gradient — because that is the thing the swatch exists to show, and a
|
|
* class name was only ever a way of naming it indirectly.
|
|
*/
|
|
const ColorSwatch = ({ name, hex, fill }: { name: string; hex: string; fill: string }) => {
|
|
const { t } = useI18n()
|
|
return (
|
|
<YStack gap={8}>
|
|
<Box width="100%" aspectRatio={1} rounded="var(--radius-lg)" borderWidth={1} borderColor="var(--border)" background={fill} />
|
|
<Line render="div" display="block" {...LABEL}>
|
|
{t(name)}
|
|
</Line>
|
|
<Line render="div" display="block" {...CAPTION}>
|
|
{t(hex)}
|
|
</Line>
|
|
</YStack>
|
|
)
|
|
};
|
|
|
|
/**
|
|
* One rung of the ramp, set in itself. `type` is the style the specimen is
|
|
* demonstrating, so the rung and its description travel together and neither
|
|
* can be a utility string.
|
|
*/
|
|
const TypographyExample = ({ name, type, description }: { name: string; type: LineProps; description: string }) => {
|
|
const { t } = useI18n()
|
|
return (
|
|
<YStack gap={8} mb={40}>
|
|
<Line render="div" display="block" {...type}>
|
|
{t(name)}
|
|
</Line>
|
|
<Line render="div" display="block" {...CAPTION}>
|
|
{t(description)}
|
|
</Line>
|
|
</YStack>
|
|
)
|
|
};
|
|
|
|
const ComponentShowcase = ({ title, children }: { title: string; children: React.ReactNode }) => (
|
|
<Box overflow="hidden" rounded="var(--radius-xl)" borderWidth={1} borderColor="$edge">
|
|
<Box p={16} borderBottomWidth={1} borderColor="$edge" bg={TILE}>
|
|
<Heading {...LABEL}>{title}</Heading>
|
|
</Box>
|
|
<XStack
|
|
flexWrap="wrap"
|
|
items="center"
|
|
justify="center"
|
|
gap={16}
|
|
p={32}
|
|
bg="$panel"
|
|
>
|
|
{children}
|
|
</XStack>
|
|
</Box>
|
|
);
|
|
|
|
const PRINCIPLES = [
|
|
{
|
|
icon: Sparkles,
|
|
title: phrase("Minimal & Focused"),
|
|
description: phrase("Reduce visual noise and focus on essential elements. Each design decision should have purpose and enhance the user experience.")
|
|
},
|
|
{
|
|
icon: Type,
|
|
title: phrase("Elegant & Refined"),
|
|
description: phrase("Use subtle gradients, appropriate spacing, and thoughtful typography to create elegant and sophisticated interfaces.")
|
|
},
|
|
{
|
|
icon: Grid3X3,
|
|
title: phrase("Consistent & Cohesive"),
|
|
description: phrase("Maintain consistency in design patterns, spacing, and visual hierarchy to create a unified experience across all touchpoints.")
|
|
}
|
|
] as const
|
|
|
|
/** A swatch of the design's own palette: the label is the token's value, the fill is the token. */
|
|
const swatch = (name: string, token: keyof typeof colors) => ({ name, hex: colors[token], fill: `var(--${token})` })
|
|
|
|
const BRAND_COLORS = [swatch(phrase("Hanzo Black"), "hanzo-black"), swatch(phrase("Hanzo White"), "hanzo-white")] as const
|
|
|
|
const GRAY_SCALE = [
|
|
swatch(phrase("Gray 900"), "neutral-900"),
|
|
swatch(phrase("Gray 800"), "neutral-800"),
|
|
swatch(phrase("Gray 700"), "neutral-700"),
|
|
swatch(phrase("Gray 600"), "neutral-600"),
|
|
swatch(phrase("Gray 500"), "neutral-500"),
|
|
swatch(phrase("Gray 400"), "neutral-400"),
|
|
] as const
|
|
|
|
const GRADIENTS = [
|
|
{ name: phrase("Brand Glow"), hex: phrase("Radial"), fill: "linear-gradient(to bottom right in oklab, color-mix(in oklab, var(--pure-white) 30%, transparent), transparent)" },
|
|
{ name: phrase("White Fade"), hex: phrase("Linear"), fill: "linear-gradient(to bottom in oklab, color-mix(in oklab, var(--pure-white) 10%, transparent), transparent)" },
|
|
{ name: phrase("Gray Fade"), hex: phrase("Linear"), fill: "linear-gradient(to right in oklab, color-mix(in oklab, var(--neutral-800) 50%, transparent), transparent)" },
|
|
] as const
|
|
|
|
/** The ramp, each rung set in itself. `--text-*` rather than a frozen pixel,
|
|
* so the reader's own type size still moves the specimen it describes. */
|
|
const RAMP_ROWS = [
|
|
{ name: phrase("Heading 1"), description: phrase("Large page titles, hero sections"), type: { fontSize: '$10', lineHeight: '1.1111', fontWeight: '700', letterSpacing: 'var(--tracking-tight)', color: 'var(--foreground)' } },
|
|
{ name: phrase("Heading 2"), description: phrase("Section titles, major divisions"), type: { fontSize: '$8', lineHeight: '1.2', fontWeight: '700', letterSpacing: 'var(--tracking-tight)', color: 'var(--foreground)' } },
|
|
{ name: phrase("Heading 3"), description: phrase("Subsection headings, feature headings"), type: { fontSize: '$7', lineHeight: '1.3333', fontWeight: '600', color: 'var(--foreground)' } },
|
|
{ name: phrase("Heading 4"), description: phrase("Card titles, minor section headings"), type: { fontSize: '$6', lineHeight: '1.4', fontWeight: '500', color: 'var(--foreground)' } },
|
|
{ name: phrase("Body Large"), description: phrase("Featured paragraphs, pull quotes"), type: { fontSize: '$4', lineHeight: 'var(--leading-relaxed)', color: 'color-mix(in oklab, var(--foreground) 80%, transparent)' } },
|
|
{ name: phrase("Body"), description: phrase("Primary body text"), type: { fontSize: '$3', lineHeight: 'var(--leading-relaxed)', color: 'var(--muted-foreground)' } },
|
|
{ name: phrase("Caption"), description: phrase("Supporting text, metadata, labels"), type: { fontSize: '$2', lineHeight: '1.4286', color: 'var(--muted-foreground)' } },
|
|
] as const
|
|
|
|
/**
|
|
* The two heading rungs the ramp above states at `md:` — 6xl for Heading 1 and
|
|
* 4xl for Heading 2 — stay OFF the specimen. The row is a demonstration of
|
|
* one rung at every width, not the page's own
|
|
* heading; the ladder itself lives in `Display` and `Title`, which is where
|
|
* this page's real headings take it.
|
|
*/
|
|
|
|
const Brand = () => {
|
|
const { t } = useI18n()
|
|
return (
|
|
<Box minH="100vh" $platform-web={{ color: 'var(--foreground)' }}>
|
|
|
|
<Box render="main">
|
|
{/* Hero Section */}
|
|
<Box render="section" position="relative" overflow="hidden" pt={96} pb={64} px={16} $md={{ px: 32 }} $lg={{ px: 48 }}>
|
|
|
|
<Box position="relative" z="var(--z-raised)" width="100%" maxW={1024} mx="auto">
|
|
<Box $platform-web={{ textAlign: 'center' }}>
|
|
<Chip
|
|
render={<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4 }} />}
|
|
mb={24}
|
|
px={12}
|
|
py={4}
|
|
fontSize="$1"
|
|
lineHeight="1.3333"
|
|
fontWeight="500"
|
|
bg={BADGE}
|
|
color="var(--primary)"
|
|
>
|
|
<Palette width={14} height={14} />
|
|
{t('Design System')}
|
|
</Chip>
|
|
|
|
<Display
|
|
render={<motion.h1 initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4, delay: 0.05 }} />}
|
|
mb={24}
|
|
>
|
|
{t('Hanzo Brand{br}Guidelines.', { br: <br /> })}
|
|
</Display>
|
|
|
|
<Lede
|
|
render={<motion.p initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4, delay: 0.1 }} />}
|
|
mb={40}
|
|
width="100%"
|
|
maxW={768}
|
|
mx="auto"
|
|
>
|
|
{t('How to build interfaces that look like the rest of Hanzo across our platform and products.')}
|
|
</Lede>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 16 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.4, delay: 0.15 }}
|
|
>
|
|
<XStack flexWrap="wrap" items="center" justify="center" gap={16}>
|
|
<Action href="#assets" fill px={24}>
|
|
<Download width={16} height={16} />
|
|
{t('Download Assets')}
|
|
</Action>
|
|
<Action href="/press" px={24} borderColor="var(--border)">
|
|
{t('Press Kit')}
|
|
</Action>
|
|
</XStack>
|
|
</motion.div>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
|
|
{/* Design Principles */}
|
|
<Band
|
|
rule={false}
|
|
pad="$10"
|
|
measure={1152}
|
|
ground="linear-gradient(to bottom in oklab, var(--background), color-mix(in oklab, var(--neutral-900) 30%, transparent))"
|
|
>
|
|
<Head title={t('Design Principles')} lede={t('Our core principles guide every design decision we make.')} />
|
|
|
|
<Grid columns={TRIO} gap={24}>
|
|
{PRINCIPLES.map(({ icon: Icon, title, description }, index) => (
|
|
<Leaf
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: index * 0.1 }} />}
|
|
key={title}
|
|
{...LEAF}
|
|
hoverStyle={{ borderColor: "color-mix(in oklab, var(--pure-white) 30%, transparent)" }}
|
|
>
|
|
<XStack {...MARK}>
|
|
<Icon width={24} height={24} />
|
|
</XStack>
|
|
<Heading mb={8} fontSize="$4" lineHeight="1.5556" fontWeight="600">
|
|
{t(title)}
|
|
</Heading>
|
|
<Line render="p" display="block" fontSize="$2" lineHeight="1.4286" color="var(--muted-foreground)">
|
|
{t(description)}
|
|
</Line>
|
|
</Leaf>
|
|
))}
|
|
</Grid>
|
|
</Band>
|
|
|
|
{/* Color Palette */}
|
|
<Band rule={false} pad="$10" measure={1152}>
|
|
<Head title={t('Color Palette')} lede={t('Our colors are carefully selected to create a bold, modern aesthetic.')} />
|
|
|
|
<YStack gap={48}>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<Heading mb={24} fontSize="$6" lineHeight="1.4" fontWeight="500">
|
|
{t('Brand Colors')}
|
|
</Heading>
|
|
<Grid columns={PAIR_UP} gap={24}>
|
|
{BRAND_COLORS.map((c) => (
|
|
<ColorSwatch key={c.name} name={c.name} hex={c.hex} fill={c.fill} />
|
|
))}
|
|
</Grid>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<Heading mb={24} fontSize="$6" lineHeight="1.4" fontWeight="500">
|
|
{t('Gray Scale')}
|
|
</Heading>
|
|
<Grid columns={RAMP} gap={24}>
|
|
{GRAY_SCALE.map((c) => (
|
|
<ColorSwatch key={c.name} name={c.name} hex={c.hex} fill={c.fill} />
|
|
))}
|
|
</Grid>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<Heading mb={24} fontSize="$6" lineHeight="1.4" fontWeight="500">
|
|
{t('Gradients')}
|
|
</Heading>
|
|
<Grid columns={TRIO} gap={24}>
|
|
{GRADIENTS.map((c) => (
|
|
<ColorSwatch key={c.name} name={c.name} hex={c.hex} fill={c.fill} />
|
|
))}
|
|
</Grid>
|
|
</motion.div>
|
|
</YStack>
|
|
</Band>
|
|
|
|
{/* Typography */}
|
|
<Band rule={false} pad="$10" measure={1152} ground="color-mix(in oklab, var(--secondary) 30%, transparent)">
|
|
<Head title={t('Typography')} lede={t('Clean, readable typography is essential to our design language.')} />
|
|
|
|
<Box
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />}
|
|
width="100%"
|
|
maxW={768}
|
|
mx="auto"
|
|
p={32}
|
|
rounded="var(--radius-xl)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
bg={TILE}
|
|
>
|
|
{RAMP_ROWS.map((row) => (
|
|
<TypographyExample key={row.name} name={row.name} type={row.type} description={row.description} />
|
|
))}
|
|
</Box>
|
|
</Band>
|
|
|
|
{/* Components */}
|
|
<Band rule={false} pad="$10" measure={1152}>
|
|
<Head title={t('Components')} lede={t('Reusable UI components that maintain consistency across our products.')} />
|
|
|
|
<YStack gap={32}>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<ComponentShowcase title={t('Buttons')}>
|
|
<Action render="span" fill>
|
|
{t('Primary Button')}
|
|
</Action>
|
|
<Action render="span" borderColor="var(--border)">
|
|
{t('Secondary Button')}
|
|
</Action>
|
|
<Action render="span" borderWidth={0}>
|
|
{t('Ghost Button')}
|
|
</Action>
|
|
<More render="span">
|
|
{t('Link Button')}
|
|
</More>
|
|
</ComponentShowcase>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<ComponentShowcase title={t('Cards')}>
|
|
<Leaf width="100%" maxW={448} {...LEAF}>
|
|
<Heading mb={8} fontSize="$6" lineHeight="1.4" fontWeight="500">
|
|
{t('Card Title')}
|
|
</Heading>
|
|
<Line render="p" display="block" mb={16} color="var(--muted-foreground)">
|
|
{t('This is a standard card component with hover states and consistent styling.')}
|
|
</Line>
|
|
<Action render="span" minH={36} px={16} borderColor="var(--border)">
|
|
{t('Learn More')}
|
|
</Action>
|
|
</Leaf>
|
|
</ComponentShowcase>
|
|
</motion.div>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
>
|
|
<ComponentShowcase title={t('Badges')}>
|
|
<Chip px={12} py={4} borderWidth={0} fontWeight="500" bg={BADGE} color="var(--primary)">
|
|
{t('Brand Badge')}
|
|
</Chip>
|
|
<Chip px={12} py={4} borderColor="var(--border)" fontWeight="500" bg="var(--secondary)" color="color-mix(in oklab, var(--foreground) 80%, transparent)">
|
|
{t('Default Badge')}
|
|
</Chip>
|
|
<Chip px={12} py={4} borderColor="var(--border)" fontWeight="500" bg={PLATE} color="color-mix(in oklab, var(--foreground) 70%, transparent)">
|
|
{t('Success Badge')}
|
|
</Chip>
|
|
</ComponentShowcase>
|
|
</motion.div>
|
|
</YStack>
|
|
</Band>
|
|
|
|
{/* Download Assets */}
|
|
<Band id="assets" rule={false} pad="$10" measure={1152} ground="color-mix(in oklab, var(--secondary) 30%, transparent)">
|
|
<Head title={t('Download Assets')} lede={t('Get our brand assets for your projects and presentations.')} />
|
|
|
|
<Box width="100%" maxW={448} mx="auto">
|
|
<Leaf
|
|
render={<motion.a initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />}
|
|
href="/press/hanzo-press-kit.zip"
|
|
{...LEAF}
|
|
hoverStyle={{ borderColor: "color-mix(in oklab, var(--pure-white) 30%, transparent)" }}
|
|
>
|
|
<XStack {...MARK}>
|
|
<Download width={24} height={24} />
|
|
</XStack>
|
|
{/* The card decides the lift; the content says who takes part. */}
|
|
<Lift render="h3" display="block" mb={4} fontSize="$4" lineHeight="1.5556" fontWeight="600" color="var(--foreground)">
|
|
{t('Press Kit')}
|
|
</Lift>
|
|
<Line render="p" display="block" mb={8} fontSize="$2" lineHeight="1.4286" color="var(--muted-foreground)">
|
|
{t('Logos and brand assets (ZIP)')}
|
|
</Line>
|
|
</Leaf>
|
|
</Box>
|
|
</Band>
|
|
|
|
{/*
|
|
CTA Section — NOT a `Band`, and the reason is the two glows. They are
|
|
direct children of the section and are placed from ITS corners; inside
|
|
a Band they would fall to the inner measure box and step ~270px in from
|
|
each edge. A decorative element positioned against the frame keeps the
|
|
frame it was positioned against.
|
|
*/}
|
|
<Box
|
|
render="section"
|
|
position="relative"
|
|
overflow="hidden"
|
|
py={96}
|
|
px={16}
|
|
$md={{ px: 32 }}
|
|
background="linear-gradient(to bottom in oklab, color-mix(in oklab, var(--neutral-900) 30%, transparent), var(--background))"
|
|
>
|
|
<Box position="absolute" t={-160} r={-160} width={320} height={320} rounded="var(--radius-full)" pointerEvents="none" filter="blur(64px)" $platform-web={{ backgroundColor: 'color-mix(in oklab, var(--primary) 10%, transparent)' }} />
|
|
<Box position="absolute" b={-160} l={-160} width={320} height={320} rounded="var(--radius-full)" pointerEvents="none" filter="blur(64px)" $platform-web={{ backgroundColor: 'color-mix(in oklab, var(--primary) 5%, transparent)' }} />
|
|
|
|
<Box position="relative" z="var(--z-raised)" width="100%" maxW={896} mx="auto" $platform-web={{ textAlign: 'center' }}>
|
|
<Title
|
|
render={<motion.h2 initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} />}
|
|
quiet
|
|
mb={16}
|
|
>
|
|
{t('Need brand assistance?')}
|
|
</Title>
|
|
|
|
<Lede
|
|
render={<motion.p initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: 0.1 }} />}
|
|
mb={40}
|
|
width="100%"
|
|
maxW={672}
|
|
mx="auto"
|
|
mt="$3.5"
|
|
>
|
|
{t('Our team can help with custom assets, co-branding requests, and other brand-related inquiries.')}
|
|
</Lede>
|
|
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ delay: 0.2 }}
|
|
>
|
|
<XStack flexWrap="wrap" items="center" justify="center" gap={16}>
|
|
<Action href="/contact" fill px={32}>
|
|
{t('Contact Us')}
|
|
<ArrowRight width={20} height={20} data-i18n-mirror />
|
|
</Action>
|
|
<Action href="mailto:[email protected]" px={32} borderColor="var(--border)">
|
|
brand@hanzo.ai
|
|
</Action>
|
|
</XStack>
|
|
</motion.div>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default Brand;
|