Case study
A Brasileirão Série A survival pool: each player has five lives and every round must back one club not to lose — no team can be picked twice, and the winner is whoever still has lives when the current turn ends.
Survivor follows a last-player-standing rule set on the Brazilian Championship (Série A). Everyone starts with five lives and the full list of top-flight clubs; each round you stake one team that must not lose. If your pick loses, you drop a life. You can never reuse a club you already played, you can schedule future picks so you do not miss a round, and when you are out of lives you are marked eliminated.
Role — Solo developer: product, implementation, and deployment. Design is minimal and functional—focused on clarity of rules and fast iteration.
Technologies — Next.js, TypeScript, TRPC, Supabase

Built as a personal project to ship a full-stack app with real users (friends) and a clear game loop.
The outcome was a deployable game with fair rules and a single source of truth for scores—something people could actually play each round without ambiguity.
Initial design was a bracket-first model: define entities (league, round, pick), then add UX polish once flows worked.
Survivor stands or falls on the round engine: you lock a pick, results land, lives update, and the leaderboard has to stay fair when several people act at once. The fun part is also the hard part—fixtures can feel out of order and players can join mid-season, so ambiguity in state would show up as wrong scores. I made rounds explicit on the server, kept score updates idempotent, and let TRPC procedures own the truth while the client stays thin and fast—so the game feels simple even when the timing is not.