889e8621b4
The billing portal is fully configured in Stripe (cancellations, plan switching, invoice history) and the Express endpoint (POST /api/stripe/portal) existed, but nothing in the UI linked to it. Added the Next proxy (app/api/stripe/portal) and a "Manage billing →" button in the profile billing section (paid tiers) that mints a portal session and redirects. Kev still activates the hosted portal in the Stripe dashboard; this is the app-side link. Dunning verification (item 6): cancel-on-exhaustion is correctly wired — Smart Retries exhausting cancels the subscription → customer.subscription.deleted → webhook sets a 48h grace → middleware/gracePeriod.checkGracePeriod downgrades tier to free in both users + user_profiles after the grace expires. See the report for one nuance (the 48h grace on the FIRST payment_failed is shorter than Stripe's 2-week retry window — self-correcting via subscription.updated, but worth a product decision). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>