Code review: login no longer strands visitors with zero sign-in methods when
the providers fetch fails (falls back to SMS + error notice); skeleton while
providers load instead of a blank card.
Design re-audit: proof band flipped from apology to flex ('Don't take our
word for it / Every claim links to its proof') and promoted to the primary
heading tier; unverifiable '60 seconds'/'in minutes' speed claims dropped;
fg-subtle body text bumped to fg-muted (AA contrast); marketplace section
differentiated via border-y + elevated bg (adjacent hairlines removed);
preview frame traffic lights on-palette; header h-12 -> h-14 (hero svh calc
adjusted); emerald-400 -> --color-success token.
Content QA: rest-api article description drift between page and registry
resolved; ChatGPT plan-gating table and Atlassian Rovo SSE-cutoff claims now
carry dated hedges.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXUwmPVRTD8AKQtio6gCN5
656 lines
26 KiB
TypeScript
656 lines
26 KiB
TypeScript
import {
|
|
GitHubIcon,
|
|
NotionIcon,
|
|
PostgresIcon,
|
|
RestIcon,
|
|
SalesforceCloudIcon,
|
|
StripeIcon,
|
|
} from '@/components/brand-icons';
|
|
import { HeroStepRotator } from '@/components/hero-step-rotator';
|
|
import { HeroVideo } from '@/components/hero-video';
|
|
import { JsonLd } from '@/components/json-ld';
|
|
import { ParticleHero } from '@/components/particle-hero';
|
|
import { PulseLink } from '@/components/pulse';
|
|
import { ScrollCue } from '@/components/scroll-cue';
|
|
import { TIERS } from '@/lib/pricing';
|
|
import { FAQ, faqJsonLd } from '@/lib/seo';
|
|
import { Activity, ChevronDown, Container, ShieldCheck } from 'lucide-react';
|
|
import Link from 'next/link';
|
|
import type { ComponentType } from 'react';
|
|
|
|
interface ExampleEntry {
|
|
title: string;
|
|
desc: string;
|
|
/** Brand logo component rendered inside the coloured chip. */
|
|
Icon: ComponentType<{ size?: number; className?: string }>;
|
|
/** Official brand colour for the chip background. */
|
|
bg: string;
|
|
/** Foreground colour the icon paints in. */
|
|
fg: string;
|
|
}
|
|
|
|
const EXAMPLES: ExampleEntry[] = [
|
|
{
|
|
title: 'PostgreSQL',
|
|
desc: 'Read-only access to your tables with schema introspection.',
|
|
Icon: PostgresIcon,
|
|
bg: '#336791',
|
|
fg: '#ffffff',
|
|
},
|
|
{
|
|
title: 'Salesforce',
|
|
desc: 'Query opportunities, accounts and leads from Claude.',
|
|
Icon: SalesforceCloudIcon,
|
|
bg: '#00a1e0',
|
|
fg: '#ffffff',
|
|
},
|
|
{
|
|
title: 'Notion',
|
|
desc: 'Search pages, read content, append blocks.',
|
|
Icon: NotionIcon,
|
|
bg: '#ffffff',
|
|
fg: '#0a0a0b',
|
|
},
|
|
{
|
|
title: 'GitHub',
|
|
desc: 'List issues, search code, post comments. Scoped to one repo.',
|
|
Icon: GitHubIcon,
|
|
bg: '#181717',
|
|
fg: '#ffffff',
|
|
},
|
|
{
|
|
title: 'Stripe',
|
|
desc: 'Look up charges, customers, refunds (read-only by default).',
|
|
Icon: StripeIcon,
|
|
bg: '#635bff',
|
|
fg: '#ffffff',
|
|
},
|
|
{
|
|
title: 'Custom REST',
|
|
desc: 'Wrap any HTTP API behind one prompt-defined tool surface.',
|
|
Icon: RestIcon,
|
|
bg: '#6366f1',
|
|
fg: '#ffffff',
|
|
},
|
|
];
|
|
|
|
// Honest marketplace preview data: template names + tool counts only. These
|
|
// mirror the first-party starter templates; no invented fork counts, no
|
|
// "verified" theatre — the frame is labelled as a preview of the card format.
|
|
const PREVIEW_TEMPLATES: { name: string; author: string; tools: number }[] = [
|
|
{ name: 'notion-search', author: 'core', tools: 2 },
|
|
{ name: 'github-issues', author: 'core', tools: 3 },
|
|
{ name: 'stripe-readonly', author: 'core', tools: 4 },
|
|
{ name: 'postgres-readonly', author: 'core', tools: 3 },
|
|
];
|
|
|
|
const MARKETPLACE_POINTS: { t: string; d: string }[] = [
|
|
{
|
|
t: 'Fork and own',
|
|
d: 'Start from a server someone already shipped. Fork it, paste your own credentials, deploy. No prompt required.',
|
|
},
|
|
{
|
|
t: 'Secrets never travel',
|
|
d: "A template carries the spec and generated code, never the author's API keys. You add your own on fork.",
|
|
},
|
|
{
|
|
t: 'Open from day one',
|
|
d: 'Publish a server you built and anyone can fork it. The marketplace is young — early templates set the standard.',
|
|
},
|
|
];
|
|
|
|
// Proof-by-specificity band: three claims a visitor can verify with one
|
|
// click instead of taking our word for it. Substitute for social proof
|
|
// until there is social proof.
|
|
const PROOF_POINTS: {
|
|
t: string;
|
|
d: string;
|
|
href: string;
|
|
linkLabel: string;
|
|
Icon: ComponentType<{ size?: number; className?: string }>;
|
|
}[] = [
|
|
{
|
|
t: 'OAuth 2.1 authorization server',
|
|
d: 'PKCE, Dynamic Client Registration and Resource Indicators (RFC 8707) in front of every server.',
|
|
href: '/docs/oauth',
|
|
linkLabel: 'Read the auth docs',
|
|
Icon: ShieldCheck,
|
|
},
|
|
{
|
|
t: 'Live system status',
|
|
d: 'Uptime and incident history, public. If a build queue slows down, you see it before we tell you.',
|
|
href: '/status',
|
|
linkLabel: 'Check status now',
|
|
Icon: Activity,
|
|
},
|
|
{
|
|
t: 'Per-server container isolation',
|
|
d: 'Every generated server runs in its own Docker container. Secrets AES-256-GCM encrypted at rest.',
|
|
href: '/security',
|
|
linkLabel: 'Security architecture',
|
|
Icon: Container,
|
|
},
|
|
];
|
|
|
|
const PIPELINE_STEPS: { n: string; t: string; d: string }[] = [
|
|
{
|
|
n: '01',
|
|
t: 'Describe your tool',
|
|
d: 'A sentence is enough. List your secrets and which APIs to call.',
|
|
},
|
|
{
|
|
n: '02',
|
|
t: 'We generate, check, deploy',
|
|
d: 'Claude writes the spec. We render TypeScript, run static checks, build a container, deploy to your subdomain.',
|
|
},
|
|
{
|
|
n: '03',
|
|
t: 'Install in your client',
|
|
d: 'Copy the snippet into Claude Desktop, Cursor or ChatGPT. OAuth flow on first use.',
|
|
},
|
|
];
|
|
|
|
/** Mono shell-comment section kicker: `## how_it_works` */
|
|
function Kicker({ children }: { children: string }) {
|
|
return <p className="kicker">## {children}</p>;
|
|
}
|
|
|
|
export default function Landing() {
|
|
const teaserTiers = TIERS.filter((t) => t.name === 'Hobby' || t.name === 'Pro');
|
|
return (
|
|
<>
|
|
{/* Hero — left: copy + CTAs, right: cycling terminal tile. The WebGL
|
|
particle field sits behind at z-0 with pointer-events:none so the
|
|
CTAs stay interactive. */}
|
|
<section
|
|
className="relative flex items-center overflow-hidden border-b border-[--color-border]"
|
|
style={{ minHeight: 'calc(100svh - 3.5rem)' }}
|
|
>
|
|
<ParticleHero />
|
|
<div className="relative z-10 mx-auto grid w-full max-w-6xl gap-10 px-6 py-14 sm:py-20 md:grid-cols-[1.05fr_1fr] md:items-center md:gap-12">
|
|
<div className="min-w-0">
|
|
<h1 className="text-balance text-[36px] font-semibold leading-[1.04] tracking-[-0.03em] sm:text-[44px] md:text-[60px]">
|
|
Describe your tool.
|
|
<br />
|
|
We host the server.
|
|
<br />
|
|
<span className="text-[--color-fg-muted]">AI uses it.</span>
|
|
</h1>
|
|
<p className="mt-5 max-w-md text-[15px] leading-relaxed text-[--color-fg-muted] sm:text-[16px]">
|
|
From a prompt to a production MCP server — OAuth 2.1, Streamable HTTP, ready for
|
|
Claude, Cursor and ChatGPT.
|
|
</p>
|
|
<div className="mt-8 flex flex-wrap items-center gap-3">
|
|
<PulseLink
|
|
href="/login"
|
|
className="btn-brand inline-flex h-11 items-center justify-center rounded-md px-5 text-[14px] font-medium"
|
|
>
|
|
Start building free →
|
|
</PulseLink>
|
|
<PulseLink
|
|
href="#flow"
|
|
className="inline-flex h-11 items-center justify-center rounded-md border border-[--color-border] bg-[--color-bg-elevated] px-5 text-[14px] text-[--color-fg-muted] transition-colors hover:text-[--color-fg]"
|
|
>
|
|
Watch a build
|
|
</PulseLink>
|
|
</div>
|
|
<div className="mt-8 flex flex-wrap gap-x-6 gap-y-2 text-[12px] text-[--color-fg-muted]">
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-[--color-success]" /> OAuth 2.1 + PKCE
|
|
</span>
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-[--color-success]" /> Streamable HTTP
|
|
</span>
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-[--color-success]" /> AES-256 secrets
|
|
</span>
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-[--color-success]" /> Per-server isolation
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="relative min-w-0">
|
|
<HeroStepRotator />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<ScrollCue targetId="flow" />
|
|
|
|
{/* Flow video — full-width edge-to-edge under the hero. Plays when
|
|
scrolled into view (see hero-video.tsx); preload=metadata keeps the
|
|
2.6 MB mp4 off the critical path on mobile. */}
|
|
<section
|
|
id="flow"
|
|
className="relative w-full overflow-hidden border-b border-[--color-border] bg-black"
|
|
>
|
|
<div className="relative aspect-video w-full">
|
|
<HeroVideo />
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-0"
|
|
style={{
|
|
background:
|
|
'radial-gradient(ellipse at center, transparent 60%, rgba(10,10,11,0.55) 100%)',
|
|
}}
|
|
/>
|
|
</div>
|
|
</section>
|
|
|
|
{/* How it works — three pipeline nodes joined by a gradient connector.
|
|
Desktop: horizontal line behind the numbered nodes. Mobile: the grid
|
|
stacks and each card keeps its own node, joined by a left rail. */}
|
|
<section id="how" className="border-b border-[--color-border] py-14 sm:py-20">
|
|
<div className="mx-auto max-w-6xl px-6">
|
|
<div className="mb-10 max-w-2xl">
|
|
<Kicker>how_it_works</Kicker>
|
|
<h2 className="mt-3 text-[28px] font-semibold tracking-tight sm:text-[32px]">
|
|
Three steps. No JSON to write, no Docker to manage.
|
|
</h2>
|
|
</div>
|
|
|
|
<div className="relative">
|
|
{/* Gradient connector — desktop only, sits behind the node row. */}
|
|
<div
|
|
aria-hidden
|
|
className="absolute left-0 right-0 top-[22px] hidden h-px md:block"
|
|
style={{ background: 'var(--gradient-brand)', opacity: 0.35 }}
|
|
/>
|
|
{/* Mobile left rail joining the stacked nodes. */}
|
|
<div
|
|
aria-hidden
|
|
className="absolute bottom-6 left-[22px] top-[22px] w-px md:hidden"
|
|
style={{ background: 'var(--gradient-brand)', opacity: 0.35 }}
|
|
/>
|
|
<div className="grid gap-8 md:grid-cols-3 md:gap-6">
|
|
{PIPELINE_STEPS.map((s) => (
|
|
<div key={s.n} className="relative flex gap-4 md:block">
|
|
<div
|
|
className="mono relative z-10 flex size-11 shrink-0 items-center justify-center rounded-full border border-[--color-border-strong] text-[12px] tracking-widest text-[--color-fg]"
|
|
style={{
|
|
background: 'var(--color-bg)',
|
|
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.05)',
|
|
}}
|
|
>
|
|
{s.n}
|
|
</div>
|
|
<div className="min-w-0 md:mt-5">
|
|
<h3 className="text-[15px] font-semibold tracking-tight">{s.t}</h3>
|
|
<p className="mt-2 text-[13px] leading-relaxed text-[--color-fg-muted]">
|
|
{s.d}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Proof by specificity — three verifiable claims, each one click from
|
|
its evidence. Replaces the old pseudo-logo row: no invented marks,
|
|
just a plain-text compatibility line. */}
|
|
<section className="py-16 sm:py-24">
|
|
<div className="mx-auto max-w-6xl px-6">
|
|
<Kicker>verify_it_yourself</Kicker>
|
|
<h2 className="mt-3 max-w-2xl text-[32px] font-semibold leading-[1.1] tracking-tight sm:text-[40px]">
|
|
Don't take our word for it.
|
|
<br />
|
|
<span className="text-[--color-fg-muted]">Every claim links to its proof.</span>
|
|
</h2>
|
|
<div className="mt-10 grid gap-4 md:grid-cols-3">
|
|
{PROOF_POINTS.map((p) => {
|
|
const Icon = p.Icon;
|
|
return (
|
|
<Link
|
|
key={p.t}
|
|
href={p.href}
|
|
className="panel-raised group flex flex-col p-5 transition-colors hover:border-[--color-border-strong]"
|
|
>
|
|
<Icon size={20} className="text-[--color-accent]" />
|
|
<h3 className="mt-4 text-[15px] font-semibold tracking-tight">{p.t}</h3>
|
|
<p className="mt-2 flex-1 text-[13px] leading-relaxed text-[--color-fg-muted]">
|
|
{p.d}
|
|
</p>
|
|
<span className="mt-4 text-[13px] font-medium text-[--color-accent] transition-colors group-hover:text-[--color-fg]">
|
|
{p.linkLabel} →
|
|
</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
<p className="mt-10 text-center text-[13px] text-[--color-fg-muted]">
|
|
Works with Claude Desktop, Cursor, ChatGPT, VS Code Copilot and Continue.dev — anything
|
|
that speaks MCP.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Use cases — brand-coloured integration grid. No bottom hairline:
|
|
the marketplace section below brings its own border-y. */}
|
|
<section className="py-20 sm:py-28">
|
|
<div className="mx-auto max-w-6xl px-6">
|
|
<div className="mb-12 grid gap-6 md:grid-cols-[1fr_auto] md:items-end md:gap-12">
|
|
<div>
|
|
<Kicker>use_cases</Kicker>
|
|
<h2 className="mt-3 text-[32px] font-semibold leading-[1.1] tracking-tight sm:text-[40px]">
|
|
Wrap any HTTP API.
|
|
<br />
|
|
<span className="text-[--color-fg-muted]">From one prompt.</span>
|
|
</h2>
|
|
</div>
|
|
<p className="max-w-xs text-[14px] leading-relaxed text-[--color-fg-muted]">
|
|
Each shipped from a single prompt.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
{EXAMPLES.map((e) => {
|
|
const Icon = e.Icon;
|
|
return (
|
|
<div
|
|
key={e.title}
|
|
className="panel-raised group flex items-start gap-4 p-5 transition-colors hover:border-[--color-border-strong]"
|
|
>
|
|
<div
|
|
aria-hidden
|
|
className="flex size-12 shrink-0 items-center justify-center rounded-lg"
|
|
style={{
|
|
backgroundColor: e.bg,
|
|
color: e.fg,
|
|
}}
|
|
>
|
|
<Icon size={24} />
|
|
</div>
|
|
<div className="min-w-0">
|
|
<h3 className="text-[15px] font-semibold tracking-tight">{e.title}</h3>
|
|
<p className="mt-1.5 text-[13px] leading-relaxed text-[--color-fg-muted]">
|
|
{e.desc}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Marketplace — split layout: selling points left, honest preview
|
|
frame right. Elevated background (no hairline) so the page reads
|
|
in blocks instead of one continuously ruled ledger. */}
|
|
<section className="border-y border-[--color-border] bg-[--color-bg-elevated] py-20 sm:py-28">
|
|
<div className="mx-auto grid max-w-6xl gap-14 px-6 md:grid-cols-[5fr_6fr] md:items-center md:gap-16">
|
|
<div>
|
|
<Kicker>marketplace</Kicker>
|
|
<h2 className="mt-3 text-[32px] font-semibold leading-[1.1] tracking-tight sm:text-[40px]">
|
|
Skip the prompt.
|
|
<br />
|
|
<span className="text-[--color-fg-muted]">Fork what works.</span>
|
|
</h2>
|
|
<p className="mt-5 max-w-md text-[14px] leading-relaxed text-[--color-fg-muted]">
|
|
The marketplace is a library of working MCP servers. Fork one, paste your credentials,
|
|
deploy. Or publish yours and let others build on it.
|
|
</p>
|
|
|
|
<ul className="mt-8 space-y-5">
|
|
{MARKETPLACE_POINTS.map((p) => (
|
|
<li key={p.t} className="flex items-start gap-3">
|
|
<span
|
|
aria-hidden
|
|
className="mt-[7px] size-1.5 shrink-0 rounded-full bg-[--color-accent]"
|
|
/>
|
|
<div>
|
|
<h3 className="text-[14px] font-semibold tracking-tight">{p.t}</h3>
|
|
<p className="mt-1 text-[13px] leading-relaxed text-[--color-fg-muted]">
|
|
{p.d}
|
|
</p>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<PulseLink
|
|
href="/templates"
|
|
className="btn-brand mt-8 inline-flex h-11 items-center gap-2 rounded-md px-5 text-[14px] font-medium"
|
|
>
|
|
Browse the marketplace →
|
|
</PulseLink>
|
|
</div>
|
|
|
|
<MarketplacePreview />
|
|
</div>
|
|
</section>
|
|
|
|
{/* Pricing teaser — Hobby + Pro only; the full matrix lives on
|
|
/pricing. Data is shared via lib/pricing.ts so it can't drift. */}
|
|
<section id="pricing" className="border-b border-[--color-border] py-20 sm:py-28">
|
|
<div className="mx-auto max-w-4xl px-6">
|
|
<div className="mb-12 text-center">
|
|
<Kicker>pricing</Kicker>
|
|
<h2 className="mt-3 text-[32px] font-semibold leading-[1.1] tracking-tight sm:text-[40px]">
|
|
Pay for tool calls.
|
|
<br />
|
|
<span className="text-[--color-fg-muted]">Not for boilerplate.</span>
|
|
</h2>
|
|
</div>
|
|
|
|
<div className="grid gap-5 sm:grid-cols-2">
|
|
{teaserTiers.map((t) => {
|
|
const featured = Boolean(t.highlight);
|
|
return (
|
|
<div
|
|
key={t.name}
|
|
className={`relative flex flex-col gap-6 rounded-xl border p-6 ${
|
|
featured
|
|
? 'border-[--color-accent] bg-[--color-bg-elevated]'
|
|
: 'border-[--color-border] bg-[--color-bg-elevated]'
|
|
}`}
|
|
style={
|
|
featured
|
|
? {
|
|
boxShadow:
|
|
'0 0 0 4px rgba(99, 102, 241, 0.12), 0 24px 50px rgba(0, 0, 0, 0.35)',
|
|
}
|
|
: { boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.04)' }
|
|
}
|
|
>
|
|
{featured && (
|
|
<span
|
|
className="absolute -top-3 left-1/2 -translate-x-1/2 whitespace-nowrap rounded-full border border-[--color-accent] px-3 py-0.5 text-[10px] font-semibold uppercase tracking-[0.18em] text-[--color-accent]"
|
|
style={{ backgroundColor: 'var(--color-bg)' }}
|
|
>
|
|
Recommended
|
|
</span>
|
|
)}
|
|
|
|
<div>
|
|
<div className="text-[11px] font-medium uppercase tracking-[0.18em] text-[--color-fg-muted]">
|
|
{t.name}
|
|
</div>
|
|
<div className="mt-3 flex items-baseline gap-2">
|
|
<span className="text-[40px] font-semibold leading-none tracking-tight text-[--color-fg]">
|
|
{t.price}
|
|
</span>
|
|
<span className="text-[12px] text-[--color-fg-subtle]">{t.tag}</span>
|
|
</div>
|
|
<p className="mt-3 text-[13px] leading-relaxed text-[--color-fg-muted]">
|
|
{t.description}
|
|
</p>
|
|
</div>
|
|
|
|
<ul className="flex flex-1 flex-col gap-2.5 border-t border-[--color-border] pt-5 text-[13px] text-[--color-fg-muted]">
|
|
{t.features.map((f) => (
|
|
<li key={f} className="flex items-start gap-2.5">
|
|
<span
|
|
aria-hidden
|
|
className={`mt-[7px] size-1 shrink-0 rounded-full ${
|
|
featured ? 'bg-[--color-accent]' : 'bg-[--color-fg-subtle]'
|
|
}`}
|
|
/>
|
|
<span>{f}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<Link
|
|
href={t.href}
|
|
className={`inline-flex h-10 items-center justify-center rounded-md px-4 text-[13px] font-medium transition-colors ${
|
|
featured
|
|
? 'btn-brand'
|
|
: 'border border-[--color-border] bg-[--color-bg-subtle] text-[--color-fg] hover:border-[--color-border-strong]'
|
|
}`}
|
|
>
|
|
{t.cta}
|
|
</Link>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div className="mt-8 text-center">
|
|
<Link
|
|
href="/pricing"
|
|
className="text-[14px] font-medium text-[--color-accent] transition-colors hover:text-[--color-fg]"
|
|
>
|
|
See all plans — Team, Enterprise →
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* FAQ — collapsible accordion using native <details>. Crawlers and
|
|
screen readers see the full Q+A in the HTML. */}
|
|
<section className="border-b border-[--color-border] py-14 sm:py-20">
|
|
<JsonLd data={faqJsonLd()} />
|
|
<div className="mx-auto max-w-3xl px-6">
|
|
<Kicker>faq</Kicker>
|
|
<h2 className="mt-3 text-[28px] font-semibold tracking-tight">
|
|
Questions, answered straight.
|
|
</h2>
|
|
<div className="mt-8 border-t border-[--color-border]">
|
|
{FAQ.map((f) => (
|
|
<details
|
|
key={f.q}
|
|
className="group border-b border-[--color-border] [&_summary::-webkit-details-marker]:hidden"
|
|
>
|
|
<summary className="flex cursor-pointer list-none items-center justify-between gap-4 py-4 text-[14.5px] font-semibold tracking-tight text-[--color-fg] transition-colors hover:text-[--color-accent]">
|
|
<span>{f.q}</span>
|
|
<ChevronDown
|
|
size={16}
|
|
className="shrink-0 text-[--color-fg-subtle] transition-transform duration-200 group-open:rotate-180 group-open:text-[--color-accent]"
|
|
/>
|
|
</summary>
|
|
<p className="pb-5 pr-8 text-[13.5px] leading-relaxed text-[--color-fg-muted]">
|
|
{f.a}
|
|
</p>
|
|
</details>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Final CTA — the page must not end on FAQ. Gradient top border keeps
|
|
the brand mark scarce but present at the exit point. */}
|
|
<section className="relative py-20 sm:py-28">
|
|
<div
|
|
aria-hidden
|
|
className="absolute inset-x-0 top-0 h-px"
|
|
style={{ background: 'var(--gradient-brand)', opacity: 0.6 }}
|
|
/>
|
|
<div className="mx-auto max-w-3xl px-6 text-center">
|
|
<h2 className="text-balance text-[32px] font-semibold leading-[1.1] tracking-tight sm:text-[44px]">
|
|
Your first server is <span className="text-brand-gradient">one prompt away</span>.
|
|
</h2>
|
|
<p className="mx-auto mt-4 max-w-md text-[14.5px] leading-relaxed text-[--color-fg-muted]">
|
|
Free tier, full source export, no lock-in. If it doesn't work for you, take the
|
|
TypeScript and leave.
|
|
</p>
|
|
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
|
|
<PulseLink
|
|
href="/login"
|
|
className="btn-brand inline-flex h-11 items-center justify-center rounded-md px-6 text-[14px] font-medium"
|
|
>
|
|
Start building free →
|
|
</PulseLink>
|
|
<PulseLink
|
|
href="/docs"
|
|
className="inline-flex h-11 items-center justify-center rounded-md border border-[--color-border] bg-[--color-bg-elevated] px-6 text-[14px] text-[--color-fg-muted] transition-colors hover:text-[--color-fg]"
|
|
>
|
|
Read the docs
|
|
</PulseLink>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Honest marketplace preview.
|
|
*
|
|
* Static, server-rendered browser frame showing what a template card looks
|
|
* like. Card data mirrors the first-party starter templates — names, author
|
|
* and tool counts only. No fork counts, no "verified" badges: the frame is
|
|
* explicitly labelled a preview, not live marketplace traffic.
|
|
*/
|
|
function MarketplacePreview() {
|
|
return (
|
|
<div
|
|
className="overflow-hidden rounded-xl border border-[--color-border-strong] bg-[--color-bg-elevated]"
|
|
style={{
|
|
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.04), 0 24px 60px rgba(0, 0, 0, 0.45)',
|
|
}}
|
|
>
|
|
{/* Browser chrome */}
|
|
<div className="flex items-center gap-3 border-b border-[--color-border] bg-[--color-bg-subtle] px-4 py-3">
|
|
<div className="flex gap-1.5">
|
|
<span className="size-2.5 rounded-full bg-[#ff5f57]/80" />
|
|
<span className="size-2.5 rounded-full bg-[#febc2e]/80" />
|
|
<span className="size-2.5 rounded-full bg-[#28c840]/80" />
|
|
</div>
|
|
<div className="mono flex-1 truncate rounded-md border border-[--color-border] bg-[--color-bg] px-3 py-1 text-[11px] text-[--color-fg-subtle]">
|
|
buildmymcpserver.com/templates
|
|
</div>
|
|
</div>
|
|
|
|
{/* Toolbar inside the page chrome */}
|
|
<div className="flex items-center justify-between gap-4 border-b border-[--color-border] px-5 py-3">
|
|
<span className="text-[13px] font-semibold tracking-tight text-[--color-fg]">
|
|
Templates
|
|
</span>
|
|
<span className="mono text-[10px] uppercase tracking-wider text-[--color-fg-subtle]">
|
|
preview
|
|
</span>
|
|
</div>
|
|
|
|
{/* Template cards grid */}
|
|
<div className="grid gap-3 p-4 sm:grid-cols-2">
|
|
{PREVIEW_TEMPLATES.map((t) => (
|
|
<div
|
|
key={t.name}
|
|
className="rounded-md border border-[--color-border] bg-[--color-bg-subtle] p-4 transition-colors hover:border-[--color-accent]/40"
|
|
>
|
|
<div className="min-w-0">
|
|
<div className="mono truncate text-[13px] font-semibold tracking-tight text-[--color-fg]">
|
|
{t.name}
|
|
</div>
|
|
<div className="mt-1 flex items-center gap-1.5">
|
|
<span className="mono text-[10px] uppercase tracking-wider text-[--color-fg-subtle]">
|
|
{t.author}
|
|
</span>
|
|
<span className="inline-flex items-center rounded-full border border-[--color-border-strong] px-1.5 py-px text-[9.5px] font-medium uppercase tracking-wider text-[--color-fg-subtle]">
|
|
template
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 text-[11px] text-[--color-fg-subtle]">
|
|
<span className="mono">{t.tools} tools</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|