feat(landing): add landing page components

This commit is contained in:
zhenyi 2026-05-31 13:12:32 +08:00
parent 8b8c9e5e33
commit 1ef37786b1
13 changed files with 1076 additions and 0 deletions

View File

@ -0,0 +1,8 @@
export default function AmbientLight() {
return (
<div className="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
<div className="absolute -top-[30vh] -left-[10vw] h-[70vh] w-[70vw] rounded-full bg-primary/[0.025] blur-[140px]" />
<div className="absolute right-0 top-[10vh] h-[50vh] w-[50vw] rounded-full bg-primary/[0.015] blur-[120px]" />
</div>
);
}

View File

@ -0,0 +1,18 @@
import type { ReactNode } from "react";
type Props = {
icon?: ReactNode;
children: ReactNode;
className?: string;
};
export default function Badge({ icon, children, className = "" }: Props) {
return (
<div
className={`inline-flex items-center gap-2 rounded-full border border-border/60 bg-muted/50 px-3 py-1 text-xs font-medium text-muted-foreground ${className}`}
>
{icon}
{children}
</div>
);
}

View File

@ -0,0 +1,127 @@
import type { ReactNode } from "react";
import { Bot, Code2, GitBranch, GitCommitHorizontal, MessageSquare, Search } from "lucide-react";
export default function DashboardMockup() {
return (
<div className="relative mx-auto mt-16 max-w-5xl overflow-hidden rounded-2xl border border-border/40 bg-card/70 shadow-2xl shadow-primary/10 backdrop-blur-md">
<div className="flex h-[460px] sm:h-[520px]">
{/* Workspace rail */}
<div className="hidden w-[64px] shrink-0 flex-col items-center border-r border-border/40 bg-background/80 py-4 sm:flex">
<div className="grid size-9 place-items-center rounded-xl bg-primary text-primary-foreground shadow-sm shadow-primary/20">
<Code2 className="size-4" />
</div>
<div className="my-4 h-px w-7 bg-border" />
{['GD', 'AI', 'RS'].map((item, index) => (
<div
className={
index === 0
? "mb-2 grid size-9 place-items-center rounded-lg bg-primary text-[11px] font-bold text-primary-foreground ring-2 ring-primary/20"
: "mb-2 grid size-9 place-items-center rounded-xl border border-border bg-card text-[11px] font-bold text-muted-foreground"
}
key={item}
>
{item}
</div>
))}
</div>
{/* Workspace sidebar */}
<div className="hidden w-56 shrink-0 flex-col border-r border-border/40 bg-sidebar p-3 md:flex">
<div className="mb-4 flex h-9 items-center gap-2 rounded-xl px-2 text-sm font-semibold">
<div className="grid size-7 place-items-center rounded-lg bg-primary/10 text-primary">
<Code2 className="size-3.5" />
</div>
GitDataAI
</div>
<div className="grid grid-cols-2 gap-1 rounded-xl bg-muted/70 p-1 text-xs font-semibold">
<div className="rounded-lg bg-card px-2 py-2 text-center shadow-sm ring-1 ring-border">Workplan</div>
<div className="px-2 py-2 text-center text-muted-foreground">Channel</div>
</div>
<div className="mt-5 space-y-1">
<SidebarItem active icon={<GitBranch className="size-3.5" />} label="Repositories" />
<SidebarItem icon={<GitCommitHorizontal className="size-3.5" />} label="Pull requests" />
<SidebarItem icon={<MessageSquare className="size-3.5" />} label="Workplan chat" />
</div>
</div>
{/* Main content */}
<div className="flex min-w-0 flex-1 flex-col bg-background/80">
<div className="flex h-11 shrink-0 items-center gap-2 border-b border-border/40 px-4 text-[13px]">
<span className="text-muted-foreground/50">GitDataAI</span>
<span className="text-muted-foreground/20">/</span>
<span className="font-semibold">core</span>
<div className="ml-auto grid size-9 place-items-center rounded-xl text-muted-foreground/50">
<Search className="size-4" />
</div>
</div>
<div className="min-h-0 flex-1 p-4 sm:p-6">
<div className="rounded-xl border border-border bg-card shadow-sm">
<div className="flex items-center gap-3 border-b border-border px-4 py-3">
<div className="grid size-8 place-items-center rounded-lg bg-primary/10 text-primary">
<GitCommitHorizontal className="size-4" />
</div>
<div className="min-w-0 flex-1">
<div className="truncate text-[13px] font-semibold">feat: implement AI auto-review</div>
<div className="text-[11px] text-muted-foreground">Alice committed 2 hours ago</div>
</div>
<span className="rounded-lg border border-border bg-muted/40 px-2 py-1 font-mono text-[11px] text-muted-foreground">
a1b2c3d
</span>
</div>
<div className="space-y-2 p-4">
{[
{ name: "src/agent/review.rs", status: "+84", tone: "text-emerald-500" },
{ name: "src/api/handler.rs", status: "+27", tone: "text-emerald-500" },
{ name: "tests/ai_test.rs", status: "+16", tone: "text-primary" },
].map((file) => (
<div className="flex items-center gap-3 rounded-xl border border-border/60 bg-muted/20 px-3 py-2 text-[12px]" key={file.name}>
<span className="h-2 w-2 rounded-full bg-current text-primary/60" />
<span className="min-w-0 flex-1 truncate font-mono text-foreground/80">{file.name}</span>
<span className={`font-mono tabular-nums ${file.tone}`}>{file.status}</span>
</div>
))}
</div>
</div>
<div className="absolute bottom-8 right-8 hidden w-72 rounded-2xl border border-primary/20 bg-card p-4 shadow-xl shadow-primary/10 sm:block">
<div className="mb-2 flex items-center gap-2">
<div className="grid size-7 place-items-center rounded-lg bg-primary/10 text-primary">
<Bot className="size-4" />
</div>
<span className="text-[12px] font-bold text-primary">AI Reviewer</span>
</div>
<p className="text-[12px] leading-relaxed text-muted-foreground/80">
Review complete. Add a timeout to the API call before merging.
</p>
</div>
</div>
</div>
</div>
</div>
);
}
function SidebarItem({
active = false,
icon,
label,
}: {
active?: boolean;
icon: ReactNode;
label: string;
}) {
return (
<div
className={
active
? "flex h-9 items-center gap-2 rounded-lg bg-primary/10 px-2.5 text-[13px] font-semibold text-primary ring-1 ring-primary/10"
: "flex h-9 items-center gap-2 rounded-lg px-2.5 text-[13px] text-muted-foreground"
}
>
{icon}
<span className="truncate">{label}</span>
</div>
);
}

View File

@ -0,0 +1,80 @@
import { Link } from "react-router";
import { Code2 } from "lucide-react";
export default function Footer() {
return (
<footer className="border-t border-border/20 bg-muted/[0.1] py-16">
<div className="mx-auto max-w-6xl px-6">
<div className="grid gap-10 sm:grid-cols-4">
{/* Brand */}
<div className="sm:col-span-2">
<div className="flex items-center gap-2.5 font-bold">
<div className="grid size-8 place-items-center rounded-[10px] bg-primary text-primary-foreground shadow-sm shadow-primary/20">
<Code2 className="size-4" />
</div>
<span className="font-heading text-[15px] tracking-tight">
GitDataAI
</span>
</div>
<p className="mt-4 max-w-xs text-[13px] leading-relaxed text-muted-foreground/60">
The all-in-one developer platform git hosting, real-time
collaboration, and AI agents.
</p>
</div>
{/* Product */}
<div>
<h4 className="mb-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground/60">
Product
</h4>
<ul className="space-y-3">
{[
{ label: "Features", href: "#features" },
{ label: "Workflow", href: "#workflow" },
].map((l) => (
<li key={l.label}>
<a
className="text-[13px] text-muted-foreground/60 transition-colors hover:text-foreground/80"
href={l.href}
>
{l.label}
</a>
</li>
))}
</ul>
</div>
{/* Account */}
<div>
<h4 className="mb-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground/60">
Account
</h4>
<ul className="space-y-3">
<li>
<Link
className="text-[13px] text-muted-foreground/60 transition-colors hover:text-foreground/80"
to="/auth/login"
>
Sign in
</Link>
</li>
<li>
<Link
className="text-[13px] text-muted-foreground/60 transition-colors hover:text-foreground/80"
to="/auth/register"
>
Register
</Link>
</li>
</ul>
</div>
</div>
{/* Bottom bar */}
<div className="mt-12 border-t border-border/20 pt-6 text-center text-xs text-muted-foreground/40">
&copy; {new Date().getFullYear()} GitDataAI. All rights reserved.
</div>
</div>
</footer>
);
}

View File

@ -0,0 +1,50 @@
import { Link } from "react-router";
import { Code2, ArrowRight } from "lucide-react";
import { Button } from "@/components/ui/button";
const navItems = [
{ label: "Features", to: "/features" },
{ label: "Workflow", to: "/workflow" },
{ label: "Pricing", to: "/pricing" },
];
export default function Header() {
return (
<header className="sticky top-0 z-50 border-b border-border/30 bg-background/80 backdrop-blur-2xl supports-[backdrop-filter]:bg-background/60">
<div className="mx-auto flex h-14 max-w-6xl items-center px-6">
<Link className="flex items-center gap-2.5 font-bold" to="/">
<div className="grid size-8 place-items-center rounded-[10px] bg-primary text-primary-foreground shadow-sm shadow-primary/20">
<Code2 className="size-4" />
</div>
<span className="font-heading text-[15px] tracking-tight">
GitDataAI
</span>
</Link>
<nav className="ml-8 hidden items-center gap-1 sm:flex">
{navItems.map((item) => (
<Link
className="inline-flex h-9 items-center rounded-lg px-3 text-[13px] font-medium text-muted-foreground/70 transition-colors hover:bg-muted hover:text-foreground"
key={item.to}
to={item.to}
>
{item.label}
</Link>
))}
</nav>
<div className="ml-auto flex items-center gap-3">
<Button size="sm" variant="ghost" asChild>
<Link to="/auth/login">Sign in</Link>
</Button>
<Button size="sm" className="shadow-sm shadow-primary/20" asChild>
<Link to="/auth/register">
Get Started
<ArrowRight className="size-3.5" />
</Link>
</Button>
</div>
</div>
</header>
);
}

View File

@ -0,0 +1,15 @@
export default function Section({
children,
className = "",
id,
}: {
children: React.ReactNode;
className?: string;
id?: string;
}) {
return (
<section className={`relative ${className}`} id={id}>
<div className="mx-auto max-w-6xl px-6">{children}</div>
</section>
);
}

View File

@ -0,0 +1,44 @@
import { Layers } from "lucide-react";
export default function TerminalDemo() {
return (
<div className="overflow-hidden rounded-2xl border border-border/20 bg-card/60 shadow-2xl backdrop-blur">
{/* title bar */}
<div className="flex items-center gap-2 border-b border-border/20 px-4 py-3">
<div className="size-2.5 rounded-full bg-red-500/40" />
<div className="size-2.5 rounded-full bg-amber-500/40" />
<div className="size-2.5 rounded-full bg-emerald-500/40" />
<span className="ml-3 font-mono text-[11px] text-muted-foreground/40">
Terminal
</span>
</div>
{/* body */}
<div className="p-5 font-mono text-[13px] leading-relaxed">
<p className="text-muted-foreground/70">
<span className="text-emerald-500">$</span> git clone{" "}
<span className="text-sky-400">
https://gitdata.ai/acme/backend
</span>
</p>
<p className="mt-2 text-muted-foreground/70">
<span className="text-emerald-500">$</span> git checkout -b{" "}
<span className="text-amber-400">feature/payments</span>
</p>
<p className="mt-2 text-muted-foreground/70">
<span className="text-emerald-500">$</span> git commit -m{" "}
<span className="text-primary/70">
&quot;feat: add Stripe integration&quot;
</span>
</p>
<p className="mt-2 text-foreground/80">
<span className="text-emerald-500">$</span> git push origin
feature/payments
</p>
<div className="mt-5 border-t border-border/20 pt-4 text-xs text-muted-foreground/50">
<Layers className="mr-1 inline size-3" />
PR #128 created · AI review started · #backend notified
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,55 @@
import { GitMerge, MessageSquare, Bot, Workflow, Shield, Terminal } from "lucide-react";
const items = [
{
icon: <GitMerge className="size-5" />,
title: "Git Repositories",
desc: "Full git hosting with branches, tags, pull requests, code review, and webhooks. HTTPS / SSH clone.",
},
{
icon: <MessageSquare className="size-5" />,
title: "Real-time Channels",
desc: "Threads, reactions, file sharing, and article publishing. Voice channels for your team.",
},
{
icon: <Bot className="size-5" />,
title: "AI Agents",
desc: "Built-in AI for code review, issue triage, and task automation. Persistent memory & streaming.",
},
{
icon: <Workflow className="size-5" />,
title: "Issue Tracking",
desc: "Create, assign, and track issues. Labels, milestones, board view, and pull request linking.",
},
{
icon: <Shield className="size-5" />,
title: "Enterprise Security",
desc: "TOTP 2FA, role-based access, audit logs, SSH keys, and private repositories.",
},
{
icon: <Terminal className="size-5" />,
title: "Developer Experience",
desc: "REST + WebSocket APIs, OpenAPI spec, webhooks, and git-native CLI. By devs, for devs.",
},
];
export default function FeaturesGrid() {
return (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{items.map((f) => (
<div
className="group relative cursor-pointer overflow-hidden rounded-2xl border border-border/20 bg-card/50 p-6 backdrop-blur transition-[transform,background-color,border-color,box-shadow] duration-300 hover:-translate-y-0.5 hover:border-primary/10 hover:bg-card hover:shadow-xl"
key={f.title}
>
<div className="mb-5 grid size-11 place-items-center rounded-xl bg-primary/[0.05] text-primary ring-1 ring-primary/[0.04] transition-colors group-hover:bg-primary/[0.08]">
{f.icon}
</div>
<h3 className="font-heading text-[15px] font-semibold">{f.title}</h3>
<p className="mt-2 text-[13px] leading-relaxed text-muted-foreground/65">
{f.desc}
</p>
</div>
))}
</div>
);
}

View File

@ -0,0 +1,151 @@
import { Link } from "react-router";
import { ArrowRight, Zap, GitCommitHorizontal, MessageSquare, Bot } from "lucide-react";
import { Button } from "@/components/ui/button";
import FeaturesGrid from "./features-grid";
export default function FeaturesPage() {
return (
<>
<section className="py-24 lg:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="mx-auto mb-16 max-w-2xl text-center">
<div className="inline-flex items-center gap-2 rounded-full border border-border/60 bg-muted/50 px-3 py-1 text-xs font-medium text-muted-foreground">
<Zap className="size-3 text-primary" />
Platform
</div>
<h1 className="mt-4 font-heading text-4xl font-bold tracking-tight sm:text-5xl">
Everything you need to ship
</h1>
<p className="mt-6 text-lg text-muted-foreground/80">
One integrated platform no stitching together a dozen tools.
</p>
</div>
<FeaturesGrid />
</div>
</section>
{/* Deep Dives */}
<section className="border-t border-border/20 py-24 lg:py-32 bg-muted/10">
<div className="mx-auto max-w-6xl px-6 space-y-32">
{/* Git */}
<div className="grid lg:grid-cols-2 gap-12 items-center">
<div className="order-2 lg:order-1 rounded-2xl border border-border/30 bg-card p-2 shadow-2xl">
<div className="aspect-[4/3] rounded-xl bg-muted/30 flex flex-col p-6">
<div className="flex items-center gap-2 border-b border-border/30 pb-4">
<GitCommitHorizontal className="size-5 text-primary" />
<span className="font-mono text-sm font-semibold">commit history</span>
</div>
<div className="mt-4 space-y-4">
{[1,2,3].map(i => (
<div key={i} className="flex gap-4 items-center">
<div className="size-8 rounded-full bg-primary/20 shrink-0" />
<div className="space-y-1.5 flex-1">
<div className="h-3 w-3/4 bg-primary/20 rounded" />
<div className="h-2 w-1/4 bg-muted-foreground/20 rounded" />
</div>
</div>
))}
</div>
</div>
</div>
<div className="order-1 lg:order-2 space-y-6">
<div className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10 text-primary">
<GitCommitHorizontal className="size-6" />
</div>
<h2 className="text-3xl font-bold font-heading">Enterprise-grade Git Hosting</h2>
<p className="text-lg text-muted-foreground/80 leading-relaxed">
Experience lightning-fast git operations. Branch, tag, and merge with confidence. Our architecture ensures your code is safe, accessible, and always available.
</p>
<ul className="space-y-3 text-muted-foreground">
<li className="flex items-center gap-3"> SSH and HTTPS cloning</li>
<li className="flex items-center gap-3"> Branch protection rules</li>
<li className="flex items-center gap-3"> Comprehensive audit logs</li>
</ul>
</div>
</div>
{/* Chat */}
<div className="grid lg:grid-cols-2 gap-12 items-center">
<div className="space-y-6">
<div className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10 text-primary">
<MessageSquare className="size-6" />
</div>
<h2 className="text-3xl font-bold font-heading">Contextual Conversations</h2>
<p className="text-lg text-muted-foreground/80 leading-relaxed">
Real-time channels integrated directly into your workflow. Discuss code, share files, and resolve issues without context switching.
</p>
<ul className="space-y-3 text-muted-foreground">
<li className="flex items-center gap-3"> Threaded conversations</li>
<li className="flex items-center gap-3"> Link unfurling for PRs & Issues</li>
<li className="flex items-center gap-3"> Voice and screen sharing</li>
</ul>
</div>
<div className="rounded-2xl border border-border/30 bg-card p-2 shadow-2xl">
<div className="aspect-[4/3] rounded-xl bg-muted/30 flex flex-col p-6">
<div className="flex items-center gap-2 border-b border-border/30 pb-4">
<MessageSquare className="size-5 text-primary" />
<span className="font-semibold text-sm"># engineering</span>
</div>
<div className="mt-4 space-y-4 flex-1 flex flex-col justify-end">
<div className="flex gap-3">
<div className="size-8 rounded bg-primary/20 shrink-0" />
<div className="bg-background rounded-2xl rounded-tl-none p-3 shadow-sm border border-border/50">
<div className="h-2 w-48 bg-muted-foreground/20 rounded mb-2" />
<div className="h-2 w-32 bg-muted-foreground/20 rounded" />
</div>
</div>
<div className="flex gap-3 flex-row-reverse">
<div className="size-8 rounded bg-primary shrink-0" />
<div className="bg-primary text-primary-foreground rounded-2xl rounded-tr-none p-3 shadow-sm">
<div className="h-2 w-40 bg-primary-foreground/40 rounded mb-2" />
<div className="h-2 w-24 bg-primary-foreground/40 rounded" />
</div>
</div>
</div>
</div>
</div>
</div>
{/* AI */}
<div className="grid lg:grid-cols-2 gap-12 items-center">
<div className="order-2 lg:order-1 rounded-2xl border border-border/30 bg-card p-2 shadow-2xl">
<div className="aspect-[4/3] rounded-xl bg-primary/5 flex flex-col items-center justify-center p-6 relative overflow-hidden">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,var(--color-primary)_0,transparent_50%)] opacity-10 blur-xl" />
<Bot className="size-24 text-primary mb-6 animate-pulse" />
<div className="h-3 w-48 bg-primary/20 rounded mb-3" />
<div className="h-3 w-64 bg-primary/20 rounded" />
</div>
</div>
<div className="order-1 lg:order-2 space-y-6">
<div className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10 text-primary">
<Bot className="size-6" />
</div>
<h2 className="text-3xl font-bold font-heading">Intelligent Automation</h2>
<p className="text-lg text-muted-foreground/80 leading-relaxed">
Deploy autonomous agents that understand your codebase. Have them review PRs, triage issues, and answer questions.
</p>
<ul className="space-y-3 text-muted-foreground">
<li className="flex items-center gap-3"> Automated Code Review</li>
<li className="flex items-center gap-3"> Semantic Code Search</li>
<li className="flex items-center gap-3"> Smart Issue Triage</li>
</ul>
</div>
</div>
</div>
<div className="mt-32 text-center">
<h2 className="text-2xl font-bold mb-8">Ready to experience it?</h2>
<Button asChild size="lg" className="h-11 px-8 shadow-lg shadow-primary/20">
<Link to="/auth/register">
Start building free
<ArrowRight className="ml-2 size-4" />
</Link>
</Button>
</div>
</section>
</>
);
}

190
src/page/landing/home.tsx Normal file
View File

@ -0,0 +1,190 @@
import { Link } from "react-router";
import { ArrowRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import FeaturesGrid from "./features-grid";
import TerminalDemo from "@/components/landing/terminal-demo";
import DashboardMockup from "@/components/landing/dashboard-mockup";
import { GitBranch, Layers, MessageSquare, Bot } from "lucide-react";
export default function LandingHome() {
return (
<>
{/* Hero */}
<section className="relative flex min-h-[88vh] items-center">
<div className="mx-auto w-full max-w-6xl px-6 py-24 lg:py-28">
<div className="mx-auto max-w-3xl text-center">
<div className="mx-auto mb-8 inline-flex items-center gap-2 rounded-full border border-primary/15 bg-primary/[0.03] px-4 py-1.5 text-xs font-medium text-primary/80">
<Layers className="size-3" />
The all-in-one developer platform
</div>
<h1 className="text-balance font-heading text-5xl font-bold leading-[1.05] tracking-tight sm:text-6xl lg:text-7xl">
Build. Ship.{" "}
<span className="bg-gradient-to-r from-primary via-primary to-primary/40 bg-clip-text text-transparent">
With intelligence.
</span>
</h1>
<p className="mx-auto mt-6 max-w-lg text-balance text-lg leading-relaxed text-muted-foreground/80">
Git hosting, real-time collaboration, and AI agents one platform
for modern dev teams who ship fast.
</p>
<div className="mt-10 flex flex-wrap items-center justify-center gap-4">
<Button asChild size="lg" className="h-11 px-6 shadow-lg shadow-primary/20">
<Link to="/auth/register">
Start building free
<ArrowRight className="size-4" />
</Link>
</Button>
<Button asChild size="lg" variant="outline" className="h-11 px-6">
<Link to="/auth/login">Sign in</Link>
</Button>
</div>
<p className="mt-6 text-xs text-muted-foreground/45">
Free to start. No credit card required.
</p>
</div>
<DashboardMockup />
</div>
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-40 bg-gradient-to-t from-background via-background/80 to-transparent" />
</section>
{/* Product loop */}
<section className="border-t border-border/20 bg-muted/[0.1] py-10">
<div className="mx-auto grid max-w-6xl gap-3 px-6 sm:grid-cols-3">
{[
{ label: "Git-native", value: "Repos, PRs, branches" },
{ label: "AI in context", value: "Review, triage, automate" },
{ label: "Team sync", value: "Channels tied to work" },
].map((item) => (
<div className="rounded-2xl border border-border/30 bg-card/70 px-5 py-4 text-center shadow-sm" key={item.label}>
<div className="font-heading text-sm font-semibold text-foreground">{item.label}</div>
<div className="mt-1 text-xs text-muted-foreground/70">{item.value}</div>
</div>
))}
</div>
</section>
{/* Features overview */}
<section className="border-t border-border/20 bg-muted/[0.1] py-24 lg:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="mx-auto mb-12 max-w-2xl text-center">
<h2 className="font-heading text-3xl font-bold tracking-tight">
Everything you need to ship
</h2>
</div>
<FeaturesGrid />
<div className="mt-10 text-center">
<Button asChild variant="outline">
<Link to="/features">
Explore all features
<ArrowRight className="size-4" />
</Link>
</Button>
</div>
</div>
</section>
{/* Workflow + Terminal */}
<section className="border-t border-border/20 py-24 lg:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="grid items-center gap-12 lg:grid-cols-2 lg:gap-20">
<div>
<div className="inline-flex items-center gap-2 rounded-full border border-border/60 bg-muted/50 px-3 py-1 text-xs font-medium text-muted-foreground">
<GitBranch className="size-3 text-primary" />
Workflow
</div>
<h2 className="mt-4 font-heading text-3xl font-bold tracking-tight">
Standard git, supercharged
</h2>
<p className="mt-4 text-muted-foreground/70">
Same workflow you know. AI reviews every PR. Channels keep your
team in sync. Deploy with confidence.
</p>
<div className="mt-6">
<Button asChild variant="outline">
<Link to="/workflow">
See how it works
<ArrowRight className="size-4" />
</Link>
</Button>
</div>
</div>
<TerminalDemo />
</div>
</div>
</section>
{/* Product fit */}
<section className="border-t border-border/20 bg-muted/[0.1] py-24 lg:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="mx-auto mb-16 max-w-2xl text-center">
<h2 className="font-heading text-3xl font-bold tracking-tight">
One workspace for code, agents, and discussion
</h2>
<p className="mt-4 text-muted-foreground/70">
The interface mirrors the app itself: repository work on the left,
live context in channels, and AI assistance close to every task.
</p>
</div>
<div className="grid gap-6 md:grid-cols-3">
{[
{
icon: GitBranch,
title: "Trace work to code",
desc: "Repositories, issues, pull requests, and commits stay in the same workspace structure.",
},
{
icon: MessageSquare,
title: "Discuss with context",
desc: "Channels keep team decisions next to the repositories and workplans they affect.",
},
{
icon: Bot,
title: "Bring agents into flow",
desc: "AI review and automation live beside human collaboration instead of in a separate tool.",
},
].map((item) => (
<div key={item.title} className="rounded-2xl border border-border/30 bg-card p-8 shadow-sm">
<div className="grid size-11 place-items-center rounded-xl bg-primary/10 text-primary">
<item.icon className="size-5" />
</div>
<h3 className="mt-5 font-heading text-base font-semibold">{item.title}</h3>
<p className="mt-2 text-[13px] leading-relaxed text-muted-foreground/70">{item.desc}</p>
</div>
))}
</div>
</div>
</section>
{/* Bottom CTA */}
<section className="border-t border-border/20 py-24 lg:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="relative overflow-hidden rounded-3xl border border-border/20 bg-gradient-to-br from-primary/[0.05] via-primary/[0.01] to-transparent p-12 text-center shadow-2xl shadow-primary/[0.02] lg:p-16">
<div className="pointer-events-none absolute -right-16 -top-16 size-64 rounded-full bg-primary/[0.03] blur-[100px]" />
<h2 className="relative font-heading text-3xl font-bold tracking-tight">
Ready to ship faster?
</h2>
<p className="relative mt-4 text-muted-foreground/60">
Join teams using GitDataAI to build better software, together.
</p>
<div className="relative mt-8 flex items-center justify-center gap-4">
<Button asChild size="lg" className="h-11 px-6 shadow-lg shadow-primary/20">
<Link to="/auth/register">
Get started free
<ArrowRight className="size-4" />
</Link>
</Button>
<Button asChild size="lg" variant="outline" className="h-11 px-6">
<Link to="/auth/login">Sign in</Link>
</Button>
</div>
</div>
</div>
</section>
</>
);
}

View File

@ -0,0 +1,36 @@
import { useEffect } from "react";
import { Outlet, useNavigate } from "react-router";
import { Loader2 } from "lucide-react";
import { useAuth } from "@/context/auth-context";
import AmbientLight from "@/components/landing/ambient-light";
import Header from "@/components/landing/header";
import Footer from "@/components/landing/footer";
export default function LandingLayout() {
const { isAuthenticated, isLoading } = useAuth();
const navigate = useNavigate();
useEffect(() => {
if (!isLoading && isAuthenticated) navigate("/me", { replace: true });
}, [isAuthenticated, isLoading, navigate]);
if (isLoading) {
return (
<div className="flex h-screen items-center justify-center bg-background">
<Loader2 className="size-5 animate-spin text-muted-foreground/30" />
</div>
);
}
if (isAuthenticated) return null;
return (
<div className="relative min-h-screen overflow-x-hidden bg-background font-sans antialiased">
<AmbientLight />
<Header />
<main className="min-h-[60vh]">
<Outlet />
</main>
<Footer />
</div>
);
}

View File

@ -0,0 +1,193 @@
import { Link } from "react-router";
import { CheckCircle2, XCircle } from "lucide-react";
import { Button } from "@/components/ui/button";
const plans = [
{
name: "Free",
price: "$0",
period: "forever",
desc: "For individuals and small teams getting started.",
features: [
"Unlimited public repositories",
"5 private repositories",
"Real-time channels",
"Community support",
"1 GB storage",
],
cta: "Get started free",
href: "/auth/register",
primary: false,
},
{
name: "Pro",
price: "$12",
period: "/ month",
desc: "For growing teams that need more power.",
features: [
"Unlimited private repositories",
"AI code review agents",
"Voice channels",
"Priority support",
"50 GB storage",
"Advanced analytics",
],
cta: "Start Pro trial",
href: "/auth/register",
primary: true,
},
{
name: "Enterprise",
price: "Custom",
period: "",
desc: "For large organizations with custom needs.",
features: [
"Everything in Pro",
"SSO & SAML",
"Audit logs",
"Dedicated support",
"Unlimited storage",
"Custom integrations",
"On-premise option",
],
cta: "Contact sales",
href: "/auth/register",
primary: false,
},
];
const compareFeatures = [
{ name: "Public Repositories", free: true, pro: true, ent: true },
{ name: "Private Repositories", free: "5", pro: "Unlimited", ent: "Unlimited" },
{ name: "Storage", free: "1 GB", pro: "50 GB", ent: "Unlimited" },
{ name: "Real-time Channels", free: true, pro: true, ent: true },
{ name: "Voice Channels", free: false, pro: true, ent: true },
{ name: "AI Code Review", free: false, pro: true, ent: true },
{ name: "Custom AI Agents", free: false, pro: "3 included", ent: "Unlimited" },
{ name: "SSO & SAML", free: false, pro: false, ent: true },
{ name: "Audit Logs", free: false, pro: false, ent: true },
{ name: "Support", free: "Community", pro: "Priority", ent: "Dedicated" },
];
export default function PricingPage() {
return (
<>
<section className="py-24 lg:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="mx-auto mb-16 max-w-2xl text-center">
<h1 className="font-heading text-4xl font-bold tracking-tight sm:text-5xl">
Simple, transparent pricing
</h1>
<p className="mt-6 text-lg text-muted-foreground/80">
Start for free. Upgrade when you need more. No hidden fees.
</p>
</div>
<div className="grid gap-6 lg:grid-cols-3">
{plans.map((p) => (
<div
className={`relative flex flex-col rounded-3xl border p-8 transition-transform hover:-translate-y-1 ${
p.primary
? "border-primary/30 bg-primary/[0.03] shadow-2xl shadow-primary/[0.05] ring-1 ring-primary/10"
: "border-border/40 bg-card/50 shadow-lg backdrop-blur"
}`}
key={p.name}
>
{p.primary && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-gradient-to-r from-primary to-primary/80 px-4 py-1 text-xs font-bold text-primary-foreground shadow-sm">
Most popular
</div>
)}
<h3 className="font-heading text-xl font-bold">{p.name}</h3>
<div className="mt-4 flex items-baseline gap-1">
<span className="font-heading text-5xl font-bold tracking-tight">
{p.price}
</span>
{p.period && (
<span className="text-sm font-medium text-muted-foreground/70">
{p.period}
</span>
)}
</div>
<p className="mt-4 text-sm text-muted-foreground/80 leading-relaxed min-h-[40px]">{p.desc}</p>
<ul className="mt-8 flex-1 space-y-4">
{p.features.map((f) => (
<li className="flex items-start gap-3 text-sm" key={f}>
<CheckCircle2 className="mt-0.5 size-4 shrink-0 text-primary" />
<span className="text-foreground/90 font-medium">{f}</span>
</li>
))}
</ul>
<Button
asChild
className={`mt-10 h-11 w-full ${p.primary ? 'shadow-lg shadow-primary/20' : ''}`}
variant={p.primary ? "default" : "outline"}
>
<Link to={p.href}>
{p.cta}
</Link>
</Button>
</div>
))}
</div>
</div>
</section>
{/* Feature Comparison */}
<section className="py-24 border-t border-border/20 bg-muted/10">
<div className="mx-auto max-w-5xl px-6">
<h2 className="text-3xl font-bold font-heading text-center mb-12">Compare features</h2>
<div className="overflow-x-auto">
<table className="w-full text-left border-collapse">
<thead>
<tr className="border-b border-border/40">
<th className="p-4 font-semibold text-muted-foreground w-1/3">Feature</th>
<th className="p-4 font-bold text-center w-2/9">Free</th>
<th className="p-4 font-bold text-center w-2/9 text-primary">Pro</th>
<th className="p-4 font-bold text-center w-2/9">Enterprise</th>
</tr>
</thead>
<tbody className="divide-y divide-border/20">
{compareFeatures.map((f, i) => (
<tr key={i} className="hover:bg-muted/30 transition-colors">
<td className="p-4 text-sm font-medium text-foreground/80">{f.name}</td>
<td className="p-4 text-center">
{typeof f.free === 'boolean' ? (f.free ? <CheckCircle2 className="size-4 text-primary mx-auto" /> : <XCircle className="size-4 text-muted-foreground/30 mx-auto" />) : <span className="text-sm font-medium">{f.free}</span>}
</td>
<td className="p-4 text-center bg-primary/[0.02]">
{typeof f.pro === 'boolean' ? (f.pro ? <CheckCircle2 className="size-4 text-primary mx-auto" /> : <XCircle className="size-4 text-muted-foreground/30 mx-auto" />) : <span className="text-sm font-bold text-primary">{f.pro}</span>}
</td>
<td className="p-4 text-center">
{typeof f.ent === 'boolean' ? (f.ent ? <CheckCircle2 className="size-4 text-primary mx-auto" /> : <XCircle className="size-4 text-muted-foreground/30 mx-auto" />) : <span className="text-sm font-medium">{f.ent}</span>}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</section>
{/* FAQ */}
<section className="py-24 border-t border-border/20">
<div className="mx-auto max-w-3xl px-6">
<h2 className="text-3xl font-bold font-heading text-center mb-12">Frequently asked questions</h2>
<div className="space-y-6">
{[
{ q: "Can I use GitDataAI for free?", a: "Yes, our Free plan gives you everything you need to get started, including unlimited public repos, 5 private repos, and core channel features." },
{ q: "How do AI agents work?", a: "AI agents are built into the platform. You can invite them to PRs for automatic code review, or @mention them in channels to answer questions based on your codebase context." },
{ q: "Do you offer an on-premise version?", a: "Yes, GitDataAI Enterprise can be deployed on your own infrastructure (AWS, GCP, Azure, or bare metal). Contact our sales team for details." }
].map((faq, i) => (
<div key={i} className="border border-border/30 rounded-xl p-6 bg-card">
<h4 className="font-bold text-lg">{faq.q}</h4>
<p className="mt-2 text-muted-foreground/80 leading-relaxed">{faq.a}</p>
</div>
))}
</div>
</div>
</section>
</>
);
}

View File

@ -0,0 +1,109 @@
import { Link } from "react-router";
import { ArrowRight, GitBranch, ArrowDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import TerminalDemo from "@/components/landing/terminal-demo";
const steps = [
{
title: "1. Create a workspace",
desc: "Set up a workspace for your team and invite members. Public or private — you decide.",
},
{
title: "2. Push code",
desc: "Clone repositories via SSH or HTTPS. Push branches, create tags. Standard git, nothing new to learn.",
},
{
title: "3. Open pull requests",
desc: "Create PRs with descriptions and reviewers. AI agents automatically review code for common issues.",
},
{
title: "4. Collaborate in channels",
desc: "Discuss PRs and issues in real-time channels. Threads keep conversations organized. Voice channels for live reviews.",
},
{
title: "5. Ship with confidence",
desc: "Merge, tag releases, and deploy. Webhooks integrate with your CI/CD pipeline. Monitor everything.",
},
];
export default function WorkflowPage() {
return (
<>
<section className="py-24 lg:py-32">
<div className="mx-auto max-w-6xl px-6">
<div className="mx-auto mb-16 max-w-2xl text-center">
<div className="inline-flex items-center gap-2 rounded-full border border-border/60 bg-muted/50 px-3 py-1 text-xs font-medium text-muted-foreground">
<GitBranch className="size-3 text-primary" />
Workflow
</div>
<h1 className="mt-4 font-heading text-4xl font-bold tracking-tight sm:text-5xl">
From idea to production
</h1>
<p className="mt-6 text-lg text-muted-foreground/80">
Five steps to ship your next feature. Same tools you know, better together.
</p>
</div>
<div className="grid items-start gap-16 lg:grid-cols-2 lg:gap-24 mt-20">
{/* Steps */}
<div className="relative border-l-2 border-primary/20 pl-8 space-y-12">
{steps.map((s, i) => (
<div className="relative" key={s.title}>
<span className="absolute -left-[43px] top-1 flex size-5 items-center justify-center rounded-full bg-primary text-[10px] font-bold text-primary-foreground ring-4 ring-background">
{i + 1}
</span>
<h3 className="font-heading text-xl font-bold">
{s.title}
</h3>
<p className="mt-3 text-[15px] leading-relaxed text-muted-foreground/80">
{s.desc}
</p>
</div>
))}
</div>
{/* Visuals */}
<div className="lg:sticky lg:top-32 space-y-8">
<TerminalDemo />
<div className="rounded-2xl border border-border/30 bg-card p-6 shadow-xl">
<div className="flex items-center gap-3 border-b border-border/30 pb-4 mb-4">
<div className="size-10 rounded-xl bg-primary/10 flex items-center justify-center text-primary">
<ArrowDown className="size-5" />
</div>
<div>
<div className="font-semibold text-sm">Automated CI/CD Pipeline</div>
<div className="text-xs text-muted-foreground">Triggered on merge to main</div>
</div>
</div>
<div className="space-y-3">
<div className="flex justify-between text-xs p-2 rounded bg-muted/30">
<span className="font-mono text-muted-foreground">Lint & Format</span>
<span className="text-emerald-500">Success</span>
</div>
<div className="flex justify-between text-xs p-2 rounded bg-muted/30">
<span className="font-mono text-muted-foreground">Unit Tests</span>
<span className="text-emerald-500">Success</span>
</div>
<div className="flex justify-between text-xs p-2 rounded bg-primary/5 border border-primary/20">
<span className="font-mono text-primary font-medium">Deploy to Production</span>
<span className="animate-pulse text-primary">Running</span>
</div>
</div>
</div>
</div>
</div>
<div className="mt-24 text-center">
<Button asChild size="lg" className="h-11 px-8 shadow-lg shadow-primary/20">
<Link to="/auth/register">
Start building free
<ArrowRight className="ml-2 size-4" />
</Link>
</Button>
</div>
</div>
</section>
</>
);
}