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 { .landing-page {
font-family: var(--font-body), sans-serif; 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 { Metadata } from "next";
import type { ReactNode } from "react"; 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 { AuthProvider } from "@/components/providers/AuthProvider";
import { QueryProvider } from "@/components/providers/QueryProvider"; import { QueryProvider } from "@/components/providers/QueryProvider";
@@ -27,11 +27,18 @@ const headingFont = Sora({
weight: ["500", "600", "700"], 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 }) { export default function RootLayout({ children }: { children: ReactNode }) {
return ( return (
<html lang="en"> <html lang="en">
<body <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> <AuthProvider>
<QueryProvider>{children}</QueryProvider> <QueryProvider>{children}</QueryProvider>

View File

@@ -3,16 +3,22 @@ import { HeroKicker } from "@/components/atoms/HeroKicker";
export function HeroCopy() { export function HeroCopy() {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<HeroKicker>Mission Control</HeroKicker> <HeroKicker>OpenClaw Mission Control</HeroKicker>
<div className="space-y-4"> <div className="space-y-4">
<h1 className="font-heading text-4xl font-semibold leading-tight text-strong sm:text-5xl lg:text-6xl"> <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 /> <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> </h1>
<p className="max-w-xl text-base text-muted sm:text-lg"> <p className="max-w-xl text-base text-muted sm:text-lg">
Coordinate boards, agents, and approvals in one command layer. No Track tasks, approvals, and agent health in one calm surface. Get
status meetings. No blind spots. Just durable execution. realtime signals when work changes, without chasing people for status.
</p> </p>
</div> </div>
</div> </div>

View File

@@ -1,105 +1,269 @@
"use client"; "use client";
import { SignInButton, SignedIn, SignedOut } from "@/auth/clerk"; import Link from "next/link";
import { HeroCopy } from "@/components/molecules/HeroCopy"; import { SignInButton, SignedIn, SignedOut, isClerkEnabled } from "@/auth/clerk";
import { Button } from "@/components/ui/button";
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() { export function LandingHero() {
return ( const clerkEnabled = isClerkEnabled();
<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>
<div className="relative animate-fade-in-up"> return (
<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"> <section className="hero">
<span>Command surface</span> <div className="hero-content">
<span className="rounded-full border border-[color:var(--border)] px-2 py-1 text-[10px]"> <div className="hero-label">OpenClaw Mission Control</div>
Live <h1>
</span> 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>
<div className="mt-6 space-y-4">
<div> <div className="hero-features">
<p className="text-lg font-semibold text-strong"> {[
Tasks claimed, tracked, delivered. "Agent-First Operations",
</p> "Approval Queues",
<p className="text-sm text-muted"> "Live Signals",
See every queue, agent, and handoff without chasing updates. ].map((label) => (
</p> <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>
<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" }, "Cut release candidate",
{ label: "Agents live", value: "08" }, "Triage approvals backlog",
{ label: "Tasks in flow", value: "46" }, "Stabilize agent handoffs",
].map((item) => ( ].map((title) => (
<div <div key={title} className="status-item">
key={item.label} <div className="status-icon progress"></div>
className="rounded-2xl border border-[color:var(--border)] bg-[color:var(--surface-muted)] p-4 text-center" <div className="status-item-content">
> <div className="status-item-title">{title}</div>
<div className="text-xl font-semibold text-strong">
{item.value}
</div>
<div className="text-[11px] uppercase tracking-[0.18em] text-quiet">
{item.label}
</div> </div>
</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"> <div className="content-section">
<span>Signals</span> <h4>Approvals 3 Pending</h4>
<span>Updated 2m ago</span> {[
</div> { title: "Deploy window confirmed", status: "ready" as const },
<div className="mt-3 space-y-2 text-sm text-muted"> { title: "Copy reviewed", status: "waiting" as const },
<div className="flex items-center justify-between"> { title: "Security sign-off", status: "waiting" as const },
<span>Agent Delta moved task to review</span> ].map((item) => (
<span className="text-quiet">Just now</span> <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>
<div className="flex items-center justify-between"> ))}
<span>Board Growth Ops hit WIP limit</span> </div>
<span className="text-quiet">5m</span> </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>
<div className="flex items-center justify-between"> ))}
<span>Release tasks stabilized</span>
<span className="text-quiet">12m</span>
</div>
</div>
</div> </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"; "use client";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { SignOutButton, useUser } from "@/auth/clerk"; 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 { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export function UserMenu({ className }: { className?: string }) { export function UserMenu({ className }: { className?: string }) {
const [open, setOpen] = useState(false);
const { user } = useUser(); const { user } = useUser();
if (!user) return null; if (!user) return null;
@@ -19,39 +31,59 @@ export function UserMenu({ className }: { className?: string }) {
const displayEmail = user.primaryEmailAddress?.emailAddress ?? ""; const displayEmail = user.primaryEmailAddress?.emailAddress ?? "";
return ( return (
<Popover> <Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<button <button
type="button" type="button"
className={cn( 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, className,
)} )}
aria-label="Open user menu" 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 ? ( {avatarUrl ? (
<Image <Image
src={avatarUrl} src={avatarUrl}
alt="User avatar" alt="User avatar"
width={44} width={36}
height={44} height={36}
className="h-11 w-11 object-cover" className="h-9 w-9 object-cover"
/> />
) : ( ) : (
avatarLabel avatarLabel
)} )}
</span> </span>
<ChevronDown className="h-4 w-4 text-[color:var(--neutral-700,var(--text-quiet))] transition group-data-[state=open]:rotate-180" />
</button> </button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent <PopoverContent
align="end" align="end"
sideOffset={10} sideOffset={12}
className="w-64 rounded-2xl border border-slate-200 bg-white p-0 shadow-lg" 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"> <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 ? ( {avatarUrl ? (
<Image <Image
src={avatarUrl} src={avatarUrl}
@@ -65,22 +97,67 @@ export function UserMenu({ className }: { className?: string }) {
)} )}
</span> </span>
<div className="min-w-0"> <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} {displayName}
</div> </div>
{displayEmail ? ( {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} ) : null}
</div> </div>
</div> </div>
</div> </div>
<div className="p-2"> <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> <SignOutButton>
<button <button
type="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 Sign out
</button> </button>
</SignOutButton> </SignOutButton>

View File

@@ -1,39 +1,159 @@
"use client"; "use client";
import Link from "next/link";
import type { ReactNode } from "react"; 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"; import { UserMenu } from "@/components/organisms/UserMenu";
export function LandingShell({ children }: { children: ReactNode }) { export function LandingShell({ children }: { children: ReactNode }) {
return ( const clerkEnabled = isClerkEnabled();
<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"> return (
<div <div className="landing-enterprise">
className="absolute inset-0 bg-landing-grid opacity-[0.18] pointer-events-none" <nav className="landing-nav" aria-label="Primary navigation">
aria-hidden="true" <div className="nav-container">
/> <Link href="/" className="logo-section" aria-label="OpenClaw home">
<div <div className="logo-icon" aria-hidden="true">
className="absolute -top-40 right-0 h-72 w-72 rounded-full bg-[color:var(--accent-soft)] blur-3xl pointer-events-none" OC
aria-hidden="true" </div>
/> <div className="logo-text">
<div <div className="logo-name">OpenClaw</div>
className="absolute -bottom-32 left-0 h-72 w-72 rounded-full bg-[color:var(--surface-strong)] blur-3xl pointer-events-none" <div className="logo-tagline">Mission Control</div>
aria-hidden="true" </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> <SignedIn>
<Link href="/boards/new" className="btn-secondary">
Create Board
</Link>
<Link href="/boards" className="btn-primary">
Open Boards
</Link>
<UserMenu /> <UserMenu />
</SignedIn> </SignedIn>
</header> </div>
<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> </div>
); );
} }

View File

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