# MUST OBEY — Mobile-first build requirements This app's PRIMARY surface is a mobile phone. Build it impeccably on mobile FIRST, then verify on tablet and desktop. Treat the rules below as non-negotiable hard constraints, not suggestions. ## Viewports to verify (every screen, every state) - 320 px, 360 px, 375 px, 390 px, 414 px, 480 px - 768 px, 834 px (iPad portrait / Pro 11) - 1024 px, 1280 px, 1440 px, 1920 px, 2560 px - Plus: 200% browser zoom, landscape orientation on every mobile width, iPhone with safe-area insets visible ## Hard layout rules - Mobile-first CSS. Default styles target mobile; `@media (min-width: ...)` for larger viewports. - Use `dvh` and `svh` instead of `vh` for full-height surfaces (iOS Safari URL-bar bug). - Use `clamp()` for fluid typography across all viewports. - Prefer container queries (`@container`) over media queries for component-level responsiveness. - Use `min(100%, ...)` widths so content never overflows. Zero horizontal overflow at any viewport. - Add `` to every page. - Apply `padding: max(safe-area-inset-X, fallback)` on every edge-bleeding container so notched iPhones in landscape never clip content. - Wide tables and code blocks scroll INSIDE their container (`overflow-x: auto`), never push the body. - Use `background-attachment: scroll` on mobile, not `fixed` (iOS Safari repaint bug). - Avoid `backdrop-filter` on animated elements. Use it sparingly on static surfaces only. - **Canvas Scaling**: Canvases must dynamically scale with window resize events and properly handle high-DPI screens (`window.devicePixelRatio`). Set physical dimensions (`canvas.width`/`canvas.height`) using pixel ratio and render relative to this grid, using CSS to control responsive viewport scaling. - **Robust Storage**: Every access to `localStorage`/`sessionStorage` (especially `JSON.parse` of loaded state or writes) MUST be wrapped in a `try-catch` block to handle disabled storage, private browsing mode, quota limits, or corrupted JSON gracefully. Fall back to a robust in-memory object store. ## Touch & accessibility - Tap targets ≥ 44 × 44 px on touch (Apple HIG). Increase to 48 px under `@media (hover: none) and (pointer: coarse)`. - All interactive controls reachable by keyboard with a visible focus ring; respect `:focus-visible`. - Color contrast ≥ 4.5:1 for body text, 3:1 for UI components. - All images have meaningful `alt`. Decorative images use `alt=""`. - Respect `prefers-reduced-motion: reduce` — zero animation durations under that query. - Forms validate inline; error messages are specific, not "Invalid input". - Modals: focus trap, `Esc` closes, `role="dialog"`, `aria-modal="true"`, focus restored on close. ## Performance bar (Lighthouse mobile, throttled 3G/4G) - LCP < 2.5 s · INP < 200 ms · CLS < 0.1 - JS bundle gzip < 200 KB mobile-first; lazy-load non-critical screens via `React.lazy` / dynamic imports. - No render-blocking resources above the fold. - Images: WebP/AVIF preferred, `loading="lazy"`, explicit `width`/`height` attributes (zero CLS), `srcset` for retina. - Videos: `preload="metadata"`, low-resolution poster, max 720p mobile fallback. Never autoplay with audio. - Fonts: `font-display: swap`; preload only the one used above the fold. - Smooth scroll honoured via CSS `scroll-behavior: smooth` with reduced-motion fallback. ## Pre-ship mobile checklist (the deployer MUST verify before declaring done) 1. Open at 375 px in DevTools — every screen scrolls vertically only; zero horizontal scroll. 2. Browser zoom 200% — layout reflows without overlap. 3. iPhone Safari with the URL bar visible AND landscape — no content under the home indicator; no notch clipping. 4. iPad portrait (768 px) and landscape (1024 px) — no awkward gaps; tablet-specific breakpoints land cleanly. 5. Tap every interactive element with a thumb at real-device size — every target is easy to hit. 6. `prefers-reduced-motion: reduce` — every transition / animation skips cleanly, scroll-behavior becomes instant. 7. Lighthouse mobile score ≥ 90 across all 4 categories. 8. Zero `console.error` and zero CLS shift in real-device testing on a mid-tier Android (e.g. Pixel 6a) and an iPhone SE. --- The original template starts below. All rules above apply on TOP of whatever this template specifies. --- # PDF → Plain English ## 1. Project **PDF → Plain English** turns the documents people are about to sign — leases, employment contracts, insurance terms, school enrolment policies, government letters, medical information sheets, fund prospectuses, terms-of-service updates — into a short, honest, page-cited brief. Drop the PDF in. Get back a one-paragraph plain-English summary, the five clauses that matter most with the page number on each one, the three to five decisions the reader has to actually make, the four to six questions to ask the other side before signing, and a candid "watch out for" red-flag list. Almost everybody has a PDF on their desk right now that they are not reading carefully. Skim-reading is how people end up trapped — by an automatic renewal, a non-compete radius, a deductible that resets on a date nobody mentioned, an arbitration clause that quietly removed the right to sue. The app fixes this by reading the whole thing in one pass and returning a brief the reader can actually use. The single demo that proves the magic: a 47-page residential lease lands on the dropzone. In eighteen seconds the app returns a one-paragraph summary in plain English (rent, term, deposit, who pays utilities, what the pet rule actually says), the five most important clauses each cited with a page number (deposit return window p. 11, late-rent fee p. 14, sub-let rules p. 22, repair-and-deduct p. 31, early-termination penalty p. 39), the three decisions the reader still has to make ("are you willing to accept the 3 % annual rent increase?"), the four questions to ask the landlord, and the honest red-flag list ("p. 39 specifies liquidated damages of two months' rent on early termination — high end locally"; "p. 22 forbids any short-let, even one night"). In harder cases — an employer's stock-vesting appendix with a cliff and acceleration triggers, a health-insurance summary that buries out-of-network coverage on page 31, a fund prospectus with eight fee categories spread across nine pages — the same shape of attention applies. Plain-English summary short. Page citations exact. Decisions concrete. Questions practical. Red flags honest, never sensational. **Hero capability:** **Gemini 3.5 Flash** (`gemini-3.5-flash`) with 1M-token context, multimodal PDF understanding, and structured output. Post-I/O 2026, Flash beats the old 3.1 Pro on this task at roughly four times the speed. A 47-page PDF is ~35–50k tokens; a 200-page prospectus ~160–200k tokens. Both fit in one call. **Tagline:** _Drop a PDF in. Get back plain English, the page citations, and the three decisions you have to make before you sign._ ## 2. Target audience The job-to-be-done is universal — almost every adult has a PDF they need to read this week. The app does not need to know who the reader is. - People about to sign a residential lease (rental, sub-let, fixed-term, rolling). - People reviewing an employment offer (base, bonus, equity, notice, restrictive covenants, IP assignment, garden leave, repayable training costs). - People comparing health-insurance plans during open enrolment or after a job change. - People reading a school enrolment policy, code of conduct, or acceptable-use policy on behalf of a child. - People reading a government letter (tax notice, immigration decision, benefits adjustment, planning notice). - People reviewing a medical information sheet or a study consent form before signing. - People reading a fund prospectus, KID, or T&Cs update before investing. - Small-business owners reviewing a contractor agreement, a vendor SLA, an NDA, or a partnership agreement. - People helping a parent or partner read a document that arrived in a language they no longer read confidently. - Anyone who has clicked "I have read and agree" without reading and would like the option to actually read one once. ## 3. Core value propositions - **The page number is on every claim.** Every clause the app surfaces points to the exact page in the source PDF. If the app cannot cite a page, it does not claim the fact. Hovering any clause shows the PDF page inline; clicking jumps to it. - **Dollar amounts are quoted verbatim, never paraphrased.** The app never says "around two months' rent". It quotes "liquidated damages equal to two (2) months' rent" with the page citation. Numbers are too easy to lose in translation. - **Three decisions, not three hundred bullet points.** The brief surfaces exactly three to five decisions the reader actually has to make. "Are you OK with a 3 % annual increase?" is a decision. "The contract has 47 clauses" is not. - **Questions, not advice.** The app drafts the four to six specific questions to ask the landlord, employer, insurer, school, or lawyer. The questions are the value. It never tells the reader what to decide. - **A candid red-flag list, never sensational.** "p. 22 forbids any short-let, even one night" is a red flag worth seeing. "This contract is a TRAP" is not how the app talks. - **Plain English at the reader's level.** The reader can ask for the summary at 5th-grade, 9th-grade (default), or legalese-preserving level. The toggle re-runs the brief in ~3 seconds using cached input. - **Output in the reader's language, faithful to the original.** Any Gemini-supported language for the commentary; dollar amounts and party names stay verbatim from the PDF. - **Hard rule: never substitutes legal or medical advice.** The app surfaces facts and flags decisions. Whenever a document touches health, immigration, taxation, criminal liability, or a decision a qualified professional would normally take, the brief includes an "ask the lawyer" / "ask the doctor" question list with the not-a-substitute disclaimer above it. - **Fast.** A 47-page lease returns a complete brief in under twenty seconds. A 200-page prospectus under a minute. The reader watches the brief stream in section by section; they do not stare at a spinner. ## 4. Features to build - Single dropzone — the visitor drags a PDF in, or pastes a public PDF URL, or picks from the device file picker. Max 50 MB; 1,000 pages. Encrypted PDFs prompt for the password client-side and decrypt before upload. - Multi-document compare mode — drop two leases / two offers / two health plans; brief generates per document plus a side-by-side comparison brief that diffs the relevant fields per document type. - Side-by-side viewer — rendered PDF on the left, plain- English brief on the right. Clicking a cited clause jumps the PDF to that page and highlights the cited text. - Document type detection — classifies the PDF into one of {residential lease, employment contract, insurance terms, school policy, government letter, medical info, fund prospectus / KID, terms of service, vendor / contractor agreement, NDA, other}. The brief template adapts to type. - One-paragraph plain-English summary, ≤ 120 words, in the reader's chosen language and reading level. - The five (up to seven) most important clauses — each a one-line label, a verbatim quote of the most important sentence, the page number, and a one-line plain-English interpretation. - The three to five decisions the reader has to make — phrased as yes/no or A/B questions. - The four to six questions to ask before signing — each one specific and addressed to the right party. - The red-flag list — three to seven concrete items with page citations, severity (informational / watch / high), and a one-line "why this is a flag". - "Ask the lawyer / ask the doctor" panel — populated whenever the document touches a professional domain. The not-a- substitute disclaimer surfaces above this panel. - Verbatim-quote validator — every quoted sentence is checked server-side against the extracted PDF text. Failures trigger one regeneration; second failures are flagged in the UI as "approximate — see page X". - Page-citation jump on hover (thumbnail preview) and click (jump + highlight in the PDF viewer). - Plain-English glossary chips — legalese words ("liquidated damages", "indemnification", "non-compete", "deductible", "out-of-pocket maximum", "vesting cliff", "garden leave", "force majeure") appear as small chips; tapping opens a one-sentence plain-English explanation from a fixed local glossary (never hallucinated). - Reading-level toggle — 5th / 9th (default) / legalese- preserving. Switches re-run in ~3 seconds. - Language toggle — auto-detects PDF language; any Gemini- supported language for the brief. - "Ask a question about this PDF" — text input below the brief. Answers are grounded in the PDF with a page citation. Questions that require legal / medical judgement return a refusal that reframes the question for a professional. - "Is this clause normal here?" grounded search — answers a jurisdiction question with sourced citation URLs from `groundingMetadata`. - Save & share — signed-in users can save briefs and share via per-document magic-link URL (revocable). - Export — PDF copy of the brief with all citations and verbatim quotes; or Markdown for the reader's notes. - Privacy mode — session-only by default for signed-out users; PDFs and briefs deleted on tab close. Signed-in users choose "save to library" explicitly. ## 4b. Required Gemini capabilities + backend services **This template's intelligence comes from the Gemini capabilities below. Wire them up explicitly — don't substitute generic LLM calls.** ### Gemini capabilities (the load-bearing intelligence) - **Long-context PDF understanding** (`gemini-3.5-flash`, 1M context, multimodal PDF input) — the headline capability. The raw PDF is sent as `inlineData` (≤ 20 MB) or via the Files API (`files/*` resource name, e.g. `files/abc123xyz`, passed via `fileData.fileUri`) for larger files. The model reads the whole document in one pass, preserves page numbers, and emits the structured brief. **Guardrail**: a 200-page prospectus is ~160–200k tokens; a 600-page bundle ~500k. At >500k tokens of payload switch to chunked extraction (50 pages per chunk, 2-page overlap, per-chunk summaries, then a merging call). Never send more than 800k tokens in one call. - **Document type classification** (`gemini-3.5-flash`, low thinking) — a fast first call that classifies the PDF into one of the ten document types. Used to route to the right brief template. - **Structured output / JSON Schema** — the main brief call returns `DocumentBrief`; comparison returns `DocumentComparison`; Q&A returns `DocumentAnswer`. Schemas seeded verbatim in the system instruction and as `responseSchema`. Numeric `min`/`max` in Zod are documentation only — clamp server-side. Convert the Zod schema to Gemini's `Schema` type via the SDK helper before passing to `responseSchema`; do NOT pass raw Zod. - **Thinking levels** — `medium` for the main brief (it weighs which clauses matter across the long document). `low` for type classification and verbatim-quote re- extraction. `high` for the 3–4-document comparison call. - **Grounding via Google Search (separate call)** — used only when the reader explicitly asks "is this clause normal here?". `responseSchema` and `google_search` are mutually exclusive in one call; the model emits JSON in the text body and the server parses it. Citation URLs come from `response.groundingMetadata.groundingChunks[].web.uri` — do NOT include URLs in the JSON body; the model will hallucinate them. - **Embeddings** (`gemini-embedding-2`, GA 2026-04-22) — for the "ask a question about this PDF" feature on documents > 100 pages. Embed page-by-page once, then retrieve the top-8 relevant pages on each question and pass them plus the question to a Gemini 3.5 Flash call. - **Expressive TTS** (`gemini-3.1-flash-tts-preview`) — reads the brief aloud for accessibility. No SSML support; encode pauses as "…" mid-sentence and a blank-line "—" between paragraphs. Prepend a one-sentence style directive ("Read clearly and calmly, like reading back a contract summary to a friend at a kitchen table. …"). - **Nano Banana Pro** (`gemini-3-pro-image`) — generates the welcome-screen hero with legible in-image typography (the post-I/O 2026 hero capability). Renders a paper PDF on a desk with the words "PAGE 11 OF 47" legible in the margin. ### Backend services - **Auth — Optional for v1.** The dropzone works without sign-in for one-off briefs (PDF + brief in session memory, deleted on tab close). Sign-in unlocks saved library, comparison history, and magic-link sharing. Firebase Auth with Google sign-in (auto-provisioned by AI Studio Build). Apple sign-in optional, requires Developer account. - **Database — Required for signed-in mode.** Firestore for `users`, `documents`, `briefs`, `comparisons`, `questions`, `shared_links`, `embedding_index_pointers`. - **File storage — Required for signed-in mode and for PDFs > 20 MB.** Firebase Storage. NOT auto-provisioned — enable in the Firebase console. Pre-signed URLs only; lifecycle rule auto-deletes session-only PDFs (`session/` prefix) after 24 hours. - **Email — Optional.** Magic-link share emails; sender domain authorised in Firebase Auth. - **Payments — Not needed for v1.** A future Pro tier (saved library, 1,000+-page documents, batch comparisons) might charge via Stripe; not in v1. - **External APIs:** Gemini API only. Optional server-side PDF-URL fetcher to avoid client-side CORS. **Environment variables:** every secret (Gemini API key, Firebase service-account JSON) lives in environment variables — never in client bundle. Include a `.env.example`. **Auth + data privacy reminders:** never log secrets · never store passwords in plain text · HTTPS everywhere · honour 'delete my account' inside the UI · explicit opt-in for any analytics · the reader's PDFs and briefs are never sent to Gemini for model training (use the Gemini API on the paid tier, where Google does not use your content for model training, per the Gemini API Additional Terms) · sharing is per-document and revocable · session-only PDFs deleted within 24 hours. **Read this first — prompt-craft rules that apply to every call:** 1. **Name the model variant explicitly** in every Gemini API call. Do not let the agent pick the model. See the matrix. 2. **Pin `thinkingLevel` explicitly** per call. On models that do not accept `thinkingConfig` (TTS, image-gen), omit the field entirely — `n/a` in the matrix is documentation, not a value to serialise. 3. **Seed the JSON Schema as a fenced TypeScript / Zod block** in the system instruction or `responseSchema` field. **Convert the Zod schema to Gemini's `Schema` type via the SDK helper** before passing to `responseSchema`; do NOT pass raw Zod. **Numeric `min`/`max` constraints are documentation only inside `responseSchema`; clamp on the server.** 4. **Pin the system instruction separately** from user input. Use `systemInstruction` for persona + behavioural rules; use `contents` for the PDF + user question. Never concatenate. 5. **Pre-declare tools as an enable/disable list** per call. Tools NOT listed for a call should be disabled. 6. **`responseSchema` and `google_search` are mutually exclusive in one call.** If a call needs grounded search, the model emits JSON in the text body and the server parses it; citation URLs come from `response.groundingMetadata.groundingChunks[].web.uri`. 7. **PDF input over 20 MB uses the Files API** (Developer API `files/*` resource name, e.g. `files/abc123xyz`, passed via `fileData.fileUri`). Under 20 MB can use `inlineData` (base64). Do NOT pass Firebase Storage public URLs to `generateContent` — the API does not fetch them server-side. `gs://` URIs belong to Vertex AI / Cloud Storage and are NOT accepted by the Developer API surface this template runs on. 8. **Grounded responses can wrap JSON in ```json fences or add prose preamble.** Server-side, strip fences and brace-extract: ```typescript function safeExtractJSON(raw: string): T { const clean = raw.replace(/```json\s*|```/gi, '').trim(); const s = clean.indexOf('{'); const e = clean.lastIndexOf('}'); if (s === -1 || e === -1) throw new Error('No JSON boundaries in grounded response'); return JSON.parse(clean.slice(s, e + 1)) as T; } ``` 9. **Strip unsupported Zod modifiers before passing to `responseSchema`** — Gemini's OpenAPI subset rejects `.regex()` / `pattern`, fixed-length `z.tuple()`, and other custom validators. Use a sanitizer that flattens tuples to arrays and removes regex patterns before serializing. Validate those constraints in middleware AFTER parsing. ### Per-call model + tools matrix | Call | Model | thinkingLevel | Tools enabled | |------|-------|---------------|---------------| | Document type classification | `gemini-3.5-flash` | low | (none) | | Main brief generation → `DocumentBrief` | `gemini-3.5-flash` | medium | (none) — long-context across the full PDF | | Verbatim-quote re-extraction | `gemini-3.5-flash` | low | (none) | | Comparison brief (2 documents) → `DocumentComparison` | `gemini-3.5-flash` | medium | (none) | | Comparison brief (3-4 documents) → `DocumentComparison` | `gemini-3.5-flash` | high | (none) | | Embedding pages (documents > 100 pages, for Q&A) | `gemini-embedding-2` | n/a | (none) | | Q&A retrieval-augmented answer → `DocumentAnswer` | `gemini-3.5-flash` | low | (none) | | "Is this clause normal here?" grounding | `gemini-3.5-flash` | low | `google_search` (no `responseSchema` on this call) | | TTS playback of the brief | `gemini-3.1-flash-tts-preview` | n/a | n/a | | Hero / empty-state illustration | `gemini-3-pro-image` (Nano Banana Pro) | n/a | n/a | *Note for builders:* on TTS and image-generation calls, omit `thinkingConfig` entirely. The `n/a` cells are documentation only; do not serialise them. Grounded search calls emit JSON in the text body — parse it server-side and read citation URLs from `groundingMetadata`. ### Primary structured-output schemas (seed verbatim in the prompt) ```typescript import { z } from "zod"; const DocumentType = z.enum([ "residential_lease", "employment_contract", "insurance_terms", "school_policy", "government_letter", "medical_info_or_consent", "fund_prospectus_or_kid", "terms_of_service", "vendor_or_contractor_agreement", "nda", "other", ]); const ReadingLevel = z.enum([ "grade_5", "grade_9", "legalese_preserving", ]); const PageCitation = z.object({ page_number: z.number().int().min(1), // 1-indexed // The verbatim sentence from the PDF that justifies the claim. // Server validates this is a substring of the extracted page text. verbatim_quote: z.string(), is_quote_verified: z.boolean(), // server-set }); const Clause = z.object({ clause_label: z.string(), // "Early-termination penalty" one_line_plain_english: z.string(), // ≤ 25 words why_it_matters: z.string(), // ≤ 40 words citation: PageCitation, }); const Decision = z.object({ decision_question: z.string(), // phrased as yes/no or A/B what_it_depends_on: z.string(), // ≤ 30 words, neutral related_citations: z.array(PageCitation), }); const QuestionForOtherParty = z.object({ question_text: z.string(), who_to_ask: z.enum([ "landlord", "employer", "insurer", "school", "doctor", "lawyer", "tax_advisor", "government_agency", "other_party", ]), why_ask: z.string(), // ≤ 30 words related_citations: z.array(PageCitation), }); const RedFlag = z.object({ flag_label: z.string(), why_it_is_a_flag: z.string(), // ≤ 40 words, factual severity: z.enum(["informational", "watch", "high"]), citation: PageCitation, }); const VerbatimMoneyMention = z.object({ // Dollar/currency/percentage amounts quoted EXACTLY as the // PDF wrote them, with the page citation. NEVER paraphrased. amount_verbatim: z.string(), context_one_line: z.string(), citation: PageCitation, }); const ProfessionalQuestion = z.object({ // The 'ask the lawyer / ask the doctor' panel. domain: z.enum(["legal", "medical", "tax", "immigration", "other"]), question_text: z.string(), why_for_a_professional: z.string(), // ≤ 40 words citation: PageCitation.nullable(), }); const DocumentBrief = z.object({ document_id: z.string(), document_type: DocumentType, detected_language: z.string(), // BCP-47 brief_language: z.string(), // BCP-47 reading_level: ReadingLevel, // ≤ 120-word headline summary in brief_language. plain_english_summary: z.string(), important_clauses: z.array(Clause).min(3).max(7), decisions_for_reader: z.array(Decision).min(2).max(6), questions_for_other_party: z.array(QuestionForOtherParty).min(3).max(8), red_flags: z.array(RedFlag), // ALL dollar amounts verbatim, with citations. Never paraphrased. money_mentions: z.array(VerbatimMoneyMention), professional_questions: z.array(ProfessionalQuestion), not_a_substitute_disclaimer_required: z.boolean(), source_pdf_total_pages: z.number().int().min(1), brief_confidence: z.number().min(0).max(1), flagged_for_user_review: z.array(z.object({ field_path: z.string(), reason: z.string(), })), }); const DocumentComparison = z.object({ comparison_id: z.string(), document_ids: z.array(z.string()).min(2).max(4), comparison_summary: z.string(), diff_table: z.array(z.object({ field_label: z.string(), // "Monthly rent" values_per_document: z.array(z.object({ document_id: z.string(), value_verbatim: z.string().nullable(), citation: PageCitation.nullable(), })), notable_difference: z.boolean(), })), // Per-axis observations only — NEVER picks a winner. per_axis_observations: z.array(z.object({ axis: z.string(), // "cost", "flexibility" observation: z.string(), // ≤ 40 words })), }); const DocumentAnswer = z.object({ question_id: z.string(), question_text: z.string(), answer_kind: z.enum([ "quote_grounded", "refusal_requires_professional_advice", "refusal_not_in_document", ]), answer_text: z.string(), citations: z.array(PageCitation), not_a_substitute_disclaimer_required: z.boolean(), }); type DocumentBrief = z.infer; type DocumentComparison = z.infer; type DocumentAnswer = z.infer; ``` ### Common failure modes (and how to avoid them) - **Paraphrased dollar amount** — the model writes "around two months' rent" instead of quoting "two (2) months' rent". The system instruction requires `money_mentions` to contain the verbatim string; the server validates it is a substring of the extracted text. - **Fabricated page citation** — the model cites "p. 11" when the clause is on p. 13, or cites a page exceeding the PDF's page count. Every `PageCitation` passes two server checks: `page_number ≤ total_pages` and `verbatim_quote` is a substring of the extracted text of that page. Failures trigger one regeneration; second failure flags the clause as "approximate". - **Quote from the wrong page** — the model quotes a sentence that exists in the PDF but on a different page. The server re-locates the quote and corrects the page number; if not found anywhere, the clause is flagged. - **The brief gives legal advice** — the model says "you should refuse to sign this" or "this is illegal in your state". The system instruction explicitly forbids prescriptive advice; the brief surfaces decisions and questions, never recommendations. - **The "ask the lawyer" panel is empty when it should not be** — the model produces an employment-contract brief without flagging the IP-assignment clause for a lawyer. The system instruction requires: any clause touching legal liability, medical decisions, tax positions, or immigration MUST add an entry to `professional_questions`. - **Document type misclassified** — a sub-let agreement classified as a vendor agreement. The classification call sees the first 3 pages; the main brief call receives the type as a soft hint but flags the classification if its content disagrees. - **Long document exceeds context** — a 600-page bundle. The server detects > 500k tokens of payload and switches to the chunked-extraction path: 50 pages per chunk with 2-page overlap, summarise per chunk, then a merging call. - **Encrypted PDF** — file is password-protected. Decrypt client-side using `pdf-lib` or `pdf.js` before upload; never send the password to the server. - **OCR needed** — the PDF is a scan with no text layer. v1: reject with a message asking for a text-layer PDF. v2: Gemini-multimodal OCR pre-pass that emits page-by-page text and pins it as the source of truth for the verbatim- quote validator. - **Brief language ≠ PDF language and the model translates the dollar amounts** — the system instruction requires dollar amounts, party names, and dates stay verbatim from the PDF; only the plain-English commentary is translated. - **Grounding call returns URLs in the JSON body** — instruct the model to OMIT URLs from the JSON body; the server reads them from `groundingMetadata`. If URLs appear in the body, log a violation and strip them. - **The reader asks a question the document does not answer** — the Q&A call returns `refusal_not_in_document` with a brief explanation, not a fabricated answer. - **Compare-mode is asked to rank documents** — the system instruction forbids ranking. It returns per-axis observations only ("on cost, A is lower; on flexibility, B is more permissive") and never picks a winner. ### Negative constraints (hard rules) - NEVER substitute legal, medical, tax, or immigration advice. Whenever the document or the reader's question touches these domains, populate `professional_questions` and set `not_a_substitute_disclaimer_required: true`. The UI surfaces the disclaimer non-dismissably above the professional-questions panel. - NEVER paraphrase a dollar amount, percentage rate, date, duration, party name, or page citation. Quote verbatim. - NEVER claim a fact without a `PageCitation`. If the model cannot find a citation, it must omit the claim. - NEVER fabricate a page number. `PageCitation.page_number` must be ≤ `source_pdf_total_pages`. Server enforces; violators are rejected and regenerated once. - NEVER fabricate a quoted sentence. Every `verbatim_quote` must be a substring of the extracted text of the cited page. Failures trigger one regeneration, then an "approximate — see page X" flag. - NEVER rank documents in compare mode. Per-axis observations only; the decision stays with the reader. - NEVER write prescriptive advice ("you should sign", "do not sign", "refuse this clause"). Surface decisions and questions; the reader decides. - NEVER include URLs in a grounded-search JSON body. Citation URLs come from `groundingMetadata`. - NEVER train on the reader's documents. Use the Gemini API on the paid tier, where Google does not use your content for model training, per the Gemini API Additional Terms. - NEVER auto-share. Sharing is explicit, per-document, and revocable; default for signed-out users is session-only with 24-hour deletion. - NEVER moralise about the document's content. "This contract is predatory" is not how the brief talks. "p. 39 specifies liquidated damages equal to two months' rent" is. - NEVER auto-translate party names. "Acme Property Management LLC" stays "Acme Property Management LLC" in a Spanish brief. - NEVER summarise a clause without the page citation shown. If `is_quote_verified` is false after one regeneration, the clause is rendered with a visible "approximate — verify on page X" notice in the UI. ### Per-call `systemInstruction` strings Use these as the literal `systemInstruction` field for each Gemini API call. They complement the series-wide rules already uploaded as the global instructions file. ### Call: Document type classification Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` You receive the first three pages of a PDF (multimodal PDF input) plus the filename. Classify the document into exactly one of: residential_lease, employment_contract, insurance_terms, school_policy, government_letter, medical_info_or_consent, fund_prospectus_or_kid, terms_of_service, vendor_or_contractor_agreement, nda, other. Output JSON only: { "document_type": "", "confidence": <0..1>, "detected_language": "", "rationale_one_line": "<≤ 20 words, factual, no advice>" } Hard rules: - JSON only, no commentary. - Use 'other' if the document does not fit cleanly. - Confidence below 0.7 → flag for user review. - Do NOT translate party names or the language tag. ``` --- ### Call: Main brief generation → `DocumentBrief` Model: `gemini-3.5-flash` · thinkingLevel: medium · Tools: (none, long context across the full PDF) ``` You are a precise, neutral document analyst. You receive the full PDF of a real-world document the reader is considering signing or acting on. Your single job is to produce a DocumentBrief that helps the reader understand the document and decide. You are not a lawyer. You are not giving advice. You are surfacing facts and flagging decisions. Inputs: - The full PDF (Files API `files/*` resource name via `fileData.fileUri`, or inlineData) — read every page. - The document_type from the prior classification (soft hint; override only if the content disagrees). - The reader's brief_language (BCP-47) and reading_level (grade_5 / grade_9 / legalese_preserving). - The PDF's total_pages (server-supplied). Produce a DocumentBrief JSON object matching the schema. Specifically: 1. plain_english_summary — ≤ 120 words, in brief_language at the chosen reading_level. Cover what the document is, then the two or three most consequential terms. Numbers stay verbatim from the PDF. 2. important_clauses — 5 (up to 7) clauses that matter most. Each: a label, one-line plain English, "why it matters", and a PageCitation with the verbatim quote and page number. 3. decisions_for_reader — 3 to 5 decisions the reader has to make, phrased as yes/no or A/B questions. Each points to the citations it depends on. NEUTRAL framing: "are you OK with a 3 % annual rent increase?" not "the 3 % annual increase is unfair". 4. questions_for_other_party — 4 to 6 specific questions to ask the landlord / employer / insurer / school / doctor / lawyer / tax_advisor / government_agency. Each names the addressee in who_to_ask and explains why to ask. 5. red_flags — 3 to 7 concrete items where the document contains a term a typical reader would want to notice. Severity: informational / watch / high. Each has a citation. NEVER sensational ("predatory" is forbidden); factual only. 6. money_mentions — EVERY dollar / currency / percentage / date that affects what the reader pays, receives, or has to do. Quote VERBATIM. Cite the page. 7. professional_questions — if the document touches legal liability, medical decisions, tax positions, or immigration, list the specific questions a lawyer / doctor / tax advisor should weigh in on. Set not_a_substitute_disclaimer_required: true. Hard rules: - Output JSON ONLY matching DocumentBrief. No commentary. - Every claim has a PageCitation. If you cannot cite, omit. - Every verbatim_quote MUST be a substring of the actual page text. Prefer a shorter exact quote over a longer paraphrased one. - Every page_number must be ≤ source_pdf_total_pages. - Quote dollar amounts, rates, dates, party names, durations, and addresses EXACTLY as they appear. Do NOT paraphrase numbers. - Do NOT translate party names. "Acme Property Management LLC" stays as written. - Do NOT translate verbatim_quote — quotes stay in the PDF's original language; only the plain-English commentary is in brief_language. - Do NOT give prescriptive advice. No "you should sign", no "do not sign", no "refuse this clause". Surface decisions and questions. - Do NOT moralise. No "predatory", "unfair", "shady". State the fact; cite the page. - Reading level grade_5 → simple sentences, common words, one idea per sentence. grade_9 (default) → clear adult prose, no jargon without an inline gloss. legalese_preserving → preserve legal terms with brief plain-English glosses inline. - If a clause is ambiguous, say so plainly and put the ambiguity into questions_for_other_party. - brief_confidence reflects text-layer quality, classification confidence, and clause-citation completeness. Below 0.7 → flag for user review. The reader will check your citations. Be exact. ``` --- ### Call: Verbatim-quote re-extraction Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` The server's validator could not find your quoted sentence in the extracted text of the cited page. Options: 1. The quote exists elsewhere in the PDF — return the corrected page_number. 2. The quote is paraphrased — return a SHORTER quote that is a verbatim substring of the cited page. 3. The clause cannot be cited with confidence — return a refusal. Inputs: - clause_label, one_line_plain_english from the rejected clause. - The page text the server expected the quote to come from. - The full PDF text (page-by-page). Output JSON: { "outcome": "corrected" | "shortened" | "refused", "corrected_page_number": , "verbatim_quote": "", "refusal_reason": "" } Hard rules: - verbatim_quote, if returned, MUST be a substring of the extracted text of corrected_page_number. - Prefer shorter exact quotes over longer paraphrased ones. - If refused, the calling code marks the clause as "approximate — verify on page X" in the UI. ``` --- ### Call: Comparison brief (2 documents) → `DocumentComparison` Model: `gemini-3.5-flash` · thinkingLevel: medium · Tools: (none) ``` You receive 2 PDFs and their already-generated DocumentBrief objects. Produce a DocumentComparison JSON object. Rules: - comparison_summary — ≤ 120 words, neutral, in brief_language. - diff_table — for each canonical field of the document_type (e.g. residential_lease: monthly_rent, deposit, term_length, notice_period, pet_policy, early_termination_penalty, utility_responsibility, guest_policy, rent_increase_terms, renewal_terms), list verbatim value per document with its PageCitation. - per_axis_observations — for each meaningful axis (cost, flexibility, restriction, risk), name which document is more X on that axis. NEVER pick an overall winner. Hard rules: - JSON only. - Values quoted VERBATIM from each document, with citations. - Do NOT rank the documents. - Do NOT give prescriptive advice. - If a field is present in one document and absent in the other, the absent document's value_verbatim is null, and notable_difference is true. ``` --- ### Call: Comparison brief (3-4 documents) → `DocumentComparison` Model: `gemini-3.5-flash` · thinkingLevel: high · Tools: (none) ``` Same as the 2-document comparison, with 3 or 4 documents. The high thinking level is reserved for multi-document reasoning because the diff_table grows quickly. Additional rules: - Cap at 4 documents per comparison. Reject if more passed. - Per-axis observations name each document by document_id. - Do NOT rank overall. ``` --- ### Call: Embedding pages (documents > 100 pages, for Q&A) Model: `gemini-embedding-2` · n/a · n/a ``` Embed each page as one vector. Page text is extracted server- side from the PDF text layer (or the multimodal OCR pre-pass if no text layer). Each page becomes one embedding row: { "document_id": "", "page_number": <1-indexed>, "embedding": , "page_text": "" } Index keyed by (document_id, page_number); stored alongside the document for later Q&A retrieval. No system instruction beyond the embedding model's default; this entry reminds the builder to embed pages (not the whole document) for retrieval granularity. ``` --- ### Call: Q&A retrieval-augmented answer → `DocumentAnswer` Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` The reader has asked a question about the PDF. Inputs: - question_text. - Top-8 retrieved pages (page_number + page_text). - The full DocumentBrief already generated, for context. Produce a DocumentAnswer JSON object. Decision tree: 1. Answer is in the retrieved pages → answer_kind: "quote_grounded" with citations. 2. Question requires legal/medical/tax/immigration judgement ("should I sign this?", "is this enforceable?") → answer_kind: "refusal_requires_professional_advice", set not_a_substitute_disclaimer_required: true. answer_text reframes the question for the appropriate professional ("ask a lawyer who knows residential lease law in your jurisdiction whether the liquidated-damages clause on p. 39 is enforceable"). 3. Answer not in the document → answer_kind: "refusal_not_in_document" with a brief explanation. Hard rules: - Every citation MUST be one of the retrieved pages. - Every quoted sentence MUST be a substring of the cited page's text. - Do NOT make up an answer. If unsure, refuse. - Do NOT give advice. Surface facts and the corresponding professional question. Output JSON only. ``` --- ### Call: "Is this clause normal here?" grounding Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: `google_search` ``` You answer a single grounded question about whether a clause the reader has highlighted is typical for the document_type in the reader's stated jurisdiction. Examples: - "Is a two-month liquidated-damages clause normal for a residential lease in California?" - "Is a six-month non-compete normal for a software engineer in the UK?" - "Is a $7,500 out-of-pocket maximum normal for a US individual health plan in 2026?" Use google_search grounding for every answer. Do NOT answer from training memory alone for any numeric range. Output JSON in the text body (NOT via responseSchema — mutually exclusive with google_search). Server parses the JSON and reads citation URLs from response.groundingMetadata.groundingChunks[].web.uri — do NOT include URLs in the body; the model will hallucinate them. JSON shape: { "answer_one_line": "", "answer_detail": "<2-4 sentences>", "typical_range": { "low_verbatim": "", "high_verbatim": "", "unit": "" }, "jurisdiction": "", "professional_consultation_recommended": } Hard rules: - Numeric ranges quoted verbatim where sources provide them. - If sources disagree, surface the disagreement. - If jurisdiction is unstated, the answer asks for one rather than guessing. - professional_consultation_recommended: true for any question about enforceability, tax treatment, medical necessity, immigration eligibility, or anything a lay reader cannot reliably interpret. - Do NOT include URLs in the JSON body. ``` --- ### Call: TTS playback of the brief Model: `gemini-3.1-flash-tts-preview` · n/a · n/a ``` Voice: clear, calm, unhurried. Pick the Gemini 3.1 Flash TTS voice whose languageCode matches brief_language. Pre-process the brief text before sending to TTS: - Sequence: plain_english_summary → important_clauses → decisions_for_reader → questions_for_other_party → red_flags → professional_questions disclaimer (if needed). - Prepend a one-phrase signpost per section ("The most important clauses…", "Decisions you have to make…", "Questions to ask the landlord…", "Red flags…"). - At sentence boundaries, insert "…" for a natural pause. - Between sections, insert a blank line and an em-dash ("—"). Gemini 3.1 Flash TTS does NOT support SSML ; these textual cues convey pace. - Skip metadata that does not belong in spoken playback (document_id) but DO read page numbers explicitly on red- flag and important-clause items so the listener can find them. - Target rate: ~145 words per minute for clarity. Style direction: prepend ONE short directive sentence, exactly like: "Read clearly and calmly, like reading back a contract summary to a friend at a kitchen table. …". There is no separate `style` API field; the directive sentence inside the input is how style is conveyed. Phoneme overrides for party names and legal Latinisms are NOT exposed — no SSML . Pronunciation comes from the chosen voice's locale. ``` --- ### Call: Hero / empty-state illustration Model: `gemini-3-pro-image` (Nano Banana Pro) · n/a · n/a ``` Generate a single photographic-looking image for the welcome screen / empty state. The key visual element is the rendered page-number reference in the margin of the paper — Nano Banana Pro renders legible 4K typography (the I/O 2026 hero capability). Prompt anchors: - "warm afternoon light on a clean light-wood desk, a printed multi-page contract fanned slightly to the left, the words PAGE 11 OF 47 printed clearly in the margin in small black type, a white ceramic coffee mug at the right edge of frame, a black ballpoint pen resting across the contract, shallow depth of field" - "a school-policy PDF printed and laid open on a kitchen counter at golden hour, the words PAGE 6 OF 14 visible in the footer, a child's drawing on a corkboard in the soft background, no people in frame" - "an employment offer printed and bound with a single staple, cover page visible with the words OFFER OF EMPLOYMENT legible in 18-point Garamond, a tablet beside it, soft window light" Hard rules: - Photographic, not cartoon, not illustration-style. - No people in frame unless explicitly asked. - No real brand names or party names visible. Use plausible fictional names ("Acme Property Management LLC", "Northbridge Health Plan", "Crescent Public Schools"). - The page-number reference in the margin must be LEGIBLE — this is the post-I/O 2026 Nano Banana Pro trick. - Warm lighting, slight imperfection, real-paper texture. Avoid the glossy AI-render look. - Aspect ratios: 3:2 for hero, 1:1 for empty states. ``` ## 5. Use cases & content to include Build dedicated UI sections or flows for each — they tell you what content the app must support. - **The 47-page lease.** Drop a residential lease PDF. In ~18 seconds the brief returns. Summary in one paragraph; five important clauses (deposit, rent increase, sub-let, repair- and-deduct, early termination) with verbatim quotes and page citations. Three decisions (annual rent increase, notice period, pre-move-in repairs). Four questions for the landlord (deposit-scheme, boiler servicing, guest cap, rent-increase date). Red flags: high early-termination penalty; strict short-let ban. - **The 28-page employment offer.** A new-grad software engineer drops an offer PDF. The brief explains base, bonus, equity (4-year vest, 1-year cliff — verbatim from p. 14), notice (90 days, p. 21), IP assignment (broad, p. 24), non-compete (6 months / 50 miles, p. 25), arbitration (mandatory, p. 27). The "ask the lawyer" panel surfaces non-compete enforceability, IP-assignment scope, arbitration carve-outs. - **The 36-page health-insurance summary.** Brief surfaces deductible ($1,500 / $3,000), out-of-pocket max ($7,500 / $15,000), 80/20 in-network vs 60/40 out-of-network, prescription tier structure, maternity, mental-health, and the prior-authorisation list. Red flags include the high out-of-network out-of-pocket and the four-medication-class prior-authorisation requirement. - **The 14-page school acceptable-use policy.** Parent drops the policy. Brief explains what device usage is allowed, what counts as a violation, what the consequences ladder looks like, where parental consent is required. Decisions for the parent: opt-in to the photo-release; sign the device-loan agreement. - **The 1-page government letter that is actually 4 pages.** A tax notice. Brief surfaces amount owed (verbatim), deadline (verbatim), appeal route, non-payment consequences. Professional-questions panel lists the tax-advisor questions. - **The 38-page fund prospectus.** An investor drops the prospectus. Brief surfaces the fee schedule (management, performance, redemption, exit — each verbatim with citation), lockup period, redemption frequency, risk- factor list, and the "ask the tax advisor" panel. - **Compare two leases.** A renter is choosing between two apartments and drops both. The comparison brief surfaces a per-field diff (rent, deposit, term, notice, pet, early-term, guest, rent-increase) with each value quoted verbatim and cited. Per-axis observations note which lease is more permissive on each axis without picking a winner. - **Ask a question about the PDF.** After the brief renders, the reader types "is the dog allowed in the courtyard?" Q&A retrieves the relevant pages, finds the rule on p. 18, returns a quote-grounded answer. The reader types "should I sign this?" — Q&A refuses with `refusal_requires_professional_advice` and reframes the question for a lawyer. - **Ask whether a clause is normal here.** The reader taps the early-termination clause and asks "is two months' rent normal in California?" The grounded-search call returns a one-line answer with the typical-range verbatim, citation URLs from grounding metadata, and `professional_consultation_recommended: true`. - **Read aloud.** A blind reader presses play. TTS reads the brief in their chosen language at ~145 WPM, with section signposts and natural pauses. Page numbers are read aloud on red-flag and important-clause items so the listener can find them. ## 6. Page structure Build the following screens in this order. 1. **Landing / dropzone.** Hero photograph (printed contract on a wooden desk with the legible "PAGE 11 OF 47" reference, golden hour). Headline: "Drop a PDF in. Get back plain English." Subhead: "And the three decisions you have to make before you sign." A big dropzone; "or paste a public PDF URL" link below it; "or pick from device" link opens the file picker. Below: three small badges — "We never train on your documents", "Session- only by default", "Page-cited every claim". Below that: three example PDFs to try without uploading (sample lease, sample offer, sample health plan). Quiet "Sign in to save" link at the bottom. 2. **Processing view.** A progress strip ticks through: "Reading the PDF…", "Classifying document type…", "Drafting the plain-English summary…", "Pulling the most important clauses…", "Drafting the decisions and the questions…", "Checking every quote against the source…", "Done." The brief streams in section by section as each part is ready. PDF renders in a side panel as a thumbnail strip. 3. **Brief view (side-by-side).** Left: the PDF, scrollable and zoomable. Right: the brief — summary, important clauses, decisions, questions, red flags, money mentions, professional-questions panel. Each citation is a clickable chip that jumps the PDF to that page and highlights the cited sentence. Hovering shows a thumbnail preview. 4. **Reading-level + language toggles.** Top of the brief: reading level (grade 5 / grade 9 / legalese-preserving) and brief language (auto-detected from document or any Gemini-supported language). Toggling re-runs the main brief call in ~3 seconds. 5. **Ask a question.** Below the brief: "Ask a question about this PDF" with a text input. Answers stream below, each card with answer, citation chips, and a kind label (quote-grounded / needs professional / not in document). 6. **Compare mode.** "Drop another PDF to compare" button. On a second drop, the comparison brief generates and a new tab appears. The comparison view shows the diff table and per-axis observations. 7. **Saved library (signed-in).** Personal library of saved briefs. Each card shows document type, filename, date, brief language, summary excerpt. Click → brief view. Sharing icon on each card. 8. **Shared view (read-only).** Public read-only view of a shared brief. Same layout as brief view but no edit controls. Q&A unlocked only if recipient signs in. 9. **Settings & privacy.** Default brief language, default reading level, default brief retention ("session only" / "save for 30 days" / "save indefinitely"). Privacy panel restates the not-trained-on policy in plain English. "Delete all my briefs and PDFs" button with 60-second cool-off. 10. **Footer.** "Built for the PDF on your desk you have not read yet." Privacy: "Your documents and briefs are yours. We never train on them." Capabilities `(i)` icon in header. ## 6b. First-visit onboarding Show a **first-visit onboarding** the first time a visitor lands (detect via `localStorage` flag; do not show on return visits). Three slides, dismissible at any time. Persistent re-entry: a `?` icon in the header reopens it. **Slide 1 — What this is.** - Headline: "Welcome to PDF → Plain English." - Subhead: "Drop a PDF in. Get back plain English, the page citations, and the three decisions you have to make before you sign." - One paragraph (≤ 60 words) explaining the job-to-be-done: the brief surfaces facts, cites the page on every claim, quotes dollar amounts verbatim, lists the decisions, drafts questions for the other side, and flags any decision that needs a lawyer or doctor. It does not give advice. - Visual: a small annotated illustration of a brief card with the citation chip, verbatim quote, and decisions panel labelled. **Slide 2 — Try it now.** - One short prompt: "Try with the sample lease". - A live demo input pre-loaded with the 47-page sample lease. - 1-2 sentences pointing at *the specific page elements* where the Gemini magic happens (brief streams in, the page chip jumps the PDF, the verbatim-quote validator catches a near-miss in the demo). **Slide 3 — How to remix this.** - Headline: "Make this yours." - Three short bullets: - "Swap the sample PDFs in `/data/seed-pdfs/` for your own documents." - "Adjust the prompts in `/server/prompts/` to fit the documents you read most often (court filings, IRS notices, consent forms, etc.)." - "Wire up your Gemini API key and (optional) Firebase project via the env-var list in the capabilities panel." - Primary CTA: "Use this template" → AI Studio Build remix. - Secondary: "Just exploring — close" (sets localStorage flag, never auto-shows again). **Accessibility:** focus trap, `Esc` closes, `role="dialog"`, `aria-modal="true"`, `aria-labelledby`, focus restored to trigger on close. Respect `prefers-reduced-motion`. **Don't:** - Don't gate content behind the modal. The page beneath must be fully usable. - Don't auto-reshow on return visits. - Don't include unrelated CTAs (newsletter, social follow). ## 6c. Capabilities info button (persistent in header) Add a persistent `(i)` icon in the top-right of the header. Click → opens a modal/panel titled **"What powers this app"**. **Gemini capabilities used (the hero list):** - **Gemini 3.5 Flash (long-context PDF understanding)** — reads the whole document in one pass and emits the structured brief. 1M-token context, ~4× the speed of the old 3.1 Pro for this task. Post-I/O 2026 this is the new default. - **Gemini 3.5 Flash (structured output)** — every brief is a typed `DocumentBrief`; every comparison a typed `DocumentComparison`; every Q&A answer a typed `DocumentAnswer`. Schemas live in the repo. - **Gemini 3.5 Flash + grounded search** — resolves "is this clause normal here?" with sourced citation URLs. - **Gemini embedding 2** (multimodal) — embeds long documents page-by-page so Q&A stays cheap on follow-up questions to 200+ page documents. - **Gemini 3.1 Flash TTS** — reads the brief aloud in the reader's chosen language with natural pauses. - **Nano Banana Pro (`gemini-3-pro-image`)** — generates the welcome-screen hero with legible in-image typography (the post-I/O 2026 hero capability). - **Firebase Auth** — optional Google sign-in for saved library and shared links. - **Firestore** — stores saved briefs and comparison history. - **Firebase Storage** — keeps uploaded PDFs at upload quality with a 24-hour auto-delete on session-only mode. **Cost note** — see 6d. A 47-page lease brief is ~$0.09 cold; a 200-page prospectus ~$0.29; a 2-document comparison ~$0.15. Reading-level toggle re-runs use cached input (~$0.04). **Privacy note** — your PDFs and briefs are private to you and anyone you explicitly share with. This app uses the Gemini API on the paid tier, where Google does not use your content for model training, per the Gemini API Additional Terms. **Not-a-substitute note** — this app surfaces facts and flags decisions. It is NOT a substitute for legal, medical, tax, or immigration advice. Whenever a question touches those domains, the brief surfaces an "ask the lawyer / ask the doctor / ask the tax advisor" panel with the not-a- substitute disclaimer above it. **Backend services this app depends on:** - Auth: optional Firebase Auth — see section 4b - Database: Firestore (signed-in mode) — see section 4b - Storage: Firebase Storage (signed-in mode and large PDFs); REQUIRES manual enable in Firebase console — AIS Build does not auto-provision Storage today. - Email: optional magic-link share emails — see section 4b - Payments: not used in v1 - External APIs: Gemini API only **Environment variables you'll need to configure:** - `GEMINI_API_KEY` — your Google AI Studio API key - `FIREBASE_PROJECT_ID` — optional (signed-in mode only) - `FIREBASE_SERVICE_ACCOUNT` — optional (server-side only) **Documentation links:** AI Studio Build docs; Gemini API multimodal PDF input, long-context, structured output, grounded search, embeddings, TTS docs; Firebase Auth / Firestore / Storage docs. **Accessibility:** same standards as the onboarding modal. **Behaviour:** - Always available — single click from anywhere. - Tooltip on the `(i)` icon: "How this app is built". - Mobile: opens as a full-screen sheet that slides up. - Should be the most honest part of the app — never hand- wave service requirements; never say "AI" without naming the specific Gemini model and capability. ## 6d. Detailed cost breakdown (deployer reads this BEFORE shipping) Gemini 3.5 Flash pricing (post-I/O 2026): $1.50 per 1M input tokens, $9.00 per 1M output, $0.15 per 1M cached input. Nano Banana Pro: ~$2/M input, ~$12/M output (per-image price is approximate — Google has not pinned an exact public figure; verify on the Gemini API pricing page before shipping). Gemini 3.1 Flash TTS: approximate ~$10/M output (≈ ~$0.000003/character; the exact TTS character-token price was not pinned at I/O 2026, treat as a working estimate and verify before shipping). `gemini-embedding-2`: ~$0.20/M input. - **Document type classification** — first 3 pages ≈ 2.4k input + ~150 output → ~$0.005 per call. - **Main brief generation (47-page lease)** — ~35k input ($0.0525) + ~4k output ($0.036) → **~$0.09 per brief**. A 200-page prospectus at ~160k input + ~5k output → **~$0.29**. - **Verbatim-quote re-extraction** — only when validator rejects a quote. ~5k input + ~300 output → ~$0.01 per rejection. Typical brief has 0–2 rejections. - **Comparison brief, 2 documents** — ~70k input + ~5k output → **~$0.15 per comparison**. - **Comparison brief, 3-4 documents** — ~140k input + ~8k output → **~$0.28 per comparison**. - **Embedding pages** — for documents > 100 pages. ~1k tokens per page × 200 pages = ~200k → ~$0.04 per long document, one-time. - **Q&A retrieval-augmented answer** — top-8 pages × ~1k tokens + question + system instruction = ~10k input + ~400 output → ~$0.02 per question. - **Grounded "is this clause normal here?"** — small input, ~$0.003 per call. - **TTS playback** — a 600-word brief readback is ~$0.012 per playback. - **Hero image (Nano Banana Pro)** — approximate ~$0.04 per image (Google has not pinned an exact public per-image figure; verify before shipping). Generated once per app install (cached client-side) or on regenerate-empty-state. - **Cached-input savings on toggle re-runs** — when the reader switches reading level or brief language, the PDF is the same; cached input at $0.15/M makes the second brief call ~$0.04 vs ~$0.09 cold. - **Storage** — Firebase Storage standard tier ~$0.026/GB/ month. A 5 MB lease PDF ≈ $0.00013/month; a 30 MB prospectus ≈ $0.00078/month. Session-only documents are deleted within 24 hours and effectively free. ## 7. Design language - **Mood:** A quiet desk, the contract laid open, the coffee mug at the edge of frame. Not a SaaS dashboard. Not a legal-tech portal. The kitchen counter at 9 pm, the PDF printed out, a pen across the top page. A reader's working surface between "I have not read this" and "I have read this and I know what to ask". - **Typography:** Clean grotesque for app chrome and data labels (Inter or Geist). Display serif for section headings on the brief (Source Serif Pro or Fraunces). A monospace voice (JetBrains Mono or SF Mono) for verbatim quotes — to mark the difference between the reader's working language and the PDF's own words. - **Palette:** Paper white `#FAFAF7` for the brief surface, deep ink `#16161A` for body, warm amber `#A37430` for headings and citation chips, deep navy `#2A3957` for hyperlinks and active controls, muted green `#577A4B` for "informational" red-flag severity, muted ochre `#C08A2E` for "watch", warm red `#A33A2C` for "high" severity and for the not-a-substitute disclaimer chrome. Borrowed from a paper notebook, not from SaaS design systems. - **Imagery:** Photographic. A contract on a desk at golden hour. A school policy on a kitchen counter. Generated via Nano Banana Pro with prompts emphasising real surfaces, warm light, no people, no real brand names, and the legible page-number reference in the margin as the post- I/O 2026 hero detail. - **Hand-feel touches:** Each new section of the brief drops in with a thin paper-shadow. Citation chips have a tiny page-corner fold graphic. Hovering shows a thumbnail of the PDF page with the quoted sentence subtly underlined. The red-flag list uses a small vertical accent stripe in the severity colour, never a heavy alert banner. - **Spacing:** consistent 4-px base. Generous whitespace. - **Radius:** token set (6 / 12 / 20 px). Citation chips 6; brief section cards 12; hero card 20. - **Shadows:** subtle, layered, warm-tinted. - **Motion:** purposeful — entrance fades, hover lifts. Respect `prefers-reduced-motion`. The streaming-section landing animation is the place motion carries meaning; reduced-motion users see each section jump in instantly. - **States:** every interactive element has hover, focus, active, disabled. Loading uses skeletons not spinners. Empty states have helpful next-action guidance. ## 8. Content generation rules - Write **realistic, specific copy**. NO Lorem Ipsum. NO generic placeholders. - Invent plausible names, party names, page numbers, dollar amounts, and verbatim quotes that fit the domain. Party names are fictional ("Acme Property Management LLC", "Northbridge Health Plan", "Crescent Public Schools"). Dollar amounts and dates are plausible-but-fictional. - Tone: warm, direct, free of corporate language. The reader is reading the brief because they did not want to read the PDF — respect their time and never pad. - Headlines: punchy and concrete. No 'Empower your X' filler. No 'Revolutionize'. No 'Seamless'. No 'AI-powered'. - Body copy: short paragraphs (2-4 sentences). Lists where appropriate. - Plain language. Avoid jargon — except in `legalese- preserving` mode where legal terms appear with an inline gloss. - Never label AI output as "AI says" — let it speak naturally. Use small uncertainty cues only where epistemic honesty requires them (a low-confidence citation shows as a faintly outlined chip; tapping reveals the validator's note). ## 8a. Seed content (use these specific examples) **Sample documents available without sign-in:** 1. **"47-page residential lease — Acme Property Management"** (document_type: residential_lease, language: en-US) - Monthly rent: "$2,450.00 due on or before the first (1st) day of each calendar month" — p. 3 - Security deposit: "Two thousand four hundred and fifty dollars ($2,450.00), held in a New York State licensed deposit account" — p. 11 - Deposit return window: "no later than fourteen (14) days after the surrender of the premises" — p. 11 - Annual rent increase: "shall increase by three percent (3.0%) on each anniversary of the Commencement Date" — p. 14 - Late-rent fee: "five percent (5%) of the monthly rent if not received by the fifth (5th) day of the month" — p. 14 - Sub-let / short-let: "No Tenant shall sub-let, license, or otherwise permit short-term occupancy (including any rental of one or more nights through an online platform) of the Premises. Violation results in liquidated damages equal to one (1) month's rent." — p. 22 - Repair-and-deduct: "if Landlord fails to make any repair required by law within thirty (30) days of written notice, Tenant may make such repair and deduct the reasonable cost from the next month's rent, provided such deduction shall not exceed one (1) month's rent in any month." — p. 31 - Early termination: "liquidated damages equal to two (2) months' rent" — p. 39 - Decisions: 3% annual increase OK? 60-day move-out notice OK? Pre-move-in repairs in writing? - Questions for landlord: which deposit-protection scheme? boiler servicing? guest cap? rent-increase date? - Red flags: HIGH — early-termination two months' rent (p. 39); WATCH — short-let ban covers single overnight (p. 22). 2. **"28-page employment offer — Crescent Software Inc."** (document_type: employment_contract) - Base salary: "$135,000 per annum" — p. 2 - Annual bonus: "up to twenty percent (20%) of Base, subject to Company and individual performance" — p. 4 - Equity: "30,000 RSUs vesting over four (4) years with a one (1) year cliff, with twenty-five percent (25%) vesting on the first anniversary and the remainder vesting in equal quarterly instalments thereafter" — p. 14 - Notice: "ninety (90) days' written notice" — p. 21 - IP assignment: "Employee hereby assigns to Company all right, title, and interest in any Invention conceived, developed, or reduced to practice during the Term, whether or not during working hours and whether or not on Company premises" — p. 24 - Non-compete: "during the Term and for six (6) months following the Termination Date, within a fifty (50) mile radius of any office at which Employee performed services" — p. 25 - Arbitration: "any dispute arising out of this Agreement shall be resolved exclusively by binding arbitration administered by the American Arbitration Association in San Francisco, California" — p. 27 - Decisions: IP off-hours and off-premises OK? Non-compete OK? Waive jury trial via arbitration OK? - Questions for employer: narrow non-compete to direct competitors? carve-out personal projects off-hours, off-equipment? severance during notice? - Professional questions (ask the lawyer): 6-month non-compete enforceable in your jurisdiction? IP scope consistent with state law? arbitration carve- out for statutory claims (discrimination, whistleblower)? 3. **"36-page health-insurance summary — Northbridge Health"** (document_type: insurance_terms) - Monthly premium (individual): "$420.00" — p. 2 - Deductible: "$1,500 / $3,000" individual / family — p. 4 - Out-of-pocket max: "$7,500 / $15,000" individual / family — p. 4 - In-network coinsurance: "80% / 20%" — p. 6 - Out-of-network coinsurance: "60% / 40%" — p. 6 - Prescription tiers: "Tier 1 generic $10, Tier 2 preferred brand $40, Tier 3 non-preferred brand $80, Tier 4 specialty 30% coinsurance to a maximum of $300 per fill" — p. 12 - Prior-authorisation list: "all Tier 4 specialty drugs, all inpatient hospitalisations, all outpatient surgeries exceeding $5,000, and all advanced imaging (MRI, CT, PET)" — p. 28 - Decisions: $7,500 individual OOP max OK? any current prescriptions in Tier 4? current providers in- network? - Questions for insurer: current PCP in-network? prescriptions on prior-auth list? prior-auth turnaround time? - Professional questions (ask the doctor): generics in Tier 1/2 for current meds? **Sample voice copy:** - Landing dropzone: "Drop a PDF in. Or paste a public PDF URL. Or pick from device." - Pre-upload privacy line: "Your document stays in your browser session and is deleted when you close this tab, unless you sign in and save it." - Processing micro-copy: "Reading 47 pages…" / "Pulling the most important clauses…" / "Checking every quote against the source…" - Save confirmation: "Brief generated for Acme lease. 5 clauses cited. 3 decisions for you. 4 questions for the landlord." - Validator caught a near-miss: "We adjusted one citation — the model quoted a sentence that lives on p. 13, not p. 11. Corrected automatically." - Reading-level toggle: "Re-reading at 5th-grade level…" (~3-second toggle). - Q&A refusal (legal): "This is a question for a lawyer who knows residential lease law in your jurisdiction. We have rephrased it as: 'Is the liquidated-damages clause on p. 39 enforceable in New York?'" - Q&A refusal (not in document): "This document does not mention pet deposits. The pet rule on p. 18 covers presence, not deposit." - TTS preamble: "Read clearly and calmly, like reading back a contract summary to a friend at a kitchen table…" - Share email subject: "I'd like your read on the Acme lease — here's the plain-English brief" - Capabilities tooltip: "How this app is built — what Gemini capabilities and Firebase services it uses, what it costs, and what it does with your documents." - Privacy banner (signed-out): "Session-only by default — your PDF and brief disappear when you close this tab." ## 9. Media & assets - **Hero image (landing screen):** A photographed-looking shot of a printed contract on a clean light-wood desk at golden hour. The legible "PAGE 11 OF 47" reference is in the margin of the open page (Nano Banana Pro renders this legibly — the I/O 2026 hero detail). A white ceramic coffee mug at the right edge; a black ballpoint pen across the top. Generate via Nano Banana Pro with the prompt anchors in 4b. - **App icon / wordmark:** Set in the display serif. Slight paper texture behind it. No icon — just type. - **Empty-state illustration:** A simple line drawing of a rolled paper with a citation chip beside it. Hand-drawn aesthetic, not a flat icon. Generate once at build time via Nano Banana 2 (`gemini-3.1-flash-image`), 1:1 WebP at 1024×1024, prompt: "single hand-drawn ink line illustration of a rolled-up paper scroll on the left and a small rectangular pill-shaped citation chip on the right with a tiny page-number glyph inside, off-white paper background, slight pen imperfection, no shading, no colour fill, no commercial branding". Ship as a seed asset at `/public/samples/empty-state-scroll.webp`. - **Demo PDFs:** The three seed PDFs (47-page lease, 28-page offer, 36-page health plan) live in `/data/seed-pdfs/`. Plausible-but-fictional documents authored for this template; safe to ship. - **Stock fallbacks:** If image generation fails, fall back to `/public/samples/desk.jpg` (3:2 WebP, 2048×1365 — ship as a seed asset; recreate via Nano Banana Pro (`gemini-3-pro-image`) with the prompt: "photographic printed contract on a clean light-wood desk at golden hour, legible 'PAGE 11 OF 47' rendered crisply in the open page's margin at 4K, a white ceramic coffee mug at the right edge, a black ballpoint pen across the top of the page, no people, no commercial branding, real paper texture, soft shadow under the mug"). Never a "📄" emoji. - **Generated imagery:** prefer Nano Banana Pro over stock photography. Use the legible-typography capability for the page-number reference detail — this is the post-I/O 2026 feature that justifies the model choice. - **Optimisation:** WebP/AVIF, `loading="lazy"`, explicit `width`/`height` to prevent layout shift. - **Icons:** `lucide-react` for UI. Use sparingly. ## 10. Interactivity & states - Every interactive element has hover, focus, active, and disabled states. - Forms validate inline with specific error messages. "This PDF is encrypted — enter the password to decrypt client- side" is the right shape. - Loading states use skeletons matching the eventual layout, not spinners. The brief streams in section by section; each section's skeleton is replaced as the section arrives. - Empty states explain the next action with a domain- specific button label: "Drop a PDF in to start", "Try with the sample lease", "Ask a question about this PDF" — never a generic "Add your first item". - Smooth scroll for in-page anchors. - All AI-generated content streams in section-by-section with a clear "thinking…" indicator before content arrives. - If an AI call fails, show a calm, specific error ("The brief could not finish because the document is in a language we couldn't detect. Want to try selecting the language manually?") and offer retry. - Low-confidence citations show as faintly outlined chips; tapping reveals the validator's note. - The streaming-section landing animation takes ~250 ms with `prefers-reduced-motion` falling back to instant. - The not-a-substitute disclaimer surfaces as a non- dismissable inline banner above the "ask the lawyer / doctor / tax advisor" panel — not a toast that scrolls off-screen. - Citation chips on hover show a thumbnail preview; on click, the side-by-side PDF viewer jumps to that page and highlights the cited sentence with a soft underline (not a yellow highlighter — more readable on printed text). - Reading-level and brief-language toggles re-run the main call in ~3 seconds; the brief sections re-stream in the new shape. - "Ask a question" answers stream below the question card; each answer has a kind label (quote-grounded / needs professional / not in document) with a small coloured dot for at-a-glance recognition. - Compare-mode opens as a new tab beside the per-document tabs; diff table is scrollable horizontally on narrow viewports with sticky first column. - TTS playback exposes a transcript that scrolls in sync; tapping a paragraph jumps the audio to that point. ## 11. Tech & responsive requirements - **Stack:** React + TypeScript + Tailwind CSS. Functional components + hooks. Shadcn UI primitives where appropriate. PDF rendering via `react-pdf` (wraps `pdf.js`). - **Build runtime:** AI Studio Build — full-stack with Cloud Run server-side functions. All Gemini API calls happen server-side; API key lives in Secrets Manager, never in client bundle. First two apps deploy free to Cloud Run with no credit card (post-I/O 2026 AI Studio Build default). - **Model selection:** explicitly pin `gemini-3.5-flash` for brief generation, classification, comparison, Q&A; `gemini-embedding-2` for page embeddings; `gemini-3.1- flash-tts-preview` for TTS; `gemini-3-pro-image` for the hero. Set `thinkingLevel` explicitly per call. Do NOT wire `gemini-3.5-flash` — pre-GA until June 2026. - **PDF handling:** client-side render via `pdf.js`. Page- text extraction is done client-side via `pdf.js` and the extracted text is included in the server payload alongside the multimodal PDF input, so the verbatim- quote validator has the source-of-truth text. For PDFs > 20 MB, upload to Firebase Storage and then server-side re-upload to the Gemini Developer API Files API; pass the resulting `files/*` resource name via `fileData.fileUri` to `generateContent`. Encrypted PDFs decrypted client-side before upload. - **Local save fallback (FSA non-Chromium):** the "save summary as PDF / markdown" affordance uses `showSaveFilePicker()` on Chromium; on Safari / Firefox, fall back to an `` element pointed at a blob URL — FSA `showSaveFilePicker()` is not supported there. - **Database:** Firestore (auto-provisioned by AI Studio Build). Signed-in mode only. - **Auth:** Firebase Auth — Google sign-in by default; Apple sign-in optional; magic-link email for share links. - **Storage:** Firebase Storage for PDFs > 20 MB and signed-in saved documents. Pre-signed URLs only. Session-only PDFs stored under a `session/` prefix with a 24-hour Lifecycle rule. - **Streaming:** Gemini SDK streaming on the main brief call so the reader sees the summary land while clauses are still being drafted. - **Mobile-first.** Verify at 375 px (iPhone SE), 768 px (iPad), 1024 px, 1440 px+. - Use `clamp()` for fluid typography. Prefer container queries over media queries for component-level responsiveness. - Use `dvh` / `svh` instead of `vh`. Respect safe-area insets on iOS. - Zero horizontal overflow at any width. Zero layout shift on load. - Persist user data in Firestore (signed-in mode). Real- time listeners on the saved-library view. - Optimistic UI on saves; reconcile on response. - Side-by-side PDF + brief view collapses to a stacked layout below 768 px, with the brief on top and the PDF pages accessible via a tappable "Open page 11" chip. ## 12. Accessibility (WCAG 2.2 AA) - Semantic HTML — `header`, `nav`, `main`, `section`, `article`, `footer`. - All interactive controls reachable by keyboard with a visible focus ring. - Color contrast ≥ 4.5:1 for body, 3:1 for large text and UI components. Red-flag severity stripes meet 3:1 against the paper-white background. - All images have meaningful `alt` text. The hero's `alt` describes the artefact ("photographed printed contract on a wooden desk at golden hour, page number 11 of 47 visible in the margin, white coffee mug at the edge of frame"). - Form fields have associated `