Vertex
Dark deployment-platform page with a terminal that types a live deploy, metrics band, feature grid and pricing.
app/page.tsx
"use client";
import { motion } from "motion/react";
import { ArrowRight, Check, Cpu, GitBranch, Globe, Lock, Terminal, Zap } from "lucide-react";
import { useEffect, useState } from "react";
const ease = [0.22, 1, 0.36, 1] as const;
const up = {
initial: { opacity: 0, y: 24 },
whileInView: { opacity: 1, y: 0 },
viewport: { once: true, amount: 0.3 },
transition: { duration: 0.7, ease },
};
const LINES = [
{ text: "$ vertex deploy --prod", tone: "text-zinc-200" },
{ text: "› Building 142 routes…", tone: "text-zinc-500" },
{ text: "› Optimising images and fonts", tone: "text-zinc-500" },
{ text: "› Deployed to 310 edge locations", tone: "text-zinc-500" },
{ text: "✓ Live at https://acme.vertex.app (11.4s)", tone: "text-emerald-400" },
];
/** Terminal that types its first line, then reveals the output line by line, and loops. */
function DeployTerminal() {
const [typed, setTyped] = useState(0);
const [shown, setShown] = useState(1);
const command = LINES[0].text;
useEffect(() => {
if (typed < command.length) {
const t = setTimeout(() => setTyped((n) => n + 1), 55);
return () => clearTimeout(t);
}
if (shown < LINES.length) {
const t = setTimeout(() => setShown((n) => n + 1), 650);
return () => clearTimeout(t);
}
const t = setTimeout(() => {
setTyped(0);
setShown(1);
}, 3200);
return () => clearTimeout(t);
}, [typed, shown, command.length]);
return (
<div className="overflow-hidden rounded-2xl border border-white/10 bg-zinc-900/80 shadow-2xl shadow-indigo-500/20 backdrop-blur">
<div className="flex items-center gap-1.5 border-b border-white/10 px-4 py-3">
<span className="size-2.5 rounded-full bg-rose-400" />
<span className="size-2.5 rounded-full bg-amber-400" />
<span className="size-2.5 rounded-full bg-emerald-400" />
<span className="ml-3 flex items-center gap-1.5 text-xs text-zinc-500">
<Terminal className="size-3.5" /> acme-web
</span>
</div>
<div className="h-44 p-5 font-mono text-[13px] leading-7" aria-label="Deploy log">
<p className={LINES[0].tone}>
{command.slice(0, typed)}
{typed < command.length && <span className="ml-0.5 inline-block h-4 w-2 animate-pulse bg-zinc-300 align-middle" />}
</p>
{LINES.slice(1, shown).map((l) => (
<motion.p key={l.text} initial={{ opacity: 0, x: -6 }} animate={{ opacity: 1, x: 0 }} className={l.tone}>
{l.text}
</motion.p>
))}
</div>
</div>
);
}
const features = [
{ icon: Zap, title: "Deploys in seconds", text: "Push to main and your change is live worldwide before you switch tabs." },
{ icon: Globe, title: "310 edge locations", text: "Every request is served from the city closest to your user." },
{ icon: GitBranch, title: "Preview every branch", text: "Each pull request gets its own URL, with comments built in." },
{ icon: Lock, title: "Secure by default", text: "TLS, DDoS protection, secrets and audit logs on every plan." },
{ icon: Cpu, title: "Serverless compute", text: "Functions that scale to zero and wake in under 50 ms." },
{ icon: Terminal, title: "One CLI", text: "Develop, preview, deploy and roll back from your terminal." },
];
const plans = [
{ name: "Hobby", price: "$0", note: "For side projects", features: ["Unlimited deploys", "100 GB bandwidth", "Community support"], featured: false },
{ name: "Team", price: "$20", note: "per member / month", features: ["Everything in Hobby", "Preview comments", "Analytics", "Email support"], featured: true },
{ name: "Scale", price: "Custom", note: "For large teams", features: ["SSO and audit logs", "99.99% SLA", "Dedicated support"], featured: false },
];
export default function VertexTemplate() {
return (
<div className="min-h-screen bg-zinc-950 font-sans text-zinc-300 antialiased">
<header className="sticky top-0 z-40 border-b border-white/5 bg-zinc-950/80 backdrop-blur-xl">
<div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
<span className="flex items-center gap-2 font-semibold text-white">
<span className="grid size-7 place-items-center rounded-lg bg-gradient-to-br from-indigo-500 to-cyan-400 text-xs font-bold text-white">V</span> Vertex
</span>
<nav className="hidden gap-8 text-sm md:flex">
{["Product", "Docs", "Pricing", "Changelog"].map((l) => (
<a key={l} href="#" className="transition-colors hover:text-white">
{l}
</a>
))}
</nav>
<a href="#" className="rounded-lg bg-white px-4 py-2 text-sm font-medium text-zinc-900 transition hover:bg-zinc-200">
Start free
</a>
</div>
</header>
<section className="relative isolate overflow-hidden">
<div aria-hidden className="absolute inset-0 -z-10 [background-image:linear-gradient(to_right,rgba(255,255,255,.04)_1px,transparent_1px),linear-gradient(to_bottom,rgba(255,255,255,.04)_1px,transparent_1px)] [background-size:56px_56px] [mask-image:radial-gradient(ellipse_70%_60%_at_50%_0%,black,transparent)]" />
<motion.div aria-hidden className="absolute left-1/2 top-[-14rem] -z-10 size-[40rem] -translate-x-1/2 rounded-full bg-indigo-600/30 blur-3xl" animate={{ scale: [1, 1.15, 1], opacity: [0.6, 1, 0.6] }} transition={{ duration: 9, repeat: Infinity, ease: "easeInOut" }} />
<div className="mx-auto grid max-w-6xl items-center gap-14 px-6 pb-24 pt-20 lg:grid-cols-[1.1fr_1fr] lg:pt-28">
<div>
<motion.a href="#" initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs text-zinc-300">
<span className="size-1.5 animate-pulse rounded-full bg-emerald-400" /> Vertex 3.0 is here <ArrowRight className="size-3" />
</motion.a>
<motion.h1 initial={{ opacity: 0, y: 24, filter: "blur(8px)" }} animate={{ opacity: 1, y: 0, filter: "blur(0px)" }} transition={{ duration: 0.8, delay: 0.1, ease }} className="mt-6 text-5xl font-semibold leading-[1.05] tracking-tight text-white sm:text-6xl">
Ship to the edge in{" "}
<span className="bg-gradient-to-r from-indigo-400 via-cyan-300 to-emerald-300 bg-clip-text text-transparent">eleven seconds</span>
</motion.h1>
<motion.p initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.25, duration: 0.7 }} className="mt-6 max-w-lg text-lg leading-relaxed text-zinc-400">
The deployment platform for teams who would rather build than babysit infrastructure. Git push, and you are live everywhere.
</motion.p>
<motion.div initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.4, duration: 0.7 }} className="mt-9 flex flex-wrap gap-3">
<a href="#" className="group inline-flex items-center gap-2 rounded-xl bg-gradient-to-r from-indigo-500 to-cyan-500 px-6 py-3 text-sm font-medium text-white shadow-lg shadow-indigo-500/30 transition hover:-translate-y-0.5">
Deploy your first app <ArrowRight className="size-4 transition-transform group-hover:translate-x-0.5" />
</a>
<code className="inline-flex items-center rounded-xl border border-white/10 bg-white/5 px-5 py-3 font-mono text-sm text-zinc-300">npm i -g vertex</code>
</motion.div>
</div>
<motion.div initial={{ opacity: 0, y: 30, rotateX: 12 }} animate={{ opacity: 1, y: 0, rotateX: 0 }} transition={{ duration: 0.9, delay: 0.3, ease }} style={{ transformPerspective: 1200 }}>
<DeployTerminal />
</motion.div>
</div>
</section>
<section className="border-y border-white/5 py-10">
<div className="mx-auto grid max-w-6xl grid-cols-2 gap-8 px-6 text-center md:grid-cols-4">
{[
["11.4s", "median deploy"],
["310", "edge locations"],
["99.99%", "uptime, last 12 months"],
["48 ms", "global p50 latency"],
].map(([value, label], i) => (
<motion.div key={label} {...up} transition={{ duration: 0.6, delay: i * 0.08, ease }}>
<p className="text-3xl font-semibold tracking-tight text-white">{value}</p>
<p className="mt-1 text-sm text-zinc-500">{label}</p>
</motion.div>
))}
</div>
</section>
<section className="mx-auto max-w-6xl px-6 py-24">
<motion.div {...up} className="max-w-2xl">
<p className="text-sm font-medium text-cyan-300">Platform</p>
<h2 className="mt-2 text-4xl font-semibold tracking-tight text-white">Everything between your code and your users</h2>
</motion.div>
<div className="mt-12 grid gap-px overflow-hidden rounded-3xl border border-white/10 bg-white/10 sm:grid-cols-2 lg:grid-cols-3">
{features.map((f, i) => (
<motion.div key={f.title} {...up} transition={{ duration: 0.6, delay: (i % 3) * 0.08, ease }} className="group bg-zinc-950 p-8 transition-colors hover:bg-zinc-900">
<span className="grid size-10 place-items-center rounded-xl bg-white/5 text-cyan-300 ring-1 ring-white/10 transition-transform group-hover:-rotate-6 group-hover:scale-110">
<f.icon className="size-5" />
</span>
<h3 className="mt-5 font-semibold text-white">{f.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-zinc-400">{f.text}</p>
</motion.div>
))}
</div>
</section>
<section className="mx-auto max-w-6xl px-6 pb-24">
<motion.div {...up} className="mx-auto max-w-2xl text-center">
<h2 className="text-4xl font-semibold tracking-tight text-white">Simple pricing</h2>
<p className="mt-3 text-zinc-400">Free for hobby projects. Pay per member when your team grows.</p>
</motion.div>
<div className="mt-12 grid gap-5 md:grid-cols-3">
{plans.map((p, i) => (
<motion.div key={p.name} {...up} transition={{ duration: 0.6, delay: i * 0.1, ease }} className={`rounded-3xl p-7 ${p.featured ? "bg-gradient-to-b from-indigo-500/20 to-transparent ring-1 ring-indigo-400/50" : "ring-1 ring-white/10"}`}>
<p className="font-semibold text-white">{p.name}</p>
<p className="mt-4 text-4xl font-semibold tracking-tight text-white">{p.price}</p>
<p className="text-sm text-zinc-500">{p.note}</p>
<ul className="mt-6 space-y-2.5 text-sm">
{p.features.map((x) => (
<li key={x} className="flex items-center gap-2">
<Check className="size-4 text-cyan-300" /> {x}
</li>
))}
</ul>
<a href="#" className={`mt-7 block rounded-xl py-2.5 text-center text-sm font-medium transition ${p.featured ? "bg-white text-zinc-900 hover:bg-zinc-200" : "bg-white/5 text-white ring-1 ring-white/10 hover:bg-white/10"}`}>
{p.price === "Custom" ? "Talk to sales" : "Get started"}
</a>
</motion.div>
))}
</div>
</section>
<section className="px-6 pb-24">
<motion.div {...up} className="relative mx-auto max-w-6xl overflow-hidden rounded-[2rem] bg-gradient-to-br from-indigo-600 via-indigo-700 to-cyan-600 px-8 py-16 text-center">
<div aria-hidden className="absolute inset-0 opacity-20 [background-image:radial-gradient(#fff_1px,transparent_1px)] [background-size:22px_22px]" />
<h2 className="relative text-4xl font-semibold tracking-tight text-white sm:text-5xl">Your next deploy is 11 seconds away</h2>
<a href="#" className="relative mt-8 inline-flex items-center gap-2 rounded-xl bg-white px-6 py-3 text-sm font-medium text-zinc-900 transition hover:-translate-y-0.5">
Start free <ArrowRight className="size-4" />
</a>
</motion.div>
</section>
<footer className="border-t border-white/5 py-10">
<div className="mx-auto flex max-w-6xl flex-wrap items-center justify-between gap-4 px-6 text-sm text-zinc-500">
<span>© 2026 Vertex Labs</span>
<span className="flex gap-6">
{["Status", "Privacy", "Terms"].map((l) => (
<a key={l} href="#" className="hover:text-white">
{l}
</a>
))}
</span>
</div>
</footer>
</div>
);
}
What's included
- Navbar
- Hero + live terminal
- Metrics
- Feature grid
- Pricing
- CTA
- Footer
Use it in 3 steps
1. Install dependencies in your Next.js + Tailwind project:
$ npm i motion lucide-react- 2. Copy the code into
app/page.tsx. - 3. Replace the copy, colors and links with your own — then deploy.