All checks were successful
Deploy to Production / deploy (push) Successful in 1m32s
The false RBAC / 99.9 SLA / BYOC / custom-domain claims were not only on /pricing but also on the landing-page tier cards, the in-app billing upgrade cards, and — most seriously — the AGB and Terms as a binding 99.9 monthly uptime SLA the single-host infra cannot meet. Aligned all of them: SLA removed from AGB/Terms (best-effort, no guaranteed SLA for self-serve; Enterprise by contract); landing+billing cards now show Audit log, RBAC coming-soon, custom-domain coming-soon, honest Enterprise infra; landing Team price corrected 149->199; billing cards model name Haiku/Sonnet -> Claude AI. Privacy page intentionally keeps exact model names for data-residency disclosure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
644 lines
28 KiB
TypeScript
644 lines
28 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 { StaticCodeBlock } from '@/components/static-code-block';
|
|
import { FAQ, faqJsonLd } from '@/lib/seo';
|
|
import { ChevronDown } from 'lucide-react';
|
|
import type { ComponentType } from 'react';
|
|
import Link from 'next/link';
|
|
|
|
const PROMPT_EXAMPLE = `Create an MCP server that searches our Notion workspace.
|
|
Tools: search_pages, get_page_content.
|
|
Auth: NOTION_API_KEY.`;
|
|
|
|
const OUTPUT_EXAMPLE = `> Generating spec... OK (2 tools)
|
|
> Static checks OK
|
|
> Building image bmm-mcp-notion OK 17.2s
|
|
> Deploying container OK
|
|
> Live at https://notion-x9.mcp.buildmymcpserver.com
|
|
> First request: 401 → token → 200 OK`;
|
|
|
|
const INSTALL_SNIPPET = `{
|
|
"mcpServers": {
|
|
"notion": {
|
|
"url": "https://notion-x9.mcp.buildmymcpserver.com/mcp",
|
|
"auth": "oauth2"
|
|
}
|
|
}
|
|
}`;
|
|
|
|
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' },
|
|
];
|
|
|
|
interface ClientEntry {
|
|
name: string;
|
|
/** Single-character mark for inline visual identity. */
|
|
mark: string;
|
|
}
|
|
|
|
const CLIENTS: ClientEntry[] = [
|
|
{ name: 'Claude Desktop', mark: 'C' },
|
|
{ name: 'Cursor', mark: '⌘' },
|
|
{ name: 'ChatGPT', mark: '✦' },
|
|
{ name: 'VS Code Copilot', mark: '<>' },
|
|
{ name: 'Continue.dev', mark: '→' },
|
|
];
|
|
|
|
interface MockTemplate {
|
|
name: string;
|
|
author: string;
|
|
tools: number;
|
|
forks: number;
|
|
verified: boolean;
|
|
}
|
|
|
|
const MOCK_TEMPLATES: MockTemplate[] = [
|
|
{ name: 'notion-search', author: 'core', tools: 2, forks: 247, verified: true },
|
|
{ name: 'github-issues', author: 'core', tools: 3, forks: 89, verified: true },
|
|
{ name: 'stripe-readonly', author: 'core', tools: 4, forks: 156, verified: true },
|
|
{ name: 'linear-tasks', author: 'community', tools: 5, forks: 34, verified: false },
|
|
];
|
|
|
|
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: 'Ranked by real usage',
|
|
d: 'Templates rise on fork count and active deploys, not vanity stars. The useful ones surface themselves.',
|
|
},
|
|
];
|
|
|
|
const TIERS = [
|
|
{
|
|
name: 'Hobby',
|
|
price: '€0',
|
|
tag: 'Forever free',
|
|
features: ['1 server', '100k calls/mo', 'BMM subdomain', 'Community support'],
|
|
},
|
|
{
|
|
name: 'Pro',
|
|
price: '€49',
|
|
tag: '/ month',
|
|
features: [
|
|
'5 servers',
|
|
'1M calls/mo',
|
|
'Priority build queue',
|
|
'Custom domain · soon',
|
|
'Email support',
|
|
],
|
|
},
|
|
{
|
|
name: 'Team',
|
|
price: '€199',
|
|
tag: '/ month',
|
|
features: ['25 servers', '10M calls/mo', 'Audit log', 'RBAC · soon', 'Slack support'],
|
|
},
|
|
{
|
|
name: 'Enterprise',
|
|
price: '€499+',
|
|
tag: '/ month',
|
|
features: ['Unlimited', 'Custom infra · on request', 'SSO/SAML · on request', 'Dedicated hosting', 'Customer success'],
|
|
},
|
|
];
|
|
|
|
export default function Landing() {
|
|
return (
|
|
<>
|
|
{/* Hero — left: copy + CTAs, right: cycling step-rotator tile.
|
|
The old layout stacked three static code blocks vertically; the
|
|
new layout shows one centered tile that cycles through the same
|
|
three artifacts (prompt → build.log → claude config) with a
|
|
mouse-reactive 3D tilt and a step indicator. Shorter overall
|
|
so the video section below is teased above the fold. */}
|
|
<section
|
|
className="relative flex items-center overflow-hidden border-b border-[--color-border]"
|
|
style={{ minHeight: 'calc(100svh - 3rem)' }}
|
|
>
|
|
{/* WebGL particle field — capability-detected client component.
|
|
Sits behind the hero content at z-0 with pointer-events:none
|
|
so the CTAs above remain fully interactive. The canvas listens
|
|
for pointermove on window itself, so the ring still tracks
|
|
the cursor through the content above. With the hero now
|
|
filling the full first-viewport (minus the 48px sticky nav),
|
|
the field has cinematic-scale room and the indigo radial
|
|
glow + dot mask read as the dominant background motif. */}
|
|
<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">
|
|
<span className="mono inline-block rounded-full border border-[--color-border] bg-[--color-bg-elevated] px-2.5 py-0.5 text-[11px] tracking-wide text-[--color-fg-muted]">
|
|
v0.1 · updated 2026-05-20
|
|
</span>
|
|
<h1 className="mt-6 text-balance text-[30px] font-semibold leading-[1.06] tracking-tight sm:text-[40px] md:text-[52px]">
|
|
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]">
|
|
From prompt to production MCP server in 60 seconds. OAuth 2.1, Streamable HTTP, ready
|
|
for Claude, Cursor and ChatGPT.
|
|
</p>
|
|
<div className="mt-7 flex flex-wrap items-center gap-3">
|
|
<PulseLink
|
|
href="/login"
|
|
className="inline-flex h-9 items-center justify-center rounded-md bg-[--color-accent] px-4 text-[13px] font-medium text-white transition-colors duration-200 hover:bg-[#5557e8]"
|
|
>
|
|
Start building free
|
|
</PulseLink>
|
|
<PulseLink
|
|
href="/docs"
|
|
className="inline-flex h-9 items-center justify-center rounded-md border border-[--color-border] bg-[--color-bg-elevated] px-4 text-[13px] text-[--color-fg-muted] transition-colors hover:text-[--color-fg]"
|
|
>
|
|
Read the docs
|
|
</PulseLink>
|
|
</div>
|
|
<div className="mt-8 flex flex-wrap gap-x-6 gap-y-2 text-[12px] text-[--color-fg-subtle]">
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-emerald-400" /> OAuth 2.1 + PKCE
|
|
</span>
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-emerald-400" /> Streamable HTTP
|
|
</span>
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-emerald-400" /> AES-256 secrets
|
|
</span>
|
|
<span className="inline-flex items-center gap-1.5">
|
|
<span className="size-1.5 rounded-full bg-emerald-400" /> Per-server isolation
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="relative min-w-0">
|
|
<HeroStepRotator />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{/* Scroll cue — fixed at the bottom of the loadscreen rather than
|
|
inside the hero, so it sits at the natural lower edge of the
|
|
first viewport regardless of how tall the hero ends up. Fades
|
|
out once the user has scrolled past the loadscreen. */}
|
|
<ScrollCue targetId="flow" />
|
|
|
|
{/* Flow video — full-width edge-to-edge under the hero. The clip
|
|
shows the real flow (prompt → server schematic → live connection
|
|
to Claude Desktop) in three smooth phases. autoplay-muted-loop +
|
|
playsInline satisfies every mobile browser autoplay policy; the
|
|
`poster` carries first paint while the video decodes. */}
|
|
<section
|
|
id="flow"
|
|
className="relative w-full overflow-hidden border-b border-[--color-border] bg-black"
|
|
>
|
|
<div className="relative aspect-video w-full">
|
|
{/* HeroVideo: native <video> with autoplay+muted+loop, plus a
|
|
frosted mute toggle pinned bottom-right so visitors can
|
|
switch the narration on. See components/hero-video.tsx for
|
|
the autoplay/unmute mechanics. */}
|
|
<HeroVideo />
|
|
{/* Subtle vignette to integrate edges into the rest of the page */}
|
|
<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 */}
|
|
<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">
|
|
<h2 className="text-[28px] font-semibold tracking-tight">How it works</h2>
|
|
<p className="mt-2 text-[14px] text-[--color-fg-muted]">
|
|
Three steps. No JSON to write, no Docker to manage.
|
|
</p>
|
|
</div>
|
|
|
|
{/* The same video used to live here; it now has its own
|
|
full-width section directly under the hero so it's teased
|
|
above the fold and gets edge-to-edge real estate. This
|
|
section keeps the three explanatory cards as supporting
|
|
copy under the video. */}
|
|
<div className="grid gap-6 md:grid-cols-3">
|
|
{[
|
|
{
|
|
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.',
|
|
},
|
|
].map((s) => (
|
|
<div key={s.n} className="panel p-5">
|
|
<div className="mono text-[11px] tracking-widest text-[--color-fg-subtle]">
|
|
{s.n}
|
|
</div>
|
|
<h3 className="mt-4 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>
|
|
</section>
|
|
|
|
{/* ── Clients · trust-signal row ─────────────────────────────────
|
|
Archetype: typographic logo row. No panels, no bullets, just
|
|
well-spaced word-marks with a small symbolic mark. The whole
|
|
section breathes — generous py — so it reads as a moment of
|
|
trust, not a feature card. */}
|
|
<section className="border-b border-[--color-border] bg-[--color-bg] py-20 sm:py-24">
|
|
<div className="mx-auto max-w-6xl px-6">
|
|
<p className="text-center text-[11px] uppercase tracking-[0.24em] text-[--color-fg-subtle]">
|
|
Connects everywhere your AI lives
|
|
</p>
|
|
<div className="mt-12 flex flex-wrap items-center justify-center gap-x-14 gap-y-7 sm:gap-x-20">
|
|
{CLIENTS.map((c) => (
|
|
<div
|
|
key={c.name}
|
|
className="group flex items-center gap-3 transition-colors"
|
|
>
|
|
<span
|
|
aria-hidden
|
|
className="mono flex size-7 items-center justify-center rounded-md border border-[--color-border] text-[12px] text-[--color-fg-muted] transition-colors group-hover:border-[--color-accent] group-hover:text-[--color-accent]"
|
|
>
|
|
{c.mark}
|
|
</span>
|
|
<span className="text-[17px] font-medium tracking-tight text-[--color-fg-muted] transition-colors group-hover:text-[--color-fg]">
|
|
{c.name}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Examples · integration grid with brand-coloured marks ──────
|
|
Archetype: 2-col header (title left, supporting copy right) +
|
|
asymmetric 3-col card grid where each card carries a coloured
|
|
square brand mark for the service. The marks give each card
|
|
its own visual identity, breaking the "every card looks the
|
|
same" pattern of the previous version. */}
|
|
<section className="border-b border-[--color-border] 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>
|
|
<p className="text-[11px] uppercase tracking-[0.22em] text-[--color-fg-subtle]">
|
|
Use cases
|
|
</p>
|
|
<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]">In minutes.</span>
|
|
</h2>
|
|
</div>
|
|
<p className="max-w-xs text-[14px] leading-relaxed text-[--color-fg-muted]">
|
|
Real integrations our customers ship today. Built from one prompt each.
|
|
</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="group flex items-start gap-4 rounded-xl border border-[--color-border] bg-[--color-bg-elevated] 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 · two-column with product preview ──────────────
|
|
Archetype: split layout. Left column carries the headline,
|
|
three bullet-form selling points, and the CTA. Right column
|
|
carries a mock browser frame containing real-looking template
|
|
cards — so the visitor SEES the marketplace, not just reads
|
|
a description of it. */}
|
|
<section className="border-b border-[--color-border] 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">
|
|
{/* Left — text + points + CTA */}
|
|
<div>
|
|
<p className="text-[11px] uppercase tracking-[0.22em] text-[--color-fg-subtle]">
|
|
Marketplace
|
|
</p>
|
|
<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 the community already built.
|
|
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="mt-8 inline-flex h-9 items-center gap-2 rounded-md bg-[--color-accent] px-4 text-[13px] font-medium text-white transition-colors duration-200 hover:bg-[#5557e8]"
|
|
>
|
|
Browse the marketplace →
|
|
</PulseLink>
|
|
</div>
|
|
|
|
{/* Right — mock marketplace browser frame */}
|
|
<MarketplaceMock />
|
|
</div>
|
|
</section>
|
|
|
|
{/* ── Pricing · 4 cards with Pro highlighted as recommended ─────
|
|
Archetype: centred header + 4-card row where the Pro tier is
|
|
visually distinct (accent border, indigo glow shadow, floating
|
|
"recommended" pill, slightly elevated). Other tiers stay calm
|
|
so the eye lands on Pro first. Big price typography (text-
|
|
[40px]) replaces the previous flat 26px. */}
|
|
<section id="pricing" className="border-b border-[--color-border] py-20 sm:py-28">
|
|
<div className="mx-auto max-w-6xl px-6">
|
|
<div className="mb-14 text-center">
|
|
<p className="text-[11px] uppercase tracking-[0.22em] text-[--color-fg-subtle]">
|
|
Pricing
|
|
</p>
|
|
<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 md:grid-cols-2 lg:grid-cols-4">
|
|
{TIERS.map((t) => {
|
|
const featured = t.name === 'Pro';
|
|
return (
|
|
<div
|
|
key={t.name}
|
|
className={`relative flex flex-col gap-6 rounded-xl border p-6 transition-colors ${
|
|
featured
|
|
? 'border-[--color-accent] bg-[--color-bg-elevated]'
|
|
: 'border-[--color-border] bg-[--color-bg-elevated] hover:border-[--color-border-strong]'
|
|
}`}
|
|
style={
|
|
featured
|
|
? {
|
|
boxShadow:
|
|
'0 0 0 4px rgba(99, 102, 241, 0.12), 0 24px 50px rgba(0, 0, 0, 0.35)',
|
|
}
|
|
: undefined
|
|
}
|
|
>
|
|
{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>
|
|
</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>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* FAQ — collapsible accordion using native <details>. Crawlers
|
|
and screen readers still see the full Q+A in the HTML; users
|
|
see one question at a time and expand on demand. No JS, no
|
|
state, semantically correct. `list-none` + the WebKit-marker
|
|
pseudo-class suppress the default disclosure triangle so we
|
|
can render our own chevron that rotates via `group-open`. */}
|
|
<section className="py-14 sm:py-20">
|
|
<JsonLd data={faqJsonLd()} />
|
|
<div className="mx-auto max-w-3xl px-6">
|
|
<h2 className="text-[28px] font-semibold tracking-tight">FAQ</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>
|
|
</>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Mock marketplace preview.
|
|
*
|
|
* Static, server-rendered. Renders a faux-browser frame containing four
|
|
* realistic template cards so the visitor sees what the marketplace
|
|
* actually looks like rather than reading marketing copy about it.
|
|
*
|
|
* The card data lives in `MOCK_TEMPLATES` at the top of this file —
|
|
* those are real template-flavoured names, modest fork counts, and the
|
|
* `core` / `community` author tag. Numbers are deliberately small and
|
|
* truthful-looking for a young marketplace; nothing here pretends to be
|
|
* GitHub-trending traffic.
|
|
*/
|
|
function MarketplaceMock() {
|
|
return (
|
|
<div
|
|
className="overflow-hidden rounded-xl border border-[--color-border-strong] bg-[--color-bg-elevated]"
|
|
style={{ boxShadow: '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-zinc-700" />
|
|
<span className="size-2.5 rounded-full bg-zinc-700" />
|
|
<span className="size-2.5 rounded-full bg-zinc-700" />
|
|
</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">
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-[13px] font-semibold tracking-tight text-[--color-fg]">
|
|
Templates
|
|
</span>
|
|
<span className="text-[11px] text-[--color-fg-subtle]">·</span>
|
|
<span className="text-[11px] text-[--color-fg-subtle]">{MOCK_TEMPLATES.length} live</span>
|
|
</div>
|
|
<div className="hidden items-center gap-1.5 rounded-md border border-[--color-border] px-2 py-1 sm:flex">
|
|
<span className="text-[11px] text-[--color-fg-subtle]">trending</span>
|
|
<ChevronDown size={11} className="text-[--color-fg-subtle]" />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Template cards grid */}
|
|
<div className="grid gap-3 p-4 sm:grid-cols-2">
|
|
{MOCK_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="flex items-start justify-between gap-3">
|
|
<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>
|
|
{t.verified && (
|
|
<span className="inline-flex items-center gap-0.5 rounded-full border border-[--color-accent]/50 bg-[--color-accent]/10 px-1.5 py-px text-[9.5px] font-medium uppercase tracking-wider text-[--color-accent]">
|
|
✓ verified
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 flex items-center justify-between text-[11px] text-[--color-fg-subtle]">
|
|
<span className="mono">{t.tools} tools</span>
|
|
<span className="mono inline-flex items-center gap-1">
|
|
<ForkGlyph />
|
|
{t.forks}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ForkGlyph() {
|
|
return (
|
|
<svg width="11" height="11" viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
<circle cx="3.5" cy="3" r="1.4" stroke="currentColor" strokeWidth={1.2} />
|
|
<circle cx="10.5" cy="3" r="1.4" stroke="currentColor" strokeWidth={1.2} />
|
|
<circle cx="7" cy="11" r="1.4" stroke="currentColor" strokeWidth={1.2} />
|
|
<path
|
|
d="M 3.5 4.5 L 3.5 6.5 Q 3.5 7.5 4.5 7.5 L 9.5 7.5 Q 10.5 7.5 10.5 6.5 L 10.5 4.5"
|
|
stroke="currentColor"
|
|
strokeWidth={1.2}
|
|
fill="none"
|
|
/>
|
|
<path d="M 7 7.5 L 7 9.5" stroke="currentColor" strokeWidth={1.2} />
|
|
</svg>
|
|
);
|
|
}
|