Appendix — The Guest House
05 JUL AT 08:41 PM

Appendix — The Guest House

0 LOVES 3 VIEWS
Flutter leads, web adopts.

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 kontent base.gohtml:20; legacy kon_guide.css retired + deleted 2026-06-18; no other file in static/css/ defines o-guide-lightbox rules). The lookalike docs/mockups/under_the_tuscan_sun/uts.css is 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.

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.

Review appendix — claim verification (2026-07-03)

Measured set: all 14 doc claims checked against source at the cited lines.

Claim Verdict
populateReviewAnswer @ js:1257; inline ✗/✓ markup (js:1271-1281) ✅ exact
Flutter labels + 11px/600/uppercase/--g-faint (guide_lightbox_review.dart:205,214,227-234)
Pill pad .5/.7rem = tokens gCalloutPadV/H = 8/11; radius; icons
Pill tints = .g-opt--correct/--incorrect palette byte-exact (v2:126,129)
Web review shows no explanations (gohtml:139,154,332,347 lesson-only; review :196-219 none)
Flutter review explanation gated answered+question (review.dart:134-145)
.g-explain exists (v2:100); $step.Explanation available in review loop
populateCompletionScore @ js:1112; flashcard skip :1123-1124 ✅ exact
Flutter note (completion.dart:157) inside totalQuestions > 0
Nit: --revealed #555 @ v2:586; Flutter --g-muted italic (review.dart:179-187)

Findings folded above: F1 dead rules v2:492-494 + :475 (extend or retire) · F2 hydrated-wrong-answer data gap on web (correctness exposure for answered steps is the required companion) · F3 (c)'s no-questions gate is required · F4 (b)'s gate is client-side (d-none + unhide in enterReviewMode, derefStr, question-only).

Residual (not measured): screenshots not re-reproduced (behavior verified in source only); dead-rule usage checked in the current tree only. Confidence ~95%, computed from the table above.

As-built (implemented 2026-07-04, dev-smoked)

All three proposals + the F2 companion landed. Files: guide_progress.go (service), _guide_lightbox.gohtml, kon_guide_v2.css, guide-lightbox.js, progress_test.go.

F2 mechanism chosen: hydration-payload field, not a template attr. StepResponseExport gained correct_option_id (omitempty), resolved in BuildProgressJSON from the guide's already-loaded step edges (zero extra queries). Redaction-safe by construction: response entries exist only for answered steps. JS persists it as data-correct-option-id on the step page (hydration + live submit + cached-progress mirror), cleared in resetHydrationState and the cook reset sweep. Lesson options stay neutral after hydration (parity #6 intact) — only review uses it.

Deviations from the proposal text (all flagged): 1. (b)'s "zero new CSS" became ONE new rule — .o-guide-lightbox-review-explain aligns the explanation with the answer indent (calc(28px + .75rem)); the box itself is pure .g-explain as proposed. 2. populateReviewAnswer was rebuilt DOM-node-based (no innerHTML string concat — option labels are author content; the old code parsed them as HTML). It also now clears before rebuilding, fixing a pre-existing nit where a reset flashcard's revealed text lingered in review. 3. Adjacent retirement: the dead Phase-4h leftovers GetProgressForGuides + ExportProgressForJS (zero live callers) were deleted; their 4 tests were rewritten against the LIVE path (EnrichGuideEdges + BuildProgressJSON) — which previously had zero coverage — including the hydrated-wrong-answer correct_option_id assertion.

Verification: go build ./... + full go test ./... green (4 new/rewritten tests). Dev smoke (main.test:8443, guide 3 / post 48, demo user, button-driven): (a) pills exact palette + 11px/600 uppercase labels, wrong = YOUR ANSWER/CORRECT ANSWER pair, right-first-try = single CORRECT; (b) explanations visible for all 4 answered questions, hidden pre-answer, 40px indent; (c) note hidden until complete, visible with "1/4 correct (25%)"; F2 reload test: after a pure-hydration reload, all 3 wrong answers still render their CORRECT ANSWER pills (options confirmed neutral) — the previously-impossible case. Console 0 errors.

Residuals CLOSED same day (second smoke, posts 49 + 50): guide 4 (learn, 2 flashcards + 1 MC) — completion reads "1/1 correct (100%)" with both flashcards revealed = the exact silent exclusion (c) explains, note visible; review shows the revealed-flashcard text in --g-muted italic (computed rgb(107,114,128) — the #555 fold live) with no pills (correct branch split); a revealed flashcard WITH an explanation shows the box, one without shows nothing (parity with Flutter's question-type gate — the F4 wording above was corrected accordingly). Guide 6 (cook, 1 MC) — answering set data-correct-option-id in cook; Reset Progress → confirm cleared it (the :715 sweep), classes and radios reset. Smoke progress cleaned up via Studio insights per-user resets (demo only; guides 3→0/9, 4→0/7; guide 6 self-cleaned by its reset). uts.css mockup declared FROZEN (it had already drifted pre-W12; header note added).

← Back to Under the Tuscan Sun