Skip to content
8 changes: 5 additions & 3 deletions content/whitepaper.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: The Builder's Operating System
subtitle: A technical architecture for sustained autonomous execution across many projects simultaneously
publishedAt: 2026-05-15
version: 0.1
publishedAt: 2026-08-12
version: 0.2
---

## The Execution Gap
Expand Down Expand Up @@ -135,7 +135,9 @@ FleetCrown makes this visible together so operators can reason about their actua

## Subscription Tiers

FleetCrown is offered as a hosted SaaS product with three subscription levels.
FleetCrown is offered as a hosted SaaS product with four subscription levels: a free tier and three paid tiers.

**Free** — for commanding your first projects. The full captain dashboard with your own runner and agent keys, limited in project count — enough to see the whole loop working before paying anything.

**Personal** — for solo builders managing up to 5 projects. Local runtime with cloud backup and remote access via the daemon bridge. Full project/agent/life OS features. Designed for the individual operator who wants to run the full system without self-hosting.

Expand Down
14 changes: 7 additions & 7 deletions src/app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import ThoughtsPage from "@/app/thoughts/page";
import { redirect } from "next/navigation";

export const metadata = {
title: "Blog",
description: "FleetCrown essays on agents, execution systems, and building in public.",
};

export default ThoughtsPage;
// /blog used to re-export the Thoughts page wholesale, serving identical
// content on two URLs under two different names. /thoughts is the canonical
// URL (it is what the page calls itself); this route only forwards.
export default function BlogRedirect() {
redirect("/thoughts");
}
14 changes: 7 additions & 7 deletions src/app/changelog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import ReleasesPage from "@/app/releases/page";
import { redirect } from "next/navigation";

export const metadata = {
title: "Changelog",
description: "Every FleetCrown and Fleet Runner release, newest first.",
};

export default ReleasesPage;
// /changelog used to re-export the Releases page wholesale, serving identical
// content on two URLs. /releases is the canonical URL; this route only
// forwards so old links and bookmarks keep working.
export default function ChangelogRedirect() {
redirect("/releases");
}
106 changes: 100 additions & 6 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,10 @@
--text-primary: oklch(0.92 0 0);
--text-secondary: oklch(0.65 0 0);
--text-tertiary: oklch(0.48 0 0);
--text-muted: oklch(0.34 0 0);
/* 0.55 (was 0.34) — 0.34 on the 0.07 page surface was ~2:1 contrast, an
unreadable WCAG failure for text that carries real copy (pricing
features, doc metadata). Keep "muted" muted, but keep it legible. */
--text-muted: oklch(0.55 0 0);
--text-inverted: oklch(0.07 0 0);
--surface-page: oklch(0.07 0 0);
--surface-base: oklch(0.10 0 0);
Expand Down Expand Up @@ -732,11 +735,13 @@
.ui-changelog-meta {
@apply flex flex-wrap items-center gap-x-3 gap-y-1;
}
/* white/60 (was /40) — dates and version numbers are primary scanning
targets on a changelog, not decoration; /40 failed WCAG AA on black. */
.ui-changelog-date {
@apply text-micro font-medium uppercase tracking-[0.16em] text-white/40;
@apply text-micro font-medium uppercase tracking-[0.16em] text-white/60;
}
.ui-changelog-version {
@apply font-mono text-micro uppercase text-white/40;
@apply font-mono text-micro uppercase text-white/60;
}
.ui-changelog-current {
@apply rounded-full border border-white/15 px-2 py-0.5 text-nano font-medium uppercase tracking-[0.12em] text-white/75;
Expand Down Expand Up @@ -775,6 +780,26 @@
.ui-changelog-code {
@apply font-mono text-white;
}
/* Compact version index under the changelog header. */
.ui-changelog-index {
@apply mt-10 flex flex-wrap gap-x-4 gap-y-2;
}
.ui-changelog-index-link {
@apply font-mono text-xs text-white/45 transition-colors hover:text-white;
}
/* Collapsed minor-line groups — everything below the current minor. */
.ui-changelog-archive {
@apply border-t border-white/[0.12] py-6;
}
.ui-changelog-archive-summary {
@apply flex cursor-pointer list-none flex-wrap items-baseline gap-x-4 gap-y-1 text-sm font-medium text-white/60 transition-colors hover:text-white;
}
.ui-changelog-archive-summary::-webkit-details-marker {
display: none;
}
.ui-changelog-archive-range {
@apply text-xs font-normal text-white/40;
}
.ui-public-release-section-heading {
@apply mb-2 mt-4 text-micro uppercase tracking-caps text-white/40;
}
Expand Down Expand Up @@ -832,14 +857,20 @@
.ui-public-footer-fleet {
@apply mb-8 border-t border-white/10 pt-8;
}
.ui-public-footer-fleet-summary {
@apply cursor-pointer list-none text-micro font-semibold uppercase tracking-caps text-white/40 transition-colors hover:text-white/70;
}
.ui-public-footer-fleet-summary::-webkit-details-marker {
display: none;
}
.ui-public-footer-fleet-grid {
@apply grid grid-cols-1 gap-x-8 gap-y-3 sm:grid-cols-2 lg:grid-cols-3;
}
.ui-public-footer-fleet-name {
@apply text-sm text-text-secondary;
}
.ui-public-footer-fleet-blurb {
@apply text-xs text-white/40;
@apply text-xs text-white/55;
}
.ui-public-prose-divider {
@apply mb-10 space-y-4 border-t border-white/10 pt-4;
Expand Down Expand Up @@ -929,11 +960,63 @@
letter-spacing: -0.03em;
}
.ui-public-doc-subtitle {
@apply text-lg leading-relaxed text-text-muted sm:text-xl;
/* text-secondary, not text-muted — the subtitle is the one-line pitch of
the whole document; it must not be the faintest text on the page. */
@apply text-lg leading-relaxed text-text-secondary sm:text-xl;
}
.ui-public-prose {
@apply space-y-8;
}
/* Anchored table of contents for long documents (whitepaper). */
.ui-public-doc-toc {
@apply mb-14 rounded-2xl border border-border-subtle bg-surface-base p-6;
}
.ui-public-doc-toc-title {
@apply mb-4 text-micro font-semibold uppercase tracking-caps text-text-tertiary;
}
.ui-public-doc-toc-list {
@apply grid gap-x-8 gap-y-2.5 sm:grid-cols-2;
}
.ui-public-doc-toc-link {
@apply flex items-baseline gap-2.5 text-sm text-text-secondary transition-colors hover:text-text-primary;
}
.ui-public-doc-toc-num {
@apply shrink-0 font-mono text-xs text-text-muted;
}
/* Collapsed archive sections on public index pages (thoughts, releases) —
native <details>, opens without JS, links stay crawlable. */
.ui-public-archive {
@apply rounded-2xl border border-border-subtle bg-surface-base px-5 py-4;
}
.ui-public-archive-summary {
@apply cursor-pointer list-none text-sm font-medium text-text-secondary transition-colors hover:text-text-primary;
}
.ui-public-archive-summary::-webkit-details-marker {
display: none;
}
.ui-public-archive-item {
@apply flex flex-col gap-0.5 rounded-lg px-2 py-1.5 text-sm transition-colors hover:bg-surface-raised;
}
.ui-public-archive-item-meta {
@apply text-xs text-text-tertiary;
}
/* Bare inline disclosure trigger — for collapsed detail bullets inside
prose sections (roadmap items). */
.ui-public-disclosure-summary {
@apply cursor-pointer list-none text-sm text-text-tertiary transition-colors hover:text-text-primary;
}
.ui-public-disclosure-summary::-webkit-details-marker {
display: none;
}
/* Long-document variant — the whitepaper is ~15 sections of continuous
prose; at the default text-2xl the section headings barely separate from
body copy. Scoped here so short thought posts keep the tighter scale. */
.ui-public-prose-doc .ui-public-prose-h2 {
@apply pt-10 text-3xl;
}
.ui-public-prose-doc .ui-public-prose-h3 {
@apply pt-4 text-xl;
}
.ui-public-prose-h2 {
@apply pt-6 text-2xl font-semibold tracking-tight text-text-primary;
letter-spacing: -0.02em;
Expand Down Expand Up @@ -1205,6 +1288,14 @@
min-height: 84vh;
}
}
/* Compact variant — statement pages (mission) pair the fold with body copy
directly below; the full 84vh fold forces ~700px of empty black between
statement and body. Declared after the base rules so it wins the cascade
at equal specificity. */
.ui-public-hero-fold-compact {
@apply pb-16 pt-24 sm:pb-20 sm:pt-32;
min-height: 0;
}
.ui-public-section-lede {
@apply max-w-2xl text-xl text-text-secondary;
}
Expand All @@ -1221,7 +1312,10 @@
@apply border-t border-border-default bg-surface-page;
}
.ui-public-surface-card {
@apply flex min-h-[320px] flex-col rounded-2xl border border-border-subtle bg-surface-base p-6 text-left;
/* min-h 200px (was 320px) — typical card content is ~120px tall, so 320px
painted ~200px of dead space per card; 200px keeps rows aligned without
the void. */
@apply flex min-h-[200px] flex-col rounded-2xl border border-border-subtle bg-surface-base p-6 text-left;
}
.ui-public-surface-card-label {
@apply mb-8 font-mono text-xs uppercase tracking-[0.18em] text-text-secondary;
Expand Down
50 changes: 39 additions & 11 deletions src/app/investors/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { PublicSurface } from "@/components/public/PublicSurface";
import { PublicHeaderActions } from "@/components/public/PublicHeaderActions";
import { FinalCta } from "@/components/public/FinalCta";
import { INVESTORS, INVESTOR_DETAILS } from "@/config/marketing-content";
import { getDefaultUser } from "@/db/queries/users";
import { getHeroFleetSnapshot, type HeroFleetSnapshot } from "@/db/queries/public-fleet";

export const metadata = {
title: "For Investors",
description: INVESTORS.thesis,
};

export default function InvestorsPage() {
export default async function InvestorsPage() {
// Same real data source as the homepage hero — the founder's actual fleet,
// public-safe fields only. Degrades to nothing rather than fake numbers.
const owner = await getDefaultUser().catch(() => null);
const fleet: HeroFleetSnapshot = owner
? await getHeroFleetSnapshot(owner.id).catch(() => ({ isLive: false, projects: [], metrics: [] }))
: { isLive: false, projects: [], metrics: [] };

return (
<PublicSurface right={<PublicHeaderActions />}>
<div className="mx-auto max-w-4xl px-6 py-24 sm:py-32">
Expand Down Expand Up @@ -39,7 +47,24 @@ export default function InvestorsPage() {
</div>
<div>
<div className="ui-public-eyebrow">TRACTION</div>
<p className="ui-public-body-lg mt-6">{INVESTORS.traction}</p>
{fleet.metrics.length > 0 && (
<div className="mt-6 grid grid-cols-3 gap-6">
{fleet.metrics.map((metric) => (
<div key={metric.label}>
<div className="ui-public-display-md">{metric.value}</div>
<div className="ui-public-meta mt-1">{metric.label}</div>
</div>
))}
</div>
)}
<ul className="mt-6 space-y-4">
{INVESTORS.traction.map((point, i) => (
<li key={i} className="ui-public-prose-li">
<span className="ui-public-prose-bullet" />
<span>{point}</span>
</li>
))}
</ul>
</div>
</div>
</div>
Expand All @@ -48,17 +73,20 @@ export default function InvestorsPage() {
<div className="mx-auto max-w-4xl px-6">
<div className="ui-public-eyebrow">THE ASK</div>
<p className="ui-public-section-lede mt-6">{INVESTORS.ask}</p>

<div className="mt-16 flex flex-col gap-2">
<a href={`mailto:${INVESTOR_DETAILS.contact}`} className="ui-public-prose-strong text-lg underline-offset-4 hover:underline">
{INVESTOR_DETAILS.contact}
</a>
<p className="ui-public-meta">{INVESTOR_DETAILS.deck}</p>
</div>
</div>
</div>

<FinalCta />
{/* Investor-specific closing CTA — a "Begin." sign-up pitch is the wrong
ask for this audience; the next step here is a conversation. */}
<div className="border-t border-border-subtle py-24 text-center">
<h2 className="ui-public-display-lg">Talk to the founder.</h2>
<p className="ui-public-meta mx-auto mt-6 max-w-md">Deck {INVESTOR_DETAILS.deck.toLowerCase()}.</p>
<div className="mt-10">
<a href={`mailto:${INVESTOR_DETAILS.contact}`} className="ui-public-cta-lg">
{INVESTOR_DETAILS.contact}
</a>
</div>
</div>
</PublicSurface>
);
}
2 changes: 1 addition & 1 deletion src/app/mission/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const metadata = {
export default function MissionPage() {
return (
<PublicSurface right={<PublicHeaderActions />}>
<div className="ui-public-hero-fold">
<div className="ui-public-hero-fold ui-public-hero-fold-compact">
<div className="w-full max-w-5xl">
<div className="ui-public-eyebrow">{MISSION.eyebrow}</div>
<h1 className="ui-public-hero-title mt-6">{MISSION.title}</h1>
Expand Down
62 changes: 33 additions & 29 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { getUserCount, getDefaultUser } from "@/db/queries/users";
import { getHeroFleetSnapshot, getShippedFromFeedbackSnapshot, type HeroFleetSnapshot, type ShippedFeedbackSnapshot } from "@/db/queries/public-fleet";
import { PublicSurface } from "@/components/public/PublicSurface";
import { PublicHeaderActions } from "@/components/public/PublicHeaderActions";
import { DesktopDownload } from "@/components/public/DesktopDownload";
import { PRODUCT_SURFACES, START_PATHS, HOME_HERO_CONSOLE } from "@/config/marketing-content";
import { PRODUCT_SURFACES, START_PATHS, HOME_HERO_CONSOLE, DESKTOP_DOWNLOAD } from "@/config/marketing-content";
import {
APP_NAME,
MARKETING_TAGLINE,
Expand Down Expand Up @@ -273,13 +272,43 @@ export default async function LandingPage() {
</div>
</div>

{!insideRunner && <DesktopDownload />}
{/* Short download band — the full DesktopDownload embed (platform picker,
comparison table, setup steps, prerequisites) duplicated /download
wholesale and made the homepage endless. Two lines and a link. */}
{!insideRunner && (
<div className="ui-public-band py-20 text-center">
<div className="mx-auto max-w-3xl px-6">
<div className="ui-public-eyebrow">DESKTOP APP</div>
<h2 className="ui-public-display-md mt-4">{DESKTOP_DOWNLOAD.title}</h2>
<p className="ui-public-section-lede mx-auto mt-6">{DESKTOP_DOWNLOAD.lede}</p>
<p className="ui-public-meta mt-4">
{DESKTOP_DOWNLOAD.platforms.filter((p) => p.status === "ready").map((p) => p.label).join(" · ")} today
{DESKTOP_DOWNLOAD.platforms.some((p) => p.status !== "ready") &&
` · ${DESKTOP_DOWNLOAD.platforms.filter((p) => p.status !== "ready").map((p) => p.label).join(" and ")} coming soon`}
</p>
<div className="mt-8">
<Link href="/download" className="ui-public-cta">
Download Fleet Runner
</Link>
</div>
</div>
</div>
)}

{/* One closing section — "Choose your entry point" and "Not another
coding agent" were two back-to-back closers making the same pitch;
the differentiation points also repeated the EXECUTION/CONTROL and
SURFACE sections above. Merged: one differentiating line, then the
three concrete entry paths. */}
<div className="py-24">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center">
<div className="ui-public-eyebrow">GET STARTED</div>
<h2 className="ui-public-display-lg mt-4">Choose your entry point.</h2>
<h2 className="ui-public-display-lg mt-4">Not another coding agent.</h2>
<p className="ui-public-section-lede mx-auto mt-4">
Most tools help you write code faster in one file or one project. FleetCrown is for
running real agent operations at fleet scale — choose your entry point.
</p>
</div>

<div className="mt-14 grid gap-4 md:grid-cols-3">
Expand All @@ -298,31 +327,6 @@ export default async function LandingPage() {
</div>
</div>

<div className="border-t border-border-subtle py-20">
<div className="mx-auto max-w-4xl px-6">
<div className="ui-public-eyebrow">THE DIFFERENCE</div>
<h2 className="ui-public-display-lg mt-4">Not another coding agent.</h2>
<p className="ui-public-section-lede mt-4">
Most tools help you write code faster in one file or one project. We help serious builders run and orchestrate real agent operations at fleet scale.
</p>

<div className="ui-public-body-lg mt-12 space-y-10">
<div>
<div className="ui-public-prose-strong">Local execution is the foundation</div>
<p className="ui-public-prose-muted mt-2">Your agents run on your machines with full access to your environment. We do not force everything through remote sandboxes.</p>
</div>
<div>
<div className="ui-public-prose-strong">Fleet orchestration, not single-agent assistance</div>
<p className="ui-public-prose-muted mt-2">Built for people already running many agents across many projects. Explicit per-project autonomy levels instead of one generic agent.</p>
</div>
<div>
<div className="ui-public-prose-strong">Local runner + web command center</div>
<p className="ui-public-prose-muted mt-2">The desktop application executes. The web portal gives you fleet visibility and remote control. Two surfaces, one system.</p>
</div>
</div>
</div>
</div>

<div className="border-t border-border-subtle py-20 text-center">
<Link href={signedIn ? ROUTES.APP_HOME : ROUTES.SIGN_UP} className="ui-public-cta-lg">
{signedIn ? `Open ${APP_NAME}` : "Begin"}
Expand Down
Loading
Loading