The site you are reading: a public Next.js app, a separate admin panel, and a Supabase backend.
Two Next.js applications and one database. The public site renders everything you can read here. The admin panel is a separate project where courses, posts, projects, stories, publications and galleries are written and published. Both talk to the same Supabase project.
| Part | Job |
|---|---|
web | Public site. Server rendered, revalidated when something is published. |
admin | Writing and publishing. Markdown editor with live preview and a media library. |
supabase | PostgreSQL schema, row level security, storage for media. |
Visitors only ever read rows that are published and whose publish date has arrived, which is enforced in the database rather than in application code.
The home page is a single drafting sheet. Every construction on it exists twice: once as a blue pencil underdrawing and once as an ink pass. Scrolling moves a camera across the sheet, the blue arrives first, and the ink follows. At the end the camera pulls back and the whole sheet is visible at once.
Without JavaScript, or when reduced motion is requested, the same content is served as an ordinary page with every drawing already inked.
Content is written in Markdown with a few additions: math, code with titles and highlighted lines, callouts, collapsible blocks, and media files that turn into players.
export function readingMinutes(markdown: string): number {
const words = markdown.trim().split(/\s+/).filter(Boolean).length;
return Math.max(1, Math.ceil(words / 220));
}Last updated .