- Shipped
- September 7, 2026 at 5:06 AM UTC
- Author
- Kamo
- Commit
- f82dd80
Adds the Achievements surface: the trophy in the rail, /achievements, the launchpad leaderboard, and the unlock popup that arrives over whatever page a member happens to be on. THE POPUP IS PUSHED, NOT FOUND, and the queue is what makes that reliable. Two sources land in one order — the pending rows read on mount, and live frames off **************** The REST read is the one that MATTERS and the socket is the optimisation; the other way round, a member whose laptop was shut when they earned something would simply never be told. Cards are confirmed one at a time, oldest first, and merged by unlock id so the frame and the fetch describing the same row do not produce two identical cards. The host is mounted beside NotificationToastHost and INSIDE NotificationProvider. Outside it the hooks fail soft rather than throwing, and the symptom is indistinguishable from a member who has nothing to celebrate — the trap NewEmailNotifier's own comment records. "Awesome!" dismisses and stays put; "View My Achievements" dismisses and navigates. The big obvious control must never take a member off the page they were on. Escape confirms rather than hiding — there is no "close without reading" state, because the row is either acknowledged or it comes back. RARITY DECIDES HOW LOUD. Common shimmers; Legendary gets rays, a screen shake, two hundred pieces of hand-rolled canvas confetti and a longer hold. Without that, the fanfare stops meaning anything by the third one. Reduced motion gets the same content and none of the movement. The page is the one DARK surface in the product, because a cabinet has to make what is in it glow and a glow on white is a smudge. Every token is mixed from --color-primary so a white-label org gets its own neon — except rarity, which carries fixed hues for the reason the affiliate scoreboard hard-codes its podium: an org whose brand is amber must not render Common, Rare and Legendary as three shades of itself. The leaderboard widget is a close copy of the affiliate scoreboard — same 1x2 footprint, same podium, same bar measured against the leader, same self-line pinned below, same three-minute poll paused while hidden — because the two panes answer the same shape of question and a member should not learn two idioms for it. The row chrome is now literally the same code: RankBadge, MemberFace and CountUp move to components/common/BoardChrome rather than being copied. It drops the scoreboard's per-selector board cache, which exists for four metrics over four windows and has nothing to do here, and writes the score in full — the digits ARE the comparison, so two members forty points apart must not both read "2.4k". Also fixes ServiceType.ts, which was missing REPORTS entirely: the mirror parity guard reports an app Java offers that the client cannot see, and adding an app beside it would have left that failing.