← back to Quadrille Showroom

docs/ROOM-TYPES-SPEC.md

528 lines

# ROOM_TYPES Spec — Quadrille 3D Showroom

**Status:** DESIGN ONLY. No implementation. This is a registry spec for a future
`ROOM_TYPES` object that would sit parallel to the existing `THEMES` / `MODES`
registries. Nothing here edits `showroom.js`, `viewmodes.js`, or any code file.

**Purpose:** Today the showroom is a single fixed environment — a 6.10 × 6.10 × 2.9 m
PJ-style white-box gallery with a 50-board wingboard arc, one consultation nook, one
olive tree, and a 3-point warm lighting rig (`showroom.js` lines ~692–939, ~963–993).
This spec defines FIVE alternate *room presets* — **BEDROOM, OFFICE, HOTEL LOBBY,
LIVING ROOM, ART GALLERY** — so a shopper can preview the clad Quadrille pattern in a
realistic furnished context instead of only the sample-board arc. Each preset is a
data record: room geometry, which surfaces get clad vs painted, floor/ceiling
material + a matching ceiling fixture, a furniture set with concrete positions, a
lighting rig, a default camera pose, and a hook for shifting the furniture palette to
match the wallcovering's `hue` / `color_bucket`.

---

## 0. Grounding conventions (read before the records)

These are the *actual* conventions the existing code uses. Every position/rotation in
this spec is expressed in the same frame so a future implementer can drop values in
without re-deriving them.

**Coordinate frame (from `buildRoom`, `buildFurniture`, `buildAvatar`):**
- Room is centered on the origin. `W = width (x)`, `D = depth (z)`, `H = height (y)`.
- `x`: right = `+x`, left = `-x`. Half-width `= W/2`.
- `z`: **the viewer faces `-z`.** The **back wall** (the primary feature surface, the
  one the wing-arc hangs on) sits at `z = -D/2`. The **front wall** (doorway + DW
  sign) sits at `z = +D/2`. So "deeper into the room / toward the feature wall" = more
  negative z.
- `y`: floor at `y = 0`, ceiling at `y = H`. Eye height ≈ 1.6 m.
- Wall rotations (`addWall(x,y,z,w,h,rotY,mat)`): back `rotY=0`, left `rotY=π/2`,
  right `rotY=-π/2`, front `rotY=π`.
- `roomWalls[]` is the cladding registry — each entry `{ side, mesh, w, h, origMat }`.
  Clicking a pattern re-maps `.map` on the clad walls; painted walls keep `origMat`.
  `window._wallSides` is the canonical side list. **This spec's "clad vs painted"
  column tells a future builder which `roomWalls[]` entries receive the pattern map
  and which keep a flat paint material.**

**Everything is a THREE primitive.** No GLTF. Floors/ceilings/walls are
`PlaneGeometry`; furniture is composed from `BoxGeometry` / `CylinderGeometry` /
`SphereGeometry` / `IcosahedronGeometry` groups. Every furniture record below is
buildable from the primitive helpers already in the file (`capsuleMesh`,
`buildConsultationNook`, `buildOliveTree`, `buildBookShelf`, plus the proto
`sofa()` in `v9-walkin.html`).

**Material library available now (`initMaterials`, lines ~375–416):**
`MAT.floor` (warm oak), `MAT.ceiling` (warm plaster), `MAT.wall` / `MAT.brick`
(photoreal limewash + normal/rough maps), `MAT.chrome` (brushed brass),
`MAT.wood` (satin walnut), `MAT.chair` (cognac leather), `MAT.dark`, `MAT.frame`,
`MAT.lightPanel` (emissive white), `MAT.baseboard`, `MAT.books[8]`.

**Textures on disk (`public/textures/`):** `floor-oak.png` (+ normal/rough),
`floor-stone.png`, `wall-limewash.png` (+ normal/rough). New floor looks
(carpet / marble / polished-concrete) do NOT have a bundled texture yet — this spec
flags them as **"needs texture or procedural fallback"** so they're honest about the
asset gap. `floor-oak` and `floor-stone` are the only two floor albedos that exist.

**Lighting rig shape today (lines ~895–939):** `AmbientLight` + `HemisphereLight`
(both ~0.42) + a shadow-casting `DirectionalLight` KEY (1.05, warm) + a cool no-shadow
`DirectionalLight` FILL (0.30) + a warm `DirectionalLight` RIM (0.22) + a
`SpotLight` picture-light (0.55) grazing the feature wall. `MODES` already scales
these per view (`Gallery` / `Evening`). Each record below re-tunes those five channels
plus adds the fixture's own emissive.

**Ceiling fixtures are emissive primitives, not real area lights** (matches the
existing emissive cove-strip approach at lines ~726–754 — emissive `MeshBasicMaterial`
+ optional additive-glow quad = self-lit, zero light cost). A record's *fixture*
gives the visible object; the *rig* gives the actual illumination. Where a fixture
should also throw light (chandelier, pendant), the record adds one cheap `PointLight`
co-located with the emissive mesh.

---

## 1. BEDROOM

Intimate, warm, low-key. The pattern reads as a **behind-the-bed accent wall** — the
classic residential wallcovering placement.

| Field | Value |
|---|---|
| **Geometry (W×D×H)** | `4.6 × 5.0 × 2.7` m — smaller & lower than the square default; a real bedroom, cozier ceiling. |
| **Clad surfaces** | **Back wall only** (`side:'back'`, the headboard wall) gets the pattern. Left/right/front = painted. |
| **Painted surfaces** | Left/right/front walls = soft warm off-white (`MAT.wall` tinted, or a flat `0xece4d6`). |
| **Floor** | Warm oak — `floor-oak.png` (exists), repeat ~2.0. Add a soft area rug under the bed (see furniture) so it doesn't read cold. |
| **Ceiling** | Flat matte warm off-white (`0xf2eee5`, like current). |
| **Ceiling fixture** | **Single pendant** dropped over the bed centerline — a slim brass stem (`CylinderGeometry` `MAT.chrome`) + a linen drum shade (`CylinderGeometry`, emissive `0xffe6c0` ~0.5). Co-locate one warm `PointLight` inside it. Position `(0, H-0.55, -D/2+1.4)`. |
| **Mood** | Warm-dim. |

**Furniture set + layout** (room-center origin, faces `-z`, back wall = headboard):
- **Bed** — platform: `BoxGeometry(1.8, 0.35, 2.1)` mattress on a low walnut frame
  (`MAT.wood`). Headboard: `BoxGeometry(1.9, 1.0, 0.12)` against the back wall.
  Position bed center `(0, 0.35, -D/2 + 1.35)`, headboard `(0, 0.9, -D/2 + 0.35)`,
  rotation `y=0` (headboard flush to the clad wall). Duvet = a slightly larger soft
  box in bouclé cream (`0xe8e0d0`).
- **Nightstands** ×2 — `BoxGeometry(0.5, 0.5, 0.4)` walnut, one each side of the bed:
  `(-1.15, 0.25, -D/2 + 0.55)` and `(+1.15, 0.25, -D/2 + 0.55)`. A small emissive
  table-lamp glow on each (echoes the proto lamp shade).
- **Dresser** — `BoxGeometry(1.4, 0.85, 0.5)` walnut against the **left** wall:
  `(-W/2 + 0.3, 0.42, 0.4)`, rotation `y=π/2`. Optional framed mirror above it.
- **Area rug** — `CircleGeometry` or rounded box, `2.6 × 1.8`, muted wool
  (`0xb9a88c`), under the foot of the bed `(0, 0.012, -D/2 + 2.6)`.

**Lighting rig** (warm-dim):
- Ambient `0xfff0dd` @ **0.30** (lower than default — intimate).
- Hemisphere `0xfff1e0 / 0xe4d8c4` @ **0.28**.
- KEY DirectionalLight `0xfff2e0` @ **0.75**, from `(1.6, H+0.8, 1.6)` targeting the
  bed — soft, shadow on, wide PCF radius (~4).
- FILL cool `0xdfe6ff` @ **0.18** (barely lifts shadows).
- Pendant `PointLight` `0xffcaa0` @ **0.6**, range ~5, at the fixture position — the
  dominant *felt* source, pooling warm light on the accent wall + bed.
- No hard RIM; keep contrast gentle.

**Camera framing:** stand near the foot of the bed looking at the headboard/accent
wall. `position (0, 1.55, D/2 - 1.4)`, `lookAt (0, 1.4, -D/2 + 0.4)`, fov 55. Frames
the clad back wall behind the bed with the pendant in the upper third.

**Furniture-style-matching hook:** shift the **duvet + rug + one accent throw** to a
tone drawn from the product. Read `color_bucket`; if warm (red/orange/gold/brown) push
the duvet toward warm cream + a terracotta throw; if cool (blue/green/purple) push the
rug toward greige + a slate throw. Bed frame + nightstands stay walnut (neutral
anchor) so only the *soft goods* chase the hue — 60-30-10: walls (pattern) = 60,
walnut = 30, textile accent tuned to `hue` = 10.

---

## 2. OFFICE

Neutral-bright, crisp, productive. Pattern on the **wall behind the desk** (the
video-call / focal wall).

| Field | Value |
|---|---|
| **Geometry (W×D×H)** | `4.8 × 5.2 × 2.8` m — compact private office. |
| **Clad surfaces** | **Back wall only** (the wall the desk faces away from / the focal wall behind the seated person). Others painted. |
| **Painted surfaces** | Left/right/front = cool neutral white (`0xf1f1ee`). |
| **Floor** | Polished concrete **(needs texture or procedural fallback — no `floor-concrete.png` yet)**; interim = `floor-stone.png` (exists) at low warmth, or a flat `MeshStandardMaterial(0x9a9a98, roughness 0.35)`. |
| **Ceiling** | Flat off-white with **recessed / track grid** cue. |
| **Ceiling fixture** | **Recessed troffers OR a track**: two emissive linear strips (reuse the existing cove-strip pattern, but cooler `0xf6f8ff` and brighter) running front→back at `x = ±W*0.22`, plus optional 3 small emissive downlight discs over the desk. Neutral-white emissive. |
| **Mood** | Neutral-bright (highest overall illumination of the five). |

**Furniture set + layout** (desk faces the viewer / `+z`, so the clad back wall is
behind the seat):
- **Desk** — top `BoxGeometry(1.6, 0.05, 0.75)` walnut (`MAT.wood`) at height 0.74,
  two panel legs. Center `(0, 0.74, -D/2 + 1.1)`, rotation `y=0` (long edge parallel
  to back wall). A slim emissive monitor plane + a couple swatch/paper boxes on top.
- **Office chair** — swivel: `CylinderGeometry` 5-star base + gas column + a
  `BoxGeometry` seat + curved back, cognac leather (`MAT.chair`). Position between
  desk and back wall `(0, 0, -D/2 + 0.55)`, rotation `y=π` (faces `+z`, back to the
  clad wall). Contact shadow under the base.
- **Shelving** — reuse `buildBookShelf(x, z, rot, unitW)` verbatim. Place against the
  **right** wall: `buildBookShelf(W/2 - 0.2, 0, -Math.PI/2, 2.4)`. `MAT.books[8]` give
  color. Optionally a second unit on the left for symmetry.
- Optional **guest chair** — one bouclé armchair (reuse the nook chair builder) in the
  near corner `(W/2 - 0.9, 0, D/2 - 1.2)`, angled toward the desk.

**Lighting rig** (neutral-bright):
- Ambient `0xffffff` @ **0.5**.
- Hemisphere `0xf4f6ff / 0xdedad2` @ **0.45**.
- KEY DirectionalLight near-white `0xfff8f2` @ **1.0**, from `(2.0, H+1.0, 2.0)`,
  shadow on, medium-soft radius (~2.5) — crisper contact than bedroom.
- FILL cool `0xeef2ff` @ **0.35** — office fills are stronger so the room reads even
  and flat-lit, no drama.
- Recessed/track emissive strips carry the ceiling read; add one faint `PointLight`
  `0xf6f8ff` @ 0.25 over the desk for a subtle pool.

**Camera framing:** from the doorway looking across the desk at the clad focal wall.
`position (0, 1.6, D/2 - 1.0)`, `lookAt (0, 1.35, -D/2 + 0.3)`, fov 58. Desk in mid-
ground, pattern fills the back wall.

**Furniture-style-matching hook:** the **desk + shelving stay walnut/neutral**
(office reads professional, not costume-y). Chase the hue only in the **chair
upholstery + the book-block colors**: if the product `color_bucket` is cool, bias the
chair to a slate/charcoal fabric and re-weight `MAT.books` toward blues/greens; if
warm, keep cognac leather and warm the book palette. Keep saturation *low* here — an
office palette should be muted so the pattern is the only saturated element.

---

## 3. HOTEL LOBBY

Dramatic, generous, double-height. This is the **large** preset. Pattern on a **tall
feature wall behind the reception desk** — the money shot.

| Field | Value |
|---|---|
| **Geometry (W×D×H)** | `8.0 × 8.0 × 4.2` m — noticeably larger + taller than the default; a lobby needs volume and a double-height feature wall. |
| **Clad surfaces** | **Back wall (full height, behind reception)** is the hero clad surface. Optionally **also** clad the **front wall** as a secondary accent for a wrapped, enveloping feel. Left/right painted. |
| **Painted surfaces** | Left/right = warm stone plaster (`MAT.wall`), lets the feature wall dominate. |
| **Floor** | Polished stone / marble — `floor-stone.png` (exists) at ~2.4 repeat, high anisotropy for a reflective sheen; nudge `roughness` down (~0.3) for a lobby polish. True marble veining = **would benefit from a marble texture (not bundled)**; `floor-stone` is the honest interim. |
| **Ceiling** | Coffered / recessed-cove; taller so it reads grand. |
| **Ceiling fixture** | **Chandelier** — the statement piece. A brass ring/tiered cluster of small emissive spheres (`SphereGeometry`, emissive `0xffe6bd`) hung on a brass stem, centered over the seating group `(0, H-1.1, 0.2)`. Co-locate one warm `PointLight` @ ~0.9 range ~10. This is the one preset where the fixture is a focal object, not just a cue. |
| **Mood** | Dramatic (highest contrast — bright feature wall, pooled warm seating, darker perimeter). |

**Furniture set + layout** (larger room, origin center, reception against the clad
back wall):
- **Reception desk** — a long low curved/straight counter: `BoxGeometry(3.0, 1.1,
  0.7)` walnut front + a stone-cap top, against the back wall `(0, 0.55, -D/2 + 0.9)`,
  rotation `y=0`. A backlit logo plane can sit on the clad wall behind it.
- **Lobby sofa** — reuse the proto `sofa()` shape (2.2-wide base + back + arms +
  cushions, `v9-walkin.html` lines ~338–351), scaled up or paired. Place a **facing
  pair** framing a coffee table in the room center: sofa A `(0, 0, 1.2)` rot `y=π`
  (faces `-z` toward reception), sofa B `(0, 0, -1.2)` rot `y=0`. Or an L-arrangement
  in the near-right quadrant.
- **Coffee table** — round stone/brass: `CylinderGeometry(0.6, 0.6, 0.4)` top on a
  brass drum base, center `(0, 0.4, 0)`.
- **Accents** — two olive/fig planters (`buildOliveTree`) flanking the reception
  `(-2.4, 0, -D/2 + 0.8)` and `(+2.4, 0, -D/2 + 0.8)`; an area rug under the seating
  group `(0, 0.012, 0)` ~4×3.
- Optional **luggage bench** or pair of accent stools near the front wall.

**Lighting rig** (dramatic):
- Ambient `0x2a2620` @ **0.30** (low base = lets the pools read).
- Hemisphere `0x4a4436 / 0x1a1712` @ **0.25**.
- KEY DirectionalLight warm `0xfff1da` @ **1.15** grazing the feature wall from
  `(3.0, H+1.5, 3.0)`, shadow on, wide radius — the hero-wall wash.
- FILL warm-low `0xffd9a8` @ **0.20**.
- RIM `0xffe6bd` @ **0.30** behind the reception to separate desk silhouette from the
  clad wall.
- Chandelier `PointLight` warm @ **0.9** pools the seating; a second `SpotLight`
  (reuse picture-light params) accents the feature wall behind reception. High
  key-to-fill ratio = the "dramatic" signature.

**Camera framing:** wide establishing shot from near the front wall, low-ish, taking
in seating foreground + reception + the tall clad feature wall + chandelier.
`position (0.8, 1.6, D/2 - 1.5)`, `lookAt (0, 1.8, -D/2 + 0.9)`, **fov 65** (wider to
capture the volume). This is the shot that sells the pattern at architectural scale.

**Furniture-style-matching hook:** lobby is where the palette shift is boldest. Read
`color_bucket` and set a **coordinated accent family** across sofa upholstery + rug +
coffee-table accents: warm bucket → camel/cognac sofas, oxblood rug accent, brass
warm; cool bucket → dove-grey sofas, teal/ink rug, cooler brass. Reception desk stays
walnut+stone (permanent anchor). Because the room is big, tie the accent to the
product's *dominant* `hue` and use a complementary for the rug so the seating group
reads as intentionally styled to the wallcovering.

---

## 4. LIVING ROOM

Warm, lived-in, residential. Ports the proto `v9-walkin.html` furnished scene
directly. Pattern on the **sofa/back feature wall** (chimney-breast placement).

| Field | Value |
|---|---|
| **Geometry (W×D×H)** | `6.10 × 6.10 × 2.9` m — the **default square** (this preset is the closest to the existing room, so it reuses the canonical dims). |
| **Clad surfaces** | **Back wall** (behind the sofa) is clad. Optionally clad the flanking segment only, treating it as a chimney breast; simplest = full back wall. Others painted. |
| **Painted surfaces** | Left/right/front = warm limewash (`MAT.wall`, current default). |
| **Floor** | Warm oak (`floor-oak.png`, exists) + a large round wool rug under the seating (the proto already builds one). |
| **Ceiling** | Flat warm off-white with slim cove strips (reuse the two existing emissive strips exactly). |
| **Ceiling fixture** | **Flush / semi-flush mount** — a low warm dome: `SphereGeometry` hemisphere emissive `0xffe6c0` on a slim brass ring, centered `(0, H-0.18, 0)`. Understated (living rooms rarely have statement ceiling lights); the **floor lamp** does the mood work. |
| **Mood** | Warm (warm-neutral, brighter than bedroom, softer than office). |

**Furniture set + layout** (ported from `v9-walkin.html` `sofa()` IIFE, coords already
in this frame — sofa against back wall facing `+z`/the viewer):
- **Sofa** — proto build: base `BoxGeometry(2.2, 0.45, 0.95)`, back `(2.2,0.7,0.22)`,
  seat cushion, two arms. Position `(0, 0, -D/2 + 0.78)` against the clad back wall,
  rotation `y=0`. Cream (`0xcfc4b0`). Contact shadow `(0, -D/2+1.08)`.
- **Coffee table** — add in front of the sofa (the proto omits one): round walnut
  `CylinderGeometry(0.5, 0.5, 0.4)` top on a pedestal, `(0, 0.4, -D/2 + 1.9)`.
- **Rug** — proto round rug: `CircleGeometry(1.7)` wool `0xb9a88c`, `(0, 0.012, -D/2 +
  1.7)`, receives shadow.
- **Floor lamp** — proto lamp: pole `CylinderGeometry(0.02,0.02,1.5)` + emissive drum
  shade `0xffe6c0` @ 0.6, at `(W*0.32, ·, -D*0.30)`. This is the warm key source's
  visible body.
- Optional **accent armchair** (reuse nook bouclé chair) angled in the near-left
  corner `(-W*0.30, 0, D*0.20)` facing the sofa, and the olive planter (`buildOliveTree`)
  in a back corner.

**Lighting rig** (warm — closely mirrors the current default rig, which was tuned for
exactly this warm residential feel):
- Ambient `0xffe8cf` @ **0.30**.
- Hemisphere `0xfff3e0 / 0x3a2f26` @ **0.40**.
- KEY DirectionalLight `0xfff1da` @ **1.0** from `(2.4, H+1.2, 2.2)` targeting the sofa
  wall, shadow on, soft radius.
- FILL cool `0xdfe6ff` @ **0.25**.
- Floor-lamp `PointLight` `0xffcaa0` @ **0.55**, range ~9, at the lamp shade — the
  cozy corner glow that makes the room read lived-in (proto value).
- Optional faint day-spill `PointLight` `0xfff4e0` @ 0.4 from a window direction if a
  window plane is added.

**Camera framing:** seated-guest eyeline looking at the sofa + clad wall.
`position (0, 1.5, D/2 - 1.6)`, `lookAt (0, 1.3, -D/2 + 0.6)`, fov 58. Sofa +
coffee table foreground, pattern fills the wall behind. (Very close to the proto's
own default camera.)

**Furniture-style-matching hook:** classic 60-30-10 residential styling. Walls
(pattern) = 60; sofa neutral cream = 30 anchor; **throw pillows + rug border + lamp-
shade tone = the 10**, tuned to `hue`. Warm `color_bucket` → ochre/rust pillows, warm
rug; cool → indigo/sage pillows, greige rug. Sofa itself stays cream so any pattern
sits against a neutral seat — this is the most "interior-designer-correct" of the five
because it directly mirrors how a real designer would place a statement wallcovering
in a living room.

---

## 5. ART GALLERY

High-contrast, minimal, tall. The existing white-box arc room is spiritually a
gallery, but this preset is the *black-box* gallery (see `v12-gallery-noir.html`):
dark walls, spot-lit **single feature wall**, sculptural minimalism. Pattern is
presented as *the artwork*.

| Field | Value |
|---|---|
| **Geometry (W×D×H)** | `7.0 × 7.0 × 3.8` m — larger footprint and **noticeably taller** (gallery volume + breathing room around the feature wall). |
| **Clad surfaces** | **One feature wall only** (`side:'back'`) is clad — the pattern is the exhibited piece. |
| **Painted surfaces** | Left/right/front = near-black gallery wall (`0x161a22` matte, per the noir proto) — deliberately dark so the lit feature wall pops. |
| **Floor** | Polished concrete **(needs texture — interim `floor-stone.png` desaturated, or flat `0x2a2a2c` roughness 0.4)**. Gallery floors are matte dark; no rug. |
| **Ceiling** | Dark ceiling (`0x14161c`) so it disappears — only the track spots read. |
| **Ceiling fixture** | **Track spots** — a slim brass/black track rail (`BoxGeometry`) across the ceiling parallel to the feature wall at `(0, H-0.12, -D/2 + 1.4)`, carrying 3–5 small barrel spot heads (`CylinderGeometry` `MAT.dark`), each with a real `SpotLight` aimed at a section of the clad wall. These are the ONLY meaningful lights. |
| **Mood** | High-contrast (dark room, bright raked feature wall — the strongest key-to-ambient ratio of all five). |

**Furniture set + layout** (sparse, sculptural — a gallery is mostly empty):
- **Benches** ×1–2 — low minimalist slab: `BoxGeometry(1.6, 0.10, 0.45)` on two block
  legs, dark oak or stone. Centered facing the feature wall `(0, 0.42, 1.2)`, rotation
  `y=0`. Optional second bench `(0, 0.42, 2.2)`.
- **Pedestals** ×2 — white/stone plinths `BoxGeometry(0.4, 1.1, 0.4)` flanking the
  view axis `(-2.0, 0.55, -0.5)` and `(+2.0, 0.55, -0.5)`, each carrying a small
  sculptural object (a turned form / a folded-fabric swatch display) so the room isn't
  empty.
- **Framed art** ×2 — on the **left** wall, two framed pieces (`BoxGeometry` frame
  `MAT.frame` + an image plane) at eye height `(-W/2 + 0.05, 1.5, -1.0)` and
  `(-W/2 + 0.05, 1.5, 1.0)`, rotation `y=π/2` — these can display *other* colorways of
  the same pattern (a gallery wall of the collection).
- A discreet wall label plaque beside the feature wall (small emissive/canvas plane)
  with the pattern name — mirrors the DW sign approach.

**Lighting rig** (high-contrast):
- Ambient dark `0x161a22` @ **0.42** (per noir proto — this is a *dark* ambient, it
  sets the black-box base without lifting it to grey).
- Hemisphere — **omit or ~0.05** (galleries kill fill bounce for pure spot contrast).
- KEY: replace the broad directional key with the **track SpotLights**. Each spot
  `0xffe6bd`, intensity ~**1.4**, tight cone (`Math.PI/7.5`), penumbra 0.45, targeting
  a band of the feature wall (per noir proto `spot` params, but ON). Shadow on the
  primary center spot only (cost control).
- FILL cool `0x6f7d96` @ **0.22** from the left (barely models the benches/pedestals).
- RIM warm `0xffd9a0` @ **0.30** from behind-right to edge-light the pedestals.
- Net effect: brilliant raked feature wall, near-black surroundings, sculptural pools
  on the benches. **Do not** brighten the ambient — the contrast IS the aesthetic.

**Camera framing:** centered, straight-on, reverent — the pattern as artwork.
`position (0, 1.55, D/2 - 1.8)`, `lookAt (0, 1.6, -D/2 + 0.05)`, fov 52 (slightly
tighter/longer lens = flatter, more "photographed artwork" framing). Bench in
foreground, clad wall filling the frame under the track spots.

**Furniture-style-matching hook:** the gallery deliberately does **not** color-match
the furniture — benches/pedestals/floor stay achromatic (black/white/stone) so the
pattern's `hue` is the *only* color in the room (maximum contrast, museum-neutral).
The one place the hook applies: **tint the track-spot color temperature** slightly
toward or away from the pattern's warmth (warm `color_bucket` → keep `0xffe6bd`; cool
bucket → shift spots toward a neutral `0xfff4ec` so cool patterns don't go muddy), and
optionally set the two **framed-art side pieces** to complementary colorways of the
same SKU. Everything else stays monochrome by design.

---

## 6. Cross-cutting notes for the implementer

- **Reuse before rebuild.** BEDROOM/OFFICE lean on `buildBookShelf` + the nook chair
  + `buildOliveTree`; LIVING ROOM ports the proto `sofa()` almost verbatim; GALLERY
  reuses the noir-proto spot params. Only the bed, desk, reception desk, chandelier,
  and pedestals are genuinely new primitive builds.
- **Cladding is a data flip, not new geometry.** Every preset builds all 4 walls as
  `roomWalls[]` entries (as today); the record's "clad surfaces" column just selects
  which entries receive the pattern `.map` on pattern-select and which keep their
  `origMat` paint. This preserves the existing "click clads the wall" mechanism.
- **Floor honesty.** Only `floor-oak` and `floor-stone` textures exist. OFFICE
  (concrete), LOBBY (marble), GALLERY (dark concrete) are flagged as needing a texture
  or a flat/procedural `MeshStandardMaterial` fallback — don't ship a preset claiming
  a marble floor it can't render.
- **Fixtures = emissive primitive (+ optional co-located PointLight).** Follows the
  existing emissive cove-strip convention (zero shadow/light cost) so adding five
  ceiling fixtures doesn't blow the FPS budget. Only chandelier / pendant / floor lamp
  / track spots add real lights, and those are single cheap `PointLight` /
  `SpotLight`s.
- **Camera poses** are given as `position` + `lookAt` + `fov`; a future `ROOM_TYPES`
  consumer would feed these into the same camera the `MODES` registry already drives.
- **Palette hook is uniform in shape:** every record reads `product.color_bucket`
  (coarse warm/cool/neutral) and `product.hue` (fine), then shifts only the *soft
  goods / accents* (never the wood/stone/leather anchors), following 60-30-10 with the
  pattern as the 60. GALLERY is the intentional exception (stays monochrome).

---

## 7. JS-shaped `ROOM_TYPES` sketch (drop-in parallel to THEMES / MODES)

> Sketch only — illustrative literal, not wired to anything. Positions are in the
> room-center frame (viewer faces `-z`, back wall at `z = -D/2`). `clad` lists the
> `roomWalls[].side` values that receive the pattern; all other sides stay painted.
> `fixture` / `furniture` entries are declarative descriptors a future builder maps to
> the primitive helpers. Colors are hex ints to match `initMaterials`.

```js
// Parallel to THEMES / MODES. DESIGN SKETCH — not implemented.
const ROOM_TYPES = {
  bedroom: {
    label: 'Bedroom',
    room: { width: 4.6, depth: 5.0, height: 2.7 },
    clad: ['back'],                                  // headboard accent wall
    paint: { color: 0xece4d6 },                      // warm off-white
    floor: { tex: 'floor-oak', repeat: 2.0 },
    ceiling: { color: 0xf2eee5 },
    fixture: { type: 'pendant', pos: [0, 'H-0.55', -1.1], emissive: 0xffe6c0,
               light: { kind: 'point', color: 0xffcaa0, intensity: 0.6, range: 5 } },
    furniture: [
      { type: 'bed',        pos: [0, 0.35, -1.15], rot: 0, size: [1.8, 0.35, 2.1] },
      { type: 'nightstand', pos: [-1.15, 0.25, -1.95] },
      { type: 'nightstand', pos: [ 1.15, 0.25, -1.95] },
      { type: 'dresser',    pos: ['-W/2+0.3', 0.42, 0.4], rot: 'PI/2', size: [1.4, 0.85, 0.5] },
      { type: 'rug',        pos: [0, 0.012, 0.0], size: [2.6, 1.8], color: 0xb9a88c }
    ],
    lights: { ambient: [0xfff0dd, 0.30], hemi: [0xfff1e0, 0xe4d8c4, 0.28],
              key: { color: 0xfff2e0, intensity: 0.75, pos: [1.6, 'H+0.8', 1.6], shadow: true, radius: 4 },
              fill:{ color: 0xdfe6ff, intensity: 0.18 } },
    camera: { pos: [0, 1.55, 1.1], lookAt: [0, 1.4, -2.1], fov: 55 },
    mood: 'warm-dim',
    paletteHook: { anchor: ['bed', 'nightstand', 'dresser'], accent: ['rug', 'duvet', 'throw'] }
  },

  office: {
    label: 'Office',
    room: { width: 4.8, depth: 5.2, height: 2.8 },
    clad: ['back'],                                  // focal wall behind the seat
    paint: { color: 0xf1f1ee },                      // cool neutral white
    floor: { tex: 'floor-stone', repeat: 2.2, fallback: { color: 0x9a9a98, roughness: 0.35 }, note: 'concrete pending' },
    ceiling: { color: 0xf4f4f0, treatment: 'recessed-track' },
    fixture: { type: 'track-recessed', strips: [['-W*0.22', 'H-0.02', 0], ['W*0.22', 'H-0.02', 0]],
               emissive: 0xf6f8ff, downlights: 3,
               light: { kind: 'point', color: 0xf6f8ff, intensity: 0.25 } },
    furniture: [
      { type: 'desk',      pos: [0, 0.74, -1.5], rot: 0, size: [1.6, 0.05, 0.75], mat: 'wood' },
      { type: 'office-chair', pos: [0, 0, -2.05], rot: 'PI', mat: 'chair' },
      { type: 'bookshelf', pos: ['W/2-0.2', 0, 0], rot: '-PI/2', unitW: 2.4 },  // reuse buildBookShelf
      { type: 'guest-chair', pos: ['W/2-0.9', 0, 1.2], rot: 2.6, mat: 'boucle' }
    ],
    lights: { ambient: [0xffffff, 0.50], hemi: [0xf4f6ff, 0xdedad2, 0.45],
              key: { color: 0xfff8f2, intensity: 1.0, pos: [2.0, 'H+1.0', 2.0], shadow: true, radius: 2.5 },
              fill:{ color: 0xeef2ff, intensity: 0.35 } },
    camera: { pos: [0, 1.6, 1.6], lookAt: [0, 1.35, -2.3], fov: 58 },
    mood: 'neutral-bright',
    paletteHook: { anchor: ['desk', 'bookshelf'], accent: ['office-chair', 'books'], saturation: 'low' }
  },

  lobby: {
    label: 'Hotel Lobby',
    room: { width: 8.0, depth: 8.0, height: 4.2 },   // large + tall
    clad: ['back'],                                  // + optional 'front' for a wrap
    paint: { mat: 'wall' },                          // warm stone plaster
    floor: { tex: 'floor-stone', repeat: 2.4, roughness: 0.3, note: 'marble pending' },
    ceiling: { color: 0xece6da, treatment: 'coffered-cove' },
    fixture: { type: 'chandelier', pos: [0, 'H-1.1', 0.2], emissive: 0xffe6bd,
               light: { kind: 'point', color: 0xffe6bd, intensity: 0.9, range: 10 } },
    furniture: [
      { type: 'reception',    pos: [0, 0.55, -3.1], rot: 0, size: [3.0, 1.1, 0.7], mat: 'wood' },
      { type: 'sofa',         pos: [0, 0, 1.2], rot: 'PI' },     // proto sofa()
      { type: 'sofa',         pos: [0, 0, -1.2], rot: 0 },
      { type: 'coffee-table', pos: [0, 0.4, 0], r: 0.6 },
      { type: 'olive',        pos: [-2.4, 0, -3.2] },            // buildOliveTree
      { type: 'olive',        pos: [ 2.4, 0, -3.2] },
      { type: 'rug',          pos: [0, 0.012, 0], size: [4.0, 3.0] }
    ],
    lights: { ambient: [0x2a2620, 0.30], hemi: [0x4a4436, 0x1a1712, 0.25],
              key: { color: 0xfff1da, intensity: 1.15, pos: [3.0, 'H+1.5', 3.0], shadow: true, radius: 4 },
              fill:{ color: 0xffd9a8, intensity: 0.20 },
              rim: { color: 0xffe6bd, intensity: 0.30 },
              accentSpot: { color: 0xffe6bd, intensity: 0.6, target: 'featureWall' } },
    camera: { pos: [0.8, 1.6, 2.5], lookAt: [0, 1.8, -3.1], fov: 65 },
    mood: 'dramatic',
    paletteHook: { anchor: ['reception'], accent: ['sofa', 'rug'], bold: true, complementaryRug: true }
  },

  living: {
    label: 'Living Room',
    room: { width: 6.10, depth: 6.10, height: 2.9 },  // canonical default dims
    clad: ['back'],                                    // chimney-breast / sofa wall
    paint: { mat: 'wall' },                            // warm limewash (current)
    floor: { tex: 'floor-oak', repeat: 2.4 },
    ceiling: { color: 0xf2eee5, coveStrips: true },    // reuse existing emissive strips
    fixture: { type: 'flush-mount', pos: [0, 'H-0.18', 0], emissive: 0xffe6c0 },
    furniture: [
      { type: 'sofa',         pos: [0, 0, -2.27], rot: 0, color: 0xcfc4b0 },  // proto sofa(): -D/2+0.78
      { type: 'coffee-table', pos: [0, 0.4, -1.15], r: 0.5, mat: 'wood' },
      { type: 'rug',          pos: [0, 0.012, -1.35], r: 1.7, color: 0xb9a88c }, // proto round rug
      { type: 'floor-lamp',   pos: ['W*0.32', 0, '-D*0.30'], emissive: 0xffe6c0 },  // proto lamp
      { type: 'armchair',     pos: ['-W*0.30', 0, 'D*0.20'], rot: 2.4, mat: 'boucle' },
      { type: 'olive',        pos: [-2.2, 0, -2.2] }
    ],
    lights: { ambient: [0xffe8cf, 0.30], hemi: [0xfff3e0, 0x3a2f26, 0.40],
              key: { color: 0xfff1da, intensity: 1.0, pos: [2.4, 'H+1.2', 2.2], shadow: true, radius: 3.5 },
              fill:{ color: 0xdfe6ff, intensity: 0.25 },
              lampGlow: { kind: 'point', color: 0xffcaa0, intensity: 0.55, range: 9, at: 'floor-lamp' } },
    camera: { pos: [0, 1.5, 1.45], lookAt: [0, 1.3, -2.45], fov: 58 },
    mood: 'warm',
    paletteHook: { anchor: ['sofa'], accent: ['pillows', 'rug', 'lampshade'], rule: '60-30-10' }
  },

  gallery: {
    label: 'Art Gallery',
    room: { width: 7.0, depth: 7.0, height: 3.8 },     // large + tall
    clad: ['back'],                                    // single feature wall = the artwork
    paint: { color: 0x161a22 },                        // near-black gallery wall (noir proto)
    floor: { tex: 'floor-stone', desaturate: true, fallback: { color: 0x2a2a2c, roughness: 0.4 }, note: 'dark concrete pending' },
    ceiling: { color: 0x14161c },
    fixture: { type: 'track-spots', rail: [0, 'H-0.12', -2.1], heads: 4, mat: 'dark',
               spot: { color: 0xffe6bd, intensity: 1.4, angle: 'PI/7.5', penumbra: 0.45, target: 'featureWall' } },
    furniture: [
      { type: 'bench',    pos: [0, 0.42, 1.2], rot: 0, size: [1.6, 0.10, 0.45] },
      { type: 'bench',    pos: [0, 0.42, 2.2], rot: 0, size: [1.6, 0.10, 0.45] },
      { type: 'pedestal', pos: [-2.0, 0.55, -0.5], size: [0.4, 1.1, 0.4] },
      { type: 'pedestal', pos: [ 2.0, 0.55, -0.5], size: [0.4, 1.1, 0.4] },
      { type: 'framed-art', pos: ['-W/2+0.05', 1.5, -1.0], rot: 'PI/2', showsColorway: true },
      { type: 'framed-art', pos: ['-W/2+0.05', 1.5,  1.0], rot: 'PI/2', showsColorway: true },
      { type: 'wall-label', pos: [1.2, 1.4, -3.4] }
    ],
    lights: { ambient: [0x161a22, 0.42], hemi: [0x161a22, 0x0a0c10, 0.05],
              key: 'track-spots',                       // the spots ARE the key
              fill:{ color: 0x6f7d96, intensity: 0.22 },
              rim: { color: 0xffd9a0, intensity: 0.30 } },
    camera: { pos: [0, 1.55, 1.7], lookAt: [0, 1.6, -3.45], fov: 52 },
    mood: 'high-contrast',
    paletteHook: { monochromeFurniture: true, tune: 'spot-temperature', sidePiecesShowComplementaryColorway: true }
  }
};
```