feat(website): complete landing page redesign

- New dark design system: pure black base (#050505), animated mesh gradient
  hero with floating orbs, film grain overlay, shimmer/glow card effects
- Floating pill nav with frosted glass backdrop blur
- Bento grid feature cards with colored accents and hover glow
- Animated workflow pipeline with gradient progress line
- Animated stat counters with gradient text
- Staggered scroll reveal animations
- Gradient text utilities (orange, purple, blue)
- Step-by-step HowItWorks with code previews
- CTA with radial orange glow background
- All 14 landing components rewritten
- ~2000 lines of redesigned code
This commit is contained in:
Priyanchew 2026-05-27 21:46:29 +00:00
parent 96ea22a774
commit af8887d4ff
17 changed files with 1132 additions and 517 deletions

View File

@ -72,158 +72,342 @@ a {
color: inherit;
}
/* ── Landing Page ─────────────────────────────────────────────────── */
/*
LANDING PAGE Redesigned Design System
*/
.landing-page {
--landing-bg: #121110;
--landing-fg: #f0ece8;
--landing-muted: #a8a29e;
--landing-muted-dim: #57534e;
--landing-accent: #f97316;
--landing-border-subtle: rgba(255, 240, 220, 0.07);
--landing-border-default: rgba(255, 240, 220, 0.13);
--landing-border-strong: rgba(255, 240, 220, 0.24);
--landing-surface: #1a1918;
--landing-card-bg: #1c1b19;
/* Core palette */
--landing-bg: #050505;
--landing-bg-secondary: #0a0a0a;
--landing-fg: #fafafa;
--landing-fg-secondary: #e5e5e5;
--landing-muted: #888;
--landing-muted-dim: #444;
--landing-border: rgba(255,255,255,0.06);
--landing-border-hover: rgba(255,255,255,0.12);
--landing-border-strong: rgba(255,255,255,0.18);
/* Accent colors */
--accent-orange: #f97316;
--accent-blue: #3b82f6;
--accent-purple: #8b5cf6;
--accent-green: #22c55e;
--accent-cyan: #06b6d4;
/* Surfaces */
--surface-card: rgba(255,255,255,0.02);
--surface-card-hover: rgba(255,255,255,0.04);
--surface-glass: rgba(255,255,255,0.03);
background: var(--landing-bg);
color: var(--landing-fg);
font-family: var(--font-sans, -apple-system, "SF Pro Text", system-ui, sans-serif);
overflow-x: hidden;
}
.liquid-glass-solid {
background: var(--landing-accent);
color: #121110;
font-weight: 500;
border: none;
}
.liquid-glass-solid:hover {
opacity: 0.9;
/* ── Grain overlay ─────────────────────────────────────────────── */
.landing-page::before {
content: "";
position: fixed;
inset: 0;
z-index: 9999;
pointer-events: none;
opacity: 0.035;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
background-repeat: repeat;
background-size: 256px 256px;
}
/* ── Glass card ────────────────────────────────────────────────── */
.landing-card {
background: var(--landing-card-bg);
border: 1px solid var(--landing-border-subtle);
transition: border-color 0.2s, background 0.2s;
background: var(--surface-card);
border: 1px solid var(--landing-border);
border-radius: 16px;
transition: border-color 0.3s, background 0.3s, box-shadow 0.3s;
}
.landing-card:hover {
border-color: var(--landing-border-default);
border-color: var(--landing-border-hover);
background: var(--surface-card-hover);
}
.landing-hero-grid {
background-image: radial-gradient(rgba(255, 240, 220, 0.04) 1px, transparent 1px);
background-size: 24px 24px;
mask-image: radial-gradient(ellipse at center, black 30%, transparent 70%);
-webkit-mask-image: radial-gradient(ellipse at center, black 30%, transparent 70%);
/* ── Shimmer border card ───────────────────────────────────────── */
.shimmer-card {
position: relative;
background: var(--surface-card);
border-radius: 16px;
overflow: hidden;
}
.shimmer-card::before {
content: "";
position: absolute;
inset: 0;
border-radius: 16px;
padding: 1px;
background: linear-gradient(
135deg,
rgba(255,255,255,0) 0%,
rgba(255,255,255,0.1) 25%,
rgba(255,255,255,0) 50%
);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
animation: shimmer-rotate 6s linear infinite;
}
@keyframes shimmer-rotate {
0% { background-position: 0% 0%; }
100% { background-position: 200% 200%; }
}
@keyframes landing-fade-rise {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
/* ── Glow card ─────────────────────────────────────────────────── */
.glow-card {
position: relative;
background: var(--surface-card);
border: 1px solid var(--landing-border);
border-radius: 16px;
transition: all 0.4s ease;
}
.glow-card:hover {
border-color: var(--landing-border-hover);
box-shadow: 0 0 40px -10px rgba(249,115,22,0.15);
}
@keyframes landing-pulse-dot {
0%,
100% {
opacity: 0.4;
}
50% {
opacity: 1;
}
/* ── Animated gradient mesh ────────────────────────────────────── */
.mesh-gradient {
position: absolute;
inset: 0;
overflow: hidden;
}
.mesh-gradient .orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.3;
animation: float-orb 20s ease-in-out infinite;
}
.mesh-gradient .orb-1 {
width: 600px; height: 600px;
background: radial-gradient(circle, rgba(249,115,22,0.4), transparent 70%);
top: -20%; left: -10%;
animation-delay: 0s;
}
.mesh-gradient .orb-2 {
width: 500px; height: 500px;
background: radial-gradient(circle, rgba(139,92,246,0.3), transparent 70%);
top: 10%; right: -15%;
animation-delay: -7s;
}
.mesh-gradient .orb-3 {
width: 400px; height: 400px;
background: radial-gradient(circle, rgba(59,130,246,0.25), transparent 70%);
bottom: -10%; left: 30%;
animation-delay: -14s;
}
@keyframes float-orb {
0%, 100% { transform: translate(0, 0) scale(1); }
25% { transform: translate(30px, -40px) scale(1.05); }
50% { transform: translate(-20px, 20px) scale(0.95); }
75% { transform: translate(40px, 30px) scale(1.02); }
}
.landing-fade-rise {
animation: landing-fade-rise 0.8s ease-out both;
/* ── Grid pattern background ───────────────────────────────────── */
.grid-pattern {
background-image:
linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(ellipse 70% 50% at 50% 50%, black 30%, transparent 70%);
-webkit-mask-image: radial-gradient(ellipse 70% 50% at 50% 50%, black 30%, transparent 70%);
}
.landing-fade-rise-d1 {
animation: landing-fade-rise 0.8s ease-out 0.2s both;
}
.landing-fade-rise-d2 {
animation: landing-fade-rise 0.8s ease-out 0.4s both;
/* ── Fade-in animations ────────────────────────────────────────── */
@keyframes fade-up {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.landing-fade-up {
animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.landing-fade-up-d1 {
animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.15s both;
}
.landing-fade-up-d2 {
animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.3s both;
}
.landing-fade-up-d3 {
animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.45s both;
}
.landing-fade-up-d4 {
animation: fade-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.6s both;
}
/* ── Scroll reveal ─────────────────────────────────────────────── */
.landing-reveal {
opacity: 0;
transform: translateY(32px);
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
transform: translateY(40px);
transition: opacity 0.9s cubic-bezier(0.16, 1, 0.3, 1), transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}
.landing-reveal.visible {
opacity: 1;
transform: translateY(0);
}
.landing-agent-dot {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(134, 239, 172, 0.7);
animation: landing-pulse-dot 2s ease-in-out infinite;
/* ── Cursor blink ──────────────────────────────────────────────── */
@keyframes cursor-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
/* Terminal typing animations */
.landing-line-appear {
animation: landing-fade-rise 0.3s ease-out both;
}
@keyframes landing-cursor-blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
.landing-cursor-blink {
animation: landing-cursor-blink 0.8s step-end infinite;
animation: cursor-blink 0.8s step-end infinite;
}
/* Workflow pipeline pulse */
@keyframes landing-node-pulse {
0% {
box-shadow: 0 0 0 0 currentColor;
opacity: 0.6;
}
70% {
box-shadow: 0 0 0 10px transparent;
opacity: 0;
}
100% {
box-shadow: 0 0 0 0 transparent;
opacity: 0;
}
/* ── Pulse dot ─────────────────────────────────────────────────── */
@keyframes pulse-dot {
0%, 100% { opacity: 0.5; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
}
.landing-pulse-dot {
animation: pulse-dot 2s ease-in-out infinite;
}
/* ── Node pulse ring ───────────────────────────────────────────── */
@keyframes node-pulse {
0% { box-shadow: 0 0 0 0 currentColor; opacity: 0.6; }
70% { box-shadow: 0 0 0 12px transparent; opacity: 0; }
100% { box-shadow: 0 0 0 0 transparent; opacity: 0; }
}
.landing-node-pulse {
border: 1.5px solid;
animation: landing-node-pulse 1.5s ease-out infinite;
animation: node-pulse 1.5s ease-out infinite;
}
/* Landing page spacing protection
Fumadocs bundles its own Tailwind inside @layer fumadocs. Because that sheet
loads after the root layout, @layer fumadocs has higher cascade priority than
root-level @layer utilities. Its universal preflight reset (* { margin:0;
padding:0 }) zeros any spacing utility not present in fumadocs' own bundle.
These unlayered rules (outside any @layer) beat ALL named layers and restore
the correct values for every margin/padding class used in the landing page. */
/* ── Line appear ───────────────────────────────────────────────── */
.landing-line-appear {
animation: fade-up 0.3s ease-out both;
}
/* ── Gradient text ─────────────────────────────────────────────── */
.text-gradient-orange {
background: linear-gradient(135deg, #f97316, #fb923c, #fbbf24);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.text-gradient-purple {
background: linear-gradient(135deg, #8b5cf6, #a78bfa, #c4b5fd);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.text-gradient-blue {
background: linear-gradient(135deg, #3b82f6, #60a5fa, #93c5fd);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* ── Animated counter ──────────────────────────────────────────── */
@keyframes count-up {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* ── Feature icon glow ─────────────────────────────────────────── */
.icon-glow {
position: relative;
}
.icon-glow::after {
content: "";
position: absolute;
inset: -4px;
border-radius: inherit;
background: inherit;
filter: blur(12px);
opacity: 0.4;
z-index: -1;
}
/* ── Button styles ─────────────────────────────────────────────── */
.btn-primary {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 24px;
border-radius: 10px;
font-weight: 600;
font-size: 0.9375rem;
background: linear-gradient(135deg, #f97316, #ea580c);
color: #fff;
border: none;
cursor: pointer;
transition: all 0.2s;
box-shadow: 0 0 20px -5px rgba(249,115,22,0.4);
text-decoration: none;
}
.btn-primary:hover {
box-shadow: 0 0 30px -5px rgba(249,115,22,0.6);
transform: translateY(-1px);
}
.btn-secondary {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 24px;
border-radius: 10px;
font-weight: 500;
font-size: 0.9375rem;
background: var(--surface-card);
color: var(--landing-fg);
border: 1px solid var(--landing-border);
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
}
.btn-secondary:hover {
border-color: var(--landing-border-hover);
background: var(--surface-card-hover);
}
/* ── Animated pipeline line ────────────────────────────────────── */
@keyframes pipeline-fill {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* ── Stagger children animation ────────────────────────────────── */
.stagger-children > * {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.stagger-children.visible > *:nth-child(1) { transition-delay: 0s; opacity: 1; transform: translateY(0); }
.stagger-children.visible > *:nth-child(2) { transition-delay: 0.1s; opacity: 1; transform: translateY(0); }
.stagger-children.visible > *:nth-child(3) { transition-delay: 0.2s; opacity: 1; transform: translateY(0); }
.stagger-children.visible > *:nth-child(4) { transition-delay: 0.3s; opacity: 1; transform: translateY(0); }
.stagger-children.visible > *:nth-child(5) { transition-delay: 0.4s; opacity: 1; transform: translateY(0); }
.stagger-children.visible > *:nth-child(6) { transition-delay: 0.5s; opacity: 1; transform: translateY(0); }
/*
SPACING PROTECTION fumadocs cascade override
Unlayered rules beat all named layers.
*/
/* Margin top */
.landing-page .mt-1 { margin-top: calc(var(--spacing) * 1); }
.landing-page .mt-2 { margin-top: calc(var(--spacing) * 2); }
.landing-page .mt-3 { margin-top: calc(var(--spacing) * 3); }
.landing-page .mt-4 { margin-top: calc(var(--spacing) * 4); }
.landing-page .mt-5 { margin-top: calc(var(--spacing) * 5); }
.landing-page .mt-6 { margin-top: calc(var(--spacing) * 6); }
.landing-page .mt-8 { margin-top: calc(var(--spacing) * 8); }
.landing-page .mt-10 { margin-top: calc(var(--spacing) * 10); }
@ -231,6 +415,7 @@ a {
.landing-page .mt-16 { margin-top: calc(var(--spacing) * 16); }
.landing-page .mt-20 { margin-top: calc(var(--spacing) * 20); }
/* Margin bottom */
.landing-page .mb-1 { margin-bottom: calc(var(--spacing) * 1); }
.landing-page .mb-2 { margin-bottom: calc(var(--spacing) * 2); }
.landing-page .mb-3 { margin-bottom: calc(var(--spacing) * 3); }
@ -243,19 +428,22 @@ a {
.landing-page .mb-16 { margin-bottom: calc(var(--spacing) * 16); }
.landing-page .mb-1\.5 { margin-bottom: calc(var(--spacing) * 1.5); }
/* Margin left/right */
.landing-page .ml-1 { margin-left: calc(var(--spacing) * 1); }
.landing-page .ml-2 { margin-left: calc(var(--spacing) * 2); }
.landing-page .ml-1\.5 { margin-left: calc(var(--spacing) * 1.5); }
.landing-page .mr-1 { margin-right: calc(var(--spacing) * 1); }
.landing-page .mr-1\.5 { margin-right: calc(var(--spacing) * 1.5); }
/* Padding */
.landing-page .p-2 { padding: calc(var(--spacing) * 2); }
.landing-page .p-3 { padding: calc(var(--spacing) * 3); }
.landing-page .p-6 { padding: calc(var(--spacing) * 6); }
.landing-page .p-7 { padding: calc(var(--spacing) * 7); }
.landing-page .p-8 { padding: calc(var(--spacing) * 8); }
.landing-page .p-2\.5 { padding: calc(var(--spacing) * 2.5); }
.landing-page .p-10 { padding: calc(var(--spacing) * 10); }
.landing-page .p-12 { padding: calc(var(--spacing) * 12); }
.landing-page .pt-10 { padding-top: calc(var(--spacing) * 10); }
.landing-page .pt-32 { padding-top: calc(var(--spacing) * 32); }
@ -285,33 +473,36 @@ a {
/* Arbitrary padding values */
.landing-page .py-\[100px\] { padding-top: 100px; padding-bottom: 100px; }
.landing-page .py-\[120px\] { padding-top: 120px; padding-bottom: 120px; }
.landing-page .py-\[140px\] { padding-top: 140px; padding-bottom: 140px; }
.landing-page .py-\[160px\] { padding-top: 160px; padding-bottom: 160px; }
.landing-page .pt-\[60px\] { padding-top: 60px; }
.landing-page .pb-\[120px\] { padding-bottom: 120px; }
/* Border colors fumadocs * { border-color: var(--color-fd-border) } persists from docs
navigation. In light mode --color-fd-border = #d6d3d1 which looks bright on the dark
landing background, overriding explicit border-[var(--landing-border-*)] utilities. */
.landing-page .border-\[var\(--landing-border-subtle\)\] { border-color: var(--landing-border-subtle); }
.landing-page .border-\[var\(--landing-border-default\)\] { border-color: var(--landing-border-default); }
.landing-page .border-\[var\(--landing-border-strong\)\] { border-color: var(--landing-border-strong); }
/* Border colors */
.landing-page .border-\[var\(--landing-border\)\] { border-color: var(--landing-border); }
.landing-page .border-\[var\(--landing-border-hover\)\] { border-color: var(--landing-border-hover); }
.landing-page .border-\[var\(--landing-border-strong\)\] { border-color: var(--landing-border-strong); }
/* Font weight — fumadocs heading reset zeroes h1-h6 { font-weight: inherit } */
/* Font weight */
.landing-page .font-\[680\] { font-weight: 680; }
.landing-page .font-\[700\] { font-weight: 700; }
/* Font size (clamp values) — fumadocs heading reset zeroes h1-h6 { font-size: inherit } */
/* Font size (clamp values) */
.landing-page .text-\[clamp\(1\.75rem\,4vw\,2\.75rem\)\] { font-size: clamp(1.75rem, 4vw, 2.75rem); }
.landing-page .text-\[clamp\(1\.375rem\,3vw\,2rem\)\] { font-size: clamp(1.375rem, 3vw, 2rem); }
.landing-page .text-\[clamp\(2rem\,4vw\,3rem\)\] { font-size: clamp(2rem, 4vw, 3rem); }
.landing-page .text-\[clamp\(2rem\,5vw\,3\.5rem\)\] { font-size: clamp(2rem, 5vw, 3.5rem); }
.landing-page .text-\[clamp\(2\.5rem\,6vw\,4\.5rem\)\] { font-size: clamp(2.5rem, 6vw, 4.5rem); }
.landing-page .text-\[clamp\(3rem\,8vw\,6rem\)\] { font-size: clamp(3rem, 8vw, 6rem); }
/* Responsive md: utilities fumadocs bundles .hidden but not all md: variants,
so its higher-priority layer keeps elements hidden / wrong grid at desktop widths. */
/* Responsive md: utilities */
@media (min-width: 768px) {
.landing-page .md\:flex { display: flex; }
.landing-page .md\:block { display: block; }
.landing-page .md\:hidden { display: none; }
.landing-page .md\:flex-row { flex-direction: row; }
.landing-page .md\:flex-col { flex-direction: column; }
.landing-page .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.landing-page .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@ -330,3 +521,8 @@ a {
.landing-page .md\:order-1 { order: 1; }
.landing-page .md\:order-2 { order: 2; }
}
/* Border radius overrides */
.landing-page .rounded-2xl { border-radius: 16px; }
.landing-page .rounded-xl { border-radius: 12px; }
.landing-page .rounded-lg { border-radius: 8px; }

View File

@ -22,8 +22,8 @@ export default async function LandingPage() {
<ScrollRevealProvider>
<LandingNav />
<LandingHero starsLabel={formatCompactNumber(githubStats.stars)} />
<LandingAbout />
<LandingAgentsBar />
<LandingAbout />
<LandingFeatures />
<LandingWorkflow />
<LandingUseCases />
@ -34,8 +34,11 @@ export default async function LandingPage() {
<LandingTestimonials />
<LandingQuickStart />
<LandingCTA />
<footer className="py-12 px-8 text-center text-[var(--landing-muted)] opacity-30 text-[0.8125rem] border-t border-white/[0.04]">
MIT Licensed · Open Source
<footer className="py-12 px-8 text-center border-t" style={{
borderColor: "var(--landing-border)",
color: "var(--landing-muted-dim)"
}}>
<div className="text-[0.8125rem]">MIT Licensed · Open Source</div>
</footer>
</ScrollRevealProvider>
);

View File

@ -1,54 +1,118 @@
'use client';
export function LandingAbout() {
return (
<div className="bg-[radial-gradient(ellipse_at_top,rgba(255,240,220,0.015)_0%,transparent_70%)]">
<section className="landing-reveal py-[100px] px-6 max-w-[72rem] mx-auto">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted-dim)] mb-6 font-mono">
The problem
</div>
<h2 className="font-sans font-[680] text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] mb-10 max-w-[48rem]">
You&apos;re running AI agents in 10 browser tabs.{" "}
<span className="text-[var(--landing-muted)]">
Checking if PRs landed. Re-running failed CI. Copy-pasting error logs.
<section className="landing-reveal relative">
{/* Subtle radial glow behind section */}
<div className="absolute inset-0 pointer-events-none overflow-hidden">
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[500px] bg-[radial-gradient(ellipse_at_center,rgba(249,115,22,0.04)_0%,transparent_70%)]" />
</div>
<div className="relative py-[120px] px-6 max-w-[72rem] mx-auto">
{/* ── Section Label ─────────────────────────────────────── */}
<div className="flex items-center gap-3 mb-8">
<span className="inline-block w-1.5 h-1.5 rounded-full bg-[var(--accent-orange)] landing-pulse-dot" />
<span className="font-mono text-[0.6875rem] tracking-[0.2em] uppercase text-[var(--accent-orange)]">
The problem
</span>
</div>
{/* ── Headline ─────────────────────────────────────────── */}
<h2 className="font-sans font-[700] text-[clamp(1.75rem,4vw,2.75rem)] leading-[1.08] tracking-[-1.5px] mb-6 max-w-[40rem]">
You&apos;re running AI agents{' '}
<span className="text-gradient-orange">in 10 browser tabs.</span>
</h2>
<p className="text-[0.9375rem] text-[var(--landing-muted)] leading-[1.7] max-w-[32rem] mb-16">
Checking if PRs landed. Re-running failed CI. Copy-pasting error logs into ChatGPT.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 items-start">
<p className="text-[0.9375rem] text-[var(--landing-muted)] leading-[1.8] max-w-[28rem]">
Agent Orchestrator replaces that with one YAML file. Point it at
your GitHub issues, pick your agents, and walk away. Each agent
spawns in its own git worktree, creates PRs, fixes CI failures,
addresses review comments, and moves toward merge. If you are new, start with the <a href="/docs/" className="underline decoration-[var(--landing-border-default)] underline-offset-4 hover:text-white">docs quickstart and configuration guides</a>.
</p>
{/* ── Side-by-side Layout ──────────────────────────────── */}
<div className="grid grid-cols-1 lg:grid-cols-[1fr_1.1fr] gap-12 items-center">
{/* Left — Explanation */}
<div className="space-y-6">
<p className="text-[0.9375rem] text-[var(--landing-fg-secondary)] leading-[1.85] max-w-[28rem]">
Agent Orchestrator replaces that with one YAML file. Point it at
your GitHub issues, pick your agents, and walk away. Each agent
spawns in its own git worktree, creates PRs, fixes CI failures,
addresses review comments, and moves toward merge.
</p>
{/* Config preview — show how simple setup is */}
<div className="landing-card rounded-2xl overflow-hidden">
<div className="flex items-center gap-2 px-4 py-2.5 border-b border-[var(--landing-border-subtle)]">
<div className="w-2 h-2 rounded-full bg-[rgba(255,240,220,0.12)]" />
<div className="w-2 h-2 rounded-full bg-[rgba(255,240,220,0.12)]" />
<div className="w-2 h-2 rounded-full bg-[rgba(255,240,220,0.12)]" />
<span className="ml-1.5 font-mono text-[0.5625rem] text-[var(--landing-muted-dim)]">
<p className="text-[0.8125rem] text-[var(--landing-muted)] leading-[1.75]">
If you&apos;re new, start with the{' '}
<a
href="/docs/"
className="text-[var(--accent-orange)] underline underline-offset-4 decoration-[rgba(249,115,22,0.3)] hover:decoration-[var(--accent-orange)] transition-colors"
>
docs quickstart and configuration guides
</a>.
</p>
{/* Feature pills */}
<div className="flex flex-wrap gap-2 pt-4">
{['Git Worktrees', 'Auto CI Fix', 'PR Reviews', 'Slack Alerts'].map((tag) => (
<span
key={tag}
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-[0.6875rem] font-medium tracking-wide text-[var(--landing-muted)] border border-[var(--landing-border)] bg-[var(--surface-card)]"
>
<span className="w-1 h-1 rounded-full bg-[var(--accent-green)]" />
{tag}
</span>
))}
</div>
</div>
{/* Right — Terminal Code Preview Card */}
<div className="glow-card overflow-hidden">
{/* Terminal title bar */}
<div className="flex items-center justify-between px-5 py-3 border-b border-[var(--landing-border)] bg-[rgba(255,255,255,0.015)]">
<div className="flex items-center gap-2">
<div className="w-[10px] h-[10px] rounded-full bg-[rgba(255,255,255,0.06)]" />
<div className="w-[10px] h-[10px] rounded-full bg-[rgba(255,255,255,0.06)]" />
<div className="w-[10px] h-[10px] rounded-full bg-[rgba(255,255,255,0.06)]" />
</div>
<span className="font-mono text-[0.5625rem] tracking-wider text-[var(--landing-muted-dim)]">
agent-orchestrator.yaml
</span>
<div className="w-[38px]" />
</div>
<pre className="px-5 py-4 font-mono text-[0.75rem] leading-[1.9] overflow-x-auto">
<span className="text-[var(--landing-muted-dim)]">agent:</span>{" "}
<span className="text-[var(--landing-fg)]">claude-code</span>
{"\n"}
<span className="text-[var(--landing-muted-dim)]">tracker:</span>{" "}
<span className="text-[var(--landing-fg)]">github</span>
{"\n"}
<span className="text-[var(--landing-muted-dim)]">workspace:</span>{" "}
<span className="text-[var(--landing-fg)]">worktree</span>
{"\n"}
<span className="text-[var(--landing-muted-dim)]">runtime:</span>{" "}
<span className="text-[var(--landing-fg)]">tmux</span>
{"\n"}
<span className="text-[var(--landing-muted-dim)]">notifier:</span>{" "}
<span className="text-[var(--landing-fg)]">slack</span>
</pre>
{/* Code body */}
<div className="px-6 py-5 font-mono text-[0.8125rem] leading-[2.1] overflow-x-auto">
<code>
<Line label="agent" value="claude-code" accent />
<Line label="tracker" value="github" />
<Line label="workspace" value="worktree" />
<Line label="runtime" value="tmux" />
<Line label="notifier" value="slack" />
<span className="inline-block w-[7px] h-[15px] bg-[var(--accent-orange)] landing-cursor-blink align-middle ml-0.5" />
</code>
</div>
{/* Subtle bottom accent line */}
<div className="h-[1px] bg-gradient-to-r from-transparent via-[rgba(249,115,22,0.2)] to-transparent" />
</div>
</div>
</section>
</div>
</div>
</section>
);
}
/* ── Helper: single YAML keyvalue line ─────────────────────────── */
function Line({
label,
value,
accent = false,
}: {
label: string;
value: string;
accent?: boolean;
}) {
return (
<span className="block">
<span className="text-[var(--landing-muted-dim)]">{label}:</span>{' '}
<span className={accent ? 'text-[var(--accent-green)]' : 'text-[var(--landing-fg)]'}>
{value}
</span>
</span>
);
}

View File

@ -28,19 +28,24 @@ const agents = [
export function LandingAgentsBar() {
return (
<div className="landing-reveal text-center px-6 pt-[60px]">
<div className="text-[0.6875rem] tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-40 mb-5">
<div className="landing-reveal text-center px-6 pt-[60px] pb-4">
<div className="text-[0.6875rem] tracking-[0.2em] uppercase text-[var(--landing-muted)] mb-8 font-mono">
Works with your favorite AI agents
</div>
<div className="flex items-center justify-center gap-6 flex-wrap">
<div className="flex items-center justify-center gap-8 flex-wrap">
{agents.map((agent) => (
<div key={agent.name} className="flex flex-col items-center gap-2">
<img
src={agent.src}
alt={agent.alt}
className="w-8 h-8 rounded-md object-contain"
/>
<div className="text-[0.6875rem] font-mono text-[var(--landing-muted)] opacity-50">
<div
key={agent.name}
className="group flex flex-col items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 hover:bg-[var(--landing-border)]"
>
<div className="w-10 h-10 rounded-lg bg-[var(--landing-border)] flex items-center justify-center p-2 transition-all duration-300 group-hover:bg-[var(--landing-border-hover)] group-hover:shadow-[0_0_20px_rgba(249,115,22,0.08)]">
<img
src={agent.src}
alt={agent.alt}
className="w-full h-full object-contain transition-transform duration-300 group-hover:scale-110"
/>
</div>
<div className="text-[0.6875rem] font-mono text-[var(--landing-muted)] transition-colors duration-300 group-hover:text-[var(--landing-fg-secondary)]">
{agent.name}
</div>
</div>

View File

@ -1,28 +1,37 @@
export function LandingCTA() {
return (
<section className="text-center py-40 px-6 bg-[radial-gradient(ellipse_at_center,rgba(255,255,255,0.015)_0%,transparent_60%)]">
<div className="landing-reveal">
<p className="text-[var(--landing-muted)] opacity-50 text-2xl font-sans font-[680] tracking-tight mb-4">
<section className="relative py-[160px] px-6 text-center overflow-hidden">
{/* Background glow */}
<div className="absolute inset-0" style={{
background: "radial-gradient(ellipse 60% 50% at 50% 50%, rgba(249,115,22,0.06) 0%, transparent 70%)"
}} />
<div className="relative landing-reveal">
<p className="text-[var(--landing-muted)] text-2xl font-bold tracking-tight mb-4">
Stop babysitting.
</p>
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-2px] mb-4">
Start <em className="italic text-[var(--landing-muted)]">orchestrating.</em>
<h2 className="text-[clamp(1.5rem,3vw,2.25rem)] leading-[1.1] tracking-[-1px] font-bold mb-6">
Start <span className="text-gradient-orange">orchestrating.</span>
</h2>
<div className="landing-card inline-flex items-center gap-3 rounded-lg px-6 py-3 font-mono text-[0.9375rem] text-white mb-8">
<span className="text-[var(--landing-muted)] opacity-40">$</span> npm i -g @aoagents/ao
{/* Install command */}
<div className="landing-card inline-flex items-center gap-3 rounded-xl px-7 py-4 font-mono text-[0.9375rem] mb-10" style={{
background: "rgba(0,0,0,0.4)",
backdropFilter: "blur(10px)"
}}>
<span className="text-[var(--accent-orange)]">$</span>
<span className="text-[var(--landing-fg)]">npm i -g @aoagents/ao</span>
</div>
<div className="flex items-center justify-center gap-4 flex-wrap">
<a
href="/docs"
className="landing-card rounded-lg px-6 py-3 text-[0.9375rem] text-[var(--landing-muted)] no-underline transition-colors hover:text-white"
>
Read Docs
<a href="/docs" className="btn-primary">
Read Docs
</a>
<a
href="https://github.com/ComposioHQ/agent-orchestrator"
target="_blank"
rel="noopener noreferrer"
className="liquid-glass-solid rounded-lg px-6 py-3 text-[0.9375rem] no-underline transition-transform hover:scale-[1.03]"
className="btn-secondary"
>
View on GitHub
</a>

View File

@ -11,12 +11,12 @@ export function LandingDifferentiators() {
return (
<section className="py-[100px] px-6 max-w-[72rem] mx-auto">
<div className="landing-reveal">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">
<div className="text-xs tracking-[0.2em] uppercase text-[var(--accent-purple)] mb-4 font-mono font-medium">
Why Agent Orchestrator
</div>
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] mb-6 max-w-[42rem]">
<h2 className="font-sans font-[680] tracking-tight text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] mb-6 max-w-[42rem] text-gradient-purple">
The only{" "}
<em className="italic text-[var(--landing-muted)]">open-source, web-based</em>{" "}
<em className="italic text-[var(--landing-fg-secondary)]">open-source, web-based</em>{" "}
agent orchestrator
</h2>
<p className="text-[0.9375rem] text-[var(--landing-muted)] leading-[1.7] max-w-[36rem] mb-12">
@ -24,17 +24,19 @@ export function LandingDifferentiators() {
your browser, works on any OS, and you can self-host or extend it.
</p>
</div>
<div className="landing-reveal landing-card rounded-2xl overflow-hidden">
<div className="landing-reveal shimmer-card rounded-2xl overflow-hidden">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[var(--landing-border-subtle)]">
<th className="text-left px-6 py-4 font-mono text-[0.625rem] tracking-[0.1em] uppercase text-[var(--landing-muted)] opacity-40">
<tr className="border-b border-[var(--landing-border)]">
<th className="text-left px-6 py-5 font-mono text-[0.625rem] tracking-[0.15em] uppercase text-[var(--landing-muted)]">
Feature
</th>
<th className="text-center px-6 py-4 font-mono text-[0.625rem] tracking-[0.1em] uppercase text-[var(--landing-fg)] opacity-80">
AO
<th className="text-center px-6 py-5">
<span className="font-mono text-[0.625rem] tracking-[0.15em] uppercase text-[var(--accent-orange)] font-medium">
AO
</span>
</th>
<th className="text-center px-6 py-4 font-mono text-[0.625rem] tracking-[0.1em] uppercase text-[var(--landing-muted)] opacity-40">
<th className="text-center px-6 py-5 font-mono text-[0.625rem] tracking-[0.15em] uppercase text-[var(--landing-muted)]">
Others
</th>
</tr>
@ -43,15 +45,19 @@ export function LandingDifferentiators() {
{rows.map((row, i) => (
<tr
key={row.feature}
className={i < rows.length - 1 ? "border-b border-[var(--landing-border-subtle)]" : ""}
className={`transition-colors duration-200 hover:bg-[rgba(255,255,255,0.02)] ${
i < rows.length - 1 ? "border-b border-[var(--landing-border)]" : ""
}`}
>
<td className="px-6 py-3.5 text-[0.8125rem] text-[var(--landing-fg)]/80">
<td className="px-6 py-4 text-[0.8125rem] text-[var(--landing-fg-secondary)]">
{row.feature}
</td>
<td className="px-6 py-3.5 text-center text-[rgba(134,239,172,0.8)]">
<td className="px-6 py-4 text-center">
<span className="inline-flex items-center justify-center w-6 h-6 rounded-full bg-[rgba(34,197,94,0.1)] text-[var(--accent-green)] text-xs font-bold">
</span>
</td>
<td className="px-6 py-3.5 text-center text-[0.75rem] text-[var(--landing-muted)] opacity-40">
<td className="px-6 py-4 text-center text-[0.75rem] text-[var(--landing-muted-dim)]">
{row.others}
</td>
</tr>

View File

@ -3,62 +3,82 @@ const features = [
label: "PARALLEL",
title: "Multi-agent execution",
desc: "Run Claude Code, Codex, Cursor, Aider, and OpenCode simultaneously. Each agent gets its own git worktree, its own branch, its own context.",
icon: "⚡",
accent: "var(--accent-orange)",
span: "col-span-1 md:col-span-2",
},
{
label: "RECOVERY",
title: "Autonomous CI + review handling",
desc: "CI fails? The agent reads the logs and pushes a fix. Review comments land? The agent addresses them. You sleep, your agents ship.",
icon: "🔄",
accent: "var(--accent-green)",
span: "col-span-1 md:col-span-1",
},
{
label: "PLUGINS",
title: "7 swappable slots",
desc: "Runtime, Agent, Workspace, Tracker, SCM, Notifier, Terminal. Use tmux or process. GitHub or GitLab. Slack or webhooks. Swap anything.",
icon: "🧩",
accent: "var(--accent-purple)",
span: "col-span-1 md:col-span-1",
},
{
label: "DASHBOARD",
title: "Real-time Kanban + terminal",
desc: "Every agent's state in one view. Attach to any terminal via the browser. SSE updates every 5 seconds. WebSocket for live terminal I/O.",
icon: "📊",
accent: "var(--accent-cyan)",
span: "col-span-1 md:col-span-2",
},
];
export function LandingFeatures() {
return (
<section className="py-[100px] px-6 max-w-[72rem] mx-auto" id="features">
<section className="py-[140px] px-6 max-w-[72rem] mx-auto" id="features">
<div className="landing-reveal">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted-dim)] mb-6 font-mono">
<div className="font-mono text-[0.75rem] tracking-[0.2em] uppercase text-[var(--accent-orange)] mb-4">
Capabilities
</div>
<h2 className="font-sans font-[680] text-[clamp(2rem,5vw,3.5rem)] leading-[1.1] tracking-[-1.5px] mb-5">
<h2 className="text-[clamp(2rem,5vw,3.5rem)] leading-[1.1] tracking-[-1.5px] font-bold mb-4">
What it does
</h2>
<a href="/docs" className="text-[0.8125rem] text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
Explore full docs and plugin references
<a href="/docs" className="text-[0.9375rem] text-[var(--landing-muted)] no-underline hover:text-[var(--landing-fg)] transition-colors">
Explore full docs and plugin references
</a>
</div>
<div className="flex flex-col gap-0 mt-6">
{features.map((f, i) => (
{/* Bento grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-14">
{features.map((f) => (
<div
key={f.label}
className={`landing-reveal flex flex-col md:flex-row md:items-baseline gap-3 md:gap-12 py-8 ${
i < features.length - 1
? "border-b border-[var(--landing-border-subtle)]"
: ""
}`}
className={`landing-reveal glow-card ${f.span} p-8 group`}
>
<div className="font-mono text-[0.625rem] tracking-[0.12em] text-[var(--landing-muted-dim)] w-20 shrink-0">
{/* Icon */}
<div className="w-12 h-12 rounded-xl flex items-center justify-center text-2xl mb-6" style={{
background: `color-mix(in srgb, ${f.accent} 10%, transparent)`,
}}>
{f.icon}
</div>
{/* Label */}
<div className="font-mono text-[0.625rem] tracking-[0.15em] uppercase mb-3" style={{ color: f.accent }}>
{f.label}
</div>
<div className="flex-1">
<h3 className="font-sans font-[680] text-lg tracking-tight mb-1.5">
{f.title}
</h3>
<p className="text-[var(--landing-muted)] text-[0.875rem] leading-[1.7] max-w-[36rem]">
{f.desc}
</p>
</div>
{/* Title */}
<h3 className="text-xl font-bold tracking-tight mb-3">
{f.title}
</h3>
{/* Description */}
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.75]">
{f.desc}
</p>
</div>
))}
</div>
</section>
);
}
}

View File

@ -50,7 +50,7 @@ function TerminalTyping() {
}, []);
return (
<div ref={ref} className="px-5 py-4 font-mono text-[0.8125rem] leading-[1.9] text-left min-h-[280px]">
<div ref={ref} className="px-6 py-5 font-mono text-[0.8125rem] leading-[2] text-left min-h-[280px]">
{terminalLines.slice(0, visibleCount).map((line, i) => {
if (line.type === "blank") return <div key={i}>&nbsp;</div>;
@ -58,10 +58,10 @@ function TerminalTyping() {
line.type === "cmd"
? "text-[var(--landing-fg)]"
: line.type === "success"
? "text-[rgba(134,239,172,0.8)]"
? "text-[var(--accent-green)]"
: line.type === "status"
? "text-[var(--landing-muted)]"
: "text-[var(--landing-muted)] opacity-50";
? "text-[var(--landing-fg-secondary)]"
: "text-[var(--landing-muted)]";
return (
<div
@ -69,17 +69,20 @@ function TerminalTyping() {
className={`${colorClass} landing-line-appear`}
>
{line.type === "cmd" && (
<span className="text-[var(--landing-muted)] opacity-50">$ </span>
<span className="text-[var(--accent-orange)] mr-1">$</span>
)}
{line.type === "status" && (
<span className="landing-agent-dot mr-1.5 inline-block" />
<span className="landing-pulse-dot mr-1.5 inline-block w-1.5 h-1.5 rounded-full bg-[var(--accent-green)]" />
)}
{line.type === "cmd" ? line.text.slice(2) : line.type === "status" ? line.text.slice(2) : line.text}
{line.type === "success" && (
<span className="text-[var(--accent-green)] mr-1"></span>
)}
{line.type === "cmd" ? line.text.slice(2) : line.type === "status" ? line.text.slice(2) : line.type === "success" ? line.text.slice(2) : line.text}
</div>
);
})}
{visibleCount > 0 && visibleCount < terminalLines.length && (
<span className="inline-block w-2 h-4 bg-[var(--landing-fg)] opacity-70 landing-cursor-blink" />
<span className="inline-block w-2 h-4 bg-[var(--accent-orange)] landing-cursor-blink" />
)}
</div>
);
@ -87,50 +90,91 @@ function TerminalTyping() {
export function LandingHero({ starsLabel }: LandingHeroProps) {
return (
<div className="relative min-h-screen overflow-hidden">
<div className="absolute inset-0 z-[1] landing-hero-grid" />
<section className="relative z-10 flex flex-col items-center justify-center text-center px-6 pt-32 pb-20 min-h-screen">
<div className="landing-fade-rise landing-card inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs text-[var(--landing-muted)] mb-8">
<span className="w-1.5 h-1.5 rounded-full bg-[rgba(134,239,172,0.7)]" />
Open Source · MIT Licensed · {starsLabel} GitHub Stars
<div className="relative min-h-screen overflow-hidden flex items-center justify-center">
{/* Animated mesh gradient background */}
<div className="mesh-gradient">
<div className="orb orb-1" />
<div className="orb orb-2" />
<div className="orb orb-3" />
</div>
{/* Grid pattern overlay */}
<div className="absolute inset-0 grid-pattern z-[1]" />
{/* Radial vignette */}
<div className="absolute inset-0 z-[2]" style={{
background: "radial-gradient(ellipse 80% 60% at 50% 40%, transparent 0%, var(--landing-bg) 70%)"
}} />
<section className="relative z-10 flex flex-col items-center justify-center text-center px-6 pt-28 pb-16 min-h-screen max-w-[72rem] mx-auto">
{/* Badge */}
<div className="landing-fade-up landing-card inline-flex items-center gap-2 rounded-full px-4 py-2 text-xs mb-8" style={{
background: "rgba(249,115,22,0.08)",
borderColor: "rgba(249,115,22,0.2)"
}}>
<span className="landing-pulse-dot w-2 h-2 rounded-full bg-[var(--accent-green)]" />
<span className="text-[var(--landing-fg-secondary)]">
Open Source · MIT ·{" "}
<span className="text-[var(--accent-orange)] font-semibold">{starsLabel}</span> GitHub Stars
</span>
</div>
<h1 className="landing-fade-rise font-sans font-[680] text-[clamp(1.75rem,4vw,2.75rem)] leading-[1] tracking-[-2px] max-w-[56rem]">
Run 30 AI agents in parallel.
{/* Main heading */}
<h1 className="landing-fade-up-d1 text-[clamp(2.5rem,6vw,4.5rem)] leading-[1.05] tracking-[-2.5px] max-w-[56rem] font-bold">
Run 30 AI agents
<br />
<span className="text-gradient-orange">in parallel.</span>
<br />
<span className="text-[var(--landing-muted)]">One dashboard.</span>
</h1>
<p className="landing-fade-rise-d1 text-[var(--landing-muted)] text-[0.9375rem] max-w-[38rem] mt-6 leading-[1.7]">
Agent Orchestrator spawns Claude Code, Codex, Cursor, Aider, and OpenCode
{/* Subtitle */}
<p className="landing-fade-up-d2 text-[var(--landing-muted)] text-[1.0625rem] max-w-[40rem] mt-7 leading-[1.75]">
Agent Orchestrator spawns Claude Code, Codex, Cursor, Aider &amp; OpenCode
in isolated git worktrees. Each agent gets its own branch, creates PRs,
fixes CI, and addresses reviews autonomously.
fixes CI, and addresses reviews <span className="text-[var(--landing-fg-secondary)] font-medium">autonomously</span>.
</p>
<div className="landing-fade-rise-d2 flex items-center gap-3 mt-10 flex-wrap justify-center">
<div className="landing-card rounded-lg px-6 py-3 font-mono text-sm">
<span className="text-[var(--landing-muted)] opacity-40">$</span> npx @aoagents/ao start
{/* CTA buttons */}
<div className="landing-fade-up-d3 flex items-center gap-3 mt-10 flex-wrap justify-center">
<div className="landing-card rounded-xl px-6 py-3.5 font-mono text-sm flex items-center gap-2" style={{
background: "rgba(0,0,0,0.4)",
backdropFilter: "blur(10px)"
}}>
<span className="text-[var(--accent-orange)]">$</span>
<span>npx @aoagents/ao start</span>
</div>
<a
href="/docs"
className="landing-card rounded-lg px-6 py-3 text-sm no-underline transition-colors hover:text-white"
>
Read Docs
<a href="/docs" className="btn-primary">
Get Started
</a>
<a
href="https://github.com/ComposioHQ/agent-orchestrator"
target="_blank"
rel="noopener noreferrer"
className="liquid-glass-solid rounded-lg px-6 py-3 text-sm no-underline transition-colors"
className="btn-secondary"
>
View on GitHub
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
GitHub
</a>
</div>
<div className="landing-fade-rise-d2 w-full max-w-[52rem] mt-16">
<div className="landing-card rounded-2xl overflow-hidden">
<div className="flex items-center gap-2 px-4 py-3 border-b border-[var(--landing-border-subtle)]">
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.08)]" />
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.08)]" />
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.08)]" />
<span className="ml-2 font-mono text-[0.625rem] text-[var(--landing-muted)] opacity-40">
{/* Terminal demo */}
<div className="landing-fade-up-d4 w-full max-w-[56rem] mt-16">
<div className="landing-card rounded-2xl overflow-hidden" style={{
background: "rgba(0,0,0,0.5)",
backdropFilter: "blur(20px)",
boxShadow: "0 0 60px -15px rgba(249,115,22,0.1), 0 0 120px -30px rgba(139,92,246,0.08)"
}}>
{/* Terminal header */}
<div className="flex items-center gap-3 px-5 py-3.5 border-b" style={{ borderColor: "var(--landing-border)" }}>
<div className="flex gap-2">
<div className="w-3 h-3 rounded-full bg-[#ff5f57]" />
<div className="w-3 h-3 rounded-full bg-[#febc2e]" />
<div className="w-3 h-3 rounded-full bg-[#28c840]" />
</div>
<span className="font-mono text-[0.6875rem] text-[var(--landing-muted)] opacity-60">
agent-orchestrator my-saas-app
</span>
</div>

View File

@ -1,156 +1,136 @@
"use client";
import { useEffect, useRef, useState } from "react";
const stepDetails = [
{
num: "01",
title: "Configure &",
titleItalic: "assign",
desc: "Point Agent Orchestrator at your repo with a YAML config. Choose your agent, set up trackers and notifiers. One file, full control.",
code: `# agent-orchestrator.yaml
agent: claude-code
tracker: github
workspace: worktree
runtime: tmux
notifier: slack`,
accent: "var(--accent-orange)",
},
{
num: "02",
title: "Agents",
titleItalic: "work",
desc: "Each agent clones into a git worktree, reads the issue, plans, writes code, runs tests, and opens a PR — all autonomously.",
code: `$ ao spawn 42
Spawning claude-code for #42
Worktree: .ao/worktrees/feat-auth
Agent reading issue... writing code...
PR #312 opened feat/auth-flow`,
accent: "var(--accent-blue)",
},
{
num: "03",
title: "PRs",
titleItalic: "land",
desc: "Agents handle CI failures, address review comments, and keep pushing until merge. You just review and approve.",
code: `✓ CI passing (48/48 tests)
Review comments addressed
Branch up-to-date with main
Ready for merge
Merged into main`,
accent: "var(--accent-green)",
},
];
export function LandingHowItWorks() {
return (
<section className="py-[120px] px-6 max-w-[72rem] mx-auto" id="how">
<section className="py-[140px] px-6 max-w-[72rem] mx-auto" id="how">
<div className="landing-reveal">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">
<div className="font-mono text-[0.75rem] tracking-[0.2em] uppercase text-[var(--accent-cyan)] mb-4">
Process
</div>
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] mb-6">
<h2 className="text-[clamp(1.5rem,3vw,2.25rem)] leading-[1.1] tracking-[-1px] font-bold">
Three steps to{" "}
<em className="italic text-[var(--landing-muted)]">orchestration</em>
<span className="text-gradient-purple">orchestration</span>
</h2>
</div>
<div className="mt-20 flex flex-col gap-20">
{/* Step 1 */}
<div className="landing-reveal grid grid-cols-1 md:grid-cols-2 gap-12 items-center">
<div>
<div className="font-mono text-xs tracking-[0.1em] text-[var(--landing-muted)] opacity-50 mb-4">01</div>
<h3 className="font-sans font-[680] tracking-tight text-lg tracking-tight mb-4">
Configure &amp; <em className="italic text-[var(--landing-muted)]">assign</em>
</h3>
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.7] max-w-[28rem]">
Point Agent Orchestrator at your repo with a YAML config. Choose your agent, set up trackers and notifiers. One file, full control.
</p>
</div>
<div className="landing-card rounded-2xl p-8 min-h-[260px]">
<div className="bg-black/40 rounded-xl overflow-hidden font-mono text-[0.8125rem]">
<div className="flex items-center gap-2 px-4 py-3 bg-[var(--landing-surface)]">
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.12)]" />
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.12)]" />
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.12)]" />
</div>
<div className="px-5 py-4 leading-[1.8]">
<div><span className="text-[var(--landing-muted)]">$</span> <span className="text-white">ao batch-spawn 42 43 44 45 46</span></div>
<div className="text-[var(--landing-muted)] opacity-60">&nbsp;</div>
<div className="text-[var(--landing-muted)] opacity-60"> Loading config from agent-orchestrator.yaml</div>
<div className="text-[var(--landing-muted)] opacity-60"> Resolving 5 issues from GitHub</div>
<div className="text-[var(--landing-muted)] opacity-60"> Spawning sessions in worktrees...</div>
<div className="text-[rgba(134,239,172,0.8)]"> Session s-001 spawned issue #42</div>
<div className="text-[rgba(134,239,172,0.8)]"> Session s-002 spawned issue #43</div>
<div className="text-[rgba(134,239,172,0.8)]"> Session s-003 spawned issue #44</div>
<div className="text-[rgba(134,239,172,0.8)]"> Session s-004 spawned issue #45</div>
<div className="text-[rgba(134,239,172,0.8)]"> Session s-005 spawned issue #46</div>
<div className="text-[var(--landing-muted)] opacity-60">&nbsp;</div>
<div><span className="landing-agent-dot mr-1.5" /><span className="text-[var(--landing-muted)] opacity-60">5 agents working · Dashboard http://localhost:3000</span></div>
</div>
</div>
</div>
</div>
{/* Step 2 */}
<div className="landing-reveal grid grid-cols-1 md:grid-cols-2 gap-12 items-center">
<div className="md:order-2">
<div className="font-mono text-xs tracking-[0.1em] text-[var(--landing-muted)] opacity-50 mb-4">02</div>
<h3 className="font-sans font-[680] tracking-tight text-lg tracking-tight mb-4">
Agents <em className="italic text-[var(--landing-muted)]">work</em>
</h3>
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.7] max-w-[28rem]">
Each agent spawns in an isolated worktree. They write code, create PRs, run tests, and fix failures. Monitor everything from the live dashboard, or let them run.
</p>
</div>
<div className="landing-card rounded-2xl p-8 min-h-[260px] md:order-1">
<div className="rounded-2xl overflow-hidden bg-black/30">
<div className="flex items-center gap-2 px-4 py-2.5 bg-[var(--landing-card-bg)] border-b border-[var(--landing-border-subtle)]">
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.12)]" />
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.12)]" />
<div className="w-2.5 h-2.5 rounded-full bg-[rgba(255,240,220,0.12)]" />
<span className="text-[0.6875rem] text-[var(--landing-muted)] opacity-50 ml-2">my-saas-app · 5 sessions</span>
</div>
<div className="grid grid-cols-4 gap-2 p-3">
<DashColumn title="Working" cards={[
{ title: "Add user auth flow", meta: "#42 · feat/auth", agent: "claude-code" },
{ title: "Fix pagination bug", meta: "#43 · fix/pagination", agent: "codex" },
]} />
<DashColumn title="Pending" cards={[
{ title: "Add rate limiting", meta: "#44 · PR #312", agent: "aider" },
]} />
<DashColumn title="Review" cards={[
{ title: "Update API tests", meta: "#45 · PR #310", agent: "claude-code", amber: true },
]} />
<DashColumn title="Merged" cards={[
{ title: "Refactor DB layer", meta: "#46 · PR #308", agent: "opencode", done: true },
]} />
</div>
</div>
</div>
</div>
{/* Step 3 */}
<div className="landing-reveal grid grid-cols-1 md:grid-cols-2 gap-12 items-center">
<div>
<div className="font-mono text-xs tracking-[0.1em] text-[var(--landing-muted)] opacity-50 mb-4">03</div>
<h3 className="font-sans font-[680] tracking-tight text-lg tracking-tight mb-4">
PRs <em className="italic text-[var(--landing-muted)]">land</em>
</h3>
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.7] max-w-[28rem]">
Agents create pull requests, address review comments, fix CI failures, and get them to mergeable state. Your morning starts with merged PRs, not a backlog.
</p>
</div>
<div className="landing-card rounded-2xl p-6 min-h-[260px]">
<div className="flex flex-col gap-2.5">
{[
{ branch: "feat/user-auth", title: "Add user authentication flow" },
{ branch: "fix/pagination-offset", title: "Fix off-by-one in cursor pagination" },
{ branch: "feat/rate-limiting", title: "Add Redis-backed rate limiter" },
{ branch: "refactor/db-layer", title: "Extract repository pattern from services" },
].map((pr) => (
<div key={pr.branch} className="bg-[var(--landing-surface)] border border-[var(--landing-border-subtle)] rounded-xl px-5 py-4 flex items-center justify-between">
<div className="flex flex-col gap-1">
<div className="font-mono text-xs text-[var(--landing-fg)]/70">{pr.branch}</div>
<div className="text-[0.8125rem] text-[var(--landing-muted)]">{pr.title}</div>
</div>
<div className="font-mono text-[0.625rem] tracking-[0.05em] px-3 py-1 rounded-full bg-[rgba(134,239,172,0.08)] text-[rgba(134,239,172,0.7)]">
Merged
</div>
</div>
))}
</div>
</div>
</div>
<div className="flex flex-col gap-6 mt-16">
{stepDetails.map((step) => (
<StepCard key={step.num} step={step} />
))}
</div>
</section>
);
}
interface DashCardData {
title: string;
meta: string;
agent: string;
amber?: boolean;
done?: boolean;
}
function StepCard({ step }: { step: typeof stepDetails[0] }) {
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setVisible(true);
observer.disconnect();
}
},
{ threshold: 0.2 }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
function DashColumn({ title, cards }: { title: string; cards: DashCardData[] }) {
return (
<div>
<div className="font-mono text-[0.625rem] tracking-[0.1em] uppercase text-[var(--landing-muted)] opacity-40 px-2 mb-1">
{title}
</div>
{cards.map((card) => (
<div key={card.meta} className="bg-[var(--landing-surface)] border border-[var(--landing-border-subtle)] rounded-lg p-2.5 mb-1.5 text-[0.6875rem]">
<div className="text-[var(--landing-fg)]/70 mb-1">{card.title}</div>
<div className="font-mono text-[0.5625rem] text-[var(--landing-muted)] opacity-50">{card.meta}</div>
<div className="flex items-center gap-1 mt-1 font-mono text-[0.5625rem] text-[var(--landing-muted)] opacity-60">
{card.done ? (
<span></span>
) : (
<span className="inline-block w-1.5 h-1.5 rounded-full" style={{ background: card.amber ? "rgba(251,191,36,0.7)" : "rgba(134,239,172,0.7)" }} />
)}
{card.agent}
<div
ref={ref}
className={`glow-card p-8 md:p-10 transition-all duration-700 ${
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
}`}
style={{ transitionDelay: "0.1s" }}
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-10 items-start">
{/* Text */}
<div>
<div className="flex items-center gap-3 mb-5">
<span className="font-mono text-[0.75rem] px-2.5 py-1 rounded-md" style={{
background: `color-mix(in srgb, ${step.accent} 10%, transparent)`,
color: step.accent,
}}>
{step.num}
</span>
</div>
<h3 className="text-2xl font-bold tracking-tight mb-4">
{step.title}
<span className="text-[var(--landing-muted)] italic"> {step.titleItalic}</span>
</h3>
<p className="text-[var(--landing-muted)] text-[0.9375rem] leading-[1.8]">
{step.desc}
</p>
</div>
))}
{/* Code preview */}
<div className="rounded-xl overflow-hidden" style={{
background: "rgba(0,0,0,0.4)",
border: "1px solid var(--landing-border)",
}}>
<div className="flex items-center gap-2 px-4 py-2.5" style={{
borderBottom: "1px solid var(--landing-border)"
}}>
<div className="w-2.5 h-2.5 rounded-full bg-[#ff5f57]" />
<div className="w-2.5 h-2.5 rounded-full bg-[#febc2e]" />
<div className="w-2.5 h-2.5 rounded-full bg-[#28c840]" />
</div>
<pre className="px-5 py-4 font-mono text-[0.75rem] leading-[2] text-[var(--landing-muted)] overflow-x-auto">
{step.code.split("\n").map((line, i) => (
<div key={i} className={line.startsWith("✓") ? "text-[var(--accent-green)]" : line.startsWith("$") ? "text-[var(--landing-fg-secondary)]" : line.startsWith("→") ? "text-[var(--accent-orange)]" : ""}>
{line || "\u00A0"}
</div>
))}
</pre>
</div>
</div>
</div>
);
}

View File

@ -26,38 +26,37 @@ function GithubIcon() {
export function LandingNav() {
return (
<nav className="fixed top-0 left-0 right-0 z-50 flex items-center justify-between px-8 py-6 max-w-[80rem] mx-auto bg-[var(--landing-bg)]/90 backdrop-blur-sm">
<nav className="fixed top-4 left-1/2 -translate-x-1/2 z-50 flex items-center gap-1 px-2 py-2 rounded-full" style={{
background: "rgba(10,10,10,0.7)",
backdropFilter: "blur(20px)",
border: "1px solid rgba(255,255,255,0.06)",
boxShadow: "0 8px 32px rgba(0,0,0,0.4)"
}}>
<a
href="#"
className="inline-flex items-center gap-2 text-base font-semibold text-white no-underline font-sans font-[680] tracking-tight"
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-sm font-semibold text-white no-underline hover:bg-white/5 transition-colors"
>
<img src="/ao-logo.svg" alt="" aria-hidden="true" width={28} height={28} className="h-7 w-7" />
Agent Orchestrator
<img src="/ao-logo.svg" alt="" aria-hidden="true" width={22} height={22} className="h-[22px] w-[22px]" />
<span className="hidden md:inline">Agent Orchestrator</span>
</a>
<ul className="hidden md:flex items-center gap-8 list-none">
<li>
<a href="/docs" className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
Docs
</a>
</li>
<li>
<a href="#features" className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
Features
</a>
</li>
<li>
<a href="#how" className="text-sm text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
How It Works
</a>
</li>
</ul>
<div className="flex items-center gap-2">
<div className="hidden md:flex items-center gap-0.5 ml-2">
<a href="/docs" className="px-3 py-1.5 rounded-full text-sm text-[var(--landing-muted)] no-underline hover:text-white hover:bg-white/5 transition-all">
Docs
</a>
<a href="#features" className="px-3 py-1.5 rounded-full text-sm text-[var(--landing-muted)] no-underline hover:text-white hover:bg-white/5 transition-all">
Features
</a>
<a href="#how" className="px-3 py-1.5 rounded-full text-sm text-[var(--landing-muted)] no-underline hover:text-white hover:bg-white/5 transition-all">
How It Works
</a>
</div>
<div className="flex items-center gap-0.5 ml-2">
<a
href="https://x.com/aoagents"
target="_blank"
rel="noopener noreferrer"
aria-label="X (Twitter)"
className="inline-flex h-9 w-9 items-center justify-center rounded-md text-white/80 transition-colors hover:text-white"
className="inline-flex h-8 w-8 items-center justify-center rounded-full text-[var(--landing-muted)] transition-all hover:text-white hover:bg-white/5"
>
<XIcon />
</a>
@ -66,7 +65,7 @@ export function LandingNav() {
target="_blank"
rel="noopener noreferrer"
aria-label="Discord"
className="inline-flex h-9 w-9 items-center justify-center rounded-md text-white/80 transition-colors hover:text-white"
className="inline-flex h-8 w-8 items-center justify-center rounded-full text-[var(--landing-muted)] transition-all hover:text-white hover:bg-white/5"
>
<DiscordIcon />
</a>
@ -75,7 +74,7 @@ export function LandingNav() {
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub"
className="inline-flex h-9 w-9 items-center justify-center rounded-md text-white/80 transition-colors hover:text-white"
className="inline-flex h-8 w-8 items-center justify-center rounded-full text-[var(--landing-muted)] transition-all hover:text-white hover:bg-white/5"
>
<GithubIcon />
</a>

View File

@ -6,37 +6,50 @@ const steps = [
export function LandingQuickStart() {
return (
<section className="py-[120px] px-6 max-w-[72rem] mx-auto bg-[radial-gradient(ellipse_at_bottom,rgba(255,255,255,0.015)_0%,transparent_60%)]">
<div className="landing-reveal">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">
<section className="py-[120px] px-6 max-w-[72rem] mx-auto relative">
{/* Subtle radial glow background */}
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(139,92,246,0.03)_0%,transparent_60%)] pointer-events-none" />
<div className="landing-reveal relative">
<div className="text-xs tracking-[0.2em] uppercase text-[var(--accent-cyan)] mb-4 font-mono font-medium">
Get started in 60 seconds
</div>
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] mb-6">
<h2 className="font-sans font-[680] tracking-tight text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px]">
Three commands to{" "}
<em className="italic text-[var(--landing-muted)]">launch</em>
<span className="text-gradient-purple">launch</span>
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mt-12">
{steps.map((s) => (
<div key={s.num} className="landing-reveal landing-card rounded-2xl p-7">
<div className="font-mono text-[0.625rem] tracking-[0.1em] text-[var(--landing-muted)] opacity-40 mb-3">
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mt-12 stagger-children relative">
{steps.map((s, idx) => (
<div key={s.num} className="landing-reveal shimmer-card rounded-2xl p-7 group relative">
{/* Step number accent */}
<div className="absolute top-0 right-6 text-[6rem] font-sans font-[800] leading-none text-[rgba(255,255,255,0.02)] select-none pointer-events-none">
{idx + 1}
</div>
<div className="font-mono text-[0.625rem] tracking-[0.15em] text-[var(--accent-cyan)] mb-4 uppercase">
{s.num}
</div>
<h3 className="font-sans font-[680] tracking-tight text-xl mb-2 tracking-tight">
<h3 className="font-sans font-[680] tracking-tight text-xl mb-2 tracking-tight text-[var(--landing-fg)] group-hover:text-[var(--accent-cyan)] transition-colors duration-300">
{s.title}
</h3>
<p className="text-[var(--landing-muted)] text-[0.8125rem] leading-[1.6] mb-4">
<p className="text-[var(--landing-muted)] text-[0.8125rem] leading-[1.6] mb-5">
{s.desc}
</p>
<div className="font-mono text-xs text-[var(--landing-fg)]/70 bg-black/30 px-3.5 py-2.5 rounded-lg">
<span className="text-[var(--landing-muted)] opacity-40">$</span> {s.cmd}
<div className="font-mono text-xs text-[var(--landing-fg-secondary)] bg-[rgba(0,0,0,0.4)] px-4 py-3 rounded-lg border border-[var(--landing-border)]">
<span className="text-[var(--accent-cyan)] select-none"> </span>
<span>{s.cmd}</span>
</div>
</div>
))}
</div>
<div className="landing-reveal mt-8 text-center">
<a href="/docs/" className="landing-card inline-flex rounded-lg px-4 py-2 text-[0.8125rem] text-[var(--landing-muted)] no-underline hover:text-white transition-colors">
<div className="landing-reveal mt-10 text-center relative">
<a
href="/docs/"
className="btn-secondary inline-flex items-center gap-2 rounded-xl px-6 py-3 text-[0.8125rem] no-underline"
>
Explore docs for setup and workflows
<span className="text-[var(--landing-muted)] transition-transform duration-200 group-hover:translate-x-0.5"></span>
</a>
</div>
</section>

View File

@ -1,51 +1,312 @@
"use client";
import { useEffect, useRef, useState, useCallback } from "react";
import type { GitHubRepoStats } from "@/lib/github-repo";
interface LandingStatsProps {
stats: GitHubRepoStats;
}
/* ── Animated counter hook ──────────────────────────────────── */
function useAnimatedCounter(
target: number,
duration = 1800,
enabled = true,
) {
const [value, setValue] = useState(enabled ? 0 : target);
const raf = useRef<number>(0);
useEffect(() => {
if (!enabled) return;
const start = performance.now();
const tick = (now: number) => {
const t = Math.min((now - start) / duration, 1);
// ease-out cubic
const eased = 1 - Math.pow(1 - t, 3);
setValue(Math.round(eased * target));
if (t < 1) raf.current = requestAnimationFrame(tick);
};
raf.current = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf.current);
}, [target, duration, enabled]);
return value;
}
/* ── Stat card ──────────────────────────────────────────────── */
function StatCard({
raw,
label,
icon,
delay,
visible,
}: {
raw: number;
label: string;
icon: React.ReactNode;
delay: number;
visible: boolean;
}) {
const display = useAnimatedCounter(raw, 2000, visible);
const formatted = display.toLocaleString();
return (
<div
className="landing-card glow-card group relative flex flex-col items-center justify-center rounded-2xl py-9 px-5
transition-all duration-500 ease-out
hover:scale-[1.04] hover:shadow-[0_0_40px_rgba(249,115,22,0.08)]"
style={{
opacity: visible ? 1 : 0,
transform: visible ? "translateY(0)" : "translateY(24px)",
transitionDelay: `${delay}ms`,
}}
>
{/* Icon */}
<span className="mb-3 text-[var(--landing-muted)] opacity-50 transition-opacity duration-300 group-hover:opacity-90">
{icon}
</span>
{/* Number — gradient text */}
<span
className="font-sans font-[800] leading-none tracking-tight
text-[clamp(2.25rem,4.5vw,3.5rem)]"
style={{
background:
"linear-gradient(135deg, var(--landing-fg) 30%, var(--accent-orange) 100%)",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text",
}}
>
{formatted}
</span>
{/* Label */}
<span
className="mt-2 text-[0.8rem] font-medium tracking-wide uppercase"
style={{ color: "var(--landing-muted)" }}
>
{label}
</span>
{/* Subtle border glow on hover */}
<div
className="pointer-events-none absolute inset-0 rounded-2xl opacity-0 transition-opacity duration-500 group-hover:opacity-100"
style={{
background:
"linear-gradient(135deg, rgba(249,115,22,0.06) 0%, transparent 60%)",
}}
/>
</div>
);
}
/* ── Main section ───────────────────────────────────────────── */
export function LandingStats({ stats }: LandingStatsProps) {
const cards = [
{ number: stats.stars.toLocaleString(), label: "GitHub Stars" },
{ number: stats.forks.toLocaleString(), label: "Forks" },
{ number: stats.openIssues.toLocaleString(), label: "Open Issues" },
{ number: stats.watchers.toLocaleString(), label: "Watchers" },
const sectionRef = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
const observerCallback = useCallback(
(entries: IntersectionObserverEntry[]) => {
if (entries[0]?.isIntersecting) setVisible(true);
},
[],
);
useEffect(() => {
const el = sectionRef.current;
if (!el) return;
const obs = new IntersectionObserver(observerCallback, {
threshold: 0.2,
});
obs.observe(el);
return () => obs.disconnect();
}, [observerCallback]);
const cards: {
raw: number;
label: string;
icon: React.ReactNode;
}[] = [
{
raw: stats.stars,
label: "GitHub Stars",
icon: <StarIcon />,
},
{
raw: stats.forks,
label: "Forks",
icon: <ForkIcon />,
},
{
raw: stats.openIssues,
label: "Open Issues",
icon: <IssueIcon />,
},
{
raw: stats.watchers,
label: "Watchers",
icon: <EyeIcon />,
},
];
return (
<section className="py-20 px-6 max-w-[72rem] mx-auto">
<section ref={sectionRef} className="py-20 px-6 max-w-[72rem] mx-auto">
{/* ── Stat cards grid ── */}
<div className="landing-reveal grid grid-cols-2 md:grid-cols-4 gap-5">
{cards.map((stat) => (
<div
{cards.map((stat, i) => (
<StatCard
key={stat.label}
className="landing-card rounded-2xl py-8 px-6 text-center"
>
<div className="font-sans font-[680] tracking-tight text-[clamp(2rem,4vw,3rem)] tracking-tight mb-1">
{stat.number}
</div>
<div className="text-xs text-[var(--landing-muted)] opacity-60">
{stat.label}
</div>
</div>
raw={stat.raw}
label={stat.label}
icon={stat.icon}
delay={i * 120}
visible={visible}
/>
))}
</div>
<div className="landing-reveal text-center mt-8">
{/* ── Bottom badges ── */}
<div
className="landing-reveal mt-10 flex flex-col sm:flex-row items-center justify-center gap-4"
style={{
opacity: visible ? 1 : 0,
transform: visible ? "translateY(0)" : "translateY(16px)",
transition: "opacity 0.6s ease, transform 0.6s ease",
transitionDelay: "600ms",
}}
>
{/* GitHub stars link */}
<a
href="https://github.com/ComposioHQ/agent-orchestrator"
target="_blank"
rel="noopener noreferrer"
className="landing-card inline-flex items-center gap-2 rounded-lg px-4 py-2 text-[0.8125rem] text-[var(--landing-muted)] no-underline transition-all hover:text-white mb-3"
className="landing-card inline-flex items-center gap-2.5 rounded-xl px-5 py-2.5
text-[0.8125rem] no-underline transition-all duration-300
hover:scale-[1.03] hover:shadow-[0_0_24px_rgba(249,115,22,0.12)]"
style={{
color: "var(--landing-muted)",
border: "1px solid var(--landing-border)",
}}
>
<span className="text-[rgba(251,191,36,0.7)] text-sm"></span>
<span className="font-mono text-xs text-[var(--landing-fg)] opacity-80">{stats.stars.toLocaleString()}</span>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="currentColor"
style={{ color: "var(--landing-fg)", opacity: 0.7 }}
>
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
</svg>
<span
className="font-mono text-xs font-semibold"
style={{ color: "var(--landing-fg)", opacity: 0.9 }}
>
{stats.stars.toLocaleString()}
</span>
<span>stars on GitHub</span>
</a>
<br />
<div className="landing-card inline-flex items-center gap-2 rounded-lg px-4 py-2 text-[0.8125rem] text-[var(--landing-muted)]">
<span className="w-2 h-2 rounded-full bg-[rgba(134,239,172,0.7)] animate-pulse" />
Built with itself this repo is managed by Agent Orchestrator
{/* Built with itself badge */}
<div
className="landing-card inline-flex items-center gap-2.5 rounded-xl px-5 py-2.5
text-[0.8125rem] transition-all duration-300"
style={{
color: "var(--landing-muted)",
border: "1px solid var(--landing-border)",
}}
>
<span className="relative flex h-2 w-2">
<span
className="absolute inline-flex h-full w-full animate-ping rounded-full opacity-75"
style={{ backgroundColor: "var(--accent-green)" }}
/>
<span
className="relative inline-flex h-2 w-2 rounded-full"
style={{ backgroundColor: "var(--accent-green)" }}
/>
</span>
<span>
Built with itself this repo is managed by Agent&nbsp;Orchestrator
</span>
</div>
</div>
</section>
);
}
}
/* ── Inline SVG icons ───────────────────────────────────────── */
function StarIcon() {
return (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
</svg>
);
}
function ForkIcon() {
return (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="18" r="3" />
<circle cx="6" cy="6" r="3" />
<circle cx="18" cy="6" r="3" />
<path d="M18 9v2c0 .6-.4 1-1 1H7c-.6 0-1-.4-1-1V9" />
<path d="M12 12v3" />
</svg>
);
}
function IssueIcon() {
return (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="10" />
<path d="M12 8v4" />
<path d="M12 16h.01" />
</svg>
);
}
function EyeIcon() {
return (
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z" />
<circle cx="12" cy="12" r="3" />
</svg>
);
}

View File

@ -26,26 +26,29 @@ export function LandingTestimonials() {
return (
<section className="py-20 px-6 pb-[120px] max-w-[72rem] mx-auto">
<div className="landing-reveal">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted)] opacity-60 mb-6">
<div className="text-xs tracking-[0.2em] uppercase text-[var(--accent-blue)] mb-4 font-mono font-medium">
What engineers say
</div>
<h2 className="font-sans font-[680] tracking-tight font-normal text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] mb-6">
Trusted by <em className="italic text-[var(--landing-muted)]">builders</em>
<h2 className="font-sans font-[680] tracking-tight text-[clamp(1.375rem,3vw,2rem)] leading-[1.05] tracking-[-1.5px] text-gradient-blue">
Trusted by <em className="italic text-[var(--landing-fg-secondary)]">builders</em>
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mt-12">
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mt-12 stagger-children">
{testimonials.map((t) => (
<div key={t.initials} className="landing-reveal landing-card rounded-2xl p-8">
<p className="text-[0.9375rem] text-[var(--landing-fg)]/80 leading-[1.7] mb-5 italic font-sans font-[680] tracking-tight">
&ldquo;{t.quote}&rdquo;
</p>
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-full bg-[var(--landing-surface)] flex items-center justify-center text-xs font-semibold text-[var(--landing-muted)]">
<div key={t.initials} className="landing-reveal glow-card rounded-2xl p-8 flex flex-col justify-between">
<div>
<div className="text-[var(--accent-blue)] text-2xl mb-4 leading-none select-none">&ldquo;</div>
<p className="text-[0.9375rem] text-[var(--landing-fg-secondary)] leading-[1.7] mb-6 italic">
{t.quote}
</p>
</div>
<div className="flex items-center gap-3 pt-4 border-t border-[var(--landing-border)]">
<div className="w-9 h-9 rounded-full bg-[rgba(59,130,246,0.08)] border border-[rgba(59,130,246,0.15)] flex items-center justify-center text-xs font-mono font-semibold text-[var(--accent-blue)]">
{t.initials}
</div>
<div>
<div className="text-[0.8125rem] font-medium">{t.name}</div>
<div className="text-[0.6875rem] text-[var(--landing-muted)] opacity-60">
<div className="text-[0.8125rem] font-medium text-[var(--landing-fg)]">{t.name}</div>
<div className="text-[0.6875rem] text-[var(--landing-muted)]">
{t.role}
</div>
</div>

View File

@ -23,45 +23,47 @@ export function LandingUseCases() {
return (
<section className="py-[100px] px-6 max-w-[72rem] mx-auto">
<div className="landing-reveal">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted-dim)] mb-6 font-mono">
<div className="text-xs tracking-[0.2em] uppercase text-[var(--accent-orange)] mb-4 font-mono font-medium">
Use cases
</div>
<h2 className="font-sans font-[680] text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] mb-16">
<h2 className="font-sans font-[680] text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] text-gradient-orange">
What teams run with AO
</h2>
</div>
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-6 mt-12 stagger-children">
{cases.map((c) => (
<div
key={c.scenario}
className="landing-reveal landing-card rounded-2xl p-8"
className="landing-reveal glow-card rounded-2xl p-8 group"
>
<h3 className="font-sans font-[680] text-lg tracking-tight mb-4">
<h3 className="font-sans font-[680] text-lg tracking-tight text-[var(--landing-fg)] mb-6">
{c.scenario}
</h3>
<div className="grid grid-cols-1 md:grid-cols-[1fr_auto_1fr_1fr] gap-4 md:gap-6 items-start">
<div>
<div className="font-mono text-[0.5625rem] tracking-[0.1em] uppercase text-[var(--landing-muted-dim)] mb-1.5">
<div className="bg-[rgba(255,255,255,0.02)] rounded-xl p-4 border border-[var(--landing-border)]">
<div className="font-mono text-[0.5625rem] tracking-[0.15em] uppercase text-[var(--landing-muted)] mb-2">
Before
</div>
<p className="text-[0.8125rem] text-[var(--landing-muted)]">
<p className="text-[0.8125rem] text-[var(--landing-muted)] leading-relaxed">
{c.before}
</p>
</div>
<div className="hidden md:flex items-center text-[var(--landing-muted-dim)] text-lg">
<div className="hidden md:flex items-center justify-center">
<div className="w-8 h-8 rounded-full bg-[var(--landing-border)] flex items-center justify-center text-[var(--landing-muted-dim)]">
</div>
</div>
<div>
<div className="font-mono text-[0.5625rem] tracking-[0.1em] uppercase text-[rgba(134,239,172,0.7)] mb-1.5">
<div className="bg-[rgba(34,197,94,0.04)] rounded-xl p-4 border border-[rgba(34,197,94,0.1)]">
<div className="font-mono text-[0.5625rem] tracking-[0.15em] uppercase text-[var(--accent-green)] mb-2">
After
</div>
<p className="text-[0.8125rem] text-[var(--landing-fg)]">
<p className="text-[0.8125rem] text-[var(--landing-fg)] leading-relaxed">
{c.after}
</p>
</div>
<div className="font-mono text-[0.6875rem] text-[var(--landing-muted)] bg-black/30 px-3.5 py-2.5 rounded-lg self-center">
<span className="text-[var(--landing-muted-dim)]">$</span>{" "}
{c.command}
<div className="font-mono text-[0.75rem] text-[var(--landing-fg-secondary)] bg-[rgba(0,0,0,0.4)] px-4 py-3 rounded-lg self-center border border-[var(--landing-border)]">
<span className="text-[var(--accent-orange)] select-none"> </span>
<span className="text-[var(--landing-fg)]">{c.command}</span>
</div>
</div>
</div>

View File

@ -1,12 +1,14 @@
export function LandingVideo() {
return (
<section className="landing-reveal px-6 pb-[120px] pt-10 max-w-[72rem] mx-auto">
<div className="text-center mb-5">
<span className="text-[0.6875rem] tracking-[0.12em] uppercase text-[var(--landing-muted)] opacity-50">
<section className="landing-reveal px-6 pb-[140px] pt-10 max-w-[72rem] mx-auto">
<div className="text-center mb-6">
<span className="font-mono text-[0.75rem] tracking-[0.2em] uppercase text-[var(--accent-orange)]">
See it in action
</span>
</div>
<div className="landing-card rounded-2xl overflow-hidden aspect-video">
<div className="glow-card rounded-2xl overflow-hidden aspect-video" style={{
boxShadow: "0 0 80px -20px rgba(249,115,22,0.1)"
}}>
<iframe
src="https://www.youtube.com/embed/QdwaeEXOmDs?autoplay=0&rel=0&modestbranding=1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"

View File

@ -3,12 +3,12 @@
import { useEffect, useRef, useState } from "react";
const steps = [
{ label: "Issue assigned", mono: "#42", color: "rgba(255,240,220,0.5)" },
{ label: "Agent spawns", mono: "claude-code", color: "rgba(96,165,250,0.8)" },
{ label: "Worktree created", mono: "feat/auth", color: "rgba(234,179,8,0.7)" },
{ label: "PR opened", mono: "PR #312", color: "rgba(167,139,250,0.7)" },
{ label: "CI passes", mono: "✓ 48/48", color: "rgba(134,239,172,0.7)" },
{ label: "Merged", mono: "main", color: "rgba(34,197,94,0.9)" },
{ label: "Issue assigned", mono: "#42", color: "var(--accent-orange)" },
{ label: "Agent spawns", mono: "claude-code", color: "var(--accent-blue)" },
{ label: "Worktree created", mono: "feat/auth", color: "var(--accent-purple)" },
{ label: "PR opened", mono: "PR #312", color: "var(--accent-cyan)" },
{ label: "CI passes", mono: "✓ 48/48", color: "var(--accent-green)" },
{ label: "Merged", mono: "main", color: "var(--accent-green)" },
];
export function LandingWorkflow() {
@ -39,42 +39,49 @@ export function LandingWorkflow() {
}, []);
return (
<section ref={ref} className="py-[100px] px-6 max-w-[72rem] mx-auto">
<section ref={ref} className="py-[140px] px-6 max-w-[72rem] mx-auto">
<div className="landing-reveal">
<div className="text-xs tracking-[0.15em] uppercase text-[var(--landing-muted-dim)] mb-6 font-mono">
<div className="font-mono text-[0.75rem] tracking-[0.2em] uppercase text-[var(--accent-purple)] mb-4">
Lifecycle
</div>
<h2 className="font-sans font-[680] text-[clamp(1.375rem,3vw,2rem)] leading-[1.1] tracking-[-1.5px] mb-16">
<h2 className="text-[clamp(1.5rem,3vw,2.25rem)] leading-[1.1] tracking-[-1px] font-bold">
From issue to merged PR
</h2>
</div>
{/* Pipeline */}
<div className="relative">
{/* Connection line */}
<div className="absolute top-6 left-6 right-6 h-px bg-[var(--landing-border-subtle)] hidden md:block" />
<div className="relative mt-16">
{/* Connection line — background */}
<div className="absolute top-6 left-6 right-6 h-[2px] hidden md:block" style={{
background: "var(--landing-border)"
}} />
{/* Connection line — animated fill */}
<div
className="absolute top-6 left-6 right-6 h-px hidden md:block transition-all duration-700 ease-out origin-left"
className="absolute top-6 left-6 right-6 h-[2px] hidden md:block transition-all duration-700 ease-out origin-left"
style={{
background: "var(--landing-accent)",
background: "linear-gradient(90deg, var(--accent-orange), var(--accent-purple), var(--accent-green))",
transform: `scaleX(${activeStep >= 0 ? Math.min(activeStep / (steps.length - 1), 1) : 0})`,
}}
/>
{/* Steps */}
<div className="grid grid-cols-2 md:grid-cols-6 gap-6 md:gap-0">
<div className="grid grid-cols-2 md:grid-cols-6 gap-8 md:gap-0">
{steps.map((step, i) => {
const isActive = i <= activeStep;
return (
<div key={step.label} className="flex flex-col items-center text-center relative">
{/* Node */}
<div
className={`w-12 h-12 rounded-xl flex items-center justify-center mb-4 transition-all duration-500 ${
className={`w-12 h-12 rounded-2xl flex items-center justify-center mb-4 transition-all duration-500 ${
isActive
? "landing-card border-[var(--landing-border-default)]"
: "border border-[var(--landing-border-subtle)] bg-transparent"
? "border-[1.5px]"
: "border border-[var(--landing-border)]"
}`}
style={isActive ? { borderColor: step.color, boxShadow: `0 0 12px ${step.color.replace(/[\d.]+\)$/, "0.15)") }` } : undefined}
style={isActive ? {
borderColor: step.color,
background: `color-mix(in srgb, ${step.color} 8%, transparent)`,
boxShadow: `0 0 20px -4px ${step.color}`
} : undefined}
>
<span
className={`w-2.5 h-2.5 rounded-full transition-all duration-500 ${
@ -86,24 +93,24 @@ export function LandingWorkflow() {
{/* Label */}
<div
className={`text-[0.6875rem] font-medium mb-1 transition-all duration-500 ${
className={`text-[0.75rem] font-medium mb-1 transition-all duration-500 ${
isActive ? "text-[var(--landing-fg)]" : "text-[var(--landing-muted-dim)]"
}`}
>
{step.label}
</div>
<div
className={`font-mono text-[0.5625rem] transition-all duration-500 ${
className={`font-mono text-[0.625rem] transition-all duration-500 ${
isActive ? "text-[var(--landing-muted)]" : "text-[var(--landing-muted-dim)] opacity-50"
}`}
>
{step.mono}
</div>
{/* Pulse on active */}
{/* Pulse ring on active */}
{i === activeStep && (
<div
className="absolute top-0 w-12 h-12 rounded-xl landing-node-pulse"
className="absolute top-0 w-12 h-12 rounded-2xl landing-node-pulse"
style={{ borderColor: step.color }}
/>
)}

View File

@ -12,10 +12,11 @@ export function ScrollRevealProvider({ children }: { children: React.ReactNode }
}
});
},
{ threshold: 0.1, rootMargin: "-50px" }
{ threshold: 0.1, rootMargin: "-40px" }
);
document.querySelectorAll(".landing-reveal").forEach((el) => {
// Observe both landing-reveal and stagger-children elements
document.querySelectorAll(".landing-reveal, .stagger-children").forEach((el) => {
observer.observe(el);
});