appview: persist newsletter signup/dismiss per-user for cross-device hiding master
the newsletter widget used only localStorage to remember whether a user had signed up or dismissed it, so the cta kept reappearing whenever a user opened tangled on another device or browser. for logged-in users, store the state in a newsletter_preferences table keyed on did with an enum status ('subscribed' | 'dismissed') and the email they gave us. the home and timeline handlers read this row to decide whether to render the widget, and the server-rendered gfi banner widens when the widget is gone so the grid doesn't leave an empty column. resend stays the source of truth for the mailing list itself (sending, bounces, one-click unsubscribes) — the new table only answers the render-time question 'should this did see the widget right now?', which resend cannot cheaply answer because it's keyed on email rather than did and would add a network hop to every timeline render. anonymous visitors keep the localStorage fallback. the client-side 'already dismissed in a past session' path deliberately only calls hide() (not dismiss()) so that a stale localStorage flag can't clobber a subscribed row set from another device. Signed-off-by: eti <eti@eti.tf>