diff --git a/DESIGN-interactive-features.md b/DESIGN-interactive-features.md
new file mode 100644
index 0000000..6218055
--- /dev/null
+++ b/DESIGN-interactive-features.md
@@ -0,0 +1,985 @@
+# Interactive Features Design — Phase 2
+
+**Task:** t-05700
+**Date:** 2026-02-07
+**Role:** Inventor
+
+## Overview
+
+Four interactive feature areas to transform the viewer into an editor:
+
+1. **Drag-and-drop furniture** — move, rotate, place from catalog
+2. **Room editing** — resize rooms, edit wall openings
+3. **Style themes** — switchable color/material palettes
+4. **Export** — save designs, screenshot, share
+
+Design principle: **Enhance the existing vanilla JS + Three.js stack**. No framework rewrite. Each feature is an independent module that plugs into `HouseRenderer`.
+
+---
+
+## Architecture Strategy
+
+### Module Structure
+
+```
+src/
+ renderer.js (existing — 3D core)
+ index.html (existing — entry point)
+ interaction.js (NEW — drag/drop, selection, gizmos)
+ room-editor.js (NEW — room resize, wall editing)
+ themes.js (NEW — style theme system)
+ export.js (NEW — save/export functionality)
+ ui-panels.js (NEW — sidebar panels, catalog browser, property inspector)
+```
+
+Each module exports a class that receives the `HouseRenderer` instance and extends it via composition (not inheritance). This keeps renderer.js stable while adding capabilities.
+
+```javascript
+// Pattern for all modules:
+export class InteractionManager {
+ constructor(renderer) {
+ this.renderer = renderer;
+ // Hook into renderer's scene, camera, controls
+ }
+ dispose() { /* cleanup */ }
+}
+```
+
+### State Management
+
+Currently state lives in scattered instance variables. For interactive editing, we need a lightweight state layer:
+
+```javascript
+// src/state.js — simple observable state
+export class DesignState {
+ constructor(initialDesign) {
+ this._state = structuredClone(initialDesign);
+ this._listeners = new Set();
+ this._undoStack = [];
+ this._redoStack = [];
+ }
+
+ // Read
+ get design() { return this._state; }
+ getRoomDesign(roomId) { ... }
+ getFurniture(roomId, index) { ... }
+
+ // Write (all mutations go through here)
+ updateFurniture(roomId, index, changes) {
+ this._pushUndo();
+ Object.assign(this._state.rooms[...].furniture[index], changes);
+ this._notify('furniture-update', { roomId, index });
+ }
+
+ moveFurniture(roomId, index, newPosition) { ... }
+ rotateFurniture(roomId, index, degrees) { ... }
+ addFurniture(roomId, catalogId, position, rotation) { ... }
+ removeFurniture(roomId, index) { ... }
+
+ // Undo/Redo
+ undo() { ... }
+ redo() { ... }
+
+ // Observers
+ onChange(listener) { this._listeners.add(listener); }
+ _notify(type, detail) { for (const fn of this._listeners) fn(type, detail); }
+ _pushUndo() { this._undoStack.push(structuredClone(this._state)); this._redoStack = []; }
+}
+```
+
+This is intentionally minimal — no Redux, no Zustand, just a class with undo/redo.
+
+---
+
+## Feature 1: Drag-and-Drop Furniture
+
+### Interaction Modes
+
+The viewer operates in one of these modes:
+
+| Mode | Behavior | Activation |
+|------|----------|------------|
+| **View** | Current behavior — orbit, zoom, click rooms | Default |
+| **Select** | Click furniture to select, show properties | Toggle button |
+| **Move** | Drag selected furniture on floor plane | Select + drag |
+| **Rotate** | Rotate selected furniture around Y axis | R key or gizmo |
+| **Place** | Drag new item from catalog into scene | Catalog click |
+
+### Selection System
+
+```javascript
+class InteractionManager {
+ constructor(renderer) {
+ this.renderer = renderer;
+ this.selectedObject = null;
+ this.mode = 'view'; // view | select | move | rotate | place
+ this.dragPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); // floor plane
+ this.dragOffset = new THREE.Vector3();
+ this._ghostMesh = null; // preview during placement
+
+ // Event listeners
+ this.renderer.renderer.domElement.addEventListener('pointerdown', e => this._onPointerDown(e));
+ this.renderer.renderer.domElement.addEventListener('pointermove', e => this._onPointerMove(e));
+ this.renderer.renderer.domElement.addEventListener('pointerup', e => this._onPointerUp(e));
+ window.addEventListener('keydown', e => this._onKeyDown(e));
+ }
+}
+```
+
+### Furniture Selection
+
+When user clicks on a furniture object:
+
+1. Raycast from camera through click point
+2. Walk hit object up to find `userData.isFurniture` group
+3. Apply selection highlight (outline or emissive tint)
+4. Show property panel in sidebar
+5. Enable move/rotate controls
+
+**Selection visual:** Outline effect using a slightly scaled-up wireframe clone with a distinct color (e.g., cyan). This avoids modifying original materials.
+
+```javascript
+_selectFurniture(meshGroup) {
+ this._clearSelection();
+ this.selectedObject = meshGroup;
+
+ // Create outline by cloning with wireframe material
+ const outline = meshGroup.clone();
+ outline.traverse(child => {
+ if (child.isMesh) {
+ child.material = this._outlineMaterial; // cyan wireframe, slightly larger scale
+ child.scale.multiplyScalar(1.02);
+ }
+ });
+ outline.userData._isOutline = true;
+ meshGroup.add(outline);
+}
+```
+
+### Drag-to-Move
+
+When user drags a selected furniture piece:
+
+1. On `pointerdown`: if hitting selected object, enter move mode
+2. Disable OrbitControls (prevent camera rotation during drag)
+3. Raycast pointer against floor plane (Y=0) each frame
+4. Apply offset so object doesn't jump to cursor
+5. Optionally snap to grid (0.1m or 0.25m increments)
+6. Constrain within room bounds
+7. On `pointerup`: commit new position to DesignState, re-enable OrbitControls
+
+```javascript
+_onPointerMove(event) {
+ if (this.mode !== 'move' || !this.selectedObject) return;
+
+ const mouse = this._getNDC(event);
+ this.renderer.raycaster.setFromCamera(mouse, this.renderer.camera);
+ const intersection = new THREE.Vector3();
+ this.renderer.raycaster.ray.intersectPlane(this.dragPlane, intersection);
+
+ if (intersection) {
+ // Apply grid snapping
+ if (this.snapEnabled) {
+ intersection.x = Math.round(intersection.x / this.snapSize) * this.snapSize;
+ intersection.z = Math.round(intersection.z / this.snapSize) * this.snapSize;
+ }
+
+ // Apply room bounds constraint
+ const room = this._getContainingRoom(intersection);
+ if (room) {
+ intersection.x = clamp(intersection.x, room.bounds.minX + padding, room.bounds.maxX - padding);
+ intersection.z = clamp(intersection.z, room.bounds.minZ + padding, room.bounds.maxZ - padding);
+ }
+
+ this.selectedObject.position.copy(intersection.sub(this.dragOffset));
+ }
+}
+```
+
+### Rotation
+
+Two mechanisms:
+
+1. **Quick rotate:** Press R key to rotate 90 degrees
+2. **Gizmo:** Circular handle at base of selected furniture, drag to rotate freely
+
+Quick rotate is simplest to implement first:
+
+```javascript
+_onKeyDown(event) {
+ if (event.key === 'r' && this.selectedObject) {
+ this.selectedObject.rotation.y -= Math.PI / 2;
+ this.state.rotateFurniture(roomId, index, -90);
+ }
+ if (event.key === 'Delete' && this.selectedObject) {
+ this.state.removeFurniture(roomId, index);
+ this._removeFromScene(this.selectedObject);
+ this._clearSelection();
+ }
+ if (event.key === 'Escape') {
+ this._clearSelection();
+ this.mode = 'view';
+ }
+}
+```
+
+### Catalog Drag-to-Place
+
+Adding new furniture from the catalog sidebar:
+
+1. User clicks a catalog item in sidebar
+2. Create a ghost (semi-transparent) mesh from catalog definition
+3. Ghost follows cursor, projected onto floor plane
+4. Show green/red tint for valid/invalid placement
+5. Click to place — add to DesignState and create real mesh
+6. Right-click or Escape to cancel
+
+**Alternative (simpler):** Click catalog item, then click on room floor to place at that point. No drag-from-sidebar needed. This avoids the complexity of HTML-to-3D coordinate mapping.
+
+**Recommendation:** Start with click-to-place, add drag-from-sidebar later.
+
+### Room Boundary Detection
+
+For constraining furniture to rooms, build bounding boxes from room data:
+
+```javascript
+_buildRoomBounds() {
+ const floor = this.renderer.houseData.floors[this.renderer.currentFloor];
+ this.roomBounds = new Map();
+ for (const room of floor.rooms) {
+ this.roomBounds.set(room.id, {
+ minX: room.position.x,
+ maxX: room.position.x + room.dimensions.width,
+ minZ: room.position.y,
+ maxZ: room.position.y + room.dimensions.length
+ });
+ }
+}
+```
+
+### Collision Detection (Optional, Phase 2)
+
+Basic AABB overlap check to prevent furniture stacking:
+
+```javascript
+_checkCollision(candidate, exclude) {
+ const box1 = new THREE.Box3().setFromObject(candidate);
+ for (const [key, mesh] of this.renderer.furnitureMeshes) {
+ if (mesh === exclude) continue;
+ const box2 = new THREE.Box3().setFromObject(mesh);
+ if (box1.intersectsBox(box2)) return true;
+ }
+ return false;
+}
+```
+
+### Implementation Priority
+
+| Step | What | Complexity | Depends On |
+|------|------|------------|------------|
+| 1 | Click-to-select furniture | Low | Existing raycaster |
+| 2 | Selection outline visual | Low | Step 1 |
+| 3 | Property panel in sidebar | Low | Step 1 |
+| 4 | Drag-to-move on floor plane | Medium | Step 1, OrbitControls toggle |
+| 5 | Grid snapping | Low | Step 4 |
+| 6 | Room bounds constraint | Low | Step 4 |
+| 7 | R key rotation | Low | Step 1 |
+| 8 | Delete key removal | Low | Step 1, DesignState |
+| 9 | Catalog click-to-place | Medium | DesignState |
+| 10 | Ghost preview during placement | Medium | Step 9 |
+| 11 | Undo/redo | Medium | DesignState |
+| 12 | Collision detection | Medium | Step 4 |
+
+---
+
+## Feature 2: Room Editing
+
+### Scope
+
+Room editing is more complex than furniture interaction. Recommended phased approach:
+
+**Phase 2a (Do first):** Edit room properties only — name, flooring type, wall colors
+**Phase 2b (Later):** Resize rooms — drag walls to change dimensions
+**Phase 2c (Future):** Add/remove rooms, edit doors/windows
+
+### Phase 2a: Room Property Editing
+
+When a room is selected, show editable properties in sidebar:
+
+```
+Room: Wohnzimmer
+────────────────
+Name: [Wohnzimmer ]
+Type: [Living Room ▾]
+Flooring: [Hardwood ▾] [Custom color: #b5894e]
+Size: 4.5m × 5.5m (24.8 m²)
+Ceiling: 2.6m
+
+Walls:
+ North: [Interior ▾] — Door to Esszimmer
+ South: [Exterior ▾] — Window (1.8m)
+ East: [Interior ▾] — Door to Flur
+ West: [Exterior ▾] — Patio door (2.0m)
+```
+
+Changes update both DesignState and the 3D scene in real-time.
+
+### Phase 2b: Room Resize
+
+Allow dragging room edges to resize:
+
+1. When room is selected, show drag handles on each wall midpoint
+2. Dragging a handle moves that wall, changing room width or length
+3. Adjacent rooms may need to adjust (constraint system)
+4. Minimum room size: 1.5m × 1.5m
+5. Grid snap: 0.25m increments
+
+**Wall drag handles:** Small sphere or cube meshes placed at wall midpoints, highlighted on hover.
+
+```javascript
+_createResizeHandles(room) {
+ const handles = [];
+ const walls = ['north', 'south', 'east', 'west'];
+ for (const wall of walls) {
+ const handle = new THREE.Mesh(
+ new THREE.SphereGeometry(0.1),
+ new THREE.MeshStandardMaterial({ color: 0x4a90d9 })
+ );
+ handle.userData = { isHandle: true, wall, roomId: room.id };
+ // Position at wall midpoint
+ handles.push(handle);
+ }
+ return handles;
+}
+```
+
+**Constraint challenge:** When room A's east wall moves, room B's west wall (if adjacent) should also move. This requires understanding room adjacency from door connections. The house data already has `connectsTo` fields on doors — use these to build an adjacency graph.
+
+### Phase 2c: Door/Window Editing (Future)
+
+- Click wall to add/remove doors or windows
+- Drag door/window along wall to reposition
+- Change door/window types from property panel
+- This modifies `sample-house.json` structure
+
+**Recommendation:** Defer this to a future phase. It's architecturally complex (wall segmentation recalculation, connectivity validation) and the current house data is well-defined.
+
+### Implementation Priority
+
+| Step | What | Complexity |
+|------|------|------------|
+| 1 | Room property panel (read-only) | Low |
+| 2 | Editable flooring type | Low |
+| 3 | Editable room name | Low |
+| 4 | Wall color customization | Medium |
+| 5 | Room resize handles | High |
+| 6 | Adjacent room constraint system | High |
+| 7 | Door/window editing | Very High |
+
+---
+
+## Feature 3: Style Themes
+
+### Concept
+
+Predefined color/material palettes that restyle the entire house. Themes override the `COLORS` object and material properties.
+
+### Theme Data Structure
+
+```javascript
+// src/themes.js
+export const THEMES = {
+ default: {
+ name: 'Standard',
+ colors: {
+ wall: { exterior: 0xe8e0d4, interior: 0xf5f0eb },
+ floor: { tile: 0xc8beb0, hardwood: 0xb5894e },
+ ceiling: 0xfaf8f5,
+ door: 0x8b6914,
+ window: 0x87ceeb,
+ windowFrame: 0xd0d0d0,
+ grid: 0xcccccc,
+ selected: 0x4a90d9
+ },
+ materials: {
+ wallRoughness: 0.9,
+ floorRoughness: 0.8,
+ doorRoughness: 0.6
+ },
+ scene: {
+ background: 0xf0f0f0,
+ ambientIntensity: 0.6,
+ directionalIntensity: 0.8
+ }
+ },
+
+ modern: {
+ name: 'Modern Minimal',
+ colors: {
+ wall: { exterior: 0xf5f5f5, interior: 0xffffff },
+ floor: { tile: 0xe0e0e0, hardwood: 0xc4a882 },
+ ceiling: 0xffffff,
+ door: 0x333333,
+ window: 0xa8d4f0,
+ windowFrame: 0x666666,
+ grid: 0xe0e0e0,
+ selected: 0x2196f3
+ },
+ materials: { wallRoughness: 0.3, floorRoughness: 0.4, doorRoughness: 0.2 },
+ scene: { background: 0xfafafa, ambientIntensity: 0.7, directionalIntensity: 0.6 }
+ },
+
+ warm: {
+ name: 'Warm Rustic',
+ colors: {
+ wall: { exterior: 0xddd0b8, interior: 0xf0e8d8 },
+ floor: { tile: 0xb8a890, hardwood: 0x9b6b3a },
+ ceiling: 0xf5efe5,
+ door: 0x6b4423,
+ window: 0x8bc4e0,
+ windowFrame: 0x8b7355,
+ grid: 0xc8b8a0,
+ selected: 0xd48b2c
+ },
+ materials: { wallRoughness: 0.95, floorRoughness: 0.9, doorRoughness: 0.8 },
+ scene: { background: 0xf5efe5, ambientIntensity: 0.5, directionalIntensity: 0.9 }
+ },
+
+ dark: {
+ name: 'Dark Mode',
+ colors: {
+ wall: { exterior: 0x3a3a3a, interior: 0x4a4a4a },
+ floor: { tile: 0x2a2a2a, hardwood: 0x5a4030 },
+ ceiling: 0x333333,
+ door: 0x5a4030,
+ window: 0x4080b0,
+ windowFrame: 0x555555,
+ grid: 0x444444,
+ selected: 0x64b5f6
+ },
+ materials: { wallRoughness: 0.7, floorRoughness: 0.6, doorRoughness: 0.5 },
+ scene: { background: 0x222222, ambientIntensity: 0.4, directionalIntensity: 1.0 }
+ },
+
+ scandinavian: {
+ name: 'Scandinavian',
+ colors: {
+ wall: { exterior: 0xf0ece4, interior: 0xfaf6f0 },
+ floor: { tile: 0xe8ddd0, hardwood: 0xd4b88c },
+ ceiling: 0xffffff,
+ door: 0xc4a87a,
+ window: 0xc0ddf0,
+ windowFrame: 0xb0b0b0,
+ grid: 0xd8d8d8,
+ selected: 0x5b9bd5
+ },
+ materials: { wallRoughness: 0.5, floorRoughness: 0.6, doorRoughness: 0.4 },
+ scene: { background: 0xf8f6f2, ambientIntensity: 0.65, directionalIntensity: 0.7 }
+ }
+};
+```
+
+### Theme Application
+
+```javascript
+export class ThemeManager {
+ constructor(renderer) {
+ this.renderer = renderer;
+ this.currentTheme = 'default';
+ }
+
+ applyTheme(themeId) {
+ const theme = THEMES[themeId];
+ if (!theme) return;
+ this.currentTheme = themeId;
+
+ // Update COLORS object (renderer reads from this)
+ Object.assign(COLORS.wall, theme.colors.wall);
+ Object.assign(COLORS.floor, theme.colors.floor);
+ COLORS.ceiling = theme.colors.ceiling;
+ COLORS.door = theme.colors.door;
+ COLORS.window = theme.colors.window;
+ COLORS.windowFrame = theme.colors.windowFrame;
+
+ // Update scene
+ this.renderer.scene.background.setHex(theme.scene.background);
+
+ // Clear material cache (forces recreation with new colors)
+ // Then re-render current floor
+ this.renderer._clearFloor();
+ this.renderer.showFloor(this.renderer.currentFloor);
+ }
+
+ getThemes() {
+ return Object.entries(THEMES).map(([id, t]) => ({ id, name: t.name }));
+ }
+}
+```
+
+**Key insight:** The renderer already uses a `COLORS` constant and caches materials keyed by color. Changing `COLORS` and clearing the cache forces a full re-render with new colors. This is clean and requires minimal renderer changes — just make `COLORS` mutable (change `const` to `let`, or use an object that can be mutated).
+
+### Renderer Change Required
+
+The `COLORS` constant in renderer.js needs to be accessible for mutation. Two options:
+
+**Option A (minimal):** Export `COLORS` and let ThemeManager mutate it directly.
+```javascript
+export const COLORS = { ... }; // already an object, properties are mutable
+```
+
+**Option B (cleaner):** Add a `setColors(newColors)` method to HouseRenderer.
+
+Recommend Option A for simplicity — `COLORS` is already a mutable object.
+
+### UI: Theme Selector
+
+Add a theme dropdown or button row to the sidebar:
+
+```html
+
Theme
+
+
+
+ ...
+
+```
+
+Each theme button shows a small color swatch preview.
+
+### Per-Room Color Override (Stretch)
+
+Allow individual rooms to override theme colors:
+
+```javascript
+// In DesignState
+roomOverrides: {
+ "eg-wohnzimmer": {
+ floorColor: "#a0522d",
+ wallColor: "#f0e0d0"
+ }
+}
+```
+
+The renderer checks for overrides before falling back to theme defaults.
+
+### Implementation Priority
+
+| Step | What | Complexity |
+|------|------|------------|
+| 1 | Define 5 theme presets | Low |
+| 2 | Theme application (clear cache + re-render) | Low |
+| 3 | Theme selector UI | Low |
+| 4 | Smooth transition (fade between themes) | Medium |
+| 5 | Per-room color overrides | Medium |
+| 6 | Custom theme builder | High |
+
+---
+
+## Feature 4: Export
+
+### Export Formats
+
+| Format | What | Use Case |
+|--------|------|----------|
+| **JSON** | Design state (furniture placements) | Save/load, share |
+| **PNG** | Screenshot of current view | Quick sharing |
+| **PDF** | 2D floor plan with furniture | Printing |
+| **glTF** | 3D model of entire house | Use in other 3D apps |
+
+### JSON Save/Load
+
+```javascript
+export class ExportManager {
+ constructor(renderer, state) {
+ this.renderer = renderer;
+ this.state = state;
+ }
+
+ // Save design to JSON
+ exportDesignJSON() {
+ const data = {
+ ...this.state.design,
+ exportedAt: new Date().toISOString(),
+ theme: this.themeManager?.currentTheme || 'default'
+ };
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
+ this._download(blob, `${data.name || 'design'}.json`);
+ }
+
+ // Load design from JSON
+ async importDesignJSON(file) {
+ const text = await file.text();
+ const data = JSON.parse(text);
+ this.state.loadDesign(data);
+ this.renderer._clearFloor();
+ this.renderer.designData = data;
+ this.renderer._placeFurnitureForFloor();
+ }
+}
+```
+
+### PNG Screenshot
+
+Three.js makes this straightforward:
+
+```javascript
+exportScreenshot(width = 1920, height = 1080) {
+ // Temporarily resize renderer for high-res capture
+ const prevSize = new THREE.Vector2();
+ this.renderer.renderer.getSize(prevSize);
+
+ this.renderer.renderer.setSize(width, height);
+ this.renderer.camera.aspect = width / height;
+ this.renderer.camera.updateProjectionMatrix();
+ this.renderer.renderer.render(this.renderer.scene, this.renderer.camera);
+
+ const dataURL = this.renderer.renderer.domElement.toDataURL('image/png');
+
+ // Restore original size
+ this.renderer.renderer.setSize(prevSize.x, prevSize.y);
+ this.renderer.camera.aspect = prevSize.x / prevSize.y;
+ this.renderer.camera.updateProjectionMatrix();
+
+ this._downloadDataURL(dataURL, 'house-design.png');
+}
+```
+
+**Enhancement:** Option to hide UI overlays (sidebar, info bar) during capture, or render to an offscreen canvas.
+
+### PDF Floor Plan (Phase 2)
+
+Generate a 2D top-down floor plan suitable for printing:
+
+1. Create a 2D canvas (or use jsPDF)
+2. Draw rooms as rectangles with labels
+3. Draw furniture as simplified top-down shapes
+4. Add dimension lines and measurements
+5. Include legend with furniture names
+
+This is a separate rendering pipeline from the 3D view. Consider using the `canvas` API directly or a library like `jspdf` + `html2canvas`.
+
+```javascript
+exportFloorPlanPDF() {
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+ const scale = 50; // 50px per meter
+
+ const floor = this.renderer.houseData.floors[this.renderer.currentFloor];
+ // ... draw rooms, furniture, dimensions, legend
+ // Convert to PDF using jsPDF or similar
+}
+```
+
+### glTF Export (Phase 3)
+
+Three.js has a built-in `GLTFExporter`:
+
+```javascript
+import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
+
+exportGLTF() {
+ const exporter = new GLTFExporter();
+ exporter.parse(this.renderer.scene, (gltf) => {
+ const blob = new Blob([JSON.stringify(gltf)], { type: 'application/json' });
+ this._download(blob, 'house-design.gltf');
+ }, { binary: false });
+
+ // Or binary .glb:
+ exporter.parse(this.renderer.scene, (buffer) => {
+ const blob = new Blob([buffer], { type: 'application/octet-stream' });
+ this._download(blob, 'house-design.glb');
+ }, { binary: true });
+}
+```
+
+### Auto-Save
+
+Save design state to `localStorage` periodically:
+
+```javascript
+// Every 30 seconds, save to localStorage
+setInterval(() => {
+ localStorage.setItem('house-design-autosave', JSON.stringify(this.state.design));
+}, 30000);
+
+// On load, offer to restore
+const saved = localStorage.getItem('house-design-autosave');
+if (saved) {
+ // Show "Restore previous session?" prompt
+}
+```
+
+### Implementation Priority
+
+| Step | What | Complexity |
+|------|------|------------|
+| 1 | JSON export (download) | Low |
+| 2 | JSON import (file picker) | Low |
+| 3 | PNG screenshot | Low |
+| 4 | Auto-save to localStorage | Low |
+| 5 | 2D floor plan PDF | High |
+| 6 | glTF/GLB export | Medium |
+| 7 | Share via URL (encode state) | Medium |
+
+---
+
+## UI Design
+
+### Updated Sidebar Layout
+
+```
+┌─────────────────────────────┐
+│ ☰ Musterhaus │
+├─────────────────────────────┤
+│ [View] [Edit] [Place] │ ← Mode buttons
+├─────────────────────────────┤
+│ FLOORS │
+│ [EG] [OG] │
+├─────────────────────────────┤
+│ ROOMS │
+│ ▸ Flur 18.0 m² │
+│ ▸ Wohnzimmer 24.8 m² │ ← Expandable to show furniture
+│ ▸ Küche 15.0 m² │
+│ ... │
+├─────────────────────────────┤
+│ CATALOG 🔍 search │ ← Only visible in Place mode
+│ ┌──────┐ ┌──────┐ │
+│ │ Sofa │ │Chair │ │
+│ └──────┘ └──────┘ │
+│ ┌──────┐ ┌──────┐ │
+│ │Table │ │ Bed │ │
+│ └──────┘ └──────┘ │
+├─────────────────────────────┤
+│ PROPERTIES │ ← Only visible when item selected
+│ Sofa 3-Sitzer │
+│ Position: (2.1, 3.4) │
+│ Rotation: 180° │
+│ [Rotate 90°] [Delete] │
+├─────────────────────────────┤
+│ THEME │
+│ [Standard] [Modern] [Warm] │
+│ [Dark] [Scandinavian] │
+├─────────────────────────────┤
+│ [💾 Save] [📷 Screenshot] │ ← Always visible
+│ [📂 Load] [📤 Export 3D] │
+└─────────────────────────────┘
+```
+
+### Toolbar (Top)
+
+For quick access to common actions:
+
+```
+┌─────────────────────────────────────────────────────┐
+│ [Undo] [Redo] | [Snap: 0.25m ▾] | [Grid ✓] [Labels ✓] │
+└─────────────────────────────────────────────────────┘
+```
+
+### Keyboard Shortcuts
+
+| Key | Action |
+|-----|--------|
+| `Escape` | Cancel current action, deselect |
+| `Delete` / `Backspace` | Remove selected furniture |
+| `R` | Rotate selected 90° clockwise |
+| `Shift+R` | Rotate selected 90° counter-clockwise |
+| `Ctrl+Z` | Undo |
+| `Ctrl+Shift+Z` | Redo |
+| `Ctrl+S` | Save design JSON |
+| `G` | Toggle grid |
+| `L` | Toggle room labels |
+| `1`-`9` | Quick-select room by index |
+
+---
+
+## Required Renderer Changes
+
+To support these features, `HouseRenderer` needs a few additions:
+
+### 1. Make COLORS Exportable and Mutable
+
+```javascript
+// Change from const to let, or export the object
+export const COLORS = { ... }; // Already works — object properties are mutable
+```
+
+### 2. Add Furniture Click Detection
+
+Extend `_onClick` to distinguish room clicks from furniture clicks:
+
+```javascript
+_onClick(event) {
+ // ... existing raycaster setup ...
+ for (const hit of intersects) {
+ let obj = hit.object;
+ // Check furniture first (more specific)
+ while (obj && !obj.userData.isFurniture && !obj.userData.roomId) {
+ obj = obj.parent;
+ }
+ if (obj?.userData.isFurniture) {
+ this.container.dispatchEvent(new CustomEvent('furnitureclick', {
+ detail: { ...obj.userData, mesh: obj, point: hit.point }
+ }));
+ return;
+ }
+ if (obj?.userData.roomId) {
+ // existing room click behavior
+ }
+ }
+}
+```
+
+### 3. Add OrbitControls Toggle
+
+```javascript
+setControlsEnabled(enabled) {
+ this.controls.enabled = enabled;
+}
+```
+
+### 4. Expose Scene for External Modules
+
+The renderer already exposes `this.scene`, `this.camera`, `this.raycaster` as public properties. No changes needed — external modules can access these directly.
+
+### 5. Floor-switch Event
+
+```javascript
+showFloor(index) {
+ // ... existing code ...
+ this.container.dispatchEvent(new CustomEvent('floorchange', {
+ detail: { index, floor: this.houseData.floors[index] }
+ }));
+}
+```
+
+---
+
+## Trade-off Analysis
+
+### Framework vs. Vanilla JS
+
+**Decision: Stay vanilla JS.**
+
+Pros of staying vanilla:
+- No build step needed (works with static file server)
+- No dependency management
+- Project already works well this way
+- Interactive features can be added as ES6 modules
+
+Cons:
+- No reactive UI updates (must manually sync DOM)
+- No component system for sidebar panels
+- State management is DIY
+
+Mitigation: Keep UI simple. Use custom events for cross-module communication. The sidebar can be built with template literals and direct DOM manipulation — it doesn't need React for this complexity level.
+
+### 2D vs. 3D Interaction
+
+**Decision: 3D interaction (existing stack).**
+
+The RESEARCH.md recommended a 2D Konva.js approach, but the project already has a working 3D viewer with raycasting. Adding drag-and-drop to 3D is harder than 2D, but:
+
+1. We already have raycasting and hit detection
+2. Floor plane projection for drag is straightforward
+3. Users already understand the 3D orbit camera
+4. A 2D top-down view can be added later as an alternative mode
+
+### Undo/Redo Approach
+
+**Decision: Full state snapshots with structuredClone.**
+
+Alternative: Command pattern (store individual operations). More memory-efficient but harder to implement correctly.
+
+For a house design with ~50 furniture items, the design JSON is ~10-20KB. Storing 50 undo snapshots = ~1MB. That's fine. Simplicity wins.
+
+### Collision Detection
+
+**Decision: Defer to Phase 2.**
+
+AABB collision detection is useful but not critical for MVP. Users can visually avoid overlaps. Implement only if users find placement confusing without it.
+
+---
+
+## Implementation Roadmap
+
+### Sprint 1: Foundation (3-4 tasks)
+
+1. **DesignState class** — observable state with undo/redo
+2. **Renderer events** — `furnitureclick`, `floorchange`, controls toggle
+3. **InteractionManager skeleton** — mode system, keyboard shortcuts
+4. **Selection visual** — outline on selected furniture
+
+### Sprint 2: Drag & Drop (3-4 tasks)
+
+5. **Drag-to-move** — floor plane projection, OrbitControls toggle
+6. **Grid snapping** — configurable snap size
+7. **Room bounds constraint** — keep furniture in rooms
+8. **Rotate & delete** — R key, Delete key
+
+### Sprint 3: Catalog & Placement (2-3 tasks)
+
+9. **Catalog sidebar panel** — browsable, filterable
+10. **Click-to-place** — new furniture from catalog
+11. **Ghost preview** — semi-transparent placement preview
+
+### Sprint 4: Themes & Export (3-4 tasks)
+
+12. **Theme system** — 5 presets, apply/switch
+13. **Theme selector UI**
+14. **JSON save/load**
+15. **PNG screenshot**
+
+### Sprint 5: Room Editing & Polish (3-4 tasks)
+
+16. **Room property panel** — read-only info
+17. **Editable flooring/name**
+18. **Auto-save to localStorage**
+19. **Toolbar with undo/redo buttons**
+
+### Future Sprints
+
+20. Room resize handles
+21. 2D floor plan export (PDF)
+22. glTF 3D export
+23. Collision detection
+24. Per-room color overrides
+25. Door/window editing
+
+---
+
+## Task Breakdown for mai
+
+These are the tasks to create for implementation:
+
+```
+1. "Create DesignState class with undo/redo" (coder)
+2. "Add furniture click events and controls toggle to renderer" (coder)
+3. "Build InteractionManager with mode system and keyboard shortcuts" (coder)
+4. "Implement furniture selection with outline visual" (coder)
+5. "Add drag-to-move furniture on floor plane" (coder)
+6. "Add grid snapping and room bounds constraint" (coder)
+7. "Build catalog sidebar panel with categories and search" (coder)
+8. "Implement click-to-place new furniture from catalog" (coder)
+9. "Create theme system with 5 presets" (coder)
+10. "Add theme selector UI to sidebar" (coder)
+11. "Implement JSON save/load and PNG screenshot export" (coder)
+12. "Add room property panel and editable properties" (coder)
+13. "Add toolbar with undo/redo, grid toggle, snap settings" (coder)
+14. "Implement auto-save to localStorage" (coder)
+```
+
+---
+
+## Summary
+
+This design adds full interactivity to the house viewer while respecting the existing architecture:
+
+- **No framework rewrite** — vanilla JS modules that compose with HouseRenderer
+- **No new dependencies** — everything built with Three.js and browser APIs
+- **Incremental delivery** — each sprint produces a usable improvement
+- **Clean separation** — interaction, themes, export, and UI are independent modules
+- **Undo/redo from day 1** — critical for any editor
+- **14 implementation tasks**, roughly 5 sprints
diff --git a/RESEARCH.md b/RESEARCH.md
new file mode 100644
index 0000000..04167ef
--- /dev/null
+++ b/RESEARCH.md
@@ -0,0 +1,256 @@
+# Interior Design Visualization - Tech Stack Research
+
+**Task:** t-fb166
+**Date:** 2026-02-07
+**Researcher:** bohr
+
+## Executive Summary
+
+**Recommended approach: 2D-first with Konva.js (react-konva), Vite+React app, with optional 3D preview via React Three Fiber later.**
+
+This is the simplest viable path to a working room planner prototype. Start with 2D canvas-based floor plan editing, add 3D viewing as a second phase.
+
+---
+
+## Options Evaluated
+
+### Option A: Pure 3D with React Three Fiber (R3F)
+
+**Stack:** Vite + React + @react-three/fiber + @react-three/drei
+
+**How it works:** Everything rendered in a WebGL 3D scene. Room walls are 3D meshes, furniture are 3D models (GLTF/GLB), camera orbits around the scene.
+
+**Pros:**
+- Visually impressive - realistic lighting, shadows, materials
+- R3F is mature (huge community, active maintenance, excellent docs)
+- drei provides orbit controls, drag controls, environment maps out of the box
+- Good ecosystem of 3D furniture models (Sketchfab, etc.)
+
+**Cons:**
+- **Hardest to build** - 3D drag-and-drop is complex (raycasting, plane projection)
+- Precise measurement/placement is difficult in 3D
+- Requires 3D model assets for all furniture (heavy, complex pipeline)
+- Performance concerns with many objects + shadows
+- Steep learning curve for 3D math (vectors, quaternions, raycasting)
+- Drawing room outlines in 3D is unintuitive for users
+
+**Complexity: HIGH** | **Time to prototype: 3-4 weeks**
+
+**Key repos:**
+- [threejs-3d-room-designer](https://github.com/CodeHole7/threejs-3d-room-designer) - React + Three.js room planner
+- [threejs-room-planner](https://github.com/nickorzha/threejs-room-planner) - Similar approach
+
+---
+
+### Option B: Hybrid 2D Editor + 3D Preview (react-planner style)
+
+**Stack:** Vite + React + SVG (2D) + Three.js (3D preview)
+
+**How it works:** Users draw/edit floor plans in a 2D SVG view, then toggle to a 3D preview. The same data model drives both views.
+
+**Pros:**
+- Best UX - 2D for precise editing, 3D for visualization
+- Proven concept (react-planner has 1.4k GitHub stars)
+- 2D editing is simpler and more intuitive for users
+- 3D adds wow factor for presentations
+
+**Cons:**
+- Must maintain two rendering paths (SVG + Three.js)
+- react-planner itself is **outdated** (React 16, Redux, Immutable.js - needs modernization)
+- More code to maintain than either pure approach
+- SVG can become slow with very complex plans
+
+**Complexity: MEDIUM-HIGH** | **Time to prototype: 2-3 weeks**
+
+**Key repos:**
+- [react-planner](https://github.com/cvdlab/react-planner) - 1.4k stars, SVG 2D + Three.js 3D, but old React stack
+- [arcada](https://github.com/mehanix/arcada) - React + Pixi.js, 196 stars, includes backend
+
+---
+
+### Option C: 2D Canvas with Konva.js (RECOMMENDED)
+
+**Stack:** Vite + React + react-konva
+
+**How it works:** Room plans rendered on HTML5 Canvas via Konva.js. Rooms are drawn as polygons/rectangles, furniture items are draggable shapes/images. Top-down 2D view.
+
+**Pros:**
+- **Simplest to build** - Konva handles drag-drop, transforms, snapping natively
+- react-konva integrates cleanly with React component model
+- Canvas performs well with hundreds of objects
+- Built-in: drag-and-drop, resize handles, rotation, snapping, grouping, layers
+- Easy to add grid snapping, measurements, labels
+- Export to PNG/PDF straightforward
+- Familiar 2D interaction model for users (like Google Slides)
+- Can add 3D preview later using same data model
+
+**Cons:**
+- No 3D visualization (initially)
+- Less visually impressive than 3D
+- Canvas text rendering less crisp than SVG (mitigated by high-DPI)
+
+**Complexity: LOW-MEDIUM** | **Time to prototype: 1-2 weeks**
+
+**Alternative: Fabric.js** - Similar capabilities, also canvas-based, slightly different API. Konva has better React integration via react-konva.
+
+---
+
+### Option D: 2D SVG with React
+
+**Stack:** Vite + React + raw SVG (or svg.js)
+
+**How it works:** Floor plans rendered as SVG elements directly in React. Furniture items are SVG groups with drag behavior.
+
+**Pros:**
+- SVG is resolution-independent (crisp at all zoom levels)
+- React manages SVG elements naturally (they're just DOM nodes)
+- Good for precise measurements and labels
+- Easy to style with CSS
+
+**Cons:**
+- Performance degrades with many elements (SVG is DOM-based)
+- Custom drag-and-drop implementation needed (no built-in like Konva)
+- Transform handles, snapping, rotation all manual work
+- More boilerplate than Konva for interactive features
+
+**Complexity: MEDIUM** | **Time to prototype: 2 weeks**
+
+---
+
+## Comparison Matrix
+
+| Criteria | A: Pure 3D (R3F) | B: Hybrid 2D+3D | C: 2D Konva (rec) | D: 2D SVG |
+|-----------------------|-------------------|------------------|---------------------|-----------|
+| Time to prototype | 3-4 weeks | 2-3 weeks | **1-2 weeks** | 2 weeks |
+| Visual impact | **Highest** | High | Medium | Medium |
+| Ease of furniture add | Hard (3D models) | Medium | **Easy (images)** | Easy |
+| Drag-and-drop | Complex | Medium | **Built-in** | Manual |
+| Precise placement | Hard | Good | **Good** | Good |
+| Performance | Medium | Medium | **Good** | Medium |
+| Upgrade path to 3D | N/A | Built-in | **Add later** | Add later |
+| Learning curve | Steep | Medium | **Low** | Low |
+| Mobile support | Limited | Limited | **Good (touch)** | Good |
+
+---
+
+## Recommended Architecture
+
+### Phase 1: 2D Room Planner (MVP)
+
+```
+Tech Stack:
+- Vite + React + TypeScript
+- react-konva (2D canvas rendering)
+- Zustand (lightweight state management)
+- Tailwind CSS (UI styling)
+- shadcn/ui (UI components)
+
+Features:
+- Draw room outlines (rectangles, L-shapes)
+- Grid-based canvas with snap-to-grid
+- Furniture catalog sidebar (chair, table, bed, sofa, etc.)
+- Drag furniture from catalog onto room
+- Move, rotate, resize placed furniture
+- Room dimensions and measurements
+- Export as PNG image
+- Save/load room plans (JSON)
+```
+
+### Phase 2: Enhanced Features
+
+```
+- Upload room image as background trace layer
+- More furniture with realistic top-down SVG/PNG icons
+- Multiple rooms / full floor plans
+- Wall thickness and door/window placement
+- Measurement labels and area calculation
+- Color/material swatches for floors and walls
+```
+
+### Phase 3: 3D Preview (Optional)
+
+```
+Add-on stack:
+- @react-three/fiber + @react-three/drei
+- Same room data model → 3D scene generation
+- Orbit camera view of the room
+- Basic lighting and materials
+```
+
+---
+
+## Key Libraries
+
+| Library | Purpose | npm Weekly Downloads | Maturity |
+|---------|---------|---------------------|----------|
+| react-konva | React bindings for Konva canvas | ~100k+ | Stable, active |
+| konva | 2D canvas framework | ~200k+ | Stable, v9+ |
+| zustand | State management | ~3M+ | Very active |
+| @react-three/fiber | React Three.js (Phase 3) | ~500k+ | Very active |
+| @react-three/drei | R3F helpers (Phase 3) | ~500k+ | Very active |
+
+---
+
+## Existing Projects Worth Studying
+
+1. **[react-planner](https://github.com/cvdlab/react-planner)** - Best reference for hybrid 2D/3D architecture. Study its data model even if not using the code directly (it's outdated React 16).
+
+2. **[arcada](https://github.com/mehanix/arcada)** - React + Pixi.js floor planner. Good reference for feature set and UX patterns. Has a [bachelor's thesis document](https://github.com/mehanix/arcada/blob/master/docs/) explaining the architecture.
+
+3. **[Floorplan.js](https://floorplanjs.org/)** - Commercial-ish but has good UX patterns to study.
+
+4. **Sweet Home 3D** - Java desktop app, but gold standard for feature set reference. Shows what users expect from a room planner.
+
+---
+
+## Data Model Sketch
+
+```typescript
+interface RoomPlan {
+ id: string;
+ name: string;
+ rooms: Room[];
+ furniture: FurnitureItem[];
+}
+
+interface Room {
+ id: string;
+ points: Point[]; // polygon vertices
+ label: string;
+ floorColor: string;
+}
+
+interface FurnitureItem {
+ id: string;
+ catalogId: string; // reference to catalog
+ x: number;
+ y: number;
+ rotation: number; // degrees
+ scaleX: number;
+ scaleY: number;
+}
+
+interface CatalogEntry {
+ id: string;
+ name: string;
+ category: string;
+ width: number; // real-world cm
+ height: number;
+ icon: string; // SVG/PNG path for 2D view
+ model3d?: string; // GLTF path for future 3D view
+}
+```
+
+This data model works for both 2D rendering (Konva) and future 3D rendering (R3F), enabling a clean upgrade path.
+
+---
+
+## Conclusion
+
+**Start with Option C (2D Konva.js)** because:
+1. Fastest path to a working prototype
+2. Konva handles 90% of the interactive features we need out of the box
+3. Clean data model that separates room geometry from rendering
+4. 3D can be added as a second view of the same data later
+5. Modern stack (Vite + React + TS + Zustand) with excellent DX
+6. Low risk - all well-maintained, well-documented libraries