Goal
Implement exterior-360, hotspot, and poi-detail modules — the screen Nadezhda's IA pivots on. First visible Riviera 360° in the React app, consuming the Phase 1 Pannellum wrapper + fixture POIs.
Council Review (CONV-30) amendments apply — see
plans/sales-app-react-module-sequence.md§Council Review. Phase 2 amendments: A-SEQ-2 (two-stage page build — Phase 2 ships minimal route mounting<Exterior360>), A-DEMO-3 (author disabled-CTA tooltip copy), A-BRAND-5 (document cote/velvet visual gap in Storybook decorator).Per-module 4-step design loop (CONV-30 amendment A-DEVUX-1): every module below is photo-heavy → skip wireframes; brief Roman first, build real-stub-with-real-assets, live-iterate via
/inspect+ optional/make-tweakable. PAUSE checkboxes are mandatory.Drift-check (A-DEVUX-5): task list reordered to match What's Next (asset-fallback first, then hotspot, poi-detail, exterior-360, then route).
Tasks
- [ ]
src/lib/asset-fallback.tsx—<AssetOrPlaceholder src alt kind="render|panorama">component; on missing/load-fail renders Skeleton-White card with "Render available on request" copy in JetBrains Mono small caps. Used by all three anchor modules + Phase 5 ProjectTour.
Hotspot module
- [ ] PAUSE: Brief Roman on Hotspot module before coding (loop step 1) — what's on the marker, what's NOT, hover/focus states, accent treatment.
- [ ]
src/features/hotspot/Hotspot.tsx— small clickable marker at Pannellum hotspot pixel position; two visual states ('in'interior building parts,'out'surrounding POIs); hover lifts opacity + shadcn Tooltip label. - [ ]
src/features/hotspot/Hotspot.stories.tsx+README.md(rewrite stub). - [ ] PAUSE: Live-iterate with Roman in browser (loop step 4) —
/inspectfor size/colour/positioning tweaks; optional/make-tweakablepanel. - [ ] AU English check (per A-DEVUX-4): grep
-iE 'colorize|organize|behavior|catalog\b'on touched files returns 0 hits.
PoiDetail module
- [ ] PAUSE: Brief Roman on PoiDetail module before coding — sheet width, image treatment, copy hierarchy, CTA copy (use A-DEMO-3 register: "Interior walkthrough — available on request from your advisor").
- [ ]
src/features/poi-detail/PoiDetail.tsx— slide-in detail (shadcn Sheet, right side w=420); receivespoi: Poi; large image with<AssetOrPlaceholder>fallback; title + copy paragraph + optional CTA (disabled with A-DEMO-3 tooltip if destination 360° node missing). - [ ]
src/features/poi-detail/PoiDetail.stories.tsx+README.md(rewrite stub). - [ ] PAUSE: Live-iterate with Roman in browser.
- [ ] AU English check on touched files.
Exterior360 module
- [ ] PAUSE: Brief Roman on Exterior360 module before coding — chrome over panorama, scrim opacity, TopBar overlay z-index, hotspot density.
- [ ]
src/features/exterior-360/Exterior360.tsx— composes<Panorama>(withloadingslot per A-DEMO-1) + scrim gradient overlay + slot for<TopBar>overlay + slot for<InfoPanel>overlay; receivesproject: Project,pois: Poi[],onPoiClick. Phase 2 Server-Component route mounts this directly per A-SEQ-2 — no JourneyShell wrapping yet (that lands in Phase 5). - [ ]
src/features/exterior-360/Exterior360.stories.tsx— render against the fixture; combined storywith-poisshows panorama + 6 hotspots + open PoiDetail. - [ ] Rewrite
src/features/exterior-360/README.md— real spec (purpose, props, states, used by). - [ ] PAUSE: Live-iterate Exterior360 with Roman in browser — both Storybook and the minimal Phase 2 route at
/projects/riviera./inspect+/make-tweakablefor chrome polish. - [ ] AU English check on touched files.
Phase 2 minimal route (A-SEQ-2)
- [ ]
src/app/projects/[slug]/page.tsx— Server Component,const { slug } = await params;. Loads project viagetProject(slug)(A-HEALTH-5 helper). Mounts<Exterior360>directly with no JourneyShell wrapping. (Phase 5 enriches this with JourneyShell + Welcome routing + URL state.)
Smoke + verification
- [ ] Smoke: each module renders standalone in Storybook + combined story works + Vitest smoke for
Exterior360. - [ ] Phase-gate handoff record (A-DEVUX-3): /handoff at end of Phase 2 writes
Gate-passed: phase-2to session, lists Storybook URLs Roman walked, appends signed-off note to this workstream's Session Log.
What's Next
Confirm BOTH blockers are complete:
sales-app-react-foundation— types extended (Project/Unit/Layout/Floor/Floorplate/TourNode/Building per A-BUYUX-4; UnitStatus + Aspect as discriminated unions per A-IMPL-4), Riviera fixture populated, Pannellum wrapper story works (or fallback to next/dynamic iframe per A-RES-1), buyer-profile Zustand store exists (A-SCALE-1 + A-RES-3), AssetOrPlaceholder helper exists, getProject helper exists (A-HEALTH-5), Wordmark.tsx extracted (A-BRAND-4), ESLint bare-string rule + Vitest fixture-token assertion shipped (A-HEALTH-1).brand-language-and-identity—os/docs/design/visual-register.mdratified (per A-BRAND-3 minimum-viable subset is OK if full 8 dimensions not yet locked), 8-dimension brief locked (or partial per A-BRAND-3), wordmark + icon system + photographic + motion + status palette decisions made, tone-of-voice/copy register decided per A-BRAND-1 (fixture re-pass if needed).
Then run the per-module 4-step design loop for each of: Hotspot → PoiDetail → Exterior360. All photo-heavy → skip wireframes, go straight to real-stub-with-real-assets. Brief Roman first ("here's what's on screen, what's not, why"). Implement. Live-iterate via /inspect and optionally /make-tweakable. Phase-end sign-off (A-DEVUX-3 evidence trail) before sales-app-react-selection unblocks.
Start with src/lib/asset-fallback.tsx (small helper, all three anchor modules consume), then Hotspot, then PoiDetail, then Exterior360 (composes the others), then the minimal Server-Component route per A-SEQ-2.
Key Context
- Plan:
plans/sales-app-react-module-sequence.mdPhase 2 - Path 1 reference for visual register:
os/design-system/sales/02-exterior.html(Pannellum scrim gradient + grid topbar pattern; reference only, do not port code 1:1) - Phase 1 must be DONE before this workstream starts. If
sales-app-react-foundationis still in progress, do not start anchor work — switch back to foundation. - Eval pack: see plan §Phase 2 evaluation table (artefact = Storybook combined story URL + screenshot; 2-min check = Roman drags panorama + clicks 2 hotspots + sees Riviera content)
Session Log
- CONV-30 (2026-05-09): Workstream created via
/planratification ofsales-app-react-module-sequence - CONV-30 (2026-05-11, buyer-journey track close): Plan integrated post-council (7 CRITICAL + 22 HIGH + 7 MEDIUM amendments inline), renamed from path2- slug to sales-app-react-, parallel brand-language-and-identity track established. State ratified, ready for /build (or /plan plans/brand-language-and-identity.md for the brand track).