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,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>
);
};