page.tsx196 lines · main
1import type { Metadata } from 'next';
2import Link from 'next/link';
3
4import { BackgroundGrid } from '../../components/marketing/background-grid';
5import { SiteFooter } from '../../components/marketing/site-footer';
6import { SiteHeader } from '../../components/marketing/site-header';
7import { getSessionUser } from '../../lib/session';
8
9export const metadata: Metadata = {
10 title: 'projects — what we\'re building on briven',
11 description:
12 'the flndrn projects lined up to run on briven: web down, isy, mavi finans. honest dogfood — not external customers yet.',
13};
14
15interface Customer {
16 slug: string;
17 name: string;
18 tagline: string;
19 url: string;
20 category: string;
21 using: string[];
22 blurb: string;
23 metrics: { label: string; value: string }[];
24 status: 'live' | 'migrating' | 'planned';
25}
26
27const CUSTOMERS: Customer[] = [
28 {
29 slug: 'web-down',
30 name: 'web down',
31 tagline: 'website uptime monitoring & alerts',
32 url: 'https://web-down.com',
33 category: 'saas · monitoring',
34 using: ['schema', 'functions', 'scheduled', 'auth'],
35 blurb:
36 'web down watches websites and alerts the moment one goes down. its scheduled checks, time-series writes, and alert state map cleanly onto briven\'s postgres + functions + scheduled jobs.',
37 metrics: [],
38 status: 'planned',
39 },
40 {
41 slug: 'isy',
42 name: 'isy',
43 tagline: 'modern icq-style chat app',
44 url: 'https://isy.work',
45 category: 'saas · b2c messaging',
46 using: ['schema', 'functions', 'realtime', 'auth'],
47 blurb:
48 'isy is a modern take on the classic icq messenger — real-time chat with presence and message history. its realtime + auth + postgres needs map directly onto briven, making it an early build target on the platform.',
49 metrics: [],
50 status: 'planned',
51 },
52 {
53 slug: 'mavi',
54 name: 'mavi finans',
55 tagline: 'investing for first-generation europeans',
56 url: 'https://mavifinans.be',
57 category: 'fintech · b2c',
58 using: ['schema', 'functions', 'auth', 'audit log'],
59 blurb:
60 'mavi finans is the strict-compliance case: audit log on every mutation, project-scoped reads, secrets encrypted at rest, eu-only data residency. those needs are exactly why briven\'s auth + audit + encryption layer exists — which makes mavi a natural build target as briven matures.',
61 metrics: [],
62 status: 'planned',
63 },
64];
65
66export default async function CustomersPage() {
67 const user = await getSessionUser().catch(() => null);
68 return (
69 <main className="relative min-h-dvh overflow-hidden bg-[var(--color-bg)] text-[var(--color-text)]">
70 <BackgroundGrid />
71 <SiteHeader user={user} />
72
73 <section className="relative z-10 mx-auto w-full max-w-6xl px-6 pb-12 pt-16 sm:pt-24">
74 <p className="font-mono uppercase tracking-[0.12em] text-[var(--color-primary)] text-[var(--text-xs)]">
75 dogfood
76 </p>
77 <h1 className="mt-4 max-w-3xl font-sans font-medium leading-[1.05] tracking-[-0.03em] text-[var(--color-text)] text-[var(--text-display-3)] sm:text-[var(--text-display-2)]">
78 the projects we&apos;re
79 <br />
80 building on briven.
81 </h1>
82 <p className="mt-6 max-w-2xl leading-[1.6] text-[var(--color-text-muted)] text-[var(--text-body)]">
83 briven is built dogfood-first. these are flndrn&apos;s own projects — the ones lined up to
84 run on briven as the platform matures. honest about where each stands: build targets, not
85 external customers (yet).
86 </p>
87 </section>
88
89 <section className="relative z-10 mx-auto w-full max-w-6xl px-6 pb-16">
90 <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
91 {CUSTOMERS.map((c) => (
92 <CustomerCard key={c.slug} customer={c} />
93 ))}
94 </div>
95 </section>
96
97 <section className="relative z-10 mx-auto w-full max-w-6xl px-6 pb-20">
98 <div className="rounded-[var(--radius-lg)] border border-[var(--color-border-subtle)] bg-[var(--color-surface)] p-8 sm:p-10">
99 <h2 className="font-sans font-medium tracking-[-0.02em] text-[var(--color-text)] text-[var(--text-h2)]">
100 running briven in production?
101 </h2>
102 <p className="mt-3 max-w-2xl leading-[1.6] text-[var(--color-text-muted)] text-[var(--text-body)]">
103 we collect a short writeup per project we host — what you migrated from, what stuck out,
104 what we should fix. zero marketing pressure; the goal is the operator runbook getting
105 better every month. open a thread in the alpha discord or email the team.
106 </p>
107 <div className="mt-6 flex flex-wrap gap-3">
108 <Link
109 href="https://docs.briven.tech/support"
110 className="inline-flex h-11 items-center justify-center rounded-[var(--radius-md)] bg-[var(--color-primary)] px-5 font-sans font-medium text-[var(--color-text-inverse)] transition-colors hover:bg-[var(--color-primary-hover)]"
111 >
112 talk to the team
113 </Link>
114 <Link
115 href="https://docs.briven.tech/operator"
116 className="inline-flex h-11 items-center justify-center rounded-[var(--radius-md)] border border-[var(--color-border)] px-5 font-sans font-medium text-[var(--color-text)] hover:border-[var(--color-border-strong)]"
117 >
118 operator runbook
119 </Link>
120 </div>
121 </div>
122 </section>
123
124 <SiteFooter />
125 </main>
126 );
127}
128
129function CustomerCard({ customer }: { customer: Customer }) {
130 return (
131 <article className="flex flex-col gap-4 rounded-[var(--radius-lg)] border border-[var(--color-border-subtle)] bg-[var(--color-surface)] p-6">
132 <header className="flex items-start justify-between gap-3">
133 <div>
134 <Link
135 href={customer.url}
136 className="font-sans text-[var(--text-h3)] font-medium tracking-[-0.02em] text-[var(--color-text)] hover:text-[var(--color-text-link)]"
137 >
138 {customer.name}
139 </Link>
140 <p className="mt-1 font-mono text-xs text-[var(--color-text-muted)]">{customer.tagline}</p>
141 </div>
142 <StatusPill status={customer.status} />
143 </header>
144
145 <p className="font-mono text-[10px] uppercase tracking-wider text-[var(--color-text-subtle)]">
146 {customer.category}
147 </p>
148
149 <p className="leading-[1.6] text-[var(--color-text-muted)] text-[var(--text-small)]">
150 {customer.blurb}
151 </p>
152
153 {customer.metrics.length > 0 ? (
154 <dl className="grid grid-cols-1 gap-3 border-t border-[var(--color-border-subtle)] pt-4 sm:grid-cols-3">
155 {customer.metrics.map((m) => (
156 <div key={m.label} className="flex flex-col gap-0.5">
157 <dt className="font-mono text-[10px] uppercase tracking-wider text-[var(--color-text-subtle)]">
158 {m.label}
159 </dt>
160 <dd className="font-mono text-sm text-[var(--color-text)]">{m.value}</dd>
161 </div>
162 ))}
163 </dl>
164 ) : null}
165
166 <div className="flex flex-wrap gap-1.5 pt-1">
167 {customer.using.map((u) => (
168 <span
169 key={u}
170 className="rounded-[var(--radius-sm)] border border-[var(--color-border-subtle)] px-2 py-0.5 font-mono text-[10px] text-[var(--color-text-muted)]"
171 >
172 {u}
173 </span>
174 ))}
175 </div>
176 </article>
177 );
178}
179
180function StatusPill({ status }: { status: Customer['status'] }) {
181 const styles = {
182 live: 'border-[var(--color-border-primary)] bg-[var(--color-primary-subtle)] text-[var(--color-primary)]',
183 migrating:
184 'border-[var(--color-border-subtle)] bg-[var(--color-surface-raised)] text-[var(--color-text-muted)]',
185 planned:
186 'border-[var(--color-border-subtle)] bg-transparent text-[var(--color-text-subtle)]',
187 } as const;
188 const label = { live: 'live', migrating: 'migrating', planned: 'planned' } as const;
189 return (
190 <span
191 className={`rounded-[var(--radius-full)] border px-2 py-0.5 font-mono text-[10px] uppercase tracking-wider ${styles[status]}`}
192 >
193 {label[status]}
194 </span>
195 );
196}