The invite card's terms dialog at desktop and at phone width — the one screen that still has to show the customer contract now that its page requires a session.
16 Sept 2026 · branch gera/terms-behind-login · captured against propflow-stage · at a02429c0d6+local
Data note. Every name and balance below comes from propflow-stage, which is anonymized by construction (ADR-0097/0110 — phones in the impossible +1000 NPA, emails @example.test). The identical capture against prod would carry real tenants' names and balances and is not safe to attach anywhere, which is why the generator refuses to run against one.
The real InviteClient from this branch, with the real renderCustomerTerms output passed down from a server component exactly as /invite/<token>/page.tsx passes it. Clicking Terms and Conditions in the assent sentence opens the contract in place instead of navigating to /legal/terms, which now needs a session this reader does not have yet.
This capture changed the code. The first run showed the document titled three times over: the dialog's Terms and Conditions, then an orphaned Version 2026-09-08, then the contract's own <h1>PropFlow Terms and Conditions</h1> — which lives inside the rendered html and is invisible in a diff. TermsDocument now takes showHeader rather than showHeading and drops the whole header block in a dialog, and the version moved into the modal's own description where it reads as chrome. One title, one version line, then the contract.
Asserted in the captured DOM — Terms and Conditions · Version 2026-09-08 · By checking this box, I acknowledge that I have read and agree to the PropFlow. The dialog title, the version line and the assent sentence are all in the DOM in the state the screenshot shows — i.e. after the click. If the link had navigated instead of opening the dialog, the version line would be missing and the run would abort.
The same state on the narrowest screen in the product. This card has a documented overflow incident at this width, and a multi-screen legal document inside max-h-[70vh] overflow-y-auto is exactly the unhappy state a diff cannot show: the question is whether the contract scrolls inside the dialog rather than pushing the dialog off the viewport, and whether the long unbroken strings in the document body wrap instead of forcing a horizontal scroll.
Asserted in the captured DOM — Version 2026-09-08 · By checking this box, I acknowledge that I have read and agree to the PropFlow. Same assertions at phone width — the contract is present and readable, not clipped out of the DOM by the modal's max-height.
/invite/<token> needs a live pending invite row, which the stage smoke account has no way to mint, so the capture mounted this branch's real InviteClient on an uncommitted fixture route inside the worktree — the same approach as pr-8565-evidence. The component and the terms rendering under the camera are exactly this branch's files; the fixture route is not part of the PR and does not exist on main.
What it proves: the client half — the link opens the contract in place instead of navigating, the document renders inside the dialog at both widths, and the dialog does not repeat its own title. What it does not prove: that the middleware actually turns an anonymous /legal/terms away — that half is pinned by src/__tests__/middleware.test.ts (307 to /login with from=/legal/terms, and the path absent from the sitemap) and by the signed-out leg of e2e/terms-acceptance-admin-invite.spec.ts.
/legal/terms itself is not captured: the TermsDocument extraction is markup-identical to the JSX it replaced, so that page is a refactor with no visual change.