Orbit: AI agent platform
A dark, four-page site for an AI agent platform: a hero that plays a live agent run, tools, an eval chart, traces, security, pricing with a usage calculator, a changelog and sign-in.
// Ballmac UI: Orbit template fonts. https://ui.ballmac.com/templates/template-orbit
import { Instrument_Sans, JetBrains_Mono } from "next/font/google"
export const orbitSans = Instrument_Sans({ variable: "--orbit-sans", subsets: ["latin"], display: "swap", preload: false })
export const orbitMono = JetBrains_Mono({ variable: "--orbit-mono", subsets: ["latin"], display: "swap", preload: false })// Ballmac UI: Orbit template shell. https://ui.ballmac.com/templates/template-orbit
"use client"
import * as React from "react"
import { ArrowUpRight, Menu, X } from "lucide-react"
import { cn } from "@/lib/utils"
import { orbitMono, orbitSans } from "@/components/ballmac/templates/orbit/orbit-fonts"
type OrbitPage = "home" | "pricing" | "changelog" | "login"
/** Where each page lives. Defaults match the routes the template installs. */
type OrbitHrefs = Record<OrbitPage, string>
const defaultHrefs: OrbitHrefs = { home: "/orbit", pricing: "/orbit/pricing", changelog: "/orbit/changelog", login: "/orbit/login" }
/** Orbit's palette: deep ink with a violet light. Every value is a standard Ballmac token, so blocks inside follow it. */
const orbitVars = {
"--background": "oklch(0.135 0.026 275)",
"--foreground": "oklch(0.965 0.01 275)",
"--card": "oklch(0.172 0.03 275)",
"--card-foreground": "oklch(0.965 0.01 275)",
"--popover": "oklch(0.19 0.032 275)",
"--popover-foreground": "oklch(0.965 0.01 275)",
"--primary": "oklch(0.965 0.01 275)",
"--primary-foreground": "oklch(0.18 0.03 275)",
"--secondary": "oklch(0.24 0.035 275)",
"--secondary-foreground": "oklch(0.965 0.01 275)",
"--muted": "oklch(0.22 0.034 275)",
"--muted-foreground": "oklch(0.73 0.03 275)",
"--accent": "oklch(0.26 0.04 275)",
"--accent-foreground": "oklch(0.965 0.01 275)",
"--border": "oklch(1 0 0 / 9%)",
"--input": "oklch(1 0 0 / 13%)",
"--ring": "oklch(0.7 0.19 285)",
"--surface": "oklch(0.155 0.028 275)",
"--destructive": "oklch(0.7 0.19 22)",
"--chart-1": "oklch(0.72 0.19 285)",
"--chart-2": "oklch(0.82 0.14 185)",
"--chart-3": "oklch(0.84 0.14 85)",
"--chart-4": "oklch(0.74 0.19 350)",
"--chart-5": "oklch(0.72 0.14 245)",
"--radius": "0.9rem",
} as React.CSSProperties
function OrbitMark({ className }: { className?: string }) {
return (
<svg viewBox="0 0 32 32" fill="none" aria-hidden="true" className={cn("size-7", className)}>
<circle cx="16" cy="16" r="5" fill="var(--chart-1)" />
<ellipse cx="16" cy="16" rx="13" ry="6" stroke="currentColor" strokeWidth="1.6" transform="rotate(-28 16 16)" opacity="0.9" />
<circle cx="26.2" cy="10.3" r="2.2" fill="var(--chart-2)" />
</svg>
)
}
const nav: { key: OrbitPage; label: string }[] = [
{ key: "pricing", label: "Pricing" },
{ key: "changelog", label: "Changelog" },
]
type OrbitShellProps = React.ComponentProps<"div"> & {
/** The page being shown, so its nav link is marked current. */
page: OrbitPage
/** Override where pages live (used by previews). */
hrefs?: Partial<OrbitHrefs>
/** Hide the header and footer (the sign-in page draws its own frame). */
bare?: boolean
}
/** Orbit's frame: the dark theme, fonts, floating header and footer. Wrap every Orbit page in it. */
function OrbitShell({ page, hrefs: hrefOverrides, bare, className, children, style, ...props }: OrbitShellProps) {
const hrefs = { ...defaultHrefs, ...hrefOverrides }
const [open, setOpen] = React.useState(false)
return (
<div
data-slot="orbit"
className={cn("dark bg-background text-foreground relative isolate min-h-dvh overflow-x-clip [color-scheme:dark]", orbitSans.variable, orbitMono.variable, className)}
style={{ ...orbitVars, fontFamily: "var(--orbit-sans), ui-sans-serif, system-ui, sans-serif", ...style }}
{...props}
>
{!bare && (
<header className="sticky top-0 z-40 px-3 pt-3 sm:px-6">
<div className="bg-background/70 mx-auto flex h-14 max-w-6xl items-center justify-between rounded-2xl border pe-2 ps-4 backdrop-blur-xl">
<a href={hrefs.home} className="focus-visible:ring-ring/50 flex items-center gap-2 rounded-md font-semibold tracking-tight outline-none focus-visible:ring-[3px]">
<OrbitMark />
Orbit
</a>
<nav aria-label="Main" className="hidden items-center gap-1 md:flex">
{[{ key: "home" as const, label: "Product" }, ...nav].map((l) => (
<a
key={l.key}
href={hrefs[l.key]}
aria-current={page === l.key ? "page" : undefined}
className="text-muted-foreground hover:text-foreground aria-[current=page]:text-foreground focus-visible:ring-ring/50 rounded-lg px-3 py-1.5 text-sm outline-none transition-colors focus-visible:ring-[3px]"
>
{l.label}
</a>
))}
<a href="#" className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm outline-none transition-colors focus-visible:ring-[3px]">
Docs <ArrowUpRight className="size-3.5 rtl:-scale-x-100" aria-hidden="true" />
</a>
</nav>
<div className="flex items-center gap-1.5">
<a href={hrefs.login} className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 hidden rounded-lg px-3 py-1.5 text-sm outline-none transition-colors focus-visible:ring-[3px] sm:block">
Sign in
</a>
<a href={hrefs.login} className="bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-9 items-center rounded-xl px-3.5 text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]">
Start free
</a>
<button
type="button"
aria-label={open ? "Close menu" : "Open menu"}
aria-expanded={open}
aria-controls="orbit-mobile-menu"
onClick={() => setOpen((v) => !v)}
className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex size-9 items-center justify-center rounded-lg outline-none focus-visible:ring-[3px] md:hidden"
>
{open ? <X className="size-5" aria-hidden="true" /> : <Menu className="size-5" aria-hidden="true" />}
</button>
</div>
</div>
{open && (
<nav id="orbit-mobile-menu" aria-label="Mobile" className="bg-popover mx-auto mt-2 max-w-6xl rounded-2xl border p-2 md:hidden">
{[{ key: "home" as const, label: "Product" }, ...nav, { key: "login" as const, label: "Sign in" }].map((l) => (
<a key={l.key} href={hrefs[l.key]} className="hover:bg-accent block rounded-xl px-3 py-2.5 text-sm">
{l.label}
</a>
))}
</nav>
)}
</header>
)}
{children}
{!bare && (
<footer className="mx-auto max-w-6xl px-4 pt-8 pb-12 sm:px-6">
<div className="grid gap-10 border-t pt-12 sm:grid-cols-2 lg:grid-cols-[1.4fr_repeat(3,1fr)]">
<div>
<a href={hrefs.home} className="flex items-center gap-2 font-semibold tracking-tight">
<OrbitMark /> Orbit
</a>
<p className="text-muted-foreground mt-3 max-w-xs text-sm text-pretty">The platform for building, testing and running AI agents in production.</p>
<p className="text-muted-foreground mt-5 flex items-center gap-2 text-xs" style={{ fontFamily: "var(--orbit-mono)" }}>
<span className="bg-chart-2 size-1.5 rounded-full" aria-hidden="true" /> All systems operational
</p>
</div>
{[
{ title: "Product", links: ["Agents", "Tools", "Evals", "Traces", "Security"] },
{ title: "Developers", links: ["Documentation", "API reference", "SDKs", "Status", "Changelog"] },
{ title: "Company", links: ["About", "Customers", "Careers", "Contact", "Brand"] },
].map((c) => (
<div key={c.title}>
<h2 className="text-sm font-medium">{c.title}</h2>
<ul className="mt-4 space-y-2.5">
{c.links.map((l) => (
<li key={l}>
<a href={l === "Changelog" ? hrefs.changelog : "#"} className="text-muted-foreground hover:text-foreground text-sm transition-colors">
{l}
</a>
</li>
))}
</ul>
</div>
))}
</div>
<p className="text-muted-foreground mt-12 text-xs">© 2026 Orbit Labs, Inc. SOC 2 Type II · GDPR · HIPAA-ready</p>
</footer>
)}
</div>
)
}
export { OrbitMark, OrbitShell, defaultHrefs as orbitDefaultHrefs, type OrbitHrefs, type OrbitPage, type OrbitShellProps }// Ballmac UI: Orbit agent run. https://ui.ballmac.com/templates/template-orbit
"use client"
import * as React from "react"
import { Check, CircleDashed, Loader2, RotateCcw, ShieldCheck } from "lucide-react"
import { useReducedMotion } from "motion/react"
import { cn } from "@/lib/utils"
type Step = { id: string; kind: "plan" | "tool" | "approval" | "reply"; label: string; detail: string; ms: number; tokens: number }
const task = "Reconcile last week’s refunds and send finance a summary."
const steps: Step[] = [
{ id: "plan", kind: "plan", label: "Plan", detail: "3 steps · pull refunds, match to orders, draft summary", ms: 640, tokens: 412 },
{ id: "refunds", kind: "tool", label: "stripe.refunds.list", detail: "214 refunds · $18,420.50", ms: 820, tokens: 96 },
{ id: "orders", kind: "tool", label: "warehouse.query", detail: "208 matched · 6 need review", ms: 1180, tokens: 268 },
{ id: "approval", kind: "approval", label: "Needs approval", detail: "Send email to finance@acme.co", ms: 1500, tokens: 0 },
{ id: "reply", kind: "reply", label: "Summary sent", detail: "Draft delivered · 6 flagged refunds attached", ms: 520, tokens: 534 },
]
const STEP_DELAY = 1150
function formatMs(ms: number) {
return ms >= 1000 ? `${(ms / 1000).toFixed(1)} s` : `${ms} ms`
}
/** A scripted agent run that plays when it scrolls into view: plan, tool calls, an approval and a reply, with a live trace and cost. */
function OrbitAgentRun({ className }: { className?: string }) {
const reduce = useReducedMotion()
const ref = React.useRef<HTMLDivElement>(null)
const [done, setDone] = React.useState(0)
const [run, setRun] = React.useState(0)
const [started, setStarted] = React.useState(false)
// Start once the card is on screen.
React.useEffect(() => {
const el = ref.current
if (!el) return
const io = new IntersectionObserver(
([e]) => {
if (e.isIntersecting) {
setStarted(true)
io.disconnect()
}
},
{ threshold: 0.35 }
)
io.observe(el)
return () => io.disconnect()
}, [])
// Step through the script. Reduced motion jumps straight to the end.
React.useEffect(() => {
if (!started) return
if (reduce) {
const t = setTimeout(() => setDone(steps.length), 0)
return () => clearTimeout(t)
}
if (done >= steps.length) return
const t = setTimeout(() => setDone((d) => d + 1), done === 0 ? 700 : STEP_DELAY)
return () => clearTimeout(t)
}, [started, done, reduce, run])
const finished = done >= steps.length
const elapsed = steps.slice(0, done).reduce((n, s) => n + s.ms, 0)
const tokens = steps.slice(0, done).reduce((n, s) => n + s.tokens, 0)
const cost = tokens * 0.0000042
function replay() {
setDone(0)
setRun((r) => r + 1)
}
return (
<div ref={ref} data-slot="orbit-agent-run" className={cn("bg-card/80 relative overflow-hidden rounded-3xl border shadow-[0_40px_120px_-40px_oklch(0.6_0.2_285/0.5)] backdrop-blur", className)}>
<div className="flex items-center justify-between gap-3 border-b px-4 py-3 sm:px-5">
<div className="flex items-center gap-2.5">
<span className="flex gap-1.5" aria-hidden="true">
<span className="bg-muted-foreground/30 size-2.5 rounded-full" />
<span className="bg-muted-foreground/30 size-2.5 rounded-full" />
<span className="bg-muted-foreground/30 size-2.5 rounded-full" />
</span>
<span className="text-muted-foreground text-xs" style={{ fontFamily: "var(--orbit-mono)" }}>
run_8f3k2 · refund-reconciler
</span>
</div>
<span
className={cn("flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs", finished ? "text-chart-2" : "text-muted-foreground")}
role="status"
>
{finished ? <Check className="size-3" aria-hidden="true" /> : <Loader2 className="size-3 motion-safe:animate-spin" aria-hidden="true" />}
{finished ? "Completed" : started ? "Running" : "Queued"}
</span>
</div>
<div className="grid lg:grid-cols-[1.15fr_1fr]">
<div className="min-w-0 space-y-4 p-4 sm:p-6">
<div className="bg-secondary/70 ms-auto max-w-[88%] rounded-2xl rounded-ee-md px-4 py-3 text-sm text-pretty">{task}</div>
<ol className="space-y-2.5" aria-label="Agent steps">
{steps.map((s, i) => {
const state = i < done ? "done" : i === done && started && !finished ? "active" : "waiting"
return (
<li
key={s.id}
data-state={state}
className={cn(
"flex items-start gap-3 rounded-2xl border px-3.5 py-3 transition-[opacity,border-color,background-color] duration-500 motion-reduce:transition-none",
state === "waiting" && "opacity-40",
state === "active" && "border-chart-1/50 bg-chart-1/10",
s.kind === "approval" && state === "done" && "border-chart-3/40"
)}
>
<span className="mt-0.5 shrink-0">
{state === "done" ? (
s.kind === "approval" ? <ShieldCheck className="text-chart-3 size-4" aria-hidden="true" /> : <Check className="text-chart-2 size-4" aria-hidden="true" />
) : state === "active" ? (
<Loader2 className="text-chart-1 size-4 motion-safe:animate-spin" aria-hidden="true" />
) : (
<CircleDashed className="text-muted-foreground size-4" aria-hidden="true" />
)}
<span className="sr-only">{state === "done" ? "Done" : state === "active" ? "In progress" : "Waiting"}</span>
</span>
<div className="min-w-0 flex-1">
<p className={cn("truncate text-sm font-medium", s.kind === "tool" && "text-[13px]")} style={s.kind === "tool" ? { fontFamily: "var(--orbit-mono)" } : undefined}>
{s.label}
</p>
<p className="text-muted-foreground mt-0.5 text-xs text-pretty">{state === "waiting" ? "Waiting" : s.detail}</p>
</div>
{state === "done" && s.ms > 0 && (
<span className="text-muted-foreground shrink-0 text-xs tabular-nums" style={{ fontFamily: "var(--orbit-mono)" }}>
{formatMs(s.ms)}
</span>
)}
</li>
)
})}
</ol>
</div>
<div className="bg-background/40 min-w-0 border-t p-4 sm:p-6 lg:border-t-0 lg:border-s">
<p className="text-muted-foreground text-xs font-medium tracking-wide uppercase">Trace</p>
<div className="mt-4 space-y-2.5" role="img" aria-label={`Trace of ${done} of ${steps.length} steps, ${formatMs(elapsed)} elapsed`}>
{steps.map((s, i) => {
const before = steps.slice(0, i).reduce((n, x) => n + x.ms, 0)
const total = steps.reduce((n, x) => n + x.ms, 0)
const visible = i < done
return (
<div key={s.id} className="flex items-center gap-3">
<span className="text-muted-foreground w-[4.75rem] shrink-0 text-[11px]" style={{ fontFamily: "var(--orbit-mono)" }}>
{s.kind === "tool" ? s.label.split(".")[0] : s.kind}
</span>
<div className="bg-muted/60 relative h-2 flex-1 overflow-hidden rounded-full">
<div
className={cn("absolute inset-y-0 rounded-full transition-[opacity,transform] duration-700 motion-reduce:transition-none", s.kind === "approval" ? "bg-chart-3" : s.kind === "tool" ? "bg-chart-1" : "bg-chart-2")}
style={{ left: `${(before / total) * 100}%`, width: `${Math.max((s.ms / total) * 100, 4)}%`, opacity: visible ? 1 : 0, transform: visible ? "scaleX(1)" : "scaleX(0.2)", transformOrigin: "left" }}
/>
</div>
</div>
)
})}
</div>
<dl className="mt-6 grid grid-cols-3 gap-3">
{[
{ label: "Elapsed", value: formatMs(elapsed) },
{ label: "Tokens", value: tokens.toLocaleString("en-US") },
{ label: "Cost", value: `$${cost.toFixed(3)}` },
].map((m) => (
<div key={m.label} className="bg-card/70 rounded-xl border px-3 py-2.5">
<dt className="text-muted-foreground text-[11px]">{m.label}</dt>
<dd className="mt-1 text-base font-medium tabular-nums" style={{ fontFamily: "var(--orbit-mono)" }}>
{m.value}
</dd>
</div>
))}
</dl>
<div className="mt-6 flex items-center justify-between gap-3">
<p className="text-muted-foreground text-xs text-pretty" aria-live="polite">
{finished ? "Every step is stored. Replay it, diff it, turn it into an eval." : "Watching the run live."}
</p>
<button
type="button"
onClick={replay}
className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex h-8 shrink-0 items-center gap-1.5 rounded-lg border px-2.5 text-xs outline-none transition-colors focus-visible:ring-[3px]"
>
<RotateCcw className="size-3.5" aria-hidden="true" /> Replay
</button>
</div>
</div>
</div>
</div>
)
}
export { OrbitAgentRun }// Ballmac UI: Orbit home page. https://ui.ballmac.com/templates/template-orbit
"use client"
import * as React from "react"
import { ArrowRight, Check, FileCheck2, Fingerprint, GitBranch, KeyRound, Lock, Network, ScrollText, Server } from "lucide-react"
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"
import { AuroraBackground } from "@/components/ballmac/aurora-background"
import { BlurFade } from "@/components/ballmac/blur-fade"
import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ballmac/chart"
import { CopyButton } from "@/components/ballmac/copy-button"
import { Marquee } from "@/components/ballmac/marquee"
import { NumberTicker } from "@/components/ballmac/number-ticker"
import { OrbitAgentRun } from "@/components/ballmac/templates/orbit/orbit-agent-run"
import { OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme"
import { cn } from "@/lib/utils"
const mono = { fontFamily: "var(--orbit-mono)" } as const
const display = "font-semibold tracking-[-0.045em] text-balance"
const customers = ["Brightwell", "Lattice", "Fieldly", "Halcyon", "Parallel", "Ridgeline", "Tessera", "Quanta", "Meridian"]
const tools = [
{ name: "stripe", calls: "1.2M", scope: "Refunds, invoices" },
{ name: "slack", calls: "860k", scope: "Post, read threads" },
{ name: "github", calls: "540k", scope: "PRs, issues, checks" },
{ name: "postgres", calls: "2.1M", scope: "Read-only queries" },
{ name: "gmail", calls: "310k", scope: "Drafts, send with approval" },
{ name: "browser", calls: "190k", scope: "Click, type, extract" },
]
const evalData = [
{ build: "#1", baseline: 61, orbit: 62 },
{ build: "#2", baseline: 63, orbit: 68 },
{ build: "#3", baseline: 62, orbit: 74 },
{ build: "#4", baseline: 64, orbit: 79 },
{ build: "#5", baseline: 63, orbit: 83 },
{ build: "#6", baseline: 65, orbit: 86 },
{ build: "#7", baseline: 64, orbit: 91 },
{ build: "#8", baseline: 66, orbit: 94 },
]
const evalConfig = { orbit: { label: "With Orbit evals", color: "var(--chart-1)" }, baseline: { label: "Shipping on vibes", color: "var(--chart-4)" } } satisfies ChartConfig
const security = [
{ icon: Fingerprint, title: "SSO and SCIM", text: "Okta, Entra ID and Google. Roles map to agents, tools and environments." },
{ icon: KeyRound, title: "Scoped credentials", text: "Agents never see secrets. Tools get short-lived, least-privilege tokens." },
{ icon: Lock, title: "Approvals", text: "Require a human for anything that spends, sends or deletes." },
{ icon: ScrollText, title: "Audit log", text: "Every prompt, tool call and decision, streamed to your SIEM." },
{ icon: Server, title: "Your cloud", text: "Run the data plane in your own VPC. Nothing leaves it." },
{ icon: FileCheck2, title: "Certified", text: "SOC 2 Type II, ISO 27001, GDPR, and HIPAA-ready." },
]
type OrbitHomeProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> }
function SectionIntro({ eyebrow, title, children }: { eyebrow: string; title: string; children: React.ReactNode }) {
return (
<div className="max-w-xl">
<p className="text-chart-1 text-sm font-medium" style={mono}>{eyebrow}</p>
<h2 className={cn("mt-3 text-3xl sm:text-4xl", display)}>{title}</h2>
<p className="text-muted-foreground mt-4 text-pretty sm:text-lg">{children}</p>
</div>
)
}
/** The Orbit home page: a live agent run, tools, evals, traces, security and a closing call to action. */
function OrbitHome({ hrefs, ...props }: OrbitHomeProps) {
return (
<OrbitShell page="home" hrefs={hrefs} {...props}>
<main>
{/* Hero */}
<section className="relative -mt-[4.25rem] overflow-hidden px-4 pt-36 pb-16 sm:px-6 sm:pt-44">
<AuroraBackground aria-hidden="true" className="absolute inset-0 -z-10" colors={["var(--chart-1)", "var(--chart-5)", "var(--chart-4)"]} intensity={0.55} radialMask />
<div className="mx-auto max-w-4xl text-center">
<a href="#" className="bg-card/60 hover:bg-card focus-visible:ring-ring/50 mx-auto inline-flex items-center gap-2 rounded-full border py-1 pe-3 ps-1 text-sm backdrop-blur outline-none transition-colors focus-visible:ring-[3px]">
<span className="bg-chart-1 text-background rounded-full px-2 py-0.5 text-xs font-medium">New</span>
Computer use is generally available
<ArrowRight className="size-3.5 rtl:rotate-180" aria-hidden="true" />
</a>
<h1 className={cn("mt-7 text-[2.6rem] leading-[1.02] sm:text-6xl lg:text-7xl", display)}>
Agents that <span className="from-chart-1 via-chart-5 to-chart-2 bg-gradient-to-r bg-clip-text text-transparent">finish the job.</span>
</h1>
<p className="text-muted-foreground mx-auto mt-6 max-w-2xl text-lg text-pretty sm:text-xl">
Build, test and run AI agents with the tools, evals and guardrails a production team needs. Ship the first one this afternoon.
</p>
<div className="mt-9 flex flex-col items-center justify-center gap-3 sm:flex-row">
<a href="#" className="bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-12 items-center gap-2 rounded-2xl px-6 font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]">
Start building <ArrowRight className="size-4 rtl:rotate-180" aria-hidden="true" />
</a>
<div className="bg-card/70 flex h-12 items-center gap-2 rounded-2xl border pe-1.5 ps-4 text-sm backdrop-blur" style={mono}>
<span className="text-muted-foreground" aria-hidden="true">$</span> npm i @orbit/sdk
<CopyButton value="npm i @orbit/sdk" ariaLabel="Copy install command" />
</div>
</div>
</div>
<BlurFade className="mx-auto mt-16 max-w-5xl" delay={0.1}>
<OrbitAgentRun />
</BlurFade>
</section>
{/* Customers */}
<section aria-label="Customers" className="mx-auto max-w-6xl px-4 py-14 sm:px-6">
<p className="text-muted-foreground text-center text-sm">Powering agents at 1,800 teams, from startups to the Fortune 100</p>
<Marquee className="mt-8" speed={30} gap={56} fade pauseOnHover>
{customers.map((c) => (
<span key={c} className="text-muted-foreground/80 text-xl font-semibold tracking-tight">{c}</span>
))}
</Marquee>
</section>
{/* Tools */}
<section className="mx-auto grid max-w-6xl items-center gap-12 px-4 py-20 sm:px-6 lg:grid-cols-2 lg:gap-20">
<SectionIntro eyebrow="01 / Tools" title="Give agents hands, not just words.">
Connect any API, database or browser as a typed tool. Orbit handles auth, retries, rate limits and sandboxing, so the agent only decides what to do.
</SectionIntro>
<ul className="grid gap-3 sm:grid-cols-2">
{tools.map((t) => (
<li key={t.name} className="bg-card/60 hover:border-chart-1/40 group rounded-2xl border p-4 transition-colors">
<div className="flex items-center justify-between">
<span className="text-sm font-medium" style={mono}>{t.name}</span>
<span className="bg-chart-2 size-1.5 rounded-full" aria-hidden="true" />
</div>
<p className="text-muted-foreground mt-2 text-sm">{t.scope}</p>
<p className="text-muted-foreground mt-3 text-xs tabular-nums" style={mono}>{t.calls} calls / mo</p>
</li>
))}
</ul>
</section>
{/* Evals */}
<section className="mx-auto grid max-w-6xl items-center gap-12 px-4 py-20 sm:px-6 lg:grid-cols-[1.1fr_1fr] lg:gap-20">
<figure className="bg-card/60 order-2 min-w-0 rounded-3xl border p-5 sm:p-6 lg:order-1">
<figcaption className="flex items-baseline justify-between gap-3">
<span className="text-sm font-medium">Task success rate</span>
<span className="text-muted-foreground text-xs">Last 8 builds, 400 cases</span>
</figcaption>
<ChartContainer config={evalConfig} label="Task success rate over eight builds" summary="With Orbit evals, task success climbed from 62 to 94 percent over eight builds. Without evals it stayed between 61 and 66 percent." className="mt-5 aspect-[16/10] w-full">
<LineChart data={evalData} margin={{ left: 0, right: 10, top: 8 }} accessibilityLayer>
<CartesianGrid vertical={false} />
<XAxis dataKey="build" tickLine={false} axisLine={false} tickMargin={8} />
<YAxis domain={[50, 100]} tickLine={false} axisLine={false} width={36} tickFormatter={(v: number) => `${v}%`} />
<ChartTooltip cursor={false} content={<ChartTooltipContent indicator="line" formatter={(v) => `${v}%`} />} />
<Line dataKey="baseline" type="monotone" stroke="var(--color-baseline)" strokeWidth={2} strokeDasharray="4 4" dot={false} isAnimationActive={false} />
<Line dataKey="orbit" type="monotone" stroke="var(--color-orbit)" strokeWidth={2.5} dot={{ r: 3 }} isAnimationActive={false} />
<ChartLegend content={<ChartLegendContent />} />
</LineChart>
</ChartContainer>
</figure>
<div className="order-1 lg:order-2">
<SectionIntro eyebrow="02 / Evals" title="Stop shipping on vibes.">
Turn any trace into a test case. Run the suite on every commit, compare models side by side, and block the merge when success drops.
</SectionIntro>
<ul className="mt-7 space-y-3 text-sm">
{["Replay production runs against a new prompt", "LLM and code graders, with human review", "GitHub check that fails the build on regressions"].map((x) => (
<li key={x} className="flex items-start gap-2.5"><Check className="text-chart-2 mt-0.5 size-4 shrink-0" aria-hidden="true" />{x}</li>
))}
</ul>
</div>
</section>
{/* Traces */}
<section className="mx-auto max-w-6xl px-4 py-20 sm:px-6">
<SectionIntro eyebrow="03 / Traces" title="See every decision an agent made.">
Each run is a tree you can read: prompts, tool inputs, outputs, cost and latency. Branch from any step and rerun it.
</SectionIntro>
<div className="bg-card/60 mt-10 overflow-hidden rounded-3xl border" style={mono}>
<div className="text-muted-foreground flex items-center gap-2 border-b px-5 py-3 text-xs"><GitBranch className="size-3.5" aria-hidden="true" /> run_8f3k2 · 5 spans · 4.7 s</div>
<ul className="divide-y text-[13px]">
{[
{ depth: 0, name: "refund-reconciler", ms: "4.7 s", w: 100, tone: "bg-chart-2" },
{ depth: 1, name: "plan", ms: "640 ms", w: 14, tone: "bg-chart-2" },
{ depth: 1, name: "stripe.refunds.list", ms: "820 ms", w: 18, tone: "bg-chart-1" },
{ depth: 1, name: "warehouse.query", ms: "1.2 s", w: 25, tone: "bg-chart-1" },
{ depth: 1, name: "gmail.send_draft", ms: "1.5 s", w: 32, tone: "bg-chart-3" },
].map((r, i) => (
<li key={r.name} className="grid grid-cols-[1fr_5rem] items-center gap-4 px-5 py-3 sm:grid-cols-[minmax(0,16rem)_1fr_5rem]">
<span className="truncate" style={{ paddingInlineStart: `${r.depth * 1.25}rem` }}>{r.name}</span>
<span className="bg-muted/60 relative hidden h-2 overflow-hidden rounded-full sm:block">
<span className={cn("absolute inset-y-0 rounded-full", r.tone)} style={{ left: `${i === 0 ? 0 : [0, 14, 32, 57][i - 1]}%`, width: `${r.w}%` }} />
</span>
<span className="text-muted-foreground text-end tabular-nums">{r.ms}</span>
</li>
))}
</ul>
</div>
</section>
{/* Stats */}
<section aria-label="Orbit in numbers" className="mx-auto max-w-6xl px-4 py-16 sm:px-6">
<dl className="grid gap-px overflow-hidden rounded-3xl border bg-border sm:grid-cols-2 lg:grid-cols-4">
{[
{ v: 4.2, suffix: "B", label: "Tool calls a month", decimals: 1 },
{ v: 99.99, suffix: "%", label: "Uptime, last 12 months", decimals: 2 },
{ v: 1.4, suffix: " s", label: "p95 time to first action", decimals: 1 },
{ v: 38, suffix: "", label: "Regions, including yours", decimals: 0 },
].map((s) => (
<div key={s.label} className="bg-background p-7">
<dd className="text-4xl font-semibold tracking-[-0.04em] tabular-nums">
<NumberTicker value={s.v} format={{ minimumFractionDigits: s.decimals, maximumFractionDigits: s.decimals }} />{s.suffix}
</dd>
<dt className="text-muted-foreground mt-2 text-sm">{s.label}</dt>
</div>
))}
</dl>
</section>
{/* Security */}
<section className="mx-auto max-w-6xl px-4 py-20 sm:px-6">
<SectionIntro eyebrow="04 / Trust" title="Built for the security review.">
The controls your CISO asks for are on by default, not an enterprise upsell.
</SectionIntro>
<ul className="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{security.map((s) => (
<li key={s.title} className="bg-card/60 rounded-2xl border p-6">
<span className="bg-chart-1/15 text-chart-1 flex size-10 items-center justify-center rounded-xl"><s.icon className="size-5" aria-hidden="true" /></span>
<h3 className="mt-5 font-medium">{s.title}</h3>
<p className="text-muted-foreground mt-2 text-sm text-pretty">{s.text}</p>
</li>
))}
</ul>
</section>
{/* Quote */}
<section className="mx-auto max-w-4xl px-4 py-20 text-center sm:px-6">
<blockquote>
<p className={cn("text-2xl leading-snug sm:text-4xl", display)}>
“We moved 40 percent of tier-one support to agents in a quarter, and the eval suite is the reason nobody was scared to ship it.”
</p>
<footer className="text-muted-foreground mt-8 flex items-center justify-center gap-3 text-sm">
<span className="bg-chart-4/25 flex size-10 items-center justify-center rounded-full font-medium text-foreground" aria-hidden="true">PN</span>
<span className="text-start"><span className="text-foreground block font-medium">Priya Nair</span>VP Engineering, Fieldly</span>
</footer>
</blockquote>
</section>
{/* CTA */}
<section className="px-4 pb-20 sm:px-6">
<div className="bg-card/60 relative mx-auto max-w-5xl overflow-hidden rounded-[2rem] border px-6 py-16 text-center sm:py-20">
<AuroraBackground aria-hidden="true" className="absolute inset-0 -z-10 opacity-70" colors={["var(--chart-1)", "var(--chart-5)"]} intensity={0.45} radialMask />
<h2 className={cn("mx-auto max-w-2xl text-3xl sm:text-5xl", display)}>Your first agent is ten minutes away.</h2>
<p className="text-muted-foreground mx-auto mt-4 max-w-lg text-pretty">Free for your first 10,000 runs. No credit card, no sales call.</p>
<div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row">
<a href="#" className="bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-12 items-center gap-2 rounded-2xl px-6 font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]">
Start building <ArrowRight className="size-4 rtl:rotate-180" aria-hidden="true" />
</a>
<a href="#" className="hover:bg-accent focus-visible:ring-ring/50 inline-flex h-12 items-center gap-2 rounded-2xl border px-6 font-medium outline-none transition-colors focus-visible:ring-[3px]">
<Network className="size-4" aria-hidden="true" /> Talk to an engineer
</a>
</div>
</div>
</section>
</main>
</OrbitShell>
)
}
export { OrbitHome, type OrbitHomeProps }// Ballmac UI: Orbit pricing page. https://ui.ballmac.com/templates/template-orbit
"use client"
import * as React from "react"
import { Check } from "lucide-react"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ballmac/accordion"
import { SegmentedControl, SegmentedControlItem } from "@/components/ballmac/segmented-control"
import { Slider } from "@/components/ballmac/slider"
import { OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme"
import { cn } from "@/lib/utils"
const mono = { fontFamily: "var(--orbit-mono)" } as const
const money = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 })
const moneyCents = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", minimumFractionDigits: 2 })
const count = new Intl.NumberFormat("en-US")
type Billing = "monthly" | "yearly"
const plans = [
{ id: "hobby", name: "Hobby", price: 0, blurb: "For side projects and learning.", cta: "Start free", runs: "10,000 runs included", features: ["3 agents", "Community tools", "7-day trace history", "Email support"] },
{ id: "team", name: "Team", price: 49, blurb: "For teams shipping agents to customers.", cta: "Start 14-day trial", runs: "250,000 runs included", features: ["Unlimited agents", "Evals and GitHub checks", "90-day trace history", "Approvals and audit log", "Priority support"], featured: true },
{ id: "scale", name: "Scale", price: null, blurb: "For regulated and high-volume workloads.", cta: "Talk to sales", runs: "Volume pricing", features: ["Your VPC or on-prem data plane", "SSO, SCIM and custom roles", "1-year trace retention", "99.99% uptime SLA", "Dedicated engineer"] },
] as const
const faqs = [
{ q: "What counts as a run?", a: "One end-to-end execution of an agent, from the first prompt to the final reply, however many tool calls or model turns it takes. Retries of the same run are free." },
{ q: "Do I pay for model tokens?", a: "Bring your own keys and pay your model provider directly, or use Orbit’s pooled capacity at cost plus 8 percent. Either way the bill shows tokens per run." },
{ q: "What happens when I pass my included runs?", a: "Nothing breaks. Extra runs are billed at the rate in the calculator, and you can set a hard monthly cap so a loop can never surprise you." },
{ q: "Can I self-host?", a: "The Scale plan runs the data plane in your own cloud or on-prem. The control plane stays hosted, and no prompts, tool data or secrets ever leave your network." },
{ q: "Is there a discount for startups or nonprofits?", a: "Yes. Startups under two years old get 50 percent off Team for a year, and verified nonprofits get 40 percent off for good." },
]
function Calculator({ billing }: { billing: Billing }) {
const [runs, setRuns] = React.useState(400_000)
const seats = 5
const included = 250_000
const extra = Math.max(0, runs - included)
const seatPrice = billing === "yearly" ? 39 : 49
const base = seats * seatPrice
// Overage gets cheaper in steps: $2.00 per thousand to 1M, then $1.40.
const tier1 = Math.min(extra, 750_000)
const tier2 = Math.max(0, extra - 750_000)
const overage = (tier1 / 1000) * 2 + (tier2 / 1000) * 1.4
const total = base + overage
return (
<section aria-labelledby="orbit-calc" className="bg-card/60 mx-auto mt-16 max-w-5xl rounded-3xl border p-6 sm:p-10">
<div className="grid gap-10 lg:grid-cols-[1.2fr_1fr] lg:gap-14">
<div>
<h2 id="orbit-calc" className="text-2xl font-semibold tracking-[-0.03em]">Estimate your bill</h2>
<p className="text-muted-foreground mt-2 text-sm text-pretty">Team plan for {seats} seats. Drag to see how overage scales.</p>
<div className="mt-10">
<div className="flex items-baseline justify-between text-sm">
<label id="orbit-runs-label" className="text-muted-foreground">Runs per month</label>
<output className="text-xl font-medium tabular-nums" style={mono} aria-live="polite">{count.format(runs)}</output>
</div>
<Slider className="mt-5" aria-labelledby="orbit-runs-label" min={50_000} max={2_000_000} step={50_000} value={[runs]} onValueChange={(v) => setRuns(v[0] ?? runs)} formatValue={(v) => `${count.format(v)} runs`} />
<div className="text-muted-foreground mt-2 flex justify-between text-xs tabular-nums" style={mono}><span>50k</span><span>2M</span></div>
</div>
</div>
<div className="bg-background/60 space-y-3 rounded-2xl border p-5 text-sm">
<dl className="space-y-3">
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">{seats} seats × {money.format(seatPrice)}</dt><dd className="tabular-nums" style={mono}>{money.format(base)}</dd></div>
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">{count.format(included)} runs included</dt><dd className="tabular-nums" style={mono}>$0</dd></div>
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">{count.format(extra)} extra runs</dt><dd className="tabular-nums" style={mono}>{moneyCents.format(overage)}</dd></div>
<div className="flex items-baseline justify-between gap-4 border-t pt-4"><dt className="font-medium">Estimated per month</dt><dd className="text-3xl font-semibold tracking-[-0.03em] tabular-nums">{money.format(total)}</dd></div>
</dl>
<p className="text-muted-foreground text-xs">Model tokens are billed by your provider. Set a hard cap in settings.</p>
</div>
</div>
</section>
)
}
type OrbitPricingProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> }
/** Orbit pricing: three plans with a monthly and yearly switch, a usage calculator and common questions. */
function OrbitPricing({ hrefs, ...props }: OrbitPricingProps) {
const [billing, setBilling] = React.useState<Billing>("yearly")
return (
<OrbitShell page="pricing" hrefs={hrefs} {...props}>
<main className="px-4 pt-16 pb-20 sm:px-6 sm:pt-24">
<div className="mx-auto max-w-3xl text-center">
<p className="text-chart-1 text-sm font-medium" style={mono}>Pricing</p>
<h1 className="mt-3 text-4xl font-semibold tracking-[-0.045em] text-balance sm:text-6xl">Pay for runs, not for hope.</h1>
<p className="text-muted-foreground mt-5 text-lg text-pretty">Start free, then grow into seats and volume. Every plan includes traces, tools and the full SDK.</p>
<SegmentedControl aria-label="Billing period" value={billing} onValueChange={(v) => setBilling(v as Billing)} className="mt-9">
<SegmentedControlItem value="monthly">Monthly</SegmentedControlItem>
<SegmentedControlItem value="yearly">Yearly · save 20%</SegmentedControlItem>
</SegmentedControl>
</div>
<div className="mx-auto mt-12 grid max-w-6xl gap-4 lg:grid-cols-3">
{plans.map((p) => {
const price = p.price === null ? null : p.price === 0 ? 0 : billing === "yearly" ? Math.round(p.price * 0.8) : p.price
return (
<article key={p.id} className={cn("bg-card/60 relative flex flex-col rounded-3xl border p-7", "featured" in p && p.featured && "border-chart-1/60 shadow-[0_30px_80px_-40px_oklch(0.6_0.2_285/0.7)]")}>
{"featured" in p && p.featured && <span className="bg-chart-1 text-background absolute -top-3 start-7 rounded-full px-3 py-0.5 text-xs font-medium">Most popular</span>}
<h2 className="text-lg font-semibold">{p.name}</h2>
<p className="text-muted-foreground mt-1 text-sm text-pretty">{p.blurb}</p>
<p className="mt-6 flex items-baseline gap-1.5">
{price === null ? <span className="text-4xl font-semibold tracking-[-0.04em]">Custom</span> : (
<>
<span className="text-5xl font-semibold tracking-[-0.045em] tabular-nums">{money.format(price)}</span>
<span className="text-muted-foreground text-sm">{price === 0 ? "forever" : "per seat / month"}</span>
</>
)}
</p>
<p className="text-muted-foreground mt-2 text-sm" style={mono}>{p.runs}</p>
<a href="#" className={cn("focus-visible:ring-ring/50 mt-6 inline-flex h-11 items-center justify-center rounded-xl text-sm font-medium outline-none transition-opacity focus-visible:ring-[3px]", "featured" in p && p.featured ? "bg-foreground text-background hover:opacity-90" : "hover:bg-accent border")}>{p.cta}</a>
<ul className="mt-7 space-y-3 border-t pt-6 text-sm">
{p.features.map((f) => (
<li key={f} className="flex items-start gap-2.5"><Check className="text-chart-2 mt-0.5 size-4 shrink-0" aria-hidden="true" />{f}</li>
))}
</ul>
</article>
)
})}
</div>
<Calculator billing={billing} />
<section className="mx-auto mt-24 max-w-3xl" aria-labelledby="orbit-faq">
<h2 id="orbit-faq" className="text-3xl font-semibold tracking-[-0.04em]">Questions, answered</h2>
<Accordion type="single" collapsible className="mt-6">
{faqs.map((f) => (
<AccordionItem key={f.q} value={f.q}>
<AccordionTrigger className="text-base">{f.q}</AccordionTrigger>
<AccordionContent className="text-muted-foreground text-pretty">{f.a}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</section>
</main>
</OrbitShell>
)
}
export { OrbitPricing, type OrbitPricingProps }// Ballmac UI: Orbit changelog page. https://ui.ballmac.com/templates/template-orbit
import * as React from "react"
import { Rss } from "lucide-react"
import { ChangelogFeed, type ChangelogEntry } from "@/components/ballmac/changelog-feed"
import { OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme"
const entries: ChangelogEntry[] = [
{
id: "2-4",
version: "2.4",
date: "2026-09-24",
title: "Computer use is generally available",
summary: "Agents can now see a screen, click, type and extract data from any web app, inside a sandboxed browser you control.",
changes: [
{ type: "new", text: "Browser tool with screenshots, DOM snapshots and a full action trace." },
{ type: "new", text: "Session recording: replay what the agent saw, frame by frame." },
{ type: "improved", text: "Approvals can now be routed to Slack channels by risk level." },
{ type: "fixed", text: "Long-running runs no longer lose their cost counter after a deploy." },
],
},
{
id: "2-3",
version: "2.3",
date: "2026-08-27",
title: "Evals as GitHub checks",
summary: "Every pull request can now run your eval suite and block the merge when success rate drops.",
changes: [
{ type: "new", text: "GitHub app with per-case results and a diff against main." },
{ type: "new", text: "Model comparison view: run one dataset against three models." },
{ type: "improved", text: "LLM graders accept a rubric and return a rationale you can edit." },
{ type: "removed", text: "The v1 evaluation endpoints, deprecated in March." },
],
},
{
id: "2-2",
version: "2.2",
date: "2026-07-30",
title: "Bring your own cloud",
summary: "Run the data plane in your VPC. Prompts, tool data and secrets never leave your network.",
changes: [
{ type: "new", text: "Terraform module for AWS, GCP and Azure." },
{ type: "improved", text: "Traces load 3x faster for runs over 500 spans." },
{ type: "fixed", text: "Tool retries respected the wrong backoff when the API returned Retry-After." },
],
},
{
id: "2-1",
version: "2.1",
date: "2026-06-26",
title: "Branch any trace",
changes: [
{ type: "new", text: "Branch a run from any step, edit the input and compare outcomes." },
{ type: "improved", text: "SDK streams partial tool results to your UI." },
{ type: "fixed", text: "Python SDK now cancels in-flight tool calls on Ctrl+C." },
],
},
]
type OrbitChangelogProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> }
/** Orbit changelog: a filterable release feed under a calm header. */
function OrbitChangelog({ hrefs, ...props }: OrbitChangelogProps) {
return (
<OrbitShell page="changelog" hrefs={hrefs} {...props}>
<main className="mx-auto max-w-3xl px-4 pt-16 pb-24 sm:px-6 sm:pt-24">
<p className="text-chart-1 text-sm font-medium" style={{ fontFamily: "var(--orbit-mono)" }}>Changelog</p>
<div className="mt-3 flex flex-wrap items-end justify-between gap-4">
<h1 className="text-4xl font-semibold tracking-[-0.045em] text-balance sm:text-6xl">What we shipped.</h1>
<a href="#" className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex items-center gap-2 rounded-lg text-sm outline-none focus-visible:ring-[3px]">
<Rss className="size-4" aria-hidden="true" /> RSS
</a>
</div>
<p className="text-muted-foreground mt-5 max-w-xl text-lg text-pretty">New releases land every month. Breaking changes are announced 90 days ahead.</p>
<ChangelogFeed className="mt-12" entries={entries} filterable />
</main>
</OrbitShell>
)
}
export { OrbitChangelog, type OrbitChangelogProps }// Ballmac UI: Orbit sign-in page. https://ui.ballmac.com/templates/template-orbit
"use client"
import * as React from "react"
import { ArrowRight, Check } from "lucide-react"
import { OrbitMark, OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme"
const mono = { fontFamily: "var(--orbit-mono)" } as const
type OrbitLoginProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> }
/** Orbit sign-in: a focused form beside a live-looking trace. */
function OrbitLogin({ hrefs, ...props }: OrbitLoginProps) {
const [sent, setSent] = React.useState(false)
const [email, setEmail] = React.useState("")
const home = hrefs?.home ?? "/orbit"
return (
<OrbitShell page="login" hrefs={hrefs} bare {...props}>
<main className="grid min-h-dvh lg:grid-cols-2">
<div className="flex flex-col px-6 py-8 sm:px-12">
<a href={home} className="focus-visible:ring-ring/50 flex w-fit items-center gap-2 rounded-md font-semibold tracking-tight outline-none focus-visible:ring-[3px]"><OrbitMark /> Orbit</a>
<div className="mx-auto flex w-full max-w-sm flex-1 flex-col justify-center py-12">
{sent ? (
<div role="status">
<span className="bg-chart-2/15 text-chart-2 flex size-12 items-center justify-center rounded-2xl"><Check className="size-6" aria-hidden="true" /></span>
<h1 className="mt-6 text-3xl font-semibold tracking-[-0.04em]">Check your inbox</h1>
<p className="text-muted-foreground mt-3 text-pretty">We sent a sign-in link to <span className="text-foreground font-medium">{email}</span>. It works for 15 minutes.</p>
<button type="button" onClick={() => setSent(false)} className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 mt-6 rounded-md text-sm underline underline-offset-4 outline-none focus-visible:ring-[3px]">Use a different email</button>
</div>
) : (
<>
<h1 className="text-3xl font-semibold tracking-[-0.04em]">Welcome back</h1>
<p className="text-muted-foreground mt-2">Sign in to your Orbit workspace.</p>
<button type="button" className="hover:bg-accent focus-visible:ring-ring/50 mt-8 inline-flex h-11 items-center justify-center gap-2.5 rounded-xl border text-sm font-medium outline-none transition-colors focus-visible:ring-[3px]">
<svg viewBox="0 0 24 24" className="size-4" aria-hidden="true" fill="currentColor"><path d="M12 .5a11.5 11.5 0 0 0-3.64 22.41c.58.1.79-.25.79-.56v-2c-3.2.7-3.88-1.54-3.88-1.54-.52-1.33-1.28-1.69-1.28-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.19 1.76 1.19 1.03 1.76 2.7 1.25 3.36.96.1-.75.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.28 1.18-3.09-.12-.29-.51-1.46.11-3.05 0 0 .97-.31 3.17 1.18a11 11 0 0 1 5.77 0c2.2-1.49 3.17-1.18 3.17-1.18.62 1.59.23 2.76.11 3.05.74.81 1.18 1.83 1.18 3.09 0 4.42-2.69 5.39-5.25 5.68.41.36.78 1.06.78 2.14v3.17c0 .31.21.67.8.56A11.5 11.5 0 0 0 12 .5Z" /></svg>
Continue with GitHub
</button>
<div className="text-muted-foreground my-6 flex items-center gap-3 text-xs"><span className="bg-border h-px flex-1" />or with email<span className="bg-border h-px flex-1" /></div>
<form
onSubmit={(e) => {
e.preventDefault()
if (email.trim()) setSent(true)
}}
>
<label htmlFor="orbit-email" className="text-sm font-medium">Work email</label>
<input
id="orbit-email"
type="email"
required
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@company.com"
className="bg-card/60 focus-visible:ring-ring/50 placeholder:text-muted-foreground mt-2 h-11 w-full rounded-xl border px-3.5 text-sm outline-none focus-visible:ring-[3px]"
/>
<button type="submit" className="bg-foreground text-background focus-visible:ring-ring/50 mt-4 inline-flex h-11 w-full items-center justify-center gap-2 rounded-xl text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]">
Email me a sign-in link <ArrowRight className="size-4 rtl:rotate-180" aria-hidden="true" />
</button>
</form>
<p className="text-muted-foreground mt-6 text-xs text-pretty">New to Orbit? Signing in creates your workspace. By continuing you agree to the Terms and Privacy Policy.</p>
</>
)}
</div>
</div>
<div aria-hidden="true" className="bg-surface relative hidden overflow-hidden border-s lg:block">
<div className="from-chart-1/25 to-chart-5/10 absolute inset-0 bg-gradient-to-br via-transparent" />
<div className="relative flex h-full flex-col justify-center gap-4 px-14">
{[
{ t: "09:41:02", n: "refund-reconciler", s: "completed", tone: "text-chart-2" },
{ t: "09:41:00", n: "stripe.refunds.list", s: "214 rows", tone: "text-chart-1" },
{ t: "09:40:58", n: "support-triage", s: "approval needed", tone: "text-chart-3" },
{ t: "09:40:55", n: "warehouse.query", s: "208 matched", tone: "text-chart-1" },
{ t: "09:40:51", n: "onboarding-guide", s: "completed", tone: "text-chart-2" },
].map((r, i) => (
<div key={r.t} className="bg-card/70 flex items-center justify-between rounded-2xl border px-5 py-4 text-sm backdrop-blur" style={{ ...mono, marginInlineStart: `${(i % 3) * 1.5}rem`, opacity: 1 - i * 0.14 }}>
<span className="text-muted-foreground">{r.t}</span>
<span>{r.n}</span>
<span className={r.tone}>{r.s}</span>
</div>
))}
<p className="text-muted-foreground mt-6 max-w-sm text-lg text-pretty">“Orbit is the first agent platform our security team signed off on in a week.”</p>
</div>
</div>
</main>
</OrbitShell>
)
}
export { OrbitLogin, type OrbitLoginProps }// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit
import type { Metadata } from "next"
import { OrbitHome } from "@/components/ballmac/templates/orbit/orbit-home"
export const metadata: Metadata = {
title: "Orbit: Agents that finish the job",
description: "Build, test and run AI agents with tools, evals and guardrails.",
}
export default function Page() {
return <OrbitHome />
}// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit
import type { Metadata } from "next"
import { OrbitPricing } from "@/components/ballmac/templates/orbit/orbit-pricing"
export const metadata: Metadata = {
title: "Orbit pricing",
description: "Pay for runs, not for hope. Start free and grow into seats and volume.",
}
export default function Page() {
return <OrbitPricing />
}// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit
import type { Metadata } from "next"
import { OrbitChangelog } from "@/components/ballmac/templates/orbit/orbit-changelog"
export const metadata: Metadata = {
title: "Orbit changelog",
description: "Everything we shipped, month by month.",
}
export default function Page() {
return <OrbitChangelog />
}// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit
import type { Metadata } from "next"
import { OrbitLogin } from "@/components/ballmac/templates/orbit/orbit-login"
export const metadata: Metadata = {
title: "Sign in to Orbit",
description: "Sign in to your Orbit workspace.",
}
export default function Page() {
return <OrbitLogin />
}Installation
$ pnpm dlx shadcn@latest add @ballmac/template-orbitInstall the dependencies.
$ pnpm add lucide-react motion@^12 recharts@^3Add the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/accordion @ballmac/aurora-background @ballmac/blur-fade @ballmac/changelog-feed @ballmac/chart @ballmac/copy-button @ballmac/marquee @ballmac/number-ticker @ballmac/segmented-control @ballmac/sliderCopy the source into your project.
components/ballmac/templates/orbit/orbit-fonts.ts// Ballmac UI: Orbit template fonts. https://ui.ballmac.com/templates/template-orbit import { Instrument_Sans, JetBrains_Mono } from "next/font/google" export const orbitSans = Instrument_Sans({ variable: "--orbit-sans", subsets: ["latin"], display: "swap", preload: false }) export const orbitMono = JetBrains_Mono({ variable: "--orbit-mono", subsets: ["latin"], display: "swap", preload: false })components/ballmac/templates/orbit/orbit-theme.tsx// Ballmac UI: Orbit template shell. https://ui.ballmac.com/templates/template-orbit "use client" import * as React from "react" import { ArrowUpRight, Menu, X } from "lucide-react" import { cn } from "@/lib/utils" import { orbitMono, orbitSans } from "@/components/ballmac/templates/orbit/orbit-fonts" type OrbitPage = "home" | "pricing" | "changelog" | "login" /** Where each page lives. Defaults match the routes the template installs. */ type OrbitHrefs = Record<OrbitPage, string> const defaultHrefs: OrbitHrefs = { home: "/orbit", pricing: "/orbit/pricing", changelog: "/orbit/changelog", login: "/orbit/login" } /** Orbit's palette: deep ink with a violet light. Every value is a standard Ballmac token, so blocks inside follow it. */ const orbitVars = { "--background": "oklch(0.135 0.026 275)", "--foreground": "oklch(0.965 0.01 275)", "--card": "oklch(0.172 0.03 275)", "--card-foreground": "oklch(0.965 0.01 275)", "--popover": "oklch(0.19 0.032 275)", "--popover-foreground": "oklch(0.965 0.01 275)", "--primary": "oklch(0.965 0.01 275)", "--primary-foreground": "oklch(0.18 0.03 275)", "--secondary": "oklch(0.24 0.035 275)", "--secondary-foreground": "oklch(0.965 0.01 275)", "--muted": "oklch(0.22 0.034 275)", "--muted-foreground": "oklch(0.73 0.03 275)", "--accent": "oklch(0.26 0.04 275)", "--accent-foreground": "oklch(0.965 0.01 275)", "--border": "oklch(1 0 0 / 9%)", "--input": "oklch(1 0 0 / 13%)", "--ring": "oklch(0.7 0.19 285)", "--surface": "oklch(0.155 0.028 275)", "--destructive": "oklch(0.7 0.19 22)", "--chart-1": "oklch(0.72 0.19 285)", "--chart-2": "oklch(0.82 0.14 185)", "--chart-3": "oklch(0.84 0.14 85)", "--chart-4": "oklch(0.74 0.19 350)", "--chart-5": "oklch(0.72 0.14 245)", "--radius": "0.9rem", } as React.CSSProperties function OrbitMark({ className }: { className?: string }) { return ( <svg viewBox="0 0 32 32" fill="none" aria-hidden="true" className={cn("size-7", className)}> <circle cx="16" cy="16" r="5" fill="var(--chart-1)" /> <ellipse cx="16" cy="16" rx="13" ry="6" stroke="currentColor" strokeWidth="1.6" transform="rotate(-28 16 16)" opacity="0.9" /> <circle cx="26.2" cy="10.3" r="2.2" fill="var(--chart-2)" /> </svg> ) } const nav: { key: OrbitPage; label: string }[] = [ { key: "pricing", label: "Pricing" }, { key: "changelog", label: "Changelog" }, ] type OrbitShellProps = React.ComponentProps<"div"> & { /** The page being shown, so its nav link is marked current. */ page: OrbitPage /** Override where pages live (used by previews). */ hrefs?: Partial<OrbitHrefs> /** Hide the header and footer (the sign-in page draws its own frame). */ bare?: boolean } /** Orbit's frame: the dark theme, fonts, floating header and footer. Wrap every Orbit page in it. */ function OrbitShell({ page, hrefs: hrefOverrides, bare, className, children, style, ...props }: OrbitShellProps) { const hrefs = { ...defaultHrefs, ...hrefOverrides } const [open, setOpen] = React.useState(false) return ( <div data-slot="orbit" className={cn("dark bg-background text-foreground relative isolate min-h-dvh overflow-x-clip [color-scheme:dark]", orbitSans.variable, orbitMono.variable, className)} style={{ ...orbitVars, fontFamily: "var(--orbit-sans), ui-sans-serif, system-ui, sans-serif", ...style }} {...props} > {!bare && ( <header className="sticky top-0 z-40 px-3 pt-3 sm:px-6"> <div className="bg-background/70 mx-auto flex h-14 max-w-6xl items-center justify-between rounded-2xl border pe-2 ps-4 backdrop-blur-xl"> <a href={hrefs.home} className="focus-visible:ring-ring/50 flex items-center gap-2 rounded-md font-semibold tracking-tight outline-none focus-visible:ring-[3px]"> <OrbitMark /> Orbit </a> <nav aria-label="Main" className="hidden items-center gap-1 md:flex"> {[{ key: "home" as const, label: "Product" }, ...nav].map((l) => ( <a key={l.key} href={hrefs[l.key]} aria-current={page === l.key ? "page" : undefined} className="text-muted-foreground hover:text-foreground aria-[current=page]:text-foreground focus-visible:ring-ring/50 rounded-lg px-3 py-1.5 text-sm outline-none transition-colors focus-visible:ring-[3px]" > {l.label} </a> ))} <a href="#" className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm outline-none transition-colors focus-visible:ring-[3px]"> Docs <ArrowUpRight className="size-3.5 rtl:-scale-x-100" aria-hidden="true" /> </a> </nav> <div className="flex items-center gap-1.5"> <a href={hrefs.login} className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 hidden rounded-lg px-3 py-1.5 text-sm outline-none transition-colors focus-visible:ring-[3px] sm:block"> Sign in </a> <a href={hrefs.login} className="bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-9 items-center rounded-xl px-3.5 text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]"> Start free </a> <button type="button" aria-label={open ? "Close menu" : "Open menu"} aria-expanded={open} aria-controls="orbit-mobile-menu" onClick={() => setOpen((v) => !v)} className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex size-9 items-center justify-center rounded-lg outline-none focus-visible:ring-[3px] md:hidden" > {open ? <X className="size-5" aria-hidden="true" /> : <Menu className="size-5" aria-hidden="true" />} </button> </div> </div> {open && ( <nav id="orbit-mobile-menu" aria-label="Mobile" className="bg-popover mx-auto mt-2 max-w-6xl rounded-2xl border p-2 md:hidden"> {[{ key: "home" as const, label: "Product" }, ...nav, { key: "login" as const, label: "Sign in" }].map((l) => ( <a key={l.key} href={hrefs[l.key]} className="hover:bg-accent block rounded-xl px-3 py-2.5 text-sm"> {l.label} </a> ))} </nav> )} </header> )} {children} {!bare && ( <footer className="mx-auto max-w-6xl px-4 pt-8 pb-12 sm:px-6"> <div className="grid gap-10 border-t pt-12 sm:grid-cols-2 lg:grid-cols-[1.4fr_repeat(3,1fr)]"> <div> <a href={hrefs.home} className="flex items-center gap-2 font-semibold tracking-tight"> <OrbitMark /> Orbit </a> <p className="text-muted-foreground mt-3 max-w-xs text-sm text-pretty">The platform for building, testing and running AI agents in production.</p> <p className="text-muted-foreground mt-5 flex items-center gap-2 text-xs" style={{ fontFamily: "var(--orbit-mono)" }}> <span className="bg-chart-2 size-1.5 rounded-full" aria-hidden="true" /> All systems operational </p> </div> {[ { title: "Product", links: ["Agents", "Tools", "Evals", "Traces", "Security"] }, { title: "Developers", links: ["Documentation", "API reference", "SDKs", "Status", "Changelog"] }, { title: "Company", links: ["About", "Customers", "Careers", "Contact", "Brand"] }, ].map((c) => ( <div key={c.title}> <h2 className="text-sm font-medium">{c.title}</h2> <ul className="mt-4 space-y-2.5"> {c.links.map((l) => ( <li key={l}> <a href={l === "Changelog" ? hrefs.changelog : "#"} className="text-muted-foreground hover:text-foreground text-sm transition-colors"> {l} </a> </li> ))} </ul> </div> ))} </div> <p className="text-muted-foreground mt-12 text-xs">© 2026 Orbit Labs, Inc. SOC 2 Type II · GDPR · HIPAA-ready</p> </footer> )} </div> ) } export { OrbitMark, OrbitShell, defaultHrefs as orbitDefaultHrefs, type OrbitHrefs, type OrbitPage, type OrbitShellProps }components/ballmac/templates/orbit/orbit-agent-run.tsx// Ballmac UI: Orbit agent run. https://ui.ballmac.com/templates/template-orbit "use client" import * as React from "react" import { Check, CircleDashed, Loader2, RotateCcw, ShieldCheck } from "lucide-react" import { useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" type Step = { id: string; kind: "plan" | "tool" | "approval" | "reply"; label: string; detail: string; ms: number; tokens: number } const task = "Reconcile last week’s refunds and send finance a summary." const steps: Step[] = [ { id: "plan", kind: "plan", label: "Plan", detail: "3 steps · pull refunds, match to orders, draft summary", ms: 640, tokens: 412 }, { id: "refunds", kind: "tool", label: "stripe.refunds.list", detail: "214 refunds · $18,420.50", ms: 820, tokens: 96 }, { id: "orders", kind: "tool", label: "warehouse.query", detail: "208 matched · 6 need review", ms: 1180, tokens: 268 }, { id: "approval", kind: "approval", label: "Needs approval", detail: "Send email to finance@acme.co", ms: 1500, tokens: 0 }, { id: "reply", kind: "reply", label: "Summary sent", detail: "Draft delivered · 6 flagged refunds attached", ms: 520, tokens: 534 }, ] const STEP_DELAY = 1150 function formatMs(ms: number) { return ms >= 1000 ? `${(ms / 1000).toFixed(1)} s` : `${ms} ms` } /** A scripted agent run that plays when it scrolls into view: plan, tool calls, an approval and a reply, with a live trace and cost. */ function OrbitAgentRun({ className }: { className?: string }) { const reduce = useReducedMotion() const ref = React.useRef<HTMLDivElement>(null) const [done, setDone] = React.useState(0) const [run, setRun] = React.useState(0) const [started, setStarted] = React.useState(false) // Start once the card is on screen. React.useEffect(() => { const el = ref.current if (!el) return const io = new IntersectionObserver( ([e]) => { if (e.isIntersecting) { setStarted(true) io.disconnect() } }, { threshold: 0.35 } ) io.observe(el) return () => io.disconnect() }, []) // Step through the script. Reduced motion jumps straight to the end. React.useEffect(() => { if (!started) return if (reduce) { const t = setTimeout(() => setDone(steps.length), 0) return () => clearTimeout(t) } if (done >= steps.length) return const t = setTimeout(() => setDone((d) => d + 1), done === 0 ? 700 : STEP_DELAY) return () => clearTimeout(t) }, [started, done, reduce, run]) const finished = done >= steps.length const elapsed = steps.slice(0, done).reduce((n, s) => n + s.ms, 0) const tokens = steps.slice(0, done).reduce((n, s) => n + s.tokens, 0) const cost = tokens * 0.0000042 function replay() { setDone(0) setRun((r) => r + 1) } return ( <div ref={ref} data-slot="orbit-agent-run" className={cn("bg-card/80 relative overflow-hidden rounded-3xl border shadow-[0_40px_120px_-40px_oklch(0.6_0.2_285/0.5)] backdrop-blur", className)}> <div className="flex items-center justify-between gap-3 border-b px-4 py-3 sm:px-5"> <div className="flex items-center gap-2.5"> <span className="flex gap-1.5" aria-hidden="true"> <span className="bg-muted-foreground/30 size-2.5 rounded-full" /> <span className="bg-muted-foreground/30 size-2.5 rounded-full" /> <span className="bg-muted-foreground/30 size-2.5 rounded-full" /> </span> <span className="text-muted-foreground text-xs" style={{ fontFamily: "var(--orbit-mono)" }}> run_8f3k2 · refund-reconciler </span> </div> <span className={cn("flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs", finished ? "text-chart-2" : "text-muted-foreground")} role="status" > {finished ? <Check className="size-3" aria-hidden="true" /> : <Loader2 className="size-3 motion-safe:animate-spin" aria-hidden="true" />} {finished ? "Completed" : started ? "Running" : "Queued"} </span> </div> <div className="grid lg:grid-cols-[1.15fr_1fr]"> <div className="min-w-0 space-y-4 p-4 sm:p-6"> <div className="bg-secondary/70 ms-auto max-w-[88%] rounded-2xl rounded-ee-md px-4 py-3 text-sm text-pretty">{task}</div> <ol className="space-y-2.5" aria-label="Agent steps"> {steps.map((s, i) => { const state = i < done ? "done" : i === done && started && !finished ? "active" : "waiting" return ( <li key={s.id} data-state={state} className={cn( "flex items-start gap-3 rounded-2xl border px-3.5 py-3 transition-[opacity,border-color,background-color] duration-500 motion-reduce:transition-none", state === "waiting" && "opacity-40", state === "active" && "border-chart-1/50 bg-chart-1/10", s.kind === "approval" && state === "done" && "border-chart-3/40" )} > <span className="mt-0.5 shrink-0"> {state === "done" ? ( s.kind === "approval" ? <ShieldCheck className="text-chart-3 size-4" aria-hidden="true" /> : <Check className="text-chart-2 size-4" aria-hidden="true" /> ) : state === "active" ? ( <Loader2 className="text-chart-1 size-4 motion-safe:animate-spin" aria-hidden="true" /> ) : ( <CircleDashed className="text-muted-foreground size-4" aria-hidden="true" /> )} <span className="sr-only">{state === "done" ? "Done" : state === "active" ? "In progress" : "Waiting"}</span> </span> <div className="min-w-0 flex-1"> <p className={cn("truncate text-sm font-medium", s.kind === "tool" && "text-[13px]")} style={s.kind === "tool" ? { fontFamily: "var(--orbit-mono)" } : undefined}> {s.label} </p> <p className="text-muted-foreground mt-0.5 text-xs text-pretty">{state === "waiting" ? "Waiting" : s.detail}</p> </div> {state === "done" && s.ms > 0 && ( <span className="text-muted-foreground shrink-0 text-xs tabular-nums" style={{ fontFamily: "var(--orbit-mono)" }}> {formatMs(s.ms)} </span> )} </li> ) })} </ol> </div> <div className="bg-background/40 min-w-0 border-t p-4 sm:p-6 lg:border-t-0 lg:border-s"> <p className="text-muted-foreground text-xs font-medium tracking-wide uppercase">Trace</p> <div className="mt-4 space-y-2.5" role="img" aria-label={`Trace of ${done} of ${steps.length} steps, ${formatMs(elapsed)} elapsed`}> {steps.map((s, i) => { const before = steps.slice(0, i).reduce((n, x) => n + x.ms, 0) const total = steps.reduce((n, x) => n + x.ms, 0) const visible = i < done return ( <div key={s.id} className="flex items-center gap-3"> <span className="text-muted-foreground w-[4.75rem] shrink-0 text-[11px]" style={{ fontFamily: "var(--orbit-mono)" }}> {s.kind === "tool" ? s.label.split(".")[0] : s.kind} </span> <div className="bg-muted/60 relative h-2 flex-1 overflow-hidden rounded-full"> <div className={cn("absolute inset-y-0 rounded-full transition-[opacity,transform] duration-700 motion-reduce:transition-none", s.kind === "approval" ? "bg-chart-3" : s.kind === "tool" ? "bg-chart-1" : "bg-chart-2")} style={{ left: `${(before / total) * 100}%`, width: `${Math.max((s.ms / total) * 100, 4)}%`, opacity: visible ? 1 : 0, transform: visible ? "scaleX(1)" : "scaleX(0.2)", transformOrigin: "left" }} /> </div> </div> ) })} </div> <dl className="mt-6 grid grid-cols-3 gap-3"> {[ { label: "Elapsed", value: formatMs(elapsed) }, { label: "Tokens", value: tokens.toLocaleString("en-US") }, { label: "Cost", value: `$${cost.toFixed(3)}` }, ].map((m) => ( <div key={m.label} className="bg-card/70 rounded-xl border px-3 py-2.5"> <dt className="text-muted-foreground text-[11px]">{m.label}</dt> <dd className="mt-1 text-base font-medium tabular-nums" style={{ fontFamily: "var(--orbit-mono)" }}> {m.value} </dd> </div> ))} </dl> <div className="mt-6 flex items-center justify-between gap-3"> <p className="text-muted-foreground text-xs text-pretty" aria-live="polite"> {finished ? "Every step is stored. Replay it, diff it, turn it into an eval." : "Watching the run live."} </p> <button type="button" onClick={replay} className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex h-8 shrink-0 items-center gap-1.5 rounded-lg border px-2.5 text-xs outline-none transition-colors focus-visible:ring-[3px]" > <RotateCcw className="size-3.5" aria-hidden="true" /> Replay </button> </div> </div> </div> </div> ) } export { OrbitAgentRun }components/ballmac/templates/orbit/orbit-home.tsx// Ballmac UI: Orbit home page. https://ui.ballmac.com/templates/template-orbit "use client" import * as React from "react" import { ArrowRight, Check, FileCheck2, Fingerprint, GitBranch, KeyRound, Lock, Network, ScrollText, Server } from "lucide-react" import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts" import { AuroraBackground } from "@/components/ballmac/aurora-background" import { BlurFade } from "@/components/ballmac/blur-fade" import { ChartContainer, ChartLegend, ChartLegendContent, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ballmac/chart" import { CopyButton } from "@/components/ballmac/copy-button" import { Marquee } from "@/components/ballmac/marquee" import { NumberTicker } from "@/components/ballmac/number-ticker" import { OrbitAgentRun } from "@/components/ballmac/templates/orbit/orbit-agent-run" import { OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme" import { cn } from "@/lib/utils" const mono = { fontFamily: "var(--orbit-mono)" } as const const display = "font-semibold tracking-[-0.045em] text-balance" const customers = ["Brightwell", "Lattice", "Fieldly", "Halcyon", "Parallel", "Ridgeline", "Tessera", "Quanta", "Meridian"] const tools = [ { name: "stripe", calls: "1.2M", scope: "Refunds, invoices" }, { name: "slack", calls: "860k", scope: "Post, read threads" }, { name: "github", calls: "540k", scope: "PRs, issues, checks" }, { name: "postgres", calls: "2.1M", scope: "Read-only queries" }, { name: "gmail", calls: "310k", scope: "Drafts, send with approval" }, { name: "browser", calls: "190k", scope: "Click, type, extract" }, ] const evalData = [ { build: "#1", baseline: 61, orbit: 62 }, { build: "#2", baseline: 63, orbit: 68 }, { build: "#3", baseline: 62, orbit: 74 }, { build: "#4", baseline: 64, orbit: 79 }, { build: "#5", baseline: 63, orbit: 83 }, { build: "#6", baseline: 65, orbit: 86 }, { build: "#7", baseline: 64, orbit: 91 }, { build: "#8", baseline: 66, orbit: 94 }, ] const evalConfig = { orbit: { label: "With Orbit evals", color: "var(--chart-1)" }, baseline: { label: "Shipping on vibes", color: "var(--chart-4)" } } satisfies ChartConfig const security = [ { icon: Fingerprint, title: "SSO and SCIM", text: "Okta, Entra ID and Google. Roles map to agents, tools and environments." }, { icon: KeyRound, title: "Scoped credentials", text: "Agents never see secrets. Tools get short-lived, least-privilege tokens." }, { icon: Lock, title: "Approvals", text: "Require a human for anything that spends, sends or deletes." }, { icon: ScrollText, title: "Audit log", text: "Every prompt, tool call and decision, streamed to your SIEM." }, { icon: Server, title: "Your cloud", text: "Run the data plane in your own VPC. Nothing leaves it." }, { icon: FileCheck2, title: "Certified", text: "SOC 2 Type II, ISO 27001, GDPR, and HIPAA-ready." }, ] type OrbitHomeProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> } function SectionIntro({ eyebrow, title, children }: { eyebrow: string; title: string; children: React.ReactNode }) { return ( <div className="max-w-xl"> <p className="text-chart-1 text-sm font-medium" style={mono}>{eyebrow}</p> <h2 className={cn("mt-3 text-3xl sm:text-4xl", display)}>{title}</h2> <p className="text-muted-foreground mt-4 text-pretty sm:text-lg">{children}</p> </div> ) } /** The Orbit home page: a live agent run, tools, evals, traces, security and a closing call to action. */ function OrbitHome({ hrefs, ...props }: OrbitHomeProps) { return ( <OrbitShell page="home" hrefs={hrefs} {...props}> <main> {/* Hero */} <section className="relative -mt-[4.25rem] overflow-hidden px-4 pt-36 pb-16 sm:px-6 sm:pt-44"> <AuroraBackground aria-hidden="true" className="absolute inset-0 -z-10" colors={["var(--chart-1)", "var(--chart-5)", "var(--chart-4)"]} intensity={0.55} radialMask /> <div className="mx-auto max-w-4xl text-center"> <a href="#" className="bg-card/60 hover:bg-card focus-visible:ring-ring/50 mx-auto inline-flex items-center gap-2 rounded-full border py-1 pe-3 ps-1 text-sm backdrop-blur outline-none transition-colors focus-visible:ring-[3px]"> <span className="bg-chart-1 text-background rounded-full px-2 py-0.5 text-xs font-medium">New</span> Computer use is generally available <ArrowRight className="size-3.5 rtl:rotate-180" aria-hidden="true" /> </a> <h1 className={cn("mt-7 text-[2.6rem] leading-[1.02] sm:text-6xl lg:text-7xl", display)}> Agents that <span className="from-chart-1 via-chart-5 to-chart-2 bg-gradient-to-r bg-clip-text text-transparent">finish the job.</span> </h1> <p className="text-muted-foreground mx-auto mt-6 max-w-2xl text-lg text-pretty sm:text-xl"> Build, test and run AI agents with the tools, evals and guardrails a production team needs. Ship the first one this afternoon. </p> <div className="mt-9 flex flex-col items-center justify-center gap-3 sm:flex-row"> <a href="#" className="bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-12 items-center gap-2 rounded-2xl px-6 font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]"> Start building <ArrowRight className="size-4 rtl:rotate-180" aria-hidden="true" /> </a> <div className="bg-card/70 flex h-12 items-center gap-2 rounded-2xl border pe-1.5 ps-4 text-sm backdrop-blur" style={mono}> <span className="text-muted-foreground" aria-hidden="true">$</span> npm i @orbit/sdk <CopyButton value="npm i @orbit/sdk" ariaLabel="Copy install command" /> </div> </div> </div> <BlurFade className="mx-auto mt-16 max-w-5xl" delay={0.1}> <OrbitAgentRun /> </BlurFade> </section> {/* Customers */} <section aria-label="Customers" className="mx-auto max-w-6xl px-4 py-14 sm:px-6"> <p className="text-muted-foreground text-center text-sm">Powering agents at 1,800 teams, from startups to the Fortune 100</p> <Marquee className="mt-8" speed={30} gap={56} fade pauseOnHover> {customers.map((c) => ( <span key={c} className="text-muted-foreground/80 text-xl font-semibold tracking-tight">{c}</span> ))} </Marquee> </section> {/* Tools */} <section className="mx-auto grid max-w-6xl items-center gap-12 px-4 py-20 sm:px-6 lg:grid-cols-2 lg:gap-20"> <SectionIntro eyebrow="01 / Tools" title="Give agents hands, not just words."> Connect any API, database or browser as a typed tool. Orbit handles auth, retries, rate limits and sandboxing, so the agent only decides what to do. </SectionIntro> <ul className="grid gap-3 sm:grid-cols-2"> {tools.map((t) => ( <li key={t.name} className="bg-card/60 hover:border-chart-1/40 group rounded-2xl border p-4 transition-colors"> <div className="flex items-center justify-between"> <span className="text-sm font-medium" style={mono}>{t.name}</span> <span className="bg-chart-2 size-1.5 rounded-full" aria-hidden="true" /> </div> <p className="text-muted-foreground mt-2 text-sm">{t.scope}</p> <p className="text-muted-foreground mt-3 text-xs tabular-nums" style={mono}>{t.calls} calls / mo</p> </li> ))} </ul> </section> {/* Evals */} <section className="mx-auto grid max-w-6xl items-center gap-12 px-4 py-20 sm:px-6 lg:grid-cols-[1.1fr_1fr] lg:gap-20"> <figure className="bg-card/60 order-2 min-w-0 rounded-3xl border p-5 sm:p-6 lg:order-1"> <figcaption className="flex items-baseline justify-between gap-3"> <span className="text-sm font-medium">Task success rate</span> <span className="text-muted-foreground text-xs">Last 8 builds, 400 cases</span> </figcaption> <ChartContainer config={evalConfig} label="Task success rate over eight builds" summary="With Orbit evals, task success climbed from 62 to 94 percent over eight builds. Without evals it stayed between 61 and 66 percent." className="mt-5 aspect-[16/10] w-full"> <LineChart data={evalData} margin={{ left: 0, right: 10, top: 8 }} accessibilityLayer> <CartesianGrid vertical={false} /> <XAxis dataKey="build" tickLine={false} axisLine={false} tickMargin={8} /> <YAxis domain={[50, 100]} tickLine={false} axisLine={false} width={36} tickFormatter={(v: number) => `${v}%`} /> <ChartTooltip cursor={false} content={<ChartTooltipContent indicator="line" formatter={(v) => `${v}%`} />} /> <Line dataKey="baseline" type="monotone" stroke="var(--color-baseline)" strokeWidth={2} strokeDasharray="4 4" dot={false} isAnimationActive={false} /> <Line dataKey="orbit" type="monotone" stroke="var(--color-orbit)" strokeWidth={2.5} dot={{ r: 3 }} isAnimationActive={false} /> <ChartLegend content={<ChartLegendContent />} /> </LineChart> </ChartContainer> </figure> <div className="order-1 lg:order-2"> <SectionIntro eyebrow="02 / Evals" title="Stop shipping on vibes."> Turn any trace into a test case. Run the suite on every commit, compare models side by side, and block the merge when success drops. </SectionIntro> <ul className="mt-7 space-y-3 text-sm"> {["Replay production runs against a new prompt", "LLM and code graders, with human review", "GitHub check that fails the build on regressions"].map((x) => ( <li key={x} className="flex items-start gap-2.5"><Check className="text-chart-2 mt-0.5 size-4 shrink-0" aria-hidden="true" />{x}</li> ))} </ul> </div> </section> {/* Traces */} <section className="mx-auto max-w-6xl px-4 py-20 sm:px-6"> <SectionIntro eyebrow="03 / Traces" title="See every decision an agent made."> Each run is a tree you can read: prompts, tool inputs, outputs, cost and latency. Branch from any step and rerun it. </SectionIntro> <div className="bg-card/60 mt-10 overflow-hidden rounded-3xl border" style={mono}> <div className="text-muted-foreground flex items-center gap-2 border-b px-5 py-3 text-xs"><GitBranch className="size-3.5" aria-hidden="true" /> run_8f3k2 · 5 spans · 4.7 s</div> <ul className="divide-y text-[13px]"> {[ { depth: 0, name: "refund-reconciler", ms: "4.7 s", w: 100, tone: "bg-chart-2" }, { depth: 1, name: "plan", ms: "640 ms", w: 14, tone: "bg-chart-2" }, { depth: 1, name: "stripe.refunds.list", ms: "820 ms", w: 18, tone: "bg-chart-1" }, { depth: 1, name: "warehouse.query", ms: "1.2 s", w: 25, tone: "bg-chart-1" }, { depth: 1, name: "gmail.send_draft", ms: "1.5 s", w: 32, tone: "bg-chart-3" }, ].map((r, i) => ( <li key={r.name} className="grid grid-cols-[1fr_5rem] items-center gap-4 px-5 py-3 sm:grid-cols-[minmax(0,16rem)_1fr_5rem]"> <span className="truncate" style={{ paddingInlineStart: `${r.depth * 1.25}rem` }}>{r.name}</span> <span className="bg-muted/60 relative hidden h-2 overflow-hidden rounded-full sm:block"> <span className={cn("absolute inset-y-0 rounded-full", r.tone)} style={{ left: `${i === 0 ? 0 : [0, 14, 32, 57][i - 1]}%`, width: `${r.w}%` }} /> </span> <span className="text-muted-foreground text-end tabular-nums">{r.ms}</span> </li> ))} </ul> </div> </section> {/* Stats */} <section aria-label="Orbit in numbers" className="mx-auto max-w-6xl px-4 py-16 sm:px-6"> <dl className="grid gap-px overflow-hidden rounded-3xl border bg-border sm:grid-cols-2 lg:grid-cols-4"> {[ { v: 4.2, suffix: "B", label: "Tool calls a month", decimals: 1 }, { v: 99.99, suffix: "%", label: "Uptime, last 12 months", decimals: 2 }, { v: 1.4, suffix: " s", label: "p95 time to first action", decimals: 1 }, { v: 38, suffix: "", label: "Regions, including yours", decimals: 0 }, ].map((s) => ( <div key={s.label} className="bg-background p-7"> <dd className="text-4xl font-semibold tracking-[-0.04em] tabular-nums"> <NumberTicker value={s.v} format={{ minimumFractionDigits: s.decimals, maximumFractionDigits: s.decimals }} />{s.suffix} </dd> <dt className="text-muted-foreground mt-2 text-sm">{s.label}</dt> </div> ))} </dl> </section> {/* Security */} <section className="mx-auto max-w-6xl px-4 py-20 sm:px-6"> <SectionIntro eyebrow="04 / Trust" title="Built for the security review."> The controls your CISO asks for are on by default, not an enterprise upsell. </SectionIntro> <ul className="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> {security.map((s) => ( <li key={s.title} className="bg-card/60 rounded-2xl border p-6"> <span className="bg-chart-1/15 text-chart-1 flex size-10 items-center justify-center rounded-xl"><s.icon className="size-5" aria-hidden="true" /></span> <h3 className="mt-5 font-medium">{s.title}</h3> <p className="text-muted-foreground mt-2 text-sm text-pretty">{s.text}</p> </li> ))} </ul> </section> {/* Quote */} <section className="mx-auto max-w-4xl px-4 py-20 text-center sm:px-6"> <blockquote> <p className={cn("text-2xl leading-snug sm:text-4xl", display)}> “We moved 40 percent of tier-one support to agents in a quarter, and the eval suite is the reason nobody was scared to ship it.” </p> <footer className="text-muted-foreground mt-8 flex items-center justify-center gap-3 text-sm"> <span className="bg-chart-4/25 flex size-10 items-center justify-center rounded-full font-medium text-foreground" aria-hidden="true">PN</span> <span className="text-start"><span className="text-foreground block font-medium">Priya Nair</span>VP Engineering, Fieldly</span> </footer> </blockquote> </section> {/* CTA */} <section className="px-4 pb-20 sm:px-6"> <div className="bg-card/60 relative mx-auto max-w-5xl overflow-hidden rounded-[2rem] border px-6 py-16 text-center sm:py-20"> <AuroraBackground aria-hidden="true" className="absolute inset-0 -z-10 opacity-70" colors={["var(--chart-1)", "var(--chart-5)"]} intensity={0.45} radialMask /> <h2 className={cn("mx-auto max-w-2xl text-3xl sm:text-5xl", display)}>Your first agent is ten minutes away.</h2> <p className="text-muted-foreground mx-auto mt-4 max-w-lg text-pretty">Free for your first 10,000 runs. No credit card, no sales call.</p> <div className="mt-8 flex flex-col items-center justify-center gap-3 sm:flex-row"> <a href="#" className="bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-12 items-center gap-2 rounded-2xl px-6 font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]"> Start building <ArrowRight className="size-4 rtl:rotate-180" aria-hidden="true" /> </a> <a href="#" className="hover:bg-accent focus-visible:ring-ring/50 inline-flex h-12 items-center gap-2 rounded-2xl border px-6 font-medium outline-none transition-colors focus-visible:ring-[3px]"> <Network className="size-4" aria-hidden="true" /> Talk to an engineer </a> </div> </div> </section> </main> </OrbitShell> ) } export { OrbitHome, type OrbitHomeProps }components/ballmac/templates/orbit/orbit-pricing.tsx// Ballmac UI: Orbit pricing page. https://ui.ballmac.com/templates/template-orbit "use client" import * as React from "react" import { Check } from "lucide-react" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ballmac/accordion" import { SegmentedControl, SegmentedControlItem } from "@/components/ballmac/segmented-control" import { Slider } from "@/components/ballmac/slider" import { OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme" import { cn } from "@/lib/utils" const mono = { fontFamily: "var(--orbit-mono)" } as const const money = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }) const moneyCents = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", minimumFractionDigits: 2 }) const count = new Intl.NumberFormat("en-US") type Billing = "monthly" | "yearly" const plans = [ { id: "hobby", name: "Hobby", price: 0, blurb: "For side projects and learning.", cta: "Start free", runs: "10,000 runs included", features: ["3 agents", "Community tools", "7-day trace history", "Email support"] }, { id: "team", name: "Team", price: 49, blurb: "For teams shipping agents to customers.", cta: "Start 14-day trial", runs: "250,000 runs included", features: ["Unlimited agents", "Evals and GitHub checks", "90-day trace history", "Approvals and audit log", "Priority support"], featured: true }, { id: "scale", name: "Scale", price: null, blurb: "For regulated and high-volume workloads.", cta: "Talk to sales", runs: "Volume pricing", features: ["Your VPC or on-prem data plane", "SSO, SCIM and custom roles", "1-year trace retention", "99.99% uptime SLA", "Dedicated engineer"] }, ] as const const faqs = [ { q: "What counts as a run?", a: "One end-to-end execution of an agent, from the first prompt to the final reply, however many tool calls or model turns it takes. Retries of the same run are free." }, { q: "Do I pay for model tokens?", a: "Bring your own keys and pay your model provider directly, or use Orbit’s pooled capacity at cost plus 8 percent. Either way the bill shows tokens per run." }, { q: "What happens when I pass my included runs?", a: "Nothing breaks. Extra runs are billed at the rate in the calculator, and you can set a hard monthly cap so a loop can never surprise you." }, { q: "Can I self-host?", a: "The Scale plan runs the data plane in your own cloud or on-prem. The control plane stays hosted, and no prompts, tool data or secrets ever leave your network." }, { q: "Is there a discount for startups or nonprofits?", a: "Yes. Startups under two years old get 50 percent off Team for a year, and verified nonprofits get 40 percent off for good." }, ] function Calculator({ billing }: { billing: Billing }) { const [runs, setRuns] = React.useState(400_000) const seats = 5 const included = 250_000 const extra = Math.max(0, runs - included) const seatPrice = billing === "yearly" ? 39 : 49 const base = seats * seatPrice // Overage gets cheaper in steps: $2.00 per thousand to 1M, then $1.40. const tier1 = Math.min(extra, 750_000) const tier2 = Math.max(0, extra - 750_000) const overage = (tier1 / 1000) * 2 + (tier2 / 1000) * 1.4 const total = base + overage return ( <section aria-labelledby="orbit-calc" className="bg-card/60 mx-auto mt-16 max-w-5xl rounded-3xl border p-6 sm:p-10"> <div className="grid gap-10 lg:grid-cols-[1.2fr_1fr] lg:gap-14"> <div> <h2 id="orbit-calc" className="text-2xl font-semibold tracking-[-0.03em]">Estimate your bill</h2> <p className="text-muted-foreground mt-2 text-sm text-pretty">Team plan for {seats} seats. Drag to see how overage scales.</p> <div className="mt-10"> <div className="flex items-baseline justify-between text-sm"> <label id="orbit-runs-label" className="text-muted-foreground">Runs per month</label> <output className="text-xl font-medium tabular-nums" style={mono} aria-live="polite">{count.format(runs)}</output> </div> <Slider className="mt-5" aria-labelledby="orbit-runs-label" min={50_000} max={2_000_000} step={50_000} value={[runs]} onValueChange={(v) => setRuns(v[0] ?? runs)} formatValue={(v) => `${count.format(v)} runs`} /> <div className="text-muted-foreground mt-2 flex justify-between text-xs tabular-nums" style={mono}><span>50k</span><span>2M</span></div> </div> </div> <div className="bg-background/60 space-y-3 rounded-2xl border p-5 text-sm"> <dl className="space-y-3"> <div className="flex justify-between gap-4"><dt className="text-muted-foreground">{seats} seats × {money.format(seatPrice)}</dt><dd className="tabular-nums" style={mono}>{money.format(base)}</dd></div> <div className="flex justify-between gap-4"><dt className="text-muted-foreground">{count.format(included)} runs included</dt><dd className="tabular-nums" style={mono}>$0</dd></div> <div className="flex justify-between gap-4"><dt className="text-muted-foreground">{count.format(extra)} extra runs</dt><dd className="tabular-nums" style={mono}>{moneyCents.format(overage)}</dd></div> <div className="flex items-baseline justify-between gap-4 border-t pt-4"><dt className="font-medium">Estimated per month</dt><dd className="text-3xl font-semibold tracking-[-0.03em] tabular-nums">{money.format(total)}</dd></div> </dl> <p className="text-muted-foreground text-xs">Model tokens are billed by your provider. Set a hard cap in settings.</p> </div> </div> </section> ) } type OrbitPricingProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> } /** Orbit pricing: three plans with a monthly and yearly switch, a usage calculator and common questions. */ function OrbitPricing({ hrefs, ...props }: OrbitPricingProps) { const [billing, setBilling] = React.useState<Billing>("yearly") return ( <OrbitShell page="pricing" hrefs={hrefs} {...props}> <main className="px-4 pt-16 pb-20 sm:px-6 sm:pt-24"> <div className="mx-auto max-w-3xl text-center"> <p className="text-chart-1 text-sm font-medium" style={mono}>Pricing</p> <h1 className="mt-3 text-4xl font-semibold tracking-[-0.045em] text-balance sm:text-6xl">Pay for runs, not for hope.</h1> <p className="text-muted-foreground mt-5 text-lg text-pretty">Start free, then grow into seats and volume. Every plan includes traces, tools and the full SDK.</p> <SegmentedControl aria-label="Billing period" value={billing} onValueChange={(v) => setBilling(v as Billing)} className="mt-9"> <SegmentedControlItem value="monthly">Monthly</SegmentedControlItem> <SegmentedControlItem value="yearly">Yearly · save 20%</SegmentedControlItem> </SegmentedControl> </div> <div className="mx-auto mt-12 grid max-w-6xl gap-4 lg:grid-cols-3"> {plans.map((p) => { const price = p.price === null ? null : p.price === 0 ? 0 : billing === "yearly" ? Math.round(p.price * 0.8) : p.price return ( <article key={p.id} className={cn("bg-card/60 relative flex flex-col rounded-3xl border p-7", "featured" in p && p.featured && "border-chart-1/60 shadow-[0_30px_80px_-40px_oklch(0.6_0.2_285/0.7)]")}> {"featured" in p && p.featured && <span className="bg-chart-1 text-background absolute -top-3 start-7 rounded-full px-3 py-0.5 text-xs font-medium">Most popular</span>} <h2 className="text-lg font-semibold">{p.name}</h2> <p className="text-muted-foreground mt-1 text-sm text-pretty">{p.blurb}</p> <p className="mt-6 flex items-baseline gap-1.5"> {price === null ? <span className="text-4xl font-semibold tracking-[-0.04em]">Custom</span> : ( <> <span className="text-5xl font-semibold tracking-[-0.045em] tabular-nums">{money.format(price)}</span> <span className="text-muted-foreground text-sm">{price === 0 ? "forever" : "per seat / month"}</span> </> )} </p> <p className="text-muted-foreground mt-2 text-sm" style={mono}>{p.runs}</p> <a href="#" className={cn("focus-visible:ring-ring/50 mt-6 inline-flex h-11 items-center justify-center rounded-xl text-sm font-medium outline-none transition-opacity focus-visible:ring-[3px]", "featured" in p && p.featured ? "bg-foreground text-background hover:opacity-90" : "hover:bg-accent border")}>{p.cta}</a> <ul className="mt-7 space-y-3 border-t pt-6 text-sm"> {p.features.map((f) => ( <li key={f} className="flex items-start gap-2.5"><Check className="text-chart-2 mt-0.5 size-4 shrink-0" aria-hidden="true" />{f}</li> ))} </ul> </article> ) })} </div> <Calculator billing={billing} /> <section className="mx-auto mt-24 max-w-3xl" aria-labelledby="orbit-faq"> <h2 id="orbit-faq" className="text-3xl font-semibold tracking-[-0.04em]">Questions, answered</h2> <Accordion type="single" collapsible className="mt-6"> {faqs.map((f) => ( <AccordionItem key={f.q} value={f.q}> <AccordionTrigger className="text-base">{f.q}</AccordionTrigger> <AccordionContent className="text-muted-foreground text-pretty">{f.a}</AccordionContent> </AccordionItem> ))} </Accordion> </section> </main> </OrbitShell> ) } export { OrbitPricing, type OrbitPricingProps }components/ballmac/templates/orbit/orbit-changelog.tsx// Ballmac UI: Orbit changelog page. https://ui.ballmac.com/templates/template-orbit import * as React from "react" import { Rss } from "lucide-react" import { ChangelogFeed, type ChangelogEntry } from "@/components/ballmac/changelog-feed" import { OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme" const entries: ChangelogEntry[] = [ { id: "2-4", version: "2.4", date: "2026-09-24", title: "Computer use is generally available", summary: "Agents can now see a screen, click, type and extract data from any web app, inside a sandboxed browser you control.", changes: [ { type: "new", text: "Browser tool with screenshots, DOM snapshots and a full action trace." }, { type: "new", text: "Session recording: replay what the agent saw, frame by frame." }, { type: "improved", text: "Approvals can now be routed to Slack channels by risk level." }, { type: "fixed", text: "Long-running runs no longer lose their cost counter after a deploy." }, ], }, { id: "2-3", version: "2.3", date: "2026-08-27", title: "Evals as GitHub checks", summary: "Every pull request can now run your eval suite and block the merge when success rate drops.", changes: [ { type: "new", text: "GitHub app with per-case results and a diff against main." }, { type: "new", text: "Model comparison view: run one dataset against three models." }, { type: "improved", text: "LLM graders accept a rubric and return a rationale you can edit." }, { type: "removed", text: "The v1 evaluation endpoints, deprecated in March." }, ], }, { id: "2-2", version: "2.2", date: "2026-07-30", title: "Bring your own cloud", summary: "Run the data plane in your VPC. Prompts, tool data and secrets never leave your network.", changes: [ { type: "new", text: "Terraform module for AWS, GCP and Azure." }, { type: "improved", text: "Traces load 3x faster for runs over 500 spans." }, { type: "fixed", text: "Tool retries respected the wrong backoff when the API returned Retry-After." }, ], }, { id: "2-1", version: "2.1", date: "2026-06-26", title: "Branch any trace", changes: [ { type: "new", text: "Branch a run from any step, edit the input and compare outcomes." }, { type: "improved", text: "SDK streams partial tool results to your UI." }, { type: "fixed", text: "Python SDK now cancels in-flight tool calls on Ctrl+C." }, ], }, ] type OrbitChangelogProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> } /** Orbit changelog: a filterable release feed under a calm header. */ function OrbitChangelog({ hrefs, ...props }: OrbitChangelogProps) { return ( <OrbitShell page="changelog" hrefs={hrefs} {...props}> <main className="mx-auto max-w-3xl px-4 pt-16 pb-24 sm:px-6 sm:pt-24"> <p className="text-chart-1 text-sm font-medium" style={{ fontFamily: "var(--orbit-mono)" }}>Changelog</p> <div className="mt-3 flex flex-wrap items-end justify-between gap-4"> <h1 className="text-4xl font-semibold tracking-[-0.045em] text-balance sm:text-6xl">What we shipped.</h1> <a href="#" className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex items-center gap-2 rounded-lg text-sm outline-none focus-visible:ring-[3px]"> <Rss className="size-4" aria-hidden="true" /> RSS </a> </div> <p className="text-muted-foreground mt-5 max-w-xl text-lg text-pretty">New releases land every month. Breaking changes are announced 90 days ahead.</p> <ChangelogFeed className="mt-12" entries={entries} filterable /> </main> </OrbitShell> ) } export { OrbitChangelog, type OrbitChangelogProps }components/ballmac/templates/orbit/orbit-login.tsx// Ballmac UI: Orbit sign-in page. https://ui.ballmac.com/templates/template-orbit "use client" import * as React from "react" import { ArrowRight, Check } from "lucide-react" import { OrbitMark, OrbitShell, type OrbitHrefs } from "@/components/ballmac/templates/orbit/orbit-theme" const mono = { fontFamily: "var(--orbit-mono)" } as const type OrbitLoginProps = React.ComponentProps<"div"> & { hrefs?: Partial<OrbitHrefs> } /** Orbit sign-in: a focused form beside a live-looking trace. */ function OrbitLogin({ hrefs, ...props }: OrbitLoginProps) { const [sent, setSent] = React.useState(false) const [email, setEmail] = React.useState("") const home = hrefs?.home ?? "/orbit" return ( <OrbitShell page="login" hrefs={hrefs} bare {...props}> <main className="grid min-h-dvh lg:grid-cols-2"> <div className="flex flex-col px-6 py-8 sm:px-12"> <a href={home} className="focus-visible:ring-ring/50 flex w-fit items-center gap-2 rounded-md font-semibold tracking-tight outline-none focus-visible:ring-[3px]"><OrbitMark /> Orbit</a> <div className="mx-auto flex w-full max-w-sm flex-1 flex-col justify-center py-12"> {sent ? ( <div role="status"> <span className="bg-chart-2/15 text-chart-2 flex size-12 items-center justify-center rounded-2xl"><Check className="size-6" aria-hidden="true" /></span> <h1 className="mt-6 text-3xl font-semibold tracking-[-0.04em]">Check your inbox</h1> <p className="text-muted-foreground mt-3 text-pretty">We sent a sign-in link to <span className="text-foreground font-medium">{email}</span>. It works for 15 minutes.</p> <button type="button" onClick={() => setSent(false)} className="text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 mt-6 rounded-md text-sm underline underline-offset-4 outline-none focus-visible:ring-[3px]">Use a different email</button> </div> ) : ( <> <h1 className="text-3xl font-semibold tracking-[-0.04em]">Welcome back</h1> <p className="text-muted-foreground mt-2">Sign in to your Orbit workspace.</p> <button type="button" className="hover:bg-accent focus-visible:ring-ring/50 mt-8 inline-flex h-11 items-center justify-center gap-2.5 rounded-xl border text-sm font-medium outline-none transition-colors focus-visible:ring-[3px]"> <svg viewBox="0 0 24 24" className="size-4" aria-hidden="true" fill="currentColor"><path d="M12 .5a11.5 11.5 0 0 0-3.64 22.41c.58.1.79-.25.79-.56v-2c-3.2.7-3.88-1.54-3.88-1.54-.52-1.33-1.28-1.69-1.28-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.19 1.76 1.19 1.03 1.76 2.7 1.25 3.36.96.1-.75.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.28 1.18-3.09-.12-.29-.51-1.46.11-3.05 0 0 .97-.31 3.17 1.18a11 11 0 0 1 5.77 0c2.2-1.49 3.17-1.18 3.17-1.18.62 1.59.23 2.76.11 3.05.74.81 1.18 1.83 1.18 3.09 0 4.42-2.69 5.39-5.25 5.68.41.36.78 1.06.78 2.14v3.17c0 .31.21.67.8.56A11.5 11.5 0 0 0 12 .5Z" /></svg> Continue with GitHub </button> <div className="text-muted-foreground my-6 flex items-center gap-3 text-xs"><span className="bg-border h-px flex-1" />or with email<span className="bg-border h-px flex-1" /></div> <form onSubmit={(e) => { e.preventDefault() if (email.trim()) setSent(true) }} > <label htmlFor="orbit-email" className="text-sm font-medium">Work email</label> <input id="orbit-email" type="email" required autoComplete="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@company.com" className="bg-card/60 focus-visible:ring-ring/50 placeholder:text-muted-foreground mt-2 h-11 w-full rounded-xl border px-3.5 text-sm outline-none focus-visible:ring-[3px]" /> <button type="submit" className="bg-foreground text-background focus-visible:ring-ring/50 mt-4 inline-flex h-11 w-full items-center justify-center gap-2 rounded-xl text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]"> Email me a sign-in link <ArrowRight className="size-4 rtl:rotate-180" aria-hidden="true" /> </button> </form> <p className="text-muted-foreground mt-6 text-xs text-pretty">New to Orbit? Signing in creates your workspace. By continuing you agree to the Terms and Privacy Policy.</p> </> )} </div> </div> <div aria-hidden="true" className="bg-surface relative hidden overflow-hidden border-s lg:block"> <div className="from-chart-1/25 to-chart-5/10 absolute inset-0 bg-gradient-to-br via-transparent" /> <div className="relative flex h-full flex-col justify-center gap-4 px-14"> {[ { t: "09:41:02", n: "refund-reconciler", s: "completed", tone: "text-chart-2" }, { t: "09:41:00", n: "stripe.refunds.list", s: "214 rows", tone: "text-chart-1" }, { t: "09:40:58", n: "support-triage", s: "approval needed", tone: "text-chart-3" }, { t: "09:40:55", n: "warehouse.query", s: "208 matched", tone: "text-chart-1" }, { t: "09:40:51", n: "onboarding-guide", s: "completed", tone: "text-chart-2" }, ].map((r, i) => ( <div key={r.t} className="bg-card/70 flex items-center justify-between rounded-2xl border px-5 py-4 text-sm backdrop-blur" style={{ ...mono, marginInlineStart: `${(i % 3) * 1.5}rem`, opacity: 1 - i * 0.14 }}> <span className="text-muted-foreground">{r.t}</span> <span>{r.n}</span> <span className={r.tone}>{r.s}</span> </div> ))} <p className="text-muted-foreground mt-6 max-w-sm text-lg text-pretty">“Orbit is the first agent platform our security team signed off on in a week.”</p> </div> </div> </main> </OrbitShell> ) } export { OrbitLogin, type OrbitLoginProps }app/orbit/page.tsx// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit import type { Metadata } from "next" import { OrbitHome } from "@/components/ballmac/templates/orbit/orbit-home" export const metadata: Metadata = { title: "Orbit: Agents that finish the job", description: "Build, test and run AI agents with tools, evals and guardrails.", } export default function Page() { return <OrbitHome /> }app/orbit/pricing/page.tsx// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit import type { Metadata } from "next" import { OrbitPricing } from "@/components/ballmac/templates/orbit/orbit-pricing" export const metadata: Metadata = { title: "Orbit pricing", description: "Pay for runs, not for hope. Start free and grow into seats and volume.", } export default function Page() { return <OrbitPricing /> }app/orbit/changelog/page.tsx// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit import type { Metadata } from "next" import { OrbitChangelog } from "@/components/ballmac/templates/orbit/orbit-changelog" export const metadata: Metadata = { title: "Orbit changelog", description: "Everything we shipped, month by month.", } export default function Page() { return <OrbitChangelog /> }app/orbit/login/page.tsx// Ballmac UI: Orbit template route. https://ui.ballmac.com/templates/template-orbit import type { Metadata } from "next" import { OrbitLogin } from "@/components/ballmac/templates/orbit/orbit-login" export const metadata: Metadata = { title: "Sign in to Orbit", description: "Sign in to your Orbit workspace.", } export default function Page() { return <OrbitLogin /> }Update the import paths to match your project setup.
The page is added as a route in your app, with every block it uses.
What's inside
- Pages
- Home/orbit
- Pricing/orbit/pricing
- Changelog/orbit/changelog
- Sign in/orbit/login
- Fonts
- Instrument Sans, JetBrains Mono (Google Fonts, loaded with next/font)
- Files
- 11 files · 10 Ballmac blocks and components
Built with
Dependencies
Use with AI
Installs a four-page dark site for an AI product. Change the palette in orbit-theme.tsx (orbitVars), the copy in each page file, and the agent-run script (steps) in orbit-agent-run.tsx. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Orbit: AI agent platform (@ballmac/template-orbit) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Marketing sites for AI agents, LLM tooling or developer platforms
- A dark, high-contrast brand with a live product demo in the hero
Not for
- Light, editorial brands (a Northwind-style template)
- Mac app landing pages (a Ledger-style template)
Registry JSON: https://ui.ballmac.com/r/template-orbit.json
Free to use in personal and commercial projects.