+ );
+}
diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx
index f974339..1b7e6dc 100644
--- a/apps/web/app/page.tsx
+++ b/apps/web/app/page.tsx
@@ -6,27 +6,14 @@ import { db } from "@tasks/database/client";
import { workspaceMembers, workspaces } from "@tasks/database/schema";
/**
- * Root landing route. Decides where an authenticated user goes when they
- * hit `/`. The post-sign-in `callbackUrl` defaults here, so this is the
- * single source of truth for "where does a user actually land?"
- *
- * Decision: land on the user's oldest workspace home (`/{slug}/`). Why oldest:
- * we don't have a "last visited" column yet (deliberately deferred — see
- * Task-pick-workspace-landing-route) and oldest-membership is stable across
- * page loads. The workspace home is no longer a mockup; it now shows real
- * stats and recent activity via `objects.stats` / `objects.listRecent`.
- *
- * The auth callback (`ensureUserHasWorkspace` in `apps/web/lib/auth.ts`)
- * provisions a personal workspace on first sign-in, so the zero-workspace
- * branch below should be effectively unreachable for fresh sign-ins. It
- * survives only as a defensive fallback for sessions that were minted
- * before that provisioning logic existed.
+ * Root landing route. Unauthenticated visitors go to the public marketing site;
+ * signed-in users land on their oldest workspace home (`/{slug}/`).
*/
export default async function HomePage() {
const session = await auth();
if (!session?.user?.id) {
- redirect("/sign-in");
+ redirect("/landing");
}
const [membership] = await db
@@ -41,11 +28,5 @@ export default async function HomePage() {
redirect(`/${membership.slug}`);
}
- // Zero-workspace fallback. See header doc — this should be unreachable
- // post-`ensureUserHasWorkspace`. If we hit it anyway, bounce through
- // sign-in so the auth callback re-runs and provisions a workspace.
- // Track follow-up: a proper onboarding flow for users in this state
- // is captured in Plan-daily-driver-finish/Epic-shipping-the-shell/
- // Task-onboarding-zero-workspace-flow.md.
redirect("/sign-in?error=no_workspace");
}
diff --git a/apps/web/components/marketing/border-glow-card.tsx b/apps/web/components/marketing/border-glow-card.tsx
new file mode 100644
index 0000000..70bb3bc
--- /dev/null
+++ b/apps/web/components/marketing/border-glow-card.tsx
@@ -0,0 +1,22 @@
+import type { ReactNode } from "react";
+
+import { cn } from "@/lib/utils";
+
+type BorderGlowCardProps = {
+ children: ReactNode;
+ className?: string;
+ as?: "div" | "article" | "li";
+};
+
+export function BorderGlowCard({ children, className, as: Tag = "div" }: BorderGlowCardProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/web/components/marketing/comparison-section.tsx b/apps/web/components/marketing/comparison-section.tsx
new file mode 100644
index 0000000..f3f52b9
--- /dev/null
+++ b/apps/web/components/marketing/comparison-section.tsx
@@ -0,0 +1,78 @@
+import { Reveal } from "@/components/marketing/reveal";
+
+const rows = [
+ { label: "Markdown backlog in git", sbmtr: true, linear: false, notion: "partial", convoy: false },
+ { label: "MCP claim / complete", sbmtr: true, linear: false, notion: false, convoy: true },
+ { label: "Full workspace surface", sbmtr: true, linear: "partial", notion: true, convoy: false },
+ { label: "Role-based convoys", sbmtr: true, linear: false, notion: false, convoy: true },
+ { label: "Human approval gates", sbmtr: true, linear: false, notion: false, convoy: "partial" },
+] as const;
+
+function Cell({ value }: { value: boolean | string }) {
+ if (value === true) return yes;
+ if (value === false) return —;
+ return {value};
+}
+
+export function ComparisonSection() {
+ return (
+
+
+
+
Compare
+
+ The agent-native lane
+
+
+
+
+
+
+
+
+
+ Capability
+
+
+ SBMTR
+
+
+ Linear
+
+
+ Notion
+
+
+ Convoy tools
+
+
+
+
+ {rows.map((row) => (
+
+
{row.label}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/components/marketing/cta-section.tsx b/apps/web/components/marketing/cta-section.tsx
new file mode 100644
index 0000000..5407791
--- /dev/null
+++ b/apps/web/components/marketing/cta-section.tsx
@@ -0,0 +1,35 @@
+import Link from "next/link";
+import { ArrowRight } from "lucide-react";
+
+import { Reveal } from "@/components/marketing/reveal";
+
+export function CtaSection() {
+ return (
+
+
+
+
---
+
SBMTR
+
+ Subagents. Frontmatter. One workspace. Open Monday morning in Submatter — not a demo shell.
+
+ Fast implementers, premium architects — declared in convoy frontmatter and enforced by your
+ agent pipeline.
+
+
+
+
+ model_policy: composer-fast
+
+ tier: fast | premium
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/components/marketing/hero-section.tsx b/apps/web/components/marketing/hero-section.tsx
new file mode 100644
index 0000000..d6e5616
--- /dev/null
+++ b/apps/web/components/marketing/hero-section.tsx
@@ -0,0 +1,75 @@
+import Link from "next/link";
+import { ArrowRight } from "lucide-react";
+
+import { DotField } from "@/components/marketing/dot-field";
+import { ProductDemo } from "@/components/marketing/product-demo";
+import { Reveal } from "@/components/marketing/reveal";
+
+export function HeroSection() {
+ return (
+
+
+
+
+
+
+
+
+ Agent-native workspace
+
+
+
+
+
+ The matter beneath{" "}
+
+ SBMTR
+
+
+
+
+
+
+ Submatter syncs your plans/{" "}
+ backlog to a real workspace, then lets Cursor subagents claim tasks through MCP — with convoy gates
+ humans actually approve.
+
+
+
+
+
+
+ Start free
+
+
+
+ See the pipeline
+
+
+
+
+
+
+ subagents · frontmatter · one workspace
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/components/marketing/how-it-works-section.tsx b/apps/web/components/marketing/how-it-works-section.tsx
new file mode 100644
index 0000000..88a2ea2
--- /dev/null
+++ b/apps/web/components/marketing/how-it-works-section.tsx
@@ -0,0 +1,72 @@
+import { Reveal, Stagger, StaggerItem } from "@/components/marketing/reveal";
+
+const steps = [
+ {
+ n: "01",
+ title: "Write in plans/",
+ body: "YAML frontmatter on every Plan, Epic, and Task. Versioned in git like the rest of your codebase.",
+ code: "plans/Plan-*/Epic-*/Task-*.md",
+ },
+ {
+ n: "02",
+ title: "Sync to Submatter",
+ body: "Importer mirrors frontmatter into Postgres. Cursor mappings keep agent ids aligned with slugs.",
+ code: "watch:markdown-backlog",
+ },
+ {
+ n: "03",
+ title: "Agents claim work",
+ body: "Subagents call MCP tools against a scoped workspace_id. Runs appear in fleet history.",
+ code: "claim_task → complete_task",
+ },
+ {
+ n: "04",
+ title: "Humans gate convoys",
+ body: "Reviewer and architect roles pause for approval. Status flows back to markdown on merge.",
+ code: "ready → in_progress → done",
+ },
+] as const;
+
+export function HowItWorksSection() {
+ return (
+
+
+
+
+
+ How it works
+
+
+ From frontmatter to shipped
+
+
+
+ Interactive product demo pattern — show the file, show the pipeline, show the gate.
+