AINA Academy · Frontend & Product-Experience Lane · 2026-08-06

Every screen, walked as a real learner — what's rough, and the plan to polish it

Evidence-first inventory of the whole surface — marketing landing through lesson, practice, progress, checkout — with root causes verified in source, three sequencing options, and one recommendation. Nothing here has touched code yet: this is the plan for your ruling.

Signed-in evidence: staging under real auth, today's green S5-r1 run (31116101411), head d869e4cd Everything else: local walk of the same code, desktop 1440 + mobile 390 Staging untouched (S5 campaign in flight)

Status: PROPOSED. Plan of record: docs/plans/frontend-polish-lane.md. This lane keeps ONE plan — new asks append, never fork.

The one-paragraph verdict

The learner loop is real and works end-to-end — the lesson shell is genuinely strong. But the product currently lies to learners in small ways (invented dimension scores, a decorative readiness number that reads as yours), leaks internal machinery (raw enums, config strings, snake_case keys as learner copy), has invisible text on its three dark info cards, and dead-ends anonymous visitors on four routes. The marketing face is still the pre-V6 page you already ruled superseded, with no footer, no privacy link, and analytics that start before consent. All of it is fixable in small, reversible, screenshot-reviewed PRs — and the honest fixes should land before the new face does.

Surfaces inventory

SurfaceRouteConditionWhat the walk showed
Marketing landing/supersededOld MarketingLanding, copy variants rotate per visitor (career-switchers / team-leads). No footer, no privacy/terms, no cookie consent; PostHog inits for every visitor. V6 (PR #222) is the ruled replacement.
Sign-in / Sign-up/sign-in /sign-uproughAnonymous visitor gets the full signed-in app chrome + 6-item nav; no fallback when the auth widget can't load.
Home dashboard/ signed inroughWorks; "Welcome back, learner" placeholder, account-deletion card on the home surface, empty em-dash tiles, stale readiness number.
Diagnose → snapshot/diagnosedishonestFlow is good; but per-dimension scores are fabricated from the overall score, and the hero's fake "68/100" reads as yours.
My Path/pathfineHonest gate; loading state is a bare sentence.
Lesson shell/lessonstrong, clutteredBest screen in the product. Same takeaway rendered 3–4× at once; "Lesson 00"; internal chips visible on mobile.
Practice arena/practiceleaks"Mode: Pure Simulation · Skin: Instructor Studio" shown as a chat message; scenario card text near-invisible.
Progress/progressbroken cardRaw enum "NEXT_IN_SEQUENCE" in the heading; next-best-action card text invisible; "+0pp" jargon.
Settings (DAT-170)/settingsanon dead endSigned-in consent card shipped well; anonymous visitors get a title and then a blank page.
Checkout/checkoutbare"AI FLUENT PLUS · $349" — different product name than the landing; no what's-included, no trust signals, no way back.
Library/libraryfineClean, honest empty state.
Playground/playgroundanon dead end"Loading missions…" forever for anonymous visitors — no redirect, no error.
Memory inspector/memoryanon dead endLesson chrome + "SCORE 0/100" for anonymous; loads forever.
Workspace/workspacefineRedirects home.
Lesson preview/lesson-previewbroken cardSlide subtitle invisible on the dark card; the caption repeats the invisible line just below it.
Mobile / Admin appsnot walkedExpo app and admin need their own pass; excluded from this proof set.

Cross-cutting on mobile: the bottom nav's six items wrap "Progress" onto a lonely second row on every page, and the header logo sits in a white box on the cream header.

P0 — dishonest or broken for real users (all root-caused in source; smallest, highest-impact fixes)

P0-1

Dark info cards render near-invisible text — three surfaces, one root cause

Progress "Next best action", Practice scenario, and Lesson-preview slide all show barely-visible text on the navy card. Source says cream-on-navy — the real render says otherwise, so the failure is at the token/color-mix layer. One fix at the token level, not three per-card patches.

Progress page: next best action card with near-invisible text and raw enum NEXT_IN_SEQUENCE
Progress, staging, real auth. The dark card's heading and body are unreadable — and the heading that IS legible says "NEXT_IN_SEQUENCE" (P0-3).
Lesson preview slide with invisible dark subtitle on dark card
Lesson preview. The slide subtitle is invisible on the dark card; the caption below repeats the same sentence.
P0-2

The snapshot's per-dimension scores are invented

dimensionScores() fabricates "Tool fluency = score+8 · Output review = score−10 …" from the single overall score and presents them as measurement — at score 0 every bar clamps to "10%". Real dimension evidence exists in the progress API. Either wire the real data or label the panel honestly; a learning product cannot fake precision to learners.

Assessment snapshot: fluency by dimension bars all reading 10%, snake_case focus areas
Snapshot, staging, real auth. All four "dimensions" read 10% (the clamp floor of invented numbers). Also visible: focus areas as raw keys — tool_judgment, workflow_design, verification_habits — and "Mesa assessment features" (internal system name) in learner-facing rationale.
P0-3

Internal machinery leaks into learner copy

Raw enum NEXT_IN_SEQUENCE on Progress; snake_case focus areas on the snapshot; "Mesa" jargon; practice chat opens with "Mode: Pure Simulation · Skin: Instructor Studio"; mobile lesson shows demo:prompt_task_success_criteria and lesson_id / learner_work chips. One label-map + copy pass, one PR.

Practice arena mobile: internal mode/skin config shown as chat message, dark scenario card barely readable
Practice, mobile. Internal config surfaces as the first "message" a learner reads; the scenario card repeats the contrast bug.
Lesson mobile: raw demo tokens and lesson_id/learner_work chips visible
Lesson, mobile. demo:prompt_task_success_criteria and raw chips visible below the mission card.
P0-4

Anonymous dead ends on four routes

/settings renders a heading then nothing; /playground and /memory spin forever; sign-in renders the app shell with no fallback. Every guarded route should either redirect to sign-in or say plainly what's needed. One route-guard consistency pass.

Settings page blank for anonymous visitor
/settings, anonymous. "Manage how AINA works for your account." — then a blank page.
Playground stuck loading missions forever
/playground, anonymous. "Loading missions…" never resolves.

P1 — coherence & trust (your taste calls live here)

P1-5

V6 landing reconcile — the centerpiece

PR #222 carries your ruled V6 direction (pi.dev-style sticky product-demo scroll) and is now behind main. Plan: rebase onto current main — reconcile, never fork — wire its CTAs to the real /diagnose and /dashboard routes (in-page anchors today), design the footer + DAT-171 consent banner with the page, retire MarketingLanding in the same motion, keep the E2E harness selectors true in the same PR. I'll bring 2–3 cheap in-context variants of the contested calls (hero copy angle, CTA strategy) for you to react to first.

Current pre-V6 marketing landing, team-leads copy variant
Current landing (team-leads variant), local walk. Nine-line serif hero, abstract product card, no footer, no privacy link, no consent — and a second copy variant (career-switchers) rotates in for other visitors. This whole page is scheduled to be replaced by V6.
P1-6

The funnel tells three different stories

Landing sells "$349 · L1-L5 Spine + Credential" via waitlist; checkout sells "AI FLUENT PLUS · $349"; the header offers Sign in; on scroll, two identical waitlist CTAs stack on top of each other. One product name, one primary CTA, one anon → sign-up → paid story. Blocked only on your naming/positioning call (Question 3 below).

Checkout page: bare AI Fluent Plus card at $349
/checkout. A different product name than the landing, no what's-included, no refund/security reassurance, no back path.
P1-7

Home dashboard truthfulness

"Welcome back, learner" (name is available); account deletion parked on the home surface (belongs in /settings — natural DAT-170 extension); readiness circle stays 0 after a lesson moved fluency to 3% on Progress; em-dash empty tiles.

Signed-in home dashboard with placeholder greeting and delete-account card
Home, staging, real auth. Placeholder greeting up top; "Delete my account & data" as a first-class home card at the bottom.
Home dashboard mobile: tall empty stat tiles and delete card
Home, mobile. Full-width empty tiles stack into a long scroll that ends at the deletion card.
P1-8

Diagnose hero shows a fake score as if it were yours

"YOUR READINESS · 68/100 · Building fluency · Confidence 56-78%" is decoration rendered before you've answered anything. Label it as a sample report or replace it with product truth.

Diagnose hero with fake 68/100 readiness panel
/diagnose, staging. The panel reads as the learner's own score. It's a hard-coded prop.
P1-9

Lesson screen repeats itself

The same takeaway paragraph renders up to four times on one screen (chat body, right-rail KEY TAKEAWAY, MISSION CARD, "The durable move"). And lessons are zero-indexed learner-facing ("Lesson 00"). Collapse the duplication; human-number the lessons.

Lesson shell with the same takeaway text repeated in four places
Lesson, staging, real auth. Strong bones — the duplication is the tell that three panels are fed the same string.

P2 — polish

P2-10

Mobile nav + brand details

Six bottom-nav items wrap "Progress" onto its own row on every mobile page; the header logo ships with a white background on the cream header; page-title styles mix "—" and "-"; path/playground/memory load as bare sentences instead of skeletons; checkout needs its what's-included + trust block.

Sequencing — three options, one recommendation

Recommended

Option A — truth first, then the new face, then coherence

Wave 1 (P0): 2–3 small PRs — contrast root-cause · honest dimensions · label/leak pass + route guards. Each with screenshots for your label, sequenced between S5 rounds. Wave 2 (V6): #222 rebased and wired, footer + CMP banner designed in, old landing retired; you react to 2–3 in-context variants before the PR. Wave 3 (P1/P2): funnel naming, home truthfulness, lesson dedup, then polish. Why: the P0s are honesty bugs live in front of real users today, they're already root-caused and tiny, and V6 deserves your unhurried taste pass — not a rush job.

Option B — V6 first

Biggest visible change lands first; P0 fixes follow. Cost: learners keep hitting invisible text and invented numbers meanwhile, and the #222 rebase gets riskier if Wave-1 style fixes land after it anyway.

Option C — one big polish PR

Rejected: un-reviewable under the founder gate, one giant QA receipt, and it collides with the staging campaign's merge sequencing.

Three questions for you (nothing blocks Wave 1)

  1. Launch audience of record — the old landing rotates career-switcher and team-lead pitches; the meta description says working professionals. Which voice does V6 speak in? (blocks Wave-2 copy)
  2. CMP pick — you said you have consent-management tools chosen (DAT-171). Which one(s), so the V6 footer/banner integrates it rather than hand-rolling. (blocks the consent part of Wave 2)
  3. Product name + primary CTA — "AI Fluent Plus" vs "L1-L5 Spine + Credential"; waitlist vs direct sign-up on the landing. (blocks the Wave-3 funnel PR)

Rails this lane honors, every wave

All product frontend is founder-gated (D-039 am.2/3): you personally see screenshots before anything merges; I never self-apply your label. The E2E journey harness navigates the real UI — aria-labels, routes, roles, CTA text are load-bearing proof selectors; any rename updates scripts/e2e/headless-full-journey.ts in the same PR, impact noted in the PR body. Merges to main redeploy staging — sequenced with the S5 campaign session between rounds; deploy.yml is never dispatched. Design language extends global.css + @aina-academy/ui tokens — no forks. One plan (docs/plans/frontend-polish-lane.md); asks append to it.