website: add /cloud design partners page and shared shell

- New /cloud page with signup form (email, name, company, role, message)
  posting to a Discord webhook via a TanStack Start server function on
  the Cloudflare Worker. Webhook URL read from DISCORD_WEBHOOK_URL secret.
- Extract SiteShell, SiteFooter, FAQItem from landing-page.
- Migrate /privacy, /download, /blog, /changelog, /cloud to SiteShell so
  they share header + footer with consistent width.
- Add /cloud to header and footer nav.
- Make /docs sidebar full-width; bump footer text from xs to sm.
- Add Cloudflare Workers types and ignore .dev.vars.
This commit is contained in:
Mohamed Boudra
2026-05-06 10:45:14 +07:00
parent ea4d6bb7ce
commit 0eacc98e17
16 changed files with 730 additions and 431 deletions
@@ -0,0 +1,14 @@
import type { ReactNode } from "react";
export function FAQItem({ question, children }: { question: string; children: ReactNode }) {
return (
<details className="group">
<summary className="font-medium text-sm cursor-pointer list-none flex items-start gap-2 -ml-4">
<span className="font-mono text-white/40 flex-shrink-0 group-open:hidden">+</span>
<span className="font-mono text-white/40 flex-shrink-0 hidden group-open:inline">−</span>
{question}
</summary>
<div className="text-sm text-muted-foreground space-y-2 mt-2 prose">{children}</div>
</details>
);
}
@@ -48,6 +48,8 @@ import { useRelease } from "~/routes/__root";
import { Mic } from "lucide-react";
import { HeroMockup } from "~/components/hero-mockup";
import { ClaudeIcon } from "~/components/mockup";
import { FAQItem } from "~/components/faq-item";
import { SiteFooter } from "~/components/site-footer";
import { SiteHeader } from "~/components/site-header";
import "~/styles.css";
@@ -97,126 +99,7 @@ export function LandingPage({ title, subtitle }: LandingPageProps) {
<SponsorCTA />
</div>
</main>
<footer className="p-6 md:p-20 md:pt-0 max-w-5xl mx-auto">
<div className="border-t border-white/10 pt-8 pb-4 grid grid-cols-2 sm:grid-cols-4 gap-8 text-xs">
<div className="space-y-3">
<p className="text-white/60 font-medium">Product</p>
<div className="space-y-2">
<a
href="/blog"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Blog
</a>
<a
href="/docs"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Docs
</a>
<a
href="/changelog"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Changelog
</a>
<a
href="/docs/cli"
className="block text-white/40 hover:text-white/60 transition-colors"
>
CLI
</a>
<a
href="/privacy"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Privacy
</a>
</div>
</div>
<div className="space-y-3">
<p className="text-white/60 font-medium">Agents</p>
<div className="space-y-2">
<a
href="/claude-code"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Claude Code
</a>
<a
href="/codex"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Codex
</a>
<a
href="/opencode"
className="block text-white/40 hover:text-white/60 transition-colors"
>
OpenCode
</a>
</div>
</div>
<div className="space-y-3">
<p className="text-white/60 font-medium">Community</p>
<div className="space-y-2">
<a
href="https://discord.gg/jz8T2uahpH"
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Discord
</a>
<a
href="https://github.com/getpaseo/paseo"
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
GitHub
</a>
</div>
</div>
<div className="space-y-3">
<p className="text-white/60 font-medium">Download</p>
<div className="space-y-2">
<a
href={appStoreUrl}
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
App Store
</a>
<a
href={playStoreUrl}
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Google Play
</a>
<a
href="https://github.com/getpaseo/paseo/releases"
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Desktop
</a>
<a
href={webAppUrl}
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Web App
</a>
</div>
</div>
</div>
</footer>
<SiteFooter />
</div>
</CursorFieldProvider>
);
@@ -1685,16 +1568,3 @@ function SponsorCTA() {
</motion.div>
);
}
function FAQItem({ question, children }: { question: string; children: React.ReactNode }) {
return (
<details className="group">
<summary className="font-medium text-sm cursor-pointer list-none flex items-start gap-2 -ml-4">
<span className="font-mono text-white/40 flex-shrink-0 group-open:hidden">+</span>
<span className="font-mono text-white/40 flex-shrink-0 hidden group-open:inline">−</span>
{question}
</summary>
<div className="text-sm text-muted-foreground space-y-2 mt-2 prose">{children}</div>
</details>
);
}
@@ -0,0 +1,126 @@
import { appStoreUrl, playStoreUrl, webAppUrl } from "~/downloads";
interface SiteFooterProps {
width?: "default" | "prose";
}
export function SiteFooter({ width = "default" }: SiteFooterProps) {
const widthClasses =
width === "prose" ? "max-w-prose p-6 md:p-12 md:pt-0" : "max-w-5xl p-6 md:p-20 md:pt-0";
return (
<footer className={`${widthClasses} mx-auto`}>
<div className="border-t border-white/10 pt-8 pb-4 grid grid-cols-2 sm:grid-cols-4 gap-8 text-sm">
<div className="space-y-3">
<p className="text-white/60 font-medium">Product</p>
<div className="space-y-2">
<a href="/blog" className="block text-white/40 hover:text-white/60 transition-colors">
Blog
</a>
<a href="/docs" className="block text-white/40 hover:text-white/60 transition-colors">
Docs
</a>
<a
href="/changelog"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Changelog
</a>
<a href="/cloud" className="block text-white/40 hover:text-white/60 transition-colors">
Cloud
</a>
<a
href="/docs/cli"
className="block text-white/40 hover:text-white/60 transition-colors"
>
CLI
</a>
<a
href="/privacy"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Privacy
</a>
</div>
</div>
<div className="space-y-3">
<p className="text-white/60 font-medium">Agents</p>
<div className="space-y-2">
<a
href="/claude-code"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Claude Code
</a>
<a href="/codex" className="block text-white/40 hover:text-white/60 transition-colors">
Codex
</a>
<a
href="/opencode"
className="block text-white/40 hover:text-white/60 transition-colors"
>
OpenCode
</a>
</div>
</div>
<div className="space-y-3">
<p className="text-white/60 font-medium">Community</p>
<div className="space-y-2">
<a
href="https://discord.gg/jz8T2uahpH"
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Discord
</a>
<a
href="https://github.com/getpaseo/paseo"
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
GitHub
</a>
</div>
</div>
<div className="space-y-3">
<p className="text-white/60 font-medium">Download</p>
<div className="space-y-2">
<a
href={appStoreUrl}
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
App Store
</a>
<a
href={playStoreUrl}
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Google Play
</a>
<a
href="https://github.com/getpaseo/paseo/releases"
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Desktop
</a>
<a
href={webAppUrl}
target="_blank"
rel="noopener noreferrer"
className="block text-white/40 hover:text-white/60 transition-colors"
>
Web App
</a>
</div>
</div>
</div>
</footer>
);
}
@@ -28,6 +28,12 @@ export function SiteHeader() {
>
Changelog
</a>
<a
href="/cloud"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Cloud
</a>
<a
href="/download"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
@@ -0,0 +1,24 @@
import type { ReactNode } from "react";
import { SiteFooter } from "~/components/site-footer";
import { SiteHeader } from "~/components/site-header";
interface SiteShellProps {
children: ReactNode;
width?: "default" | "prose";
}
export function SiteShell({ children, width = "default" }: SiteShellProps) {
const mainClasses =
width === "prose" ? "max-w-prose p-6 md:p-12 mx-auto" : "max-w-5xl p-6 md:p-20 mx-auto";
return (
<div className="min-h-screen bg-background">
<main className={mainClasses}>
<div className="mb-12">
<SiteHeader />
</div>
{children}
</main>
<SiteFooter width={width} />
</div>
);
}