- Fix the real bug: the dialog clipped its bottom links (flex column + max-h
calc(100dvh-2rem), content min-h-0 flex-1 overflow-y-auto).
- One dominant action per state: 'New here' is a compact row, not a second hero.
- Plain card labels (Browser extension / Signer app / Sign in with your phone)
with tiny NIP-07/46/55 hints; protocol jargon out of the main labels.
- Bunker: input + Connect with instructions collapsed under 'Where do I get a
bunker?'; no duplicate heading/divider.
- Secret key moved to a quiet bottom link; subtitle says 'or keep browsing'.
- 8px spacing grid (px-5 gutters, space-y-4 sections, gap-2 cards, p-2.5 cards,
448px max width). New strings backfilled into all 20 locales.
Verified with headless 1280x900 + 390x844 screenshots.
The MLS private-chat panel gets an admin 'Invite' action that shares the crew's
private-chat link (…?tab=mls) via the OS share sheet or clipboard, landing the
invitee on the Ask-to-join flow. Added cordnUi.shareInvite/shareInviteText/
inviteCopied to en/es and backfilled them into all 20 locale drift fragments so
i18n stays at 100%.
245 keys had accumulated in en/es only (trustUi, labOptions, cordnUi, socialUi,
walletUi, marketTerms, …), leaving every other locale at 97%. Backfilled 245x20 =
4,900 strings into src/i18n/<lang>/drift.ts and merged them through a new shared
src/i18n/deepMerge.ts — a shallow ...drift spread would clobber existing
namespaces, so index.ts deep-merges instead. Verified: check-i18n all 20 at 100%,
tsc + eslint clean.