# 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. --- # Read This To Me ## 1. Project **Read This To Me** turns any long article, essay, newsletter, paper, PDF, or web page into expressive, walk-paced narration you can listen to while doing dishes, commuting, walking the dog, or sitting on a train with the phone face-down. Paste a URL or drop a PDF, tap play, and within three seconds a natural human-feeling voice — warm, focused, or casual — is reading the piece out loud at a comfortable walking pace of about 130 words per minute, with subtle emphasis on the load-bearing sentences, slightly different timbre on direct quotes, and natural breath at every paragraph break. Every word is read; nothing is skipped or paraphrased. The player locks the phone, keeps playing in your pocket, and remembers exactly where you stopped. This is the kind of app a commuter wants on the morning walk to the station — they saved a 6,000-word longform piece in a "read later" tab three weeks ago and have never opened it. It is the kind of app a PhD student wants when they have a 40-page paper to read before a meeting at three and would rather hear it on a walk than skim it on a screen. It is the kind of app a parent wants on Sunday afternoon when the dishes need doing and there is a newsletter sitting in the inbox they actually want to hear. Same job, three different scales of attention. The app handles all three. The single demo that proves the magic: a visitor pastes the URL of a 4,000-word essay, taps "Read this to me", and within three seconds the narration starts. The voice is warm and unhurried. At the end of each paragraph there is a real breath. Quoted dialogue lands with a slightly different vocal colour so the listener feels the speaker change without being told. The key claim of the second section gets a faint emphasis the listener notices but cannot quite name. Chapter markers appear at every heading; one tap skips ahead. The lock-screen shows a clean cover, the article title, and play / pause / skip controls. The phone goes in the pocket and the listener walks. In the harder cases — a 78-page PDF research paper with figures and footnotes, a newsletter with embedded code blocks the listener does not want read out, a long-form interview where two speakers alternate — the app reads the long arc too: pages are stitched cleanly across column breaks, footnotes get a one-line "footnote 14, three sentences, tap to expand" mention rather than a full read-through that derails the flow, code blocks are skipped with a "code block, twelve lines, tap to read" handoff, and the two speakers in an interview get two distinguishable voice timbres so the listener can follow who is talking. **Tagline:** _Paste a URL, drop a PDF. Walk away with the essay read to you — at walking pace, in a real human-feeling voice, every word._ ## 2. Target audience - Commuters and walkers who already have a stack of saved-for-later longform pieces and never make it back to the tab - PhD students, lawyers, policy analysts, and anyone whose inbox fills with PDFs they should read on the train but read poorly on a screen - Parents doing dishes, folding laundry, walking the dog — listening is the only time of day they actually have to read - People with dyslexia, post-concussion symptoms, eye strain, migraine, or visual fatigue for whom an audio companion to written longform is genuinely accessible - Newsletter readers who subscribe to ten weekly essays and never get through more than two; the audio version cuts through - Researchers who want to "read" a paper while running and form an opinion before the lab meeting - Listeners on flights, in airports, in long car queues, on the bus — anywhere the eyes are taxed but the ears are free - People who simply prefer audio — they would buy an audiobook version of every essay if such a thing existed, and find paid per-article narration services (which exist but stop reading after one weekly piece) too restrictive ## 3. Core value propositions Surface these clearly through copy, visual emphasis, and section ordering — they are the reasons users pick this app. - **Expressive voice, not robotic TTS** — the post-I/O 2026 expressive TTS model (`gemini-3.1-flash-tts-preview`) produces narration with real prosody: natural breath at paragraph breaks, subtle emphasis on the sentence the author is leaning on, a different vocal colour for quoted material, and a pace that matches how a friend would read the piece aloud — not the flat, equal-stress cadence of stock TTS. - **Every word, no shortcuts** — the app NEVER skips or paraphrases. Summarisation is a separate, opt-in mode; the default contract is "every word the author wrote, in order, read out loud". This is the load-bearing trust contract — a listener who has been burned by summaries that skipped the part they wanted to hear can rely on this app to read the whole piece. - **Walking pace by default** — narration runs at ~130 words per minute by default, the pace at which a thoughtful friend reads a piece aloud on a walk. Adjustable from 100 wpm (close-listen) to 180 wpm (fast catch-up); the default is tuned for ambient listening while moving. The pace knob lives on the player face, not in a settings page. - **Three voices, three jobs** — _Warm_ for personal essays, newsletters, memoir; _Focused_ for research papers, technical pieces, policy writing; _Casual_ for blog posts, light writing, conversational longform. The voice change is conveyed via a single style-directive sentence prepended to the input (Gemini 3.1 Flash TTS does not support SSML), and the listener picks it once per piece via a one-tap chooser. - **Reading-aware pause structure** — paragraph breaks become real pauses; sentence boundaries become micro-pauses. Encoded as the textual cues `…` for sentence pauses and a blank line plus `—` for paragraph pauses (since Gemini 3.1 TTS has no SSML `` tag). Headings get a short tonal lift; quotations get a slightly more intimate timbre. Foot-of-page and end notes are flagged inline rather than read full-bleed. - **Offline-cached playback** — once the narration has been generated, the MP3 is cached on-device so the user can listen on a flight, underground, or in dead-zone wilderness. The download chip on the player face shows MB size and percent cached. - **Background-audio first-class** — the app implements the Media Session API with full lock-screen art, scrubbing, play/pause, skip forward / back by chapter, and per-15s seek. The phone screen locks and the audio keeps playing. The user can answer a call, take a photo, or run another app and come back without losing position. - **Honest about what gets skipped** — code blocks, large tables, raw equations, and figures that do not render as prose are surfaced as one-line spoken handoffs ("code block, twelve lines, tap to read") rather than awkwardly narrated. The listener decides whether to break narration flow to engage with them, and a per-piece toggle "read everything verbatim, even code" exists for the listener who wants no exceptions. ## 4. Features to build - Paste-URL flow — single text input on the home screen accepts a URL to an article, a Substack post, a Medium post, a personal-blog page, a Wikipedia article, an arXiv preprint page, a news article. The fetcher resolves the canonical reading view (boilerplate stripped) before passing prose to the TTS pipeline. - Drop-PDF flow — drag-and-drop a PDF, or tap to upload from the device; PDFs up to 200 pages handled in a single pipeline pass with multimodal page-parsing on Gemini 3.5 Flash. - Share-extension entry (iOS Share Sheet / Android Share intent / web Web Share Target) — share any URL from any app and the piece is queued and ready to play before the user opens the app. - Player face — large cover art (generated or extracted), title, author, estimated runtime, voice picker, pace knob, chapter list, scrubbing timeline, play/pause/skip-15/back-15/skip-chapter/ back-chapter, sleep timer. - Voice picker (one tap, three options) — Warm / Focused / Casual, with a 6-second preview clip of each reading the article's opening line so the listener picks the right tone for the piece. - Chapter skip — headings (`

`–`

` for HTML; outline tree for PDFs) become chapter markers. The listener can jump between chapters with one tap. - Highlight-emphasis pipeline — Gemini 3.5 Flash reads the prose before TTS generation and emits a JSON map of `paragraph_index → emphasis_directive`. Directives are encoded as inline textual cues the expressive TTS model honours (italics-equivalent slight slow, inline emphasis on a single phrase via short pre-pause). Never more than 1-2 emphasis cues per paragraph. - Quote-aware voicing — quoted material inside the prose is detected by the prose-analyser call and rendered with a slight tonal shift by prepending a short directive sentence to the TTS input for that segment (e.g. "the next sentence is a direct quotation; read it with a slightly more intimate timbre"). For interviews with two speakers, the second speaker's segments get a different voice selection within the same family. - Reading-position persistence — the player remembers exactly where the listener stopped, to the second, across sessions and devices. - Offline cache — generated MP3s saved on-device (IndexedDB on web, on-disk on mobile). Cache chip shows size and "downloaded" state. - Background audio — Media Session API, lock-screen controls, Bluetooth headphone controls, Airpods double-tap, CarPlay / Android Auto when available. - Sleep timer — 5 / 10 / 20 / 30 minutes or "end of chapter". - Search-within-piece — full-text search over the parsed prose; tapping a match plays from that sentence. - Listening queue — a simple linear queue of pieces; auto-advance to the next piece when the current one finishes. - Listening history — what was listened to, when, how much. Local only; never sent to a server unless the user explicitly turns on cross-device sync. - Per-piece "read everything verbatim" toggle — for listeners who want zero handoffs (every code block, table, footnote read in full). - Per-piece "skip footnotes" toggle — for listeners who want a cleaner narrative pass; the footnotes are listed at the end and can be played as a single trailing segment. - Article-cover generation — Nano Banana 2 generates a clean cover image for the piece based on the title and the first paragraph, used in lock-screen art and in the queue list. - Cross-device sync (opt-in) — Firestore stores the listener's position and queue, syncs across devices when the user signs in with Google or Apple. Off by default. - Personal-feed import — connect an RSS reader (Feedly, Inoreader, generic OPML upload) and saved-for-later items appear as a listening queue. - One-tap "send to my phone" from a web session — when the listener pastes a URL on desktop and wants to listen on the walk home, a signed-in user can push the piece to their phone with one tap (Firestore real-time listener; no notifications needed). ## 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) - **Expressive TTS** (`gemini-3.1-flash-tts-preview`) — the load-bearing hero. Generates expressive, prosody-rich narration from plain prose at ~24 kHz output. Style is steered by a single one-sentence directive prepended to the input ("Read this with a warm, unhurried voice, like reading a friend's essay aloud on a walk. …"); there is no SSML in Gemini 3.1 Flash TTS — pauses are encoded as `…` for sentence breaks and blank-line `—` for paragraph breaks. The model produces noticeably more natural prosody than the prior `gemini-3.1-flash-tts-preview` (released 2026-04-15; pre-I/O 2026 templates used the older ID — replaced here). - **Prose analyser + emphasis planner** (`gemini-3.5-flash`, thinkingLevel `medium`) — reads the parsed prose of the whole piece in a single 1M-context call and emits a structured `NarrationPlan` that describes per-paragraph metadata: paragraph type (body / heading / quote / footnote / code / table-handoff), emphasis directives (at most 1-2 per paragraph), voice handoff cues for multi-speaker interviews, chapter boundaries. **Guardrail**: a 60-page PDF or 30,000-word longform can run to ~100k tokens of prose; cap at 500k input tokens (well below the 1M ceiling) and if the parsed prose exceeds that, chunk on top-level headings and emit a per-chunk NarrationPlan, then concatenate. - **URL / PDF prose extractor** (`gemini-3.5-flash`, thinkingLevel `low`, multimodal image/PDF input) — accepts the full PDF or fetched HTML and emits a structured `ParsedPiece`: title, author, publication, date, ordered list of `Paragraph` blocks with type and content. For PDFs the model reads page images directly via the Files API; for HTML the server pre-strips boilerplate (using a Readability-style heuristic) before sending the residual prose. **Multipage / multi-image input** uses the Gemini Developer API Files API (`files/*` resource name, e.g. `files/abc123xyz`, passed via `fileData: { fileUri, mimeType }`) for PDFs over 5MB; sub-5MB PDFs can be sent as `inlineData` (base64). NEVER pass Firebase Storage public URLs to `generateContent` — the API does not fetch them server-side. `gs://` URIs belong to Vertex AI / Cloud Storage — a different surface, not accepted on the Developer API. - **Cover-art generator** (`gemini-3.1-flash-image`, Nano Banana 2) — generates a clean cover image for each piece based on title and first paragraph. Used in lock-screen art and queue thumbnails. Reserved hero use of `gemini-3-pro-image` (Nano Banana Pro) for the optional 4K poster export feature (see section 5). - **Structured output / JSON Schema** — the prose extractor returns the `ParsedPiece` schema; the analyser returns the `NarrationPlan` schema. Both schemas are seeded verbatim in the system instruction and in `responseSchema`. Convert the Zod schema to Gemini's `Schema` type via the SDK helper before passing as `responseSchema` — do NOT pass raw Zod. Numeric `min`/`max` constraints in the schemas are documentation only; clamp on the server after the response arrives. - **Thinking levels** — `medium` for the analyser (it must reason about emphasis, structure, speaker turns across the whole piece); `low` for the prose extractor (mechanical structure extraction); not applicable to TTS or image generation calls (TTS and image-gen models do NOT accept `thinkingConfig` — omit the field entirely). - **Optional: streaming TTS** — the TTS call can stream PCM audio back as it generates so playback can start within ~3 seconds rather than waiting for the full file. The server forwards the PCM stream as a chunked HTTP response; the client wraps it in a Media Source Extensions buffer for the `