fix(web): dispatch #lobby/<id> to the join screen or the invite splash master
The actual fix: router.ts now recognizes "lobby" as a route (a direct "#lobby/<id>" hash, or a stash lobby-invite.ts left before an OAuth redirect) and renders lobbyScreen's join path for a signed-in visitor, lobbyInviteScreen for a signed-out one - bypassing the generic isAllowed() bounce-to-Home every other signed-in-only route gets, since this one wants a splash instead. A stash-restored address is replaceState'd back into the bar before rendering. shownLobbyId is new alongside the existing `shown`: "lobby" alone cannot tell two different lobbies apart, so onHashChange's own-address comparison would otherwise treat browser Back/Forward between two different "#lobby/<id>" entries as no change at all. markEntered (wired to nav.markShown) is what the previous commits' fresh-open and join paths were already calling; this is where it is finally read. This closes the loop the router-extension and lobbyScreen-split commits set up: clicking MATCHES from inside a lobby now actually changes the screen, because `shown` no longer gets stuck wherever start() last left it.