feat(web): marketing landing page with Anthropic design system

Landing page at / matching claude.com/product/claude-code structure:
hero, surfaces, pricing, laptop-to-laptop, features, meets-you, faq, cta,
+ floating "Latest news" toaster. Motion-based scroll reveals.

Design system extracted from claude.com via playwriter reverse-engineering:
- Self-hosted Anthropic Sans/Serif/Mono fonts (6 woff2 files)
- --cm-* tokens in globals.css (clay #d97757, gray-050..900, fluid clamps)
- Serif display, Sans UI, Mono terminals & section markers
- Italic clay phrases for emphasis

Header rewritten for design consistency: claudemesh wordmark (mesh glyph +
serif), dark bg, nav (Docs · Pricing · Changelog · GitHub), "Start free" CTA.

Free-first messaging: hero subhead "Free and open-source. Forever.", primary
CTA "Start free", pricing defaults to Solo=Free.

Fixes:
- packages/api: comment out aiRouter (module removed in 1f094c4)
- packages/db/schema/mesh.ts: rename memberRelations → meshMemberRelations
  (missed in beeaa3b rename pass, caught via web build — ack'd by BotMou)
- credits/{api,server,index}: stub out @turbostarter/ai/credits/utils
- remove (marketing)/legal/[slug] route and common/mdx.tsx (cms-backed)
- sitemap: drop blog/legal enumeration (cms removed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 22:09:38 +01:00
parent e25115f1b0
commit 84e14ff410
28 changed files with 1358 additions and 2058 deletions

View File

@@ -1,76 +0,0 @@
import { notFound } from "next/navigation";
import {
CollectionType,
getContentItemBySlug,
getContentItems,
} from "@turbostarter/cms";
import { getTranslation } from "@turbostarter/i18n/server";
import { getMetadata } from "~/lib/metadata";
import { Mdx } from "~/modules/common/mdx";
import {
Section,
SectionBadge,
SectionDescription,
SectionHeader,
SectionTitle,
} from "~/modules/marketing/layout/section";
interface PageParams {
params: Promise<{
slug: string;
locale: string;
}>;
}
export default async function Page({ params }: PageParams) {
const item = getContentItemBySlug({
collection: CollectionType.LEGAL,
slug: (await params).slug,
locale: (await params).locale,
});
if (!item) {
return notFound();
}
const { t } = await getTranslation({ ns: "common" });
return (
<Section>
<SectionHeader>
<SectionBadge>{t("legal.label")}</SectionBadge>
<SectionTitle as="h1">{item.title}</SectionTitle>
<SectionDescription>{item.description}</SectionDescription>
</SectionHeader>
<Mdx mdx={item.mdx} />
</Section>
);
}
export function generateStaticParams() {
return getContentItems({ collection: CollectionType.LEGAL }).items.map(
({ slug, locale }) => ({
slug,
locale,
}),
);
}
export async function generateMetadata({ params }: PageParams) {
const item = getContentItemBySlug({
collection: CollectionType.LEGAL,
slug: (await params).slug,
locale: (await params).locale,
});
if (!item) {
return notFound();
}
return getMetadata({
title: item.title,
description: item.description,
})({ params });
}

View File

@@ -1,42 +1,29 @@
"use client";
import { useTranslation } from "@turbostarter/i18n";
import { buttonVariants } from "@turbostarter/ui-web/button";
import { Icons } from "@turbostarter/ui-web/icons";
import { pathsConfig } from "~/config/paths";
import { TurboLink } from "~/modules/common/turbo-link";
import { Hero } from "~/modules/marketing/home/hero";
import { Surfaces } from "~/modules/marketing/home/surfaces";
import { Pricing } from "~/modules/marketing/home/pricing";
import { LaptopToLaptop } from "~/modules/marketing/home/laptop-to-laptop";
import { Features } from "~/modules/marketing/home/features";
import { MeetsYou } from "~/modules/marketing/home/meets-you";
import { FAQ } from "~/modules/marketing/home/faq";
import { CallToAction } from "~/modules/marketing/home/cta";
import { LatestNewsToaster } from "~/modules/marketing/home/toaster";
const HomePage = () => {
const { t } = useTranslation("common");
return (
<main className="flex min-h-[calc(100vh-4rem)] flex-col items-center justify-center px-4">
<div className="mx-auto max-w-3xl text-center">
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl">
{t("home.title", { defaultValue: "Welcome to TurboStarter" })}
</h1>
<p className="mt-6 text-lg leading-8 text-muted-foreground">
{t("home.description", { defaultValue: "The fastest way to build your next SaaS. Authentication, billing, database, and UI components — all pre-configured and ready to go." })}
</p>
<div className="mt-10 flex items-center justify-center gap-x-6">
<TurboLink
href={pathsConfig.auth.login}
className={buttonVariants({ size: "lg" })}
>
{t("home.getStarted", { defaultValue: "Get Started" })}
<Icons.ArrowRight className="ml-2 size-4" />
</TurboLink>
<TurboLink
href="https://turbostarter.dev/docs"
className={buttonVariants({ variant: "outline", size: "lg" })}
target="_blank"
>
{t("home.documentation", { defaultValue: "Documentation" })}
</TurboLink>
</div>
</div>
</main>
<div
className="bg-[var(--cm-bg)] text-[var(--cm-fg)] antialiased"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<Hero />
<Surfaces />
<Pricing />
<LaptopToLaptop />
<Features />
<MeetsYou />
<FAQ />
<CallToAction />
<LatestNewsToaster />
</div>
);
};

View File

@@ -1,4 +1,3 @@
import { CollectionType, getContentItems } from "@turbostarter/cms";
import { getPathname, config } from "@turbostarter/i18n";
import { appConfig } from "~/config/app";
@@ -52,29 +51,5 @@ export default function sitemap(): MetadataRoute.Sitemap {
changeFrequency: "monthly",
priority: 0.8,
},
{
...getEntry(pathsConfig.marketing.blog.index),
lastModified: new Date(),
changeFrequency: "monthly",
priority: 0.8,
},
...getContentItems({
collection: CollectionType.BLOG,
locale: appConfig.locale,
}).items.map<MetadataRoute.Sitemap[number]>((post) => ({
...getEntry(pathsConfig.marketing.blog.post(post.slug)),
lastModified: new Date(post.lastModifiedAt),
changeFrequency: "monthly",
priority: 0.7,
})),
...getContentItems({
collection: CollectionType.LEGAL,
locale: appConfig.locale,
}).items.map<MetadataRoute.Sitemap[number]>((post) => ({
...getEntry(pathsConfig.marketing.legal(post.slug)),
lastModified: new Date(post.lastModifiedAt),
changeFrequency: "yearly",
priority: 0.5,
})),
];
}

View File

@@ -2,3 +2,102 @@
@import "@turbostarter/ui-web/globals.css";
@source "../../../../../packages/ui";
/* ============================================================
claudemesh — Anthropic design system
Fonts, tokens, and primitives extracted from claude.com
============================================================ */
@font-face {
font-family: "Anthropic Sans";
src: url("/fonts/AnthropicSans-Roman.woff2") format("woff2");
font-weight: 300 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Anthropic Sans";
src: url("/fonts/AnthropicSans-Italic.woff2") format("woff2");
font-weight: 300 800;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Anthropic Serif";
src: url("/fonts/AnthropicSerif-Roman.woff2") format("woff2");
font-weight: 300 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Anthropic Serif";
src: url("/fonts/AnthropicSerif-Italic.woff2") format("woff2");
font-weight: 300 800;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Anthropic Mono";
src: url("/fonts/AnthropicMono-Roman.woff2") format("woff2");
font-weight: 300 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Anthropic Mono";
src: url("/fonts/AnthropicMono-Italic.woff2") format("woff2");
font-weight: 300 800;
font-style: italic;
font-display: swap;
}
:root {
/* --- Anthropic swatch palette (extracted from claude.com) --- */
--cm-clay: #d97757;
--cm-clay-hover: #c96442;
--cm-fig: #c46686;
--cm-oat: #e3dacc;
--cm-cactus: #bcd1ca;
--cm-gray-000: #ffffff;
--cm-gray-050: #faf9f5;
--cm-gray-150: #f0eee6;
--cm-gray-350: #c2c0b6;
--cm-gray-450: #9c9a92;
--cm-gray-800: #262624;
--cm-gray-850: #1f1e1d;
--cm-gray-900: #141413;
--cm-bg: var(--cm-gray-900);
--cm-bg-elevated: var(--cm-gray-850);
--cm-bg-hover: var(--cm-gray-800);
--cm-fg: var(--cm-gray-050);
--cm-fg-secondary: #c2c0b6;
--cm-fg-tertiary: #87867f;
--cm-border: rgba(217, 119, 87, 0.2);
--cm-border-hover: rgba(217, 119, 87, 0.5);
/* --- Type families --- */
--cm-font-sans: "Anthropic Sans", -apple-system, system-ui, Arial, sans-serif;
--cm-font-serif: "Anthropic Serif", Georgia, "Times New Roman", serif;
--cm-font-mono: "Anthropic Mono", "JetBrains Mono", ui-monospace, monospace;
/* --- Type scale (fluid, from Anthropic clamps) --- */
--cm-text-h1: clamp(2.125rem, 1.8rem + 2.6vw, 3.25rem);
--cm-text-h2: clamp(1.875rem, 1.625rem + 1.95vw, 2.75rem);
--cm-text-h3: clamp(1.75rem, 1.607rem + 1.12vw, 2.25rem);
--cm-text-body-lg: clamp(1.1875rem, 1.17rem + 0.14vw, 1.25rem);
/* --- Spacing --- */
--cm-gutter: 2rem;
--cm-max-w: 90rem;
/* --- Radii --- */
--cm-radius-xs: 0.25rem;
--cm-radius-md: 0.5rem;
--cm-radius-lg: 1rem;
/* --- Motion --- */
--cm-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
--cm-dur: 300ms;
}

View File

@@ -1,11 +1,9 @@
import { handle } from "@turbostarter/api/utils";
import { api } from "~/lib/api/client";
// AI credits were backed by the removed @turbostarter/ai package.
// claudemesh does not meter AI credits, so this stubs the query to return null.
export const queries = {
get: (params: { id: string }) => ({
queryKey: ["credits", params.id],
queryFn: () => handle(api.ai.credits.$get)(),
queryFn: () => Promise.resolve(null as number | null),
}),
};

View File

@@ -2,10 +2,6 @@ import NumberFlow from "@number-flow/react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { motion } from "motion/react";
import {
getCreditsLevel,
getCreditsProgress,
} from "@turbostarter/ai/credits/utils";
import { useTranslation } from "@turbostarter/i18n";
import { cn } from "@turbostarter/ui";
@@ -13,6 +9,15 @@ import { authClient } from "~/lib/auth/client";
import { credits } from "./api";
// Local replacements — @turbostarter/ai package removed in claudemesh fork.
// claudemesh does not meter AI credits (not an AI consumption product), but
// the surrounding UI still calls these with a number.
type CreditsLevel = "high" | "medium" | "low";
const getCreditsLevel = (n: number): CreditsLevel =>
n > 500 ? "high" : n > 100 ? "medium" : "low";
const getCreditsProgress = (n: number): number =>
Math.max(0, Math.min(1, n / 1000));
export const useCredits = () => {
const queryClient = useQueryClient();
const { data } = authClient.useSession();

View File

@@ -1,17 +1,9 @@
import { handle } from "@turbostarter/api/utils";
import { api } from "~/lib/api/server";
import { getQueryClient } from "~/lib/query/server";
import { credits } from "./api";
export const prefetchCredits = async (id: string) => {
const queryClient = getQueryClient();
await queryClient.prefetchQuery({
...credits.queries.get({ id }),
queryFn: handle(api.ai.credits.$get),
});
await queryClient.prefetchQuery(credits.queries.get({ id }));
return queryClient;
};

View File

@@ -1,13 +0,0 @@
import { MDXContent } from "@content-collections/mdx/react";
interface MdxProps {
readonly mdx: string;
}
export const Mdx = ({ mdx }: MdxProps) => {
return (
<div className="prose dark:prose-invert prose-headings:font-semibold py-6">
<MDXContent code={mdx} />
</div>
);
};

View File

@@ -0,0 +1,108 @@
"use client";
import { motion, type Variants } from "motion/react";
import type { ReactNode } from "react";
const fade: Variants = {
hidden: { opacity: 0, y: 32 },
visible: (i: number = 0) => ({
opacity: 1,
y: 0,
transition: {
duration: 0.7,
ease: [0.22, 0.61, 0.36, 1],
delay: i * 0.08,
},
}),
};
export const Reveal = ({
children,
delay = 0,
as: Tag = "div",
className,
}: {
children: ReactNode;
delay?: number;
as?: keyof typeof motion;
className?: string;
}) => {
const M = motion[Tag] as typeof motion.div;
return (
<M
className={className}
variants={fade}
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: "-80px" }}
custom={delay}
>
{children}
</M>
);
};
export const RevealStagger = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => (
<motion.div
className={className}
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: "-80px" }}
variants={{
hidden: {},
visible: { transition: { staggerChildren: 0.1 } },
}}
>
{children}
</motion.div>
);
export const StaggerItem = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => (
<motion.div className={className} variants={fade}>
{children}
</motion.div>
);
const leafPath =
"M12 2c-2 4-5 6-5 10a5 5 0 0010 0c0-4-3-6-5-10z";
export const SectionIcon = ({
glyph = "leaf",
}: {
glyph?: "leaf" | "arrow" | "grid" | "phone" | "terminal" | "mesh";
}) => {
const paths: Record<string, string> = {
leaf: leafPath,
arrow: "M5 12h14m-6-6l6 6-6 6",
grid: "M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4zM14 14h6v6h-6z",
phone: "M7 3h10a1 1 0 011 1v16a1 1 0 01-1 1H7a1 1 0 01-1-1V4a1 1 0 011-1zm5 15v.01",
terminal: "M4 6l4 4-4 4M12 16h8",
mesh: "M12 3l9 5-9 5-9-5 9-5zm-9 12l9 5 9-5M3 10l9 5 9-5",
};
return (
<svg
width="32"
height="32"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
className="text-[var(--cm-clay)]"
>
<path d={paths[glyph]} />
</svg>
);
};

View File

@@ -0,0 +1,64 @@
import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
export const CallToAction = () => {
return (
<section className="relative overflow-hidden bg-[var(--cm-bg)] px-6 py-32 md:px-12 md:py-40">
<div
className="absolute inset-0 z-0 opacity-[0.1]"
style={{
backgroundImage:
"radial-gradient(circle at 50% 100%, var(--cm-clay) 0%, transparent 55%)",
}}
/>
<div className="relative z-10 mx-auto max-w-5xl text-center">
<Reveal className="mb-8 flex justify-center">
<SectionIcon glyph="mesh" />
</Reveal>
<Reveal delay={1}>
<h2
className="text-[clamp(2.25rem,5.5vw,4.5rem)] font-medium leading-[1.05] tracking-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Connect what&apos;s scattered.
<br />
<span className="italic text-[var(--cm-clay)]">
Ship what ships together.
</span>
</h2>
</Reveal>
<Reveal delay={2}>
<p
className="mx-auto mt-8 max-w-2xl text-lg leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Anthropic built Claude Code per developer. The next unlock is
between developers. Build the layer with us.
</p>
</Reveal>
<Reveal delay={3}>
<div className="mt-12 flex flex-col items-stretch justify-center gap-3 sm:flex-row sm:items-center">
<Link
href="https://github.com/claudemesh/claudemesh"
target="_blank"
className="group inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-6 py-3.5 text-[15px] font-medium text-[var(--cm-fg)] transition-colors duration-300 hover:bg-[var(--cm-clay-hover)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Star on GitHub
<span className="transition-transform duration-300 group-hover:translate-x-0.5">
</span>
</Link>
<Link
href="#docs"
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-6 py-3.5 text-[15px] font-medium text-[var(--cm-fg)] transition-colors duration-300 hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg-elevated)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Read the docs
</Link>
</div>
</Reveal>
</div>
</section>
);
};

View File

@@ -1,89 +1,103 @@
import { getTranslation } from "@turbostarter/i18n/server";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@turbostarter/ui-web/accordion";
import { buttonVariants } from "@turbostarter/ui-web/button";
import { Icons } from "@turbostarter/ui-web/icons";
"use client";
import { useState } from "react";
import { Reveal } from "./_reveal";
import { pathsConfig } from "~/config/paths";
import { TurboLink } from "~/modules/common/turbo-link";
import {
Section,
SectionHeader,
SectionBadge,
SectionTitle,
SectionDescription,
} from "~/modules/marketing/layout/section";
const questions = [
{
question: "faq.question.whatDoesOurPlatformDo.question",
answer: "faq.question.whatDoesOurPlatformDo.answer",
},
{
question: "faq.question.howWillThisBenefitMyBusiness.question",
answer: "faq.question.howWillThisBenefitMyBusiness.answer",
},
{
question: "faq.question.isMyDataSafe.question",
answer: "faq.question.isMyDataSafe.answer",
},
{
question: "faq.question.whatKindOfIntegrationsAreAvailable.question",
answer: "faq.question.whatKindOfIntegrationsAreAvailable.answer",
},
{
question: "faq.question.howEasyIsItToOnboardMyTeam.question",
answer: "faq.question.howEasyIsItToOnboardMyTeam.answer",
},
{
question: "faq.question.whatTypesOfBusinessesCanUseThis.question",
answer: "faq.question.whatTypesOfBusinessesCanUseThis.answer",
},
{
question: "faq.question.canICustomizeThisToFitMyBusinessNeeds.question",
answer: "faq.question.canICustomizeThisToFitMyBusinessNeeds.answer",
},
] as const;
export const Faq = async () => {
const { t } = await getTranslation({ ns: "marketing" });
const ITEMS = [
{
q: "Is claudemesh free?",
a: "Yes — the broker, CLI, dashboard, and SDK are MIT-licensed and free forever. Solo developers and small teams can self-host at no cost. Paid tiers add hosted brokers, SSO, audit retention, and support.",
},
{
q: "How do I get started?",
a: "Install the broker with one curl command. Add one env var to your Claude Code config. Your session joins the mesh. `npx claudemesh init` does both in 60 seconds.",
},
{
q: "Does claudemesh send my code or prompts to the cloud?",
a: "No. The broker is a local WebSocket server. Messages stay on your network. The only data that leaves your machines is what your Claude Code already sends to Anthropic — we don't touch it.",
},
{
q: "Do I need to run a server?",
a: "Yes — one machine on your network runs the broker. That can be your laptop, a shared dev box, a Raspberry Pi, or a container in your cluster. It's one binary, SQLite-backed, ~15 MB.",
},
{
q: "Does it work across offices / continents?",
a: "Yes. Put the broker on a VPS, or expose it through Tailscale / WireGuard. Every Claude Code session that can reach the broker joins the mesh.",
},
{
q: "How does it route messages?",
a: "By peer name (alex, jordan), by repo, or by priority level (now / next / low). Messages are queued until the target peer is idle, then delivered. You can also set a peer to DND to block all but priority:now.",
},
{
q: "Which Claude Code versions work with claudemesh?",
a: "Claude Code 2.0 and above. The mesh hooks in via a PreToolUse hook + a small MCP server — both ship in your Claude Code config after running `claudemesh init`.",
},
];
export const FAQ = () => {
const [open, setOpen] = useState<number | null>(0);
return (
<Section id="faq" className="lg:flex-row lg:items-start">
<SectionHeader className="grow basis-0 lg:items-start">
<SectionBadge>{t("faq.label")}</SectionBadge>
<SectionTitle className="lg:text-left">{t("faq.title")}</SectionTitle>
<SectionDescription className="lg:text-left">
{t("faq.description")}
</SectionDescription>
<TurboLink
href={pathsConfig.marketing.contact}
className={buttonVariants({
variant: "outline",
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-4xl">
<Reveal>
<h2
className="mb-16 text-center text-[clamp(2rem,4.5vw,3.25rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
FAQ
</h2>
</Reveal>
<div className="divide-y divide-[var(--cm-border)] border-y border-[var(--cm-border)]">
{ITEMS.map((item, i) => {
const isOpen = open === i;
return (
<Reveal key={i} delay={i * 0.5}>
<button
onClick={() => setOpen(isOpen ? null : i)}
className="group flex w-full items-start justify-between gap-8 py-6 text-left transition-colors"
aria-expanded={isOpen}
>
<h3
className={
"text-xl font-medium leading-snug transition-colors " +
(isOpen
? "text-[var(--cm-fg)]"
: "text-[var(--cm-fg-secondary)] group-hover:text-[var(--cm-fg)]")
}
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{item.q}
</h3>
<span
className={
"flex-shrink-0 text-2xl leading-none text-[var(--cm-clay)] transition-transform duration-300 " +
(isOpen ? "rotate-45" : "rotate-0")
}
>
+
</span>
</button>
<div
className={
"grid overflow-hidden transition-all duration-500 " +
(isOpen
? "grid-rows-[1fr] pb-6 opacity-100"
: "grid-rows-[0fr] opacity-0")
}
>
<div className="min-h-0">
<p
className="max-w-3xl text-base leading-[1.7] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{item.a}
</p>
</div>
</div>
</Reveal>
);
})}
>
{t("faq.cta")}
<Icons.ArrowRight className="ml-1 size-4" />
</TurboLink>
</SectionHeader>
<Accordion type="multiple" className="grow basis-0">
{questions.map((question) => (
<AccordionItem key={question.question} value={question.question}>
<AccordionTrigger className="text-base">
{t(question.question)}
</AccordionTrigger>
<AccordionContent className="text-base">
{t(question.answer)}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</Section>
</div>
</div>
</section>
);
};

File diff suppressed because one or more lines are too long

View File

@@ -1,231 +1,120 @@
import { getTranslation } from "@turbostarter/i18n/server";
import { buttonVariants } from "@turbostarter/ui-web/button";
import { Icons } from "@turbostarter/ui-web/icons";
import { Marquee } from "@turbostarter/ui-web/marquee";
import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
import { pathsConfig } from "~/config/paths";
import { ThemedImage } from "~/modules/common/themed-image";
import { TurboLink } from "~/modules/common/turbo-link";
import { CtaButton } from "~/modules/marketing/layout/cta-button";
import { Section, SectionBadge } from "~/modules/marketing/layout/section";
export const Hero = async () => {
const { t } = await getTranslation();
const LOGOS = [
"Vercel",
"Linear",
"Stripe",
"Supabase",
"Shopify",
"Figma",
];
export const Hero = () => {
return (
<Section id="hero" className="gap-6 sm:gap-6 md:gap-6 lg:gap-6">
<TurboLink href="#" className="animate-fade-in -translate-y-4 opacity-0">
<SectionBadge>
<div className="w-fit py-0.5 text-center text-xs sm:text-sm">🎉</div>
<section className="relative overflow-hidden border-b border-[var(--cm-border)] bg-[var(--cm-bg)]">
{/* faint mesh backdrop */}
<div
className="absolute inset-0 z-0 opacity-[0.08]"
style={{
backgroundImage:
"radial-gradient(circle at 50% 50%, var(--cm-clay) 0%, transparent 60%)",
}}
/>
<div className="relative z-10 mx-auto flex max-w-[var(--cm-max-w)] flex-col items-center px-6 py-20 md:px-12 md:py-28">
<Reveal className="mb-8">
<SectionIcon glyph="mesh" />
</Reveal>
<Reveal delay={1} className="mb-5">
<div
data-orientation="vertical"
role="none"
className="bg-border mx-2 h-full w-px shrink-0"
></div>
{t("announcement")}
<Icons.ChevronRight className="text-foreground ml-1.5 size-3 shrink-0 transition-transform group-hover:translate-x-0.5" />
</SectionBadge>
</TurboLink>
<h1 className="animate-fade-in mt-4 -translate-y-4 text-center text-5xl leading-[0.95] font-semibold tracking-tighter text-balance opacity-0 [--animation-delay:200ms] sm:text-6xl md:text-7xl lg:text-[5.5rem]">
{t("product.title")}
</h1>
<p className="animate-fade-in text-muted-foreground mx-auto mb-3 max-w-[560px] -translate-y-4 text-center text-lg leading-[26px] text-balance opacity-0 [--animation-delay:400ms] sm:text-xl">
{t("product.description")}
</p>
<div className="animate-fade-in mx-auto flex w-full -translate-y-4 flex-col gap-2 opacity-0 ease-in-out [--animation-delay:600ms] sm:w-auto sm:flex-row sm:gap-3">
<CtaButton />
<TurboLink
href={pathsConfig.marketing.contact}
className={buttonVariants({ variant: "outline" })}
>
{t("contact.cta")}
</TurboLink>
</div>
<div className="animate-fade-up relative mt-4 -mb-10 opacity-0 [--animation-delay:400ms] [perspective:2000px] after:absolute after:inset-0 after:z-50 after:[background:linear-gradient(to_top,var(--background)_10%,transparent)] sm:mt-8 md:mt-12 lg:mt-16">
<div className="bg-opacity-[0.01] before:animate-image-glow rounded-md before:absolute before:top-0 before:bottom-1/2 before:left-0 before:h-full before:w-full before:[background-image:linear-gradient(to_bottom,var(--primary),var(--secondary),transparent_40%)] before:opacity-0 before:[filter:blur(180px)] md:rounded-lg lg:rounded-xl">
<ThemedImage
light="/images/hero/light.webp"
dark="/images/hero/dark.webp"
alt="Hero Image"
className="relative h-full w-full rounded-[inherit] border object-contain"
width={2626}
height={1894}
priority
fetchPriority="high"
/>
</div>
</div>
<div className="md:gap8 flex w-full min-w-0 flex-col items-center justify-center gap-6 lg:gap-10">
<span className="text-muted-foreground mt-4 text-sm font-medium uppercase sm:text-base md:mt-6 lg:mt-10 lg:text-lg">
{t("shippedWith")}
</span>
<div className="relative -mx-6 flex w-[calc(100%+3rem)] flex-col items-center justify-center overflow-hidden">
<Marquee
pauseOnHover
className="[--duration:60s] [--gap:1.5rem] md:[--gap:2rem] lg:[--gap:3rem] xl:[--gap:3.5rem]"
className="flex items-center gap-2 text-[11px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<svg
viewBox="0 0 128 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-5 md:h-6 lg:h-8"
>
<path
d="M107.386 0.01075V4.12575H98.5415V25.775H94.119V4.126H85.0905V0.01075H107.386ZM108.38 23.3207C108.691 23.3207 108.964 23.4327 109.195 23.6572C109.429 23.8815 109.545 24.1502 109.549 24.4632C109.548 24.6686 109.491 24.8698 109.383 25.045C109.278 25.2177 109.131 25.3608 108.955 25.4613C108.781 25.5653 108.582 25.6195 108.38 25.6178C108.056 25.6178 107.781 25.5055 107.55 25.2812C107.319 25.0567 107.205 24.785 107.208 24.4632C107.205 24.1502 107.319 23.8816 107.55 23.6572C107.781 23.4327 108.056 23.3207 108.38 23.3207ZM20.729 25.775L4.4225 5.507V25.7645H0V0H5.52775L26.1292 25.5823L26.1295 4.126L26.1278 0.01075H48.4227V4.12575H30.5518V10.7458H44.9238V14.861H30.5518V21.66H48.423V25.775H20.729ZM62.5962 14.8217L65.4945 18.425L59.5558 25.8055H53.752L62.5962 14.8217ZM59.5558 0.0215L67.0383 9.314L74.4973 0.05L80.2848 0.04125L69.9358 12.9125L80.3015 25.7857H74.4973L53.7685 0.0215H59.5558ZM115.928 14.764H117.887V22.314C117.885 23.0077 117.734 23.601 117.441 24.1003C117.144 24.599 116.733 24.98 116.206 25.2488C115.682 25.5143 115.068 25.6502 114.369 25.6502C113.731 25.6502 113.159 25.5352 112.65 25.3107C112.14 25.0862 111.736 24.7497 111.439 24.3067C111.139 23.864 110.993 23.3117 110.993 22.6505H112.955C112.958 22.9397 113.024 23.1905 113.15 23.4005C113.274 23.6078 113.455 23.7749 113.672 23.8815C113.896 23.994 114.154 24.0502 114.444 24.05C114.759 24.05 115.029 23.985 115.247 23.852C115.466 23.722 115.634 23.5272 115.751 23.2675C115.865 23.0105 115.925 22.6917 115.928 22.3137V14.764ZM125.95 17.7165C125.903 17.259 125.693 16.9015 125.327 16.6478C124.958 16.3908 124.482 16.264 123.898 16.264C123.488 16.264 123.134 16.326 122.84 16.447C122.547 16.571 122.319 16.7362 122.163 16.946C122.008 17.1555 121.929 17.3947 121.923 17.6635C121.923 17.8877 121.977 18.0828 122.082 18.245C122.191 18.4152 122.337 18.5578 122.51 18.6615C122.699 18.7798 122.9 18.8758 123.11 18.9478C123.332 19.0244 123.553 19.0894 123.775 19.1427L124.797 19.3937C125.208 19.4882 125.606 19.615 125.987 19.7775C126.367 19.937 126.711 20.1407 127.014 20.3857C127.317 20.6307 127.557 20.926 127.733 21.2715C127.91 21.617 128 22.0215 128 22.488C128 23.1168 127.838 23.669 127.511 24.1472C127.185 24.6227 126.715 24.9948 126.097 25.2635C125.483 25.5293 124.74 25.665 123.865 25.665C123.02 25.665 122.283 25.535 121.662 25.2752C121.039 25.0185 120.554 24.6403 120.203 24.1443C119.852 23.6483 119.664 23.043 119.637 22.3315H121.579C121.606 22.7035 121.726 23.0135 121.929 23.2645C122.136 23.5125 122.406 23.6955 122.735 23.8195C123.068 23.9407 123.439 24.0028 123.85 24.0028C124.278 24.0028 124.656 23.9377 124.982 23.8107C125.306 23.6837 125.561 23.5068 125.744 23.2763C125.93 23.0488 126.022 22.7802 126.025 22.4732C126.022 22.1927 125.938 21.9595 125.776 21.7763C125.612 21.5933 125.384 21.4398 125.093 21.3158C124.8 21.1918 124.458 21.0795 124.069 20.9823L122.828 20.6693C121.932 20.4418 121.222 20.0965 120.704 19.6327C120.183 19.1692 119.925 18.5553 119.925 17.7845C119.925 17.1528 120.098 16.5975 120.449 16.122C120.797 15.647 121.273 15.2778 121.876 15.015C122.481 14.7493 123.164 14.6193 123.925 14.6193C124.698 14.6193 125.375 14.7493 125.96 15.015C126.544 15.2778 127.002 15.6438 127.335 16.1103C127.667 16.577 127.841 17.1112 127.85 17.7165H125.95Z"
fill="var(--foreground)"
/>
</svg>
<span className="inline-block h-1 w-1 rounded-full bg-[var(--cm-clay)]" />
meshing
</div>
</Reveal>
<svg
viewBox="0 0 512 145"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-6 md:h-8 lg:h-11"
>
<path
d="M66.678 46.541C67.829 44.845 69.088 44.629 70.111 44.629C71.134 44.629 72.835 44.845 73.986 46.541C83.056 59.001 98.029 83.818 109.074 102.126C116.276 114.065 121.808 123.234 122.943 124.401C127.203 128.782 133.047 126.051 136.442 121.083C139.785 116.191 140.712 112.756 140.712 109.091C140.712 106.595 92.285 16.529 87.408 9.032C82.718 1.822 81.19 0 73.162 0H67.155C59.152 0 57.995 1.821 53.305 9.032C48.428 16.53 0 106.595 0 109.092C0 112.756 0.928 116.191 4.27 121.082C7.666 126.052 13.51 128.782 17.77 124.402C18.905 123.234 24.437 114.065 31.64 102.126C42.683 83.818 57.607 59 66.677 46.54M387.842 39.395C407.788 39.395 423.649 57.402 423.649 79.674C423.649 101.946 407.788 120.111 387.842 120.111C377.162 120.111 368.839 115.688 363.499 107.632V144.12H341.355V40.974H363.5V51.874C368.84 43.818 377.164 39.394 387.843 39.394M471.953 39.394C494.567 39.395 512 56.77 512 79.832C512 102.894 494.566 120.111 471.953 120.111C449.179 120.111 431.903 102.893 431.903 79.831C431.903 56.77 449.179 39.394 471.953 39.394ZM241.753 7.96V30.39H197.622V50.293H236.885V72.723H197.62V96.1H241.751V118.53H174.377V7.961L241.753 7.96ZM277.786 40.973L290.506 59.296L303.385 40.973H329.77L303.7 78.409L331.654 118.53H304.955L290.35 97.523L275.744 118.531H249.36L277.157 78.568L251.087 40.974L277.786 40.973ZM382.818 60.56C371.981 60.56 363.5 68.932 363.5 79.673C363.5 90.572 371.98 98.786 382.818 98.786C393.497 98.786 402.135 90.414 402.135 79.673C402.135 69.09 393.497 60.56 382.818 60.56ZM471.954 60.718C461.428 60.718 453.578 68.774 453.578 79.831C453.578 90.571 461.428 98.786 471.954 98.786C482.317 98.786 490.329 90.572 490.329 79.831C490.329 68.774 482.317 60.718 471.954 60.718Z"
fill="var(--foreground)"
/>
</svg>
<Reveal delay={2}>
<h1
className="max-w-5xl text-center text-[clamp(2.75rem,7vw,5.75rem)] font-medium leading-[1.05] tracking-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Built for{" "}
<span className="inline-flex items-baseline gap-2 text-[var(--cm-clay)]">
<span className="italic">{"<"}</span>
<span className="italic">swarms</span>
<span className="italic">{">"}</span>
</span>
</h1>
</Reveal>
<svg
viewBox="0 0 512 116"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-6 md:h-8 lg:h-10"
>
<g clipPath="url(#clip0_4002_15928)">
<path
d="M255.42 28.976C235.427 28.976 221.012 42.015 221.012 61.573C221.012 81.132 237.238 94.171 257.232 94.171C269.311 94.171 279.959 89.39 286.552 81.331L272.697 73.327C269.039 77.329 263.479 79.665 257.231 79.665C248.557 79.665 241.186 75.138 238.451 67.894H289.195C289.594 65.865 289.829 63.764 289.829 61.555C289.829 42.015 275.414 28.976 255.42 28.976ZM238.29 55.235C240.553 48.009 246.747 43.463 255.403 43.463C264.078 43.463 270.272 48.009 272.517 55.235H238.29ZM450.428 28.975C430.435 28.975 416.019 42.015 416.019 61.573C416.019 81.132 432.245 94.171 452.239 94.171C464.318 94.171 474.966 89.39 481.559 81.331L467.704 73.327C464.046 77.329 458.487 79.665 452.239 79.665C443.564 79.665 436.193 75.138 433.459 67.894H484.2C484.599 65.865 484.834 63.764 484.834 61.555C484.834 42.015 470.419 28.975 450.426 28.975M433.312 55.235C435.576 48.009 441.769 43.463 450.426 43.463C459.1 43.463 465.294 48.009 467.539 55.235H433.312ZM362.629 61.573C362.629 72.439 369.729 79.683 380.739 79.683C388.2 79.683 393.796 76.297 396.676 70.773L410.584 78.796C404.825 88.394 394.032 94.171 380.739 94.171C360.728 94.171 346.331 81.131 346.331 61.573C346.331 42.015 360.746 28.976 380.739 28.976C394.032 28.976 404.807 34.753 410.584 44.351L396.676 52.374C393.796 46.85 388.2 43.464 380.739 43.464C369.747 43.464 362.629 50.706 362.629 61.573ZM512 9.055V92.36H495.701V9.055H512ZM66.916 0L133.831 115.903H0L66.916 0ZM234.214 9.055L184.032 95.982L133.849 9.055H152.666L184.032 63.385L215.398 9.055H234.214ZM340.899 30.787V48.335C339.088 47.81 337.169 47.448 335.104 47.448C324.582 47.448 316.994 54.692 316.994 65.558V92.36H300.695V30.787H316.994V47.447C316.994 38.247 327.697 30.787 340.899 30.787Z"
fill="var(--foreground)"
/>
</g>
<defs>
<clipPath id="clip0_4002_15928">
<rect width="512" height="116" fill="white" />
</clipPath>
</defs>
</svg>
<Reveal delay={3}>
<p
className="mx-auto mt-6 max-w-2xl text-center text-lg leading-[1.65] text-[var(--cm-fg-secondary)] md:text-xl"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Connect every Claude Code session on your team into one live mesh.
Ship context, not screenshots. Self-host the broker. Own the wire.
<span className="block pt-2 text-[var(--cm-clay)]">
Free and open-source. Forever.
</span>
</p>
</Reveal>
<svg
viewBox="0 0 450 165"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-7 md:h-9 lg:h-12"
<Reveal delay={4}>
<div className="mt-10 flex flex-col items-stretch gap-3 sm:flex-row sm:items-center">
<Link
href="https://github.com/claudemesh/claudemesh"
target="_blank"
className="group inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-5 py-3 text-[15px] font-medium text-[var(--cm-fg)] transition-colors duration-300 hover:bg-[var(--cm-clay-hover)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<path
opacity="0.993"
d="M67.0645 0.0144212C67.5029 -0.0422455 67.8894 0.0680878 68.224 0.345421C80.5358 15.3702 92.1437 30.9581 103.011 47.0589C111.058 59.1743 117.905 71.9846 123.552 85.4899C132.57 109.544 127.877 130.471 109.472 148.272C93.3672 161.882 74.8142 167.183 53.8125 164.174C28.7085 159.056 11.6464 144.534 2.62605 120.608C0.276714 112.819 -0.496286 104.868 0.307047 96.7549C1.64505 82.7896 4.95805 69.3166 10.246 56.3359C12.4494 51.0399 15.3207 46.1809 18.86 41.7589C21.7445 45.1944 24.5071 48.7304 27.1425 52.3604C28.3625 53.6354 29.6325 54.8503 30.9525 56.0049C41.0302 36.0059 53.0677 17.3424 67.065 0.0144212"
fill="#FF5B11"
/>
<path
d="M64.7445 26.8501C76.9012 40.9501 87.8895 55.9691 97.7095 71.9071C100.782 77.1678 103.334 82.7157 105.33 88.4721C109.499 104.874 105.026 118.402 91.9115 129.057C79.2282 137.981 65.3135 140.632 50.1675 137.008C33.8345 131.939 24.3924 120.951 21.841 104.043C21.222 98.7121 21.7744 93.5218 23.498 88.4721C25.9436 82.2554 28.937 76.2685 32.443 70.5821L42.382 56.0046C49.9011 46.3361 57.3556 36.6175 64.745 26.8496"
fill="#FF9758"
/>
<path
d="M185.088 129V49.9485H201.05V81.0622H232.325V49.9485H248.287V129H232.325V94.4352H201.05V129H185.088ZM257.915 99.5579C257.915 94.5251 259.155 89.654 261.636 84.9447C264.116 80.2354 267.621 76.6405 272.151 74.1601C276.716 71.6796 281.803 70.4394 287.411 70.4394C296.075 70.4394 303.175 73.2614 308.711 78.9053C314.247 84.5133 317.015 91.6132 317.015 100.205C317.015 108.869 314.211 116.058 308.603 121.774C303.031 127.454 296.003 130.294 287.519 130.294C282.27 130.294 277.256 129.108 272.474 126.735C267.729 124.363 264.116 120.894 261.636 116.328C259.155 111.727 257.915 106.137 257.915 99.5579ZM273.445 100.367C273.445 106.047 274.793 110.396 277.489 113.416C280.185 116.436 283.511 117.946 287.465 117.946C291.419 117.946 294.727 116.436 297.387 113.416C300.083 110.396 301.431 106.011 301.431 100.259C301.431 94.6509 300.083 90.337 297.387 87.3173C294.727 84.2976 291.419 82.7878 287.465 82.7878C283.511 82.7878 280.185 84.2976 277.489 87.3173C274.793 90.337 273.445 94.6868 273.445 100.367ZM377.709 129H362.556V99.7736C362.556 93.5904 362.233 89.6001 361.586 87.8026C360.939 85.9693 359.878 84.5493 358.404 83.5427C356.966 82.5361 355.223 82.0329 353.174 82.0329C350.549 82.0329 348.195 82.7518 346.11 84.1898C344.025 85.6277 342.587 87.533 341.796 89.9057C341.041 92.2783 340.663 96.664 340.663 103.063V129H325.511V71.7335H339.585V80.1455C344.582 73.6748 350.873 70.4394 358.458 70.4394C361.801 70.4394 364.857 71.0505 367.625 72.2728C370.393 73.4591 372.478 74.9869 373.88 76.8562C375.318 78.7256 376.307 80.8466 376.846 83.2192C377.421 85.5918 377.709 88.989 377.709 93.4107V129ZM386.313 99.5579C386.313 94.5251 387.553 89.654 390.033 84.9447C392.514 80.2354 396.019 76.6405 400.548 74.1601C405.114 71.6796 410.201 70.4394 415.809 70.4394C424.472 70.4394 431.572 73.2614 437.108 78.9053C442.645 84.5133 445.413 91.6132 445.413 100.205C445.413 108.869 442.609 116.058 437.001 121.774C431.429 127.454 424.401 130.294 415.917 130.294C410.668 130.294 405.653 129.108 400.872 126.735C396.127 124.363 392.514 120.894 390.033 116.328C387.553 111.727 386.313 106.137 386.313 99.5579ZM401.843 100.367C401.843 106.047 403.191 110.396 405.887 113.416C408.583 116.436 411.908 117.946 415.863 117.946C419.817 117.946 423.124 116.436 425.785 113.416C428.481 110.396 429.829 106.011 429.829 100.259C429.829 94.6509 428.481 90.337 425.785 87.3173C423.124 84.2976 419.817 82.7878 415.863 82.7878C411.908 82.7878 408.583 84.2976 405.887 87.3173C403.191 90.337 401.843 94.6868 401.843 100.367Z"
fill="var(--foreground)"
/>
</svg>
<svg
className="h-7 md:h-9 lg:h-11"
viewBox="0 0 473 76"
fill="none"
Start free
<span className="transition-transform duration-300 group-hover:translate-x-0.5">
</span>
</Link>
<div
className="flex items-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-4 py-3 text-[13px] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<path
d="M130.998 30.6565V22.3773H91.0977V30.6565H106.16V58.1875H115.935V30.6565H130.998Z"
fill="var(--foreground)"
></path>
<path
d="M153.542 58.7362C165.811 58.7362 172.544 52.5018 172.544 42.2275V22.3773H162.768V41.2799C162.768 47.0155 159.776 50.2574 153.542 50.2574C147.307 50.2574 144.315 47.0155 144.315 41.2799V22.3773H134.539V42.2275C134.539 52.5018 141.272 58.7362 153.542 58.7362Z"
fill="var(--foreground)"
></path>
<path
d="M187.508 46.3173H197.234L204.914 58.1875H216.136L207.458 45.2699C212.346 43.5243 215.338 39.634 215.338 34.3473C215.338 26.6665 209.603 22.3773 200.874 22.3773H177.732V58.1875H187.508V46.3173ZM187.508 38.5867V30.5568H200.376C203.817 30.5568 205.712 32.053 205.712 34.5967C205.712 36.9907 203.817 38.5867 200.376 38.5867H187.508Z"
fill="var(--foreground)"
></path>
<path
d="M219.887 58.1875H245.472C253.452 58.1875 258.041 54.397 258.041 48.0629C258.041 43.8235 255.348 40.9308 252.156 39.634C254.35 38.5867 257.043 36.0929 257.043 32.1528C257.043 25.8187 252.555 22.3773 244.625 22.3773H219.887V58.1875ZM229.263 36.3922V30.3074H243.627C246.32 30.3074 247.817 31.3548 247.817 33.3498C247.817 35.3448 246.32 36.3922 243.627 36.3922H229.263ZM229.263 43.7238H244.525C247.168 43.7238 248.615 45.0205 248.615 46.9657C248.615 48.9108 247.168 50.2075 244.525 50.2075H229.263V43.7238Z"
fill="var(--foreground)"
></path>
<path
d="M281.942 21.7788C269.423 21.7788 260.396 29.6092 260.396 40.2824C260.396 50.9557 269.423 58.786 281.942 58.786C294.461 58.786 303.438 50.9557 303.438 40.2824C303.438 29.6092 294.461 21.7788 281.942 21.7788ZM281.942 30.2575C288.525 30.2575 293.463 34.1478 293.463 40.2824C293.463 46.417 288.525 50.3073 281.942 50.3073C275.359 50.3073 270.421 46.417 270.421 40.2824C270.421 34.1478 275.359 30.2575 281.942 30.2575Z"
fill="var(--foreground)"
></path>
<path
d="M317.526 46.3173H327.251L334.932 58.1875H346.154L337.476 45.2699C342.364 43.5243 345.356 39.634 345.356 34.3473C345.356 26.6665 339.62 22.3773 330.892 22.3773H307.75V58.1875H317.526V46.3173ZM317.526 38.5867V30.5568H330.394C333.835 30.5568 335.73 32.053 335.73 34.5967C335.73 36.9907 333.835 38.5867 330.394 38.5867H317.526Z"
fill="var(--foreground)"
></path>
<path
d="M349.904 22.3773V58.1875H384.717V49.9083H359.48V44.0729H381.874V35.9932H359.48V30.6565H384.717V22.3773H349.904Z"
fill="var(--foreground)"
></path>
<path
d="M399.204 46.7662H412.221C420.95 46.7662 426.685 42.5767 426.685 34.5967C426.685 26.5668 420.95 22.3773 412.221 22.3773H389.428V58.1875H399.204V46.7662ZM399.204 38.6365V30.5568H411.673C415.164 30.5568 417.059 32.053 417.059 34.5967C417.059 37.0904 415.164 38.6365 411.673 38.6365H399.204Z"
fill="var(--foreground)"
></path>
<path
d="M450.948 21.7788C438.43 21.7788 429.402 29.6092 429.402 40.2824C429.402 50.9557 438.43 58.786 450.948 58.786C463.467 58.786 472.444 50.9557 472.444 40.2824C472.444 29.6092 463.467 21.7788 450.948 21.7788ZM450.948 30.2575C457.532 30.2575 462.469 34.1478 462.469 40.2824C462.469 46.417 457.532 50.3073 450.948 50.3073C444.365 50.3073 439.427 46.417 439.427 40.2824C439.427 34.1478 444.365 30.2575 450.948 30.2575Z"
fill="var(--foreground)"
></path>
<path
d="M38.5017 18.0956C27.2499 18.0956 18.0957 27.2498 18.0957 38.5016C18.0957 49.7534 27.2499 58.9076 38.5017 58.9076C49.7535 58.9076 58.9077 49.7534 58.9077 38.5016C58.9077 27.2498 49.7535 18.0956 38.5017 18.0956ZM38.5017 49.0618C32.6687 49.0618 27.9415 44.3346 27.9415 38.5016C27.9415 32.6686 32.6687 27.9414 38.5017 27.9414C44.3347 27.9414 49.0619 32.6686 49.0619 38.5016C49.0619 44.3346 44.3347 49.0618 38.5017 49.0618Z"
fill="var(--foreground)"
></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M40.2115 14.744V7.125C56.7719 8.0104 69.9275 21.7208 69.9275 38.5016C69.9275 55.2824 56.7719 68.989 40.2115 69.8782V62.2592C52.5539 61.3776 62.3275 51.0644 62.3275 38.5016C62.3275 25.9388 52.5539 15.6256 40.2115 14.744ZM20.5048 54.0815C17.233 50.3043 15.124 45.4935 14.7478 40.2115H7.125C7.5202 47.6025 10.4766 54.3095 15.1088 59.4737L20.501 54.0815H20.5048ZM36.7916 69.8782V62.2592C31.5058 61.883 26.695 59.7778 22.9178 56.5022L17.5256 61.8944C22.6936 66.5304 29.4006 69.483 36.7878 69.8782H36.7916Z"
fill="url(#paint0_linear_2028_252)"
></path>
<defs>
<linearGradient
id="paint0_linear_2028_252"
x1="41.443"
y1="11.5372"
x2="10.5567"
y2="42.4236"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#0096FF"></stop>
<stop offset="1" stopColor="#FF1E56"></stop>
</linearGradient>
</defs>
</svg>
<span className="text-[var(--cm-clay)]">$</span>
<span>curl -fsSL claudemesh.sh/install | bash</span>
</div>
</div>
</Reveal>
<svg
viewBox="0 0 407 139"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-6 md:h-8 lg:h-10"
<Reveal delay={6}>
<p
className="mt-6 text-sm text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Or{" "}
<Link
href="#docs"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 transition-colors hover:text-[var(--cm-fg)] hover:decoration-[var(--cm-clay)]"
>
<path
d="M54.0447 105.252H45.0427V137.633H69.6851V129.783H54.0447V105.252ZM96.4651 104.562C86.4812 104.562 78.9784 112.123 78.9784 121.413V121.528C78.9784 130.821 86.3666 138.265 96.3506 138.265C106.335 138.265 113.837 130.703 113.837 121.413V121.299C113.837 112.005 106.449 104.562 96.4651 104.562ZM104.717 121.528C104.717 126.202 101.428 130.186 96.4651 130.186C91.5583 130.186 88.2128 126.147 88.2128 121.472V121.358C88.2128 116.683 91.5026 112.699 96.4095 112.699C101.372 112.699 104.721 116.739 104.721 121.413L104.717 121.528ZM145.868 123.492C145.868 127.993 143.501 130.13 139.923 130.13C136.345 130.13 133.979 127.937 133.979 123.263V105.255H124.859V123.436C124.859 133.594 130.63 138.212 139.805 138.212C149.04 138.212 154.984 133.653 154.984 123.207V105.259H145.865L145.868 123.492ZM181.192 105.252H168.726V137.63H181.018C192.678 137.63 199.487 130.703 199.487 121.354V121.24C199.487 111.891 192.734 105.252 181.192 105.252ZM190.308 121.472C190.308 126.723 186.73 129.669 181.362 129.669V129.61H177.725V113.161H181.362C186.73 113.161 190.308 116.163 190.308 121.358V121.472ZM212.528 137.633H221.474V126.088H236.827V118.644H221.474V113.102H238.444V105.252H212.528V137.633ZM259.856 105.252H250.909V137.633H275.555V129.783H259.856V105.252ZM298.986 105.023L285.251 137.633H294.659L296.966 131.862H309.432L311.798 137.633H321.435L307.641 105.023H298.986ZM299.677 124.876L303.314 115.701L306.892 124.876H299.677ZM361.722 116.451V116.336C361.722 113.105 360.74 110.624 358.834 108.774C356.641 106.522 353.237 105.252 348.271 105.252H332.978V137.689H341.98V127.878H345.904L352.425 137.689H362.815L355.08 126.376C359.067 124.703 361.722 121.413 361.722 116.451ZM352.661 116.971C352.661 119.279 350.929 120.781 347.872 120.781H341.927V113.046H347.813C350.815 113.046 352.661 114.316 352.661 116.857V116.971ZM383.536 130.013V124.876H399.062V117.776H383.536V112.873H400.679V105.252H374.648V137.633H400.908V130.013H383.536ZM17.9747 112.699C21.6671 112.699 24.4953 114.951 25.6509 118.068H35.1177C33.6184 110.333 26.8064 104.62 18.0336 104.62C8.04959 104.62 0.546875 112.182 0.546875 121.472V121.587C0.546875 130.88 7.93502 138.324 17.919 138.324C26.4594 138.324 33.1569 132.782 34.8885 125.397L25.425 125.338C24.1549 128.166 21.5001 130.186 18.0369 130.186C13.13 130.186 9.78452 126.088 9.78452 121.472V121.358C9.77797 116.68 13.0678 112.699 17.9747 112.699Z"
fill="var(--foreground)"
/>
<path
d="M390.081 53.605L365.32 39.4048L361.048 37.5586L259.762 38.2526V89.675H390.081V53.605Z"
fill="var(--background)"
/>
<path
d="M344.99 84.9333C346.201 80.7793 345.74 76.969 343.72 74.1408C341.874 71.5449 338.757 70.0424 335.006 69.8689L263.959 68.9458C263.743 68.9478 263.53 68.8962 263.339 68.7957C263.147 68.6951 262.984 68.5488 262.863 68.3697C262.75 68.1806 262.676 67.9706 262.646 67.7524C262.617 67.5341 262.631 67.3121 262.689 67.0996C262.819 66.7513 263.045 66.447 263.342 66.2222C263.638 65.9974 263.992 65.8612 264.362 65.8295L336.044 64.9064C344.528 64.5038 353.763 57.6328 356.994 49.207L361.092 38.5291C361.273 38.0905 361.313 37.6067 361.207 37.1444C358.9 26.7904 353.13 17.533 344.851 10.9011C336.571 4.26925 326.278 0.659642 315.67 0.668473C295.122 0.668473 277.695 13.9423 271.462 32.3553C267.236 29.2087 261.989 27.7477 256.745 28.257C251.939 28.751 247.449 30.8858 244.033 34.3022C240.617 37.7185 238.482 42.2077 237.988 47.0138C237.748 49.4688 237.924 51.9466 238.508 54.343C230.744 54.5638 223.373 57.8052 217.962 63.3774C212.551 68.9497 209.528 76.4133 209.535 84.1804C209.535 85.624 209.65 87.0676 209.823 88.5079C209.938 89.2019 210.517 89.7191 211.208 89.7191H342.335C342.713 89.7115 343.078 89.5845 343.378 89.3563C343.679 89.128 343.899 88.8104 344.008 88.449L344.99 84.9333Z"
fill="#F38020"
/>
<path
d="M367.599 39.2847C366.964 39.2847 366.27 39.2847 365.638 39.3436C365.176 39.3436 364.774 39.6906 364.6 40.1521L361.831 49.7891C360.62 53.9431 361.081 57.7534 363.101 60.5817C364.947 63.1775 368.063 64.68 371.815 64.8535L386.935 65.7766C387.396 65.7766 387.799 66.009 388.031 66.3527C388.144 66.5419 388.218 66.7518 388.248 66.9701C388.278 67.1883 388.263 67.4103 388.205 67.6228C388.075 67.9711 387.849 68.2754 387.553 68.5002C387.257 68.7251 386.903 68.8612 386.532 68.8929L370.777 69.816C362.237 70.2187 353.058 77.0896 349.827 85.5154L348.672 88.4582C348.439 89.0344 348.845 89.6138 349.48 89.6138H403.616C404.251 89.6138 404.827 89.2111 405.001 88.5761C405.924 85.2274 406.445 81.7084 406.445 78.0716C406.441 56.7157 389.01 39.2847 367.599 39.2847Z"
fill="#FAAE40"
/>
</svg>
</Marquee>
read the documentation
</Link>
</p>
</Reveal>
<div className="from-background pointer-events-none absolute inset-y-0 left-0 w-1/6 bg-gradient-to-r sm:w-1/5"></div>
<div className="from-background pointer-events-none absolute inset-y-0 right-0 w-1/6 bg-gradient-to-l sm:w-1/5"></div>
</div>
<Reveal delay={8}>
<div className="mt-20 flex flex-wrap items-center justify-center gap-x-12 gap-y-6 opacity-70">
{LOGOS.map((logo) => (
<div
key={logo}
className="text-xl font-medium tracking-tight text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{logo}
</div>
))}
</div>
</Reveal>
</div>
</Section>
</section>
);
};

View File

@@ -0,0 +1,92 @@
import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
const STEPS = [
{
id: "01",
title: "Start a task on your laptop",
body: "Open Claude Code. Work normally. Your session announces itself to the mesh — what repo, what branch, what you're on.",
},
{
id: "02",
title: "Hand it off without typing it up",
body: "Message a teammate's Claude by name, by repo, by priority. The broker routes. The other session picks it up when its human goes idle.",
},
{
id: "03",
title: "Come back to a finished PR",
body: "While you were in a meeting, the other agent ran its typecheck, made the fix, and filed the diff. You review. You merge. You ship.",
},
];
export const LaptopToLaptop = () => {
return (
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]">
<Reveal className="mb-6 flex justify-center">
<SectionIcon glyph="phone" />
</Reveal>
<Reveal delay={1}>
<h2
className="mx-auto max-w-4xl text-center text-[clamp(2rem,4.5vw,3.25rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Start a task on one laptop,
<br />
<span className="italic text-[var(--cm-clay)]">
come back to a finished PR.
</span>
</h2>
</Reveal>
<Reveal delay={2}>
<p
className="mx-auto mt-6 max-w-2xl text-center text-lg leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Route work between Claude Code sessions on different machines. The
broker handles presence, priority, and queueing. Your humans handle
the interesting parts.
</p>
</Reveal>
<Reveal delay={3} className="mt-10 flex justify-center">
<Link
href="#"
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-5 py-3 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Pair your machines
</Link>
</Reveal>
<Reveal delay={4}>
<div className="mt-20 grid gap-6 md:grid-cols-3">
{STEPS.map((s) => (
<div
key={s.id}
className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg)] p-8"
>
<div
className="mb-6 text-[11px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
[{s.id}]
</div>
<h3
className="mb-3 text-xl font-medium leading-snug text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{s.title}
</h3>
<p
className="text-[14px] leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{s.body}
</p>
</div>
))}
</div>
</Reveal>
</div>
</section>
);
};

View File

@@ -0,0 +1,142 @@
import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
const CARDS = [
{
accent: "clay",
title: "Start in your terminal",
body: "Drop the broker next to Claude Code. One env var. Your session joins the mesh.",
cta: { label: "Install", href: "#" },
mock: (
<div
className="rounded-[8px] bg-[#D97757] p-6 font-mono text-[11px] leading-[1.6] text-[#141413]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<div className="mb-2 opacity-70">$ claudemesh join</div>
<div> connected to mesh.team.local</div>
<div> announced: alex · api-gateway · working</div>
<div> 5 peers online</div>
<div className="mt-3 opacity-70">
ready. messages route to your Claude Code.
</div>
</div>
),
},
{
accent: "oat",
title: "Bridge to your editor",
body: "VS Code, Cursor, JetBrains — the mesh exposes an MCP server your editor's agent can call.",
cta: { label: "VS Code", href: "#" },
cta2: { label: "JetBrains", href: "#" },
mock: (
<div
className="rounded-[8px] border border-[var(--cm-border)] bg-[var(--cm-bg)] p-4"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<div className="text-[10px] text-[var(--cm-fg-tertiary)]">
.claude/mcp.json
</div>
<pre className="mt-2 text-[11px] leading-[1.6] text-[var(--cm-fg)]">
{`{
"servers": {
"mesh": {
"url": "ws://mesh.team:7899"
}
}
}`}
</pre>
</div>
),
},
{
accent: "cactus",
title: "Reach across machines",
body: "Tailscale, WireGuard, or plain WS over your LAN. The broker is one binary, anywhere.",
cta: { label: "Open the dashboard", href: "#" },
mock: (
<div
className="rounded-[8px] border border-[var(--cm-border)] bg-[var(--cm-bg)] p-4"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<div className="mb-3 text-[10px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]">
Peers on mesh
</div>
{[
["alex", "macOS · working"],
["jordan", "linux · idle"],
["mo", "macOS · dnd"],
].map(([n, s]) => (
<div
key={n}
className="flex items-center justify-between border-b border-[var(--cm-border)] py-1.5 text-[12px] last:border-b-0"
>
<span className="text-[var(--cm-fg)]">{n}</span>
<span className="text-[var(--cm-fg-tertiary)]">{s}</span>
</div>
))}
</div>
),
},
];
export const MeetsYou = () => {
return (
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]">
<Reveal className="mb-6 flex justify-center">
<SectionIcon glyph="terminal" />
</Reveal>
<Reveal delay={1}>
<h2
className="mx-auto max-w-4xl text-center text-[clamp(2rem,4.5vw,3.25rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Meets every agent where it runs
</h2>
</Reveal>
<Reveal delay={2}>
<div className="mt-16 grid gap-6 md:grid-cols-3">
{CARDS.map((c) => (
<article
key={c.title}
className="flex flex-col overflow-hidden rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-6"
>
<div className="mb-6">{c.mock}</div>
<h3
className="mb-2 text-xl font-medium leading-snug text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{c.title}
</h3>
<p
className="mb-6 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{c.body}
</p>
<div className="mt-auto flex flex-wrap gap-2">
<Link
href={c.cta.href}
className="inline-flex items-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-4 py-2 text-[13px] font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{c.cta.label}
</Link>
{c.cta2 && (
<Link
href={c.cta2.href}
className="inline-flex items-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] px-4 py-2 text-[13px] font-medium text-[var(--cm-fg-secondary)] transition-colors hover:border-[var(--cm-fg-tertiary)] hover:text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{c.cta2.label}
</Link>
)}
</div>
</article>
))}
</div>
</Reveal>
</div>
</section>
);
};

View File

@@ -0,0 +1,147 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
const TIERS = {
individual: [
{
name: "Solo",
desc: "Run the broker on your laptop. Pair your Claude Code sessions across repos.",
price: "Free",
cta: "Install locally",
href: "https://github.com/claudemesh/claudemesh",
},
{
name: "Pro",
desc: "Mesh dashboard, peer registry, message history, priority routing.",
price: "$12",
note: "per month",
cta: "Start free trial",
href: "#",
},
{
name: "Plus",
desc: "Cross-machine mesh via Tailscale / WireGuard, MCP bridge, audit log.",
price: "$24",
note: "per month",
cta: "Start free trial",
href: "#",
},
],
team: [
{
name: "Team",
desc: "Self-hosted broker. SSO, shared presence, team audit log, 25 peers.",
price: "$99",
note: "per month · unlimited peers",
cta: "Get started",
href: "#",
},
{
name: "Business",
desc: "Multi-region brokers, retention controls, Slack/Linear bridges.",
price: "$499",
note: "per month",
cta: "Get started",
href: "#",
},
{
name: "Enterprise",
desc: "Air-gapped deploy, custom SAML, dedicated support, SOC 2 pack.",
price: "Contact",
cta: "Contact sales",
href: "#",
},
],
};
export const Pricing = () => {
const [tab, setTab] = useState<"individual" | "team">("individual");
const tiers = TIERS[tab];
return (
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]">
<Reveal className="mb-6 flex justify-center">
<SectionIcon glyph="leaf" />
</Reveal>
<Reveal delay={1}>
<h2
className="text-center text-[clamp(2rem,4.5vw,3.25rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Get started with claudemesh
</h2>
</Reveal>
<Reveal delay={2} className="mt-10 flex justify-center">
<div className="inline-flex rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-1">
{(["individual", "team"] as const).map((k) => (
<button
key={k}
onClick={() => setTab(k)}
className={
"rounded-[calc(var(--cm-radius-xs)-2px)] px-4 py-2 text-[13px] font-medium transition-colors " +
(tab === k
? "bg-[var(--cm-fg)] text-[var(--cm-bg)]"
: "text-[var(--cm-fg-secondary)] hover:text-[var(--cm-fg)]")
}
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{k === "individual" ? "Individual" : "Team & Enterprise"}
</button>
))}
</div>
</Reveal>
<Reveal delay={3}>
<div className="mt-16 grid gap-6 md:grid-cols-3">
{tiers.map((tier) => (
<article
key={tier.name}
className="flex flex-col rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-8 transition-colors hover:border-[var(--cm-clay)]"
>
<div className="mb-5">
<SectionIcon glyph="leaf" />
</div>
<h3
className="mb-2 text-[28px] font-medium leading-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{tier.name}
</h3>
<p
className="mb-6 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{tier.desc}
</p>
<div className="mb-6 mt-auto">
<div
className="text-[32px] font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{tier.price}
</div>
{tier.note && (
<div
className="text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{tier.note}
</div>
)}
</div>
<Link
href={tier.href}
className="inline-flex items-center justify-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-5 py-2.5 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{tier.cta}
</Link>
</article>
))}
</div>
</Reveal>
</div>
</section>
);
};

View File

@@ -0,0 +1,152 @@
import Image from "next/image";
import Link from "next/link";
import { Reveal } from "./_reveal";
export const Surfaces = () => {
return (
<section className="relative border-b border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]">
<Reveal className="mb-16 max-w-3xl">
<div
className="mb-5 text-[11px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
surfaces
</div>
<h2
className="text-[clamp(2rem,4.5vw,3.25rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Use claudemesh where your team already works
</h2>
</Reveal>
<Reveal delay={1}>
<div className="overflow-hidden rounded-[var(--cm-radius-lg)] border border-[var(--cm-border)] bg-[var(--cm-bg)]">
{/* top browser bar */}
<div className="flex items-center gap-2 border-b border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-4 py-3">
<div className="flex gap-1.5">
<span className="h-3 w-3 rounded-full bg-[#FF5F57]" />
<span className="h-3 w-3 rounded-full bg-[#FEBC2E]" />
<span className="h-3 w-3 rounded-full bg-[#28C840]" />
</div>
<div
className="ml-4 flex-1 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
mesh.yourteam.local live sessions: 6
</div>
</div>
<div className="grid gap-0 md:grid-cols-[320px_1fr]">
{/* sidebar */}
<aside className="border-b border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-6 md:border-b-0 md:border-r">
<div
className="mb-4 text-[10px] uppercase tracking-[0.2em] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
peers · 6 online
</div>
{[
{ name: "alex", repo: "api-gateway", state: "working" },
{ name: "sam", repo: "billing-svc", state: "idle" },
{ name: "jordan", repo: "infra", state: "working" },
{ name: "mo", repo: "dashboard", state: "dnd" },
].map((p) => (
<div
key={p.name}
className="flex items-center justify-between border-b border-[var(--cm-border)] py-3 last:border-b-0"
>
<div>
<div
className="text-sm text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{p.name}
</div>
<div
className="text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{p.repo}
</div>
</div>
<span
className={
"h-2 w-2 rounded-full " +
(p.state === "working"
? "bg-[var(--cm-clay)] animate-pulse"
: p.state === "idle"
? "bg-[var(--cm-gray-350)]"
: "bg-[#c46686]")
}
/>
</div>
))}
</aside>
{/* main */}
<div className="p-8 md:p-12">
<div
className="mb-2 text-xs text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
alex jordan · 2m ago · priority: next
</div>
<div
className="mb-8 rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5 text-[15px] leading-[1.6] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Renamed <code className="text-[var(--cm-clay)]">AUTH_TOKEN</code> {" "}
<code className="text-[var(--cm-clay)]">AUTH_TOKEN_V2</code> in
terraform/secrets.tf. When you go idle, bump your env loader in{" "}
<code className="text-[var(--cm-clay)]">api-gateway/src/env.ts</code>.
</div>
<div
className="text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
queued · will deliver when jordan&apos;s session goes idle
</div>
</div>
</div>
</div>
</Reveal>
<Reveal delay={2}>
<div className="mt-16 grid gap-8 md:grid-cols-2">
<div>
<h3
className="mb-3 text-2xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Mesh Dashboard
</h3>
<span
className="inline-block rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] px-2 py-0.5 text-[10px] uppercase tracking-wider text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
Beta
</span>
</div>
<div
className="flex flex-col items-start gap-5 text-[15px] leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
<p>
Watch every Claude Code session on your team. Who&apos;s working
on what. Who&apos;s idle. What messages are in flight. Route by
name, by repo, by priority.
</p>
<Link
href="#"
className="inline-flex items-center gap-2 rounded-[var(--cm-radius-xs)] border border-[var(--cm-fg-tertiary)] px-5 py-2.5 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:border-[var(--cm-fg)] hover:bg-[var(--cm-bg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Open the dashboard
</Link>
</div>
</div>
</Reveal>
</div>
</section>
);
};

View File

@@ -0,0 +1,142 @@
"use client";
import { useState } from "react";
import Link from "next/link";
const NEWS = [
{
tag: "Beta",
title: "Mesh Dashboard",
body: "Watch every Claude Code session on your team. Routes, presence, priority — all live.",
href: "#",
},
{
tag: "New",
title: "MCP bridge",
body: "Expose mesh messages as MCP tools. Your agent can message peers without leaving its context.",
href: "#",
},
{
tag: "Launch",
title: "Self-hosted broker",
body: "One binary. SQLite-backed. Runs on a Pi. Your mesh, never the cloud's.",
href: "#",
},
];
export const LatestNewsToaster = () => {
const [index, setIndex] = useState(0);
const [hidden, setHidden] = useState(false);
if (hidden) return null;
const item = NEWS[index];
if (!item) return null;
return (
<div
className="fixed bottom-6 right-6 z-[100] hidden w-[384px] rounded-[12px] border border-[var(--cm-border)] bg-[var(--cm-bg)] p-5 shadow-[0_20px_60px_rgba(0,0,0,0.45)] md:block"
role="complementary"
aria-label="Latest news"
>
{/* head */}
<div className="mb-4 flex items-center justify-between">
<div
className="flex items-center gap-1.5 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<svg width="14" height="14" viewBox="0 0 16 16" fill="none">
<path
d="M8 2C11.3137 2 14 4.68629 14 8C14 11.3137 11.3137 14 8 14C4.68629 14 2 11.3137 2 8C2 4.68629 4.68629 2 8 2ZM8 2.8C5.12812 2.8 2.8 5.12812 2.8 8C2.8 10.8719 5.12812 13.2 8 13.2C10.8719 13.2 13.2 10.8719 13.2 8C13.2 5.12812 10.8719 2.8 8 2.8ZM8.4 7.6V10H9.2C9.42091 10 9.6 10.1791 9.6 10.4C9.6 10.6209 9.42091 10.8 9.2 10.8H6.8C6.57909 10.8 6.4 10.6209 6.4 10.4C6.4 10.1791 6.57909 10 6.8 10H7.6V8H6.8C6.57909 8 6.4 7.82091 6.4 7.6C6.4 7.37909 6.57909 7.2 6.8 7.2H8C8.22091 7.2 8.4 7.37909 8.4 7.6ZM8 5.2C8.33137 5.2 8.6 5.46863 8.6 5.8C8.6 6.13137 8.33137 6.4 8 6.4C7.66863 6.4 7.4 6.13137 7.4 5.8C7.4 5.46863 7.66863 5.2 8 5.2Z"
fill="currentColor"
/>
</svg>
Latest news
</div>
<button
onClick={() => setHidden(true)}
className="rounded p-1 text-[var(--cm-fg-tertiary)] transition-colors hover:bg-[var(--cm-bg-elevated)] hover:text-[var(--cm-fg)]"
aria-label="Close"
>
<svg width="16" height="16" viewBox="0 0 20 20" fill="none">
<path
d="M15.15 4.15a.5.5 0 01.7.7L10.71 10l5.14 5.15a.5.5 0 01-.7.7L10 10.71l-5.15 5.14a.5.5 0 01-.7-.7L9.29 10 4.15 4.85a.5.5 0 01.7-.7L10 9.29l5.15-5.14z"
fill="currentColor"
/>
</svg>
</button>
</div>
{/* body */}
<div className="grid grid-cols-[1fr_108px] gap-4">
<div>
<h4
className="mb-2 text-[22px] font-medium leading-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{item.title}
</h4>
<p
className="mb-4 text-[12px] leading-[1.5] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{item.body}
</p>
<Link
href={item.href}
className="inline-flex items-center gap-1.5 rounded-[6px] bg-[var(--cm-fg)] px-3 py-1.5 text-[12px] font-medium text-[var(--cm-bg)] transition-colors hover:bg-[var(--cm-gray-150)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Learn more
</Link>
</div>
{/* illustration tile */}
<div className="flex h-[108px] w-[108px] items-center justify-center rounded-[8px] bg-[var(--cm-clay)]">
<svg width="68" height="68" viewBox="0 0 68 68" fill="none">
<circle cx="20" cy="20" r="4" fill="#141413" />
<circle cx="48" cy="16" r="4" fill="#141413" />
<circle cx="52" cy="40" r="4" fill="#141413" />
<circle cx="24" cy="44" r="4" fill="#141413" />
<path
d="M20 20L48 16L52 40L24 44L20 20z"
stroke="#141413"
strokeWidth="1.5"
fill="none"
/>
<path
d="M10 56c6-4 12-4 20 0s14 4 24-2"
stroke="#141413"
strokeWidth="1.5"
fill="none"
strokeLinecap="round"
/>
</svg>
</div>
</div>
{/* pager */}
<div className="mt-4 flex items-center justify-between border-t border-[var(--cm-border)] pt-3">
<div
className="text-[10px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{String(index + 1).padStart(2, "0")} / {String(NEWS.length).padStart(2, "0")}
</div>
<div className="flex gap-1">
<button
onClick={() =>
setIndex((i) => (i - 1 + NEWS.length) % NEWS.length)
}
className="rounded border border-[var(--cm-border)] px-2 py-1 text-xs text-[var(--cm-fg-secondary)] transition-colors hover:border-[var(--cm-fg)] hover:text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
aria-label="Previous"
>
</button>
<button
onClick={() => setIndex((i) => (i + 1) % NEWS.length)}
className="rounded border border-[var(--cm-border)] px-2 py-1 text-xs text-[var(--cm-fg-secondary)] transition-colors hover:border-[var(--cm-fg)] hover:text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
aria-label="Next"
>
</button>
</div>
</div>
</div>
);
};

View File

@@ -1,111 +1,87 @@
"use client";
import Link from "next/link";
import { useTranslation } from "@turbostarter/i18n";
import { Icons } from "@turbostarter/ui-web/icons";
import { pathsConfig } from "~/config/paths";
import { ThemeControls } from "~/modules/common/theme";
import { TurboLink } from "~/modules/common/turbo-link";
import { CtaButton } from "~/modules/marketing/layout/cta-button";
import { MobileNavigation } from "./navigation/mobile-navigation";
import { Navigation } from "./navigation/navigation";
const links = [
const NAV = [
{ label: "Docs", href: "#docs" },
{ label: "Pricing", href: "#pricing" },
{ label: "Changelog", href: "#changelog" },
{
label: "product",
items: [
{
title: "marketing:product.mobile.ios.title",
description: "marketing:product.mobile.ios.description",
href: "https://apps.apple.com/app/id6754278899",
icon: Icons.AppleStroke,
},
{
title: "marketing:product.mobile.android.title",
description: "marketing:product.mobile.android.description",
href: "https://play.google.com/store/apps/details?id=com.turbostarter.core",
icon: Icons.AndroidStroke,
},
{
title: "marketing:product.extension.chrome.title",
description: "marketing:product.extension.chrome.description",
href: "https://chromewebstore.google.com/detail/bcjmonmlfbnngpkllpnpmnjajaciaboo",
icon: Icons.ChromeStroke,
},
{
title: "marketing:product.extension.firefox.title",
description: "marketing:product.extension.firefox.description",
href: "https://addons.mozilla.org/addon/turbostarter_",
icon: Icons.FirefoxStroke,
},
{
title: "marketing:product.extension.edge.title",
description: "marketing:product.extension.edge.description",
href: "https://microsoftedge.microsoft.com/addons/detail/turbostarter/ianbflanmmoeleokihabnmmcahhfijig",
icon: Icons.EdgeStroke,
},
],
label: "GitHub",
href: "https://github.com/claudemesh/claudemesh",
external: true,
},
{
label: "resources",
items: [
{
title: "marketing:contact.label",
description: "marketing:contact.description",
href: pathsConfig.marketing.contact,
icon: Icons.SendHorizontal,
},
{
title: "marketing:roadmap.title",
description: "marketing:roadmap.description",
href: "https://github.com/orgs/turbostarter/projects/1",
icon: Icons.ChartNoAxesGantt,
},
{
title: "marketing:docs.title",
description: "marketing:docs.description",
href: "https://turbostarter.dev/docs/web",
icon: Icons.BookOpen,
},
{
title: "marketing:api.title",
description: "marketing:api.description",
href: "#",
icon: Icons.Webhook,
},
],
},
{
label: "billing:pricing.label",
href: pathsConfig.marketing.pricing,
},
{
label: "marketing:blog.label",
href: pathsConfig.marketing.blog.index,
},
] as const;
];
export const Header = () => {
const { t } = useTranslation("common");
return (
<header className="bg-background/80 sticky inset-0 top-[var(--banner-height)] z-40 w-full py-3 backdrop-blur-sm">
<div className="flex items-center justify-between px-6 pr-4 sm:container">
<TurboLink
href={pathsConfig.index}
className="flex shrink-0 items-center gap-3"
aria-label={t("home")}
<header
className="sticky top-0 z-40 w-full border-b border-[var(--cm-border)] bg-[var(--cm-bg)]/85 backdrop-blur-md"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<div className="mx-auto flex h-16 max-w-[var(--cm-max-w)] items-center justify-between px-6 md:px-10">
{/* wordmark */}
<Link
href="/"
aria-label="claudemesh home"
className="group flex shrink-0 items-center gap-2.5"
>
<Icons.Logo className="text-primary h-8" />
<Icons.LogoText className="text-foreground h-4" />
</TurboLink>
<svg
width="22"
height="22"
viewBox="0 0 24 24"
fill="none"
className="text-[var(--cm-clay)] transition-transform duration-300 group-hover:rotate-180"
>
<circle cx="12" cy="4" r="2" fill="currentColor" />
<circle cx="4" cy="12" r="2" fill="currentColor" />
<circle cx="20" cy="12" r="2" fill="currentColor" />
<circle cx="12" cy="20" r="2" fill="currentColor" />
<path
d="M12 4L4 12M12 4L20 12M4 12L12 20M20 12L12 20M4 12L20 12M12 4L12 20"
stroke="currentColor"
strokeWidth="1.2"
opacity="0.45"
/>
</svg>
<span
className="text-[17px] font-medium tracking-tight text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
claudemesh
</span>
</Link>
<Navigation links={links} />
{/* center nav */}
<nav className="hidden items-center gap-8 md:flex">
{NAV.map((item) => (
<Link
key={item.href}
href={item.href}
{...(item.external
? { target: "_blank", rel: "noreferrer" }
: {})}
className="text-[14px] text-[var(--cm-fg-secondary)] transition-colors hover:text-[var(--cm-fg)]"
>
{item.label}
</Link>
))}
</nav>
<div className="flex items-center justify-center lg:gap-2">
<ThemeControls />
<CtaButton className="hidden lg:inline-flex" />
<MobileNavigation links={links} />
{/* right */}
<div className="flex items-center gap-2">
<Link
href="/auth/login"
className="hidden rounded-[var(--cm-radius-xs)] px-3 py-2 text-[14px] text-[var(--cm-fg-secondary)] transition-colors hover:text-[var(--cm-fg)] md:inline-flex"
>
Sign in
</Link>
<Link
href="https://github.com/claudemesh/claudemesh"
target="_blank"
className="inline-flex items-center gap-1.5 rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-4 py-2 text-[14px] font-medium text-[var(--cm-fg)] transition-colors hover:bg-[var(--cm-clay-hover)]"
>
Start free
<span className="hidden sm:inline"></span>
</Link>
</div>
</div>
</header>