feat(landing): enhance landing page with new enterprise design and features

This commit is contained in:
Abhimanyu Saharan
2026-02-07 14:01:04 +05:30
parent a7175d9a6f
commit 2c13c5b5ce
7 changed files with 1341 additions and 130 deletions

View File

@@ -132,3 +132,839 @@ body {
.landing-page {
font-family: var(--font-body), sans-serif;
}
/* Landing (Enterprise) */
.landing-enterprise {
--primary-navy: #0a1628;
--secondary-navy: #1a2942;
--accent-gold: #d4af37;
--accent-teal: #2dd4bf;
--neutral-100: #f8fafb;
--neutral-200: #e5e9ed;
--neutral-300: #cbd2d9;
--neutral-700: #3e4c59;
--neutral-800: #1e293b;
--success: #10b981;
--warning: #f59e0b;
min-height: 100vh;
font-family: var(--font-body), -apple-system, sans-serif;
background: var(--neutral-100);
color: var(--neutral-800);
line-height: 1.6;
overflow-x: hidden;
}
@keyframes landing-slide-down {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes landing-fade-in-up {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes landing-pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.5;
transform: scale(1.1);
}
}
.landing-enterprise .landing-nav {
position: fixed;
top: 0;
width: 100%;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--neutral-200);
z-index: 40;
animation: landing-slide-down 0.6s ease-out;
}
.landing-enterprise .nav-container {
max-width: 1400px;
margin: 0 auto;
padding: 1.25rem 2.5rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: 1.5rem;
}
.landing-enterprise .logo-section {
display: flex;
align-items: center;
gap: 0.75rem;
text-decoration: none;
}
.landing-enterprise .logo-icon {
width: 36px;
height: 36px;
background: linear-gradient(135deg, var(--primary-navy), var(--secondary-navy));
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 600;
font-size: 18px;
box-shadow: 0 2px 8px rgba(10, 22, 40, 0.15);
}
.landing-enterprise .logo-text {
display: flex;
flex-direction: column;
line-height: 1.2;
}
.landing-enterprise .logo-name {
font-weight: 600;
font-size: 20px;
letter-spacing: -0.02em;
color: var(--primary-navy);
}
.landing-enterprise .logo-tagline {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--neutral-700);
font-weight: 500;
}
.landing-enterprise .nav-links {
display: flex;
gap: 2.5rem;
align-items: center;
}
.landing-enterprise .nav-links a {
color: var(--neutral-700);
text-decoration: none;
font-weight: 500;
font-size: 15px;
transition: color 0.3s ease;
letter-spacing: -0.01em;
}
.landing-enterprise .nav-links a:hover {
color: var(--primary-navy);
}
.landing-enterprise .nav-cta {
display: flex;
gap: 1rem;
align-items: center;
}
.landing-enterprise .btn-secondary {
padding: 0.625rem 1.25rem;
border: 1.5px solid var(--neutral-300);
background: white;
color: var(--neutral-800);
border-radius: 8px;
font-weight: 500;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
}
.landing-enterprise .btn-secondary:hover {
border-color: var(--primary-navy);
background: var(--neutral-100);
}
.landing-enterprise .btn-primary {
padding: 0.625rem 1.5rem;
background: var(--primary-navy);
color: white;
border: none;
border-radius: 8px;
font-weight: 500;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(10, 22, 40, 0.15);
}
.landing-enterprise .btn-primary:hover {
background: var(--secondary-navy);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(10, 22, 40, 0.2);
}
.landing-enterprise .hero {
margin-top: 80px;
padding: 6rem 2.5rem 4rem;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
display: grid;
grid-template-columns: 1fr;
gap: 4rem;
align-items: center;
}
.landing-enterprise .hero-content {
animation: landing-fade-in-up 0.8s ease-out 0.2s both;
}
.landing-enterprise .hero-label {
display: inline-block;
padding: 0.5rem 1rem;
background: linear-gradient(135deg, rgba(10, 22, 40, 0.05), rgba(45, 212, 191, 0.08));
border: 1px solid rgba(45, 212, 191, 0.2);
border-radius: 50px;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--accent-teal);
margin-bottom: 1.5rem;
}
.landing-enterprise .hero h1 {
font-family: var(--font-display), serif;
font-size: 56px;
line-height: 1.15;
color: var(--primary-navy);
margin-bottom: 1.5rem;
font-weight: 400;
letter-spacing: -0.02em;
}
.landing-enterprise .hero-highlight {
background: linear-gradient(135deg, var(--accent-teal), var(--accent-gold));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-style: italic;
}
.landing-enterprise .hero p {
font-size: 19px;
line-height: 1.7;
color: var(--neutral-700);
margin-bottom: 2.5rem;
font-weight: 400;
}
.landing-enterprise .hero-actions {
display: flex;
gap: 1rem;
margin-bottom: 3rem;
}
.landing-enterprise .btn-large {
padding: 1rem 2rem;
font-size: 16px;
font-weight: 500;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
}
.landing-enterprise .btn-large.primary {
background: var(--primary-navy);
color: white;
border: none;
box-shadow: 0 4px 12px rgba(10, 22, 40, 0.2);
}
.landing-enterprise .btn-large.primary:hover {
background: var(--secondary-navy);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(10, 22, 40, 0.25);
}
.landing-enterprise .btn-large.secondary {
background: white;
color: var(--neutral-800);
border: 1.5px solid var(--neutral-300);
}
.landing-enterprise .btn-large.secondary:hover {
border-color: var(--primary-navy);
background: var(--neutral-100);
}
.landing-enterprise .hero-features {
display: flex;
gap: 2rem;
padding-top: 2rem;
border-top: 1px solid var(--neutral-200);
}
.landing-enterprise .hero-feature {
display: flex;
align-items: center;
gap: 0.5rem;
}
.landing-enterprise .feature-icon {
width: 20px;
height: 20px;
background: var(--accent-teal);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
flex-shrink: 0;
}
.landing-enterprise .hero-feature span {
font-size: 14px;
font-weight: 500;
color: var(--neutral-700);
}
.landing-enterprise .command-surface {
background: white;
border-radius: 16px;
box-shadow: 0 8px 32px rgba(10, 22, 40, 0.08);
overflow: hidden;
animation: landing-fade-in-up 0.8s ease-out 0.4s both;
border: 1px solid var(--neutral-200);
}
.landing-enterprise .surface-header {
padding: 1.5rem 2rem;
background: linear-gradient(135deg, var(--primary-navy), var(--secondary-navy));
color: white;
display: flex;
justify-content: space-between;
align-items: center;
}
.landing-enterprise .surface-title {
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 600;
opacity: 0.9;
}
.landing-enterprise .live-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 12px;
font-weight: 600;
}
.landing-enterprise .live-dot {
width: 8px;
height: 8px;
background: var(--accent-teal);
border-radius: 50%;
animation: landing-pulse 2s infinite;
}
.landing-enterprise .surface-subtitle {
padding: 1.25rem 2rem;
background: var(--neutral-100);
border-bottom: 1px solid var(--neutral-200);
}
.landing-enterprise .surface-subtitle h3 {
font-size: 16px;
font-weight: 600;
color: var(--primary-navy);
margin-bottom: 0.25rem;
}
.landing-enterprise .surface-subtitle p {
font-size: 13px;
color: var(--neutral-700);
}
.landing-enterprise .metrics-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0;
border-bottom: 1px solid var(--neutral-200);
}
.landing-enterprise .metric {
padding: 1.75rem 2rem;
text-align: center;
border-right: 1px solid var(--neutral-200);
}
.landing-enterprise .metric:last-child {
border-right: none;
}
.landing-enterprise .metric-value {
font-size: 36px;
font-weight: 300;
color: var(--primary-navy);
letter-spacing: -0.02em;
margin-bottom: 0.25rem;
}
.landing-enterprise .metric-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--neutral-700);
font-weight: 600;
}
.landing-enterprise .surface-content {
padding: 2rem;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.landing-enterprise .content-section h4 {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--neutral-700);
font-weight: 600;
margin-bottom: 1rem;
}
.landing-enterprise .status-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 0;
border-bottom: 1px solid var(--neutral-200);
}
.landing-enterprise .status-item:last-child {
border-bottom: none;
}
.landing-enterprise .status-icon {
width: 28px;
height: 28px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 14px;
}
.landing-enterprise .status-icon.progress {
background: rgba(45, 212, 191, 0.1);
color: var(--accent-teal);
}
.landing-enterprise .status-item-title {
font-size: 14px;
font-weight: 500;
color: var(--primary-navy);
}
.landing-enterprise .approval-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.875rem 0;
border-bottom: 1px solid var(--neutral-200);
gap: 0.75rem;
}
.landing-enterprise .approval-item:last-child {
border-bottom: none;
}
.landing-enterprise .approval-title {
font-size: 14px;
color: var(--neutral-800);
font-weight: 500;
}
.landing-enterprise .approval-badge {
padding: 0.35rem 0.75rem;
border-radius: 6px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
white-space: nowrap;
}
.landing-enterprise .approval-badge.ready {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.landing-enterprise .approval-badge.waiting {
background: rgba(245, 158, 11, 0.1);
color: var(--warning);
}
.landing-enterprise .signal-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 0;
border-bottom: 1px solid var(--neutral-200);
gap: 0.75rem;
}
.landing-enterprise .signal-item:last-child {
border-bottom: none;
}
.landing-enterprise .signal-text {
font-size: 13px;
color: var(--neutral-700);
}
.landing-enterprise .signal-time {
font-size: 12px;
color: var(--neutral-700);
font-weight: 500;
white-space: nowrap;
}
.landing-enterprise .features-section {
padding: 6rem 2.5rem;
max-width: 1400px;
margin: 0 auto;
}
.landing-enterprise #capabilities {
scroll-margin-top: 110px;
}
.landing-enterprise .features-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 2rem;
margin-top: 3rem;
}
.landing-enterprise .feature-card {
background: white;
padding: 2rem;
border-radius: 12px;
border: 1px solid var(--neutral-200);
transition: all 0.3s ease;
animation: landing-fade-in-up 0.6s ease-out both;
}
.landing-enterprise .feature-card:nth-child(1) {
animation-delay: 0.1s;
}
.landing-enterprise .feature-card:nth-child(2) {
animation-delay: 0.2s;
}
.landing-enterprise .feature-card:nth-child(3) {
animation-delay: 0.3s;
}
.landing-enterprise .feature-card:nth-child(4) {
animation-delay: 0.4s;
}
.landing-enterprise .feature-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(10, 22, 40, 0.12);
border-color: var(--accent-teal);
}
.landing-enterprise .feature-number {
width: 48px;
height: 48px;
background: linear-gradient(135deg, rgba(10, 22, 40, 0.05), rgba(45, 212, 191, 0.08));
border: 1px solid var(--neutral-200);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
margin-bottom: 1.5rem;
color: var(--primary-navy);
font-weight: 300;
}
.landing-enterprise .feature-card h3 {
font-size: 18px;
font-weight: 600;
color: var(--primary-navy);
margin-bottom: 0.75rem;
letter-spacing: -0.01em;
}
.landing-enterprise .feature-card p {
font-size: 14px;
line-height: 1.6;
color: var(--neutral-700);
}
.landing-enterprise .cta-section {
padding: 5rem 2.5rem;
background: linear-gradient(135deg, var(--primary-navy), var(--secondary-navy));
text-align: center;
}
.landing-enterprise .cta-content {
max-width: 800px;
margin: 0 auto;
}
.landing-enterprise .cta-section h2 {
font-family: var(--font-display), serif;
font-size: 42px;
color: white;
margin-bottom: 1rem;
font-weight: 400;
letter-spacing: -0.01em;
}
.landing-enterprise .cta-section p {
font-size: 18px;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 2.5rem;
}
.landing-enterprise .cta-actions {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.landing-enterprise .btn-large.white {
background: white;
color: var(--primary-navy);
border: none;
}
.landing-enterprise .btn-large.white:hover {
background: var(--neutral-100);
transform: translateY(-2px);
}
.landing-enterprise .btn-large.outline {
background: transparent;
color: white;
border: 1.5px solid rgba(255, 255, 255, 0.3);
}
.landing-enterprise .btn-large.outline:hover {
background: rgba(255, 255, 255, 0.1);
border-color: white;
}
.landing-enterprise .landing-footer {
background: var(--neutral-100);
border-top: 1px solid var(--neutral-200);
padding: 3rem 2.5rem 2rem;
}
.landing-enterprise .footer-content {
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr;
gap: 4rem;
margin-bottom: 3rem;
}
.landing-enterprise .footer-brand h3 {
font-size: 18px;
font-weight: 600;
color: var(--primary-navy);
margin-bottom: 0.75rem;
}
.landing-enterprise .footer-brand p {
font-size: 14px;
color: var(--neutral-700);
line-height: 1.6;
margin-bottom: 1.5rem;
}
.landing-enterprise .footer-tagline {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--neutral-700);
font-weight: 600;
}
.landing-enterprise .footer-column h4 {
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--neutral-700);
font-weight: 600;
margin-bottom: 1rem;
}
.landing-enterprise .footer-links {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.landing-enterprise .footer-links a,
.landing-enterprise .footer-links button {
color: var(--neutral-700);
text-decoration: none;
font-size: 14px;
transition: color 0.3s ease;
background: transparent;
border: none;
padding: 0;
cursor: pointer;
text-align: left;
font-weight: 400;
}
.landing-enterprise .footer-links a:hover,
.landing-enterprise .footer-links button:hover {
color: var(--primary-navy);
}
.landing-enterprise .footer-bottom {
max-width: 1400px;
margin: 0 auto;
padding-top: 2rem;
border-top: 1px solid var(--neutral-200);
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
}
.landing-enterprise .footer-copyright {
font-size: 13px;
color: var(--neutral-700);
}
.landing-enterprise .footer-bottom-links {
display: flex;
gap: 2rem;
flex-wrap: wrap;
}
.landing-enterprise .footer-bottom-links a {
font-size: 13px;
color: var(--neutral-700);
text-decoration: none;
transition: color 0.3s ease;
}
.landing-enterprise .footer-bottom-links a:hover {
color: var(--primary-navy);
}
@media (max-width: 1024px) {
.landing-enterprise .nav-container {
padding: 1rem 1.5rem;
}
.landing-enterprise .nav-links {
display: none;
}
.landing-enterprise .hero {
grid-template-columns: 1fr;
gap: 3rem;
}
.landing-enterprise .features-grid {
grid-template-columns: repeat(2, 1fr);
}
.landing-enterprise .footer-content {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 768px) {
.landing-enterprise .hero {
padding: 4.5rem 1.25rem 3rem;
}
.landing-enterprise .hero h1 {
font-size: 40px;
}
.landing-enterprise .hero-actions {
flex-direction: column;
}
.landing-enterprise .btn-large {
width: 100%;
justify-content: center;
}
.landing-enterprise .hero-features {
flex-direction: column;
gap: 1rem;
}
.landing-enterprise .features-section {
padding: 4.5rem 1.25rem;
}
.landing-enterprise .features-grid {
grid-template-columns: 1fr;
}
.landing-enterprise .metrics-row {
grid-template-columns: 1fr;
}
.landing-enterprise .metric {
border-right: none;
border-bottom: 1px solid var(--neutral-200);
}
.landing-enterprise .metric:last-child {
border-bottom: none;
}
.landing-enterprise .surface-content {
grid-template-columns: 1fr;
}
.landing-enterprise .landing-footer {
padding: 2.5rem 1.25rem 2rem;
}
.landing-enterprise .footer-content {
grid-template-columns: 1fr;
gap: 2rem;
}
}

View File

@@ -3,7 +3,7 @@ import "./globals.css";
import type { Metadata } from "next";
import type { ReactNode } from "react";
import { IBM_Plex_Sans, Sora } from "next/font/google";
import { DM_Serif_Display, IBM_Plex_Sans, Sora } from "next/font/google";
import { AuthProvider } from "@/components/providers/AuthProvider";
import { QueryProvider } from "@/components/providers/QueryProvider";
@@ -27,11 +27,18 @@ const headingFont = Sora({
weight: ["500", "600", "700"],
});
const displayFont = DM_Serif_Display({
subsets: ["latin"],
display: "swap",
variable: "--font-display",
weight: ["400"],
});
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body
className={`${bodyFont.variable} ${headingFont.variable} min-h-screen bg-app text-strong antialiased`}
className={`${bodyFont.variable} ${headingFont.variable} ${displayFont.variable} min-h-screen bg-app text-strong antialiased`}
>
<AuthProvider>
<QueryProvider>{children}</QueryProvider>

View File

@@ -3,16 +3,22 @@ import { HeroKicker } from "@/components/atoms/HeroKicker";
export function HeroCopy() {
return (
<div className="space-y-6">
<HeroKicker>Mission Control</HeroKicker>
<HeroKicker>OpenClaw Mission Control</HeroKicker>
<div className="space-y-4">
<h1 className="font-heading text-4xl font-semibold leading-tight text-strong sm:text-5xl lg:text-6xl">
Enterprise control for
Command autonomous work.
<br />
autonomous execution.
<span className="relative inline-flex">
Keep human oversight.
<span
className="absolute inset-x-0 bottom-1 -z-10 h-[0.55em] rounded-md bg-[color:var(--accent-soft)]"
aria-hidden="true"
/>
</span>
</h1>
<p className="max-w-xl text-base text-muted sm:text-lg">
Coordinate boards, agents, and approvals in one command layer. No
status meetings. No blind spots. Just durable execution.
Track tasks, approvals, and agent health in one calm surface. Get
realtime signals when work changes, without chasing people for status.
</p>
</div>
</div>

View File

@@ -1,105 +1,269 @@
"use client";
import { SignInButton, SignedIn, SignedOut } from "@/auth/clerk";
import Link from "next/link";
import { HeroCopy } from "@/components/molecules/HeroCopy";
import { Button } from "@/components/ui/button";
import { SignInButton, SignedIn, SignedOut, isClerkEnabled } from "@/auth/clerk";
const ArrowIcon = () => (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path
d="M6 12L10 8L6 4"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
export function LandingHero() {
return (
<section className="grid w-full items-center gap-12 lg:grid-cols-[1.1fr_0.9fr]">
<div className="space-y-8 animate-fade-in-up">
<HeroCopy />
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
<SignedOut>
<SignInButton
mode="modal"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<Button size="lg" className="w-full sm:w-auto">
Sign in to open mission control
</Button>
</SignInButton>
</SignedOut>
<SignedIn>
<div className="text-sm text-muted">
You&apos;re signed in. Open your boards when you&apos;re ready.
</div>
</SignedIn>
</div>
<div className="flex flex-wrap gap-3 text-xs font-semibold uppercase tracking-[0.28em] text-quiet">
<span className="rounded-full border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-1">
Enterprise ready
</span>
<span className="rounded-full border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-1">
Agent-first ops
</span>
<span className="rounded-full border border-[color:var(--border)] bg-[color:var(--surface)] px-3 py-1">
24/7 visibility
</span>
</div>
</div>
const clerkEnabled = isClerkEnabled();
<div className="relative animate-fade-in-up">
<div className="surface-panel rounded-3xl p-6">
<div className="flex items-center justify-between text-xs font-semibold uppercase tracking-[0.3em] text-quiet">
<span>Command surface</span>
<span className="rounded-full border border-[color:var(--border)] px-2 py-1 text-[10px]">
Live
</span>
return (
<>
<section className="hero">
<div className="hero-content">
<div className="hero-label">OpenClaw Mission Control</div>
<h1>
Command <span className="hero-highlight">autonomous work.</span>
<br />
Keep human oversight.
</h1>
<p>
Track tasks, approvals, and agent health in one unified command
center. Get real-time signals when work changes, without losing the
thread of execution.
</p>
<div className="hero-actions">
<SignedOut>
{clerkEnabled ? (
<>
<SignInButton
mode="modal"
forceRedirectUrl="/boards"
signUpForceRedirectUrl="/boards"
>
<button type="button" className="btn-large primary">
Open Boards <ArrowIcon />
</button>
</SignInButton>
<SignInButton
mode="modal"
forceRedirectUrl="/boards/new"
signUpForceRedirectUrl="/boards/new"
>
<button type="button" className="btn-large secondary">
Create Board
</button>
</SignInButton>
</>
) : (
<>
<Link href="/boards" className="btn-large primary">
Open Boards <ArrowIcon />
</Link>
<Link href="/boards/new" className="btn-large secondary">
Create Board
</Link>
</>
)}
</SignedOut>
<SignedIn>
<Link href="/boards" className="btn-large primary">
Open Boards <ArrowIcon />
</Link>
<Link href="/boards/new" className="btn-large secondary">
Create Board
</Link>
</SignedIn>
</div>
<div className="mt-6 space-y-4">
<div>
<p className="text-lg font-semibold text-strong">
Tasks claimed, tracked, delivered.
</p>
<p className="text-sm text-muted">
See every queue, agent, and handoff without chasing updates.
</p>
<div className="hero-features">
{[
"Agent-First Operations",
"Approval Queues",
"Live Signals",
].map((label) => (
<div key={label} className="hero-feature">
<div className="feature-icon"></div>
<span>{label}</span>
</div>
))}
</div>
</div>
<div className="command-surface">
<div className="surface-header">
<div className="surface-title">Command Surface</div>
<div className="live-indicator">
<div className="live-dot" />
LIVE
</div>
<div className="grid grid-cols-3 gap-3">
</div>
<div className="surface-subtitle">
<h3>Ship work without losing the thread.</h3>
<p>Tasks, approvals, and agent status stay synced across the board.</p>
</div>
<div className="metrics-row">
{[
{ label: "Boards", value: "12" },
{ label: "Agents", value: "08" },
{ label: "Tasks", value: "46" },
].map((item) => (
<div key={item.label} className="metric">
<div className="metric-value">{item.value}</div>
<div className="metric-label">{item.label}</div>
</div>
))}
</div>
<div className="surface-content">
<div className="content-section">
<h4>Board In Progress</h4>
{[
{ label: "Active boards", value: "12" },
{ label: "Agents live", value: "08" },
{ label: "Tasks in flow", value: "46" },
].map((item) => (
<div
key={item.label}
className="rounded-2xl border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-4 text-center"
>
<div className="text-xl font-semibold text-strong">
{item.value}
</div>
<div className="text-[11px] uppercase tracking-[0.18em] text-quiet">
{item.label}
"Cut release candidate",
"Triage approvals backlog",
"Stabilize agent handoffs",
].map((title) => (
<div key={title} className="status-item">
<div className="status-icon progress"></div>
<div className="status-item-content">
<div className="status-item-title">{title}</div>
</div>
</div>
))}
</div>
<div className="rounded-2xl border border-[color:var(--border)] bg-[color:var(--surface)] p-4">
<div className="flex items-center justify-between text-xs font-semibold uppercase tracking-[0.2em] text-quiet">
<span>Signals</span>
<span>Updated 2m ago</span>
</div>
<div className="mt-3 space-y-2 text-sm text-muted">
<div className="flex items-center justify-between">
<span>Agent Delta moved task to review</span>
<span className="text-quiet">Just now</span>
<div className="content-section">
<h4>Approvals 3 Pending</h4>
{[
{ title: "Deploy window confirmed", status: "ready" as const },
{ title: "Copy reviewed", status: "waiting" as const },
{ title: "Security sign-off", status: "waiting" as const },
].map((item) => (
<div key={item.title} className="approval-item">
<div className="approval-title">{item.title}</div>
<div className={`approval-badge ${item.status}`}>
{item.status}
</div>
</div>
<div className="flex items-center justify-between">
<span>Board Growth Ops hit WIP limit</span>
<span className="text-quiet">5m</span>
))}
</div>
</div>
<div
style={{
padding: "2rem",
borderTop: "1px solid var(--neutral-200)",
}}
>
<div className="content-section">
<h4>Signals Updated Moments Ago</h4>
{[
{ text: "Agent Delta moved task to review", time: "Now" },
{ text: "Growth Ops hit WIP limit", time: "5m" },
{ text: "Release pipeline stabilized", time: "12m" },
].map((signal) => (
<div key={signal.text} className="signal-item">
<div className="signal-text">{signal.text}</div>
<div className="signal-time">{signal.time}</div>
</div>
<div className="flex items-center justify-between">
<span>Release tasks stabilized</span>
<span className="text-quiet">12m</span>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</section>
</section>
<section className="features-section" id="capabilities">
<div className="features-grid">
{[
{
title: "Boards as ops maps",
description:
"Keep tasks, priorities, dependencies, and ownership visible at a glance.",
},
{
title: "Approvals that move",
description:
"Queue, comment, and approve without losing context or slowing execution.",
},
{
title: "Realtime signals",
description:
"See work change as it happens: tasks, agent status, and approvals update live.",
},
{
title: "Audit trail built in",
description:
"Every decision leaves a trail, so the board stays explainable and reviewable.",
},
].map((feature, idx) => (
<div key={feature.title} className="feature-card">
<div className="feature-number">
{String(idx + 1).padStart(2, "0")}
</div>
<h3>{feature.title}</h3>
<p>{feature.description}</p>
</div>
))}
</div>
</section>
<section className="cta-section">
<div className="cta-content">
<h2>Start with one board. Grow into a control room.</h2>
<p>
Onboard a board, name a lead agent, and keep approvals and signals
visible from day one.
</p>
<div className="cta-actions">
<SignedOut>
{clerkEnabled ? (
<>
<SignInButton
mode="modal"
forceRedirectUrl="/boards/new"
signUpForceRedirectUrl="/boards/new"
>
<button type="button" className="btn-large white">
Create Board
</button>
</SignInButton>
<SignInButton
mode="modal"
forceRedirectUrl="/boards"
signUpForceRedirectUrl="/boards"
>
<button type="button" className="btn-large outline">
View Boards
</button>
</SignInButton>
</>
) : (
<>
<Link href="/boards/new" className="btn-large white">
Create Board
</Link>
<Link href="/boards" className="btn-large outline">
View Boards
</Link>
</>
)}
</SignedOut>
<SignedIn>
<Link href="/boards/new" className="btn-large white">
Create Board
</Link>
<Link href="/boards" className="btn-large outline">
View Boards
</Link>
</SignedIn>
</div>
</div>
</section>
</>
);
}

View File

@@ -1,13 +1,25 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { SignOutButton, useUser } from "@/auth/clerk";
import { LogOut } from "lucide-react";
import {
Activity,
Bot,
ChevronDown,
LayoutDashboard,
LogOut,
Plus,
Server,
Trello,
} from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
export function UserMenu({ className }: { className?: string }) {
const [open, setOpen] = useState(false);
const { user } = useUser();
if (!user) return null;
@@ -19,39 +31,59 @@ export function UserMenu({ className }: { className?: string }) {
const displayEmail = user.primaryEmailAddress?.emailAddress ?? "";
return (
<Popover>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<button
type="button"
className={cn(
"flex h-11 items-center rounded-lg border border-transparent px-1 text-slate-900 transition hover:border-slate-200 hover:bg-slate-50",
"group inline-flex h-9 items-center gap-2 rounded-[10px] bg-transparent px-1 py-1 transition",
"hover:bg-white/70",
// Avoid the default browser focus outline (often bright blue) on click.
// Keep a subtle, enterprise-looking focus ring for keyboard navigation.
"focus:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--neutral-300,var(--border-strong))] focus-visible:ring-offset-2 focus-visible:ring-offset-white",
"data-[state=open]:bg-white",
className,
)}
aria-label="Open user menu"
>
<span className="flex h-11 w-11 items-center justify-center overflow-hidden rounded-lg bg-slate-100 text-sm font-semibold text-slate-900 shadow-sm">
<span
className={cn(
"relative flex h-9 w-9 items-center justify-center overflow-hidden rounded-[10px] text-xs font-semibold text-white shadow-sm",
avatarUrl
? "bg-[color:var(--neutral-200,var(--surface-muted))]"
: "bg-gradient-to-br from-[color:var(--primary-navy,var(--accent))] to-[color:var(--secondary-navy,var(--accent-strong))]",
)}
>
{avatarUrl ? (
<Image
src={avatarUrl}
alt="User avatar"
width={44}
height={44}
className="h-11 w-11 object-cover"
width={36}
height={36}
className="h-9 w-9 object-cover"
/>
) : (
avatarLabel
)}
</span>
<ChevronDown className="h-4 w-4 text-[color:var(--neutral-700,var(--text-quiet))] transition group-data-[state=open]:rotate-180" />
</button>
</PopoverTrigger>
<PopoverContent
align="end"
sideOffset={10}
className="w-64 rounded-2xl border border-slate-200 bg-white p-0 shadow-lg"
sideOffset={12}
className="w-80 overflow-hidden rounded-2xl border border-[color:var(--neutral-200,var(--border))] bg-white/95 p-0 shadow-[0_8px_32px_rgba(10,22,40,0.08)] backdrop-blur"
>
<div className="border-b border-slate-200 px-4 py-3">
<div className="border-b border-[color:var(--neutral-200,var(--border))] px-4 py-3">
<div className="flex items-center gap-3">
<span className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-lg bg-slate-100 text-sm font-semibold text-slate-900">
<span
className={cn(
"flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl text-sm font-semibold text-white",
avatarUrl
? "bg-[color:var(--neutral-200,var(--surface-muted))]"
: "bg-gradient-to-br from-[color:var(--primary-navy,var(--accent))] to-[color:var(--secondary-navy,var(--accent-strong))]",
)}
>
{avatarUrl ? (
<Image
src={avatarUrl}
@@ -65,22 +97,67 @@ export function UserMenu({ className }: { className?: string }) {
)}
</span>
<div className="min-w-0">
<div className="truncate text-sm font-semibold text-slate-900">
<div className="truncate text-sm font-semibold text-[color:var(--primary-navy,var(--text))]">
{displayName}
</div>
{displayEmail ? (
<div className="truncate text-xs text-slate-500">{displayEmail}</div>
<div className="truncate text-xs text-[color:var(--neutral-700,var(--text-muted))]">
{displayEmail}
</div>
) : null}
</div>
</div>
</div>
<div className="p-2">
<div className="grid grid-cols-2 gap-2">
<Link
href="/boards"
className="flex w-full items-center justify-center gap-2 rounded-xl border border-[color:var(--neutral-300,var(--border-strong))] bg-white px-3 py-2 text-sm font-semibold text-[color:var(--neutral-800,var(--text))] transition hover:border-[color:var(--primary-navy,var(--accent-strong))] hover:bg-[color:var(--neutral-100,var(--surface-muted))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent-teal,var(--accent))] focus-visible:ring-offset-2"
onClick={() => setOpen(false)}
>
<Trello className="h-4 w-4 text-[color:var(--neutral-700,var(--text-quiet))]" />
Open boards
</Link>
<Link
href="/boards/new"
className="flex w-full items-center justify-center gap-2 rounded-xl bg-[color:var(--primary-navy,var(--accent))] px-3 py-2 text-sm font-semibold text-white shadow-[0_2px_8px_rgba(10,22,40,0.15)] transition hover:bg-[color:var(--secondary-navy,var(--accent-strong))] hover:translate-y-[-1px] hover:shadow-[0_4px_12px_rgba(10,22,40,0.20)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent-teal,var(--accent))] focus-visible:ring-offset-2"
onClick={() => setOpen(false)}
>
<Plus className="h-4 w-4 opacity-90" />
Create board
</Link>
</div>
<div className="my-2 h-px bg-[color:var(--neutral-200,var(--border))]" />
{(
[
{ href: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
{ href: "/activity", label: "Activity", icon: Activity },
{ href: "/agents", label: "Agents", icon: Bot },
{ href: "/gateways", label: "Gateways", icon: Server },
] as const
).map((item) => (
<Link
key={item.href}
href={item.href}
className="flex w-full items-center gap-2 rounded-xl px-3 py-2 text-sm font-semibold text-[color:var(--neutral-800,var(--text))] transition hover:bg-[color:var(--neutral-100,var(--surface-muted))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent-teal,var(--accent))] focus-visible:ring-offset-2"
onClick={() => setOpen(false)}
>
<item.icon className="h-4 w-4 text-[color:var(--neutral-700,var(--text-quiet))]" />
{item.label}
</Link>
))}
<div className="my-2 h-px bg-[color:var(--neutral-200,var(--border))]" />
<SignOutButton>
<button
type="button"
className="flex w-full items-center gap-2 rounded-xl px-3 py-2 text-sm font-semibold text-slate-900 transition hover:bg-slate-100"
className="flex w-full items-center gap-2 rounded-xl px-3 py-2 text-sm font-semibold text-[color:var(--neutral-800,var(--text))] transition hover:bg-[color:var(--neutral-100,var(--surface-muted))] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent-teal,var(--accent))] focus-visible:ring-offset-2"
onClick={() => setOpen(false)}
>
<LogOut className="h-4 w-4 text-slate-500" />
<LogOut className="h-4 w-4 text-[color:var(--neutral-700,var(--text-quiet))]" />
Sign out
</button>
</SignOutButton>

View File

@@ -1,39 +1,159 @@
"use client";
import Link from "next/link";
import type { ReactNode } from "react";
import { SignedIn } from "@/auth/clerk";
import { SignInButton, SignedIn, SignedOut, isClerkEnabled } from "@/auth/clerk";
import { BrandMark } from "@/components/atoms/BrandMark";
import { UserMenu } from "@/components/organisms/UserMenu";
export function LandingShell({ children }: { children: ReactNode }) {
return (
<div className="landing-page bg-app text-strong">
<section className="relative overflow-hidden px-4 pb-20 pt-16 sm:px-6 lg:px-8">
<div
className="absolute inset-0 bg-landing-grid opacity-[0.18] pointer-events-none"
aria-hidden="true"
/>
<div
className="absolute -top-40 right-0 h-72 w-72 rounded-full bg-[color:var(--accent-soft)] blur-3xl pointer-events-none"
aria-hidden="true"
/>
<div
className="absolute -bottom-32 left-0 h-72 w-72 rounded-full bg-[color:var(--surface-strong)] blur-3xl pointer-events-none"
aria-hidden="true"
/>
const clerkEnabled = isClerkEnabled();
return (
<div className="landing-enterprise">
<nav className="landing-nav" aria-label="Primary navigation">
<div className="nav-container">
<Link href="/" className="logo-section" aria-label="OpenClaw home">
<div className="logo-icon" aria-hidden="true">
OC
</div>
<div className="logo-text">
<div className="logo-name">OpenClaw</div>
<div className="logo-tagline">Mission Control</div>
</div>
</Link>
<div className="nav-links">
<Link href="#capabilities">Capabilities</Link>
<Link href="/boards">Boards</Link>
<Link href="/activity">Activity</Link>
<Link href="/gateways">Gateways</Link>
</div>
<div className="nav-cta">
<SignedOut>
{clerkEnabled ? (
<>
<SignInButton
mode="modal"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<button type="button" className="btn-secondary">
Sign In
</button>
</SignInButton>
<SignInButton
mode="modal"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<button type="button" className="btn-primary">
Start Free Trial
</button>
</SignInButton>
</>
) : (
<>
<Link href="/boards" className="btn-secondary">
Boards
</Link>
<Link href="/onboarding" className="btn-primary">
Get started
</Link>
</>
)}
</SignedOut>
<div className="relative mx-auto flex w-full max-w-6xl flex-col gap-12">
<header className="flex items-center justify-between gap-4">
<BrandMark />
<SignedIn>
<Link href="/boards/new" className="btn-secondary">
Create Board
</Link>
<Link href="/boards" className="btn-primary">
Open Boards
</Link>
<UserMenu />
</SignedIn>
</header>
<main>{children}</main>
</div>
</div>
</section>
</nav>
<main>{children}</main>
<footer className="landing-footer">
<div className="footer-content">
<div className="footer-brand">
<h3>OpenClaw</h3>
<p>A calm command center for boards, agents, and approvals.</p>
<div className="footer-tagline">Realtime Execution Visibility</div>
</div>
<div className="footer-column">
<h4>Product</h4>
<div className="footer-links">
<Link href="#capabilities">Capabilities</Link>
<Link href="/boards">Boards</Link>
<Link href="/activity">Activity</Link>
<Link href="/dashboard">Dashboard</Link>
</div>
</div>
<div className="footer-column">
<h4>Platform</h4>
<div className="footer-links">
<Link href="/gateways">Gateways</Link>
<Link href="/agents">Agents</Link>
<Link href="/dashboard">Dashboard</Link>
</div>
</div>
<div className="footer-column">
<h4>Access</h4>
<div className="footer-links">
<SignedOut>
{clerkEnabled ? (
<>
<SignInButton
mode="modal"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<button type="button">Sign In</button>
</SignInButton>
<SignInButton
mode="modal"
forceRedirectUrl="/onboarding"
signUpForceRedirectUrl="/onboarding"
>
<button type="button">Create Account</button>
</SignInButton>
</>
) : (
<Link href="/boards">Boards</Link>
)}
<Link href="/onboarding">Onboarding</Link>
</SignedOut>
<SignedIn>
<Link href="/boards">Open Boards</Link>
<Link href="/boards/new">Create Board</Link>
<Link href="/dashboard">Dashboard</Link>
</SignedIn>
</div>
</div>
</div>
<div className="footer-bottom">
<div className="footer-copyright">
© {new Date().getFullYear()} OpenClaw. All rights reserved.
</div>
<div className="footer-bottom-links">
<Link href="#capabilities">Capabilities</Link>
<Link href="/boards">Boards</Link>
<Link href="/activity">Activity</Link>
</div>
</div>
</footer>
</div>
);
}

View File

@@ -7,6 +7,7 @@ module.exports = {
fontFamily: {
heading: ["var(--font-heading)", "sans-serif"],
body: ["var(--font-body)", "sans-serif"],
display: ["var(--font-display)", "serif"],
},
},
},