diff --git a/web/src/globals.css b/web/src/globals.css
index 2349341..c770c15 100644
--- a/web/src/globals.css
+++ b/web/src/globals.css
@@ -369,6 +369,37 @@
CSS animations override inline styles, causing a visible position-flash.
Animate the inner content element instead (each component does this itself). */
+/* ── Force dark palette for landing page island ── */
+/* Apply class="force-dark" to a wrapper to lock dark CSS vars regardless of :root.light */
+.force-dark {
+ --background: 240 6% 6%;
+ --foreground: 240 5% 93%;
+ --card: 240 4% 9%;
+ --card-foreground: 240 5% 93%;
+ --popover: 240 5% 10%;
+ --popover-foreground: 240 5% 93%;
+ --muted: 240 4% 14%;
+ --muted-foreground: 240 4% 54%;
+ --primary: 240 5% 93%;
+ --primary-foreground: 240 6% 6%;
+ --secondary: 240 4% 13%;
+ --secondary-foreground: 240 5% 93%;
+ --accent: 240 4% 14%;
+ --accent-foreground: 240 5% 93%;
+ --destructive: 3 74% 61%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 240 4% 17%;
+ --input: 240 4% 11%;
+ --ring: 240 4% 42%;
+ --sidebar: 240 7% 7%;
+ --sidebar-foreground: 240 5% 93%;
+ --sidebar-border: 240 4% 13%;
+ --sidebar-accent: 240 4% 12%;
+ --sidebar-accent-foreground: 240 5% 93%;
+ --violet: 250 89% 70%;
+ color-scheme: dark;
+}
+
/* ── Sonner toasts ── */
[data-sonner-toaster] {
--toast-bg: hsl(var(--popover));
diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx
index e686049..96f0173 100644
--- a/web/src/routes/index.tsx
+++ b/web/src/routes/index.tsx
@@ -17,6 +17,11 @@ import {
Activity,
Terminal,
Sparkles,
+ Filter,
+ FileText,
+ Inbox,
+ ImagePlay,
+ LayoutDashboard,
} from "lucide-react";
import { LogoIcon } from "@/components/logo-icon";
@@ -39,113 +44,403 @@ function useGitHubStars() {
});
}
-function FeatureCard({
+// ── Sidebar nav pill ────────────────────────────────────────────────────────
+function SidebarItem({
icon: Icon,
- title,
- desc,
+ label,
+ active,
}: {
icon: React.ElementType;
- title: string;
- desc: string;
+ label: string;
+ active?: boolean;
}) {
return (
-
-
-
-
-
-
- {title}
-
-
{desc}
+
);
}
+// ── Rich dashboard mockup ───────────────────────────────────────────────────
function DashboardMockup() {
+ const stats = [
+ { v: "12,481", label: "Contacts" },
+ { v: "48,203", label: "Emails Sent" },
+ { v: "42.1%", label: "Open Rate" },
+ { v: "8.3%", label: "Click Rate" },
+ { v: "24", label: "Unsubscribes" },
+ ];
+
+ const events = [
+ { type: "open", user: "john@acme.com", time: "2m ago" },
+ { type: "click", user: "sarah@stripe.com", time: "4m ago" },
+ { type: "open", user: "mark@vercel.com", time: "7m ago" },
+ { type: "unsubscribe", user: "test@example.com", time: "12m ago" },
+ { type: "open", user: "amy@linear.app", time: "15m ago" },
+ { type: "click", user: "ben@figma.com", time: "19m ago" },
+ ];
+
+ const broadcasts = [
+ { name: "August Newsletter", sent: "48,203", openRate: "42.1%", status: "sent" },
+ { name: "Re-engagement Q3", sent: "8,412", openRate: "31.4%", status: "sent" },
+ { name: "Welcome Series", sent: "—", openRate: "—", status: "draft" },
+ ];
+
return (
-
- {/* Bottom glow */}
-
- {/* Outer glow ring */}
-
-
- {/* Window chrome */}
-
-
-
-
-
-
+
+ {/* Bottom ambient glow */}
+
+ {/* Top border highlight */}
+
+
+ {/* Browser chrome */}
+
+ {/* Window titlebar */}
+
+
{/* App layout */}
-
- {/* Sidebar */}
-
-
-
-
+
+
+ {/* ── Sidebar ── */}
+
+ {/* Logo + workspace */}
+
+
+ {/* Search */}
+
+
+ {/* Nav */}
+
+
+
+
+
+
+
+
+
+
+ {/* Spacer */}
+
+
+ {/* Bottom: user avatar placeholder */}
+
-
- {["Dashboard", "Contacts", "Broadcasts", "Campaigns", "Templates"].map(
- (item, i) => (
-
- )
- )}
- {/* Main content */}
-
-
-
-
+
+ {/* ── Main content ── */}
+
+ {/* Top bar */}
+
+
+ {/* Quick action chips */}
+
+ {["New Broadcast", "Add Contact"].map((label) => (
+
+ ))}
+
- {/* Stats row */}
-
- {[
- { v: "12,481", label: "Contacts" },
- { v: "48,203", label: "Sent" },
- { v: "42.1%", label: "Open Rate" },
- { v: "8.3%", label: "Clicks" },
- { v: "24", label: "Unsubs" },
- ].map(({ v, label }) => (
-
-
{label}
-
{v}
+
+
+ {/* Left panel: stats + quick actions + activity */}
+
+
+ {/* Stat cards */}
+
+ {stats.map(({ v, label }) => (
+
+
+ {label}
+
+
+ {v}
+
+
+ ))}
- ))}
-
- {/* Activity feed */}
-
-
-
-
-
-
+
+ {/* Quick action chips */}
+
+ {[
+ { icon: Mail, label: "New Broadcast" },
+ { icon: Users, label: "Add Contact" },
+ { icon: Filter, label: "New Segment" },
+ { icon: Zap, label: "Campaign" },
+ ].map(({ icon: Icon, label }) => (
+
+ ))}
+
+
+ {/* Activity feed */}
+
+ {/* Feed header */}
+
+
+ {/* Event rows */}
+ {events.map(({ type, user, time }, i) => (
+
+ {/* Event type dot */}
+
+ {/* Event label */}
+
+ {/* User */}
+
+ {/* Time */}
+
+
+ ))}
- {["Email opened", "Link clicked", "Email opened", "Unsubscribed", "Email opened"].map((ev, i) => (
-
-
-
-
+
+ {/* Right panel: recent broadcasts */}
+
+ {/* Panel header */}
+
- ))}
+
+ {/* Broadcast rows */}
+ {broadcasts.map(({ name, sent, openRate, status }, i) => (
+
+ {/* Name */}
+
+
+ {/* Status badge */}
+
+
+ {/* Metrics row */}
+
+ {/* Send progress bar (for sent broadcasts) */}
+ {status === "sent" && (
+
+ )}
+
+ ))}
+
+ {/* Segment count card */}
+
+
+
+ {[
+ { w: 28, c: "rgba(139,92,246,0.35)" },
+ { w: 20, c: "rgba(96,165,250,0.3)" },
+ { w: 24, c: "rgba(74,222,128,0.3)" },
+ ].map(({ w, c }, i) => (
+
+ ))}
+
+
+
@@ -154,27 +449,101 @@ function DashboardMockup() {
);
}
+// ── Feature card ─────────────────────────────────────────────────────────────
+function FeatureCard({
+ icon: Icon,
+ title,
+ desc,
+}: {
+ icon: React.ElementType;
+ title: string;
+ desc: string;
+}) {
+ return (
+
+
+
+
+
+
+ {title}
+
+
{desc}
+
+ );
+}
+
+// ── Main landing page ─────────────────────────────────────────────────────────
function LandingPage() {
const { data: stars } = useGitHubStars();
return (
-
-
+
{/* ── Ambient background glows ── */}
+ {/* ── Dot grid overlay ── */}
+
+
{/* ── Nav ── */}
-
+
-
{/* Logo */}
-
-
+
+
OpenMail
@@ -184,15 +553,13 @@ function LandingPage() {
{[
{ label: "Features", href: "#features" },
{ label: "AI Agents", href: "#ai" },
- { label: "Pricing", href: "#pricing" },
- { label: "Docs", href: "/docs" },
+ { label: "Pricing", href: "#pricing" },
+ { label: "Docs", href: "/docs" },
].map(({ label, href }) => (
{label}
@@ -205,9 +572,10 @@ function LandingPage() {
href={GITHUB_REPO}
target="_blank"
rel="noreferrer"
- className="hidden items-center gap-1.5 rounded-lg border border-white/[0.09] bg-white/[0.04] px-3 py-1.5 text-xs font-medium text-white/55 transition-colors duration-150 hover:bg-white/[0.08] hover:text-white/80 sm:flex tabular-nums"
+ className="hidden items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium text-white/45 transition-all duration-150 hover:text-white/75 sm:flex tabular-nums cursor-pointer"
+ style={{ border: "1px solid rgba(255,255,255,0.08)", background: "rgba(255,255,255,0.03)" }}
>
-
+
{stars != null ? stars.toLocaleString() : "Star"}
{/* ── Hero ── */}
-
- {/* Dot grid */}
-
-
+
{/* Badge */}
-
-
-
+
+
+
Open source · Free to self-host
{/* Headline */}
The open‑source{" "}
-
+
Lifecycle email marketing built for product teams. Automate onboarding,
retention, and re-engagement — without the enterprise price tag.
@@ -268,7 +635,8 @@ function LandingPage() {
Get started free
@@ -277,13 +645,34 @@ function LandingPage() {
href={GITHUB_REPO}
target="_blank"
rel="noreferrer"
- className="flex items-center gap-2 rounded-xl border border-white/[0.1] bg-white/[0.04] px-6 py-2.5 text-[13px] font-semibold text-white/60 transition-all duration-150 hover:bg-white/[0.08] hover:text-white/85"
+ className="flex items-center gap-2 rounded-xl px-6 py-2.5 text-[13px] font-semibold transition-all duration-150 cursor-pointer"
+ style={{
+ border: "1px solid rgba(255,255,255,0.09)",
+ background: "rgba(255,255,255,0.04)",
+ color: "rgba(255,255,255,0.55)",
+ }}
>
View on GitHub
+ {/* Trust signals */}
+
+ {[
+ { label: "ELv2 licensed" },
+ { label: "No per-seat fees" },
+ { label: "Self-hostable" },
+ ].map(({ label }) => (
+
+
+
+ {label}
+
+
+ ))}
+
+
{/* Dashboard mockup */}
@@ -291,41 +680,73 @@ function LandingPage() {
{/* ── Comparison ── */}
-
+
-
+
Why OpenMail
Everything Customer.io has. At a fraction of the cost.
-
-
-
Feature
-
OpenMail
-
Customer.io
+
+ {/* Header row */}
+
+
+ Feature
+
+
+ OpenMail
+
+
+ Customer.io
+
+
{[
- { feature: "Self-hosted option", us: true, them: false },
- { feature: "Full API access", us: true, them: "Limited" },
- { feature: "AI agent integration", us: true, them: false },
- { feature: "Real-time dashboards", us: true, them: false },
- { feature: "Per-seat pricing", us: "Never", them: "$1k–$10k+/mo" },
- { feature: "You own your data", us: true, them: false },
- { feature: "Open source", us: true, them: false },
+ { feature: "Self-hosted option", us: true, them: false },
+ { feature: "Full API access", us: true, them: "Limited" },
+ { feature: "AI agent integration", us: true, them: false },
+ { feature: "Real-time dashboards", us: true, them: false },
+ { feature: "Per-seat pricing", us: "Never", them: "$1k–$10k+/mo" },
+ { feature: "You own your data", us: true, them: false },
+ { feature: "Open source", us: true, them: false },
].map(({ feature, us, them }) => (
-
{feature}
+
+ {feature}
+
{typeof us === "boolean" ? (
-
+
) : (
@@ -335,12 +756,14 @@ function LandingPage() {
{typeof them === "boolean" ? (
them ? (
-
+
) : (
- —
+ —
)
) : (
- {them}
+
+ {them}
+
)}
@@ -350,37 +773,90 @@ function LandingPage() {
{/* ── Feature grid ── */}
-
-
-
+
+
+
Platform
Everything you need to run email at scale
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
{/* ── AI section ── */}
-
-
-
-
+
+
+
-
+
AI-Native
@@ -390,7 +866,7 @@ function LandingPage() {
>
Let your AI agents run email
-
+
OpenMail connects directly to Claude, GPT, Cursor, and any agent
that supports the Model Context Protocol. Your AI can create
campaigns, enroll contacts, send broadcasts, and pull
@@ -403,81 +879,103 @@ function LandingPage() {
"\"What's the open rate on our onboarding sequence?\"",
].map((q) => (
- ›
- {q}
+
+ ›
+
+
+ {q}
+
))}
-
+
- Connect in 30 seconds
+ Connect in 30 seconds
-
+
- {"{"}
+ {"{"}
{"\n "}
- "mcpServers"
- {": {"}
+ "mcpServers"
+ {": {"}
{"\n "}
- "openmail"
- {": {"}
+ "openmail"
+ {": {"}
{"\n "}
- "url"
- {": "}
- "https://mcp.openmail.win/mcp"
- {","}
+ "url"
+ {": "}
+ "https://mcp.openmail.win/mcp"
+ {","}
{"\n "}
- "headers"
- {": {"}
+ "headers"
+ {": {"}
{"\n "}
- "Authorization"
- {": "}
- "Bearer <your-api-key>"
+ "Authorization"
+ {": "}
+ "Bearer <your-api-key>"
{"\n "}
- {"}"}
+ {"}"}
{"\n "}
- {"}"}
+ {"}"}
{"\n "}
- {"}"}
+ {"}"}
{"\n"}
- {"}"}
+ {"}"}
-
+
Works with Claude Desktop, Cursor, and any MCP-compatible agent.
-
{/* ── Pricing ── */}
-
-
+
+
Pricing
Simple. Honest. No surprises.
-
+
No per-seat fees. No contact limits on self-hosted. No lock-in.
{/* Self-hosted */}
-
-
+
+
Self-hosted
-
Free
-
Forever. No credit card required.
+
+ Free
+
+
+ Forever. No credit card required.
+
{[
"Unlimited contacts",
@@ -486,7 +984,7 @@ function LandingPage() {
"Your infrastructure, your data",
"Community support",
].map((f) => (
-
+
{f}
@@ -496,7 +994,11 @@ function LandingPage() {
href={GITHUB_REPO}
target="_blank"
rel="noreferrer"
- className="flex items-center justify-center gap-2 rounded-xl border border-white/[0.09] py-2.5 text-[13px] font-semibold text-white/55 transition-all duration-150 hover:bg-white/[0.05] hover:text-white/80 cursor-pointer"
+ className="flex items-center justify-center gap-2 rounded-xl py-2.5 text-[13px] font-semibold transition-all duration-150 cursor-pointer"
+ style={{
+ border: "1px solid rgba(255,255,255,0.08)",
+ color: "rgba(255,255,255,0.5)",
+ }}
>
Clone on GitHub
@@ -504,12 +1006,28 @@ function LandingPage() {
{/* Enterprise */}
-
-
+
+
Enterprise
-
Custom
-
Fully managed, with an SLA.
+
+ Custom
+
+
+ Fully managed, with an SLA.
+
{[
"Managed cloud hosting",
@@ -518,7 +1036,7 @@ function LandingPage() {
"Dedicated onboarding",
"Priority support",
].map((f) => (
-
+
{f}
@@ -526,7 +1044,8 @@ function LandingPage() {
Talk to sales
@@ -537,30 +1056,48 @@ function LandingPage() {
{/* ── CTA ── */}
-
-
-
+
+
+
Own your email stack.
-
- Get started in minutes. No credit card, no vendor lock-in,
- no per-seat fees — ever.
+
+ Get started in minutes. No credit card, no vendor lock-in, no per-seat fees — ever.
Start for free
Talk to us
@@ -569,30 +1106,34 @@ function LandingPage() {
{/* ── Footer ── */}
-