A project board for a small design team: workspaces per collab, custom statuses, payments and mutual payouts in every row, one-link invites, live updates. Swiss typography as a tool. Next.js · Prisma · PostgreSQL · Auth.js — product, design and code entirely mine.
When several designers run projects together, the bookkeeping drifts: projects in notes, statuses in heads, money in a chat. Off-the-shelf task trackers solve the wrong problem: they're about tasks and sprints, not "how much has the client paid and how much do I owe my partner for this project".
"We needed a board for ten people where one screen shows three things: what's in progress, at what stage, and where the money is."

A demo board with placeholder projects, rendered with the app's real styles — actual workspaces live behind Google sign-in.
Workspaces instead of one shared list. Each collab is its own workspace with its projects, statuses, currency and members. Switching is a chip in the header. One person can be in several collabs at once.
Statuses for your own process. Not "To do / Done" but the stages the team actually has — with names and colours. Deleting a status doesn't delete projects: the status is simply cleared. A small decision that removes the fear of breaking something.
Money as part of the project, not a report on the side. Every row has total, paid, remaining debt and a progress bar. Above the board — a "received / expected" summary. Nothing to compute in a calculator.
Member payouts. Recurring shares (say, a fixed cut to a partner from every project) are set at workspace level and ticked off per project. Below the stats — a compact per-payee debt summary. This is the part no tracker has, and exactly why money gets lost in teams.
Invite by one link. Copy the link in the "Team" tab — the person opens it, signs in with Google and is in. No admin panels, requests or approvals.
A live board. Data is refetched every 5 seconds: colleagues' edits appear almost immediately, no reload needed. A green pulsing dot in the header shows the sync happened.

Swiss typography taken not as a style but as a tool: a table of money must be read, not admired.
#F2F1EC with a barely visible dot grid — the screen stops shining in your face over long sessions.#E2231A is only for debt and attention; green for "closed". There's no other colour in the interface — status colours are set by the team.font-variant-numeric: tabular-nums throughout — amounts in a column line up by digit.cubic-bezier(.22,.61,.36,1): rows rise in, on hover a row shifts and gets a red notch on the left.:root{ --paper:#F2F1EC; --ink:#111110; --red:#E2231A; --green:#1F9E4D;
--hair:rgba(17,17,16,.12); --ease:cubic-bezier(.22,.61,.36,1); }
body{ font-feature-settings:"tnum" 1,"kern" 1; }
.prow{ display:grid; grid-template-columns:42px 1fr 150px 220px 110px 36px;
border-bottom:1px solid var(--hair); animation:rise .55s var(--ease) both; }
.prow:hover{ background:var(--white); padding-left:12px; box-shadow:inset 3px 0 0 var(--red); }

requireMember / requireProjectAccess: access is verified on the server, not in the UI. Someone else's workspace can't be seen or changed even knowing its id./ad page — a separate route without auth, with OG metadata for messenger previews and the native share sheet on a phone.// actions.ts — access is checked on the server in every mutation
async function requireMember(workspaceId: string) {
const userId = await getUserId();
if (!userId) throw new Error("Not signed in");
const m = await prisma.membership.findUnique({ where: { userId_workspaceId: { userId, workspaceId } } });
if (!m) throw new Error("No access to this workspace");
return { userId, role: m.role };
}
async function requireProjectAccess(projectId: string) {
const project = await prisma.project.findUnique({ where: { id: projectId }, select: { workspaceId: true } });
if (!project) throw new Error("Project not found");
await requireMember(project.workspaceId);
return project.workspaceId;
}

Next.js 14 (App Router) · TypeScript · Prisma · PostgreSQL · Auth.js (Google) · Vercel + Railway
A project board for a small design team: custom statuses, payment and mutual-payout tracking, one-link invites, live updates. Product, design and code — all mine.