diff --git a/README.md b/README.md
index e215bc4..e8d2a53 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,66 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+# Vertex landing page starter
-## Getting Started
+A single-page AI SaaS landing page built with Next.js, TypeScript, Tailwind CSS, shadcn/ui, Magic UI, and next-themes. Includes light/dark themes, animated feature visuals, pricing tabs, and testimonial marquees.
-First, run the development server:
+## Getting started
+
+Use the repository's pinned Yarn version:
```bash
-npm run dev
-# or
+yarn install
yarn dev
-# or
-pnpm dev
-# or
-bun dev
```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+Open the local URL printed by Next.js. To use a specific port, run `yarn dev --port 3002`.
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+## Folder organization
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+```text
+app/
+ page.tsx # Page frame and section order
+ layout.tsx # Metadata, Geist fonts, and theme provider
+ globals.css # Theme tokens, animations, and frame styles
+components/
+ layout/ # Header, footer, theme provider, and toggle
+ sections/
+ hero-section.tsx
+ logos-section.tsx
+ features-section.tsx
+ features/ # Terminal, orbit, activity list, chart, globe
+ developers-section.tsx
+ reliability-section.tsx
+ pricing-section.tsx
+ testimonials-section.tsx
+ cta-section.tsx
+ shared/ # Brand, headings, frame crosses, reusable copy/cards
+ ui/ # shadcn/ui and Magic UI primitives
+hooks/ # Shared hooks
+lib/ # Utilities
+public/ # Brand logo and Open Graph image
+```
-## Learn More
+Each section is a named component imported directly into `app/page.tsx`. Reorder or remove its component there to change the page composition. Sections retain the shared page frame, borders, and responsive spacing.
-To learn more about Next.js, take a look at the following resources:
+Copy, logo URLs, sample metrics, plans, and testimonials live in the components that use them. There is no separate content or site configuration layer. Feature-specific data lives beside its visual in `components/sections/features/`.
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+Pricing owns its billing state. Theme controls and JavaScript-driven visuals keep their own client boundaries; the page composition and static sections do not need a page-wide client component.
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+## Customizing the template
-## Deploy on Vercel
+- Edit section copy and data in `components/sections/`.
+- Edit navigation and footer links in `components/layout/`.
+- Change the wordmark in `components/shared/brand.tsx` and replace `public/logo.svg`.
+- Adjust colors, typography tokens, and decorative effects in `app/globals.css`.
+- Update metadata in `app/layout.tsx`, the favicon in `app/favicon.ico`, and the social preview in `public/og.png`. Set `NEXT_PUBLIC_SITE_URL` to your site's origin when publishing.
+- Logos and portrait photos are loaded from `https://sb.frontend.co`. Update the source URLs in their components and `images.remotePatterns` in `next.config.js` if using another host.
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+Testimonials, portraits, metrics, and pricing are sample content, not verified customer endorsements or service guarantees. Replace them and connect the placeholder signup, sales, status, and privacy links before publishing.
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+## Checks and production build
+
+```bash
+yarn lint
+yarn tsc --noEmit
+yarn build
+yarn start
+```
diff --git a/app/layout.tsx b/app/layout.tsx
index 7b34669..357efbe 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
-import { ThemeProvider } from "@/components/theme-provider";
+import { ThemeProvider } from "@/components/layout/theme-provider";
import "./globals.css";
const geistSans = Geist({
diff --git a/app/page.tsx b/app/page.tsx
index d81ff3f..51cb699 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,5 +1,29 @@
-import { VertexLandingPage } from "@/components/vertex-landing"
+import { SiteFooter } from "@/components/layout/site-footer"
+import { SiteHeader } from "@/components/layout/site-header"
+import { CtaSection } from "@/components/sections/cta-section"
+import { DevelopersSection } from "@/components/sections/developers-section"
+import { FeaturesSection } from "@/components/sections/features-section"
+import { HeroSection } from "@/components/sections/hero-section"
+import { LogosSection } from "@/components/sections/logos-section"
+import { PricingSection } from "@/components/sections/pricing-section"
+import { ReliabilitySection } from "@/components/sections/reliability-section"
+import { TestimonialsSection } from "@/components/sections/testimonials-section"
export default function Home() {
- return
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
}
diff --git a/components/layout/site-footer.tsx b/components/layout/site-footer.tsx
new file mode 100644
index 0000000..6d3c792
--- /dev/null
+++ b/components/layout/site-footer.tsx
@@ -0,0 +1,26 @@
+import { Brand } from "@/components/shared/brand"
+
+export function SiteFooter() {
+ return (
+
+ )
+}
diff --git a/components/layout/site-header.tsx b/components/layout/site-header.tsx
new file mode 100644
index 0000000..129b24b
--- /dev/null
+++ b/components/layout/site-header.tsx
@@ -0,0 +1,43 @@
+import { ChevronDown, ChevronRight } from "lucide-react"
+
+import { ThemeToggle } from "@/components/layout/theme-toggle"
+import { Brand } from "@/components/shared/brand"
+
+export function SiteHeader() {
+ return (
+
+
+
+
+
+
+ {[
+ ["Products", "#features"],
+ ["Models", "#models"],
+ ["Features", "#features"],
+ ["Pricing", "#pricing"],
+ ["Docs", "#developers"],
+ ].map(([label, href]) => (
+
+ {label}
+ {label === "Products" && }
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/components/theme-provider.tsx b/components/layout/theme-provider.tsx
similarity index 100%
rename from components/theme-provider.tsx
rename to components/layout/theme-provider.tsx
diff --git a/components/theme-toggle.tsx b/components/layout/theme-toggle.tsx
similarity index 100%
rename from components/theme-toggle.tsx
rename to components/layout/theme-toggle.tsx
diff --git a/components/sections/cta-section.tsx b/components/sections/cta-section.tsx
new file mode 100644
index 0000000..3d188e8
--- /dev/null
+++ b/components/sections/cta-section.tsx
@@ -0,0 +1,31 @@
+import { VertexMark } from "@/components/shared/brand"
+import { FrameCrosses } from "@/components/shared/frame-crosses"
+import { FlickeringGrid } from "@/components/ui/flickering-grid"
+
+export function CtaSection() {
+ return (
+
+
+
+
+
+
+
+ Build on every model.
+ Ship through one API.
+
+
+ Your models will change. Your infrastructure doesn’t have to.
+
+
+
+
+ )
+}
diff --git a/components/sections/developers-section.tsx b/components/sections/developers-section.tsx
new file mode 100644
index 0000000..00e30e2
--- /dev/null
+++ b/components/sections/developers-section.tsx
@@ -0,0 +1,41 @@
+import { Code2, Gauge, Workflow } from "lucide-react"
+
+import { SectionHeading } from "@/components/shared/section-heading"
+import { cn } from "@/lib/utils"
+
+export function DevelopersSection() {
+ return (
+
+
+
+ {[
+ {
+ icon: Code2,
+ title: "Change one base URL",
+ text: "Keep your existing OpenAI-compatible SDK, payloads, and streaming logic.",
+ },
+ {
+ icon: Workflow,
+ title: "Set a routing policy",
+ text: "Choose a model directly or let Vertex optimize for cost, quality, or latency.",
+ },
+ {
+ icon: Gauge,
+ title: "Observe every request",
+ text: "Trace performance and spend across models, applications, and environments.",
+ },
+ ].map((step, index) => (
+
+
+ {step.title}
+ {step.text}
+
+ ))}
+
+
+ )
+}
diff --git a/components/sections/features-section.tsx b/components/sections/features-section.tsx
new file mode 100644
index 0000000..9422ce4
--- /dev/null
+++ b/components/sections/features-section.tsx
@@ -0,0 +1,63 @@
+import { ApiTerminal } from "@/components/sections/features/api-terminal"
+import { GlobalGlobe } from "@/components/sections/features/global-globe"
+import { IntegrationOrbit } from "@/components/sections/features/integration-orbit"
+import { RoutingActivity } from "@/components/sections/features/routing-activity"
+import { UsageChart } from "@/components/sections/features/usage-chart"
+import { FeatureCopy } from "@/components/shared/feature-copy"
+import { SectionHeading } from "@/components/shared/section-heading"
+
+export function FeaturesSection() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/sections/features/api-terminal.tsx b/components/sections/features/api-terminal.tsx
new file mode 100644
index 0000000..2946a81
--- /dev/null
+++ b/components/sections/features/api-terminal.tsx
@@ -0,0 +1,34 @@
+"use client"
+
+import {
+ AnimatedSpan,
+ Terminal,
+ TypingAnimation,
+} from "@/components/ui/terminal"
+
+export function ApiTerminal() {
+ return (
+
+
+
+ {"> curl https://api.vertex.ai/v1/chat/completions \\"}
+
+
+ {` -d '{ "model": "vertex/auto", "messages": [...] }'`}
+
+
+ → evaluating 14 available models
+
+
+ → route selected: anthropic/claude-sonnet
+
+
+ ✓ response streamed in 342ms
+
+
+ One endpoint. Any model. No rewrites.
+
+
+
+ )
+}
diff --git a/components/sections/features/global-globe.tsx b/components/sections/features/global-globe.tsx
new file mode 100644
index 0000000..ade5c1a
--- /dev/null
+++ b/components/sections/features/global-globe.tsx
@@ -0,0 +1,40 @@
+"use client"
+
+import { Globe } from "@/components/ui/globe"
+
+const globeConfig = {
+ width: 800,
+ height: 800,
+ onRender: () => {},
+ devicePixelRatio: 2,
+ phi: 0,
+ theta: 0.28,
+ dark: 1,
+ diffuse: 1.1,
+ mapSamples: 12000,
+ mapBrightness: 4.5,
+ baseColor: [0.05, 0.12, 0.32] as [number, number, number],
+ markerColor: [0.15, 0.46, 1] as [number, number, number],
+ glowColor: [0.08, 0.18, 0.48] as [number, number, number],
+ markers: [
+ { location: [37.7749, -122.4194] as [number, number], size: 0.08 },
+ { location: [40.7128, -74.006] as [number, number], size: 0.06 },
+ { location: [51.5072, -0.1276] as [number, number], size: 0.07 },
+ { location: [48.8566, 2.3522] as [number, number], size: 0.05 },
+ { location: [1.3521, 103.8198] as [number, number], size: 0.08 },
+ { location: [35.6762, 139.6503] as [number, number], size: 0.06 },
+ { location: [-33.8688, 151.2093] as [number, number], size: 0.05 },
+ { location: [-23.5505, -46.6333] as [number, number], size: 0.06 },
+ ],
+}
+
+export function GlobalGlobe() {
+ return (
+
+
+
+ 8 regions online
+
+
+ )
+}
diff --git a/components/sections/features/integration-orbit.tsx b/components/sections/features/integration-orbit.tsx
new file mode 100644
index 0000000..24b38df
--- /dev/null
+++ b/components/sections/features/integration-orbit.tsx
@@ -0,0 +1,79 @@
+import Image from "next/image"
+
+import { VertexMark } from "@/components/shared/brand"
+import { OrbitingCircles } from "@/components/ui/orbiting-circles"
+
+const integrations = [
+ {
+ name: "Supabase",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/supabase.svg",
+ },
+ {
+ name: "Notion",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/notion.svg",
+ },
+ {
+ name: "Figma",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/figma.svg",
+ },
+ {
+ name: "Dropbox",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/dropbox.svg",
+ },
+ {
+ name: "Google",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/google.svg",
+ },
+ {
+ name: "OpenAI",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/openai.svg",
+ },
+ {
+ name: "Stripe",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/stripe.svg",
+ },
+ {
+ name: "GitHub",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/square/github.svg",
+ },
+]
+
+function IntegrationMark({ src, name }: { src: string; name: string }) {
+ return (
+
+
+
+ )
+}
+
+export function IntegrationOrbit() {
+ return (
+
+
+
+ {integrations.slice(0, 4).map((integration) => (
+
+ ))}
+
+
+ {integrations.slice(4).map((integration) => (
+
+ ))}
+
+
+
+ )
+}
diff --git a/components/sections/features/routing-activity.tsx b/components/sections/features/routing-activity.tsx
new file mode 100644
index 0000000..8dbd0e3
--- /dev/null
+++ b/components/sections/features/routing-activity.tsx
@@ -0,0 +1,60 @@
+"use client"
+
+import { Activity, CircleCheck, Route, ShieldCheck, Zap } from "lucide-react"
+
+import { AnimatedList } from "@/components/ui/animated-list"
+import { cn } from "@/lib/utils"
+
+const routingEvents = [
+ {
+ icon: ,
+ title: "Routed to Claude Sonnet",
+ meta: "342ms · 8.2k tokens",
+ accent: "bg-blue-600/15 text-blue-600 dark:text-blue-400",
+ },
+ {
+ icon: ,
+ title: "Prompt cache hit",
+ meta: "Saved $0.018 · 42ms",
+ accent: "bg-amber-500/15 text-amber-500",
+ },
+ {
+ icon: ,
+ title: "Latency policy applied",
+ meta: "Gemini Flash · 118ms",
+ accent: "bg-blue-500/15 text-blue-500",
+ },
+ {
+ icon: ,
+ title: "Fallback completed",
+ meta: "OpenAI → Mistral · 621ms",
+ accent: "bg-emerald-500/15 text-emerald-500",
+ },
+ {
+ icon: ,
+ title: "Guardrail passed",
+ meta: "No sensitive data detected",
+ accent: "bg-cyan-500/15 text-cyan-500",
+ },
+]
+
+export function RoutingActivity() {
+ return (
+
+
+ {routingEvents.map((event) => (
+
+
+ {event.icon}
+
+
+ {event.title}
+ {event.meta}
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/components/usage-chart.tsx b/components/sections/features/usage-chart.tsx
similarity index 100%
rename from components/usage-chart.tsx
rename to components/sections/features/usage-chart.tsx
diff --git a/components/sections/hero-section.tsx b/components/sections/hero-section.tsx
new file mode 100644
index 0000000..2ee43a2
--- /dev/null
+++ b/components/sections/hero-section.tsx
@@ -0,0 +1,41 @@
+import { ArrowRight } from "lucide-react"
+
+import { FrameCrosses } from "@/components/shared/frame-crosses"
+
+export function HeroSection() {
+ return (
+
+
+
+
+ The unified AI gateway
+
+
+ One API.
+ Every AI model.
+
+
+ Route, optimize, and observe every model request through one OpenAI-compatible endpoint built for production.
+
+
+
+
+
+ )
+}
diff --git a/components/sections/logos-section.tsx b/components/sections/logos-section.tsx
new file mode 100644
index 0000000..637665c
--- /dev/null
+++ b/components/sections/logos-section.tsx
@@ -0,0 +1,93 @@
+import Image from "next/image"
+
+import { FlickeringGrid } from "@/components/ui/flickering-grid"
+
+const customerLogos = [
+ {
+ name: "Next.js",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/nextjs-logo.svg",
+ width: 394,
+ height: 80,
+ },
+ {
+ name: "OpenAI",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/openai-logo.svg",
+ width: 512,
+ height: 126,
+ },
+ {
+ name: "Stripe",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/stripe-logo.svg",
+ width: 73,
+ height: 31,
+ },
+ {
+ name: "Vercel",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/vercel-logo.svg",
+ width: 2048,
+ height: 407,
+ },
+ {
+ name: "Supabase",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/supabase-logo.svg",
+ width: 581,
+ height: 113,
+ },
+ {
+ name: "Anthropic",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/anthropic-logo.svg",
+ width: 579,
+ height: 65,
+ },
+ {
+ name: "Tailwind CSS",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/tailwindcss-logo.svg",
+ width: 665,
+ height: 82,
+ },
+ {
+ name: "Linear",
+ src: "https://sb.frontend.co/storage/v1/object/public/assets/logos/standard/linear-logo.svg",
+ width: 122,
+ height: 31,
+ },
+]
+
+export function LogosSection() {
+ return (
+
+
+
+
+
+ Built for
+ your AI stack.
+
+
+
+ {customerLogos.map((logo) => (
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/components/sections/pricing-section.tsx b/components/sections/pricing-section.tsx
new file mode 100644
index 0000000..f0a745c
--- /dev/null
+++ b/components/sections/pricing-section.tsx
@@ -0,0 +1,108 @@
+"use client"
+
+import { useState } from "react"
+import { Check } from "lucide-react"
+
+import { SectionHeading } from "@/components/shared/section-heading"
+import { cn } from "@/lib/utils"
+
+export function PricingSection() {
+ const [annual, setAnnual] = useState(false)
+
+ return (
+
+
+
+
+ setAnnual(false)}
+ className={cn("rounded-sm px-4 py-1.5 text-xs font-medium transition", !annual ? "bg-background text-foreground shadow-sm" : "text-muted-foreground")}
+ >
+ Monthly
+
+ setAnnual(true)}
+ className={cn("rounded-sm px-4 py-1.5 text-xs font-medium transition", annual ? "bg-background text-foreground shadow-sm" : "text-muted-foreground")}
+ >
+ Annual −20%
+
+
+
+
+ {[
+ {
+ name: "Developer",
+ price: "$0",
+ cadence: "/ month",
+ description: "For prototypes, side projects, and individual builders.",
+ action: "Start building",
+ popular: false,
+ features: ["Pay-as-you-go model usage", "All model providers", "Basic usage analytics", "Community support"],
+ },
+ {
+ name: "Team",
+ price: annual ? "$39" : "$49",
+ cadence: "/ month",
+ description: "For teams running reliable AI products in production.",
+ action: "Start 14-day trial",
+ popular: true,
+ features: ["Everything in Developer", "Routing and fallback policies", "Team budgets and alerts", "90-day request analytics", "Priority support"],
+ },
+ {
+ name: "Enterprise",
+ price: "Custom",
+ cadence: "",
+ description: "For organizations that need security and scale.",
+ action: "Talk to sales",
+ popular: false,
+ features: ["Everything in Team", "SAML SSO and audit logs", "Custom retention and regions", "Dedicated capacity", "Enterprise SLA"],
+ },
+ ].map((plan) => (
+
+
+
+
{plan.name}
+ {plan.popular && (
+
+ Most popular
+
+ )}
+
+
+ {plan.price}
+ {plan.cadence && {plan.cadence} }
+
+
{plan.description}
+
+ {plan.action}
+
+
+ {plan.features.map((feature) => (
+
+
+ {feature}
+
+ ))}
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/components/sections/reliability-section.tsx b/components/sections/reliability-section.tsx
new file mode 100644
index 0000000..9c20021
--- /dev/null
+++ b/components/sections/reliability-section.tsx
@@ -0,0 +1,35 @@
+import { LockKeyhole } from "lucide-react"
+
+import { Ripple } from "@/components/ui/ripple"
+import { cn } from "@/lib/utils"
+
+export function ReliabilitySection() {
+ return (
+
+
+
+
+
+
+
+ Reliability built into every request.
+
+
+ Production-grade controls keep your workloads available, observable, and protected as they scale.
+
+
+ {[
+ ["99.99%", "Gateway uptime"],
+ ["8", "Global regions"],
+ ["< 20ms", "Routing overhead"],
+ ].map(([value, label], index) => (
+
+ {value}
+ {label}
+
+ ))}
+
+
+
+ )
+}
diff --git a/components/sections/testimonials-section.tsx b/components/sections/testimonials-section.tsx
new file mode 100644
index 0000000..922f64a
--- /dev/null
+++ b/components/sections/testimonials-section.tsx
@@ -0,0 +1,71 @@
+import { ReviewCard, type Testimonial } from "@/components/shared/review-card"
+import { Marquee } from "@/components/ui/marquee"
+
+const reviews: Testimonial[] = [
+ {
+ avatar: "https://sb.frontend.co/storage/v1/object/public/assets/avatars/unsplash/female-1.jpeg",
+ name: "Maya Kim",
+ role: "AI Platform Lead",
+ quote:
+ "Vertex gave us one reliable interface for every model experiment. We stopped rebuilding provider integrations and started shipping.",
+ },
+ {
+ avatar: "https://sb.frontend.co/storage/v1/object/public/assets/avatars/unsplash/male-1.jpeg",
+ name: "Jon Bell",
+ role: "Founding Engineer",
+ quote:
+ "Fallback routing turned model outages into a non-event. Our application stays fast even when a provider is under pressure.",
+ },
+ {
+ avatar: "https://sb.frontend.co/storage/v1/object/public/assets/avatars/unsplash/male-2.jpeg",
+ name: "Ari Rao",
+ role: "VP of Engineering",
+ quote:
+ "The unified cost view finally made inference spend understandable across teams, providers, and environments.",
+ },
+ {
+ avatar: "https://sb.frontend.co/storage/v1/object/public/assets/avatars/unsplash/female-2.jpeg",
+ name: "Sofia Chen",
+ role: "Product Engineer",
+ quote:
+ "We changed one base URL and had access to the models we needed. The developer experience is exactly that simple.",
+ },
+ {
+ avatar: "https://sb.frontend.co/storage/v1/object/public/assets/avatars/unsplash/male-3.jpeg",
+ name: "Owen Taylor",
+ role: "Infrastructure Director",
+ quote:
+ "Vertex lets our developers move quickly while the platform team keeps control of budgets, policies, and reliability.",
+ },
+ {
+ avatar: "https://sb.frontend.co/storage/v1/object/public/assets/avatars/unsplash/female-3.jpeg",
+ name: "Nina Brooks",
+ role: "Applied AI Engineer",
+ quote:
+ "Model comparisons that used to take days now take minutes. The same payload works everywhere we want to test.",
+ },
+]
+
+export function TestimonialsSection() {
+ const firstReviews = reviews.slice(0, 3)
+ const secondReviews = reviews.slice(3)
+
+ return (
+
+
+
Early access feedback
+
Built for teams shipping AI
+
+
+
+ {firstReviews.map((review) => )}
+
+
+ {secondReviews.map((review) => )}
+
+
+
+
+
+ )
+}
diff --git a/components/shared/brand.tsx b/components/shared/brand.tsx
new file mode 100644
index 0000000..1f10cb0
--- /dev/null
+++ b/components/shared/brand.tsx
@@ -0,0 +1,27 @@
+import Image from "next/image"
+
+import { cn } from "@/lib/utils"
+
+export function VertexMark({ className }: { className?: string }) {
+ return (
+
+ )
+}
+
+export function Brand({ compact = false }: { compact?: boolean }) {
+ return (
+
+
+ {!compact && (
+ Vertex
+ )}
+
+ )
+}
diff --git a/components/shared/feature-copy.tsx b/components/shared/feature-copy.tsx
new file mode 100644
index 0000000..5161998
--- /dev/null
+++ b/components/shared/feature-copy.tsx
@@ -0,0 +1,14 @@
+export function FeatureCopy({
+ title,
+ description,
+}: {
+ title: string
+ description: string
+}) {
+ return (
+
+
{title}
+
{description}
+
+ )
+}
diff --git a/components/shared/frame-crosses.tsx b/components/shared/frame-crosses.tsx
new file mode 100644
index 0000000..c496232
--- /dev/null
+++ b/components/shared/frame-crosses.tsx
@@ -0,0 +1,10 @@
+export function FrameCrosses() {
+ return (
+ <>
+
+
+
+
+ >
+ )
+}
diff --git a/components/shared/review-card.tsx b/components/shared/review-card.tsx
new file mode 100644
index 0000000..71bebfe
--- /dev/null
+++ b/components/shared/review-card.tsx
@@ -0,0 +1,31 @@
+import Image from "next/image"
+
+export interface Testimonial {
+ avatar: string
+ name: string
+ role: string
+ quote: string
+}
+
+export function ReviewCard({ review }: { review: Testimonial }) {
+ return (
+
+ “{review.quote}”
+
+
+
+ {review.name}
+ {review.role}
+
+
+
+ )
+}
diff --git a/components/shared/section-heading.tsx b/components/shared/section-heading.tsx
new file mode 100644
index 0000000..8c81f49
--- /dev/null
+++ b/components/shared/section-heading.tsx
@@ -0,0 +1,29 @@
+import { cn } from "@/lib/utils"
+
+export function SectionHeading({
+ eyebrow,
+ title,
+ description,
+ className,
+}: {
+ eyebrow: string
+ title: string
+ description: string
+ className?: string
+}) {
+ return (
+
+
+
+ {eyebrow}
+
+
+ {title}
+
+
+ {description}
+
+
+
+ )
+}
diff --git a/components/vertex-landing.tsx b/components/vertex-landing.tsx
deleted file mode 100644
index 3330a5e..0000000
--- a/components/vertex-landing.tsx
+++ /dev/null
@@ -1,708 +0,0 @@
-"use client"
-
-import Image from "next/image"
-import { useState } from "react"
-import {
- Activity,
- ArrowRight,
- Check,
- ChevronDown,
- ChevronRight,
- CircleCheck,
- Code2,
- Gauge,
- LockKeyhole,
- Route,
- ShieldCheck,
- Workflow,
- Zap,
-} from "lucide-react"
-import { ThemeToggle } from "@/components/theme-toggle"
-import { UsageChart } from "@/components/usage-chart"
-import { AnimatedList } from "@/components/ui/animated-list"
-import { FlickeringGrid } from "@/components/ui/flickering-grid"
-import { Globe } from "@/components/ui/globe"
-import { Marquee } from "@/components/ui/marquee"
-import { OrbitingCircles } from "@/components/ui/orbiting-circles"
-import { Ripple } from "@/components/ui/ripple"
-import {
- AnimatedSpan,
- Terminal,
- TypingAnimation,
-} from "@/components/ui/terminal"
-import { cn } from "@/lib/utils"
-
-// Original SVG artwork sourced from https://svgl.app.
-const integrations = [
- { name: "Supabase", src: "/logos/integrations/supabase.svg" },
- { name: "Notion", src: "/logos/integrations/notion.svg" },
- { name: "Figma", src: "/logos/integrations/figma.svg" },
- { name: "Dropbox", src: "/logos/integrations/dropbox.svg" },
- { name: "Gmail", src: "/logos/integrations/gmail.svg" },
- { name: "Slack", src: "/logos/integrations/slack.svg" },
- { name: "Google Drive", src: "/logos/integrations/google-drive.svg" },
- { name: "GitHub", src: "/logos/integrations/github.svg" },
-]
-
-const customerLogos = [
- { name: "OpenAI", src: "/logos/openai.svg", width: 110, height: 31 },
- { name: "Retool", src: "/logos/retool.svg", width: 113, height: 25 },
- { name: "Stripe", src: "/logos/stripe.svg", width: 73, height: 31 },
- { name: "Wise", src: "/logos/wise.svg", width: 96, height: 23 },
- { name: "Tailwind CSS", src: "/logos/tailwindcss.svg", width: 262, height: 33, className: "w-full max-w-[158px] brightness-0" },
- { name: "Medium", src: "/logos/medium.svg", width: 132, height: 21 },
- { name: "Next.js", src: "/logos/nextjs.svg", width: 394, height: 80 },
- { name: "Linear", src: "/logos/linear.svg", width: 122, height: 31 },
-]
-
-const routingEvents = [
- {
- icon: ,
- title: "Routed to Claude Sonnet",
- meta: "342ms · 8.2k tokens",
- accent: "bg-blue-600/15 text-blue-600 dark:text-blue-400",
- },
- {
- icon: ,
- title: "Prompt cache hit",
- meta: "Saved $0.018 · 42ms",
- accent: "bg-amber-500/15 text-amber-500",
- },
- {
- icon: ,
- title: "Latency policy applied",
- meta: "Gemini Flash · 118ms",
- accent: "bg-blue-500/15 text-blue-500",
- },
- {
- icon: ,
- title: "Fallback completed",
- meta: "OpenAI → Mistral · 621ms",
- accent: "bg-emerald-500/15 text-emerald-500",
- },
- {
- icon: ,
- title: "Guardrail passed",
- meta: "No sensitive data detected",
- accent: "bg-cyan-500/15 text-cyan-500",
- },
-]
-
-const reviews = [
- {
- initials: "MK",
- name: "Maya Kim",
- role: "AI Platform Lead",
- quote:
- "Vertex gave us one reliable interface for every model experiment. We stopped rebuilding provider integrations and started shipping.",
- },
- {
- initials: "JL",
- name: "Jon Bell",
- role: "Founding Engineer",
- quote:
- "Fallback routing turned model outages into a non-event. Our application stays fast even when a provider is under pressure.",
- },
- {
- initials: "AR",
- name: "Ari Rao",
- role: "VP of Engineering",
- quote:
- "The unified cost view finally made inference spend understandable across teams, providers, and environments.",
- },
- {
- initials: "SC",
- name: "Sofia Chen",
- role: "Product Engineer",
- quote:
- "We changed one base URL and had access to the models we needed. The developer experience is exactly that simple.",
- },
- {
- initials: "OT",
- name: "Owen Taylor",
- role: "Infrastructure Director",
- quote:
- "Vertex lets our developers move quickly while the platform team keeps control of budgets, policies, and reliability.",
- },
- {
- initials: "NB",
- name: "Nina Brooks",
- role: "Applied AI Engineer",
- quote:
- "Model comparisons that used to take days now take minutes. The same payload works everywhere we want to test.",
- },
-]
-
-const globeConfig = {
- width: 800,
- height: 800,
- onRender: () => {},
- devicePixelRatio: 2,
- phi: 0,
- theta: 0.28,
- dark: 1,
- diffuse: 1.1,
- mapSamples: 12000,
- mapBrightness: 4.5,
- baseColor: [0.05, 0.12, 0.32] as [number, number, number],
- markerColor: [0.15, 0.46, 1] as [number, number, number],
- glowColor: [0.08, 0.18, 0.48] as [number, number, number],
- markers: [
- { location: [37.7749, -122.4194] as [number, number], size: 0.08 },
- { location: [40.7128, -74.006] as [number, number], size: 0.06 },
- { location: [51.5072, -0.1276] as [number, number], size: 0.07 },
- { location: [48.8566, 2.3522] as [number, number], size: 0.05 },
- { location: [1.3521, 103.8198] as [number, number], size: 0.08 },
- { location: [35.6762, 139.6503] as [number, number], size: 0.06 },
- { location: [-33.8688, 151.2093] as [number, number], size: 0.05 },
- { location: [-23.5505, -46.6333] as [number, number], size: 0.06 },
- ],
-}
-
-function VertexMark({ className }: { className?: string }) {
- return (
-
- )
-}
-
-function Brand({ compact = false }: { compact?: boolean }) {
- return (
-
-
- {!compact && (
- Vertex
- )}
-
- )
-}
-
-function IntegrationMark({ src, name }: { src: string; name: string }) {
- return (
-
-
-
- )
-}
-
-function SectionHeading({
- eyebrow,
- title,
- description,
- className,
-}: {
- eyebrow: string
- title: string
- description: string
- className?: string
-}) {
- return (
-
-
-
- {eyebrow}
-
-
- {title}
-
-
- {description}
-
-
-
- )
-}
-
-function FeatureCopy({
- title,
- description,
-}: {
- title: string
- description: string
-}) {
- return (
-
-
{title}
-
{description}
-
- )
-}
-
-function ReviewCard({ review }: { review: (typeof reviews)[number] }) {
- return (
-
- “{review.quote}”
-
-
- {review.initials}
-
-
- {review.name}
- {review.role}
-
-
-
- )
-}
-
-export function VertexLandingPage() {
- const [annual, setAnnual] = useState(false)
- const firstReviews = reviews.slice(0, 3)
- const secondReviews = reviews.slice(3)
-
- return (
-
-
-
-
-
-
-
- {[
- ["Products", "#features"],
- ["Models", "#models"],
- ["Features", "#features"],
- ["Pricing", "#pricing"],
- ["Docs", "#developers"],
- ].map(([label, href]) => (
-
- {label}
- {label === "Products" && }
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
-
- The unified AI gateway
-
-
- One API for every
- AI model.
-
-
- Route, optimize, and observe every model request through one OpenAI-compatible endpoint built for production.
-
-
-
-
-
-
-
-
-
-
-
- Every leading model,
- one gateway.
-
-
-
- {customerLogos.map((logo) => (
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
- {"> curl https://api.vertex.ai/v1/chat/completions \\"}
-
-
- {` -d '{ "model": "vertex/auto", "messages": [...] }'`}
-
-
- → evaluating 14 available models
-
-
- → route selected: anthropic/claude-sonnet
-
-
- ✓ response streamed in 342ms
-
-
- One endpoint. Any model. No rewrites.
-
-
-
-
-
-
-
-
-
-
- {integrations.slice(0, 4).map((integration) => (
-
- ))}
-
-
- {integrations.slice(4).map((integration) => (
-
- ))}
-
-
-
-
-
-
-
-
-
- {routingEvents.map((event) => (
-
-
- {event.icon}
-
-
- {event.title}
- {event.meta}
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 8 regions online
-
-
-
-
-
-
-
-
-
-
- {[
- {
- icon: Code2,
- title: "Change one base URL",
- text: "Keep your existing OpenAI-compatible SDK, payloads, and streaming logic.",
- },
- {
- icon: Workflow,
- title: "Set a routing policy",
- text: "Choose a model directly or let Vertex optimize for cost, quality, or latency.",
- },
- {
- icon: Gauge,
- title: "Observe every request",
- text: "Trace performance and spend across models, applications, and environments.",
- },
- ].map((step, index) => (
-
-
- {step.title}
- {step.text}
-
- ))}
-
-
-
-
-
-
-
-
-
-
- Reliability built into every request.
-
-
- Production-grade controls keep your workloads available, observable, and protected as they scale.
-
-
- {[
- ["99.99%", "Gateway uptime"],
- ["8", "Global regions"],
- ["< 20ms", "Routing overhead"],
- ].map(([value, label], index) => (
-
- {value}
- {label}
-
- ))}
-
-
-
-
-
-
-
-
- setAnnual(false)}
- className={cn("rounded-sm px-4 py-1.5 text-xs font-medium transition", !annual ? "bg-background text-foreground shadow-sm" : "text-muted-foreground")}
- >
- Monthly
-
- setAnnual(true)}
- className={cn("rounded-sm px-4 py-1.5 text-xs font-medium transition", annual ? "bg-background text-foreground shadow-sm" : "text-muted-foreground")}
- >
- Annual −20%
-
-
-
-
- {[
- {
- name: "Developer",
- price: "$0",
- cadence: "/ month",
- description: "For prototypes, side projects, and individual builders.",
- action: "Start building",
- popular: false,
- features: ["Pay-as-you-go model usage", "All model providers", "Basic usage analytics", "Community support"],
- },
- {
- name: "Team",
- price: annual ? "$39" : "$49",
- cadence: "/ month",
- description: "For teams running reliable AI products in production.",
- action: "Start 14-day trial",
- popular: true,
- features: ["Everything in Developer", "Routing and fallback policies", "Team budgets and alerts", "90-day request analytics", "Priority support"],
- },
- {
- name: "Enterprise",
- price: "Custom",
- cadence: "",
- description: "For organizations that need security and scale.",
- action: "Talk to sales",
- popular: false,
- features: ["Everything in Team", "SAML SSO and audit logs", "Custom retention and regions", "Dedicated capacity", "Enterprise SLA"],
- },
- ].map((plan) => (
-
-
-
-
{plan.name}
- {plan.popular && (
-
- Most popular
-
- )}
-
-
- {plan.price}
- {plan.cadence && {plan.cadence} }
-
-
{plan.description}
-
- {plan.action}
-
-
- {plan.features.map((feature) => (
-
-
- {feature}
-
- ))}
-
-
-
- ))}
-
-
-
-
-
-
Early access feedback
-
Built for teams shipping AI
-
-
-
- {firstReviews.map((review) => )}
-
-
- {secondReviews.map((review) => )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Build on every model.
- Ship through one API.
-
-
- Your models will change. Your infrastructure doesn’t have to.
-
-
-
-
-
-
-
-
- )
-}
diff --git a/next.config.js b/next.config.js
index 3a2cd1a..b8d56cc 100644
--- a/next.config.js
+++ b/next.config.js
@@ -3,5 +3,11 @@ module.exports = {
outputFileTracingRoot: __dirname,
images: {
unoptimized: true,
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "sb.frontend.co",
+ },
+ ],
},
};
diff --git a/public/logos/cash-app.svg b/public/logos/cash-app.svg
deleted file mode 100644
index 98a4d74..0000000
--- a/public/logos/cash-app.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/integrations/dropbox.svg b/public/logos/integrations/dropbox.svg
deleted file mode 100644
index 4c01c6a..0000000
--- a/public/logos/integrations/dropbox.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/integrations/figma.svg b/public/logos/integrations/figma.svg
deleted file mode 100644
index 78a05a9..0000000
--- a/public/logos/integrations/figma.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/logos/integrations/github.svg b/public/logos/integrations/github.svg
deleted file mode 100644
index 7c4a574..0000000
--- a/public/logos/integrations/github.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/integrations/gmail.svg b/public/logos/integrations/gmail.svg
deleted file mode 100644
index fc19b56..0000000
--- a/public/logos/integrations/gmail.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/logos/integrations/google-drive.svg b/public/logos/integrations/google-drive.svg
deleted file mode 100644
index 361112a..0000000
--- a/public/logos/integrations/google-drive.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/public/logos/integrations/notion.svg b/public/logos/integrations/notion.svg
deleted file mode 100644
index 0de885e..0000000
--- a/public/logos/integrations/notion.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/integrations/slack.svg b/public/logos/integrations/slack.svg
deleted file mode 100644
index 6d14c72..0000000
--- a/public/logos/integrations/slack.svg
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/public/logos/integrations/supabase.svg b/public/logos/integrations/supabase.svg
deleted file mode 100644
index 574f67e..0000000
--- a/public/logos/integrations/supabase.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/logos/linear.svg b/public/logos/linear.svg
deleted file mode 100644
index f98ad4f..0000000
--- a/public/logos/linear.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/loom.svg b/public/logos/loom.svg
deleted file mode 100644
index 95db52e..0000000
--- a/public/logos/loom.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/medium.svg b/public/logos/medium.svg
deleted file mode 100644
index a419dc3..0000000
--- a/public/logos/medium.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/nextjs.svg b/public/logos/nextjs.svg
deleted file mode 100644
index 65745d8..0000000
--- a/public/logos/nextjs.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/public/logos/openai.svg b/public/logos/openai.svg
deleted file mode 100644
index 7c57230..0000000
--- a/public/logos/openai.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/retool.svg b/public/logos/retool.svg
deleted file mode 100644
index b964e8e..0000000
--- a/public/logos/retool.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/stripe.svg b/public/logos/stripe.svg
deleted file mode 100644
index 76cce7a..0000000
--- a/public/logos/stripe.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/tailwindcss.svg b/public/logos/tailwindcss.svg
deleted file mode 100644
index 8c2cc0b..0000000
--- a/public/logos/tailwindcss.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/public/logos/wise.svg b/public/logos/wise.svg
deleted file mode 100644
index 0280b42..0000000
--- a/public/logos/wise.svg
+++ /dev/null
@@ -1 +0,0 @@
-