Intent: today the email blast is a plain-text box; an agent wants to see the email that lands in a buyer's inbox.
Done looks like:
[ ] GET /listings/:id/email renders the email asset as an email: subject line on top, hero photo, price and facts strip, the body, the agent's headshot, name, brokerage, license and contact in the brand colour, and the Equal Housing Opportunity line.
[ ] The Email tab links to it ("Preview email"), as the Flyer tab links to the flyer.
[ ] A Copy as email button puts the rendered HTML on the clipboard (text/html with a text/plain fallback) for pasting into the agent's mail tool; without JS the page still renders.
[ ] Every fact is escaped; the page keeps the security headers from src/http.ts; no remote images or fonts.
[ ] Tests: the page answers 200 for the sample, escapes a hostile address, 404s for an unknown id.
Files: src/pages/listing.ts (or a new src/pages/email.ts), src/routes.ts, static/listing.js, static/app.css, contract/openapi.json if routes are described there, README.md.
Risks: the CSP may need no change if the HTML is built in the page; do not loosen it for inline styles.
Brief: builder.
Intent: today the email blast is a plain-text box; an agent wants to see the email that lands in a buyer's inbox.
Done looks like:
GET /listings/:id/emailrenders the email asset as an email: subject line on top, hero photo, price and facts strip, the body, the agent's headshot, name, brokerage, license and contact in the brand colour, and the Equal Housing Opportunity line.text/htmlwith atext/plainfallback) for pasting into the agent's mail tool; without JS the page still renders.src/http.ts; no remote images or fonts.Files: src/pages/listing.ts (or a new src/pages/email.ts), src/routes.ts, static/listing.js, static/app.css, contract/openapi.json if routes are described there, README.md. Risks: the CSP may need no change if the HTML is built in the page; do not loosen it for inline styles. Brief: builder.