mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
119 lines
4.3 KiB
TypeScript
119 lines
4.3 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* NO TAILWIND. The section frame and its centred head are `Band` and `Head`
|
|
* from components/marketing/band — the gutter, the measure and the head's
|
|
* rhythm this file used to write out by hand — and the six tiles are gui
|
|
* props.
|
|
*
|
|
* The head loses `ChromeText`, and that is the point rather than a casualty:
|
|
* a `ChromeText` h2 carrying its own size and weight is the `Display` rule
|
|
* with its scale overridden at the call site, which is exactly the split
|
|
* `Title` exists to close. A section heading takes `Title`; the page's own
|
|
* `h1` keeps `Display`.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { motion } from "@/components/motion";
|
|
import { Cpu, Server, Zap, Network, BarChart, Globe } from "lucide-react";
|
|
import { View } from '@hanzo/ui'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { Band, Head, Words } from '@/components/marketing/band'
|
|
|
|
/** How narrow a column may get before the grid drops one. */
|
|
const DECK = { min: 304, max: 3 } as const
|
|
|
|
interface FeatureCardProps {
|
|
icon: React.ComponentType<{ width?: number; height?: number }>;
|
|
title: string;
|
|
description: string;
|
|
delay: number;
|
|
}
|
|
|
|
const FeatureCard = ({ icon: Icon, title, description, delay }: FeatureCardProps) => {
|
|
return (
|
|
<View
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay }} viewport={{ once: true }} />}
|
|
display="block"
|
|
p={24}
|
|
rounded="var(--radius-xl)"
|
|
background="color-mix(in srgb, var(--primary) 10%, transparent)"
|
|
border="1px solid color-mix(in srgb, var(--primary) 25%, transparent)"
|
|
>
|
|
{/* The icon sits on a line of its own, so the gap under it is the line's
|
|
descent plus the 16px beat, as it was when the icon carried the margin. */}
|
|
<Words render="div" mb={16}>
|
|
<Icon width={40} height={40} />
|
|
</Words>
|
|
<Words render="h3" mb={8} fontSize="$6" lineHeight="1.4" weight="700" color="var(--foreground)">{title}</Words>
|
|
<Words render="p" color="var(--muted-foreground)">
|
|
{description}
|
|
</Words>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const MachinesFeatures = () => {
|
|
const features = [
|
|
{
|
|
icon: Cpu,
|
|
title: "Ask what exists",
|
|
description: "The sizes and regions you can launch into are an endpoint, not a page in the docs. Read them, then pick one — nothing to keep in sync by hand.",
|
|
delay: 0
|
|
},
|
|
{
|
|
icon: Server,
|
|
title: "Price it before you buy it",
|
|
description: "A launch with dryRun returns the price and creates nothing. The same call without it creates the machine, so what you were quoted is what you asked for.",
|
|
delay: 0.1
|
|
},
|
|
{
|
|
icon: Zap,
|
|
title: "Metered to the organization",
|
|
description: "Time is debited from your org's prepaid balance while a machine is up. Before it launches, the cloud says whether you may — and what is missing if you may not.",
|
|
delay: 0.2
|
|
},
|
|
{
|
|
icon: Network,
|
|
title: "One list, whoever owns them",
|
|
description: "Rented machines, the droplets and cluster workers behind them, and your own hardware that dialed in with hanzo link all appear as one fleet, deduplicated.",
|
|
delay: 0.3
|
|
},
|
|
{
|
|
icon: BarChart,
|
|
title: "A row per accelerator",
|
|
description: "The GPU view counts real cards: from the size of the nodes you rented, and from what your own workers report through nvidia-smi. Where there is no inventory, it returns nothing rather than inventing something.",
|
|
delay: 0.4
|
|
},
|
|
{
|
|
icon: Globe,
|
|
title: "A machine can carry an agent",
|
|
description: "Bind a cloud agent to a machine and the pair is one thing to launch, message, stop and tear down. Unbinding is separate, so the agent can move without the machine going with it.",
|
|
delay: 0.5
|
|
}
|
|
];
|
|
|
|
return (
|
|
<Band rule={false}>
|
|
<Head
|
|
title="How renting one works"
|
|
lede="Every step is an API call, so anything you do once by hand you can put in a script afterwards"
|
|
/>
|
|
|
|
<Grid columns={DECK} gap={32}>
|
|
{features.map((feature, index) => (
|
|
<FeatureCard
|
|
key={index}
|
|
icon={feature.icon}
|
|
title={feature.title}
|
|
description={feature.description}
|
|
delay={feature.delay}
|
|
/>
|
|
))}
|
|
</Grid>
|
|
</Band>
|
|
);
|
|
};
|
|
|
|
export default MachinesFeatures;
|