Skip to content

Commit 1f214dc

Browse files
authored
Merge pull request #20 from lladawn/dev
Lead with the agent-collaborator demo; add /demo and dark landing
2 parents 7d66168 + bae7625 commit 1f214dc

18 files changed

Lines changed: 2041 additions & 428 deletions

File tree

app/api/waitlist/route.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,20 @@ export async function POST(request) {
1010
if (!email) return badRequest("email is required");
1111
if (!isValidEmail(email)) return badRequest("drop in a real email and we'll save your spot.");
1212

13+
// optional research fields — the agent-collaborator landing asks for these,
14+
// the older slack landing posts email only.
15+
const company = cleanString(body.company, 160);
16+
const teamSize = cleanString(body.teamSize, 40);
17+
const agentTools = cleanString(body.agentTools, 400);
18+
const source = cleanString(body.source, 40) || "landing";
19+
1320
const supabase = getSupabaseAdmin();
1421
const { error } = await supabase.from("waitlist_signups").insert({
1522
email,
16-
source: "landing",
23+
source,
24+
company: company || null,
25+
team_size: teamSize || null,
26+
agent_tools: agentTools || null,
1727
});
1828

1929
if (error && error.code !== "23505") throw error;

app/layout.jsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import Script from "next/script";
2+
import { Press_Start_2P } from "next/font/google";
23
import AnalyticsTracker from "../src/components/AnalyticsTracker";
34
import AppShell from "../src/components/AppShell";
45
import "../styles.css";
56

7+
// bitmap display font for headings, labels, buttons, nav. body stays Inter.
8+
const pixelFont = Press_Start_2P({
9+
weight: "400",
10+
subsets: ["latin"],
11+
variable: "--font-display",
12+
display: "swap",
13+
});
14+
615
const appUrl = process.env.NEXT_PUBLIC_APP_URL || "https://mumbl.wtf";
716
const analyticsEnabled = process.env.NEXT_PUBLIC_ENABLE_ANALYTICS === "true";
817
const umamiScriptSrc = process.env.NEXT_PUBLIC_UMAMI_SRC || "https://breathe-umami.vercel.app/script.js";
@@ -72,7 +81,7 @@ export const viewport = {
7281

7382
export default function RootLayout({ children }) {
7483
return (
75-
<html lang="en">
84+
<html lang="en" className={pixelFont.variable}>
7685
<body>
7786
<AppShell>{children}</AppShell>
7887
{analyticsEnabled && umamiWebsiteId ? <AnalyticsTracker /> : null}

app/page.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import HomeView from "../src/components/HomeView";
1+
import AgentLandingView from "../src/components/AgentLandingView";
22

33
export default function HomePage() {
4-
return <HomeView />;
4+
return <AgentLandingView />;
55
}

app/sitemap.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,23 @@ export default function sitemap() {
1515
priority: 0.8,
1616
},
1717
{
18-
url: `${appUrl}/vision`,
18+
url: `${appUrl}/demo`,
1919
lastModified: new Date(),
20-
changeFrequency: "monthly",
20+
changeFrequency: "weekly",
21+
priority: 0.9,
22+
},
23+
{
24+
url: `${appUrl}/slack`,
25+
lastModified: new Date(),
26+
changeFrequency: "weekly",
2127
priority: 0.8,
2228
},
29+
{
30+
url: `${appUrl}/slack/vision`,
31+
lastModified: new Date(),
32+
changeFrequency: "monthly",
33+
priority: 0.7,
34+
},
2335
{
2436
url: `${appUrl}/privacy`,
2537
lastModified: new Date(),

app/slack/page.jsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import HomeView from "../../src/components/HomeView";
2+
3+
export const metadata = {
4+
title: "mumbl for slack",
5+
description: "Save the why behind your team's work — one line from Slack, published as team reads.",
6+
};
7+
8+
export default function SlackPage() {
9+
return <HomeView />;
10+
}

app/slack/vision/page.jsx

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import Link from "next/link";
2+
3+
export const metadata = {
4+
title: "vision",
5+
description:
6+
"Mumbl exists to surface the human layer of work: the reasoning, taste, and growth that never makes it into a ticket, and to give people back the realest data they have — how they actually think.",
7+
alternates: { canonical: "/slack/vision" },
8+
openGraph: {
9+
title: "mumbl vision",
10+
description: "The human layer of work — the thinking, taste, and growth that never makes it into a ticket. Mumbl surfaces it.",
11+
url: "/slack/vision",
12+
},
13+
twitter: {
14+
title: "mumbl vision",
15+
description: "The human layer of work — the thinking, taste, and growth that never makes it into a ticket. Mumbl surfaces it.",
16+
},
17+
};
18+
19+
// pixel save-slots: every tool saves the *output* of work. none save the layer.
20+
const slots = [
21+
{ id: "chat", label: "slack", saves: "messages" },
22+
{ id: "doc", label: "docs", saves: "decisions" },
23+
{ id: "ticket", label: "tickets", saves: "tasks" },
24+
{ id: "code", label: "code", saves: "the result" },
25+
];
26+
27+
const beliefs = [
28+
{ id: "data", line: "your thinking is real data — about you.", tone: "coral" },
29+
{ id: "one", line: "one honest sentence is enough.", tone: "mint" },
30+
{ id: "team", line: "the team gets the layer back.", tone: "violet" },
31+
];
32+
33+
function SlotSprite({ id }) {
34+
const art = {
35+
chat: (
36+
<>
37+
<rect x="2" y="3" width="12" height="8" fill="#cdd7e6" />
38+
<rect x="2" y="3" width="12" height="1" fill="#9aa7bd" />
39+
<rect x="5" y="11" width="3" height="2" fill="#cdd7e6" />
40+
<rect x="4" y="6" width="8" height="1" fill="#6d6760" />
41+
<rect x="4" y="8" width="5" height="1" fill="#6d6760" />
42+
</>
43+
),
44+
doc: (
45+
<>
46+
<rect x="3" y="2" width="10" height="12" fill="#eee7d8" />
47+
<rect x="3" y="2" width="10" height="1" fill="#c7bda6" />
48+
<rect x="5" y="5" width="6" height="1" fill="#6d6760" />
49+
<rect x="5" y="7" width="6" height="1" fill="#6d6760" />
50+
<rect x="5" y="9" width="4" height="1" fill="#6d6760" />
51+
</>
52+
),
53+
ticket: (
54+
<>
55+
<rect x="2" y="4" width="12" height="8" fill="#d7ead9" />
56+
<rect x="2" y="4" width="12" height="1" fill="#9cc0a2" />
57+
<rect x="5" y="7" width="2" height="2" fill="#2f6b45" />
58+
<rect x="8" y="7" width="4" height="1" fill="#6d6760" />
59+
<rect x="8" y="9" width="3" height="1" fill="#6d6760" />
60+
</>
61+
),
62+
code: (
63+
<>
64+
<rect x="2" y="3" width="12" height="10" fill="#e2dced" />
65+
<rect x="4" y="6" width="1" height="1" fill="#604398" />
66+
<rect x="3" y="7" width="1" height="1" fill="#604398" />
67+
<rect x="4" y="8" width="1" height="1" fill="#604398" />
68+
<rect x="11" y="6" width="1" height="1" fill="#604398" />
69+
<rect x="12" y="7" width="1" height="1" fill="#604398" />
70+
<rect x="11" y="8" width="1" height="1" fill="#604398" />
71+
<rect x="7" y="5" width="1" height="6" fill="#604398" />
72+
</>
73+
),
74+
};
75+
return (
76+
<svg viewBox="0 0 16 16" className="px-sprite" aria-hidden="true">{art[id]}</svg>
77+
);
78+
}
79+
80+
function HumanLayerSprite() {
81+
return (
82+
<svg viewBox="0 0 16 16" className="px-sprite" aria-hidden="true">
83+
<rect x="6" y="1" width="1" height="2" fill="#ffd25a" />
84+
<rect x="9" y="1" width="1" height="2" fill="#ffd25a" />
85+
<rect x="5" y="4" width="6" height="5" fill="#f6c14f" />
86+
<rect x="4" y="5" width="8" height="3" fill="#ffd873" />
87+
<rect x="7" y="4" width="2" height="6" fill="#e79a3a" />
88+
<rect x="6" y="10" width="4" height="1" fill="#c96a55" />
89+
<rect x="5" y="11" width="6" height="2" fill="#7a4a2b" />
90+
<rect x="3" y="6" width="1" height="1" fill="#fff3c4" />
91+
<rect x="12" y="6" width="1" height="1" fill="#fff3c4" />
92+
</svg>
93+
);
94+
}
95+
96+
export default function MissionPage() {
97+
return (
98+
<section className="mission-view pixel-screen">
99+
<header className="mv-hero">
100+
<p className="eyebrow">the vision</p>
101+
<h1>the human layer of work.</h1>
102+
<p className="mv-lede">
103+
Every team runs on a layer no tool captures — the reasoning, the taste, the gut calls, the way
104+
people actually grow. It lives in heads and DMs, and walks out the door when they do.
105+
</p>
106+
</header>
107+
108+
<section className="mv-slots" aria-label="what tools save">
109+
<div className="mv-slots-head">
110+
<span className="eyebrow">every tool saves the output</span>
111+
</div>
112+
<div className="mv-slot-row">
113+
{slots.map((slot) => (
114+
<div className="mv-slot" key={slot.id}>
115+
<span className="mv-slot-art"><SlotSprite id={slot.id} /></span>
116+
<strong>{slot.label}</strong>
117+
<small>{slot.saves}</small>
118+
</div>
119+
))}
120+
<div className="mv-slot mv-slot-mumbl">
121+
<span className="mv-slot-art"><HumanLayerSprite /></span>
122+
<strong>mumbl</strong>
123+
<small>how you think</small>
124+
</div>
125+
</div>
126+
<p className="mv-slot-caption">none of the others save the human layer. mumbl does.</p>
127+
</section>
128+
129+
<section className="mv-beliefs" aria-label="mumbl beliefs">
130+
{beliefs.map((belief, i) => (
131+
<article className={`mv-belief mv-${belief.tone}`} key={belief.id}>
132+
<span className="mv-belief-num">0{i + 1}</span>
133+
<h2>{belief.line}</h2>
134+
</article>
135+
))}
136+
</section>
137+
138+
<section className="mv-loop" aria-label="how the layer comes back">
139+
<span className="eyebrow">how it comes back</span>
140+
<div className="mv-loop-row">
141+
<span className="mv-loop-step">a rough line</span>
142+
<span className="mv-loop-arrow" aria-hidden="true"></span>
143+
<span className="mv-loop-step">stays yours</span>
144+
<span className="mv-loop-arrow" aria-hidden="true"></span>
145+
<span className="mv-loop-step">compounds</span>
146+
<span className="mv-loop-arrow" aria-hidden="true"></span>
147+
<span className="mv-loop-step mv-loop-end">the team sees how it thinks</span>
148+
</div>
149+
</section>
150+
151+
<section className="mv-closing">
152+
<h2>the people are the product.</h2>
153+
<p>Tools captured the output of work for decades. The thinking behind it finally has somewhere to go.</p>
154+
<Link className="solid-button button-link" href="/api/slack/install">
155+
add mumbl to slack
156+
</Link>
157+
</section>
158+
</section>
159+
);
160+
}

app/vision/page.jsx

Lines changed: 0 additions & 129 deletions
This file was deleted.

0 commit comments

Comments
 (0)