================ 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. --- # Whiteboard Tutor ## 1. Project **Whiteboard Tutor** is a live, voice-led maths tutor that reads what the kid has written on paper. The student props their phone up against a stack of books, points the camera at the workbook page, and a quiet voice begins a conversation about the problem. The tutor never solves the problem in one shot. It looks at what the kid has already written, asks the next question a good teacher would ask, waits, watches the hand move across the page, and only then says the next thing. The working stays on paper. The tutor stays in the kid's ear. This is the kind of app a sixteen-year-old in San Jose opens at twenty-three minutes past midnight because the AP Calculus AB problem set is due first period and she has been staring at integration by parts for forty minutes and her parents are asleep and the family group chat in Vietnamese has gone quiet. It is also the kind of app a seventeen-year-old in Manchester opens after his older sister moves out and there is suddenly nobody in the house who finished A-level Further Maths, and the kind of app a fifteen-year-old in Bergen County opens because the local SAT tutor charges ninety dollars an hour and his immigrant parents have already paid for one. Same shape of moment, different curriculum, different ceiling. The single demo that proves the magic: prop the phone against a stack of textbooks, photograph the problem on the page — *Evaluate ∫ x·e^x dx* — and a calm voice says, *"Okay. Before anything else: what does this integral remind you of? Is there a product in there?"* The student answers out loud, *"…it's x times e to the x, so it's a product."* The voice says, *"Right. So which technique do we reach for when an integrand is a product of two things, one easy to differentiate and one easy to integrate?"* The student writes *u = x, dv = e^x dx* in the margin. The phone sees the new ink the moment it appears, the voice says, *"Good — keep going, what's du, what's v?"*, and the problem unfolds line by line. The student is the one holding the pencil. The tutor is the one holding the question. **Tagline:** _A live tutor that watches your paper, hears your voice, and refuses to do the problem for you — in any subject, any curriculum, at any hour._ ## 2. Target audience - High-school students in Year 11, Year 12, Year 13 (UK) / 10th, 11th, 12th grade (US) working through homework alone at night, with no adult in the house who took the subject - First-generation immigrant teenagers whose parents are present and supportive but did their own schooling in a different language or curriculum and cannot help with US Common Core or AQA A-level past papers - IB Diploma students in Math AA/AI HL whose schools provide one teacher-office-hour a week and a 35-question problem set every Tuesday - AP Calculus AB/BC, AP Physics 1/C, AP Chemistry students one month out from the May exam - A-level Further Maths, Physics, and Chemistry students working through Edexcel and OCR specifications - GCSE students rebuilding confidence in algebra, trigonometry, or chemistry stoichiometry before a mock exam - Undergraduates in their first year of engineering, economics, or pre-med revisiting calculus, linear algebra, statistics, or organic chemistry mechanism problems - Adult learners returning to study — access-to-HE courses, Open University foundation, community-college pre-calc — who haven't held a pencil for maths in fifteen years - Self-taught teenagers in countries where after-school tutoring is unaffordable or unavailable, working from a paper textbook and one phone ## 3. Core value propositions Surface these clearly through copy, visual emphasis, and section ordering — they are the reasons users pick this app. - **Socratic by hard rule, not by tone.** The tutor never says *"the answer is X"* on the first turn. It asks what the student notices, what the student has tried, where the student is stuck. It will say the next step only after the student has tried and named the obstacle. The refusal is enforced at the prompt level and surfaced in the UI as the small line under the mic icon: *"I'll ask, not tell."* - **Reads what's actually on the paper.** Pen on lined paper, pencil on graph paper, chalk on a whiteboard, sharpie on a workbook printout — Gemini 3.5 Flash multimodal handles all of them, including the half-erased substitution, the crossed-out attempt, the arrow that connects two lines on opposite sides of the page. The student writes the maths where maths belongs; the tutor reads it. - **Hears the student out loud.** The Live API holds the conversation in voice, full-duplex, with the kid interrupting freely. The tutor pauses when the student writes; it doesn't talk over the pencil. When the student starts a sentence and stops, the tutor waits. The mic stays on for the whole session. - **Knows the curriculum it's tutoring against.** The student picks the course on session start — AP Calculus AB, A-level Further Maths Pure 1, IB Math AA HL, GCSE Higher Tier, Common Core Algebra II — and the tutor speaks in the curriculum's vocabulary, with that curriculum's allowed methods. A British student sees *partial fractions* and writes ∫ in the local convention; an American student sees *u-substitution* and pronouncing-of-decimals matches the textbook. - **Refuses to do graded work.** If the student photographs a page that looks like a timed exam (a printed AP/IB question with a points value, an AQA past paper header, a Khan Academy quiz screen), the tutor names it: *"This looks like a graded assessment. I can help you study from past problems on this topic — want me to walk you through a similar problem?"* Academic integrity is a feature, not a footnote. - **Step-locked transcript.** Every spoken exchange is logged with a thumbnail of what the paper looked like at that moment. The student can scrub back to *"the bit where I got confused about why du was 1"* and replay it. Parents see the working, not just the answer. - **Built for one phone propped against books.** The camera handles glare from a desk lamp, the angle of a phone leaning against a stack of textbooks, the low-light reality of midnight homework. Auto-rotation and de-skew are silent. The student does not adjust the camera. The student writes. - **Voice carries the work.** The session can be paused; resumed the next night; replayed end-to-end as audio while the student walks to school the next morning. Closed captions sync with the paper thumbnails. ## 4. Features to build - Live API session with full-duplex voice (`gemini-3.5-flash` with `liveBidi` capability) — the tutor speaks, listens, and watches the camera frame stream in parallel - Subject + curriculum picker on session start — AP, A-level, GCSE, IB, Common Core, Edexcel, AQA, OCR, CIE Cambridge, CBSE India, ICSE India, Singapore O-level, Hong Kong DSE, Australian VCE/HSC, Canadian provincial — and a "Self-taught (tell me what you're studying)" path - Camera capture with continuous frame sampling — one frame every 2.5 seconds while the student is talking; one frame every 0.4 seconds during the *"now write your next step"* prompts; explicit frame batch at end of each Socratic turn - Handwriting multimodal parse — Gemini 3.5 Flash reads the student's working on paper, distinguishing the original printed problem from the student's pencil work, the crossings-out, the substitution boxes, the equation labels (a), (b), (c) - Diagram recognition — the student's hand-drawn right-triangle, free-body diagram, energy-level diagram, organic-chemistry mechanism arrows, graph axes; each becomes structured data the tutor can refer back to ("the triangle you drew where you labelled the hypotenuse c") - Step delta detection — the tutor sees what is new on the paper since the last turn and refers to it specifically ("I see you've written u = x — that's the choice I'd make too. What about dv?") - Socratic-mode guard — every model turn is wrapped in a server-side check that the tutor has not produced the final answer before the student has produced at least one substantive attempt; if the model slips, the turn is regenerated with a stricter prompt - Graded-assessment detector — Gemini 3.5 Flash runs in parallel on each captured frame to flag exam paper headers, question-paper watermarks, online-quiz screens, and proctored-test browser chrome; if positive, the session switches to *"study from a similar problem"* mode - Transcript timeline — every turn is logged with audio, a paper thumbnail, the model's reading of the paper at that moment, and the Socratic-step label (notice, plan, attempt, check) - Replay mode — scrub the timeline, replay the audio with the paper thumbnail in sync; export as a single MP4 (audio + paper images) for sharing with a teacher or saving to a study folder - Curriculum-grounded vocabulary — partial fractions vs. partial fraction decomposition, *u-sub* vs. *substitution*, mole ratios vs. stoichiometric ratios, force resolved vs. force component — tied to the picked curriculum - Hint ladder — within a single Socratic step the tutor offers progressively more specific hints if the student is stuck; never collapsing to the answer - Stuck-button — the student says *"I'm stuck"* (or taps a button if the room is loud and parents are asleep) → the tutor gives a smaller hint, never the full step - Step-back mode — the student can say *"can you say that again, slower"* / *"go back to the bit about du"* and the tutor walks back to that turn, with the relevant paper thumbnail highlighted in the timeline - Visual-only mode — for late-night use when the student doesn't want to talk out loud (parents asleep, dorm room, shared bedroom) — the tutor types in a side panel with the same Socratic flow, still watching the paper - Multilingual mode — the tutor can switch the spoken language ("Can we do this in Spanish? Cantonese? Tamil? Bengali? Urdu?") with curriculum vocabulary preserved in the local examination language so the student doesn't lose the exam terms - Session summary at the end — "here's what we covered, here's the step you found hardest, here are two similar problems to try tomorrow" — typed into the transcript, never sent to a parent or teacher without an explicit share action - Parent share — one-tap export of a session summary + transcript link, no personal data beyond the topic and the date - Teacher share — one-tap export of the session transcript with the step the student got stuck on flagged, so the teacher can address it in class - "Show me an easier one" — the tutor generates a similar problem at a notch easier difficulty, calibrated to the same technique, never copying a real exam question - "Show me a harder one" — same, one notch up - History — every session saved, browsable by subject, topic, date, and the Socratic-step the student got stuck on most often ## 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) - **Live API (Gemini 3.5 Flash, bidirectional)** — full-duplex voice + video session. Audio in from the student's mic; audio out to the student's earbuds; video frames in from the rear camera pointed at the paper. The session runs continuously for the duration of the homework, often 30-90 minutes. Interrupts are handled by the Live API natively; the tutor pauses when the student speaks, and the tutor speaks when the student has been silent for ≥1.6 seconds and the paper has new ink. - **Multimodal image input** (within the Live session) — reads the student's handwriting on lined or graph paper, distinguishes printed problem text from pencil working, parses hand-drawn diagrams (right triangles, free-body diagrams, organic mechanisms, graph axes), and tracks deltas between frames so the tutor refers to *"what you just wrote"* not *"what you wrote earlier"*. - **Structured output / JSON Schema** — the side-channel `SocraticTurn` schema (below) is emitted by the server after every model turn so the timeline + replay state is structured. The Live API audio is the spoken layer; the structured turn objects are the persisted record. - **Multilingual** (built into Gemini 3.5 Flash) — Cantonese for the Hong Kong DSE student, Mandarin for the CBSE-equivalent international student, Vietnamese for the San Jose student switching to her parents' language when she gets frustrated, Tamil for the Singapore O-level student, Hindi and Urdu for the South Asian diaspora student, Bengali for the Dhaka student studying for ICSE, Punjabi for the Birmingham GCSE student, Korean for the NJ student, Spanish for the Texas AP student, Portuguese for the Lisbon student, Arabic for the Manchester sixth-former, Farsi for the Tehran-educated international student, Amharic for the Addis Ababa-educated international student, Swahili for the Nairobi A-level student, Khmer for the Phnom Penh university student, Tagalog for the Manila university student. - **Curriculum grounding (Gemini 3.5 Flash long-context, weekly)** — the curriculum specifications (AP, A-level, IB, GCSE, Common Core, AQA, OCR, CIE, CBSE) are loaded as a single long-context corpus refreshed weekly by a server job. The session-time tutor receives the relevant subset of the spec for the picked curriculum and topic, so it never invents a technique the curriculum doesn't allow. - **Graded-assessment classifier (Gemini 3.5 Flash)** — a parallel side call on every 5th camera frame: *"Is this an active timed assessment (exam paper, online proctored quiz)?"* — binary + reason. If positive twice in a row, the session enters *"study from a similar problem"* mode. - **Hand-drawn diagram parse (Gemini 3.5 Flash)** — turns the student's pencil triangle into structured *{vertices: [A, B, C], labelled_sides: [{from: A, to: B, label: "c"}], labelled_angles: [{at: A, label: "θ"}]}* so the tutor can refer to *"the angle θ at vertex A"* with no ambiguity. - **Similar-problem generator (Gemini 3.5 Flash, low thinking)** — given a topic, a curriculum, and a difficulty notch, generates a fresh problem that has never been a real exam question. Tagged in the schema with `synthetic: true`. - **Thinking levels** — `medium` for the primary Live session turns (the tutor is reasoning about the student's working). `low` for the graded-assessment classifier, the similar-problem generator, and the curriculum-grounded vocabulary lookup. Surface `thoughtSummary` to the student ONLY when the student asks *"how did you read that?"* — it appears in the timeline behind a small *"(i) show how the AI read this step"* icon. ### 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 parent/teacher shares) requires the sender domain to be authorised in Firebase Auth. Sessions are private to the student account by default; parent and teacher shares are explicit per-session. - **Database — Required.** Firestore for `users`, `sessions`, `turns`, `subjects`, `curriculum_topics`, `flags`, `shares`. Real-time listeners on the active session for the live timeline view; batch writes for the persisted transcript. - **File storage — Required.** Firebase Storage for the paper thumbnail per turn + the per-turn audio clip. **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 session. Pre-signed URLs only; thumbnails and clips are never publicly addressable. - **Cloud Run server functions — Required.** All Live API session orchestration, all server-side prompts, the Socratic-mode guard, the graded-assessment side call, and the curriculum-grounding loader. The student's device never sees the Gemini API key. - **Secrets Manager — Required (auto-provisioned by AIS Build).** Holds the Gemini API key and the Firebase service-account JSON. - **Email — Required (transactional).** Parent / teacher share links via Firebase Auth magic links. - **Payments — Not needed for v1.** Free for personal use; a future *"unlimited Live API minutes"* tier could be wired to Stripe but is out of scope here. - **External APIs:** Gemini API for all intelligence; no others. **Environment variables:** every secret (Gemini API key, Firebase service-account JSON, Stripe key if a paid tier is added later) 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 student's voice, camera frames, and working pages are never sent 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 graded-assessment detector exists explicitly to prevent the app from being used as a cheating tool; this is a non-negotiable product decision. **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. **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 | |------|-------|---------------|---------------| | Live tutoring session (full-duplex voice + video) | `gemini-3.5-flash` | medium | Live API bidi, camera stream, mic stream | | Persist a Socratic turn → `SocraticTurn` schema | `gemini-3.5-flash` | low | (none) — runs server-side on the turn audio + last paper frame | | Graded-assessment classifier (every 5th frame) | `gemini-3.5-flash` | low | (none) | | Curriculum vocabulary + allowed-methods lookup | `gemini-3.5-flash` | low | (none) — long-context over the loaded specification | | Hand-drawn diagram parse → structured shape | `gemini-3.5-flash` | low | (none) | | Similar-problem generator (one notch easier / harder) | `gemini-3.5-flash` | low | (none) | | End-of-session summary | `gemini-3.5-flash` | medium | (none) — long-context over the whole session transcript | *Note for builders:* the Live API runs as a single long-running connection, not a series of `generateContent` calls. `thinkingLevel: medium` is set on the Live session config; the per-turn `SocraticTurn` persist call is a separate `generateContent` against the turn audio and the most recent paper frame. There are no TTS or image-generation calls in this template, so the *"omit `thinkingConfig` on TTS / image-gen"* rule does not bite here — but every Gemini call in this template DOES set `thinkingLevel` explicitly; none of them should be left at the SDK default. Do not serialise `n/a` cells from the matrix into the request body. ### Primary structured-output schema (seed this verbatim in the prompt) ```typescript import { z } from "zod"; const PaperFrame = z.object({ frame_uri: z.string(), // Firebase Storage gs:// URI for the captured frame captured_at_iso: z.string(), printed_problem_text: z.string().nullable(), // OCR of the printed problem on the page student_handwriting_delta: z.string(), // the new pencil work since the last frame, verbatim where readable diagrams: z.array(z.object({ type: z.enum([ "right-triangle", "general-triangle", "free-body-diagram", "energy-level-diagram", "organic-mechanism", "graph-axes", "circuit", "geometric-figure", "venn-diagram", "tree-diagram", "other", ]), structured_description: z.string(), // e.g. "right triangle, hypotenuse labelled c, angle theta at A" confidence: z.number().min(0).max(1), })), crossings_out: z.array(z.string()), // verbatim where readable reading_confidence: z.number().min(0).max(1), }); const SocraticStep = z.enum([ "notice", // What do you notice about this problem? "plan", // What technique are you going to reach for? "attempt", // Try the next line — I'll watch. "check", // Does the result make sense? Sanity-check it. "reflect", // What was the key idea you used? "stuck-hint", // Student said they're stuck; tutor gives a small hint "graded-assessment-redirect", // Detected an active exam paper; redirect to similar problem "answer-on-paper", // Final answer reached BY THE STUDENT; tutor confirms ]); const ToneFlag = z.enum([ "encouraging", "neutral-procedural", "celebratory-small-win", "patient-after-stuck", "redirect-on-graded-assessment", "academic-integrity-reminder", ]); const SocraticTurn = z.object({ turn_id: z.string(), session_id: z.string(), turn_index: z.number().int().min(0), step: SocraticStep, tone: ToneFlag, student_audio_uri: z.string().nullable(), // gs:// URI for the student's audio clip on this turn student_audio_transcript: z.string().nullable(),// transcribed student utterance, verbatim student_spoken_language: z.string(), // BCP-47, e.g. "en-US", "vi-VN", "yue-Hant-HK" tutor_audio_uri: z.string().nullable(), // gs:// URI for the tutor's audio clip on this turn tutor_spoken_transcript: z.string(), // what the tutor said, verbatim tutor_spoken_language: z.string(), paper_frame_before: PaperFrame.nullable(), paper_frame_after: PaperFrame.nullable(), delta_summary: z.string(), // "student wrote u = x, dv = e^x dx" curriculum: z.object({ spec: z.string(), // e.g. "AP Calculus AB 2024-25" topic: z.string(), // e.g. "Integration by parts" subtopic: z.string().nullable(), }), socratic_compliance: z.object({ revealed_final_answer: z.boolean(), student_attempted_before_reveal: z.boolean(), hint_specificity: z.enum(["broad", "narrow", "step-locked", "answer"]), flagged_for_regen: z.boolean(), flag_reason: z.string().nullable(), }), graded_assessment_flag: z.object({ detected: z.boolean(), detector_confidence: z.number().min(0).max(1), visible_evidence: z.string().nullable(), // "AQA logo + 'Question 4 [6 marks]' header" }), difficulty_notch: z.enum([ "below-spec", "at-spec-easy", "at-spec-typical", "at-spec-stretch", "above-spec", ]), reading_confidence: z.number().min(0).max(1), flagged_for_user_review: z.array(z.object({ field_path: z.string(), reason: z.string(), })), }); type SocraticTurn = z.infer; ``` ### Common failure modes (and how to avoid them) - Agent uses `gemini-3.5-flash` for the Live tutoring session to save quota — pin `gemini-3.5-flash` explicitly. Flash misses subtle handwriting deltas, mis-reads ł as l in non-Latin scripts, and collapses Socratic structure into one-shot answers. - Tutor reveals the final answer on turn one — server-side regen guard checks `socratic_compliance.revealed_final_answer` and `socratic_compliance.student_attempted_before_reveal`; if `revealed && !attempted`, the turn is regenerated with the *stricter* system instruction variant. - Tutor explains a technique the curriculum doesn't allow at this stage — pin the curriculum spec in the system instruction; pre-filter allowed methods from the long-context loader so the model never offers L'Hôpital's rule to a GCSE student who hasn't met derivatives yet. - Camera frame mis-identifies the printed problem as student handwriting — distinguish printed serif/sans typography from pencil handwriting on a per-frame basis; the schema separates `printed_problem_text` from `student_handwriting_delta` for exactly this reason. - Tutor talks over the student writing — the Live session must respect a *"writing pause"*: when the student stops talking AND the camera shows the hand moving across the page, the tutor waits ≥3 seconds after the hand stops before speaking. Implement via VAD on the audio channel + motion-delta heuristic on the video channel; the Live session config exposes `silenceTimeoutMs` and a custom server-side gate. - Tutor refuses to help when the page is a textbook problem that *looks* like an exam — the graded-assessment detector must require two consecutive positive frames AND a visible exam-paper signal (logo, watermark, question marks-value header) before redirecting. Textbook problems are not exam papers. - Tutor congratulates wrong working — every *"correct"* response from the tutor must follow a verified-step check on the paper frame, not just on the student's spoken claim. The student saying *"so u is x"* is not the same as the student writing *"u = x"* and the tutor seeing it. - Live session times out mid-problem — the Live API has session length limits; the client must show a soft reconnect prompt at the 90-minute mark with the state preserved. - Student switches language mid-session and the tutor stays in English — the Live session must accept a language switch on the next student utterance; the curriculum vocabulary stays in the original examination language even when the conversational language changes. - Hand-drawn diagram parsed as text — when the student draws a triangle, the model should populate `diagrams[]`, not paste *"triangle"* into `student_handwriting_delta`. - Persist call (the `SocraticTurn` write) silently includes the live audio bytes in the request body — keep audio in Firebase Storage; the persist call references the `gs://` URI, not the bytes. ### Negative constraints (hard rules) - Do NOT solve the problem in one shot. Even if the student says *"just tell me the answer"*, do not give the final answer. Offer a smaller hint. Offer to do a similar easier problem together. Offer to come back to this problem after a break. Never reveal the final answer without at least one substantive student attempt logged in the session. - Do NOT help with active timed assessments. If the graded-assessment detector fires twice in a row, switch immediately to *"study from a similar problem"* mode and do not return to the original page in this session. - Do NOT invent curriculum content. If the student is on GCSE Higher Tier and asks about a technique that is not on the spec, name it plainly: *"That's an A-level technique called integration by parts — it's the right tool, but if your teacher hasn't introduced it yet, let's use a method that's on your spec."* - Do NOT shame. If the student's first attempt is wrong, the tutor says *"okay, I can see what you tried — let's check the third line together"* not *"that's incorrect"*. Wrong working is part of the work. - Do NOT track streaks. Do NOT show points, levels, badges, or *"7-day study streak!"* notifications. This is not a gamified study app; it is a tutor. - Do NOT use the student's voice or handwriting 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 share a session transcript with a parent or teacher without an explicit per-session share action by the student. Sessions are private by default. - Do NOT roleplay as a human tutor. If the student asks *"are you a person?"* the tutor answers honestly: *"I'm a tutor running on Google's Gemini model. I can help you think through this problem."* - Do NOT take medical, mental-health, or safeguarding-disclosure conversations into the tutoring flow. If a student says something that signals distress (a one-line throwaway about not wanting to be here, about being scared of the test, about being alone), the tutor responds with one calm sentence offering a human resource for their region (Childline UK, Crisis Text Line US, Kids Help Phone Canada, headspace Australia, iCall India) and continues the homework only if the student asks to. - Do NOT invent test questions copied from real exam past papers. The similar-problem generator must produce original problems tagged `synthetic: true`; never reuse a real AP / IB / A-level past-paper question verbatim. ### 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: Live tutoring session (full-duplex voice + video) Model: `gemini-3.5-flash` · thinkingLevel: medium · Tools: Live API bidi (audio in + audio out + video in) ``` You are a patient, alert, Socratic tutor. The student is a teenager or a young adult working through a homework problem on paper. Your job is to help them think, not to do the problem for them. The session runs as a Live API conversation. The student speaks; you listen. The camera is pointed at the page on the desk; you watch what the student writes. The student writes on paper; you do not draw on their page. You always know which curriculum the student is on, which subject, which topic. The curriculum specification is provided to you as context at session start. Use only the techniques the spec allows at the student's current stage. If a technique is above the spec, name it plainly and offer the on-spec alternative. The Socratic discipline (HARD RULES): - Turn 1 of any problem: ask what the student notices about the problem. Examples: "Before anything else: what does this integral remind you of?", "Okay — what kind of force-balance is this?", "What functional group do you see in this molecule?" - Turn 2: ask what technique they want to reach for. If they don't know, offer two options to choose between, never the answer. - Turn 3+: as they write each step, watch the camera. Refer to what they actually wrote, by content, not by paraphrase. When they finish a step, ask a small check question before letting them move on. When they are stuck, give the smallest possible hint that unblocks the next single line — never a hint that reveals the next three lines. - Never say "the answer is X" before the student has written it themselves. If they finally write it and it is right, confirm it warmly and ask one reflection question ("what was the key idea you used?"). - If they finally write it and it is wrong, do not say "incorrect". Say "okay, let's check the third line together" and walk them back to the step that went wrong. Pacing rules: - When the student is writing on paper, you wait. The mic stays open, but you do not speak. Speak only after the hand has stopped for at least three seconds AND the student has either spoken or hit the "I'm stuck" button. - When the student trails off mid-sentence, wait. Do not finish their sentence for them. - When the student interrupts you, stop talking immediately. Their thought matters more than yours. - Target tutor turn length: 1-3 short sentences. Long lectures lose teenagers. Voice rules: - Voice register: calm, warm, unhurried. Slightly closer to a weekend tutor than to a classroom teacher. Never theatrical. - If the student switches into Cantonese, Mandarin, Vietnamese, Tagalog, Korean, Tamil, Hindi, Urdu, Bengali, Punjabi, Amharic, Swahili, Farsi, Khmer, Spanish, Portuguese, Arabic, French, or any other language mid-session, switch with them on the next turn. Keep the exam vocabulary (e.g. "integration by parts", "u-substitution", "stoichiometric ratio") in the original examination language so they don't lose the exam terms. - Curriculum terms are non-negotiable. AP students hear "u-substitution"; A-level students hear "integration by substitution". Use the picked spec's term every time. Camera rules: - You receive a video stream from the rear camera pointed at the page. Each frame shows the printed problem at the top of the page and the student's pencil work below. New pencil work appears between frames; refer to it specifically by content: "I see you've written u = x — that's the choice I'd make. What about dv?". Do not say "I see you've started writing" without saying what they wrote. - When the student crosses out an attempt and starts again, you acknowledge it warmly: "Okay, you've crossed that out — what made you change your mind?". Crossing out is good thinking. - When the camera frame is unreadable (glare, fingers blocking, the student moved the phone), say so once and ask them to re-prop the phone. Do not pretend to read what you cannot read. Hand-drawn diagrams: - When the student draws a triangle, a free-body diagram, a graph, an organic mechanism, refer to it by its labelled parts: "the angle θ at vertex A", "the force F₁ pointing up and to the right", "the carbonyl carbon you drew with the partial positive charge". You receive the parsed diagram structure as part of the paper frame. Academic integrity (HARD RULE): - If the page shows a printed exam paper (AQA / Edexcel / OCR / CIE / AP / IB / SAT / ACT logo or watermark; question marks-value header like "[6 marks]"; an online proctored browser chrome; a Khan Academy or proctorio-styled quiz screen), name it once and pivot: "This looks like an active assessment. I can't walk you through this one, but I can help you practice the same skill — want me to write you a similar problem on this topic?". Do not return to the original page in this session. - The graded-assessment classifier runs in parallel; you also receive its boolean. If it fires, pivot immediately. Safeguarding: - If the student says something that signals distress (one-line throwaway about not wanting to be here, about being scared, about being alone, about a parent in hospital), respond once with one calm sentence offering a human resource for their region (the client passes the region in the system context): "Hey — that sounds heavy. If you ever want to talk to someone, you can text [region-appropriate hotline] any time. Want to keep going with the problem, or take a break?". Do not press. Continue the homework only if they say yes. Honesty about what you are: - If the student asks "are you a real person?" / "are you an AI?", answer honestly in one short sentence: "I'm a tutor running on Google's Gemini model — I can help you think through this problem." Never roleplay as a named human teacher. Never: - Never say "the answer is X" on the first turn or before the student has attempted. - Never produce a final numerical answer for the student to copy. - Never congratulate without verifying on the paper frame. - Never shame, judge, or label the student's working "wrong". - Never include a streak, badge, point, or motivational pop-up in your speech. The persisted record of each turn is written separately by a server-side `generateContent` call. You do not write the SocraticTurn JSON in the Live channel; you focus on the conversation. The server reads your audio + the paper frame and emits the structured turn afterwards. Speak in short turns. Listen well. Watch the page. Wait when the hand is moving. ``` --- ### Call: Persist a Socratic turn → `SocraticTurn` schema Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` You receive: the audio clip of the most recent tutor + student exchange, the most recent two paper frames (before and after this turn), the curriculum context, and the previous turn's `SocraticTurn` record. You produce the next `SocraticTurn` record matching the schema. Hard rules: - Transcribe the student's audio verbatim. If the student switched language, record the BCP-47 code in `student_spoken_language` and transcribe in the original language. Do not translate yet. - Transcribe the tutor's spoken output verbatim into `tutor_spoken_transcript`. Do not summarise. - `student_handwriting_delta` is the verbatim new pencil text between `paper_frame_before` and `paper_frame_after`. Do not invent characters you cannot read; mark unreadable spans as "[illeg.]" and reduce `reading_confidence`. - `step` comes from the closed enum. Pick the closest. Most turns early in a problem are `notice` or `plan`; mid-problem turns are mostly `attempt` or `check`; end-of-problem is `reflect` or `answer-on-paper`. - `socratic_compliance.revealed_final_answer` is TRUE if and only if the tutor's spoken transcript contains the numerical or symbolic final answer of the problem before the student has written it. If TRUE and `socratic_compliance.student_attempted_before_reveal` is FALSE, set `flagged_for_regen: true` and write a one-sentence reason. The Live session will be re-prompted with the stricter Socratic system instruction; this turn's text will be replaced. - `graded_assessment_flag` mirrors the parallel classifier's output. Do not infer from your own reading; the classifier is authoritative for this field. - `delta_summary` is a one-sentence description of what changed on the paper since the previous turn. Examples: "student wrote u = x, dv = e^x dx", "student crossed out the previous line and wrote v = e^x instead", "no change — student spoke but did not write". - `flagged_for_user_review` names any field where confidence is below 0.7 with a one-sentence reason. Output ONLY the SocraticTurn JSON matching the provided schema. No commentary. ``` --- ### Call: Graded-assessment classifier (every 5th frame) Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` You receive one camera frame of a page on a student's desk. Your job is one binary classification: is this an active timed assessment that the student is taking right now? Positive signals: - A printed exam-board logo at the top of the page (AQA, Edexcel, OCR, CIE, IB, College Board, ACT Inc, CBSE, ICSE, Singapore Examinations and Assessment Board, Hong Kong Examinations and Assessment Authority). - A question-marks-value header ("[6 marks]", "Q4 (12)", "Total: / 75"). - A "Do not open this paper until you are told to do so" header. - A printed time limit ("1 hour 30 minutes", "Time allowed: 90 minutes"). - An online proctored browser chrome (Proctorio sidebar, ETS Test Center watermark, Khan Academy quiz-mode header, Pearson OnVUE proctor camera UI). - A candidate number / centre number box at the top. Negative signals (these are NOT graded assessments): - A textbook problem with a section number ("Section 7.2, Problem 14"). - A teacher-made worksheet without an exam-board logo. - A handwritten problem the student copied from the board. - A Khan Academy / Brilliant / Art-of-Problem-Solving problem in practice mode (no quiz timer, no submit button). - A revision guide (CGP, Pearson Revise, Kaplan, Barron's). - A past-paper PDF being used for practice OUTSIDE a timed assessment context — but be conservative; if you cannot tell whether it's being timed, prefer to flag. Output JSON: { "detected": boolean, "detector_confidence": number, // 0.0 - 1.0 "visible_evidence": string | null // one short phrase: "AQA logo + 'Question 4 [6 marks]'" } No commentary. ``` --- ### Call: Curriculum vocabulary + allowed-methods lookup Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none, long-context over the loaded specification) ``` You receive: the picked curriculum (e.g. "AP Calculus AB 2024-25", "AQA A-level Mathematics 7357, Paper 1", "IB Math AA HL 2021 syllabus", "Edexcel GCSE Mathematics 1MA1, Higher Tier", "CBSE Class 12 Mathematics 2024-25"), the picked topic (e.g. "Integration by parts", "Newton's second law", "Stoichiometry of acid-base reactions"), and the loaded specification text for the relevant pages. Output a small JSON object the Live tutoring session reads: { "spec_name": "AP Calculus AB 2024-25", "topic_name": "Integration by parts", "allowed_methods": [ "u-substitution", "integration by parts (uv - int v du)" ], "out-of-spec_methods": [ "trigonometric substitution", "partial fractions" ], "preferred_term": "u-substitution", // not "integration by substitution" "preferred_notation": "int x e^x dx", // not "int_{0}^{infty}" "common_pitfalls": [ "choosing u as e^x instead of x", "forgetting the minus sign in uv - int v du", "applying the technique to integrals that are simpler with a substitution" ], "typical_problem_difficulty_distribution": { "below-spec": 0.1, "at-spec-easy": 0.3, "at-spec-typical": 0.4, "at-spec-stretch": 0.15, "above-spec": 0.05 } } Hard rules: - `allowed_methods` is the list the tutor may reach for. Anything not on this list is out of spec — name it plainly if the student asks. - `preferred_term` is the exact phrasing the curriculum uses. Use that term in the tutor's speech. - `common_pitfalls` are the textbook errors. The tutor watches for them in the student's working and asks a sanity-check question before they happen. No commentary outside the JSON. ``` --- ### Call: Hand-drawn diagram parse → structured shape Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` You receive a cropped image of a hand-drawn diagram from the student's page. Your job is to produce a small structured description the tutor can reference unambiguously. Diagram types: - right-triangle: vertices A, B, C; right angle marker; labelled sides (a, b, c or x, y, h); labelled angles (θ, α, β). - general-triangle: vertices; labelled sides; labelled angles. - free-body-diagram: object (block, mass, particle); forces labelled with arrows (F, mg, T, N, f); coordinate system. - energy-level-diagram: horizontal lines representing energy levels; labelled E₀, E₁, E₂; transitions labelled. - organic-mechanism: starting material; arrow pushing electrons; product; partial charges (δ+, δ-); lone pairs. - graph-axes: x and y axes; labelled axes; a sketched curve or line; intercepts; turning points. - circuit: components (resistor, capacitor, battery, inductor); wires; labelled values. Output JSON: { "type": "right-triangle", "structured_description": "right triangle, right angle at C; hypotenuse from A to B labelled c; angle theta at vertex A", "labelled_parts": [ {"part": "vertex A", "label": "A"}, {"part": "vertex B", "label": "B"}, {"part": "vertex C", "label": "C", "right_angle_marker": true}, {"part": "side AB", "label": "c (hypotenuse)"}, {"part": "side BC", "label": "a"}, {"part": "side AC", "label": "b"}, {"part": "angle at A", "label": "theta"} ], "confidence": 0.91 } Hard rules: - Use the labels the student actually wrote. If they wrote θ, use "theta" in the output. If they wrote "x", use "x". - Do not invent labels the student did not write. - If the diagram is half-drawn or ambiguous, return `confidence` < 0.7 and a structured_description that says so: "partial triangle, two sides drawn, no angles labelled". No commentary outside the JSON. ``` --- ### Call: Similar-problem generator (one notch easier / harder) Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none) ``` You receive: the current problem the student is stuck on, the curriculum spec, the topic, and a difficulty notch ("at-spec-easy", "at-spec-typical", "at-spec-stretch"). You produce a fresh problem on the same topic at the requested difficulty. Hard rules: - The new problem MUST be original. Do not reuse any real AP / IB / A-level / GCSE / SAT / ACT past-paper question. Tag the output `synthetic: true`. - The new problem uses the same technique as the original (if the original needed integration by parts, the new one does too; if the original needed a Hess's law calculation, the new one does too). - The new problem uses the curriculum's preferred notation and the curriculum's preferred terms (see the vocabulary lookup output). - The new problem has one clean answer, sane numbers, no unnecessary algebra. Teenagers stuck on technique do not need extra arithmetic in the way. Output JSON: { "synthetic": true, "spec": "AP Calculus AB 2024-25", "topic": "Integration by parts", "difficulty_notch": "at-spec-easy", "problem_statement": "Evaluate the integral of x sin(x) dx.", "expected_technique": "integration by parts", "expected_answer": "-x cos(x) + sin(x) + C", "expected_pitfall": "students often pick u = sin(x), which makes du = cos(x) dx and the integral does not simplify" } No commentary outside the JSON. ``` --- ### Call: End-of-session summary Model: `gemini-3.5-flash` · thinkingLevel: medium · Tools: (none, long-context over the whole session transcript) ``` You receive: every `SocraticTurn` from the session, the picked curriculum + topic, and the elapsed time. You produce a short session summary the student sees at the end. Hard rules: - One short paragraph (≤ 80 words) on what was covered. - One sentence on the step the student found hardest, named specifically: "you got stuck on choosing u — you tried u = e^x first, then u = x, which is the standard choice". - Two suggested practice problems for tomorrow, at one notch easier and at the same level. Use the similar-problem generator output if it has already been produced this session; otherwise produce two new ones. - No grades. No scores. No "you did great!" or "needs improvement". Just the honest content summary. - One closing line in the student's spoken language at the end of the session. If the student spoke English the whole way through, the closing line is in English. Output: a single JSON object: { "session_id": string, "covered_summary": string, "hardest_step": string, "practice_problems": [ { "statement": string, "difficulty_notch": string }, { "statement": string, "difficulty_notch": string } ], "closing_line": string, "closing_language": string } No commentary outside the JSON. ``` ## 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 midnight problem set.** A sixteen-year-old in San Jose has been staring at AP Calculus AB Problem 14 for forty minutes. It's twenty-three past twelve. Her parents go to bed at ten. She opens the app, picks AP Calculus AB → Integration by parts, props the phone against a stack of textbooks pointed at the page, taps *start*. The tutor reads the problem, asks her what she notices, and the conversation begins. At one point she switches to Vietnamese — *"em không hiểu chỗ này"* — and the tutor switches with her on the next sentence, keeping *"u-substitution"* and *"integration by parts"* in English because those are the AP exam terms. By twelve forty she's written the answer and the session summary is on her phone. - **The A-level past paper.** A seventeen-year-old in Manchester is working through an AQA Further Maths Pure 1 past paper. He photographs Question 4. The graded-assessment classifier fires on the AQA logo and the *[12 marks]* header on the second consecutive frame. The tutor pivots within two sentences: *"This is an AQA paper — I can't walk you through this one. Want me to write you a similar problem on the same technique?"* The student types yes; a synthetic problem appears in the side panel; the tutor walks him through it Socratically. He goes back to the past paper alone, with the technique fresh. - **The Korean tutor cost.** A fifteen-year-old in Bergen County, NJ, opens the app at nine-thirty. His parents have already paid for one ninety-dollar-an-hour SAT tutor; they cannot afford a second. He picks SAT Math → Quadratic functions, points the phone at the prep book, and the tutor begins. His mother brings him a glass of water; she stops at the doorway because he's talking to his phone in English about completing the square and she does not want to interrupt. - **The free-body diagram.** A girl in Sydney is working through HSC Physics. She draws a block on an inclined plane in her workbook, labels the gravitational force *mg*, the normal force *N*, and the friction force *f*. The tutor's diagram-parse call recognises the free-body diagram and refers to it by its labelled parts: *"the friction force f you drew pointing up the slope — does that direction make sense for an object that's sliding down?"*. She erases the arrow and redraws it pointing down. The tutor watches the new ink, says *"okay, good — that's the direction friction acts when the object is moving down. Now let's resolve along the slope."* - **The chemistry mechanism.** A second-year undergraduate in Toronto is drawing an SN2 mechanism on lined paper. The tutor parses the curly arrow she drew from the lone pair on the hydroxide to the carbon, the partial positive charge she labelled δ+, the leaving group she labelled Br⁻. The tutor asks *"why does the arrow point at the carbon and not at the leaving group?"* and waits. - **The Bach Coach-adjacent moment.** This is not a music app — the same Socratic discipline lives here. A retired engineer learning A-level Further Maths at sixty-seven for the joy of it photographs a complex-numbers problem. The tutor's voice is the same calm, patient register — no condescending pace adjustment, no *"don't worry, it'll get easier"*. He gets stuck on the polar form; the tutor offers a smaller hint; he writes the next line; the conversation continues for forty-five minutes. - **The CBSE Class 12 stoichiometry problem.** A student in Mumbai is preparing for the CBSE board exam. She writes her balanced equation, then the mole ratio, then the limiting reagent calculation. She switches to Hindi mid-session — *"isme limiting reagent kya hai?"* — and the tutor switches with her, keeping *limiting reagent* in English because that's the CBSE term. At one point she draws a small flowchart on the side of the page connecting the moles of each reactant; the tutor parses the flowchart, refers to it, and asks her to check which arrow is the bottleneck. - **The school-laptop test.** A student in Birmingham opens the app on a Chromebook during a remote lesson and points the camera at a printed practice paper. The school's laptop has Proctorio loaded in another tab; the chrome is visible in the frame. The classifier fires; the tutor refuses; the student understands and closes the Proctorio tab to study from the practice paper outside the proctored window. - **The stuck button at one a.m.** A student in Singapore is at the eighteenth minute of staring at the same problem. She doesn't want to talk out loud — her parents are asleep next door. She taps the *stuck* button; the tutor types in the side panel *"want to try a smaller version of this problem first?"*. She nods, types yes. The tutor generates an at-spec-easy problem; she works through it in three minutes. She goes back to the hard one; she writes the next line; the tutor watches. - **The IB Math AA HL paper.** A student in Helsinki is working through an IB Math AA HL textbook problem on Maclaurin series. She has chosen Math AA HL on session start. The tutor uses *Maclaurin series*, the IB-preferred term, and refers to *standard series in the formula booklet* — also IB-specific. She finishes the problem in twenty minutes; the session summary tells her the hardest step was *"recognising when to use the standard Maclaurin series for e^x versus deriving from scratch"* and offers two practice problems for tomorrow. - **The undergraduate codebase moment.** This app's scope is high-school and early-undergraduate maths and science, but the discipline transfers: a community-college student in Houston revisiting pre-calc trigonometry holds the phone over her textbook and the tutor walks her through the unit-circle problem at her pace, switching to Spanish when she asks her grandmother in the kitchen what the Spanish word for *radian* is. ## 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 teenager's bedroom desk at night — a workbook open under a small desk lamp, a phone propped against a stack of textbooks pointed at the page, a glass of water at the edge of the frame. One paragraph: *"A live tutor that watches your paper, hears your voice, and refuses to do the problem for you."* Single Google sign-in button; Apple sign-in next to it. Below: *"Try a sample session"* → loads the demo session in section 8a. 2. **Subject + curriculum picker.** A scrollable list grouped by region (UK, US, IB, India, Singapore, Hong Kong, Australia, Canada, *"Self-taught — tell me what you're studying"*). Each option is a card with the spec name and the topics it covers. Selecting an option leads to the topic picker. 3. **Topic picker.** Within the picked spec, a list of topics. Each topic shows a one-line description and the most common sub-topics. Selecting a topic leads to the camera setup screen. 4. **Camera setup (mobile-first).** A live viewfinder with a paper-frame guide overlay. The student props the phone against books; the app coaches once: *"Get the printed problem inside the guide. We'll handle the rest."* No retries needed if the angle is okay; the app auto-rotates and de-skews silently. A *start session* button at the bottom, with a small *visual-only mode* toggle next to it (*"the room is quiet; type instead of talking"*). 5. **Live session view.** The viewfinder is the whole screen, with a slim translucent overlay at the bottom: the tutor's current spoken sentence in captions, a mic-level indicator, and a small *stuck* button. The mic and camera icons are always visible at the top corner so the student can mute or pause. The session lasts as long as the student wants; the *end session* button is in the top-left and requires a confirmation tap. 6. **Live timeline (side panel on desktop, swipe-up on mobile).** Every Socratic turn appears as a small card with the paper thumbnail, the tutor's spoken sentence, and the step label (*notice*, *plan*, *attempt*, *check*, *reflect*). Scrubbing a card replays the audio for that turn with the paper thumbnail. The graded-assessment-redirect cards are tagged with a soft warning chip. 7. **End-of-session summary.** One paragraph on what was covered. One sentence on the hardest step. Two suggested practice problems for tomorrow, generated by the similar-problem generator. A closing line in the student's spoken language. Three actions: *save to history*, *share with parent*, *share with teacher* — sharing requires explicit confirmation each time. 8. **History.** A list of past sessions, grouped by subject and date. Each session shows the topic, the duration, the hardest step, and a *replay* button that opens the timeline view. 9. **Session replay.** The timeline view with audio playback, paper thumbnails, and step labels. Scrubbable, exportable as a single MP4 (audio + paper images) for sharing with a teacher. 10. **Parent / teacher share.** A magic-link landing page showing the session summary, the topic, the duration, and the timeline. The shared view never includes the student's voice or their identity beyond their first name; the audio is replaced with the transcript text in the shared view. The student can revoke a share at any time from their history. 11. **Settings.** Spoken-language default, curriculum default, *visual-only mode* default, safeguarding-region (which hotline the tutor mentions if it ever needs to), data export (download every session as JSON), and *delete every session forever*. 12. **Footer.** *"Built for the homework hour, not the testing hour."* Privacy: *"Your sessions are private. We never train on them."* Capabilities `(i)` icon in header. ## 6b. First-visit onboarding Show a **first-visit onboarding** the first time a visitor lands 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: *"This is a tutor that won't do your homework for you."* - Subhead: *"A live conversation with a Gemini model that reads what's on your paper, hears your voice, and asks the next question a good teacher would ask. Not the answer."* - One paragraph (≤ 60 words) explaining who this is for and what makes it different from a chatbot that just answers questions: the camera watches the paper, the conversation is Socratic by hard rule, the tutor refuses to help on active exam papers, and the working stays in the student's handwriting. - Visual: a small illustration of a phone propped against a stack of books pointed at a workbook, with a faint speech bubble showing *"what does this integral remind you of?"* — not a generic chatbot icon. **Slide 2 — Try it now.** - One short prompt: *"Try the sample session."* - A live demo input pre-loaded with the calculus problem from section 8a — *Evaluate ∫ x·e^x dx* — and a scripted Socratic flow that demonstrates the Live API listening + watching pattern. - 1-2 sentences pointing at *the specific page elements* where the Gemini magic happens (the camera-watches-paper handoff, the step-locked transcript, the academic-integrity pivot when the demo paper is swapped for a printed exam). **Slide 3 — How to remix this.** - Headline: *"Make this yours."* - Three short bullets: - *"Swap the curriculum corpus in `/data/curriculum/` for your own region's spec."* - *"Adjust the prompts in `/server/prompts/` to fit your subject and language(s)."* - *"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 (Live API)** — full-duplex voice and video. The tutor speaks, listens, and watches the camera frame stream in parallel. The Live session runs continuously for the homework hour; interrupts are handled natively; the tutor pauses when the student writes. - **Gemini 3.5 Flash (multimodal)** — reads the student's handwriting on paper, distinguishes the printed problem text from the pencil working, parses hand-drawn diagrams (free-body diagrams, triangles, organic mechanisms, graph axes). - **Gemini 3.5 Flash (multilingual)** — Cantonese, Mandarin, Vietnamese, Korean, Tamil, Hindi, Urdu, Bengali, Punjabi, Amharic, Swahili, Farsi, Khmer, Tagalog, Spanish, Portuguese, Arabic, French, German, Italian, Russian, Polish, Turkish, plus English. Curriculum exam terms stay in the original examination language even when the conversational language changes. - **Gemini 3.5 Flash (long context)** — the curriculum specifications (AP, A-level, IB, GCSE, CBSE, Common Core, Edexcel, AQA, OCR, CIE) are loaded as a long-context corpus. The tutor never invents a technique the curriculum doesn't allow. - **Gemini 3.5 Flash (graded-assessment classifier)** — runs in parallel on every 5th camera frame to detect active timed assessments. Two consecutive positive frames trigger the academic-integrity pivot. - **Firebase Auth** — Google and Apple sign-in, parent/teacher shares via magic links. - **Firestore** — stores your sessions, syncs across devices in real time. - **Firebase Storage** — keeps the paper frame thumbnails and audio clips per turn. - **Cost note** — see the detailed breakdown in 6d. A typical 30-minute live tutoring session costs about $0.45 of Gemini API spend; a 90-minute session costs about $1.35. - **Privacy note** — your voice, your camera frames, and your handwriting are private to you and the people you explicitly share a session with. This app uses the Gemini API on the paid tier, where Google does not use your content for model training, per the Gemini API Additional Terms. The graded-assessment detector exists explicitly so this app cannot be used as a cheating tool; that is a non-negotiable product decision. **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) - `FIREBASE_STORAGE_BUCKET` — the Firebase Storage bucket name (must be enabled in the Firebase console — AI Studio Build does not auto-provision Storage) - `SAFEGUARDING_REGION_DEFAULT` — optional, e.g. "UK", "US", "AU", "CA", "IN", "SG" — determines which crisis-line is offered if a safeguarding signal fires **User-config requirements (flag these BEFORE shipping):** - Apple sign-in requires an Apple Developer account, Service ID, Key ID, and private key configured in the Firebase Auth console. - Magic-link parent/teacher shares require the sender domain to be authorised in Firebase Auth. - Firebase Storage must be enabled in the Firebase console before the first session runs; AIS Build provisions Firestore, Auth, Cloud Run, and Secrets Manager but not Storage. **Cost + privacy notes:** - One short paragraph per cost-sensitive capability: the Live API is billed per token of audio in + out plus per token of video in; a typical 30-minute session ≈ ~$0.45. - One short paragraph on privacy: where the data lives (your Firebase project), how to delete it (Settings → *"Delete every session forever"* — gone in 60 seconds), what is never sent for training. **Documentation links:** - AI Studio Build docs - Gemini API Live API docs (full-duplex audio + video) - Gemini API multimodal, multilingual, long-context docs - Firebase Auth, Firestore, Firebase Storage docs **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) - **Live tutoring session (Gemini 3.5 Flash, medium thinking, full-duplex audio + video)** — billed per token of audio in + audio out + video in. A 30-minute session ≈ ~0.5M total tokens (audio in ≈ ~50k, audio out ≈ ~50k, video in ≈ ~400k from one frame every 2.5s at 256x256 reduced resolution). At Pro pricing (~$1.50/M input, ~$9/M output) → ~$0.05 input + ~$0.25 output = **~$0.45 per 30-minute session**. A 90-minute session ≈ **~$1.35**. - **Persist a Socratic turn (Gemini 3.5 Flash, low thinking)** — typical turn audio ≈ ~6 seconds, paper frame at 512x512 ≈ ~5k input tokens, output ≈ ~500 tokens. ~$0.003 per turn. A 30-minute session ≈ 30 turns ≈ **~$0.09 per session**. - **Graded-assessment classifier (Gemini 3.5 Flash, low thinking)** — one frame every 12.5 seconds (every 5th of a 1-every-2.5s sample), ~3k input tokens per frame, ~50 output tokens. ~$0.0003 per frame. A 30-minute session ≈ 144 frames ≈ **~$0.04 per session**. - **Curriculum vocabulary + allowed-methods lookup (Gemini 3.5 Flash, low thinking)** — runs once per topic per session, long-context input from the loaded spec (~20k tokens), ~300 output tokens. ~$0.002 per lookup; cached per (spec, topic) for 7 days. - **Hand-drawn diagram parse (Gemini 3.5 Flash, low thinking)** — runs only when a diagram is detected on the frame; ~3k input tokens (cropped image), ~200 output tokens. ~$0.005 per diagram. - **Similar-problem generator (Gemini 3.5 Flash, low thinking)** — runs once when the student needs a swap; ~2k input tokens, ~400 output tokens. ~$0.005 per generation. - **End-of-session summary (Gemini 3.5 Flash, medium thinking, long-context over the session)** — typical 30-minute session ≈ ~30k tokens in, ~500 tokens out. ~$0.04 per session. - **Expected per-session cost (30-minute session, single topic):** ~$0.45 (Live) + ~$0.09 (persist) + ~$0.04 (classifier) + ~$0.04 (summary) = **~$0.62 per session**. - **Expected per-session cost (90-minute session, single topic):** ~$1.35 (Live) + ~$0.27 (persist) + ~$0.12 (classifier) + ~$0.04 (summary) = **~$1.78 per session**. - **Image storage:** Firebase Storage standard tier, ~$0.026/GB/month. One paper thumbnail per turn at ~80 KB (compressed JPEG) → a 30-turn session uses ~2.4 MB; a student doing one session a day uses ~75 MB/month ≈ ~$0.002/month per active student. - **Audio storage:** ~0.5 MB/turn at standard mp3; a 30-turn session uses ~15 MB; a daily user uses ~450 MB/month ≈ ~$0.01/month per active student. ## 7. Design language - **Mood:** A teenager's desk at eleven p.m. The desk-lamp warm-white circle, the workbook open, the phone propped against a stack of textbooks, the rest of the room dark. Not a classroom. Not an EdTech dashboard. The most honest version of *"a tutor is here"* — calm, attentive, present, not selling itself. Closer to a Moleskine notebook than to a SaaS product. The app should feel like it could be running on the phone in the photo on the welcome screen. - **Typography:** Clean grotesque for app chrome (Inter or Geist), and a soft humanist serif for the tutor's spoken-line captions only (Source Serif Pro or Lora). Mathematical notation in KaTeX with a slightly larger size than the body text — the working is the hero of every screen. Subject vocabulary (*u-substitution*, *integration by parts*, *Hess's law*, *limiting reagent*) in monospace where it appears as a chip — so the curriculum-grounded terms feel solid. - **Palette:** Desk-lamp warm-paper background `#F6F1E7` for the live session view, deep ink `#161310` for body text, warm coffee accent `#7A4A24` for the *step* labels and Socratic-step chips, soft graphite `#3A3A3F` for the paper thumbnails' borders. A muted teal `#2F6E6A` for the *student* turns and a muted amber `#B27A24` for the *tutor* turns — calm and warm, not classroom-cold. A faded red `#A33A2C` only for the graded-assessment redirect banner. Borrowed from a leather-bound notebook on a desk, not from a Material Design palette. - **Imagery:** The student's paper is the hero. The paper thumbnails in the timeline are the photographs the camera actually captured, not an icon. Never replace the paper with a stylised representation. The empty-state imagery is a simple line drawing of a phone propped against a stack of books pointed at a workbook — hand-drawn aesthetic, not a flat icon. - **Hand-feel touches:** A barely-visible paper grain on the live session view. The *step* labels (*notice*, *plan*, *attempt*, *check*, *reflect*) appear as small handwritten-feel tags pinned to each timeline card, in the warm coffee accent. The mic-level indicator is a thin vertical line that grows from the bottom, not a circular spinner — quiet, not theatrical. - **Spacing:** consistent 4-px base. Generous whitespace — the working needs room. - **Radius:** consistent token set (e.g. 6 / 12 / 20 px). Timeline cards use 6; the session summary card uses 12; the welcome card uses 20. - **Shadows:** subtle, layered, warm-tinted. Avoid heavy drop-shadows. - **Motion:** purposeful — entrance fades, step-label appearance, soft caption transitions. Respect `prefers-reduced-motion`. No bouncing splash animations. No theatrical hero animations. The graded-assessment-redirect banner fades in from the top of the screen with a 240 ms ease-out; reduced-motion falls back to instant. The mic-level indicator's vertical line follows the audio energy without overshoot. - **States:** every interactive element has hover, focus, active, disabled. Loading uses skeletons not spinners where possible. Empty states have helpful next-action guidance (*"Prop your phone against your books and we'll start the session"*). ## 8. Content generation rules - Write **realistic, specific copy**. NO Lorem Ipsum. NO generic placeholders like *'Your tagline here'*. - Invent plausible names, dates, problems, and seed sessions that fit the domain (use the seed content in section 8a as a starting point). When inventing problems, prefer original problems written for this template — never reuse a real AP / IB / A-level past-paper question verbatim. - Tone: warm, direct, free of corporate language. This template is for a teenager, not a company. - 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 student already speaks the jargon (the AP student wants to see *"u-substitution"*; the A-level student wants to see *"integration by substitution"*; the IB student wants to see *"the standard Maclaurin series in the formula booklet"*). - The tutor's spoken lines in the demo session must read out loud well — short sentences, plain words, no condescension. ## 8a. Seed content (use these specific examples) Anchor every generated copy + sample data point in the concrete content below. Use these names, numbers, problems, and snippets verbatim where helpful, or generate close variants that sit in the same world. **Sample sessions (sidebar):** - *"Calc — Integration by parts, Tue 11:42 pm"* (AP Calculus AB, 28 turns, 22 min) — Vietnamese-American student in San Jose; midnight session; final answer reached by the student on turn 27. - *"A-level — Newton II on incline, Wed 7:15 pm"* (AQA A-level Physics Paper 1, Mechanics, 19 turns, 16 min) — student in Manchester; resolved forces along the slope; one diagram-parse turn. - *"SAT Math — completing the square, Sat 9:30 pm"* (College Board SAT Math, 14 turns, 11 min) — Korean-American student in NJ; finished in twelve minutes; mother brought water at minute 7. - *"CBSE Chem — limiting reagent, Sun 4:10 pm"* (CBSE Class 12 Chemistry, 22 turns, 17 min) — student in Mumbai; switched to Hindi twice; tutor kept *limiting reagent* in English. - *"HSC Physics — block on incline, Fri 8:00 pm"* (NSW HSC Physics, Module 2, 17 turns, 14 min) — student in Sydney; redrew the friction-force arrow once after the tutor asked about its direction. **Sample demo session in detail (this is what the demo should show):** - **Subject:** AP Calculus AB 2024-25 — Integration by parts - **Problem on the page (printed):** *"Evaluate ∫ x · eˣ dx."* - **Session length:** 22 minutes, 28 turns - **Turn 1 — step: `notice`, tone: encouraging.** - Tutor: *"Okay. Before anything else: what does this integral remind you of? Is there a product in there?"* - Student (audio, en-US): *"…it's x times e to the x, so it's a product, yeah."* - Paper delta: none (student hasn't written anything yet). - `socratic_compliance.revealed_final_answer`: false. - **Turn 2 — step: `plan`, tone: encouraging.** - Tutor: *"Right. So which technique do we reach for when an integrand is a product of two things, one easy to differentiate and one easy to integrate?"* - Student (audio): *"Integration by parts."* - Paper delta: none. - **Turn 3 — step: `attempt`, tone: neutral-procedural.** - Tutor: *"Good. Pick your u and dv. Write them down — I'll watch."* - Student writes on paper: *u = x, dv = eˣ dx*. - Paper delta: *"u = x, dv = e^x dx"*. - Tutor: *"I see u = x and dv = e^x dx — that's the choice I'd make too. What's du, what's v?"* - **Turn 7 — step: `check`, tone: neutral-procedural.** - Student writes: *du = dx, v = eˣ*. - Tutor: *"Good — du = dx, v = e^x. Now plug into the integration-by-parts formula. What does it become?"* - **Turn 14 — student says *"I'm stuck"*, step: `stuck-hint`, tone: patient-after-stuck.** - Tutor: *"Okay — no problem. You've written x·e^x minus the integral of e^x times dx. The first term is fine. The second integral — what's the antiderivative of e^x?"* - Hint specificity: *narrow*. Did not reveal final answer. - **Turn 27 — step: `answer-on-paper`, tone: celebratory-small-win.** - Student writes: *x·eˣ − eˣ + C*. - Paper delta: *"x·e^x − e^x + C"*. - Tutor: *"Yes — x·e^x minus e^x plus C. That's it. What was the key idea you used?"* - `socratic_compliance.revealed_final_answer`: false (student wrote it first). - **Turn 28 — step: `reflect`.** - Student (audio, switching to Vietnamese): *"em chọn u là x vì khi đạo hàm nó dễ hơn."* (*"I picked u as x because the derivative is easier."*) - Tutor (responds in Vietnamese, keeps *u* and *integration by parts* in English): *"Đúng rồi — u là x vì du = dx, đơn giản. Em đã làm xong rồi. Lần sau mình thử một bài tương tự nhé."* (*"Exactly — u is x because du = dx, which is simple. You're done. Next time let's try a similar problem."*) - **End-of-session summary:** - Covered: *"Integration by parts on ∫ x·e^x dx. You chose u = x and dv = e^x dx; computed du and v; applied uv − ∫ v du; reached the answer x·e^x − e^x + C."* - Hardest step: *"choosing u and dv at the start — you took a moment before deciding x was the right choice for u."* - Practice problems for tomorrow: - Easier: *"Evaluate ∫ x·sin(x) dx."* (synthetic, at-spec-easy) - Same level: *"Evaluate ∫ x²·eˣ dx."* (synthetic, at-spec-typical; requires applying integration by parts twice). - Closing line (Vietnamese): *"Ngủ ngon nhé."* (*"Sleep well."*) **Sample input artefacts (for the build to demonstrate):** - A workbook page with the printed problem *Evaluate ∫ x·eˣ dx* at the top, lined paper below, the student's pencil work starting to appear over the course of the session. - A Physics workbook page with a printed problem about a block on a 30° incline, the student's hand-drawn free-body diagram on the right margin of the page. - A chemistry workbook page with a balanced equation at the top and the student's mole-ratio working below, including one crossed-out attempt and one corrected attempt. - A printed AQA past paper page with the *AQA* logo at the top-right and *"Question 4 [12 marks]"* header — used to demonstrate the academic-integrity pivot. - A CBSE Class 12 chemistry textbook page in English with the student's working partly in English and partly in Hindi-Devanagari script. **Sample voice copy (the tutor's spoken lines):** - Session start: *"Okay. Before anything else: what does this integral remind you of?"* - Mid-session, neutral: *"I see you've written u = x — that's the choice I'd make. What about dv?"* - Mid-session, stuck-hint: *"Okay — no problem. The first term is fine. The second integral — what's the antiderivative of e^x?"* - Mid-session, redrawn diagram: *"Okay, you've crossed that out — what made you change your mind?"* - Camera unreadable: *"Sorry — the page is hard to see from this angle. Can you tilt the phone a little towards the lamp?"* - Graded-assessment pivot: *"Hey — this looks like an active exam paper. I can't walk you through this one. Want me to write you a similar problem on the same technique?"* - Safeguarding moment (one-time): *"Hey — that sounds heavy. If you ever want to talk to someone, you can text [region hotline] any time. Want to keep going with the problem, or take a break?"* - Honesty about what the tutor is: *"I'm a tutor running on Google's Gemini model. I can help you think through this problem."* - End-of-session: *"Yes — that's it. What was the key idea you used?"* - Closing line (English default): *"Good work. Sleep well."* **Sample parent / teacher share email subject + body:** - Subject: *"Calc session summary — integration by parts, 22 min"* - Body: *"Hi — sharing tonight's homework session. I worked through ∫ x·eˣ dx with the tutor. I got stuck on choosing u and dv at the start. The session timeline is linked below if you want to see the working. — [first name]"* [Open Session] ## 9. Media & assets - **Hero image (landing screen):** A photographed-looking shot of a teenager's desk at night — a workbook open under a small desk lamp, a phone propped against a stack of textbooks pointed at the page, a glass of water at the edge of the frame, the rest of the room dark. Generate via Nano Banana 2 with a prompt emphasising *"warm desk-lamp light, workbook open with a calculus problem at the top, phone propped against a stack of textbooks pointed at the page, glass of water at the edge of frame, late evening, real worn paper, soft shadow under the books, no person in frame"*. - **App icon / wordmark:** Set in the display grotesque. Slightly worn paper texture behind it. No icon — just type. - **Empty-state illustration:** A simple line drawing of a phone propped against a stack of three books pointed at a workbook. Hand-drawn aesthetic, not a flat icon. - **Demo session paper photographs:** Generated per the prompts in section 8a — Nano Banana 2 prompts that specifically request *"workbook page with a printed calculus problem at the top, lined paper below with student's pencil working in cursive, warm desk-lamp light, no person in frame"*. Each demo paper should look photographed, not rendered. - **Curriculum picker imagery:** Each region card uses a small symbolic illustration (UK red postbox, US school bell, IB hexagon, Indian Ashoka chakra, Singapore Merlion, Hong Kong bauhinia, Australian gum leaf, Canadian maple leaf) — restrained, single-colour, never decorative-heavy. - **Stock fallbacks:** If image generation fails, fall back to the photographed sample desk from `/public/samples/sample-desk.jpg`. Never to a 📚 emoji. - **Generated imagery:** prefer Nano Banana 2 over stock photography. Prompt for warmth, asymmetry, and slight imperfection — avoid the glossy 'AI render' look. - **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"*). - Loading states use skeletons that match the eventual layout, not spinners. - Empty states explain the next action with a button whose label fits THIS app's domain: *"Prop your phone and start the session"*, *"Pick your subject"*, *"Replay this turn"* — never a generic *"Get started"*. - Smooth scroll for in-page anchors. - The Live session captions stream in token-by-token as the tutor speaks; the student's transcribed audio appears in the timeline within 800 ms of the end of their utterance. - If the Live API call fails (network blip, model hiccup), show a calm, specific error (*"We lost the tutor for a moment — tap to reconnect."*) and offer retry without losing the session state. - The graded-assessment-redirect banner fades in from the top with a 240 ms ease-out; reduced-motion falls back to instant. - The mic-level indicator is a thin vertical line that grows from the bottom, not a circular spinner. - The *stuck* button has a one-time tooltip on first appearance: *"Tap if you want a smaller hint without asking out loud."* - The student can swipe down on the timeline drawer to collapse it and see the full camera view; swipe up to expand. On desktop the timeline is a fixed right sidebar. - Sharing requires explicit confirmation each time — never a default-on toggle, never a *"remember this choice"* checkbox. ## 11. Tech & responsive requirements - **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. - **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. - **Live API session:** The Live session connects from the client to a server-side Cloud Run proxy; the proxy holds the Gemini API key and bridges the client-side WebRTC audio/video streams to the Gemini Live API. The client never speaks to the Gemini API directly. - **Model selection:** explicitly pin `gemini-3.5-flash` for the Live session, the persist call, the diagram parse, the similar-problem generator, and the end-of-session summary; `gemini-3.5-flash` for the graded-assessment classifier and the curriculum vocabulary lookup. Set `thinkingLevel` explicitly per call. - **Database:** Firestore (auto-provisioned by AI Studio Build). Real-time listeners on the active session for the live timeline view; batch writes for the persisted transcript. - **Auth:** Firebase Auth — Google sign-in by default; Apple sign-in next to it; magic-link email for parent/teacher shares. - **Storage:** Firebase Storage for paper frame thumbnails + per-turn audio clips. Pre-signed URLs only. Storage must be enabled in the Firebase console — AI Studio Build does not auto-provision Storage. - **Camera + mic:** Web Camera API for the rear camera; getUserMedia for the mic. Both must request permission once and remember it. Camera handles auto-rotation and de-skew silently on the client. - **iOS Safari gotchas (graceful degradation):** camera and mic permissions do NOT persist across page reloads on iOS — re-request on every session start and frame this as "Allow once to start tutoring" rather than a hard fail; an incoming call/Siri/alarm interrupts the audio session — listen for `MediaStreamTrack.onmute` and have the tutor pause calmly ("hold on a moment"); resume on `onunmute`; backgrounded Safari tabs throttle WebSocket and kill `getUserMedia` — combine `visibilitychange` with a screen Wake Lock so a 10-minute problem isn't dropped because the student peeked at a notification; Safari `MediaRecorder` only supports `audio/mp4` (AAC) and `video/mp4` (H.264) — stream PCM via `AudioWorklet` and feature-detect MIME types before persisting clips; iOS Safari camera tracks can drop on `orientationchange` — re-bind the stream. - **Mobile-first.** Verify layouts at 375 px (iPhone SE), 768 px (iPad), 1024 px, 1440 px+. - Use `clamp()` for fluid typography. Prefer container queries over media queries for component-level responsiveness. - Use `dvh` / `svh` instead of `vh`. Respect safe-area insets on iOS. - Zero horizontal overflow at any width. Zero layout shift on load. - Persist user data in Firestore. Use real-time listeners on the timeline view. - Optimistic UI on writes; reconcile on response. - The Live session keeps the screen awake during the session via the Screen Wake Lock API; releases on session end. - Long-context guardrail: the curriculum corpus is chunked by topic before being loaded into the session context. Never load the whole multi-million-token spec corpus into a single call — chunk by topic + subtopic so each Live session sees only the relevant ~20-30k tokens. ## 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. - All images have meaningful `alt` text. The paper thumbnails have `alt` describing the content of the page at that moment (*"workbook page with the printed problem 'Evaluate the integral of x times e to the x' and the student's working showing u = x, dv = e to the x dx"*). - Form fields have associated `