LOBBY.
The
Manifesto.
Lobby is dressed as a sports app. It is not. It is civic infrastructure — a way for communities already in motion to direct the proceeds of that motion, together.
The rest is democracy.
The feed is matches, not people.
No follows, likes, or profile scrolling. Every byte of attention is aimed at the question: are you playing tonight? Social features are toxic in sports apps; we refuse them on purpose.
Skill is honest, not optional.
Self-reported skill bootstraps Elo; Elo makes every future match fair. Honesty is rewarded because dishonest ratings break matchmaking fast and visibly.
Distance beats prestige.
A great game 400 m away is ranked above a "featured" one across the city. Lobby favors people who can actually show up, in time, to play.
Local honour is the primary currency.
Neighbourhood leaderboards. District championships. Best-in-your-area isn't a badge — it's a public role. Before money flows, reputation does.
The community votes where money goes.
When Lobby generates revenue, the players who generated it propose and vote on how it's spent — kitchens, schools, pitches, coaching programs. No central committee picks.
Football is the first sport. Not the only one.
Everything is architected so basketball, padel, and futsal drop in without rewriting the governance layer. Sport is the shell. The civic logic is the product.
Trust is the substrate.
Phone-verified accounts (BankID when we're ready). One person, one vote. We'd rather have 247 real neighbours than 24,700 anonymous accounts.
We build slowly, for keeps.
We are not a growth-hacked startup. Lobby is built to last decades, to be boringly reliable, and to earn its users' trust through physical consistency — the match happens on time, every time.
The Moat.
What stops a bigger player from copying Lobby? Three things, in order of strength. They compound — each one makes the next harder to dislodge.
Rating integrity.
An Elo system is easy to write and impossibly hard to seed. After a hundred matches in a district, our skill ratings are accurate in a way no newcomer's are. Every match widens the gap. A new competitor starts cold; we start with a year of signal.
Local ritual density.
Pickup football is a habit, not a transaction. Once Tuesday 19:30 at Frognerparken runs through Lobby for six weeks, it is Lobby. Migrating a ritual is exponentially harder than switching apps, because you'd have to migrate every player at once. Rituals are the network effect, not user counts.
Civic gravity.
The moment Lobby routes a kroner to a real soup kitchen a real player voted on, we stop being an app. We become a thing the community owns. Governments partner with that. Sponsors pay premiums to it. Copycats cannot fake it. Civic legitimacy is the deepest moat there is — and the slowest to build.
Trade secrets.
Share only with people on the access list. These are the mechanics outside competitors can't guess from the product alone — the levers that turn Lobby from "an app we made" into "a system that keeps compounding."
A competitor can copy the app in a month. They cannot copy the Tuesday 19:30 habit of 10 guys in Frogner. That's the whole game.
Design
system.
The aesthetic is fanzine crossed with constitution: paper, ink, blood, grass. Loud when it matters, quiet when it doesn't. Never generic. Every surface — web, iOS, Android — shares these tokens.
01Colour
Rule of thirds for accent use: Ink does structure. Paper tones do the surface. Blood is a scalpel — use it for exactly one thing per screen: the action, the warning, the headline. If two things are red, neither is.
02Typography
400 / condensed
Frognerparken 5v5.
italic
400 / 500 / 700
HOST: Bror Neby H. · KICKOFF 21:10
Bebas does the shouting. Instrument is the human voice in the margins. JetBrains is the data. Never use two display fonts together. Never use monospace for headlines. No exceptions.
03Motion & texture
One looping grain overlay on every surface (radial-gradient dots at 3px, 5% opacity, multiply blend). Animations exist only where they're load-bearing: a pulsing dot for "live," a slow drift behind the solo-queue card, a countdown arc on the timer. No micro-interactions on static elements. If a button doesn't do something, it doesn't shake.
Phone mockups are rotated by 0.3–0.8 degrees, never more. The typewriter-slight-crookedness is the whole point: Lobby is hand-assembled, not stamped.
04Cross-platform tokens
| Token | Web (CSS var) | iOS (Swift) | Android (Kotlin) |
|---|---|---|---|
| Paper | --paper | LobbyColor.paper | LobbyColors.Paper |
| Ink | --ink | LobbyColor.ink | LobbyColors.Ink |
| Blood | --blood | LobbyColor.blood | LobbyColors.Blood |
| Display font | 'Bebas Neue' | LobbyFont.display | BebasNeue |
| Body font | 'JetBrains Mono' | LobbyFont.mono | JetBrainsMono |
How it's built.
Three surfaces, one spine. The Rust backend owns all logic. The native clients are dumb renderers — on purpose — so business rules never drift between platforms.
┌────────────────────┐
│ CLIENTS (renderers) │
└────────────────────┘
┌─────────────────────┼─────────────────────┐
│ │ │
iOS (Swift) Android (Kotlin) Web (prototype)
SwiftUI + Combine Compose + Ktor HTML / CSS / JS
│ │ │
└─────────────┬───────┴─────────────────────┘
│ JSON · HTTPS · WebSocket
▼
┌───────────────────────┐
│ RUST BACKEND (axum) │
├───────────────────────┤
│ domain/ │ ← pure data (Player, Match, Elo)
│ matchmaking/ │ ← pure fns (rank, form teams)
│ governance/ (v0.2) │ ← proposals, votes, treasury
│ api/ (HTTP) │
│ realtime/ (WS) │
└───────────────────────┘
│
▼
┌───────────────────────┐
│ PostgreSQL + PostGIS │
│ (ST_DWithin magic) │
└───────────────────────┘
aThe pure-logic rule
domain/ and matchmaking/ in the Rust backend have zero framework dependencies. They can be unit-tested without a database, compiled to WebAssembly if we want an offline-first mode, and swapped between HTTP frameworks without touching business logic. This is not optional. Any PR adding axum/sqlx types to these modules gets bounced.
bClients speak JSON. That's it.
The iOS and Android apps mirror the same domain types, generated by hand for now (small surface). When domain/ grows past ~30 types, we auto-generate Swift and Kotlin bindings from the Rust types with uniffi-rs or a custom script. Not before. Premature tooling is the thief of shipping.
cGovernance is a first-class module
The voting system isn't bolted on — it's a governance/ module with the same architectural weight as matchmaking. Proposals, votes, vote-weighting (we'll want "match-hours played in last 30 days" as a weight multiplier), treasury allocation, audit log. All of it pure logic, all of it testable, all of it one HTTP endpoint away from both apps.
Roadmap.
Four phases. Each one earns the right to the next. We do not skip.
- Rust domain + matchmaking
- REST API + PostGIS schema
- iOS SwiftUI app
- Android Compose app
- Web prototype
- Internal platform (this)
- BankID / Vipps login
- WebSocket live roster
- Post-match rating → Elo
- Host-a-match flow
- 20–50 real players, Oslo
- Weekly standing-slot invites
- Per-district leaderboards
- Proposal creation in-app
- Treasury activation
- First cause partnership
- Vipps for split-pay pitches
- Second sport (basketball or padel)
- Regional championships
- Municipality partnerships
- Export to second city
- Sport-agnostic core hardening
Vote.
This is the working prototype of the civic layer. Same mechanics as what players will eventually see in-app. Your vote is recorded against your verified phone number — one person, one vote.
Treasury.
The pot is collectively owned, collectively spent. Activates when Lobby starts generating real revenue in Phase 02. These allocations are from the last closed vote.
kr 85,000 from first-year pitch-rental split-pays and municipality grants. See Proposal #003 for how the community has provisionally allocated it.
§Treasury principles
1. No hidden balance. The pot is public down to the øre. The backend exposes /treasury/ledger as an append-only log.
2. No discretionary spending. Not by Sivert, not by any founder, not by any employee. Every kroner leaves the pot because a vote passed.
3. Operational reserve is capped at 15%. If we need more, we propose it and the community votes. This keeps us honest.
4. Causes must be verifiable. Every recipient has a Norwegian organisasjonsnummer or a verified community ledger entry. We do not route to individuals.
Access.
Five roles, ranging from view-only to root. Trust is granular — a new engineer doesn't get Treasury approve rights on day one. Phone number is the identity anchor.
aRole matrix
| Role | Can | Cannot |
|---|---|---|
| root | Everything. Deploy prod, invite anyone, spend emergency reserve. | (Nothing. Use carefully.) |
| admin | Invite new internal members, moderate proposals, view all metrics. | Deploy prod, spend reserve. |
| engineer | Push to staging, read all code, open PRs. | Merge prod, access treasury data. |
| viewer | Read this platform. Read product metrics. | Write anything. |
| player | Use the app. Vote. Propose. | See internal platform. |
bInvite flow
Adding a new internal member: admin or root adds the phone number to the allow-list. That person receives SMS (later: BankID prompt), picks a role, signs. Their first action is logged. Removal is immediate and invalidates all sessions.