From 73a5e6c63e2f7ead07e420039c53171886bc468b Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 30 Aug 2026 15:33:53 +0200 Subject: [PATCH] feat(website): expand homepage product story --- .../website/src/components/landing-page.tsx | 1219 ++++++----------- .../website/src/components/mockup/sidebar.tsx | 14 +- .../website/src/components/mockup/window.tsx | 1 + packages/website/src/routes/index.tsx | 8 +- packages/website/src/routes/sponsor.tsx | 4 +- packages/website/src/styles.css | 1 + 6 files changed, 460 insertions(+), 787 deletions(-) diff --git a/packages/website/src/components/landing-page.tsx b/packages/website/src/components/landing-page.tsx index 2c1c5094a6..a1fe6bca48 100644 --- a/packages/website/src/components/landing-page.tsx +++ b/packages/website/src/components/landing-page.tsx @@ -1,4 +1,19 @@ import * as React from "react"; +import { + ArrowRight, + Bot, + BookOpen, + Braces, + ExternalLink, + GitFork, + Laptop, + Monitor, + Puzzle, + Smartphone, + Terminal, + Users, + type LucideIcon, +} from "lucide-react"; import { motion, AnimatePresence, @@ -25,8 +40,10 @@ const EASE_OUT_015: Transition = { duration: 0.15, ease: "easeOut" }; const DURATION_05: Transition = { duration: 0.5 }; const VIEWPORT_60 = { once: true, margin: "-60px" }; +const AGENT_LIST_GRID_STYLE = { + gridTemplateColumns: "auto auto auto minmax(0, 1fr)", +}; -const SVG_OVERFLOW_VISIBLE_STYLE = { overflow: "visible" as const }; // A ~240px-wide phone rotated 15° only foreshortens a couple percent at // perspective 1200 — it reads as a flat, skewed card. The side phones already // sit on a correctly projecting plane (the frame and its scaled interior share @@ -63,7 +80,7 @@ import "~/styles.css"; interface LandingPageProps { title: React.ReactNode; - subtitle: string; + subtitle: React.ReactNode; } export function LandingPage({ title, subtitle }: LandingPageProps) { @@ -99,10 +116,9 @@ export function LandingPage({ title, subtitle }: LandingPageProps) {
- - - - + + +
@@ -121,11 +137,13 @@ function Nav() { ); } -function Hero({ title, subtitle }: { title: React.ReactNode; subtitle: string }) { +function Hero({ title, subtitle }: { title: React.ReactNode; subtitle: React.ReactNode }) { return (

{title}

-

{subtitle}

+

+ {subtitle} +

); } @@ -254,11 +272,13 @@ function FeatureSection({ title, description, badge, + links, children, }: { title: string; description: string; badge?: string; + links?: ReadonlyArray<{ href: string; label: string }>; children: React.ReactNode; }) { return ( @@ -268,7 +288,7 @@ function FeatureSection({ viewport={VIEWPORT_60} transition={EASE_OUT_05} > - + {children} ); @@ -278,10 +298,12 @@ function SectionTitle({ title, description, badge, + links, }: { title: string; description: string; badge?: string; + links?: ReadonlyArray<{ href: string; label: string }>; }) { return (
@@ -294,6 +316,22 @@ function SectionTitle({ )}

{description}

+ {links ? ( +
+ {links.map((link) => ( + + {link.label} + + + ))} +
+ ) : null} ); } @@ -308,7 +346,7 @@ function SocialProofWall() { >
@@ -388,7 +426,7 @@ function MultiProviderSection() { return (
{providers.map((p) => ( @@ -411,456 +449,398 @@ function MultiProviderSection() { ); } -function SelfHostedDiagram() { - const clients = [ - { - name: "Desktop", - icon: ( - - - - - ), - }, - { - name: "Web", - icon: ( - - - - - ), - }, - { - name: "Mobile", - icon: ( - - - - - ), - }, - { - name: "CLI", - icon: ( - - - - - ), - }, - ]; - const hosts = ["MacBook Pro", "Hetzner VM", "Dev server"]; - const containerRef = React.useRef(null); - const clientRefs = React.useRef<(HTMLDivElement | null)[]>([]); - const hostRefs = React.useRef<(HTMLDivElement | null)[]>([]); - const centerRef = React.useRef(null); - - const setClientRef = React.useCallback( - (index: number) => (el: HTMLDivElement | null) => { - clientRefs.current[index] = el; - }, - [], - ); - const setHostRef = React.useCallback( - (index: number) => (el: HTMLDivElement | null) => { - hostRefs.current[index] = el; - }, - [], - ); - const [paths, setPaths] = React.useState<{ left: string[]; right: string[] }>({ - left: [], - right: [], - }); - - React.useEffect(() => { - function computePaths() { - const container = containerRef.current; - const center = centerRef.current; - if (!container || !center) return; - - const cRect = container.getBoundingClientRect(); - const mRect = center.getBoundingClientRect(); - const midL = mRect.left - cRect.left; - const midR = mRect.right - cRect.left; - const midY = mRect.top - cRect.top + mRect.height / 2; - - const left = clientRefs.current.map((el) => { - if (!el) return ""; - const r = el.getBoundingClientRect(); - const x1 = r.right - cRect.left; - const y1 = r.top - cRect.top + r.height / 2; - const cpx = x1 + (midL - x1) * 0.6; - return `M${x1},${y1} C${cpx},${y1} ${midL - (midL - x1) * 0.3},${midY} ${midL},${midY}`; - }); - - const right = hostRefs.current.map((el) => { - if (!el) return ""; - const r = el.getBoundingClientRect(); - const x2 = r.left - cRect.left; - const y2 = r.top - cRect.top + r.height / 2; - const cpx = midR + (x2 - midR) * 0.4; - return `M${midR},${midY} C${cpx},${midY} ${x2 - (x2 - midR) * 0.3},${y2} ${x2},${y2}`; - }); - - setPaths({ left, right }); - } - - computePaths(); - window.addEventListener("resize", computePaths); - return () => window.removeEventListener("resize", computePaths); - }, []); - - return ( - <> - {/* Mobile: vertical stack */} -
-
- {clients.map((c) => ( -
- {c.icon} - {c.name} -
- ))} -
-
-
-

E2E Encrypted Relay

-

or

-

Direct Connection

-
-
-
- {hosts.map((h) => ( -
- - - - - - - - - {h} -
- ))} -
-
- - {/* Desktop: horizontal with bezier curves */} -
- {/* SVG curves */} - - {[...paths.left, ...paths.right].map( - (d) => - d && ( - - ), - )} - - - {/* Clients */} -
- {clients.map((c, i) => ( -
- {c.icon} - {c.name} -
- ))} -
- - {/* Spacer */} -
- - {/* Center label */} -
-

E2E Encrypted Relay

-

or

-

Direct Connection

-
- - {/* Spacer */} -
- - {/* Hosts */} -
- {hosts.map((h, i) => ( -
- - - - - - - - - {h} -
- ))} -
-
- - ); -} - -function SelfHostedSection() { +function TurnkeySection() { return ( - - - ); -} - -const WORKFLOW_STEPS = ["Worktree", "Preview", "Review", "Commit", "PR", "Merge"] as const; - -const REVIEW_FILES = [ - { path: "src/auth/session.ts", delta: "+42" }, - { path: "src/auth/middleware.ts", delta: "+18 -9" }, - { path: "tests/auth.test.ts", delta: "+31" }, -] as const; - -function WorkflowSection() { - return ( -
- -
- - +
+
+
+ +
+
+

Desktop app

+

+ The one click experience, download the app and it just works +

+
+
+
+ +
+
+ + + +
); } -function WorkflowHeader() { - return ( -
-
-
- fix-auth - worktree -
-
- {WORKFLOW_STEPS.map((step) => ( - - {step} - - ))} -
-
- ); -} - -function WorkflowPreview() { - return ( -
- -
- -
- - -
-
-
- ); -} - -function BrowserChrome() { - return ( -
-
- - - -
-
- web.fix-auth.my-app.localhost -
-
- ); -} - -function PreviewHeader() { - return ( -
-
-
-
- ); -} - -function PreviewFormCard({ - titleWidth, - ctaClassName, +function TurnkeyExtensionCard({ + icon: Icon, + title, + description, + ctaHref, + ctaLabel, + showIntegrationIcons = false, }: { - titleWidth: string; - ctaClassName: string; + icon: LucideIcon; + title: string; + description: string; + ctaHref: string; + ctaLabel: string; + showIntegrationIcons?: boolean; }) { return ( -
-
-
-
-
- ); -} - -function WorkflowReviewAndShip() { - return ( -
- - -
- ); -} - -function InlineReviewPanel() { - return ( -
-
- Inline review - 3 files changed +
+
+ + {showIntegrationIcons ? ( + <> + + + + + ) : null}
-
- {REVIEW_FILES.map((file) => ( - - ))} +

{title}

+

{description}

+
); } -function ReviewFileRow({ path, delta }: { path: string; delta: string }) { - return ( -
- {path} - - {delta.split(" ").map((part) => ( - - {part} - - ))} - -
- ); -} +type AutomationKind = "mcp" | "cli" | "sdk"; + +const AUTOMATION_OPTIONS: Array<{ + kind: AutomationKind; + label: string; + caption: string; + icon: LucideIcon; +}> = [ + { + kind: "mcp", + label: "MCP", + caption: "From another agent", + icon: Bot, + }, + { + kind: "cli", + label: "CLI", + caption: "From the terminal", + icon: Terminal, + }, + { + kind: "sdk", + label: "SDK", + caption: "From code", + icon: Braces, + }, +]; + +const AUTOMATION_LINKS = [ + { href: "/docs/mcp", label: "MCP docs" }, + { href: "/docs/cli", label: "CLI docs" }, + { href: "/docs/sdk", label: "SDK docs" }, +] as const; + +function AutomationSection() { + const [activeKind, setActiveKind] = React.useState("mcp"); -function ShipPanel() { return ( -
-
- Ready to ship - - checks passed - -
-
-
- Commit + +
+
+ {AUTOMATION_OPTIONS.map((option) => ( + + ))}
-
- Open PR -
-
- Merge + +
+ + ); +} + +function AutomationSelector({ + option, + active, + onSelect, +}: { + option: (typeof AUTOMATION_OPTIONS)[number]; + active: boolean; + onSelect: (kind: AutomationKind) => void; +}) { + const Icon = option.icon; + const handleClick = React.useCallback(() => onSelect(option.kind), [onSelect, option.kind]); + + return ( + + ); +} + +function AutomationDetail({ kind }: { kind: AutomationKind }) { + return ( +
+ {kind === "mcp" ? : null} + {kind === "cli" ? : null} + {kind === "sdk" ? : null} +
+ ); +} + +function McpAutomationTranscript() { + return ( +
+
+

+ Take the open GitHub issues labeled ready and fan them out to separate worktree agents. +

+
+
+ +
+

+ I found two ready issues. I will run each in its own worktree. +

+
+ + +
+

+ Done, two agents are running. I will let you know when they finish. +

); } +function McpAgentCall({ issue, provider }: { issue: string; provider: string }) { + return ( +
+ create_agent + {issue} + {provider} + worktree +
+ ); +} + +function CliAutomationExample() { + return ( +
+
+
+ + paseo run \ + +
+ --provider{" "} + codex/gpt-5.6-sol{" "} + \ +
+
{'"Fix issue #412 and add tests."'}
+
✓ Started agent a7f3c2
+
+ +
+ + paseo ls + + +
+ +
+
# Target another host
+ + paseo ls{" "} + --host{" "} + devbox:6767 + +
+
+
+ ); +} + +function ShellPrompt({ children }: { children: React.ReactNode }) { + return ( +
+ $ + {children} +
+ ); +} + +function AgentListOutput() { + return ( +
+ AGENT + STATUS + PROVIDER/MODEL + TITLE + a7f3c2 + running + codex/gpt-5.6-sol + Fix issue #412 and add tests. +
+ ); +} + +function SdkAutomationExample() { + return ( +
+      import {"{"} createPaseoClient {"}"}{" "}
+      from{" "}
+      {'"@getpaseo/client"'};{"\n\n"}
+      const client ={" "}
+      createPaseoClient({"{"}
+      {"\n"} url: {'"ws://127.0.0.1:6767/ws"'},{"\n"}
+      {"}"});
+      {"\n"}
+      await client.
+      connect();
+      {"\n\n"}
+      const agent ={" "}
+      await client.agents.
+      create({"{"}
+      {"\n"} config: {"{"} provider:{" "}
+      {'"codex/gpt-5.6-sol"'} {"}"},{"\n"} cwd:{" "}
+      {'"/Users/me/dev/paseo"'},{"\n"} prompt:{" "}
+      {'"Fix issue #412 and add tests."'},{"\n"}
+      {"}"});
+      {"\n\n"}
+      const result ={" "}
+      await agent.
+      waitForFinish();
+    
+ ); +} + +function ExtensibleSection() { + return ( + +
+ + +
+
+ ); +} + +function ExtensibleCard({ + icon: Icon, + title, + description, + href, + linkLabel, + linkIcon, + external = false, +}: { + icon: LucideIcon; + title: string; + description: string; + href: string; + linkLabel: string; + linkIcon?: "book" | "github"; + external?: boolean; +}) { + return ( + + ); +} + function GetStarted() { return (
@@ -943,221 +923,13 @@ function ServerInstallButton() { ); } -const bashKeywords = new Set([ - "while", - "do", - "done", - "if", - "then", - "fi", - "else", - "break", - "true", - "false", -]); -const bashCommands = new Set(["paseo", "echo", "jq"]); - -function tokenizeBashComment(code: string, i: number): { node: React.ReactNode; len: number } { - const end = code.indexOf("\n", i); - const comment = end === -1 ? code.slice(i) : code.slice(i, end); - return { - node: {comment}, - len: comment.length, - }; -} - -function tokenizeBashDoubleQuoted(code: string, i: number): { node: React.ReactNode; len: number } { - let j = i + 1; - while (j < code.length && code[j] !== '"') { - if (code[j] === "\\") j++; - j++; - } - const str = code.slice(i, j + 1); - return { node: {str}, len: str.length }; -} - -function tokenizeBashSingleQuoted(code: string, i: number): { node: React.ReactNode; len: number } { - let j = i + 1; - while (j < code.length && code[j] !== "'") j++; - const str = code.slice(i, j + 1); - return { node: {str}, len: str.length }; -} - -function tokenizeBashDollar(code: string, i: number): { node: React.ReactNode; len: number } { - if (code[i + 1] === "(") { - return { node: $(, len: 2 }; - } - let j = i + 1; - while (j < code.length && /\w/.test(code[j])) j++; - return { - node: {code.slice(i, j)}, - len: j - i, - }; -} - -function tokenizeBashFlag(code: string, i: number): { node: React.ReactNode; len: number } { - let j = i; - if (code[j + 1] === "-") j++; - j++; - while (j < code.length && /[\w-]/.test(code[j])) j++; - return { - node: {code.slice(i, j)}, - len: j - i, - }; -} - -function tokenizeBashWord(code: string, i: number): { node: React.ReactNode; len: number } { - let j = i; - while (j < code.length && /\w/.test(code[j])) j++; - const word = code.slice(i, j); - const len = j - i; - if (bashKeywords.has(word)) { - return { node: {word}, len }; - } - if (bashCommands.has(word)) { - return { node: {word}, len }; - } - return { node: word, len }; -} - -function isBashFlagStart(code: string, i: number): boolean { - return ( - code[i] === "-" && - (i === 0 || /\s/.test(code[i - 1])) && - i + 1 < code.length && - /[\w-]/.test(code[i + 1]) - ); -} - -function isBashCommentStart(code: string, i: number): boolean { - return code[i] === "#" && (i === 0 || /[\s(]/.test(code[i - 1])); -} - -function tokenizeBashChar(code: string, i: number): { node: React.ReactNode; len: number } { - const c = code[i]; - if (c === "|" || (c === "&" && code[i + 1] === "&")) { - const op = c === "|" ? "|" : "&&"; - return { node: {op}, len: op.length }; - } - if (c === "\\") return { node: \, len: 1 }; - if (c === ")") return { node: ), len: 1 }; - return { node: c, len: 1 }; -} - -function nextBashToken(code: string, i: number): { node: React.ReactNode; len: number } { - if (isBashCommentStart(code, i)) return tokenizeBashComment(code, i); - if (code[i] === '"') return tokenizeBashDoubleQuoted(code, i); - if (code[i] === "'") return tokenizeBashSingleQuoted(code, i); - if (code[i] === "$") return tokenizeBashDollar(code, i); - if (isBashFlagStart(code, i)) return tokenizeBashFlag(code, i); - if (/[a-zA-Z_]/.test(code[i])) return tokenizeBashWord(code, i); - return tokenizeBashChar(code, i); -} - -function highlightBash(code: string): React.ReactNode { - const tokens: React.ReactNode[] = []; - let i = 0; - let key = 0; - - while (i < code.length) { - const { node, len } = nextBashToken(code, i); - if (React.isValidElement(node)) { - tokens.push(React.cloneElement(node, { key: key++ })); - } else { - tokens.push(node); - } - i += len; - } - - return tokens; -} - -function CLICodeBlock({ children }: { children: string }) { - const [copied, setCopied] = React.useState(false); - - const handleCopy = React.useCallback(() => { - navigator.clipboard.writeText(children); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }, [children]); - - return ( -
- -
-        {highlightBash(children)}
-      
-
- ); -} - -interface CLIExample { - title: string; - description: string; - code: string; -} - -const cliExamples: CLIExample[] = [ - { - title: "Run agents", - description: - "Launch agents locally or on any remote host. The --worktree flag spins up an isolated git branch so you can run multiple agents on the same repo without conflicts.", - code: `paseo run "implement user authentication" -paseo run --provider codex --worktree feature-x "implement feature X" -paseo run --host devbox:6767 "run the full test suite" - -paseo ls # list running agents -paseo attach abc123 # stream live output -paseo send abc123 "also add tests" # follow-up task`, - }, - { - title: "Schedules", - description: - "Run agents on a cron schedule. Automate recurring tasks like dependency updates, security audits, or report generation.", - code: `# Run a security audit every Monday at 9am -paseo schedule create --cron "0 9 * * 1" \\ - "audit the codebase for security issues and open PRs for fixes" - -paseo schedule ls # list all schedules -paseo schedule pause abc123 # pause a schedule -paseo schedule delete abc123 # remove a schedule`, - }, -]; - function PhoneShowcase() { const containerRef = React.useRef(null); const textInView = useInView(containerRef, { once: true, margin: "-80px" }); @@ -1273,81 +1045,6 @@ function PhoneShowcase() { ); } -function CLITabButton({ - title, - index, - active, - onSelect, -}: { - title: string; - index: number; - active: boolean; - onSelect: (i: number) => void; -}) { - const handleClick = React.useCallback(() => onSelect(index), [onSelect, index]); - return ( - - ); -} - -function CLISection() { - const [activeIndex, setActiveIndex] = React.useState(0); - const active = cliExamples[activeIndex]; - - return ( - -
- {cliExamples.map((example, i) => ( - - ))} -
- -
- {active.code} -
- - - Full CLI reference - - - - -
- ); -} - function FAQ() { return ( FAQ
- Yes. Paseo is free and open source. You need Claude Code, Codex, Cursor, OpenCode, or Pi - installed with your own credentials. Voice is local-first by default and can optionally - use OpenAI speech providers if you configure them. + Yes. Paseo is free and open source. You need agent providers installed with your own + credentials. Voice is local-first by default and can optionally use cloud speech providers + if you configure them. Paseo doesn't send your code anywhere. Agents run locally and talk to their own APIs @@ -1373,16 +1070,20 @@ function FAQ() { , connect directly over your local network, or use your own tunnel. - Claude Code, Codex, Cursor, OpenCode, and Pi. Each agent runs as its own process using its - own CLI or local integration. Paseo doesn't modify or wrap their behavior. + Paseo supports many providers. It has custom implementations for Claude, Codex, OpenCode, + Pi, and OMP, and supports many more via ACP. See the full list here:{" "} + + all supported providers + + . + + + Paseo runs the providers installed on your machine as you'd normally run them. Paseo + doesn't modify or change their behavior. - No. You can run the daemon headless with{" "} - - npm install -g @getpaseo/cli && paseo - {" "} - and use the CLI, web app, or mobile app to connect. The desktop app just bundles the - daemon with a UI. + No. You can run the daemon headless and use any client to connect. The desktop app just + bundles the daemon with a UI. Voice runs locally on your device by default. You talk, the app transcribes and sends it @@ -1407,14 +1108,9 @@ function FAQ() { You can run agents anywhere you'd normally work. -

We can't make promises on behalf of providers.

-

- That said, Paseo launches each provider's local CLI or integration (Claude Code, - Codex, Cursor, OpenCode, Pi) as a subprocess. It doesn't extract tokens or call - inference APIs directly. From the provider's perspective, usage through Paseo is - indistinguishable from running the provider yourself. -

-

I've been using Paseo with all providers for months without issue.

+ Paseo is designed to use each provider's officially supported integration and does + not attempt to bypass its terms of service. It doesn't extract tokens or call + inference APIs directly.
When you launch an agent with the worktree option (from the app, desktop, or CLI), Paseo @@ -1430,40 +1126,6 @@ function FAQ() { ); } -const HUB_SURFACES = [ - { name: "GitHub", icon: }, - { name: "Slack", icon: }, - { name: "Discord", icon: }, -]; - -function HubSection() { - return ( - -
- {HUB_SURFACES.map((surface) => ( -
- {surface.icon} - {surface.name} -
- ))} -
- - Learn more - -
- ); -} - function SponsorCTA() { return (
+

Paseo is an independent open source project for running coding agents.

+

Its guiding principle is optionality and freedom of choice.

- Paseo is an independent open source project for running coding agents across your own - machines, phone, desktop, and CLI. -

-

- It's built around freedom of choice: use the provider you want, run it on your own - infrastructure, and keep your workflow portable. + I wanted to use any provider without being locked into any ecosystem, run it on my own + infrastructure, access it from anywhere, and have it be fully automatable.

+

I am hoping that you will enjoy Paseo as much as I do.

If you like Paseo, sponsorship is the best way to support continued development.

- Mo

diff --git a/packages/website/src/components/mockup/sidebar.tsx b/packages/website/src/components/mockup/sidebar.tsx index 2e15b2f110..a790ddfa07 100644 --- a/packages/website/src/components/mockup/sidebar.tsx +++ b/packages/website/src/components/mockup/sidebar.tsx @@ -25,6 +25,8 @@ import type { MockupStateId } from "./states"; const SIDEBAR_STYLE = { width: SIDEBAR_W }; const TITLEBAR_STYLE = { height: TITLEBAR_H }; +const NAV_DEFAULT_LAYOUT = { height: 97 }; +const NAV_EXTENDED_LAYOUT = { height: 127 }; // --- data ------------------------------------------------------------------- @@ -278,12 +280,18 @@ export function Sidebar({ state }: { state: MockupStateId }) {
-
+ - {extending ? : null} -
+ + + +
diff --git a/packages/website/src/components/mockup/window.tsx b/packages/website/src/components/mockup/window.tsx index 565d92e5ff..ae95b68867 100644 --- a/packages/website/src/components/mockup/window.tsx +++ b/packages/website/src/components/mockup/window.tsx @@ -16,6 +16,7 @@ const PANEL_OPEN = { width: RIGHT_PANEL_W + 1, opacity: 1 }; /** Which center pane a state shows. States sharing a key keep it mounted. */ function centerKey(state: MockupStateId): string { if (state === "review") return "review"; + if (state === "extend") return "extend"; return "chat"; } diff --git a/packages/website/src/routes/index.tsx b/packages/website/src/routes/index.tsx index 0f52a92857..38806673f9 100644 --- a/packages/website/src/routes/index.tsx +++ b/packages/website/src/routes/index.tsx @@ -22,7 +22,13 @@ function Home() { for coding agents } - subtitle="Run any coding agent from your phone, desktop, or terminal. Self-hosted, multi-provider, open source." + subtitle={ + <> + Run any coding agent from anywhere. +
+ Self-hosted, multi-provider, open source + + } /> ); } diff --git a/packages/website/src/routes/sponsor.tsx b/packages/website/src/routes/sponsor.tsx index f5affb9a6c..f4f7aaa943 100644 --- a/packages/website/src/routes/sponsor.tsx +++ b/packages/website/src/routes/sponsor.tsx @@ -34,9 +34,7 @@ function Sponsor() {

Paseo started as a personal project: I wanted to run coding agents from anywhere without - giving up control of my code, my keys, or my workflow. It grew into something bigger - because developers felt the same need: a single place to orchestrate agents across - devices, without vendor lock-in. + giving up control of my code, my keys, or my workflow.

diff --git a/packages/website/src/styles.css b/packages/website/src/styles.css index 664ee47982..64a7e52870 100644 --- a/packages/website/src/styles.css +++ b/packages/website/src/styles.css @@ -664,6 +664,7 @@ --color-foreground: #fafafa; --color-muted: #1a1f1e; --color-muted-foreground: #a8adac; + --color-extra-muted-foreground: #717574; --color-card: #171d1c; --color-border: #252b2a; --color-scrollbar-handle: #717574;