Four security/UX fixes from 2026-05-24 audit:
* lib/safeUrl.ts (new): validate external URLs, reject non-http(s) schemes
(closes finding #3 — backend supplies lot.source_url verbatim from scraped
sites; javascript:/data:/file: would have executed on click).
* lib/api.ts: refactor apiFetch to delegate to apiFetchWithStatus — single
read-body-once implementation, drops the body-stream-twice anti-pattern
(finding #8).
* components/trade-in/ListingsCard.tsx: drop tr.onClick (not keyboard
accessible), keep the inline <a> as the only interactive element, gate it
through safeUrl() (findings #7 + #3).
* app/error.tsx (new): Next 15 App Router route-segment error boundary —
prevents white-screen on component crash (finding #16).
Scope-cut from original audit:
* #17 (sessionId crypto.randomUUID) — already implemented in sessionId.ts;
no change needed.
* safeUrl wiring in DealsCard / IMVBenchmark — neither renders user-supplied
URLs (DealsCard has no links, IMVBenchmark links a hardcoded domain).