# Session Handoff — Tầng 2 Su Nguyen: parity 3D + bilingual showcase, kitchen L-island corrected

## Where it started
The client supplied a scanned floor plan (`Pasted_Image_20261021_161026.png`, 1206×1104) plus four reference concept images. The deliverable was an interactive, dimension-faithful 3D interior of Tầng 2 in a modern-young palette (oak, peach, smoke blue, brass, cream, bouclé), wrapped in a Vietnamese showcase page for client review. Both files must open from `file://` with no build step.

## Decisions locked + what shipped

- **Parity over program.** The plan gives PHÒNG 02 two L-shaped kitchen runs (2-burner hob on the hall-side leg, sink + tap on the south leg) and gives PHÒNG 01 no cooking appliance at all — it is a bedroom suite with bath, vanity, WC, wardrobe and the two-way stair. The 3D follows the drawing exactly. A dual self-contained-unit scheme for both rooms was considered and rejected: it is a program change (plumbing, extraction, 19 m² allowance) that would require a new drawing, not a model edit. If it is ever wanted it must be a separate, labelled “Phương án B” scene file, never an edit to the parity model.
- **Tall block → L-island corrected (final geometry change, 2026-10-21 20:25).** `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/tang2-interactive.html` lines ~663-668: the 2.35 m peach “service niche / laundry run” cabinet at x 9.72–10.40, z 2.61–4.37 read as a wardrobe. Replaced with a 0.86 m base + 0.06 m stone worktop at 0.90 m, sink bowl inset at z 3.30, brass mixer tap at z 3.00, door seams reduced to 0.80 m. Verified in the Bếp–Ăn preset.
- **Parity rebuild (earlier).** `DEPTH` 4.2 → 4.37; lift shaft x 8.05–9.55, z 2.62–4.37 flush to the south wall, doors north, P01 door re-hung to swing clear; two-flight U-stair x 3.85–6.92, z 2.58–4.29 with west landing and brass rails; wall at z 2.58 between stair and bedroom. Ortho anchors agree to Δ ≤ 0.04 m (6.915 / 9.61 / 16.96 vs drawing 6.92 / 9.62 / 17.004).
- **Theme bridge.** Scene reads `?theme=dark|light` for first paint and `postMessage({type:'tang2-theme', theme})` for live flips; the iframe `src` is never reassigned. Verified mean frame luminance 174.9 (light) vs 85.1 (dark).
- **Walk controls fixed.** Root cause was `animate()` never calling `stepWalk()` during a camera glide; keys now cancel the glide. `e.code` primary with `e.key` fallback, `preventDefault` on arrows/Space, strafe corrected to `r=(-f.z,0,f.x)`, D-pad fallback, key HUD in `#hint`.
- **Showcase layout guard.** No orphan rows: gallery 2+2, drafts 2+2, zones 3+2, palette 4+4, facts 1×5, provenance 2+2; single column ≤ 640 px. Section 05 uses a pre-cropped landscape asset rather than CSS cropping.
- **Legend + panels.** Material legend rebuilt (316×183 px, 8 rows, label + hex). All four corner panels collapse; measured heights title 65→46, views 120→41, legend 183→44, note 81→41.
- **English UI added.** `tang2-proposal.html` gained a `#langBtn` in the header and a full text/attribute dictionary; `?lang=en|vi` presets, `localStorage['tang2-lang']` persists, `<html lang>`, `<title>` and the meta description follow. The scene gets `&lang=` on first load and `postMessage({type:'tang2-lang', lang})` for live flips, with its own chrome dictionary plus language-aware `Day/Walk` and HUD labels. Verified: EN load leaves zero Vietnamese text nodes in either document, VI restores fully, no console errors.
- **Concept renders retired.** `render-01..04` (9Router `cx/gpt-image-2.5`, 1536×1024) are badged “Đã thay thế” in the page.

## Key files for next session

| File | Why |
|------|-----|
| `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/tang2-interactive.html` | The scene. ~1000 lines, single-file, Three.js 0.160.0 via CDN importmap. All geometry constants at the top (`TOTAL_LEN`, `BAYS`, `E`, `DEPTH`, `WALL_H`, `T`). |
| `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/tang2-proposal.html` | The client-facing showcase page; embeds the scene in an iframe and owns the theme toggle. |
| `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/plan-tang2-crop.png` | 1140×630 (1.81:1) crop used by section 05. Source crop was `im.crop((40,185,1180,815))`. |
| `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/Pasted_Image_20261021_161026.png` | Original plan. Scale 62.65–62.75 px/m; drawing band y ≈ 368–683. Every parity check compares against this. |
| `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/client-provided-concept/` | Four client reference JPGs — palette and material target. |
| `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/omp-concept/` | Same four renders kept as source assets; the top-level `render-0*.png` are the versions linked by the page. |

- Memory touched: `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/.memsearch/memory/2026-10-21.md` (session journal, auto-captured); plus one Mnemopi entry recording the parity-over-program decision (PHÒNG 02 = dwelling, PHÒNG 01 = suite, no kitchenette in P01, option B as a separate file).

## Running state
- Background processes: none.
- Dev servers / ports: none — both files open directly from `file://`.
- Open worktrees / branches: none (this directory is not a git repo).

## Verification — how to confirm things still work
- Extract the module and syntax-check it: `python3` regex out `<script type="module">…</script>` to `/tmp/chk.mjs`, then `node --check /tmp/chk.mjs` → prints nothing = OK. Never run `node --check` on the HTML file itself.
- Headless render: launch the cached Chromium at `~/Library/Caches/ms-playwright/chromium-1223/chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing` with `--headless=new --enable-unsafe-swiftshader --use-angle=swiftshader --disable-vulkan --allow-file-access-from-files`, drive it over raw CDP WebSocket (Node 26 has a global `WebSocket`), wait on `window.__T2.ready`, then `Page.captureScreenshot`. Expect zero `Runtime.exceptionThrown`.
- Frame selection over CDP: use `Page.getFrameTree`, then match `Runtime.executionContextCreated.params.context.auxData.frameId`. `contexts[0]` is not reliably the main frame.
- Theme: load `?theme=dark` and `?theme=light`, compare mean frame luminance (≈85 vs ≈175). Toggle in the parent and confirm the iframe URL string is unchanged.
- Toggles: click all four panel buttons, assert each height shrinks.
- Layout guard: group each grid's children by `getBoundingClientRect().top` at 1440 and 390; flag any row with a single child in a multi-column grid.

## Deferred + open questions
- Deferred: “Phương án B” — kitchenette added to PHÒNG 01 for the dual self-contained-unit scheme. Pushed because it contradicts the drawing; the user confirmed leaving the 3D as is.
- Deferred: consolidating the hob onto a single island leg (burners currently on one leg, sink on the return — which does match the plan's two runs). Not wanted.
- Open: whether the client accepts the plan's asymmetry (right room = dwelling, left room = suite).
- Note, not a defect: `xmllint --html` reports false positives for `<header>`/`<nav>`. `loading="lazy"` images appear blank in headless full-page captures until forced eager.

## Pick up here
No geometry work is outstanding. Open `/Users/thinhkhuat/in-PROD/MY-WORKBENCH/TEST-BENCH/Su_Nguyen/tang2-proposal.html` in a browser and review the showcase end to end before any further scene change.
