import PullQuote from '../components/PullQuote.astro';

The game only had one way to share itself: the button at the end of a finished round, built to boast a score. There was no way to hand someone the game itself without playing first. A wayfinder map settled the shape of a second, more general entry point, and a footer button now opens it from the main menu.

## Deciding what "general" means

The map fixed the destination before any code was written: a text-only "Share" trigger in the footer's support-links row, between Ko-fi and Blog, opening a minimal modal with a title, a QR code, a copyable link, and one adaptive action button that calls the browser's native share sheet where available and falls back to a clipboard copy. Two questions needed their own tickets first. One decided the ref value the shared link carries for tracking, a fixed `menu-share` rather than a scheme that varies by language or entry point, exported alongside the existing `GAME_URL` constant. The other produced a rough prototype of the trigger's position and the modal's layout, reusing the PvP lobby's existing QR-code-and-copy-link cluster as a starting shape and the confirmation dialog's accessibility pattern, focus trap, `aria-modal`, Escape-to-close, for the shell, minus its two-button footer since this modal has nothing to confirm.

<PullQuote>A general invite is not a finished game's boast.</PullQuote>

What makes it a second surface rather than an extension of the first is scope. [The existing share button](/posts/endgame-and-the-share-card#one-action-at-a-time) always shares a specific result: today's score, a streak, a board state worth showing off. The new button shares nothing but the game itself, the bare homepage with a ref tag, and a generic bilingual invite line instead of score-flavored copy. Reusing that component would have meant threading a "no score" mode through code built around always having one, so the modal became its own self-contained component instead of an extension of `ShareButton.svelte`.

A two-axis review of the result found one real violation and one naming smell: a hardcoded `color: white` that should have been the design token for text on an accent background, and a `doAction` handler that said nothing about what it did until renamed to `shareOrCopyLink`. Two other flagged duplications, primary-button styling repeated instead of shared, and the modal shell echoing the confirmation dialog's, were left alone. `DESIGN.md` already documents no shared `<Button>` component as a deliberate consequence of how Svelte scopes component styles, so that duplication is the pattern, not an oversight.

## A dependency catch-up, delayed on arrival

A pair of routine dependency bumps landed first and failed every CI job at the install step, rejected by pnpm's own default minimum-release-age policy: packages published less than a day before the run get refused outright, no compatibility problem required. Five updates across two dependabot PRs, `@playwright/test`, `colorjs.io`, `svelte`, `svelte-check`, and a major jsdom bump, sat blocked until each package cleared the window, with no actual compatibility failure ever exercised. The fix was mechanical: wait it out and recreate the same five bumps as individual commits once each qualified.

The share button gives the game a second front door, one that does not require finishing anything first. Whether it gets used enough to justify a third remains to be seen.