Files
retire-planner/MOBILE-FIX-REPORT.md
T

7.1 KiB
Raw Blame History

MOBILE PASS — REPORT

Executed by DSH (local 27B) per MOBILE-FIX.md v1. All 8 fixes implemented in site/, tested, deployed to https://retire.krisforbes.ca, and verified at 390/360/320px.

Verification method: every acceptance number below is a programmatic DOM/geometry assertion run against the live deployed site with Playwright (real Chromium, iPhone-14 390×844@3x emulation) — see audit/geo_check.py → audit/geo-check-390.json. Screenshots are archived in audit/mobile-before/ and audit/mobile-after/ for human visual review. (Note: the executing model for this run has no image input, so screenshot "vision checks" were performed as DOM/geometry assertions, not by inspecting PNGs; the PNG pairs are included for the user's eye.)

Issue → fix → evidence

1. Header ate ~15% of the phone screen (126px, ~3 rows)

Fix (style.css ≤860px): .hdr-in{flex-wrap:nowrap;padding:5px 10px}, .hdr{padding-top:env(safe-area-inset-top)}, .logo{font-size:15px}, .hdr .ghost{padding:6px 9px;font-size:13px}; ≤700px hides .hdr .pill (repeated on intro + footer); .hdr #btn-print{display:none} scoped to the header so the results-page Print button in .btn-row survives. Verified: hdr.h = 55px at 390px (spec target ≤56). Single flex row (flex-wrap:nowrap). Screenshots iphone14-quiz-province.png / -results-top.png (after) vs same names in mobile-before/. Deviation: spec suggested padding:6px 10px; with .ghost{min-height:44px} (Fix 4) 6px padding makes the header 57px (> the ≤56px target), so vertical padding is 5px. Desktop untouched.

2. Chart text unreadable (~5.8px at 390px)

Fix (app.js): axes() ticks 11→13, axis-title 11→12; G1 retire 10.5→12, runs out ≈ 12→13; G3 shortfall from ~ 12→13; G4 ticks 11→13, value labels 12.5→13.5, bar names 10.5→12. (style.css, Fix 2b below.) Verified: SVG tick font-size attr = 13; rendered tick text at 390px = 11.4 CSS px (13 × 560/640), up from ~5.8. G4 value labels 13.5 → 11.8px rendered. Spec threshold ≥11px met.

2b. Charts horizontal-scroll on phones

Fix (style.css ≤860px): .chartbox{overflow-x:auto;overflow-y:hidden}, .chartbox svg{min-width:560px}, CSS-only hint .chartbox::before{content:"swipe →"} (sticky, top-right, muted, pointer-events:none; desktop unaffected). Verified: at 390px: svg width=560px (min-width applied), #g1.scrollWidth 580 > clientWidth 356 → scrollable, hasSwipe:true.

3. Bottom sheet looked broken (58px peek, clipped chips, no scrim)

Fix: transform peek removed — sheet height is its content, chips always visible; insight hidden until .open (tap-header toggle kept, ▲/▼ affordance kept); bottom:calc(8px + env(safe-area-inset-bottom)); .scr{padding-bottom:calc(185px + env(safe-area-inset-bottom))}; new #scrim (z-index:35 < panel 40, ≤860px only) — JS toggles hidden + .on in sync with .panel.open, tapping scrim closes the sheet, showScreen() re-syncs. Verified at 390px: collapsed panel occupies y=618→836 of 844 — all 4 chip labels and values fully in view (e.g. "Savings at retirement / $959,835", fullyInView:true — no mid-glyph clip); #btn-next bottom 535 ≤ panel top 618 (Next never covered); collapsed state shows no insight (insightShownByDefault:false); after header tap: open:true, scrimVisible:true, z-order scrim 35 < panel 40 (header 30 stays under the scrim); scrim click → panelOpen:false, scrimHidden:true. Screenshots iphone14-sheet-collapsed.png, iphone14-sheet-open.png, iphone14-quiz-sheet-open.png.

4. Year-by-year table 1.8× horizontal scroll

Fix (style.css ≤560px): hide columns 4 (Non-reg) and 9 (GIS) — Unfunded kept; table{font-size:13px}; right-edge fade via .tbl-wrap::before gradient (pointer-events:none). Sticky first column + sticky header untouched. (Media block placed after the base table rules so the font-size wins.) Verified at 390px: col4Hidden:true, col9Hidden:true; table font 13px; scroll ratio 494/326 = 1.52× (was 1.83×); fadePresent:true; with .tbl-wrap.scrollLeft=168 the Age column stays pinned (ageColLeftInBox:1px, ageColVisible:true). Screenshot iphone14-table-scrolled-right.png.

5. 32px ghost buttons

Fix (style.css): .ghost{min-height:44px}; button,input{touch-action:manipulation}. Verified: every visible .ghost/.opt/.cta on the quiz at 390px is ≥44px: Back 44, Start over 44, Next 50; minTouch:44.

6. Vision card race → permanent gradient on direct load into results

Fix (app.js): manifest fetch wrapped in visionReady = Promise.race([fetch, 2500ms timeout]); on resolve and on reject, if (state.screen === 'results') renderVision(state.a) re-renders; timeout keeps the gradient. img.onerror → grad() fallback kept. Verified: fresh context, localStorage screen=results, reload → visionIsImg:true, visionGrad:false, img 356×250 (real vision/dock.jpeg for dock+travel answers). Screenshot iphone14-results-top.png (before = flat gradient).

7. Share button lied when clipboard unavailable

Fix (app.js): .writeText(url).then(done, fail); on reject or missing navigator.clipboard: window.prompt('Copy your link:', url) + label "🔗 Link shown — copy it"; "✅ Link copied" only on real success. Verified: code path + smoke shim (prompt() stub, SMOKE-OK); real-clipboard path unchanged in behavior.

8. Minor

<meta name="theme-color" content="#f7f9fc"> added → themeColor:"#f7f9fc" in-page; .legend 12.5→13px (legendPx:"13px"); .opt small 11.5→12px; Enter on number steps fires Next (3 lines in renderStep).

Gates (spec Definition of done)

  • Tests: engine.test.js 36 pass, 0 fail; ui.smoke.js SMOKE-OK (run before deploy, and after the CSS-order tweak).
  • Deploy: scripts/deploy.sh → DEPLOY-OK; origin http://192.168.0.123:8103/ grep retire-planner v1 → 1; marker comment + privacy text untouched.
  • Off-LAN (Midhurst troublebox, paramiko trouble@100.96.20.61:2222): https://retire.krisforbes.ca/ → 200, https://retire.krisforbes.ca/vision/dock.jpeg → 200.
  • Engine files (site/engine/*.js) not modified. No frameworks/builds added. Desktop layout unchanged (all mobile rules in @media ≤860/700/560).

Evidence files

  • audit/geo-check-390.json — full machine-readable assertion output (spec step 6).
  • audit/geo_check.py, audit/supp_shots.py, audit/mobile_audit_run.py (workspace copy of the Hermes audit script; only the OUT path differs — the scratch dir was read-only from this sandbox; run with TMPDIR=/tmp).
  • audit/mobile-before/ (12 PNGs) vs audit/mobile-after/ (15 PNGs, incl. sheet-collapsed / sheet-open / table-scrolled-right).

Deviations from spec text (all forced, all documented)

  1. Header .hdr-in vertical padding 5px (not 6px) — 6px + 44px ghost min-height = 57px header, breaking the spec's own ≤56px target.
  2. Header print-hide rule scoped .hdr #btn-print — spec's literal #btn-print selector would also hide the results-page Print button the spec explicitly keeps.
  3. Sheet-open state on desktop: no scrim, panel unchanged (per spec).