From 6376bb3d82fa067c6f42929f4c6e46f501bae2b4 Mon Sep 17 00:00:00 2001 From: Rami Bitar Date: Tue, 15 Sep 2026 10:49:07 -0400 Subject: [PATCH] Organize Vertex starter into reusable sections Use remote logos and avatars, refine landing copy, and document the template structure. --- README.md | 70 +- app/layout.tsx | 2 +- app/page.tsx | 28 +- components/layout/site-footer.tsx | 26 + components/layout/site-header.tsx | 43 ++ components/{ => layout}/theme-provider.tsx | 0 components/{ => layout}/theme-toggle.tsx | 0 components/sections/cta-section.tsx | 31 + components/sections/developers-section.tsx | 41 + components/sections/features-section.tsx | 63 ++ components/sections/features/api-terminal.tsx | 34 + components/sections/features/global-globe.tsx | 40 + .../sections/features/integration-orbit.tsx | 79 ++ .../sections/features/routing-activity.tsx | 60 ++ .../{ => sections/features}/usage-chart.tsx | 0 components/sections/hero-section.tsx | 41 + components/sections/logos-section.tsx | 93 +++ components/sections/pricing-section.tsx | 108 +++ components/sections/reliability-section.tsx | 35 + components/sections/testimonials-section.tsx | 71 ++ components/shared/brand.tsx | 27 + components/shared/feature-copy.tsx | 14 + components/shared/frame-crosses.tsx | 10 + components/shared/review-card.tsx | 31 + components/shared/section-heading.tsx | 29 + components/vertex-landing.tsx | 708 ------------------ next.config.js | 6 + public/logos/cash-app.svg | 1 - public/logos/integrations/dropbox.svg | 1 - public/logos/integrations/figma.svg | 14 - public/logos/integrations/github.svg | 1 - public/logos/integrations/gmail.svg | 11 - public/logos/integrations/google-drive.svg | 8 - public/logos/integrations/notion.svg | 1 - public/logos/integrations/slack.svg | 24 - public/logos/integrations/supabase.svg | 15 - public/logos/linear.svg | 1 - public/logos/loom.svg | 1 - public/logos/medium.svg | 1 - public/logos/nextjs.svg | 10 - public/logos/openai.svg | 1 - public/logos/retool.svg | 1 - public/logos/stripe.svg | 1 - public/logos/tailwindcss.svg | 1 - public/logos/wise.svg | 1 - 45 files changed, 959 insertions(+), 825 deletions(-) create mode 100644 components/layout/site-footer.tsx create mode 100644 components/layout/site-header.tsx rename components/{ => layout}/theme-provider.tsx (100%) rename components/{ => layout}/theme-toggle.tsx (100%) create mode 100644 components/sections/cta-section.tsx create mode 100644 components/sections/developers-section.tsx create mode 100644 components/sections/features-section.tsx create mode 100644 components/sections/features/api-terminal.tsx create mode 100644 components/sections/features/global-globe.tsx create mode 100644 components/sections/features/integration-orbit.tsx create mode 100644 components/sections/features/routing-activity.tsx rename components/{ => sections/features}/usage-chart.tsx (100%) create mode 100644 components/sections/hero-section.tsx create mode 100644 components/sections/logos-section.tsx create mode 100644 components/sections/pricing-section.tsx create mode 100644 components/sections/reliability-section.tsx create mode 100644 components/sections/testimonials-section.tsx create mode 100644 components/shared/brand.tsx create mode 100644 components/shared/feature-copy.tsx create mode 100644 components/shared/frame-crosses.tsx create mode 100644 components/shared/review-card.tsx create mode 100644 components/shared/section-heading.tsx delete mode 100644 components/vertex-landing.tsx delete mode 100644 public/logos/cash-app.svg delete mode 100644 public/logos/integrations/dropbox.svg delete mode 100644 public/logos/integrations/figma.svg delete mode 100644 public/logos/integrations/github.svg delete mode 100644 public/logos/integrations/gmail.svg delete mode 100644 public/logos/integrations/google-drive.svg delete mode 100644 public/logos/integrations/notion.svg delete mode 100644 public/logos/integrations/slack.svg delete mode 100644 public/logos/integrations/supabase.svg delete mode 100644 public/logos/linear.svg delete mode 100644 public/logos/loom.svg delete mode 100644 public/logos/medium.svg delete mode 100644 public/logos/nextjs.svg delete mode 100644 public/logos/openai.svg delete mode 100644 public/logos/retool.svg delete mode 100644 public/logos/stripe.svg delete mode 100644 public/logos/tailwindcss.svg delete mode 100644 public/logos/wise.svg 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 ( +
+
+
+ +

+ The unified gateway for production AI applications. +

+
+
+ Models + Docs + Pricing + Status + Privacy +
+
+
+ © 2026 Vertex, Inc. All rights reserved. +
+
+ ) +} 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 ( +
+
+ + + + +
+ + + Get started + +
+
+
+ ) +} 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 ( + + {`${name} + + ) +} + +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) => ( +
+ {`${logo.name} +
+ ))} +
+
+ ) +} 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 ( +
+ +
+
+ + +
+
+
+ {[ + { + 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 ( + <> +