The problem
A document assistant is only worth having if you can check it. Most of them answer in a tidy paragraph under a footnote that names a file, which is about as useful as no footnote. The thing you want is the sentence it read. The thing you get is a filename.
What we built
A PDF beside a chat. The answer streams with numbered citations, and each one carries the page and character range retrieval actually served, its match score, and a quote checked word for word against the source text. Clicking it scrolls the PDF to that page and highlights the passage in the document's own text layer, measured in the PDF's embedded font rather than guessed at with a box. Retrieval is dense and sparse together: bge-m3 embeddings in Vectorize alongside BM25, fused by reciprocal rank, and chunked so a passage never crosses a page boundary and loses the page number it needs to cite. A key-facts panel summarises the document with a page link on every fact. When the model has nothing, it says so instead of writing a paragraph.
The result
The sample document is the NIST Cybersecurity Framework 2.0, a US government work in the public domain, and a visitor can upload their own PDF instead. An upload never leaves the browser whole: pdf.js pulls the page text out in the browser and only the text is sent, the passages and their vectors live for an hour, and a cron deletes them.