W12 — Flutter learn-lightbox leads: proposal to adopt on web
During UTS Phase 22.5 (Flutter learn-lightbox parity), three places surfaced where the Flutter implementation is deliberately richer than the web reference. Each was operator-reviewed and kept on Flutter (D22.5.a/b/c); this doc proposes adopting them on web so the surfaces reconverge. All three are small, additive, and shipped+smoked on Flutter — screenshots reproducible on the dev Mixology course (guide 3 / post 48).
Web-team review folded in (2026-07-03). Every claim below was verified against both codebases at the cited lines — 14/14 accurate, 0 wrong. The review surfaced 4 findings that change implementation details (not the proposal itself); they are folded into each section below and flagged [Review].
Which CSS file (verified): all web CSS edits go to
static/css/kon_guide_v2.css— it is the sole guide stylesheet (only guide<link>in kontentbase.gohtml:20; legacykon_guide.cssretired + deleted 2026-06-18; no other file instatic/css/defineso-guide-lightboxrules). The lookalikedocs/mockups/under_the_tuscan_sun/uts.cssis mockup-only and never served — but it was kept byte-identical to v2's body at retirement, so if (a)/(b)/(c) CSS lands, either sync it or declare it frozen.
(a) Review answers: pill boxes + "YOUR ANSWER" / "CORRECT ANSWER" labels
Web today (guide-lightbox.js:1257 populateReviewAnswer): inline text — ✗ You answered: X <br> ✓ Correct: Y (bi icons + text-danger/text-success).
Flutter (proposed): faint uppercase micro-labels above tinted pill boxes: - Label: YOUR ANSWER / CORRECT ANSWER (or CORRECT when right first try) — 11px/600, uppercase, --g-faint, letter-spacing .04em. - Pill: padding .5rem .7rem, radius --g-radius, bi x-lg/check-lg icon + option label. Incorrect = #fbe9eb bg / #842029 text; correct = #e9f5ee / #0f5132 (the existing .g-opt--correct/--incorrect palette — no new colors).
Why: the labeled pills scan much faster in a long review list, and reuse the exact tint/ink pairs the answer options already use during the lesson — visual continuity.
Touch points: populateReviewAnswer builds the markup. [Review — extend, don't add] Half of (a)'s CSS already exists as dead rules: Phase 11 (502f468c) landed .o-guide-lightbox-review-answer-label (kon_guide_v2.css:492) and .o-guide-lightbox-review-answer-text--correct/--incorrect (:493-494, same ink colors) but nothing — JS or template — has ever emitted them. Extend these in place (the label rule needs font-weight:600 added; the text-color rules become/get replaced by pill rules with the tint bg + padding + radius) next to the existing review styles (:479-494). Do NOT add duplicates. If this proposal is rejected, retire all three dead rules instead (bidirectional-audit rule).
[Review — known data gap on web; scope honestly] For a hydrated past-incorrect answer (page reload → review), web cannot render the "CORRECT ANSWER" pill at all: the hydration path only knows selected + is_correct and deliberately leaves the correct option neutral (guide-lightbox.js:336-338 says so explicitly); the server markup carries no correctness; correct_option arrives only on live submit. Flutter does NOT have this gap — RedactGuideAnswers (internal/http/api/v1/services/guide_redact.go:57) redacts is_correct only on unanswered steps, so the Flutter review's _findCorrectLabel works after restart. Adopting (a) as pure markup therefore still leaves a visible Flutter≠web divergence on exactly the surface it's meant to reconverge. Required companion (or an explicitly-accepted gap): expose correctness for answered steps on web under the same redaction rule — e.g. a server-emitted data attr on answered steps' options, or correct_option in the hydration payload. Small, but it touches template/backend — beyond the JS+CSS-only scope stated above.
(b) Explanation box in the review section
Web today: the review list never shows step explanations (they exist only on the lesson pages post-answer).
Flutter (proposed): answered questions in review show the standard .g-explain callout (neutral --g-tint bg + 3px --g-brand left edge, 13px --g-body) beneath the answer pills.
Why: review IS the study surface — the explanation is the payload. Omitting it forces a page-by-page hunt back through lessons.
Touch points: the review template partial already renders per-step content (_guide_lightbox.gohtml:196-219); append the explanation node and reuse the existing .g-explain class (kon_guide_v2.css:100) — zero new CSS. [Review — the gate is client-side] "Gated on answered" cannot be a template gate: the review markup is server-rendered without per-user answer state (answers hydrate via JS). Render the node d-none — exactly as the lesson pages already do (_guide_lightbox.gohtml:139) — and unhide it in enterReviewMode where the pills are populated. Two details: the field is *string, so use derefStr $step.Explanation; and mirror Flutter's precise gate — answered AND step_type == "question" (guide_lightbox_review.dart:134-145). Note flashcards ARE question steps: a revealed flashcard WITH an explanation shows the box in review on BOTH platforms (Flutter's gate includes them; smoke-verified on web, guide 4). [Corrected 2026-07-04 — this line originally claimed the opposite.]
(c) "Flashcards not included in score" note on the completion page
Web today (guide-lightbox.js:1112 populateCompletionScore): flashcards are silently skipped from the denominator (:1123-1124) — the score reads "2/4 correct (50%)" on a course with 6 questions and nobody knows why.
Flutter (proposed): one faint line under the score — Flashcards not included in
score (12px, --g-faint).
Why: makes the already-shipped exclusion legible; costs one static line.
Touch points: static node under [data-guide-score] in the completion partial. [Review — the no-questions gate is REQUIRED, not optional] populateCompletionScore writes All lessons complete! into the same scoreEl when there are zero scoreable questions, and renderCompletionSummary unhides it whenever the course is complete — so an always-on static sibling would render "Flashcards not included in score" under "All lessons complete!" (worst on a flashcards-only course). Flutter gates the note inside its totalQuestions > 0 branch (guide_lightbox_completion.dart:139-163); adopting without the gate creates a NEW cross-platform divergence in exactly the no-questions case. Toggle the note from populateCompletionScore alongside the score (show only when total > 0). [Review — dead CSS hook ready] the note's style already exists unused: .o-guide-lightbox-completion-detail (kon_guide_v2.css:475, 12px --g-faint, from the Phase 11 port) — ride it; if (c) is rejected, retire it.
Related nit (no decision needed)
Web's review flashcard revealed-answer is #555 italic (.o-guide-lightbox-review-answer--revealed, kon_guide_v2.css:586); #555 is not a g-* palette color. Flutter mapped it to --g-muted (#6B7280) italic. If (a)/(b) land, consider folding this to --g-muted for palette hygiene.