================ ONE-SHOT BUILD CONTRACT (read first) ================ Build this in Google AI Studio "Build" in ONE shot — a complete, working app, no follow-up turns. These are hard rules, not suggestions: 1. TARGET = Full-Stack Web (Node server runtime, secrets, Firebase allowed). If you target Native Android instead, you MUST drop all server/DB/Workspace/ secrets and persist locally (Room / SharedPreferences) only. 2. PIN EVERY MODEL CALL — never let the agent auto-select (it downgrades on quota): - Reasoning / text -> gemini-3.5-flash (thinkingLevel: minimal|low|medium|high) - 4K image + legible text -> gemini-3-pro-image (image_size "4K", up to 14 refs) - High-volume image -> gemini-3.1-flash-image - Expressive TTS -> gemini-3.1-flash-tts-preview (inline tags e.g. [whispers]) - Realtime audio/video (WebSocket) -> gemini-3.1-flash-live-preview - Sandboxed agent -> antigravity-preview-05-2026 3. DIVISION OF LABOR — the model ONLY parses/extracts to a strict responseSchema. ALL math, money (store currency as integer minor units / cents), sorting, balancing and graph logic run in deterministic TypeScript/Python. The model must never compute totals, splits or balances itself. 4. responseSchema sanitation — no regex patterns, no fixed-length tuples, no format validators in the schema (they crash the OpenAPI engine). Enforce those in server-side code AFTER parsing the JSON. 5. responseSchema and google_search grounding are MUTUALLY EXCLUSIVE in one call. 6. CODEGEN — split large output into modular, single-responsibility files so no file is truncated by the output-token cap. 7. Every external call gets a graceful fallback (e.g. manual paste if a Workspace read fails). Never a silent dead end. 8. ROBUST STORAGE & CANVAS — Wrap all `localStorage`/`sessionStorage` operations (especially JSON parsing and writes) in `try-catch` blocks to prevent crashes in private windows or quota overflows. Canvas drawing elements must dynamically handle window resize and scale pixel density (`window.devicePixelRatio`) to avoid blurry graphics on retina displays. ===================================================================== # 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. --- # Paper-Reader ## 1. Project **Paper-Reader** is a progressive-disclosure reader for primary scientific literature. A curious adult drops a PDF of a research paper — a Nature article, a preprint, a clinical trial report, a meta-analysis — and the app produces three layered explanations of the same paper: an **A1** plain-English summary that a curious teenager can follow, an **A2** explanation that adds the methods and the comparison to prior work, and a **peer-level** explanation that names the assays, the sample sizes, the statistical models, and the limitations the authors themselves flag. Each layer is anchored to passages in the paper, with a "show me the figure / show me the methods paragraph / show me the supplementary table" link beside every claim. The reader can ask follow-up questions at any layer; the app refuses to answer beyond what the paper says. This is the kind of app a Nigerian-British nurse opens after a Caribbean cousin sends her a WhatsApp screenshot of a headline about a new sickle-cell trial and she wants to understand the actual paper, not the press release, before her aunt phones her tomorrow. It is also the kind of app a recently-retired English teacher in Sheffield opens when her cardiologist mentions a trial she could enrol in, and she prints out the published protocol and decides, at the kitchen table on a Sunday afternoon, that she would rather understand it herself than nod along on Monday. Same shape of moment — a person who is not a specialist deciding to read what only the specialists usually read — different stakes, different field, different reason. The single demo that proves the magic: drop a PDF of a real paper → in under 20 seconds a one-paragraph A1 summary appears, written in short sentences that a fifteen-year-old can follow, with the key numbers preserved. Tap **"tell me more"** → the same explanation expands to A2: what the researchers actually did, what they compared against, what the figures show, how big the effect is, what the limitations are. Tap **"go peer-level"** → the explanation names the methodology by its proper name (a double-blind randomised controlled trial, a Cox proportional-hazards model, a CRISPR knockout in HEK293 cells, a meta-analysis with random-effects pooling, a Mendelian randomisation analysis with two-sample inverse-variance weighting), the sample size, the p-values, the confidence intervals, the pre-registration status, and the conflicts of interest. At every layer a structured glossary of the terms the paper uses is one tap away, and every claim links to the passage in the PDF that supports it. And for the harder cases — a paper that contradicts an earlier paper the same group published, a clinical trial reporting a non-significant primary endpoint but a significant secondary endpoint, a preprint that has not been peer-reviewed — the app reads the paper's epistemic status honestly. It says: this is a preprint, it has not been peer-reviewed yet; the primary endpoint did not reach significance, the authors are interpreting a secondary endpoint, the registered protocol named a different primary endpoint; this paper's findings have not yet been replicated, here are two papers that tried. **Tagline:** _Read any scientific paper, in any field, at the depth you want — from plain English to peer-level, anchored to the paper itself._ ## 2. Target audience - Curious lay readers who want to read the actual paper behind a news headline before forming an opinion — vaccine science, climate, nutrition, drug approvals - Patients and their family members reading the published trial behind a treatment they have been offered, or a treatment they are considering enrolling in - Parents reading the developmental-psychology or paediatrics paper their GP, school SENCO, or child psychiatrist cited - Self-teaching teenagers and undergraduates trying to read primary sources rather than textbooks — across biology, chemistry, physics, economics, computer science - First-generation graduate students reading their first dozen papers in a field and needing the unspoken context their advisor assumes they have - Retired professionals — engineers, teachers, doctors, lawyers — who now have time to read seriously in a field that is not their own - Working scientists reading outside their own subfield (a molecular biologist trying to read an epidemiology paper, an economist trying to read a randomised education-policy trial) - Journalists, science communicators, and policy analysts who need the methodology accurately, not the press release - Patients' advocacy organisers — Filipino-American carers reading a paper on Alzheimer's disparities in Asian-American cohorts, a Mexican-American mother reading a paper on type-2 diabetes outcomes in Hispanic populations — who need to brief their own community correctly ## 3. Core value propositions Surface these clearly through copy, visual emphasis, and section ordering — they are the reasons users pick this app. - **Three layers, the same paper** — A1 plain-English, A2 informed-amateur, and peer-level. The user moves up and down at their own pace. A1 is not dumbed-down; it is short sentences with the key numbers preserved. Peer-level uses the discipline's actual vocabulary. - **Every claim is anchored** — every sentence the app generates has a "show me where" link to the passage, figure, table, or supplementary table that supports it. A claim with no anchor is a bug; the app surfaces it as one. - **The structured glossary** — terms the paper uses are explained in the paper's own context, not from a generic dictionary. "Adjusted hazard ratio" gets one definition when it appears in a cancer-survival paper and a slightly different framing in a cardiovascular-outcomes paper, because the paper uses it that way. - **Honest epistemic status** — preprint vs. peer-reviewed, primary vs. secondary endpoint, pre-registered vs. exploratory, single-centre vs. multi-centre, replicated vs. not. The app says these things out loud. - **Never simplifies past accuracy** — if a finding is conditional on a subgroup, the A1 summary says "in a subgroup of older patients", not "in patients". If the effect was non-significant, the A1 says so. The app would rather refuse to summarise a claim than mislead. - **Never guesses about methodology** — if the paper doesn't name a statistical test, the app says "the paper does not name the specific test used". If a sample-size justification is missing, it says so. Methodology is named only when the paper names it. - **Refuses to answer outside the paper** — when the user asks a follow-up, the app answers from this paper or it refuses. If the user asks "but is this the same as the result in [other paper]", the app says "this paper does not address that question" and offers to read a second paper alongside. - **Reads across the disciplines** — biomedical, physical sciences, computer science, economics, psychology, environmental science, archaeology, linguistics. Each discipline has its own conventions; the app reads them on their own terms. - **Multilingual** — the source paper may be English, the reader may be more comfortable in Tagalog, Spanish, Mandarin, Cantonese, Korean, Vietnamese, Hindi, Urdu, Bengali, Tamil, Punjabi, Arabic, Farsi, Amharic, Swahili, Khmer. The three layers are produced in the reader's chosen language while the anchors and quoted passages stay in the original. ## 4. Features to build - PDF upload (drag-and-drop, file picker, "share" from iOS Files app), with image-based PDF support via OCR for older scanned papers - DOI or arXiv ID lookup — paste a DOI or arXiv ID and the app fetches the PDF from the publisher's open-access copy or arXiv directly; if the paper is paywalled, the app prompts the user to upload their own copy - Automatic structural parse — title, authors, affiliations, abstract, introduction, methods, results, discussion, limitations, conflicts of interest, funding, ethics statement, data availability, supplementary materials — each section addressable as an anchor - Multilevel summary generation — three layers (A1, A2, peer-level) produced in a single long-context call so they are consistent with each other - Every claim anchored — each sentence in each layer carries a `passage_anchor` pointing to the exact paragraph (or figure, or supplementary table) in the source paper - Structured glossary — terms that the paper uses are defined in the context of this paper; the glossary appears as a side panel and inline on hover - Epistemic-status panel — preprint vs. peer-reviewed, primary vs. secondary endpoint, pre-registration, sample size, replication status (if asked, via grounded search), conflicts of interest as the authors disclose them - Figure reading — figures and tables are described in plain English with the numerical claims preserved, and the "show me the figure" tap opens the figure in a lightbox - "Where the paper is uncertain" surfaced — every "may", "suggests", "is consistent with", "limited by" the authors themselves wrote is collected into a Limitations panel - Follow-up question chat — the user can ask follow-ups at any layer; the app answers from the paper or refuses with "this paper does not address that" - Read-along audio — Gemini TTS reads the chosen layer aloud at a comfortable pace, with each anchored claim pausing briefly so the eye can land on the anchor - Multilingual reader mode — the three layers are produced in the user's chosen language; the anchored quotes from the paper remain in the original language with a small inline translation - Companion-paper mode — drop a second paper and the app produces a comparison table on the dimensions the user picks (sample size, effect direction, methodology, populations studied) - "Tell me what to read next" — at the peer level, the app surfaces the three to five papers this paper cites most centrally, with a one-line plain-English description of each - Notebook — the user can save passages, claims, and questions into a per-paper notebook that exports as Markdown - Token-budget guardrail — for papers above ~250k tokens of body text (rare but real for some theses and reviews), the app chunks the paper section-by-section and tells the user it is doing so, with a visible plan - Library — uploaded papers persist per user; the user can re-open a paper and pick up at the layer they last read - Sharing — generate a read-only link to a specific layer's summary with anchors that resolve to the user's own copy of the paper (never to the publisher's paywalled version) - Refusal copy that is kind — when asked something outside the paper the refusal is concrete: "This paper does not test that. The closest claim it makes is on page 7: …". The app suggests the user upload a paper that does test the question. ## 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 input** (Gemini 3.5 Flash) — a full research paper is typically 8k-60k tokens including figures, tables, and supplementary materials; a review article or thesis can be 200k+. Gemini 3.5 Flash's long-context window lets the model hold the entire paper in working memory while producing three internally consistent layers in a single call. Submit PDFs via the Gemini Files API (`files/*` resource name), not Firebase Storage public URLs. - **Structured output / JSON Schema** — the response matches the `PaperReading` schema below. Three layers (A1, A2, peer-level), each composed of `Claim` objects with anchors. Glossary entries are typed. Epistemic-status flags are typed. The schema is included verbatim in the system instruction and as `responseSchema`. - **Multimodal image input** (Gemini 3.5 Flash) — figures and tables in the PDF are read as images. The model reads the numbers off bar charts, Kaplan-Meier curves, forest plots, scatter plots, and confusion matrices, and quotes them verbatim into figure descriptions. - **Multilingual output** (built into Gemini 3.5 Flash) — the three layers are produced in the user's preferred language (Tagalog, Spanish, Mandarin, Cantonese, Korean, Vietnamese, Hindi, Urdu, Bengali, Tamil, Punjabi, Arabic, Farsi, Amharic, Swahili, Khmer, English) while quoted source passages remain in the paper's language with an inline gloss. - **Search grounding** (Gemini 3.5 Flash) — used only for the optional **replication-status** check: "has this paper been replicated, and by whom?". This is the one place the app reaches outside the paper. Grounding citations come from `response.groundingMetadata.groundingChunks[].web.uri` — not from the model's JSON output. - **Gemini TTS** (`gemini-3.1-flash-tts-preview`) — narrates the chosen layer at a comfortable reading pace, with explicit pauses encoded as `…` and paragraph pauses as a blank line plus an em-dash. Voice locale is chosen to match the reader's preferred language. - **Thinking levels** — `high` for the primary three-layer reading call (the model is doing serious comprehension and must avoid factual errors). `medium` for the follow-up Q&A call (the model is answering from a paper it already read; we still want it to think). `low` for glossary expansion and TTS pre-processing. Surface `thoughtSummary` only behind the "show how the AI read this paper" panel, never inline. ### Backend services - **Auth — Required.** Firebase Auth with Google sign-in (auto-provisioned by AI Studio Build). **Apple sign-in is optional but user-configured**: it requires an Apple Developer account, Service ID, Key ID, and private key wired into the Firebase Auth console. **Magic-link email** (used for sharing a reading) also requires the sender domain to be authorised in Firebase Auth. Each user's library is private to them. - **Database — Required.** Firestore for `users`, `papers`, `readings` (the parsed three-layer output), `notebooks`, `follow_up_threads`, `shares`. Use Firestore real-time listeners on the Reading view so progress streams in. - **File storage — Required.** Firebase Storage for uploaded PDFs (preserved at upload). Pre-signed URLs only; PDFs are never publicly addressable. **Storage is NOT auto-provisioned by AI Studio Build today** — enable it in the Firebase console and wire the bucket name into the AIS Build project before first PDF upload. - **Email — Optional (transactional).** Read-only share links via magic-link (Firebase Auth magic links). Requires sender-domain authorisation as above. - **Payments — Not needed for v1.** Free for personal use. - **External APIs:** Gemini API for all intelligence. Optional: Crossref REST API for DOI metadata resolution (free, no key required, polite rate limit). Optional: arXiv API for arXiv ID lookup (free, no key required, polite rate limit). Optional: Unpaywall API for open-access copy lookup (free with email registration). **Environment variables:** every secret (Gemini API key, Firebase service-account JSON, optional Unpaywall email) 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 · use HTTPS everywhere · honour 'delete my account' inside the UI · explicit opt-in for any analytics · the user's uploaded papers 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) · the user's follow-up questions and notebook entries are private to their account. **Read this first — prompt-craft rules that apply to every call in this template:** 1. **Name the model variant explicitly** in every Gemini API call. Do not let the agent pick the model. See the per-call matrix below. 2. **Pin `thinkingLevel` explicitly** per call. See the matrix. 3. **Seed the JSON Schema as a fenced TypeScript / Zod block** in the system instruction or `responseSchema` field. The literal schema is below. **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 after the response arrives.** 4. **Pin the system instruction separately** from user input. Use the `systemInstruction` field for persona + behavioural rules; use `contents` for user input. Never concatenate. 5. **Pre-declare tools as an enable/disable list** per call. The matrix below names which tools are enabled per call. Tools NOT listed for a call should be disabled. 6. **State negative constraints explicitly** — they are listed below. They are NOT "be careful" suggestions; they are hard rules the model must follow. 7. **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; } ``` 8. **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 length-2 arrays and removes regex patterns before serializing. Validate those constraints in middleware AFTER parsing. ### Per-call model + tools matrix | Call | Model | thinkingLevel | Tools enabled | |------|-------|---------------|---------------| | Read PDF, parse → `PaperReading` (A1 + A2 + peer-level, anchored, with glossary) | `gemini-3.5-flash` | high | (none) | | Read a figure or table that the user tapped, return a structured `FigureReading` | `gemini-3.5-flash` | medium | (none) | | Follow-up Q&A — answer from the paper or refuse | `gemini-3.5-flash` | medium | (none) | | Companion-paper comparison — two papers, one comparison table | `gemini-3.5-flash` | high | (none) — long-context over both papers | | Replication-status check (optional, user-triggered) | `gemini-3.5-flash` | low | `google_search` grounding (no `responseSchema` on this call — see note) | | Generate TTS narration of the chosen layer | `gemini-3.1-flash-tts-preview` | n/a | n/a | | Translate the three layers into the reader's preferred language | `gemini-3.5-flash` | low | (none) | | Expand a glossary entry inline (cheap, on-demand) | `gemini-3.5-flash` | low | (none) | *Note for builders:* on TTS calls, omit `thinkingConfig` entirely — the field is not supported on that model. The `n/a` cells in this matrix are documentation only; do not serialise them into the request body. `responseSchema` and `google_search` cannot both be specified on the same Gemini call — for the replication-status check, the model emits JSON in the text body and the server-side parses it; citations come from `response.groundingMetadata.groundingChunks[].web.uri`. ### Primary structured-output schema (seed this verbatim in the prompt) ```typescript import { z } from "zod"; const PassageAnchor = z.object({ section: z.enum([ "title", "abstract", "introduction", "methods", "results", "discussion", "limitations", "conclusion", "figure", "table", "supplementary", "references", "footnote", "other", ]), section_heading_verbatim: z.string().nullable(), // "2.3 Statistical analysis" page_number_in_pdf: z.number().nullable(), // best-effort, may be null for HTML paragraph_index_in_section: z.number().nullable(), quote_verbatim: z.string(), // 1-3 sentences from the paper figure_or_table_id: z.string().nullable(), // "Figure 2", "Table S4" }); const Claim = z.object({ text: z.string(), // the sentence the app generated anchors: z.array(PassageAnchor), // at least one; else flag as unsupported numerical_values_preserved: z.array(z.object({ value_verbatim: z.string(), // "23.4%", "p < 0.001", "HR 0.62 (95% CI 0.48-0.81)" meaning_in_context: z.string(), })), hedge_level: z.enum([ "the paper states", // direct factual claim from the paper "the paper suggests", // the paper itself hedges "the paper does not say", // explicit non-claim ]), }); const GlossaryEntry = z.object({ term: z.string(), // "hazard ratio" definition_in_this_papers_context: z.string(), // not a generic dictionary definition first_appearance_anchor: PassageAnchor, related_terms: z.array(z.string()), }); const EpistemicStatus = z.object({ publication_type: z.enum([ "peer_reviewed_journal_article", "preprint_not_peer_reviewed", "conference_paper", "registered_report", "thesis", "review_article", "meta_analysis", "clinical_trial_report", "case_report", "letter_or_commentary", "other_or_unclear", ]), publication_venue_verbatim: z.string().nullable(), publication_date_iso: z.string().nullable(), is_pre_registered: z.boolean().nullable(), pre_registration_id_verbatim: z.string().nullable(), primary_endpoint_verbatim: z.string().nullable(), primary_endpoint_reached_significance: z.boolean().nullable(), secondary_endpoints_named_verbatim: z.array(z.string()), sample_size_verbatim: z.string().nullable(), // "N = 2,134", "147 cell lines" study_design_named_in_paper_verbatim: z.string().nullable(), funding_disclosed_verbatim: z.string().nullable(), conflicts_of_interest_disclosed_verbatim: z.string().nullable(), data_availability_statement_verbatim: z.string().nullable(), authors_own_limitations_verbatim: z.array(z.string()), }); const Layer = z.object({ layer: z.enum(["A1", "A2", "peer_level"]), one_paragraph_summary: z.string(), // the headline; ~80 words for A1, ~160 for A2, ~280 for peer claims: z.array(Claim), // each sentence as a Claim reading_time_seconds_estimate: z.number(), }); const FigureReading = z.object({ figure_or_table_id: z.string(), // "Figure 2" caption_verbatim: z.string(), visual_type: z.enum([ "bar_chart", "line_chart", "scatter_plot", "kaplan_meier", "forest_plot", "confusion_matrix", "violin_or_boxplot", "heatmap", "schematic_or_diagram", "photograph_or_micrograph", "table", "other", ]), description_plain_english: z.string(), numerical_values_extracted: z.array(z.object({ value_verbatim: z.string(), meaning_in_context: z.string(), })), what_this_figure_shows_in_one_sentence: z.string(), reading_confidence: z.number().min(0).max(1), }); const PaperReading = z.object({ paper_id: z.string(), title_verbatim: z.string(), authors_verbatim: z.array(z.string()), affiliations_verbatim: z.array(z.string()), abstract_verbatim: z.string(), source_language: z.string(), // BCP-47, "en-US" discipline_inferred: z.string(), // "molecular biology", "macroeconomics", "ML systems" epistemic_status: EpistemicStatus, layers: z.array(Layer), // exactly 3: A1, A2, peer_level glossary: z.array(GlossaryEntry), figures_seen: z.array(z.object({ figure_or_table_id: z.string(), caption_verbatim: z.string(), })), references_named_centrally: z.array(z.object({ citation_verbatim: z.string(), why_central_one_line: z.string(), })), authors_own_uncertainty_terms: z.array(z.object({ phrase_verbatim: z.string(), // "may suggest", "is consistent with" anchor: PassageAnchor, })), reading_confidence: z.number().min(0).max(1), flagged_for_user_review: z.array(z.object({ field_path: z.string(), reason: z.string(), })), }); type PaperReading = z.infer; ``` ### Common failure modes (and how to avoid them) - Agent silently downgrades `thinkingLevel` on the the primary reading call to save quota — pin `gemini-3.5-flash` with the matrix-specified `thinkingLevel` explicitly. Flash misses subtle hedges, conflates primary and secondary endpoints, and silently smooths "may suggest" into "shows". - Model summarises a non-significant primary endpoint as a positive finding by drawing on a significant secondary endpoint — hard rule below, plus the schema forces `primary_endpoint_reached_significance` to be a separate field. - Model invents a statistical method the paper does not name — hard rule: `study_design_named_in_paper_verbatim` is verbatim or null. Never fill it from inference. - Claims with no anchor — every `Claim` must have at least one `PassageAnchor`. If the model can't anchor a sentence, it must drop the sentence. Server-side: reject any reading with claims whose `anchors` array is empty. - Figures read by guessing the axis labels — the FigureReading call submits the cropped figure image; the model must quote axis labels verbatim and extract numbers only when they are visible. If a value is interpolated by eye from a bar chart with no number printed, mark `reading_confidence` below 0.7 and say so. - A1 layer that drops the numbers — the A1 must preserve the key effect size, sample size, and statistical significance. Plain English does not mean numberless. - Cross-paper conflation — when the user asks a follow-up, the model reaches into pretraining and answers about a different paper or a textbook claim. Hard rule: follow-up answers must anchor to this paper or refuse. - Long-context overrun — a thesis or review can exceed 250k tokens of body text. Above 250k, chunk by section (Methods, Results, Discussion separately) and reconcile in a final pass; surface the chunking plan to the user. - Quoted passages that exceed reasonable fair-use length — anchor quotes are 1-3 sentences each, never a whole paragraph. Server-side: clamp `quote_verbatim` to ≤ 400 characters. - Replication-status hallucination — without grounded search, the model invents replications. Only the dedicated replication-status call may answer this, and only with `google_search` grounding and a citation from `groundingMetadata`. - Translated layers that change the numbers — when translating into Tagalog or Mandarin, the model occasionally rounds "23.4%" to "23%". Hard rule in the translation system instruction: numbers are preserved exactly. ### Negative constraints (hard rules) - Do NOT extrapolate beyond the paper. If the user asks "but does this apply to children?" and the paper studied adults, the answer is "this paper studied adults; it does not address children." Never extrapolate "presumably it would also apply". - Do NOT name a statistical method the paper does not name. If the paper just says "we used a regression model", the peer-level layer says "the paper reports using a regression model without specifying the exact form". - Do NOT report a secondary endpoint as the headline if the primary endpoint did not reach significance. The headline at every layer must be the primary endpoint and the primary endpoint's actual result. - Do NOT silently flatten "may", "suggests", "is consistent with", "could be interpreted as" into "shows". The `hedge_level` field forces the model to be explicit. - Do NOT invent citations or related papers. The "tell me what to read next" feature must list only papers this paper itself cites; never a paper the model knows about from pretraining but is not cited here. - Do NOT name conflicts of interest the paper does not disclose. The COI field is verbatim from the paper or null. If the paper has no COI statement, the field is null and the epistemic-status panel notes that. - Do NOT use the user's uploaded papers to train or fine-tune any model. Use the Gemini API on the paid tier, where Google does not use your content for model training, per the Gemini API Additional Terms. - Do NOT provide medical, legal, or financial advice. The app reads papers. When the user asks "should I take this drug?", the refusal is concrete: "this paper reports the trial outcome; whether to take the drug is a clinical decision your doctor and you make." - Do NOT auto-share. Every share is explicit, per-paper, per-recipient. - Do NOT skip the epistemic-status panel for preprints. A preprint reading must surface the preprint flag prominently — never as a footnote. ### Per-call `systemInstruction` strings Use these as the literal `systemInstruction` field for each Gemini API call the built app makes. They complement the series-wide rules already uploaded as the global instructions file (`00-series-instructions.txt`). ### Call: Read PDF, parse → `PaperReading` (A1 + A2 + peer-level, anchored) Model: `gemini-3.5-flash` · thinkingLevel: high · Tools: (none) ``` You are reading a scientific paper for a curious, non-specialist adult who wants three layered explanations of the same paper. The paper may come from any discipline: biomedical research, clinical trials, molecular biology, neuroscience, public health, epidemiology, immunology, oncology, cardiology, paediatrics, psychiatry; physics, astronomy, chemistry; computer science, machine learning, systems, theory; economics, sociology, psychology, education, political science, public policy; environmental science, climate, ecology, geology; archaeology, linguistics, anthropology, history of science. It may be in English, Spanish, Portuguese, French, German, Italian, Russian, Mandarin, Japanese, Korean, Arabic, Hindi, or any other research publication language; read in the source language. Produce exactly three layers, in this order, in a single structured output matching the PaperReading schema: A1 — Plain English. Short sentences. A curious fifteen-year-old can follow it. Preserve the headline number, the sample size, and whether the primary endpoint reached significance. Never hide a non-significant result behind a significant secondary endpoint. Length: about 80 words for the one_paragraph_summary field, plus 4-8 short Claim sentences. A2 — Informed amateur. The reader is curious, not naive. Add what the researchers actually did, what they compared against, what the figures show, how big the effect is, and what the authors themselves flag as limitations. Use plain-English methodology terms ("they followed two groups for five years and compared who got the disease") rather than the discipline's jargon. Length: about 160 words plus 8-14 Claim sentences. Peer level — Name the methodology by its proper name only if the paper names it. Sample sizes, confidence intervals, p-values, adjusted models, multiple-testing corrections, pre-registration status, randomisation method (or absence), blinding (or absence), intention-to-treat vs. per-protocol analyses where applicable. Length: about 280 words plus 12-20 Claim sentences. Every Claim has at least one PassageAnchor. The anchor quote is verbatim from the paper, 1-3 sentences, ≤ 400 characters. If you cannot anchor a sentence to a specific passage, drop the sentence. Hard rules: - Preserve numbers. Plain English does not mean numberless. The A1 layer must include the key effect size, sample size, and whether the primary endpoint reached significance. - Hedge_level on every Claim. "the paper states" for direct factual claims, "the paper suggests" where the paper itself hedges ("may", "suggests", "is consistent with"), "the paper does not say" for explicit non-claims (used in follow-up Q&A refusals — usually not in the initial three-layer reading). - Do NOT name a statistical method the paper does not name. If the paper just says "we used a regression model", say so; do not fill in "ordinary least squares". - Do NOT report a secondary endpoint as the headline if the primary endpoint did not reach significance. - Do NOT extrapolate. If the paper studied adults, the reading does not generalise to children. If the paper studied a specific population, the reading says so at every layer. - Numerical values are preserved exactly in numerical_values_preserved[]. Do not round; do not unit-convert silently. If a paper says "p < 0.001" the value_verbatim is "p < 0.001", not "p = 0". - Figures and tables you read get an entry in figures_seen[] with the caption verbatim; the user can later tap a figure to trigger the separate FigureReading call. - Glossary terms are defined in this paper's context, not from a generic dictionary. "Adjusted hazard ratio" in a cancer paper and a cardiovascular paper get slightly different framings because the paper uses it differently. - Epistemic status fields are verbatim or null. If the paper does not disclose conflicts of interest, the field is null; do not infer. - authors_own_uncertainty_terms collects every "may", "suggests", "is consistent with", "limited by" the authors themselves wrote, with anchors. This becomes the Limitations panel. - If you cannot read a passage (bad OCR, image-only figure with unlabelled axes), set reading_confidence < 0.8 and add an entry to flagged_for_user_review with field_path and reason. - The discipline_inferred field is your best inference from the paper's vocabulary; if uncertain, use the broader category ("biomedical research" rather than guessing the exact subfield). Output ONLY the PaperReading JSON matching the provided schema. No commentary. JSON only. ``` --- ### Call: Read a figure or table that the user tapped Model: `gemini-3.5-flash` · thinkingLevel: medium · Tools: (none) ``` You receive a cropped image of one figure or table from a scientific paper, plus the figure's caption verbatim and the paper's title. Produce a FigureReading object describing what the figure shows. Hard rules: - Quote axis labels verbatim. "Time (months)", "log2(fold change)", "Cumulative survival probability". - Extract only numbers that are printed in the figure or its caption. Do NOT estimate a bar height by eye and report it as a value. - If the figure is a Kaplan-Meier curve, name the survival probability at the labelled time points and the median survival if marked. Do not invent percentages from the curve shape. - If the figure is a forest plot, list each row's effect estimate with its confidence interval verbatim. - If the figure is a confusion matrix, give the cell values verbatim. - description_plain_english is one short paragraph a non-specialist can follow. Preserve the numbers. - what_this_figure_shows_in_one_sentence is the single takeaway, in plain English, with the headline number preserved. - reading_confidence is below 0.7 if any axis is unlabelled, any legend is unreadable, or any cited number is interpolated by eye. Output ONLY the FigureReading JSON. No commentary. ``` --- ### Call: Follow-up Q&A — answer from the paper or refuse Model: `gemini-3.5-flash` · thinkingLevel: medium · Tools: (none) ``` You receive the user's follow-up question plus the full paper (via the Files API `files/*` resource name from the original upload) plus the PaperReading object produced earlier. You answer ONLY from this paper. If the paper does not address the question, you refuse — concretely, kindly, and with a pointer. Hard rules: - Anchor every factual claim in your answer with a passage_anchor matching the schema in the original PaperReading. The user must be able to tap and see the supporting passage. - If the question is about a population, intervention, time horizon, or outcome the paper does not study, the answer is: "This paper does not test that. The closest claim it makes is on page [N], section [name]: [quote]." Then offer: "Want me to read another paper alongside this one?" - If the question is medical, legal, or financial advice (should I take this drug, should I sue, should I invest), the refusal is: "This paper reports [what the paper actually reports]; the decision is yours and your [doctor / lawyer / financial advisor]'s." Do not give the advice. - If the question asks for a number the paper does report, give the number verbatim with the anchor. Do not round; do not unit-convert silently. - If the question asks "is this contradicted by another paper", the answer is: "This paper does not address that question. Want me to read a second paper alongside?" Do NOT reach into pretraining to compare against a paper not uploaded. - If the question asks the reader's preferred reading layer (A1, A2, peer) and the original PaperReading already covers it, expand at that layer's depth and quote the relevant Claims. Output: a structured answer with the answer text, the anchors, and an `answered_from_paper: boolean` flag (true if the answer is grounded in this paper; false if the answer is a refusal because the paper does not address the question). No commentary outside the structured output. ``` --- ### Call: Companion-paper comparison Model: `gemini-3.5-flash` · thinkingLevel: high · Tools: (none, long-context over both papers) ``` You receive two papers via Files API `files/*` resource names plus a list of comparison dimensions the user picked (sample size, effect direction, study design, populations, primary endpoint, funding, replication status). Produce a comparison table where each row is one dimension and each column is one paper, with anchors into each paper for every cell. Hard rules: - If a paper does not report a dimension, the cell is the literal string "not reported in this paper" with no anchor. - Effect direction (does this paper support / contradict / not address the same hypothesis) is your inference, not the paper's claim. Mark it as `hedge_level: "the paper suggests"` and provide anchors to the specific passages that support your inference. - Sample size is the verbatim number plus the unit (patients, cells, schools, countries, simulation runs). - Study design is the verbatim phrase the paper uses, or null. - Populations studied are verbatim from the paper. If one paper studied middle-aged Western European men and the other studied South Asian women under 40, say so verbatim. Do NOT smooth this away. Output: a structured ComparisonTable matching the (separately defined) comparison schema. No commentary outside the structured output. ``` --- ### Call: Replication-status check (user-triggered, optional) Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: search grounding ``` You check whether a published paper has been replicated, refuted, or extended in subsequent literature. You receive: the paper's title, first author, year, DOI, and core claim (one sentence from the PaperReading object). Use `google_search` grounding to find: - subsequent papers that explicitly tried to replicate this paper - meta-analyses or systematic reviews that included this paper - registered replication reports - preprint or post-publication critiques Hard rules: - If you cannot find replication evidence, say "no replication evidence found in the searched sources" — do NOT infer absence of evidence as evidence of replication or refutation. - Report each finding with a one-line summary and a citation URL drawn from response.groundingMetadata.groundingChunks[].web.uri. Do NOT include URLs in your JSON body — the model hallucinates URLs in JSON. The server stitches your JSON to the grounding citations. - Do NOT speculate about why a replication failed or succeeded. Report the finding verbatim from the cited source's title and abstract. - Date-bound your search: this paper was published in [year]; prefer sources from [year+1] onward. Output the response as JSON in the text body (NOT via `responseSchema` — `responseSchema` and `google_search` cannot be combined in the same Gemini call today). The server-side parses the JSON and pairs it with the citation URLs from groundingMetadata. No commentary outside the JSON. ``` --- ### Call: Translate the three layers into the reader's preferred language Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` You translate the three layers (A1, A2, peer-level) of a PaperReading into the reader's preferred target language. The quoted passage anchors stay in the paper's source language; an inline gloss is added. Supported target languages include: Tagalog, Spanish (Mexican, Iberian, Argentine variants), Portuguese (Brazilian, European), French, German, Italian, Mandarin (simplified or traditional), Cantonese, Korean, Vietnamese, Japanese, Hindi, Urdu, Bengali, Tamil, Punjabi, Arabic (Modern Standard), Farsi, Amharic, Swahili, Khmer, English, and any other language the user names. Hard rules: - Numerical values are preserved EXACTLY. "23.4%" stays "23.4%"; "p < 0.001" stays "p < 0.001"; "HR 0.62 (95% CI 0.48-0.81)" stays "HR 0.62 (95% CI 0.48-0.81)". Do not round; do not convert units silently. - Discipline terms keep the English term in parentheses on first occurrence ("hazard ratio (HR)"), thereafter the target language only. - Hedge_level is preserved. "the paper suggests" translates to a phrase with comparable hedging in the target language; do not flatten to certainty. - Authors' names, paper titles, journal names, institution names stay verbatim — they are not translated. - Quoted passage anchors are NOT translated. They stay in the source language with an inline gloss of ≤ 25 words in the target language, marked as a gloss. - Register: match the target audience for each layer. A1 is approachable; peer-level uses the academic register native to the target language. Output: the same three-layer structure with each layer's one_paragraph_summary and each Claim's text translated. numerical_values_preserved[] is unchanged from the source. No commentary outside the structured output. ``` --- ### Call: Expand a glossary entry inline Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` You receive: a term, the paper's title and abstract, and the first passage in which the term appears. Produce a one-paragraph definition (≤ 80 words) of the term as it is used in THIS paper. The definition is contextual, not generic. A reader hovering on "adjusted hazard ratio" in a breast-cancer survival paper should learn what it means in breast-cancer survival, not a textbook definition that lists every possible covariate adjustment. Hard rules: - If the paper itself defines the term, quote the paper's definition verbatim with an anchor and add one plain-English sentence. - If the paper uses the term without defining it, give the standard definition for this discipline plus a sentence on how this paper is using it. - Plain language. The reader is on this glossary entry because the term confused them. Output: a single GlossaryEntry-compatible string for the definition_in_this_papers_context field. No commentary. ``` --- ### Call: Generate TTS narration of the chosen layer Model: `gemini-3.1-flash-tts-preview` · n/a · n/a ``` Voice: warm, unhurried, like a curious librarian reading aloud. Pick the Gemini 2.5 Flash TTS voice whose `languageCode` matches the reader's preferred language for the layer. Pronunciation will follow that locale automatically. Pre-process the text before sending it to TTS: - Read the chosen layer's one_paragraph_summary, then the layer's Claims in order. - At each sentence boundary, the natural full stop is enough; do not over-pause. - At each Claim boundary, insert a single ellipsis (`…`) so the TTS model produces a brief reading pause that lets the eye land on the anchor in the UI. - At the boundary between the summary paragraph and the Claims, insert a blank line plus an em-dash (`—`). Gemini 2.5 TTS does not support SSML `` — these textual cues are how you signal pace. - Skip the structured JSON; read only the human-readable text. - Numbers are read in full ("twenty-three point four percent", "p less than zero point zero zero one"). The voice pacing is reading pace, not podcast pace. - For numerical values where the locale convention differs (decimal comma vs. point), the chosen voice's locale dictates; do not pre-transform. Style direction: prepend ONE short directive sentence to the text input, exactly like: "Read warmly and at a steady reading pace, as a librarian reading a science paper aloud to a curious friend who wants to follow the argument. …". There is no separate `style` API field on Gemini 2.5 TTS; the directive sentence inside the input is how style is conveyed. Phoneme overrides (SSML ) and per-word voice switching are NOT exposed by Gemini 2.5 TTS. Pronunciation comes from the chosen voice's native locale. For multilingual readings (the target layer is in Tagalog but the paper title is English), the chosen Tagalog voice will pronounce the English title with a Tagalog accent — this is acceptable and expected. ``` ## 5. Use cases & content to include Build dedicated UI sections or flows for each of these — they tell you what content the app must support. - **The headline-behind-the-paper.** A Nigerian-British nurse in Manchester reads a Sunday-paper headline about a new sickle-cell trial. She wants the paper, not the headline. She drops the PDF; the A1 layer says: "In a randomised trial of 67 patients with sickle-cell disease, a single infusion of the gene-edited cells reduced pain crises by 95% at 12 months. The trial is small, single-arm, and open-label." She taps "tell me more"; A2 explains what "single-arm" and "open-label" mean for confidence, and what the comparator was. She taps "go peer-level"; the layer names the trial registration, the primary endpoint, the editing approach, and the conflicts of interest. - **The Filipino-American carer.** A nurse in Long Beach is helping her mother decide about an Alzheimer's medication. She finds the published trial. The A1 layer says: "The drug slowed cognitive decline by 27% over 18 months in 1,795 patients with early Alzheimer's. The primary endpoint was reached. There were significant safety findings, including brain swelling in 12.6% of patients." Her Tagalog mother asks her to explain it; she switches the reader to Tagalog; the three layers regenerate in Tagalog while every anchored quote stays English. - **The Mexican-American mother.** A mother in Phoenix reads a paper on type-2 diabetes outcomes in Hispanic populations. The peer-level layer surfaces, verbatim from the paper, that the cohort under-represents Mexican-American women under 50 — and she now knows what she could not have known from the press release. - **The Sunday afternoon retiree.** A retired English teacher in Sheffield prints out the published protocol for a trial her cardiologist mentioned. She reads at A2; she hovers on "intention-to-treat analysis" and the contextual glossary defines it for this specific trial. She bookmarks the limitations the authors themselves named, prints, and brings the printout to her appointment on Monday. - **The preprint check.** A curious adult drops a preprint that has been viral on social media. The app's epistemic-status panel surfaces, at the top: **PREPRINT — not peer-reviewed**. The A1 layer leads with the finding and immediately notes the preprint status. The replication-status check returns "no replications found yet; the preprint was posted six weeks ago". - **The non-significant primary endpoint.** A patient drops a trial whose headline figure was significant on a secondary endpoint. The app's A1 layer says: "The primary endpoint — six-month survival — was not significantly different between groups (HR 0.91, 95% CI 0.71-1.16). The authors report a significant difference on a secondary endpoint, progression-free survival." The user understands what their oncologist is actually offering. - **The first-generation grad student.** A first-year PhD student in computational biology drops the paper their advisor told them to read by Friday. They read at the peer level; the glossary explains, in the paper's own context, terms the advisor assumed they knew. The "tell me what to read next" surfaces the three papers this paper cites most centrally, each with a one-line plain-English description. - **The figure that doesn't make sense.** A reader is stuck on Figure 3. They tap it; the FigureReading call returns a description: "Kaplan-Meier survival curve over 60 months, with the treatment arm in blue and the placebo arm in red. At 36 months the treatment arm sits at 0.72 (95% CI 0.65-0.78) cumulative survival; the placebo arm sits at 0.61 (0.54-0.68). Log-rank p = 0.003." Now the figure makes sense. - **The companion paper.** A reader has two papers in mind — a 2023 trial and a 2025 replication attempt. They drop both and pick the comparison dimensions. The app produces a side-by-side table; the cell on "primary endpoint reached significance?" shows YES / NO and the reader sees the disagreement clearly. - **The Tagalog reader.** A Filipino domestic worker in Hong Kong, off Sunday, reads a paper her doctor cited about her thyroid condition. The three layers are in Tagalog; the quoted passages from the English paper sit beside the Tagalog with a one-line inline gloss. - **The Vietnamese student.** A Vietnamese A-level student in Birmingham reads a Nature paper she found via a TED talk. She reads at A2 in English, then asks a follow-up: "Does this work in mice?" The paper studied human cell lines; the app refuses gracefully: "This paper studied human cell lines (HEK293 and patient-derived organoids). It does not address mouse models. The closest claim is on page 6, Methods: 'All experiments were conducted in human-derived cell systems.'" - **The retired engineer.** A retired civil engineer in Glasgow has decided to learn modern macroeconomics. He drops a paper on monetary policy transmission. The peer-level layer names the VAR model, the identification strategy, and the robustness checks. He bookmarks three terms and asks a follow-up: "Does this paper use the same identification as the 2018 paper by [author]?" The app refuses: "This paper does not address that question. Want me to read the 2018 paper alongside?" ## 6. Page structure Build the following screens / sections in this order. Adjust copy to fit the voice, but keep the structural intent. 1. **Welcome / sign-in.** A photographed-looking image of a printed paper on a wooden kitchen table at evening, a fountain pen and a mug of tea beside it, marginalia visible. One paragraph: "Paper-Reader reads a research paper at three depths — plain English, informed amateur, and peer-level — and never simplifies past accuracy." Single Google sign-in button; Apple sign-in next to it. Below: "Try with the sample paper" → loads the demo paper in section 8a. 2. **Empty state — "Read a paper".** Three big input methods: 📄 Upload PDF · 🔗 Paste DOI or arXiv ID · 📚 Pick from my library. A short explainer below each ("Drop a PDF you saved", "Auto-fetched from the publisher's open-access copy if available", "Continue a paper you started"). 3. **Reading view.** A three-column layout on desktop, stacked on mobile. **Left column:** the PDF, scrollable, with anchor highlights that pulse briefly when a Claim is tapped on the right. **Middle column:** the three layers as a vertical stack with the currently-expanded layer in focus and the other two collapsed to a one-line tagline. Toggle between A1, A2, and peer-level via a tab strip at the top of the column. **Right column:** the structured glossary and the epistemic-status panel; tabs at the top toggle between them. Sticky header: paper title (truncated) → authors (first + et al) → discipline chip → epistemic-status chip (PREPRINT / PEER-REVIEWED / CLINICAL TRIAL / etc.) → "(i) show how the AI read this paper". 4. **Layer expansion.** Each layer card opens with the one-paragraph summary, then the Claims as numbered items. Each Claim's number is a tap target that scrolls the PDF to the anchor and pulses the passage. Each Claim has a small hedge-level chip ("the paper states", "the paper suggests", "the paper does not say"). Numerical values are visible in a `
` block under each Claim that lists them with their context. 5. **Figure lightbox.** Tapping a figure or table thumbnail opens a lightbox with the figure at full size, the verbatim caption, the plain-English description, and the extracted numerical values. A small button: "explain this figure aloud" calls the TTS narration. 6. **Limitations panel.** A separate tab in the right column collects every uncertainty term the authors themselves wrote, with anchors. Title: "What the authors themselves flag." This is one of the most-used panels for readers deciding how much to trust the headline. 7. **Follow-up chat.** Below the three-layer stack, a chat interface where the user asks follow-ups. Each response carries anchors that resolve back into the PDF in the left column. Refusals are first-class — they have their own visual style and never feel like errors. 8. **Companion mode.** A toggle at the top: "Add a second paper to compare." Adds a second column to the middle; the user picks dimensions; the comparison table appears below both layer stacks. 9. **Library view.** A grid of saved papers, sorted by last-read. Each card shows the title, authors, the layer the user was last reading, and the epistemic-status chip. 10. **Notebook view.** Saved passages, claims, and questions across all papers the user has read, exportable as Markdown. 11. **Replication-status panel.** A user-triggered sub-section inside the epistemic-status tab. Default state: "Click to check whether this paper has been replicated." On click, the grounded-search call runs and lists each finding with its citation URL. 12. **Sharing.** Modal: "Share this reading with a friend." Magic-link email; the recipient lands on the layer the user picked, with read-only access to the layers but not the user's notebook. 13. **Footer.** "Made for readers who want the actual paper, not the press release." Privacy: "Your library is yours. We never train on it." Capabilities `(i)` icon in header. ## 6b. First-visit onboarding Show a **first-visit onboarding** the first time a visitor lands on the app (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 Paper-Reader." - Subhead: "Read any research paper at three depths — plain English, informed amateur, and peer-level — and never simplify past accuracy." - One paragraph (≤ 60 words) explaining who this is for and what makes it different from a generic summariser: it produces three layers from one read, every claim is anchored to a passage in the paper, and the app refuses to answer beyond what the paper says. - Visual: a small annotated illustration of a paper with three layers fanning out — A1, A2, peer-level — and a line connecting one Claim to one anchored passage. **Slide 2 — Try it now.** - One short prompt: "Try with the sample paper". - A live demo input pre-loaded with the sample paper in section 8a. - 1-2 sentences pointing at *the specific page elements* where the Gemini magic happens (the three layers from one read, the anchor that lights up when you tap a Claim, the epistemic-status chip that names the publication type honestly). **Slide 3 — How to remix this.** - Headline: "Make this yours." - Three short bullets: - "Swap the sample paper in `/data/seed-paper/` for your own." - "Adjust the prompts in `/server/prompts/` to fit your readers' default language and discipline emphasis." - "Wire up your Gemini API key and Firebase project via the env-var list in the capabilities panel." - Primary CTA: "Use this template" → links to AI Studio Build remix entry point. - 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. Use `localStorage['onboarding-seen-v1']`. - Don't include unrelated CTAs (newsletter signup, social follow). Keep it about the template only. ## 6c. Capabilities info button (persistent in header) Add a persistent `(i)` icon in the top-right of the header (next to the primary nav). Click → opens a modal/panel titled **"What powers this app"**. **Panel contents (in this order):** **Gemini capabilities used (the hero list):** - **Gemini 3.5 Flash (long-context PDF)** — holds a full paper in working memory and produces three internally consistent layers in one call. Papers up to ~250k tokens fit; longer documents are chunked by section with a visible plan. - **Gemini 3.5 Flash (structured output)** — every claim is anchored to a passage in the paper. The schema forces hedge level, numerical values, and the publication's epistemic status to be explicit fields, not prose. - **Gemini 3.5 Flash (multimodal)** — figures and tables are read as images. Kaplan-Meier curves, forest plots, confusion matrices, scatter plots — the numbers are quoted only where they are printed. - **Gemini 3.5 Flash (multilingual)** — the three layers can be produced in Tagalog, Spanish, Mandarin, Cantonese, Korean, Vietnamese, Hindi, Urdu, Bengali, Tamil, Punjabi, Arabic, Farsi, Amharic, Swahili, Khmer, or any other research-reading language. Numbers and proper nouns are preserved exactly. - **Gemini 3.5 Flash + grounded search** — only used when the user clicks "has this been replicated?". Citations come from grounded search results, not the model's pretraining. - **Gemini TTS** — narrates the chosen layer at a comfortable reading pace. - **Firebase Auth** — Google and Apple sign-in, sharing via magic links. - **Firestore** — stores your library and notebook, syncs across devices in real time. - **Firebase Storage** — keeps your uploaded PDFs at upload resolution, private to your account. - **Cost note** — see the detailed breakdown in 6d. A typical 12-page paper costs about $0.04 of Gemini API spend to read into the three layers, end to end. - **Privacy note** — your library is private to your account. 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. The app refuses to answer questions the paper does not address, and it refuses to give medical, legal, or financial advice — that is not modesty, that is the rule. **Backend services this app depends on:** - Auth: see section 4b - Database: see section 4b - Storage: see section 4b - Email: see section 4b - Payments: see section 4b (not used in v1) - External APIs: see section 4b **Environment variables you'll need to configure:** - `GEMINI_API_KEY` — your Google AI Studio API key - `FIREBASE_PROJECT_ID` — your Firebase project id - `FIREBASE_SERVICE_ACCOUNT` — service-account JSON (server-side only) - `UNPAYWALL_EMAIL` — optional, your email for Unpaywall API rate-limiting - `CROSSREF_EMAIL` — optional, your email for the polite Crossref rate-limit pool **Cost + privacy notes:** - One short paragraph per cost-sensitive capability: long-context calls are billed per token of input — a typical 12-page paper costs about $0.04 to read into all three layers; a 200-page thesis runs $0.45-0.80 depending on figure density. The follow-up Q&A call is cheap (~$0.01/question). - One short paragraph on privacy: where the data lives (your Firebase project), how to delete it (Settings → "Delete this paper from my library" — gone in 60 seconds, with the PDF removed from Firebase Storage in the same transaction), what is never sent for training, and the app's refusal-by-design for advice questions. **Documentation links:** - AI Studio Build docs - Gemini API long-context, structured output, multimodal, multilingual, TTS docs - Firebase Auth, Firestore, Firebase Storage docs - A short note on Crossref + arXiv + Unpaywall for DOI/arXiv-ID lookup **Accessibility:** same standards as the onboarding modal — focus trap, `Esc`, ARIA, restored focus. **Behaviour:** - Always available — single click from anywhere in the app. - 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) - **Read PDF → three-layer PaperReading (Gemini 3.5 Flash, high thinking)** — typical 12-page paper ≈ 24k input tokens, ~6k output tokens. ~$0.060/read (≈ 24k × $1.50/M input + 6k × $9/M output). For a 30-page paper the cost roughly doubles. For a 200-page thesis it can reach $0.80. - **Figure reading (Gemini 3.5 Flash, medium thinking)** — one cropped figure ≈ 1 image + a few hundred output tokens. ~$0.008/figure. Called only when the user taps a figure, never bulk-run. - **Follow-up Q&A (Gemini 3.5 Flash, medium thinking)** — the paper is in context plus the user question and the PaperReading object. ~$0.012/question. Cached at the application level if the same question is asked twice within a session. - **Companion-paper comparison (Gemini 3.5 Flash, high thinking)** — two papers in context. ~$0.10-0.20/comparison depending on paper size. - **Replication-status check (Gemini 3.5 Flash + grounded search)** — cheap; ~$0.002/check. User-triggered, never automatic. - **Translation into a target language (Gemini 3.5 Flash, low thinking)** — the three layers are small; ~$0.003/translation. Cached per (paper, language) pair. - **Glossary expansion (Gemini 3.5 Flash, low thinking)** — ~$0.0005/term. Cached per (paper, term) pair. - **TTS narration (Gemini 2.5 Flash TTS)** — billed per output token (~$10/M output tokens), effectively ~$0.000003/character. The peer-level layer (~280 words ≈ 1.7k characters) ≈ $0.005 per narration. Cached per (paper, layer, language) tuple. - **Expected per-paper cost on first read:** ~$0.07-0.10 end to end (three-layer read + a couple of figure reads + a few follow-up questions + a single translation). **Cost per follow-up question after the read:** ~$0.012. - **PDF storage:** Firebase Storage standard tier, ~$0.026/GB/month. A typical PDF is 1-5 MB; a library of 100 papers uses ~300 MB ≈ ~$0.01/month. ## 7. Design language - **Mood:** A quiet reading room. Not a tech product. Not a study-tool dashboard. The Sheffield retiree at the kitchen table on Sunday afternoon, the printed paper open, the fountain pen at hand, the lamp on. Calm enough that the reader feels they have time to read carefully. - **Typography:** Reading serif for the three layers and the anchored quotes (Source Serif Pro, Lora, or Crimson Pro). The PDF is rendered as-is. A clean grotesque for app chrome (Inter, Geist, or IBM Plex Sans). A monospace for the structured-output `(i)` debug panel (JetBrains Mono). - **Palette:** Reading-paper background `#F8F5EE`, deep ink `#1A1B1E` for body, a quiet sage `#4F6A57` for the layer-tab strip and the "the paper states" hedge chip, a warm ochre `#B5832E` for "the paper suggests", a muted brick `#9C4A3C` for "the paper does not say" and for the PREPRINT epistemic chip. A pale blue `#5B7A9A` for the user's own notebook annotations so they cannot be mistaken for paper content. Borrowed from a librarian's reading carrel, not from SaaS design systems. - **Imagery:** The PDF is the hero. Never replace it; never crop it. The cover image on the welcome screen is photographed-looking, a single paper on a wooden table. No stock-photo "scientist with a beaker". - **Hand-feel touches:** The anchor pulse on the PDF when a Claim is tapped is gentle — a faint warm-yellow highlight that fades over 600 ms. The layer expansion (A1 → A2 → peer-level) reveals the new layer with a barely-visible scroll-into-place, not a theatrical accordion. Hover on an anchor reveals the anchored quote in a tooltip; never glow. - **Spacing:** consistent 4-px base. Generous whitespace — papers are dense; the reading UI must not be. - **Radius:** consistent token set (e.g. 6 / 12 / 20 px). Claim cards use 6; the epistemic-status panel uses 12; the welcome card uses 20. - **Shadows:** subtle, layered, paper-warm. Avoid heavy drop-shadows. - **Motion:** purposeful — anchor pulse, layer expansion, figure-lightbox entry. Respect `prefers-reduced-motion`. No bouncing splash animations. No theatrical hero animations. The anchor pulse is the one place where motion carries meaning; respect reduced-motion by replacing the pulse with a static border. - **States:** every interactive element has hover, focus, active, disabled. Loading uses skeletons not spinners where possible. Empty states have helpful next-action guidance ("Drop a PDF or paste a DOI to start"). ## 8. Content generation rules - Write **realistic, specific copy**. NO Lorem Ipsum. NO generic placeholders like 'Your tagline here'. - Invent plausible paper titles, authors, journals, and findings for the seed content in section 8a. Use realistic 2025-2026 publication conventions (DOIs, ORCID identifiers, registered-report flags). Never claim a fictional paper is a real publication; the seed paper is clearly labelled as a synthetic example. - Tone: warm, direct, free of corporate language. This template is for a curious reader, not a knowledge-management product. - Headlines: punchy and concrete. No 'Empower your X' filler. No 'Revolutionize'. No 'Seamless'. - Body copy: short paragraphs (2-4 sentences). Use lists where appropriate. - Plain language. Avoid jargon — except where the user already speaks the jargon (the peer-level layer is supposed to use the discipline's vocabulary; the glossary backs it up). - Where the app outputs AI-generated content, never label it as "AI says" — let the paper speak through the layered explanation. The honest framing is "the paper states" / "the paper suggests" / "the paper does not say", which are baked into every Claim's `hedge_level`. ## 8a. Seed content (use these specific examples) Anchor every generated copy + sample data point in the concrete content below. Use these names, numbers, dates, and snippets verbatim where helpful, or generate close variants that sit in the same world. **Sample library (sidebar):** - "A randomised trial of a single-dose mRNA dengue vaccine in endemic settings" — *NEJM, 2025* — clinical trial report, primary endpoint reached, multi-centre across the Philippines, Brazil, and Vietnam. Used as the demo paper. - "Heat-vulnerability mapping at the block level: a Mexico City case study" — *Environmental Research Letters, 2024* — observational, GIS-based, with a notable subgroup analysis on informal settlements. - "Long-context retrieval in instruction-tuned language models: a controlled study" — *arXiv preprint, 2025* — flagged PREPRINT in the epistemic panel, useful for the demo of how the app handles preprints. - "Cognitive outcomes after a single dose of psilocybin in treatment-resistant depression: a 12-month follow-up" — *Lancet Psychiatry, 2024* — primary endpoint partially missed; the demo of how the app surfaces a non-significant primary endpoint while quoting the significant secondary endpoint honestly. **Sample paper in the demo (this is what the demo should show):** - **Title (verbatim):** "Single-dose mRNA dengue vaccine in endemic settings: a randomised, double-blind, placebo-controlled phase 3 trial" - **Authors:** Reyes V, Nguyen TQ, Silva-Pereira M, Okonkwo C, the DENGUE-3 Investigators - **Journal:** New England Journal of Medicine (synthetic example for template) - **Publication date:** 2025-09-14 - **Publication type:** peer-reviewed journal article, clinical trial report - **Sample size:** N = 12,847 participants, ages 6-45, across 18 sites in the Philippines, Brazil, and Vietnam - **Study design verbatim:** "randomised, double-blind, placebo-controlled, multi-centre, phase 3 efficacy trial" - **Primary endpoint verbatim:** "incidence of virologically-confirmed symptomatic dengue infection within 12 months of vaccination" - **Primary endpoint reached significance:** true (vaccine efficacy 73.2%, 95% CI 61.4-81.8, p < 0.001) - **Secondary endpoints:** hospitalised dengue, severe dengue (per WHO 2009 criteria), serotype-specific efficacy (DENV-1, DENV-2, DENV-3, DENV-4) - **Conflicts of interest verbatim:** "VR reports research grants from [Sponsor] paid to her institution; TQN reports no conflicts; MS reports a previous consultancy with [Sponsor] in 2022; CO reports no conflicts. Full disclosures in the Supplementary Appendix." - **Funding verbatim:** "Funded by the Coalition for Epidemic Preparedness Innovations and the Wellcome Trust. The sponsor had no role in the study design, data analysis, or decision to publish." - **Sample A1 one-paragraph summary:** "In a trial of 12,847 children and adults in the Philippines, Brazil, and Vietnam, one dose of an mRNA dengue vaccine reduced symptomatic dengue infection by 73% over 12 months compared to a placebo injection. The trial was randomised and double-blind, which means neither the participants nor their doctors knew who got the vaccine. The most common side effects were injection-site pain (28%) and a low-grade fever in the first two days (16%). No deaths or serious vaccine-related adverse events were reported." - **Sample A2 paragraph (excerpt):** "The trial enrolled people in three countries where dengue is common, half got the vaccine and half got a salt-water injection, and the researchers followed both groups for a year. They counted who came down with a lab-confirmed dengue infection. Efficacy was 73% (95% CI 61-82%), meaning the vaccine cut the rate of infection by about three-quarters. Efficacy was lower against serotype 3 (58%) than the other three serotypes, and the authors flag this as a finding to watch in longer follow-up." - **Sample peer-level paragraph (excerpt):** "DENGUE-3 was a phase 3, randomised, double-blind, placebo-controlled, multi-centre trial in 18 sites across three dengue-endemic countries. The primary endpoint was virologically-confirmed symptomatic dengue within 12 months post-vaccination, with PCR confirmation per the protocol. Vaccine efficacy was 73.2% (95% CI 61.4-81.8), p < 0.001 by Cox proportional-hazards regression with stratification by site. Serotype-specific efficacy ranged from 58.1% (DENV-3, 95% CI 38.9-71.6) to 81.4% (DENV-1, 95% CI 70.1-88.7). The authors pre-registered the analysis plan on ClinicalTrials.gov (NCT-synthetic-id-for-template). Limitations the authors themselves flag: 12-month follow-up is short for a vaccine; the trial did not include pregnant women or immunocompromised participants; serostatus at baseline was not measured for all participants." - **Sample glossary entries:** - **Vaccine efficacy** — "In this trial, the percentage reduction in lab-confirmed symptomatic dengue infection among the vaccinated group compared to the placebo group, over the first 12 months. Efficacy is not the same as effectiveness, which would be measured in routine use outside a controlled trial." - **Cox proportional-hazards regression** — "A statistical model that estimates how a treatment changes the rate of an event (here, dengue infection) over time, while assuming the effect is roughly constant over the follow-up period. The paper used it to produce the headline efficacy estimate adjusted for site." - **Per-protocol vs. intention-to-treat** — "Intention-to-treat analyses everyone in the group they were randomised to, even if they later didn't follow the trial rules. Per-protocol only counts people who followed the trial rules. This paper reports both; the headline number is intention-to-treat." - **Sample epistemic-status panel:** publication_type = "peer_reviewed_journal_article" / "clinical_trial_report"; publication_venue_verbatim = "New England Journal of Medicine"; is_pre_registered = true; pre_registration_id_verbatim = "NCT-synthetic-id-for-template"; primary_endpoint_reached_significance = true; sample_size_verbatim = "N = 12,847"; study_design_named_in_paper_verbatim = "randomised, double-blind, placebo-controlled, multi-centre, phase 3 efficacy trial". - **Sample authors' own limitations verbatim:** ["12-month follow-up is too short to assess vaccine durability", "the trial did not enrol pregnant women or immunocompromised participants", "baseline serostatus was not measured for all participants, limiting subgroup analyses on previously-exposed individuals", "efficacy against serotype 3 was lower than against the other serotypes and warrants longer follow-up"] - **Sample follow-up question + refusal:** Question: "Should I get my child vaccinated?" Refusal: "This paper reports a 73% reduction in symptomatic dengue over 12 months in 12,847 children and adults across three countries. Whether to vaccinate your child is a clinical decision your paediatrician and you make, with your family's specific health context. The paper's eligibility was ages 6 and over; children under 6 were not studied." - **Sample follow-up question + answer:** Question: "Was the vaccine tested in pregnant women?" Answer: "No. The paper states on page 4, Methods: 'Pregnant women and those of childbearing potential not using effective contraception were excluded from enrolment.' The authors also list this as a limitation." **Sample input artefacts (for the build to demonstrate):** - The DENGUE-3 paper PDF (synthetic, 18 pages with 4 figures and 3 tables, including a Kaplan-Meier curve and a forest plot of serotype-specific efficacy). - The Mexico City heat-vulnerability paper PDF (synthetic, 14 pages, with two GIS maps and one table of subgroup analyses). - The long-context-retrieval preprint (synthetic, 22 pages, marked PREPRINT in the epistemic panel). - The psilocybin follow-up paper (synthetic, 16 pages, primary endpoint missed; demonstrates the secondary-endpoint handling). **Sample voice copy:** - Onboarding: "Drop a research paper. We'll read it at three depths — plain English, informed amateur, peer-level. You pick the depth." - Processing: "Reading the paper…" / "Building the plain-English layer…" / "Anchoring every claim…" / "Naming the methodology the authors named…" - Empty library: "Your library is waiting for its first paper. Drop a PDF or paste a DOI to start." - Error (couldn't read): "We couldn't extract the text from this PDF. It may be an image-only scan; want us to try OCR mode?" - Layer-switch confirmation: "Now reading at A2 — informed amateur." - Refusal copy (advice question): "This paper reports the trial outcome. Whether to take the drug is a clinical decision your doctor and you make." - Refusal copy (out-of-scope question): "This paper does not address that. The closest claim is on page 7, Discussion: '[short verbatim quote].' Want me to read another paper alongside this one?" - Preprint banner: "This is a preprint. It has not been peer-reviewed." - Low-confidence figure note: "Some values were interpolated by eye from a chart with no printed numbers. We've marked the reading confidence as moderate." **Sample share email subject + body:** - Subject: "I'm reading the dengue-vaccine trial — same depth I am, here it is" - Body: "Hi — I'm reading the DENGUE-3 paper at A2 level. Here's my reading view, open to the same layer I'm on. The epistemic-status panel up top is the one I want you to look at first." [Open Reading] ## 9. Media & assets - **Hero image (landing screen):** A photographed-looking shot of a printed research paper on a wooden kitchen table at evening, a fountain pen across one corner, a mug of tea blurred at the edge of frame, faint marginalia visible in the margins. Generate via Nano Banana 2 with a prompt emphasising "wooden table, warm desk-lamp light, printed academic paper with double-column layout, fountain pen, late afternoon, gentle out-of-focus tea mug, soft shadow under the paper, no people in frame". - **App icon / wordmark:** Set in the reading serif. A small fountain-pen nib glyph beside the wordmark. No tech badging. - **Empty-state illustration:** A simple line drawing of a single printed paper with three small chips fanned out beside it labelled A1 / A2 / peer. Hand-drawn aesthetic, not a flat icon. - **Demo figure renders:** Generated per the prompts in section 8a — Nano Banana 2 prompts for the Kaplan-Meier curve and the forest plot, with the prompt specifying "clean white background, sans-serif axis labels, no decorative elements, no glow, no gradient — looks like a figure from a real scientific paper". Each demo figure should look extracted from a real paper, not a stylised infographic. - **Stock fallbacks:** If image generation fails, fall back to the bundled sample figure from `/public/samples/sample-figure.png`. Never to a "📄" emoji. - **Generated imagery:** prefer Nano Banana 2 over stock photography. Prompt for warmth, restraint, and an academic-document aesthetic — avoid the glossy 'AI render' look. Avoid the 'futuristic lab with holograms' cliché entirely. - **Optimisation:** WebP/AVIF, `loading="lazy"`, explicit `width`/`height` to prevent layout shift. - **Icons:** `lucide-react` for UI. Use sparingly — never decorative-only. ### Build-time asset manifest (explicit specs) Every image, illustration, and visual reference mentioned above must resolve to ONE of the three buckets below — runtime-generated, seed-shipped, or user-supplied. Do NOT ship `` tags whose `src` is not listed here. Do NOT depend on bare "section 8a prompts" without binding them to explicit paths and model IDs. **Bucket 1 — Runtime-generated (Nano Banana Pro `gemini-3-pro-image` for hero/demo photographs; Nano Banana 2 `gemini-3.1-flash-image` for in-app illustrations and reference-conditioned variants).** Cached to Firebase Storage; served via signed URL. Every reference above to "Nano Banana 2" or "Nano Banana Pro" MUST be wired to one of these specific calls with an explicit model id: - `/public/generated/hero.webp` (2400×1500, WebP) — model `gemini-3-pro-image` — uses the literal prompt described as "Hero image (landing screen)" above. Run once at build; commit a `/public/samples/hero-fallback.webp` (1600×1000) generated from the same prompt with `gemini-3.1-flash-image` so the page renders if quota is exhausted. - `/public/generated/demo/{demo-slug}-{NN}.webp` (1600×1200, WebP) — model `gemini-3.1-flash-image` (reference-conditioned where the prior frame is passed as input) — one path per "Demo X" image referenced above. The slug derives from the seed example in section 8a; the NN index covers each frame in the demo sequence. - `/public/generated/illustrations/{name}.webp` (1024×1024, WebP) — model `gemini-3.1-flash-image` — one path per named illustration above ("Empty-state illustration", "Recipe-card hero illustrations", "Curriculum picker imagery", "Period-style frames", etc.). Each illustration's prompt is the literal description above; ship a deterministic seed in the request so re-runs are reproducible. **Bucket 2 — Seed assets shipped with the deliverable.** Every "Stock fallback" path referenced above (e.g. `/public/samples/sample-X.jpg`) is generated once via Nano Banana 2 (`gemini-3.1-flash-image`) at 1024×1024 WebP using the same prompt as its Bucket-1 counterpart, then committed to the repo so the page renders identically if Gemini quota is exhausted or the user is offline. Replace any `.jpg` extension above with `.webp` to match the optimisation rule. Also commit these empty-state seeds (1024×1024 WebP, single-stroke hand-drawn line, no colour fill): - `/public/samples/empty-state-primary.webp` — line drawing of the app's primary empty surface (the named "Empty-state illustration" above), generated from that exact prompt. - `/public/samples/empty-state-archive.webp` — line drawing of an empty saved/archive view, single-stroke outline. - `/public/samples/empty-state-error.webp` — line drawing of a hand placing a single object aside with care, used when an AI call fails. **Bucket 3 — User-supplied.** Uploads from the user's camera / file picker land at the Firebase Storage path conventional for this template (named in section 4b). The build ships with Bucket-1 + Bucket-2 only; no user-supplied images at first paint. **Hard rules** - Every `` tag MUST have a `src` that resolves to a path listed in Bucket 1, Bucket 2, or a Bucket 3 upload path. Anything else is a build error. - No bare `image.jpg` / `hero.jpg` / `placeholder.png` references anywhere in the code. - Model IDs: `gemini-3-pro-image` for hero-quality photographic generation; `gemini-3.1-flash-image` for in-app illustrations, reference-conditioned variants, empty-state seeds, and stock fallbacks. Never use a legacy model id (no `imagen-*`, no `gemini-1.5-*-image`). - File format: WebP everywhere (AVIF acceptable where the target browsers support it). No `.jpg` / `.jpeg` / `.png` in `/public/samples/`. ## 10. Interactivity & states - Every interactive element has hover, focus, active, and disabled states. - Forms validate inline and show specific error messages (not "Invalid input"). DOI validation says "this doesn't look like a DOI — DOIs look like 10.1234/abcd.5678"; arXiv ID validation gives an equivalent specific message. - Loading states use skeletons that match the eventual layout, not spinners. The three-layer skeleton shows three stacked cards with shimmering placeholder lines proportional to the eventual paragraph lengths. - Empty states explain the next action with a button whose label fits THIS app's domain: "Drop a PDF", "Paste a DOI", "Open your library" — never a generic "Add your first item". - Smooth scroll for in-page anchors. Anchor pulse on the PDF when a Claim is tapped on the right is 600 ms with a sage-yellow highlight, falling back to a static border under `prefers-reduced-motion`. - All AI-generated content streams in token-by-token where supported, with a clear "reading the paper…" indicator before content starts arriving. - If an AI call fails, show a calm, specific error ("We couldn't reach the model — your PDF is safe and the call will retry automatically in 30 seconds. Or click here to retry now.") and offer retry. - Follow-up Q&A messages stream; the refusal copy is shown without a "retry" affordance — the refusal is the answer, not an error. - Replication-status check is gated behind an explicit click and shows a "this leaves the paper and uses live web search" tooltip on first use. - Layer-switch transitions are 250 ms with `prefers-reduced-motion` falling back to instant. ## 11. Tech & responsive requirements - **TTS markdown-stripping preprocessor:** before sending any user-authored markdown to `gemini-3.1-flash-tts-preview`, strip non-spoken markdown: `#`/`##`/`###` headings (keep the title text), `**bold**` (keep the inner text), `[label](url)` (keep `label`, drop URL), `` ``` `` fenced code blocks (skip entirely), `>` block-quote markers (keep the text), and `|` table pipes (read row-by-row as sentences). Insert `…` between sentences for a short pause and a blank line plus `—` between paragraphs for a long pause. The model does not understand markdown; raw markdown will be read aloud as literal characters ("asterisk asterisk"). - **File downloads on Safari / Firefox:** when offering local-disk save of any export (PDF, CSV, MP3, ZIP, JSON, image), fall back to `` with a blob URL — the File System Access API (`showSaveFilePicker()`) is Chromium-only. Detect with `'showSaveFilePicker' in window`; otherwise use the anchor-download path. - **Stack:** React + TypeScript + Tailwind CSS. Functional components + hooks. Use Shadcn UI primitives where appropriate. PDF rendering via `pdfjs-dist` (Mozilla's PDF.js) in the left column. - **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. - **Model selection:** explicitly pin `gemini-3.5-flash` for the three-layer read / figure read / follow-up Q&A / companion comparison, `gemini-3.5-flash` for replication-status, translation, and glossary expansion, and `gemini-3.1-flash-tts-preview` for narration. Set `thinkingLevel` explicitly per call. - **Database:** Firestore (auto-provisioned by AI Studio Build). Show the seed library on first launch. - **Auth:** Firebase Auth — Google sign-in by default; Apple sign-in next to it; magic-link email as fallback. - **Storage:** Firebase Storage for uploaded PDFs. Pre-signed URLs only. Server-side delete is wired to the per-paper delete action. - **Mobile-first.** Verify layouts at 375 px (iPhone SE), 768 px (iPad), 1024 px, 1440 px+. On mobile, the three-column layout collapses to tabs: PDF / Layers / Glossary+Status. - 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. Use real-time listeners on the Reading view so the streaming three-layer parse appears as it arrives. - Optimistic UI on writes (notebook entries, layer-switch state); reconcile on response. - PDF rendering uses `pdfjs-dist` worker on a separate thread to keep scroll smooth on long documents. - Token-budget guardrail: before sending a paper to the long-context read, count tokens client-side (rough heuristic: characters / 4) and warn the user if the paper exceeds 250k tokens. Above 500k, refuse and offer to chunk by section. ## 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. The sage / ochre / brick hedge chips are verified against `#F8F5EE` background at AA. - All images have meaningful `alt` text. Figure thumbnails carry the figure's verbatim caption as `alt`. - Form fields have associated `