let-them-talk 5.2.5 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +158 -592
  3. package/SECURITY.md +3 -3
  4. package/USAGE.md +151 -0
  5. package/agent-contracts.js +447 -0
  6. package/api-agents.js +760 -0
  7. package/autonomy/decision-v2.js +380 -0
  8. package/autonomy/watchdog-policy.js +572 -0
  9. package/cli.js +454 -298
  10. package/conversation-templates/autonomous-feature.json +83 -22
  11. package/conversation-templates/code-review.json +69 -21
  12. package/conversation-templates/debug-squad.json +69 -21
  13. package/conversation-templates/feature-build.json +69 -21
  14. package/conversation-templates/research-write.json +69 -21
  15. package/dashboard.html +3148 -174
  16. package/dashboard.js +823 -786
  17. package/data-dir.js +58 -0
  18. package/docs/architecture/branch-semantics.md +157 -0
  19. package/docs/architecture/canonical-event-schema.md +88 -0
  20. package/docs/architecture/markdown-workspace.md +183 -0
  21. package/docs/architecture/runtime-contract.md +459 -0
  22. package/docs/architecture/runtime-migration-hardening.md +64 -0
  23. package/events/hooks.js +154 -0
  24. package/events/log.js +457 -0
  25. package/events/replay.js +33 -0
  26. package/events/schema.js +432 -0
  27. package/managed-team-integration.js +261 -0
  28. package/office/agents.js +704 -597
  29. package/office/animation.js +1 -1
  30. package/office/assets/arcade-cabinet.js +141 -0
  31. package/office/assets/archway.js +77 -0
  32. package/office/assets/bar-counter.js +91 -0
  33. package/office/assets/bar-stool.js +71 -0
  34. package/office/assets/beanbag.js +64 -0
  35. package/office/assets/bench.js +99 -0
  36. package/office/assets/bollard.js +87 -0
  37. package/office/assets/cactus.js +100 -0
  38. package/office/assets/carpet-tile.js +46 -0
  39. package/office/assets/chair.js +123 -0
  40. package/office/assets/chandelier.js +107 -0
  41. package/office/assets/coffee-machine.js +95 -0
  42. package/office/assets/coffee-table.js +81 -0
  43. package/office/assets/column.js +95 -0
  44. package/office/assets/desk-lamp.js +102 -0
  45. package/office/assets/desk.js +76 -0
  46. package/office/assets/dining-table.js +105 -0
  47. package/office/assets/door.js +70 -0
  48. package/office/assets/dual-monitor.js +72 -0
  49. package/office/assets/fence.js +76 -0
  50. package/office/assets/filing-cabinet.js +111 -0
  51. package/office/assets/floor-lamp.js +69 -0
  52. package/office/assets/floor-tile.js +54 -0
  53. package/office/assets/flower-pot.js +76 -0
  54. package/office/assets/foosball.js +95 -0
  55. package/office/assets/fridge.js +99 -0
  56. package/office/assets/gaming-chair.js +154 -0
  57. package/office/assets/gaming-desk.js +105 -0
  58. package/office/assets/glass-door.js +72 -0
  59. package/office/assets/glass-wall.js +64 -0
  60. package/office/assets/half-wall.js +49 -0
  61. package/office/assets/hanging-plant.js +112 -0
  62. package/office/assets/index.js +151 -0
  63. package/office/assets/indoor-tree.js +90 -0
  64. package/office/assets/l-sofa.js +153 -0
  65. package/office/assets/marble-floor.js +64 -0
  66. package/office/assets/materials.js +40 -0
  67. package/office/assets/meeting-table.js +88 -0
  68. package/office/assets/microwave.js +94 -0
  69. package/office/assets/monitor.js +67 -0
  70. package/office/assets/neon-strip.js +73 -0
  71. package/office/assets/painting.js +84 -0
  72. package/office/assets/palm-tree.js +108 -0
  73. package/office/assets/pc-tower.js +91 -0
  74. package/office/assets/pendant-light.js +67 -0
  75. package/office/assets/ping-pong.js +114 -0
  76. package/office/assets/plant.js +72 -0
  77. package/office/assets/planter-box.js +95 -0
  78. package/office/assets/pool-table.js +94 -0
  79. package/office/assets/printer.js +113 -0
  80. package/office/assets/reception-desk.js +133 -0
  81. package/office/assets/rug.js +78 -0
  82. package/office/assets/sculpture.js +85 -0
  83. package/office/assets/server-rack.js +98 -0
  84. package/office/assets/sink.js +109 -0
  85. package/office/assets/sofa.js +106 -0
  86. package/office/assets/speaker.js +83 -0
  87. package/office/assets/spotlight.js +83 -0
  88. package/office/assets/street-lamp.js +97 -0
  89. package/office/assets/trash-can.js +83 -0
  90. package/office/assets/treadmill.js +126 -0
  91. package/office/assets/trophy.js +89 -0
  92. package/office/assets/tv-screen.js +79 -0
  93. package/office/assets/vase.js +84 -0
  94. package/office/assets/wall-clock.js +84 -0
  95. package/office/assets/wall.js +53 -0
  96. package/office/assets/water-cooler.js +146 -0
  97. package/office/assets/whiteboard.js +115 -0
  98. package/office/assets.js +3 -431
  99. package/office/builder.js +791 -355
  100. package/office/campus-env.js +1012 -1119
  101. package/office/environment.js +2 -0
  102. package/office/gallery.js +997 -0
  103. package/office/index.js +165 -61
  104. package/office/navigation.js +173 -152
  105. package/office/player.js +178 -68
  106. package/office/robot-character.js +272 -0
  107. package/office/spectator-camera.js +33 -10
  108. package/office/state.js +2 -0
  109. package/office/world-save.js +35 -4
  110. package/package.json +57 -3
  111. package/providers/comfyui.js +383 -0
  112. package/providers/dalle.js +79 -0
  113. package/providers/gemini.js +181 -0
  114. package/providers/ollama.js +184 -0
  115. package/providers/replicate.js +115 -0
  116. package/providers/zai.js +183 -0
  117. package/runtime-descriptor.js +270 -0
  118. package/scripts/check-agent-contract-advisory.js +132 -0
  119. package/scripts/check-api-agent-parity.js +277 -0
  120. package/scripts/check-autonomy-v2-decision.js +207 -0
  121. package/scripts/check-autonomy-v2-execution.js +588 -0
  122. package/scripts/check-autonomy-v2-watchdog.js +224 -0
  123. package/scripts/check-branch-fork-snapshot.js +337 -0
  124. package/scripts/check-branch-isolation.js +787 -0
  125. package/scripts/check-branch-semantics.js +139 -0
  126. package/scripts/check-dashboard-control-plane.js +1304 -0
  127. package/scripts/check-docs-onboarding.js +490 -0
  128. package/scripts/check-event-schema.js +276 -0
  129. package/scripts/check-evidence-completion.js +239 -0
  130. package/scripts/check-invariants.js +992 -0
  131. package/scripts/check-lifecycle-hooks.js +525 -0
  132. package/scripts/check-managed-team-integration.js +166 -0
  133. package/scripts/check-markdown-workspace-export.js +548 -0
  134. package/scripts/check-markdown-workspace-safety.js +347 -0
  135. package/scripts/check-markdown-workspace.js +136 -0
  136. package/scripts/check-message-replay.js +429 -0
  137. package/scripts/check-migration-hardening.js +300 -0
  138. package/scripts/check-performance-indexing.js +272 -0
  139. package/scripts/check-provider-capabilities.js +316 -0
  140. package/scripts/check-runtime-contract.js +109 -0
  141. package/scripts/check-session-aware-context.js +172 -0
  142. package/scripts/check-session-lifecycle.js +210 -0
  143. package/scripts/export-markdown-workspace.js +84 -0
  144. package/scripts/fixtures/message-replay/clean.jsonl +2 -0
  145. package/scripts/fixtures/message-replay/corrupt-correction-payload.jsonl +1 -0
  146. package/scripts/fixtures/message-replay/corrupt-jsonl.jsonl +1 -0
  147. package/scripts/fixtures/message-replay/corrupt-payload.jsonl +1 -0
  148. package/scripts/fixtures/message-replay/out-of-order.jsonl +2 -0
  149. package/scripts/migrate-legacy-to-canonical.js +201 -0
  150. package/scripts/run-verification-suite.js +242 -0
  151. package/scripts/sync-packaged-docs.js +69 -0
  152. package/server.js +9546 -7214
  153. package/state/agents.js +161 -0
  154. package/state/canonical.js +3068 -0
  155. package/state/dashboard-queries.js +441 -0
  156. package/state/evidence.js +56 -0
  157. package/state/io.js +69 -0
  158. package/state/markdown-workspace.js +951 -0
  159. package/state/messages.js +669 -0
  160. package/state/sessions.js +683 -0
  161. package/state/tasks-workflows.js +92 -0
  162. package/templates/debate.json +2 -2
  163. package/templates/managed.json +4 -4
  164. package/templates/pair.json +2 -2
  165. package/templates/review.json +2 -2
  166. package/templates/team.json +3 -3
@@ -3,123 +3,105 @@ import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
3
3
  import { S } from './state.js';
4
4
 
5
5
  // ============================================================
6
- // TECH CAMPUS — Premium 2-floor environment
7
- // Inspired by Google/Apple HQ: marble, glass, wood, RGB gaming
6
+ // TECH HQ — Premium 2-floor environment
7
+ // Inspired by Apple Park / Bloomberg London
8
8
  // ============================================================
9
9
 
10
- var CAMPUS_W = 50;
11
- var CAMPUS_D = 35;
12
- var WALL_H = 6;
13
- var MEZZ_H = 3.2;
14
- var MEZZ_DEPTH = 12; // how far mezzanine extends from back wall
10
+ var CAMPUS_W = 90; // total interior width (X axis)
11
+ var CAMPUS_D = 60; // total interior depth (Z axis)
12
+ var WALL_H = 6; // interior wall height
13
+ var MEZZ_H = 3.2; // mezzanine floor height
14
+ var MEZZ_DEPTH = 12; // mezzanine depth from back wall
15
15
 
16
- // Campus desk positions (gaming desk layout)
16
+ // ── 5×4 grid: X in [-8,-4,0,4,8], Z in [6,10,14,18]
17
+ // ── Manager office at world (30, 10)
17
18
  var CAMPUS_DESKS = [
18
- // Main coder zone (center, 3 rows of 4)
19
- { x: -4.5, z: 2 }, { x: -1.5, z: 2 }, { x: 1.5, z: 2 }, { x: 4.5, z: 2 },
20
- { x: -4.5, z: -1 }, { x: -1.5, z: -1 }, { x: 1.5, z: -1 }, { x: 4.5, z: -1 },
21
- { x: -4.5, z: -4 }, { x: -1.5, z: -4 }, { x: 1.5, z: -4 }, { x: 4.5, z: -4 },
22
- // Designer wing (left, 2 rows of 2)
23
- { x: -14, z: 1 }, { x: -11, z: 1 },
24
- { x: -14, z: -2 }, { x: -11, z: -2 },
25
- // Manager's private office desk (last position assigned to first "Manager" role agent)
26
- // Office at (12,5), desk at relative (0,1.5)=world(12,6.5), chair at relative (0,2.4)=world(12,7.4)
27
- // Agent sits at deskPos.z + 0.7, so set z to 6.7 → agent at 7.4 (chair pos)
28
- { x: 12, z: 6.7 },
19
+ // Row Z=6
20
+ { x: -8, z: 6 }, { x: -4, z: 6 }, { x: 0, z: 6 }, { x: 4, z: 6 }, { x: 8, z: 6 },
21
+ // Row Z=10
22
+ { x: -8, z: 10 }, { x: -4, z: 10 }, { x: 0, z: 10 }, { x: 4, z: 10 }, { x: 8, z: 10 },
23
+ // Row Z=14
24
+ { x: -8, z: 14 }, { x: -4, z: 14 }, { x: 0, z: 14 }, { x: 4, z: 14 }, { x: 8, z: 14 },
25
+ // Row Z=18
26
+ { x: -8, z: 18 }, { x: -4, z: 18 }, { x: 0, z: 18 }, { x: 4, z: 18 }, { x: 8, z: 18 },
27
+ // Manager office desk (index 20) chair at world Z=12.7, agent walks to z+0.7
28
+ { x: 30, z: 12.0 },
29
29
  ];
30
30
 
31
31
  export function getCampusDeskPositions() {
32
32
  return CAMPUS_DESKS;
33
33
  }
34
34
 
35
+ // ============================================================
36
+ // MAIN BUILD ENTRY
37
+ // ============================================================
35
38
  export function buildCampusEnvironment() {
36
39
  S.deskMeshes = [];
37
40
 
38
- // Materials palette
39
- var marbleMat = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.15, metalness: 0.05 });
40
- var marbleDarkMat = new THREE.MeshStandardMaterial({ color: 0xd4cfc7, roughness: 0.2 });
41
- var walnutMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e, roughness: 0.6 });
42
- var walnutLightMat = new THREE.MeshStandardMaterial({ color: 0x8B5E3C, roughness: 0.55 });
43
- var chromeMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.1, metalness: 0.8 });
44
- var glassMat = new THREE.MeshStandardMaterial({ color: 0xaaccee, transparent: true, opacity: 0.25, roughness: 0.05, metalness: 0.1 });
45
- var glassFrameMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.2, metalness: 0.6 });
46
- var darkMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.4 });
47
- var carpetMat = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.95 });
48
- var neonBlueMat = new THREE.MeshStandardMaterial({ color: 0x58a6ff, emissive: 0x58a6ff, emissiveIntensity: 0.6, roughness: 0.2 });
49
- var neonPurpleMat = new THREE.MeshStandardMaterial({ color: 0xa855f7, emissive: 0xa855f7, emissiveIntensity: 0.5, roughness: 0.2 });
50
- var neonGreenMat = new THREE.MeshStandardMaterial({ color: 0x22c55e, emissive: 0x22c55e, emissiveIntensity: 0.5, roughness: 0.2 });
51
- var concreteMat = new THREE.MeshStandardMaterial({ color: 0x3a3d45, roughness: 0.85 });
52
- var leatherBlackMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.7 });
53
- var leatherBrownMat = new THREE.MeshStandardMaterial({ color: 0x6b3e26, roughness: 0.65 });
54
- var goldMat = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.3, metalness: 0.7 });
55
-
56
- // ========== FLOOR ==========
57
- buildCampusFloor(marbleMat, marbleDarkMat, carpetMat);
58
-
59
- // ========== WALLS & WINDOWS ==========
60
- buildCampusWalls(concreteMat, glassMat, glassFrameMat);
61
-
62
- // ========== CEILING & SKYLIGHTS ==========
63
- buildCampusCeiling(concreteMat, glassMat);
64
-
65
- // ========== MEZZANINE (2nd floor) ==========
66
- buildMezzanine(concreteMat, chromeMat, glassMat, walnutMat);
67
-
68
- // ========== STAIRCASE ==========
69
- buildStaircase(marbleMat, chromeMat, glassMat);
70
-
71
- // ========== GRAND LOBBY ==========
72
- buildLobby(marbleMat, chromeMat, goldMat, walnutMat);
73
-
74
- // ========== GAMING DESKS (main workspace, skip last = manager office) ==========
75
- var managerDeskIdx = CAMPUS_DESKS.length - 1;
41
+ // ── Material palette (defined once, reused everywhere) ──────────
42
+ var matBlack = new THREE.MeshStandardMaterial({ color: 0x1a1c22, roughness: 0.12, metalness: 0.05 });
43
+ var matWhite = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.15, metalness: 0.05 });
44
+ var matWalnutDk = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.55 });
45
+ var matWalnutLt = new THREE.MeshStandardMaterial({ color: 0x8B5E3C, roughness: 0.50 });
46
+ var matChrPol = new THREE.MeshStandardMaterial({ color: 0xd0d0d0, roughness: 0.08, metalness: 0.85 });
47
+ var matChrBr = new THREE.MeshStandardMaterial({ color: 0x999999, roughness: 0.25, metalness: 0.70 });
48
+ var matGlassCl = new THREE.MeshStandardMaterial({ color: 0xaaccee, roughness: 0.05, metalness: 0.10, transparent: true, opacity: 0.20, side: THREE.DoubleSide });
49
+ var matGlassFr = new THREE.MeshStandardMaterial({ color: 0xd0d8e8, roughness: 0.40, transparent: true, opacity: 0.50, side: THREE.DoubleSide });
50
+ var matConcrete = new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.85 });
51
+ var matLeathBk = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.70 });
52
+ var matLeathCog = new THREE.MeshStandardMaterial({ color: 0x8B4513, roughness: 0.65 });
53
+ var matGold = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.30, metalness: 0.70 });
54
+ var matNeonBl = new THREE.MeshStandardMaterial({ color: 0x58a6ff, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.60, roughness: 0.20 });
55
+ var matNeonPu = new THREE.MeshStandardMaterial({ color: 0xa855f7, emissive: new THREE.Color(0xa855f7), emissiveIntensity: 0.50, roughness: 0.20 });
56
+ var matNeonGr = new THREE.MeshStandardMaterial({ color: 0x22c55e, emissive: new THREE.Color(0x22c55e), emissiveIntensity: 0.50, roughness: 0.20 });
57
+ var matFabric = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.95 });
58
+
59
+ // ── Sub-builders ────────────────────────────────────────────────
60
+ buildCampusFloor(matBlack, matWhite, matFabric);
61
+ buildCampusWalls(matConcrete, matGlassCl, matChrBr, matGold, matWhite);
62
+ buildCampusCeiling(matConcrete, matGlassCl);
63
+ buildStructuralColumns(matWhite, matGold, matChrPol);
64
+ buildMezzanine(matConcrete, matChrPol, matGlassCl, matWalnutDk, matFabric);
65
+ buildStaircase(matWhite, matChrPol, matGlassCl);
66
+ buildLobby(matWhite, matChrPol, matGold, matWalnutDk, matNeonBl);
67
+ buildMainCorridor(matBlack, matGold);
68
+ buildCrossCorridor(matBlack, matGold);
69
+
70
+ // ── Gaming desks (skip manager desk at index 20) ────────────────
71
+ var mgrIdx = CAMPUS_DESKS.length - 1;
76
72
  CAMPUS_DESKS.forEach(function(pos, i) {
77
- if (i === managerDeskIdx) return; // manager office has its own built-in desk
73
+ if (i === mgrIdx) return;
78
74
  buildGamingDesk(pos.x, pos.z, i);
79
75
  });
80
76
 
81
- // ========== MANAGER'S OFFICE (glass room, front right) ==========
82
- buildManagerOffice(12, 5, glassMat, glassFrameMat, walnutMat, leatherBrownMat, chromeMat);
83
-
84
- // ========== DESIGNER STUDIO (left wing) ==========
85
- buildDesignerStudio(-12.5, 0, walnutLightMat, chromeMat);
86
-
87
- // ========== BAR & CAFÉ (back left) ==========
88
- buildBar(-14, -12, walnutMat, chromeMat, neonBlueMat, neonPurpleMat);
89
- buildJukebox(-10, -13.5); // Right side of bar area, against the back wall
77
+ // ── Manager's glass office ──────────────────────────────────────
78
+ buildManagerOffice(30, 10, matGlassCl, matGlassFr, matChrBr, matWalnutDk, matLeathCog, matChrPol);
90
79
 
91
- // ========== RECREATION CENTER (back center) ==========
92
- buildRecCenter(0, -12, walnutMat, chromeMat, carpetMat);
93
-
94
- // ========== GYM (back right) ==========
95
- buildGym(14, -12, chromeMat, darkMat);
96
-
97
- // ========== PLANTS & GREENERY ==========
80
+ // ── Zones ───────────────────────────────────────────────────────
81
+ buildDesignerStudio(-28, 0, matWalnutLt, matChrPol);
82
+ buildBar(-28, -18, matWalnutDk, matChrPol, matNeonBl, matNeonPu);
83
+ buildJukebox(-18, -22);
84
+ buildRecCenter(0, -18, matWalnutDk, matChrPol, matFabric);
85
+ buildGym(22, -18, matChrPol, matConcrete);
98
86
  buildCampusPlants();
99
-
100
- // ========== PENDANT LIGHTS ==========
101
87
  buildPendantLights();
102
-
103
- // ========== GLASS PARTITIONS ==========
104
- buildGlassPartitions(glassMat, glassFrameMat);
105
-
106
- // ========== NEON SIGNS ==========
107
- buildNeonSign('INNOVATE', -7, 4.5, -CAMPUS_D / 2 + 0.2, neonBlueMat);
108
- buildNeonSign('CREATE', 7, 4.5, -CAMPUS_D / 2 + 0.2, neonPurpleMat);
109
- buildNeonSign('BUILD', 0, MEZZ_H + 2, -CAMPUS_D / 2 + MEZZ_DEPTH + 0.2, neonGreenMat);
88
+ buildGlassPartitions(matGlassCl, matChrBr);
89
+ buildNeonSign('INNOVATE', -12, 4.5, -CAMPUS_D / 2 + 0.3, matNeonBl);
90
+ buildNeonSign('CREATE', 12, 4.5, -CAMPUS_D / 2 + 0.3, matNeonPu);
91
+ buildNeonSign('BUILD', 0, MEZZ_H + 2.2, -CAMPUS_D / 2 + MEZZ_DEPTH + 0.3, matNeonGr);
110
92
  }
111
93
 
112
- // ==================== FLOOR ====================
113
- function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
114
- // High-quality procedural dark marble tile floor
94
+ // ============================================================
95
+ // FLOOR — FBM dark marble, 90×60
96
+ // ============================================================
97
+ function buildCampusFloor(matBlack, matWhite, matFabric) {
115
98
  var size = 1024;
116
99
  var cvs = document.createElement('canvas');
117
100
  cvs.width = size; cvs.height = size;
118
101
  var ctx = cvs.getContext('2d');
119
- var tiles = 12;
120
- var ts = size / tiles;
102
+ var tiles = 18;
103
+ var ts = Math.floor(size / tiles);
121
104
 
122
- // Simple 2D noise function for marble veining
123
105
  function noise(x, y) {
124
106
  var n = Math.sin(x * 12.9898 + y * 78.233) * 43758.5453;
125
107
  return n - Math.floor(n);
@@ -127,7 +109,7 @@ function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
127
109
  function smoothNoise(x, y) {
128
110
  var ix = Math.floor(x), iy = Math.floor(y);
129
111
  var fx = x - ix, fy = y - iy;
130
- var a = noise(ix, iy), b = noise(ix + 1, iy);
112
+ var a = noise(ix, iy), b = noise(ix + 1, iy);
131
113
  var c = noise(ix, iy + 1), d = noise(ix + 1, iy + 1);
132
114
  var u = fx * fx * (3 - 2 * fx), v = fy * fy * (3 - 2 * fy);
133
115
  return a + (b - a) * u + (c - a) * v + (a - b - c + d) * u * v;
@@ -144,46 +126,30 @@ function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
144
126
  for (var ti = 0; ti < tiles; ti++) {
145
127
  for (var tj = 0; tj < tiles; tj++) {
146
128
  var tx = ti * ts, ty = tj * ts;
147
-
148
- // Alternating dark/darker marble tiles
149
129
  var isDark = (ti + tj) % 2 === 0;
150
- var baseR = isDark ? 28 : 22;
151
- var baseG = isDark ? 30 : 24;
152
- var baseB = isDark ? 38 : 30;
153
-
154
- // Fill base tile color
155
- ctx.fillStyle = 'rgb(' + baseR + ',' + baseG + ',' + baseB + ')';
130
+ var bR = isDark ? 26 : 20, bG = isDark ? 28 : 22, bB = isDark ? 36 : 28;
131
+ ctx.fillStyle = 'rgb(' + bR + ',' + bG + ',' + bB + ')';
156
132
  ctx.fillRect(tx, ty, ts, ts);
157
-
158
- // Marble veining (per-pixel noise)
159
133
  var imgData = ctx.getImageData(tx, ty, ts, ts);
160
134
  var data = imgData.data;
161
135
  for (var py = 0; py < ts; py++) {
162
136
  for (var px = 0; px < ts; px++) {
163
- var wx = (ti * ts + px) / size * 6;
164
- var wy = (tj * ts + py) / size * 6;
165
-
166
- // Marble pattern: distorted sine wave + noise
167
- var vein = Math.sin(wx * 3 + fbm(wx * 2, wy * 2) * 4) * 0.5 + 0.5;
168
- var vein2 = Math.sin(wy * 2.5 + fbm(wx * 1.5 + 5, wy * 1.5 + 3) * 3.5) * 0.5 + 0.5;
137
+ var wx = (ti * ts + px) / size * 7;
138
+ var wy = (tj * ts + py) / size * 7;
139
+ var vein = Math.sin(wx * 3 + fbm(wx * 2, wy * 2) * 4) * 0.5 + 0.5;
140
+ var vein2 = Math.sin(wy * 2.5 + fbm(wx * 1.5+5, wy * 1.5+3) * 3.5) * 0.5 + 0.5;
169
141
  var combined = vein * 0.6 + vein2 * 0.4;
170
-
171
- // Color the veins gold/white streaks on dark base
172
- var veinStrength = Math.pow(combined, 3) * 0.35;
173
- var nv = smoothNoise(wx * 4, wy * 4) * 0.08;
174
-
142
+ var vs = Math.pow(combined, 3) * 0.38;
143
+ var nv = smoothNoise(wx * 4, wy * 4) * 0.09;
175
144
  var idx = (py * ts + px) * 4;
176
- // Base dark marble + gold/gray veins
177
- data[idx] = Math.min(255, baseR + veinStrength * 120 + nv * 40); // R
178
- data[idx + 1] = Math.min(255, baseG + veinStrength * 100 + nv * 35); // G
179
- data[idx + 2] = Math.min(255, baseB + veinStrength * 60 + nv * 30); // B
145
+ data[idx] = Math.min(255, bR + vs * 130 + nv * 45);
146
+ data[idx + 1] = Math.min(255, bG + vs * 110 + nv * 38);
147
+ data[idx + 2] = Math.min(255, bB + vs * 65 + nv * 30);
180
148
  data[idx + 3] = 255;
181
149
  }
182
150
  }
183
151
  ctx.putImageData(imgData, tx, ty);
184
-
185
- // Tile grout line (very thin, slightly lighter)
186
- ctx.strokeStyle = 'rgba(50,52,60,0.8)';
152
+ ctx.strokeStyle = 'rgba(48,50,58,0.75)';
187
153
  ctx.lineWidth = 1.5;
188
154
  ctx.strokeRect(tx + 0.5, ty + 0.5, ts - 1, ts - 1);
189
155
  }
@@ -191,1186 +157,1135 @@ function buildCampusFloor(marbleMat, marbleDarkMat, carpetMat) {
191
157
 
192
158
  var floorTex = new THREE.CanvasTexture(cvs);
193
159
  floorTex.wrapS = floorTex.wrapT = THREE.RepeatWrapping;
194
- floorTex.anisotropy = 4;
195
- var floorGeo = new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D);
196
- var floorMeshMat = new THREE.MeshStandardMaterial({
197
- map: floorTex, roughness: 0.12, metalness: 0.08
198
- });
199
- var floor = new THREE.Mesh(floorGeo, floorMeshMat);
160
+ floorTex.anisotropy = 8;
161
+ var floor = new THREE.Mesh(
162
+ new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D),
163
+ new THREE.MeshStandardMaterial({ map: floorTex, roughness: 0.10, metalness: 0.10 })
164
+ );
200
165
  floor.rotation.x = -Math.PI / 2;
201
166
  floor.receiveShadow = true;
202
167
  S.furnitureGroup.add(floor);
203
168
 
204
- // Carpet runner in workspace zone (dark charcoal)
205
- var carpet = new THREE.Mesh(new THREE.PlaneGeometry(14, 10), carpetMat);
169
+ // Carpet runner in workspace zone
170
+ var carpet = new THREE.Mesh(new THREE.PlaneGeometry(26, 18), matFabric);
206
171
  carpet.rotation.x = -Math.PI / 2;
207
- carpet.position.set(0, 0.01, -1);
172
+ carpet.position.set(0, 0.01, 12);
208
173
  carpet.receiveShadow = true;
209
174
  S.furnitureGroup.add(carpet);
210
175
  }
211
176
 
212
- // ==================== WALLS & WINDOWS ====================
213
- function buildCampusWalls(concreteMat, glassMat, frameMat) {
214
- // Back wall (concrete with large windows)
215
- var wallMat = new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.8, side: THREE.DoubleSide });
216
-
217
- var backWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, WALL_H), wallMat);
218
- backWall.position.set(0, WALL_H / 2, -CAMPUS_D / 2);
219
- backWall.receiveShadow = true;
220
- S.furnitureGroup.add(backWall);
221
-
222
- var leftWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_D, WALL_H), wallMat);
223
- leftWall.position.set(-CAMPUS_W / 2, WALL_H / 2, 0);
224
- leftWall.rotation.y = Math.PI / 2;
225
- leftWall.receiveShadow = true;
226
- S.furnitureGroup.add(leftWall);
227
-
228
- var rightWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_D, WALL_H), wallMat);
229
- rightWall.position.set(CAMPUS_W / 2, WALL_H / 2, 0);
230
- rightWall.rotation.y = -Math.PI / 2;
231
- rightWall.receiveShadow = true;
232
- S.furnitureGroup.add(rightWall);
233
-
234
- // Front wall with entrance gap
235
- var frontLeftWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W / 2 - 4, WALL_H), wallMat);
236
- frontLeftWall.position.set(-CAMPUS_W / 4 - 2, WALL_H / 2, CAMPUS_D / 2);
237
- frontLeftWall.rotation.y = Math.PI;
238
- S.furnitureGroup.add(frontLeftWall);
239
-
240
- var frontRightWall = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W / 2 - 4, WALL_H), wallMat);
241
- frontRightWall.position.set(CAMPUS_W / 4 + 2, WALL_H / 2, CAMPUS_D / 2);
242
- frontRightWall.rotation.y = Math.PI;
243
- S.furnitureGroup.add(frontRightWall);
244
-
245
- // Floor-to-ceiling windows (left and right walls)
246
- var windowMat = new THREE.MeshStandardMaterial({
247
- color: 0x87CEEB, emissive: 0x87CEEB, emissiveIntensity: 0.15, roughness: 0.05, transparent: true, opacity: 0.6
248
- });
249
- // Left wall windows
250
- [-10, -5, 0, 5, 10].forEach(function(wz) {
251
- var win = new THREE.Mesh(new THREE.PlaneGeometry(3, 4.5), windowMat);
252
- win.position.set(-CAMPUS_W / 2 + 0.05, 3, wz);
253
- win.rotation.y = Math.PI / 2;
254
- S.furnitureGroup.add(win);
255
- // Chrome frame
256
- var frame = new THREE.Mesh(new THREE.BoxGeometry(0.04, 4.6, 3.1), frameMat);
257
- frame.position.set(-CAMPUS_W / 2 + 0.02, 3, wz);
258
- S.furnitureGroup.add(frame);
177
+ // ============================================================
178
+ // WALLS — thick (0.4) BoxGeometry, recessed windows + frames
179
+ // ============================================================
180
+ function buildCampusWalls(matConcrete, matGlass, matFrame, matGold, matMarble) {
181
+ var wallT = 0.4; // wall thickness
182
+ var wallMat = new THREE.MeshStandardMaterial({ color: 0x22252e, roughness: 0.80 });
183
+ var wSill = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.18, metalness: 0.06 });
184
+
185
+ // Helper: build a recessed window into a wall segment
186
+ function addWindow(wx, wy, wz, rotY, winW, winH) {
187
+ var depth = wallT + 0.02;
188
+ // Recess box (cut-out fill dark interior)
189
+ var recessMat = new THREE.MeshStandardMaterial({ color: 0x15171e, roughness: 0.9 });
190
+ var recess = new THREE.Mesh(new THREE.BoxGeometry(winW + 0.08, winH + 0.08, depth), recessMat);
191
+ recess.position.set(wx, wy, wz);
192
+ recess.rotation.y = rotY;
193
+ S.furnitureGroup.add(recess);
194
+ // Glass pane
195
+ var glass = new THREE.Mesh(new THREE.PlaneGeometry(winW, winH), matGlass);
196
+ glass.rotation.y = rotY;
197
+ // Offset glass flush with inner face
198
+ var nx = Math.sin(rotY) * (depth / 2 - 0.01);
199
+ var nz = Math.cos(rotY) * (depth / 2 - 0.01);
200
+ glass.position.set(wx - nx, wy, wz - nz);
201
+ S.furnitureGroup.add(glass);
202
+ // Chrome frame (4 sides)
203
+ var ft = 0.05; // frame thickness
204
+ var frameMat2 = matFrame;
205
+ // horizontal top/bottom bars
206
+ [wy + winH / 2 + ft / 2, wy - winH / 2 - ft / 2].forEach(function(fy) {
207
+ var bar = new THREE.Mesh(new THREE.BoxGeometry(winW + ft * 2, ft, ft), frameMat2);
208
+ bar.rotation.y = rotY;
209
+ bar.position.set(wx - nx, fy, wz - nz);
210
+ S.furnitureGroup.add(bar);
211
+ });
212
+ // vertical left/right bars
213
+ [-winW / 2 - ft / 2, winW / 2 + ft / 2].forEach(function(dx) {
214
+ var cos = Math.cos(rotY), sin = Math.sin(rotY);
215
+ var vx = wx - nx + cos * dx;
216
+ var vz = wz - nz - sin * dx;
217
+ var bar = new THREE.Mesh(new THREE.BoxGeometry(ft, winH + ft * 2, ft), frameMat2);
218
+ bar.position.set(vx, wy, vz);
219
+ S.furnitureGroup.add(bar);
220
+ });
221
+ // Marble sill
222
+ var sill = new THREE.Mesh(new THREE.BoxGeometry(winW + 0.12, 0.06, 0.20), wSill);
223
+ sill.rotation.y = rotY;
224
+ var sx = wx - nx + Math.sin(rotY) * 0.08;
225
+ var sz = wz - nz + Math.cos(rotY) * 0.08;
226
+ sill.position.set(sx, wy - winH / 2 - 0.01, sz);
227
+ S.furnitureGroup.add(sill);
228
+ }
229
+
230
+ // ── BACK WALL (Z = -CAMPUS_D/2) ─────────────────────────────────
231
+ var bwZ = -CAMPUS_D / 2;
232
+ var bw = new THREE.Mesh(new THREE.BoxGeometry(CAMPUS_W, WALL_H, wallT), wallMat);
233
+ bw.position.set(0, WALL_H / 2, bwZ);
234
+ S.furnitureGroup.add(bw);
235
+ // 5 back-wall windows above mezzanine
236
+ [-32, -16, 0, 16, 32].forEach(function(wx) {
237
+ addWindow(wx, MEZZ_H + 1.2, bwZ, 0, 5, 1.8);
259
238
  });
260
- // Right wall windows
261
- [-10, -5, 0, 5, 10].forEach(function(wz) {
262
- var win = new THREE.Mesh(new THREE.PlaneGeometry(3, 4.5), windowMat);
263
- win.position.set(CAMPUS_W / 2 - 0.05, 3, wz);
264
- win.rotation.y = -Math.PI / 2;
265
- S.furnitureGroup.add(win);
239
+
240
+ // ── LEFT WALL (X = -CAMPUS_W/2) ─────────────────────────────────
241
+ var lwX = -CAMPUS_W / 2;
242
+ var lw = new THREE.Mesh(new THREE.BoxGeometry(wallT, WALL_H, CAMPUS_D), wallMat);
243
+ lw.position.set(lwX, WALL_H / 2, 0);
244
+ S.furnitureGroup.add(lw);
245
+ // 5 windows at Z = -20,-10,0,10,20
246
+ [-20, -10, 0, 10, 20].forEach(function(wz) {
247
+ addWindow(lwX, 2.8, wz, Math.PI / 2, 4, 3.2);
266
248
  });
267
- // Back wall windows (above mezzanine level)
268
- [-15, -8, 0, 8, 15].forEach(function(wx) {
269
- var win = new THREE.Mesh(new THREE.PlaneGeometry(4, 2), windowMat);
270
- win.position.set(wx, 4.5, -CAMPUS_D / 2 + 0.05);
271
- S.furnitureGroup.add(win);
249
+
250
+ // ── RIGHT WALL (X = +CAMPUS_W/2) — solid (gallery is now inside campus)
251
+ var rwX = CAMPUS_W / 2;
252
+ var rwFull = new THREE.Mesh(new THREE.BoxGeometry(wallT, WALL_H, CAMPUS_D), wallMat);
253
+ rwFull.position.set(rwX, WALL_H / 2, 0);
254
+ S.furnitureGroup.add(rwFull);
255
+
256
+ // Right-wall windows
257
+ [-20, -10, 0, 10, 20].forEach(function(wz) {
258
+ addWindow(rwX, 2.8, wz, -Math.PI / 2, 4, 3.2);
272
259
  });
260
+
261
+ // ── FRONT WALL (Z = +CAMPUS_D/2) — split with 6-unit entrance at center
262
+ var fwZ = CAMPUS_D / 2;
263
+ var entHalf = 3; // entrance half-width
264
+ var fwSideLen = (CAMPUS_W - entHalf * 2) / 2;
265
+
266
+ var fwLeft = new THREE.Mesh(new THREE.BoxGeometry(fwSideLen, WALL_H, wallT), wallMat);
267
+ fwLeft.position.set(-(entHalf + fwSideLen / 2), WALL_H / 2, fwZ);
268
+ S.furnitureGroup.add(fwLeft);
269
+
270
+ var fwRight = new THREE.Mesh(new THREE.BoxGeometry(fwSideLen, WALL_H, wallT), wallMat);
271
+ fwRight.position.set(entHalf + fwSideLen / 2, WALL_H / 2, fwZ);
272
+ S.furnitureGroup.add(fwRight);
273
+
274
+ // Gold entrance lintel above opening
275
+ var lintel = new THREE.Mesh(new THREE.BoxGeometry(entHalf * 2 + 0.4, 0.18, wallT + 0.02), matGold);
276
+ lintel.position.set(0, WALL_H - 0.09, fwZ);
277
+ S.furnitureGroup.add(lintel);
278
+
279
+ // Gold threshold strip at floor
280
+ var thresh = new THREE.Mesh(new THREE.BoxGeometry(entHalf * 2, 0.04, 0.30), matGold);
281
+ thresh.position.set(0, 0.02, fwZ - 0.15);
282
+ S.furnitureGroup.add(thresh);
273
283
  }
274
284
 
275
- // ==================== CEILING & SKYLIGHTS ====================
276
- function buildCampusCeiling(concreteMat, glassMat) {
277
- // Group for everything that should hide when camera is above roof
285
+ // ============================================================
286
+ // CEILING — coffered grid (6 X-beams + 10 Z-beams), 5 skylights
287
+ // ============================================================
288
+ function buildCampusCeiling(matConcrete, matGlass) {
278
289
  S._roofGroup = new THREE.Group();
279
290
 
280
- // Main ceiling
281
- var ceilingMat = new THREE.MeshStandardMaterial({ color: 0x1e2028, roughness: 0.9, side: THREE.DoubleSide });
282
- var ceiling = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D), ceilingMat);
291
+ var ceilMat = new THREE.MeshStandardMaterial({ color: 0x1c1f28, roughness: 0.88, side: THREE.DoubleSide });
292
+ // Main ceiling slab
293
+ var ceiling = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, CAMPUS_D), ceilMat);
283
294
  ceiling.rotation.x = Math.PI / 2;
284
295
  ceiling.position.y = WALL_H;
285
296
  S._roofGroup.add(ceiling);
286
297
 
287
- // Skylights (glass rectangles in ceiling)
298
+ // Coffered beams — X-direction (run along X axis)
299
+ var beamMat = new THREE.MeshStandardMaterial({ color: 0x1a1c24, roughness: 0.82 });
300
+ var xBeamZPositions = [-25, -15, -5, 5, 15, 25];
301
+ xBeamZPositions.forEach(function(bz) {
302
+ var bm = new THREE.Mesh(new THREE.BoxGeometry(CAMPUS_W, 0.22, 0.30), beamMat);
303
+ bm.position.set(0, WALL_H - 0.11, bz);
304
+ S._roofGroup.add(bm);
305
+ });
306
+ // Z-direction beams (run along Z axis)
307
+ var zBeamXPositions = [-40, -32, -24, -16, -8, 0, 8, 16, 24, 32, 40];
308
+ zBeamXPositions.forEach(function(bx) {
309
+ var bm = new THREE.Mesh(new THREE.BoxGeometry(0.30, 0.22, CAMPUS_D), beamMat);
310
+ bm.position.set(bx, WALL_H - 0.11, 0);
311
+ S._roofGroup.add(bm);
312
+ });
313
+
314
+ // 5 skylights in cross pattern
288
315
  var skylightMat = new THREE.MeshStandardMaterial({
289
- color: 0xaaddff, emissive: 0xaaddff, emissiveIntensity: 0.3, transparent: true, opacity: 0.4, side: THREE.DoubleSide
316
+ color: 0xaaddff, emissive: new THREE.Color(0xaaddff), emissiveIntensity: 0.35,
317
+ transparent: true, opacity: 0.45, side: THREE.DoubleSide
290
318
  });
291
- [[-6, 4], [6, 4], [-6, -4], [6, -4], [0, 0]].forEach(function(pos) {
292
- var skylight = new THREE.Mesh(new THREE.PlaneGeometry(5, 3), skylightMat);
293
- skylight.rotation.x = Math.PI / 2;
294
- skylight.position.set(pos[0], WALL_H - 0.05, pos[1]);
295
- S._roofGroup.add(skylight);
319
+ var skylightPositions = [
320
+ [0, 0],
321
+ [-16, -12], [16, -12],
322
+ [-16, 12], [16, 12],
323
+ ];
324
+ skylightPositions.forEach(function(pos) {
325
+ var sk = new THREE.Mesh(new THREE.PlaneGeometry(7, 4.5), skylightMat);
326
+ sk.rotation.x = Math.PI / 2;
327
+ sk.position.set(pos[0], WALL_H - 0.04, pos[1]);
328
+ S._roofGroup.add(sk);
329
+ // Chrome skylight frame
330
+ var sfMat = new THREE.MeshStandardMaterial({ color: 0xd0d0d0, roughness: 0.10, metalness: 0.85 });
331
+ var sfH = new THREE.Mesh(new THREE.BoxGeometry(7.2, 0.06, 0.06), sfMat);
332
+ sfH.position.set(pos[0], WALL_H - 0.01, pos[1] - 2.28);
333
+ S._roofGroup.add(sfH.clone());
334
+ sfH.position.z = pos[1] + 2.28;
335
+ S._roofGroup.add(sfH);
336
+ var sfV = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, 4.62), sfMat);
337
+ sfV.position.set(pos[0] - 3.6, WALL_H - 0.01, pos[1]);
338
+ S._roofGroup.add(sfV.clone());
339
+ sfV.position.x = pos[0] + 3.6;
340
+ S._roofGroup.add(sfV);
296
341
  });
297
342
 
298
343
  S.furnitureGroup.add(S._roofGroup);
299
344
  }
300
345
 
301
- // ==================== MEZZANINE ====================
302
- function buildMezzanine(concreteMat, chromeMat, glassMat, walnutMat) {
303
- // Platform
304
- var mezzFloor = new THREE.Mesh(new THREE.BoxGeometry(CAMPUS_W - 2, 0.2, MEZZ_DEPTH), concreteMat);
305
- mezzFloor.position.set(0, MEZZ_H, -CAMPUS_D / 2 + MEZZ_DEPTH / 2);
306
- mezzFloor.castShadow = true; mezzFloor.receiveShadow = true;
307
- S.furnitureGroup.add(mezzFloor);
308
-
309
- // Floor surface (walnut)
310
- var mezzTop = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W - 2, MEZZ_DEPTH), walnutMat);
311
- mezzTop.rotation.x = -Math.PI / 2;
312
- mezzTop.position.set(0, MEZZ_H + 0.11, -CAMPUS_D / 2 + MEZZ_DEPTH / 2);
313
- mezzTop.receiveShadow = true;
314
- S.furnitureGroup.add(mezzTop);
315
-
316
- // Glass railing along front edge
317
- var railGlass = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W - 6, 1.1), glassMat);
318
- railGlass.position.set(0, MEZZ_H + 0.65, -CAMPUS_D / 2 + MEZZ_DEPTH);
319
- S.furnitureGroup.add(railGlass);
320
- // Chrome rail bar on top
321
- var railBar = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, CAMPUS_W - 6, 8), chromeMat);
322
- railBar.rotation.z = Math.PI / 2;
323
- railBar.position.set(0, MEZZ_H + 1.2, -CAMPUS_D / 2 + MEZZ_DEPTH);
324
- S.furnitureGroup.add(railBar);
325
-
326
- // Support columns
327
- [-18, -9, 0, 9, 18].forEach(function(cx) {
328
- var col = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.15, MEZZ_H, 12), chromeMat);
329
- col.position.set(cx, MEZZ_H / 2, -CAMPUS_D / 2 + MEZZ_DEPTH);
330
- col.castShadow = true;
346
+ // ============================================================
347
+ // STRUCTURAL COLUMNS — 2 rows at X=±20, every ~10 units on Z
348
+ // Marble base shaft + gold TorusGeometry capital ring
349
+ // ============================================================
350
+ function buildStructuralColumns(matMarble, matGold, matChrome) {
351
+ var colZPositions = [-25, -15, -5, 5, 15, 25];
352
+ var colXPositions = [-20, 20];
353
+
354
+ colXPositions.forEach(function(cx) {
355
+ colZPositions.forEach(function(cz) {
356
+ // Base plinth
357
+ var plinth = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.18, 0.70), matMarble);
358
+ plinth.position.set(cx, 0.09, cz);
359
+ S.furnitureGroup.add(plinth);
360
+ // Shaft (octagonal — approximated with CylinderGeometry, 8 sides)
361
+ var shaft = new THREE.Mesh(new THREE.CylinderGeometry(0.22, 0.24, WALL_H - 0.28, 8), matMarble);
362
+ shaft.position.set(cx, WALL_H / 2 + 0.09, cz);
363
+ S.furnitureGroup.add(shaft);
364
+ // Gold capital ring (torus)
365
+ var capital = new THREE.Mesh(new THREE.TorusGeometry(0.26, 0.055, 8, 24), matGold);
366
+ capital.rotation.x = Math.PI / 2;
367
+ capital.position.set(cx, WALL_H - 0.22, cz);
368
+ S.furnitureGroup.add(capital);
369
+ // Capital top plate
370
+ var capTop = new THREE.Mesh(new THREE.BoxGeometry(0.60, 0.12, 0.60), matMarble);
371
+ capTop.position.set(cx, WALL_H - 0.06, cz);
372
+ S.furnitureGroup.add(capTop);
373
+ });
374
+ });
375
+ }
376
+
377
+ // ============================================================
378
+ // MEZZANINE — 86W × 12D platform at y=MEZZ_H, glass railing
379
+ // ============================================================
380
+ function buildMezzanine(matConcrete, matChrome, matGlass, matWalnut, matFabric) {
381
+ var mw = CAMPUS_W - 4; // 86
382
+ var md = MEZZ_DEPTH; // 12
383
+ var mz = -CAMPUS_D / 2 + md / 2;
384
+
385
+ // Platform slab
386
+ var slab = new THREE.Mesh(new THREE.BoxGeometry(mw, 0.24, md), matConcrete);
387
+ slab.position.set(0, MEZZ_H - 0.12, mz);
388
+ slab.receiveShadow = true;
389
+ S.furnitureGroup.add(slab);
390
+
391
+ // Walnut surface
392
+ var surface = new THREE.Mesh(new THREE.PlaneGeometry(mw, md), matWalnut);
393
+ surface.rotation.x = -Math.PI / 2;
394
+ surface.position.set(0, MEZZ_H + 0.01, mz);
395
+ surface.receiveShadow = true;
396
+ S.furnitureGroup.add(surface);
397
+
398
+ // Support columns (every 14.3 units along X)
399
+ var supportXs = [-36, -25, -14, -3, 3, 14, 25, 36];
400
+ supportXs.forEach(function(sx) {
401
+ var col = new THREE.Mesh(new THREE.CylinderGeometry(0.14, 0.14, MEZZ_H, 8), matChrome);
402
+ col.position.set(sx, MEZZ_H / 2, -CAMPUS_D / 2 + md);
331
403
  S.furnitureGroup.add(col);
332
404
  });
333
405
 
334
- // Meeting pods on mezzanine (2 round tables with chairs)
335
- [-10, 10].forEach(function(mx) {
336
- // Round table
337
- var table = new THREE.Mesh(new THREE.CylinderGeometry(1, 1, 0.06, 24), walnutMat);
338
- table.position.set(mx, MEZZ_H + 0.85, -CAMPUS_D / 2 + 5);
339
- table.castShadow = true;
406
+ // Glass railing front edge
407
+ var railGlass = new THREE.Mesh(new THREE.PlaneGeometry(mw - 4, 1.1), matGlass);
408
+ railGlass.position.set(0, MEZZ_H + 0.66, -CAMPUS_D / 2 + md + 0.02);
409
+ S.furnitureGroup.add(railGlass);
410
+
411
+ // Chrome top handrail bar
412
+ var topBar = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, mw - 4, 8), matChrome);
413
+ topBar.rotation.z = Math.PI / 2;
414
+ topBar.position.set(0, MEZZ_H + 1.22, -CAMPUS_D / 2 + md);
415
+ S.furnitureGroup.add(topBar);
416
+
417
+ // Railing vertical posts (every ~7 units)
418
+ for (var rx = -(mw / 2 - 3); rx <= mw / 2 - 3; rx += 7) {
419
+ var post = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 1.22, 6), matChrome);
420
+ post.position.set(rx, MEZZ_H + 0.61, -CAMPUS_D / 2 + md);
421
+ S.furnitureGroup.add(post);
422
+ }
423
+
424
+ // Meeting pods (2 round tables with chairs)
425
+ [-22, 22].forEach(function(mx2) {
426
+ var table = new THREE.Mesh(new THREE.CylinderGeometry(0.9, 0.9, 0.06, 24), matWalnut);
427
+ table.position.set(mx2, MEZZ_H + 0.78, -CAMPUS_D / 2 + 5);
340
428
  S.furnitureGroup.add(table);
341
- var tableLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.15, 0.7, 8), chromeMat);
342
- tableLeg.position.set(mx, MEZZ_H + 0.46, -CAMPUS_D / 2 + 5);
429
+ var tableLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.07, 0.14, 0.72, 8), matChrome);
430
+ tableLeg.position.set(mx2, MEZZ_H + 0.40, -CAMPUS_D / 2 + 5);
343
431
  S.furnitureGroup.add(tableLeg);
344
- // 4 chairs around
432
+ // 4 chairs
345
433
  for (var ci = 0; ci < 4; ci++) {
346
434
  var ca = (ci / 4) * Math.PI * 2;
347
- var cx2 = mx + Math.cos(ca) * 1.5;
348
- var cz2 = -CAMPUS_D / 2 + 5 + Math.sin(ca) * 1.5;
349
- buildModernChair(cx2, MEZZ_H + 0.11, cz2, ca + Math.PI, chromeMat);
435
+ buildModernChair(mx2 + Math.cos(ca) * 1.4, MEZZ_H + 0.01, -CAMPUS_D / 2 + 5 + Math.sin(ca) * 1.4, ca + Math.PI, matChrome);
350
436
  }
351
437
  });
352
438
 
353
- // Lounge sofa on mezzanine
354
- buildSofa(0, MEZZ_H + 0.11, -CAMPUS_D / 2 + 3);
439
+ // Lounge area
440
+ buildSofa(0, MEZZ_H + 0.01, -CAMPUS_D / 2 + 3);
355
441
 
356
- // "UPPER DECK" sign
357
- var signDiv = document.createElement('div');
358
- signDiv.textContent = 'UPPER DECK';
359
- signDiv.style.cssText = 'color:#d4af37;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;';
360
- var sign = new CSS2DObject(signDiv);
361
- sign.position.set(0, MEZZ_H + 2, -CAMPUS_D / 2 + MEZZ_DEPTH);
362
- S.furnitureGroup.add(sign);
442
+ // Upper Deck label
443
+ var udDiv = document.createElement('div');
444
+ udDiv.textContent = 'UPPER DECK';
445
+ udDiv.style.cssText = 'color:#d4af37;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;';
446
+ var udLabel = new CSS2DObject(udDiv);
447
+ udLabel.position.set(0, MEZZ_H + 1.8, -CAMPUS_D / 2 + md);
448
+ S.furnitureGroup.add(udLabel);
363
449
  }
364
450
 
365
- // ==================== STAIRCASE ====================
366
- function buildStaircase(marbleMat, chromeMat, glassMat) {
367
- var stairX = 20;
368
- var stairZ = -CAMPUS_D / 2 + MEZZ_DEPTH + 2;
369
- var steps = 12;
370
- var stepW = 2.5;
371
- var stepH = MEZZ_H / steps;
372
- var stepD = 0.5;
373
-
451
+ // ============================================================
452
+ // STAIRCASE switchback at X=35
453
+ // Lower 8 steps going -Z, landing at half height, upper 8 steps -Z
454
+ // ============================================================
455
+ function buildStaircase(matMarble, matChrome, matGlass) {
456
+ var stairX = 35;
457
+ var stairW = 3.0;
458
+ var steps = 8;
459
+ var stepH = MEZZ_H / (steps * 2);
460
+ var stepD = 0.55;
461
+ var startZ = -14; // ground level bottom of stairs, going -Z toward mezzanine at Z=-18
462
+
463
+ // ── Lower flight (going in -Z direction) ────────────────────────
374
464
  for (var i = 0; i < steps; i++) {
375
- var step = new THREE.Mesh(new THREE.BoxGeometry(stepW, stepH, stepD), marbleMat);
376
- step.position.set(stairX, stepH / 2 + i * stepH, stairZ - i * stepD);
377
- step.castShadow = true; step.receiveShadow = true;
465
+ var step = new THREE.Mesh(new THREE.BoxGeometry(stairW, stepH, stepD), matMarble);
466
+ step.position.set(stairX, stepH / 2 + i * stepH, startZ - i * stepD);
467
+ step.receiveShadow = true;
378
468
  S.furnitureGroup.add(step);
379
469
  }
380
470
 
381
- // Glass side panels
382
- var panelH = MEZZ_H + 1;
383
- var panelD = steps * stepD;
384
- var sidePanel = new THREE.Mesh(new THREE.PlaneGeometry(panelD, panelH), glassMat);
385
- sidePanel.position.set(stairX + stepW / 2 + 0.05, panelH / 2, stairZ - panelD / 2);
386
- sidePanel.rotation.y = Math.PI / 2;
387
- S.furnitureGroup.add(sidePanel);
388
-
389
- // Chrome handrail
390
- var railLen = Math.sqrt(panelD * panelD + MEZZ_H * MEZZ_H);
391
- var rail = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, railLen, 6), chromeMat);
392
- rail.position.set(stairX + stepW / 2 + 0.08, MEZZ_H / 2 + 0.5, stairZ - panelD / 2);
393
- rail.rotation.x = Math.atan2(MEZZ_H, panelD);
394
- S.furnitureGroup.add(rail);
471
+ // ── Landing platform at mid-height ──────────────────────────────
472
+ var landingY = steps * stepH;
473
+ var landingZ = startZ - steps * stepD;
474
+ var landing = new THREE.Mesh(new THREE.BoxGeometry(stairW, 0.10, stairW), matMarble);
475
+ landing.position.set(stairX, landingY + 0.05, landingZ - stairW / 2);
476
+ landing.receiveShadow = true;
477
+ S.furnitureGroup.add(landing);
478
+
479
+ // ── Upper flight (continuing -Z from landing) ────────────────────
480
+ var upperStartZ = landingZ - stairW;
481
+ for (var j = 0; j < steps; j++) {
482
+ var stepU = new THREE.Mesh(new THREE.BoxGeometry(stairW, stepH, stepD), matMarble);
483
+ stepU.position.set(stairX, landingY + stepH / 2 + j * stepH, upperStartZ - j * stepD);
484
+ stepU.receiveShadow = true;
485
+ S.furnitureGroup.add(stepU);
486
+ }
487
+
488
+ // ── Glass side panels ────────────────────────────────────────────
489
+ var totalFlightD = steps * stepD;
490
+ var panelH = MEZZ_H + 0.8;
491
+
492
+ // Lower flight panel (right side)
493
+ var lpLower = new THREE.Mesh(new THREE.PlaneGeometry(totalFlightD + 0.2, panelH), matGlass);
494
+ lpLower.position.set(stairX + stairW / 2 + 0.04, panelH / 2, startZ - totalFlightD / 2);
495
+ lpLower.rotation.y = Math.PI / 2;
496
+ S.furnitureGroup.add(lpLower);
497
+
498
+ // Upper flight panel (right side)
499
+ var lpUpper = new THREE.Mesh(new THREE.PlaneGeometry(totalFlightD + 0.2, panelH), matGlass);
500
+ lpUpper.position.set(stairX + stairW / 2 + 0.04, panelH / 2, upperStartZ - totalFlightD / 2);
501
+ lpUpper.rotation.y = Math.PI / 2;
502
+ S.furnitureGroup.add(lpUpper);
503
+
504
+ // ── Chrome handrails (diagonal) ──────────────────────────────────
505
+ function addHandrail(sx, sy, sz, len, slope) {
506
+ var railLen = Math.sqrt(len * len + (slope * len) * (slope * len));
507
+ var rail = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, railLen, 6), matChrome);
508
+ rail.position.set(sx, sy, sz);
509
+ rail.rotation.x = Math.atan2(slope * len, len);
510
+ S.furnitureGroup.add(rail);
511
+ }
512
+ addHandrail(stairX + stairW / 2 + 0.08, panelH * 0.6, startZ - totalFlightD / 2, totalFlightD, -stepH / stepD);
513
+ addHandrail(stairX + stairW / 2 + 0.08, panelH * 0.6, upperStartZ - totalFlightD / 2, totalFlightD, -stepH / stepD);
395
514
  }
396
515
 
397
- // ==================== GRAND LOBBY ====================
398
- function buildLobby(marbleMat, chromeMat, goldMat, walnutMat) {
399
- var lz = CAMPUS_D / 2 - 3;
516
+ // ============================================================
517
+ // GRAND LOBBY — Z = 22 to 30 (inside front zone)
518
+ // ============================================================
519
+ function buildLobby(matMarble, matChrome, matGold, matWalnut, matNeonBlue) {
520
+ var lz = CAMPUS_D / 2 - 5; // approx Z=25 from center
400
521
  var group = new THREE.Group();
401
522
 
402
- // --- Modern reception desk (sleek angular shape) ---
403
- var deskBodyMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.3, metalness: 0.15 });
404
- // Front panel (angled, facing visitors)
405
- var frontPanel = new THREE.Mesh(new THREE.BoxGeometry(4, 1.15, 0.12), deskBodyMat);
406
- frontPanel.position.set(0, 0.58, lz + 0.5);
407
- frontPanel.castShadow = true;
408
- group.add(frontPanel);
409
- // Side panels
410
- [-2, 2].forEach(function(sx) {
411
- var side = new THREE.Mesh(new THREE.BoxGeometry(0.12, 1.15, 1.2), deskBodyMat);
412
- side.position.set(sx, 0.58, lz - 0.05);
413
- side.castShadow = true;
414
- group.add(side);
523
+ // ── Entrance archway ─────────────────────────────────────────────
524
+ // Two thick columns flanking entrance (outside main walls, decorative)
525
+ [-6, 6].forEach(function(ax) {
526
+ var archCol = new THREE.Mesh(new THREE.BoxGeometry(1.0, 5.0, 0.8), matMarble);
527
+ archCol.position.set(ax, 2.5, lz + 4.0);
528
+ group.add(archCol);
529
+ // Gold capital
530
+ var cap = new THREE.Mesh(new THREE.TorusGeometry(0.58, 0.07, 8, 20), matGold);
531
+ cap.rotation.x = Math.PI / 2;
532
+ cap.position.set(ax, 5.1, lz + 4.0);
533
+ group.add(cap);
534
+ });
535
+ // Header beam across arch
536
+ var headerBeam = new THREE.Mesh(new THREE.BoxGeometry(13.2, 0.35, 0.80), matMarble);
537
+ headerBeam.position.set(0, 5.18, lz + 4.0);
538
+ group.add(headerBeam);
539
+ // Gold trim strip on beam underside
540
+ var beamTrim = new THREE.Mesh(new THREE.BoxGeometry(13.0, 0.06, 0.75), matGold);
541
+ beamTrim.position.set(0, 5.00, lz + 4.0);
542
+ group.add(beamTrim);
543
+
544
+ // ── Lower lobby ceiling (4.5 units) ─────────────────────────────
545
+ var lobbyCeilMat = new THREE.MeshStandardMaterial({ color: 0x1e2128, roughness: 0.80 });
546
+ var lobbyCeil = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, 10), lobbyCeilMat);
547
+ lobbyCeil.rotation.x = Math.PI / 2;
548
+ lobbyCeil.position.set(0, 4.5, CAMPUS_D / 2 - 5);
549
+ group.add(lobbyCeil);
550
+
551
+ // ── Column arcade — 4 pairs of decorative columns ────────────────
552
+ [-18, -9, 9, 18].forEach(function(ax) {
553
+ [-2, 2].forEach(function(az) {
554
+ var dcol = new THREE.Mesh(new THREE.CylinderGeometry(0.20, 0.22, 4.5, 12), matMarble);
555
+ dcol.position.set(ax, 2.25, lz + az);
556
+ group.add(dcol);
557
+ var dring = new THREE.Mesh(new THREE.TorusGeometry(0.24, 0.04, 6, 18), matGold);
558
+ dring.rotation.x = Math.PI / 2;
559
+ dring.position.set(ax, 4.44, lz + az);
560
+ group.add(dring);
561
+ });
415
562
  });
416
- // Marble countertop
417
- var counterTop = new THREE.Mesh(new THREE.BoxGeometry(4.2, 0.06, 1.4), marbleMat);
418
- counterTop.position.set(0, 1.17, lz - 0.05);
419
- counterTop.castShadow = true;
420
- group.add(counterTop);
421
- // Gold accent strip on front
422
- var accentStrip = new THREE.Mesh(new THREE.BoxGeometry(3.9, 0.04, 0.005), goldMat);
423
- accentStrip.position.set(0, 1.0, lz + 0.57);
424
- group.add(accentStrip);
425
- // LED underglow (blue)
426
- var ledMat = new THREE.MeshStandardMaterial({ color: 0x58a6ff, emissive: 0x58a6ff, emissiveIntensity: 0.6, roughness: 0.2 });
427
- var ledStrip = new THREE.Mesh(new THREE.BoxGeometry(3.8, 0.02, 0.02), ledMat);
428
- ledStrip.position.set(0, 0.03, lz + 0.55);
429
- group.add(ledStrip);
430
563
 
431
- // Reception monitor (thin, on desk)
432
- var monMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
433
- var mon = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.35, 0.02), monMat);
434
- mon.position.set(-0.8, 1.45, lz - 0.1);
564
+ // ── Reception desk — symmetric L-shaped (marble top, walnut body)
565
+ // Left wing
566
+ var deskBodyL = new THREE.Mesh(new THREE.BoxGeometry(5.5, 1.10, 1.0), matWalnut);
567
+ deskBodyL.position.set(-4, 0.55, lz + 0.5);
568
+ group.add(deskBodyL);
569
+ // Right wing
570
+ var deskBodyR = new THREE.Mesh(new THREE.BoxGeometry(5.5, 1.10, 1.0), matWalnut);
571
+ deskBodyR.position.set(4, 0.55, lz + 0.5);
572
+ group.add(deskBodyR);
573
+ // Center connector (back panel)
574
+ var deskCenter = new THREE.Mesh(new THREE.BoxGeometry(3.2, 1.10, 0.90), matWalnut);
575
+ deskCenter.position.set(0, 0.55, lz - 0.65);
576
+ group.add(deskCenter);
577
+ // Marble countertop spanning whole desk
578
+ var counterL = new THREE.Mesh(new THREE.BoxGeometry(5.7, 0.06, 1.22), matMarble);
579
+ counterL.position.set(-4, 1.16, lz + 0.5);
580
+ group.add(counterL);
581
+ var counterR = new THREE.Mesh(new THREE.BoxGeometry(5.7, 0.06, 1.22), matMarble);
582
+ counterR.position.set(4, 1.16, lz + 0.5);
583
+ group.add(counterR);
584
+ var counterC = new THREE.Mesh(new THREE.BoxGeometry(3.4, 0.06, 1.12), matMarble);
585
+ counterC.position.set(0, 1.16, lz - 0.65);
586
+ group.add(counterC);
587
+ // Blue LED strip under countertop
588
+ var ledStrip = new THREE.Mesh(new THREE.BoxGeometry(13.8, 0.02, 0.02), matNeonBlue);
589
+ ledStrip.position.set(0, 1.04, lz + 1.07);
590
+ group.add(ledStrip);
591
+ // Gold accent strip on front panel
592
+ var goldAccent = new THREE.Mesh(new THREE.BoxGeometry(13.8, 0.04, 0.005), matGold);
593
+ goldAccent.position.set(0, 0.92, lz + 1.0);
594
+ group.add(goldAccent);
595
+
596
+ // Reception monitor + keyboard
597
+ var monBez = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
598
+ var mon = new THREE.Mesh(new THREE.BoxGeometry(0.55, 0.38, 0.025), monBez);
599
+ mon.position.set(-2.5, 1.47, lz + 0.0);
435
600
  group.add(mon);
436
- var monScreen = new THREE.Mesh(new THREE.PlaneGeometry(0.45, 0.3),
437
- new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: 0x58a6ff, emissiveIntensity: 0.25, roughness: 0.1 }));
438
- monScreen.position.set(-0.8, 1.45, lz - 0.088);
439
- group.add(monScreen);
440
- var monStand = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.2, 6), chromeMat);
441
- monStand.position.set(-0.8, 1.28, lz - 0.1);
601
+ var monScr = new THREE.Mesh(new THREE.PlaneGeometry(0.50, 0.34),
602
+ new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.28, roughness: 0.1 }));
603
+ monScr.position.set(-2.5, 1.47, lz - 0.01);
604
+ group.add(monScr);
605
+ var monStand = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.22, 6), matChrome);
606
+ monStand.position.set(-2.5, 1.30, lz + 0.0);
442
607
  group.add(monStand);
443
-
444
- // Keyboard on desk
445
- var kbMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.5 });
446
- var kb = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.01, 0.1), kbMat);
447
- kb.position.set(-0.8, 1.2, lz - 0.4);
608
+ var kb = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.012, 0.11),
609
+ new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 }));
610
+ kb.position.set(-2.5, 1.185, lz + 0.45);
448
611
  group.add(kb);
449
612
 
450
- // --- Feature wall with big TV monitor (behind reception) ---
451
- var logoWallMat = new THREE.MeshStandardMaterial({ color: 0x15181f, roughness: 0.7 });
452
- var logoWall = new THREE.Mesh(new THREE.BoxGeometry(6, 4, 0.15), logoWallMat);
453
- logoWall.position.set(0, 2.5, lz + 1.5);
454
- logoWall.castShadow = true;
455
- group.add(logoWall);
456
-
457
- // "LET THEM TALK" logo text above the TV
458
- var logoDiv = document.createElement('div');
459
- logoDiv.textContent = 'LET THEM TALK';
460
- logoDiv.style.cssText = 'color:#ffffff;font-size:14px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:6px;text-shadow:0 0 20px rgba(88,166,255,0.6),0 0 40px rgba(88,166,255,0.3);';
461
- var logoLabel = new CSS2DObject(logoDiv);
462
- logoLabel.position.set(0, 4.3, lz + 1.6);
463
- group.add(logoLabel);
464
-
465
- // Big TV screen (dynamic canvas dashboard) — facing INTO the room (-z)
466
- var tvFrame = new THREE.Mesh(new THREE.BoxGeometry(5, 2.8, 0.06),
467
- new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 }));
468
- tvFrame.position.set(0, 2.2, lz + 1.4);
469
- tvFrame.castShadow = true;
613
+ // ── Feature wall (10W, walnut paneled, Z behind desk) ────────────
614
+ var fwMat = new THREE.MeshStandardMaterial({ color: 0x2a1e10, roughness: 0.65 });
615
+ var featureWall = new THREE.Mesh(new THREE.BoxGeometry(10, 4, 0.18), fwMat);
616
+ featureWall.position.set(0, 2.2, lz + 2.0);
617
+ group.add(featureWall);
618
+ // Walnut vertical panel strips
619
+ var stripMat = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.58 });
620
+ for (var pi = -4; pi <= 4; pi++) {
621
+ var strip = new THREE.Mesh(new THREE.BoxGeometry(0.06, 4.0, 0.02), stripMat);
622
+ strip.position.set(pi * 1.1, 2.2, lz + 2.10);
623
+ group.add(strip);
624
+ }
625
+ // Gold divider line at top of feature wall
626
+ var fwGold = new THREE.Mesh(new THREE.BoxGeometry(10.2, 0.07, 0.02), matGold);
627
+ fwGold.position.set(0, 4.23, lz + 2.10);
628
+ group.add(fwGold);
629
+
630
+ // ── TV canvas (PRESERVED: S._tvScreen contract) ───────────────────
631
+ var tvW = 960, tvH = 560;
632
+ var tvFrame = new THREE.Mesh(new THREE.BoxGeometry(5.2, 2.9, 0.07),
633
+ new THREE.MeshStandardMaterial({ color: 0x080808, roughness: 0.2 }));
634
+ tvFrame.position.set(0, 2.1, lz + 1.9);
470
635
  group.add(tvFrame);
471
- // Animated canvas
472
- var tvW = 960, tvH = 600;
473
636
  var tvCvs = document.createElement('canvas');
474
637
  tvCvs.width = tvW; tvCvs.height = tvH;
475
638
  var tvTex = new THREE.CanvasTexture(tvCvs);
476
639
  tvTex.minFilter = THREE.LinearFilter;
477
640
  var tvScreenMat = new THREE.MeshStandardMaterial({
478
- map: tvTex, emissive: 0x58a6ff, emissiveIntensity: 0.2, roughness: 0.1
641
+ map: tvTex, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.18, roughness: 0.08
479
642
  });
480
- var tvScreen = new THREE.Mesh(new THREE.PlaneGeometry(4.6, 2.5), tvScreenMat);
481
- tvScreen.position.set(0, 2.2, lz + 1.36);
643
+ var tvScreen = new THREE.Mesh(new THREE.PlaneGeometry(4.9, 2.65), tvScreenMat);
644
+ tvScreen.position.set(0, 2.1, lz + 1.86);
482
645
  tvScreen.rotation.y = Math.PI;
483
646
  group.add(tvScreen);
484
647
  S._tvScreen = { canvas: tvCvs, texture: tvTex, tickerOffset: 0 };
648
+ // TV accent light
649
+ var tvLight = new THREE.PointLight(0x58a6ff, 0.45, 7);
650
+ tvLight.castShadow = false;
651
+ tvLight.position.set(0, 4.0, lz + 1.5);
652
+ group.add(tvLight);
485
653
 
486
- // Accent light on the wall
487
- var logoSpot = new THREE.PointLight(0x58a6ff, 0.5, 6);
488
- logoSpot.position.set(0, 4.2, lz + 1);
489
- group.add(logoSpot);
490
-
491
- // --- Water feature (low rectangular pool) ---
492
- var poolFrame = new THREE.Mesh(new THREE.BoxGeometry(3, 0.2, 1.5),
493
- new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.4 }));
494
- poolFrame.position.set(0, 0.1, lz - 4);
495
- poolFrame.castShadow = true;
496
- group.add(poolFrame);
497
- var waterMat = new THREE.MeshStandardMaterial({ color: 0x2a6090, roughness: 0.05, metalness: 0.3, transparent: true, opacity: 0.7 });
498
- var water = new THREE.Mesh(new THREE.PlaneGeometry(2.7, 1.2), waterMat);
654
+ // ── LET THEM TALK logo above feature wall ────────────────────────
655
+ var logoDiv = document.createElement('div');
656
+ logoDiv.textContent = 'LET THEM TALK';
657
+ logoDiv.style.cssText = 'color:#ffffff;font-size:14px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:6px;text-shadow:0 0 20px rgba(88,166,255,0.6),0 0 40px rgba(88,166,255,0.3);';
658
+ var logoLabel = new CSS2DObject(logoDiv);
659
+ logoLabel.position.set(0, 4.6, lz + 2.1);
660
+ group.add(logoLabel);
661
+
662
+ // ── Water feature (5×2 pool, center lobby) ───────────────────────
663
+ var poolRimMat = new THREE.MeshStandardMaterial({ color: 0x22252e, roughness: 0.42 });
664
+ var poolRim = new THREE.Mesh(new THREE.BoxGeometry(5.2, 0.22, 2.2), poolRimMat);
665
+ poolRim.position.set(0, 0.11, lz - 5.0);
666
+ group.add(poolRim);
667
+ var waterMat = new THREE.MeshStandardMaterial({
668
+ color: 0x1e5a8a, roughness: 0.04, metalness: 0.28, transparent: true, opacity: 0.75
669
+ });
670
+ var water = new THREE.Mesh(new THREE.PlaneGeometry(4.8, 1.8), waterMat);
499
671
  water.rotation.x = -Math.PI / 2;
500
- water.position.set(0, 0.22, lz - 4);
672
+ water.position.set(0, 0.23, lz - 5.0);
501
673
  group.add(water);
502
- // Decorative stones in water
674
+ // Decorative smooth stones
503
675
  var stoneMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.7 });
504
- [[-0.8, -0.3], [0.5, 0.2], [-0.2, 0.1], [0.9, -0.2], [-0.5, -0.1]].forEach(function(sp) {
505
- var stone = new THREE.Mesh(new THREE.SphereGeometry(0.06 + Math.random() * 0.04, 6, 5), stoneMat);
506
- stone.position.set(sp[0], 0.2, lz - 4 + sp[1]);
676
+ [[-1.2, -0.4], [0.6, 0.3], [-0.3, 0.2], [1.3, -0.3], [-0.8, -0.1], [0.0, 0.5]].forEach(function(sp) {
677
+ var stone = new THREE.Mesh(new THREE.SphereGeometry(0.055 + Math.random() * 0.04, 6, 4), stoneMat);
678
+ stone.position.set(sp[0], 0.21, lz - 5.0 + sp[1]);
507
679
  stone.scale.y = 0.5;
508
680
  group.add(stone);
509
681
  });
510
682
 
511
- // --- Waiting area (2 modern benches) ---
512
- var benchMat = new THREE.MeshStandardMaterial({ color: 0x2a2a3a, roughness: 0.6 });
513
- [-5, 5].forEach(function(bx) {
514
- var benchSeat = new THREE.Mesh(new THREE.BoxGeometry(2.5, 0.08, 0.6), benchMat);
515
- benchSeat.position.set(bx, 0.45, lz - 2);
516
- benchSeat.castShadow = true;
517
- group.add(benchSeat);
518
- // Chrome legs
519
- [-1, 1].forEach(function(lx) {
520
- var benchLeg = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.42, 0.5), chromeMat);
521
- benchLeg.position.set(bx + lx, 0.22, lz - 2);
522
- group.add(benchLeg);
523
- });
524
- });
525
-
526
- // --- Pendant lights above reception ---
527
- [-1.2, 0, 1.2].forEach(function(px) {
528
- var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.005, 0.005, 2.5, 4),
529
- new THREE.MeshStandardMaterial({ color: 0x333333 }));
530
- wire.position.set(px, WALL_H - 1.25, lz);
683
+ // ── Pendant lights above reception (3 clusters) ──────────────────
684
+ [-5, 0, 5].forEach(function(px) {
685
+ var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.006, 0.006, 2.2, 4),
686
+ new THREE.MeshStandardMaterial({ color: 0x2a2a2a }));
687
+ wire.position.set(px, 4.5 - 1.1, lz + 0.3);
531
688
  group.add(wire);
532
- var shade = new THREE.Mesh(new THREE.SphereGeometry(0.12, 12, 10),
533
- new THREE.MeshStandardMaterial({ color: 0xffeedd, emissive: 0xffeedd, emissiveIntensity: 0.4, transparent: true, opacity: 0.8 }));
534
- shade.position.set(px, WALL_H - 2.6, lz);
689
+ var shade = new THREE.Mesh(new THREE.SphereGeometry(0.13, 14, 10),
690
+ new THREE.MeshStandardMaterial({ color: 0xffeedd, emissive: new THREE.Color(0xffeedd), emissiveIntensity: 0.40, transparent: true, opacity: 0.80 }));
691
+ shade.position.set(px, 4.5 - 2.55, lz + 0.3);
535
692
  group.add(shade);
693
+ // Gold ring
694
+ var ring = new THREE.Mesh(new THREE.TorusGeometry(0.135, 0.012, 6, 18), matGold);
695
+ ring.rotation.x = Math.PI / 2;
696
+ ring.position.set(px, 4.5 - 2.68, lz + 0.3);
697
+ group.add(ring);
536
698
  });
537
- // Warm light for reception area
538
- var receptionLight = new THREE.PointLight(0xffeedd, 0.4, 8);
539
- receptionLight.position.set(0, 4, lz);
540
- group.add(receptionLight);
699
+ var recLight = new THREE.PointLight(0xffeedd, 0.38, 10);
700
+ recLight.castShadow = false;
701
+ recLight.position.set(0, 3.8, lz + 0.3);
702
+ group.add(recLight);
541
703
 
542
- // RECEPTION sign (gold, above logo wall)
704
+ // ── RECEPTION gold sign ──────────────────────────────────────────
543
705
  var signDiv = document.createElement('div');
544
706
  signDiv.textContent = 'RECEPTION';
545
707
  signDiv.style.cssText = 'color:#d4af37;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:3px;';
546
708
  var sign = new CSS2DObject(signDiv);
547
- sign.position.set(0, 4.5, lz);
709
+ sign.position.set(0, 4.8, lz + 0.3);
548
710
  group.add(sign);
549
711
 
550
712
  S.furnitureGroup.add(group);
551
713
  }
552
714
 
553
- // ==================== GAMING DESK ====================
715
+ // ============================================================
716
+ // MAIN CORRIDOR — Z=0, 4 units wide (-2 to +2)
717
+ // Polished dark floor + gold inlay lines
718
+ // ============================================================
719
+ function buildMainCorridor(matDark, matGold) {
720
+ // Dark polished floor strip
721
+ var corrFloor = new THREE.Mesh(
722
+ new THREE.PlaneGeometry(CAMPUS_W, 4),
723
+ new THREE.MeshStandardMaterial({ color: 0x12141a, roughness: 0.08, metalness: 0.12 })
724
+ );
725
+ corrFloor.rotation.x = -Math.PI / 2;
726
+ corrFloor.position.set(0, 0.015, 0);
727
+ S.furnitureGroup.add(corrFloor);
728
+
729
+ // Gold inlay centre line
730
+ var inlayCtr = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, 0.06), matGold);
731
+ inlayCtr.rotation.x = -Math.PI / 2;
732
+ inlayCtr.position.set(0, 0.018, 0);
733
+ S.furnitureGroup.add(inlayCtr);
734
+
735
+ // Gold inlay edge lines
736
+ [-1.8, 1.8].forEach(function(ez) {
737
+ var inlay = new THREE.Mesh(new THREE.PlaneGeometry(CAMPUS_W, 0.03), matGold);
738
+ inlay.rotation.x = -Math.PI / 2;
739
+ inlay.position.set(0, 0.018, ez);
740
+ S.furnitureGroup.add(inlay);
741
+ });
742
+ }
743
+
744
+ // ============================================================
745
+ // CROSS CORRIDORS — at X=±20, 3 units wide
746
+ // ============================================================
747
+ function buildCrossCorridor(matDark, matGold) {
748
+ [-20, 20].forEach(function(cx) {
749
+ var cFloor = new THREE.Mesh(
750
+ new THREE.PlaneGeometry(3, CAMPUS_D),
751
+ new THREE.MeshStandardMaterial({ color: 0x13151c, roughness: 0.10, metalness: 0.10 })
752
+ );
753
+ cFloor.rotation.x = -Math.PI / 2;
754
+ cFloor.position.set(cx, 0.012, 0);
755
+ S.furnitureGroup.add(cFloor);
756
+
757
+ // Gold inlay centre
758
+ var inlay = new THREE.Mesh(new THREE.PlaneGeometry(0.04, CAMPUS_D), matGold);
759
+ inlay.rotation.x = -Math.PI / 2;
760
+ inlay.position.set(cx, 0.016, 0);
761
+ S.furnitureGroup.add(inlay);
762
+ });
763
+ }
764
+
765
+ // === PHASE 3-5 FUNCTIONS FOLLOW ===
766
+
767
+ // ============================================================
768
+ // GAMING DESK
769
+ // ============================================================
554
770
  function buildGamingDesk(x, z, index) {
555
771
  var group = new THREE.Group();
556
772
  group.position.set(x, 0, z);
557
773
 
558
- // L-shaped desk (main + side wing)
559
- var deskColor = 0x1a1a2e;
560
- var deskMat = new THREE.MeshStandardMaterial({ color: deskColor, roughness: 0.3, metalness: 0.1 });
561
-
562
- // Main desktop
563
- var mainTop = new THREE.Mesh(new THREE.BoxGeometry(2, 0.05, 0.9), deskMat);
774
+ var deskMat = new THREE.MeshStandardMaterial({ color: 0x1a1c22, roughness: 0.28, metalness: 0.12 });
775
+ // Main top
776
+ var mainTop = new THREE.Mesh(new THREE.BoxGeometry(2.0, 0.05, 0.90), deskMat);
564
777
  mainTop.position.y = 0.76; mainTop.castShadow = true; mainTop.receiveShadow = true;
565
778
  group.add(mainTop);
566
-
567
- // RGB LED strip under desk edge (front)
779
+ // RGB LED strip
568
780
  var rgbColors = [0x58a6ff, 0xa855f7, 0x22c55e, 0xef4444, 0x06b6d4, 0xec4899];
569
781
  var rgbColor = rgbColors[index % rgbColors.length];
570
- var rgbMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: rgbColor, emissiveIntensity: 0.8, roughness: 0.2 });
782
+ var rgbMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: new THREE.Color(rgbColor), emissiveIntensity: 0.8, roughness: 0.2 });
571
783
  var rgbStrip = new THREE.Mesh(new THREE.BoxGeometry(1.9, 0.015, 0.015), rgbMat);
572
784
  rgbStrip.position.set(0, 0.74, 0.44);
573
785
  group.add(rgbStrip);
574
-
575
- // Carbon fiber legs (angular, gaming style)
576
- var legMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.4, metalness: 0.2 });
786
+ // Legs
787
+ var legMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.40, metalness: 0.22 });
577
788
  [[-0.85, -0.35], [-0.85, 0.35], [0.85, -0.35], [0.85, 0.35]].forEach(function(p) {
578
789
  var leg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.76, 0.06), legMat);
579
790
  leg.position.set(p[0], 0.38, p[1]);
580
- leg.castShadow = true;
581
791
  group.add(leg);
582
792
  });
583
-
584
- // Curved ultrawide monitor (wider, thinner)
585
- var monBody = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.35, 0.03), new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 }));
586
- monBody.position.set(0, 1.15, -0.25);
587
- monBody.castShadow = true;
793
+ // Monitor body
794
+ var monMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
795
+ var monBody = new THREE.Mesh(new THREE.BoxGeometry(0.72, 0.38, 0.03), monMat);
796
+ monBody.position.set(0, 1.14, -0.25);
588
797
  group.add(monBody);
589
-
590
798
  // Monitor screen
591
- var screenGeo = new THREE.PlaneGeometry(0.64, 0.3);
592
- var screenMat = new THREE.MeshStandardMaterial({
593
- color: 0x333333, emissive: 0x333333, emissiveIntensity: 0.1, roughness: 0.2
594
- });
595
- var screen = new THREE.Mesh(screenGeo, screenMat);
596
- screen.position.set(0, 1.15, -0.234);
799
+ var screenMat = new THREE.MeshStandardMaterial({ color: 0x333333, emissive: new THREE.Color(0x333333), emissiveIntensity: 0.1, roughness: 0.2 });
800
+ var screen = new THREE.Mesh(new THREE.PlaneGeometry(0.65, 0.32), screenMat);
801
+ screen.position.set(0, 1.14, -0.234);
597
802
  group.add(screen);
598
-
599
- // Monitor stand (V-shaped, chrome — positioned BEHIND the screen to avoid clipping)
803
+ // Monitor stand
600
804
  var standMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.15, metalness: 0.7 });
601
- var standArm = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.04), standMat);
602
- standArm.position.set(0, 0.92, -0.27);
805
+ var standArm = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.24, 0.04), standMat);
806
+ standArm.position.set(0, 0.91, -0.27);
603
807
  group.add(standArm);
604
- var standBase = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.02, 0.15), standMat);
808
+ var standBase = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.02, 0.15), standMat);
605
809
  standBase.position.set(0, 0.78, -0.27);
606
810
  group.add(standBase);
607
-
608
- // PC tower under desk (with RGB glow)
609
- var pcMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.3 });
811
+ // PC tower with RGB glow
812
+ var pcMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.30 });
610
813
  var pcCase = new THREE.Mesh(new THREE.BoxGeometry(0.22, 0.45, 0.45), pcMat);
611
- pcCase.position.set(0.7, 0.23, 0);
612
- pcCase.castShadow = true;
814
+ pcCase.position.set(0.72, 0.23, 0);
613
815
  group.add(pcCase);
614
- // RGB glass panel on PC
615
- var pcGlowMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: rgbColor, emissiveIntensity: 0.4, transparent: true, opacity: 0.5 });
616
- var pcGlow = new THREE.Mesh(new THREE.PlaneGeometry(0.18, 0.4), pcGlowMat);
617
- pcGlow.position.set(0.7 + 0.115, 0.23, 0);
816
+ var pcGlowMat = new THREE.MeshStandardMaterial({ color: rgbColor, emissive: new THREE.Color(rgbColor), emissiveIntensity: 0.4, transparent: true, opacity: 0.5 });
817
+ var pcGlow = new THREE.Mesh(new THREE.PlaneGeometry(0.18, 0.40), pcGlowMat);
818
+ pcGlow.position.set(0.72 + 0.115, 0.23, 0);
618
819
  pcGlow.rotation.y = Math.PI / 2;
619
820
  group.add(pcGlow);
620
-
621
- // Keyboard
821
+ // Keyboard + mousepad
622
822
  var kbMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 });
623
823
  var kb = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.02, 0.12), kbMat);
624
824
  kb.position.set(-0.1, 0.78, 0.15);
625
825
  group.add(kb);
626
-
627
- // Mouse + mousepad
628
826
  var padMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.8 });
629
- var pad = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.005, 0.2), padMat);
630
- pad.position.set(0.3, 0.765, 0.15);
827
+ var pad = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.005, 0.20), padMat);
828
+ pad.position.set(0.30, 0.765, 0.15);
631
829
  group.add(pad);
632
- var mouseMat2 = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3 });
633
- var mouse = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.02, 0.07), mouseMat2);
634
- mouse.position.set(0.3, 0.78, 0.15);
830
+ var mouse = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.02, 0.07), kbMat);
831
+ mouse.position.set(0.30, 0.78, 0.15);
635
832
  group.add(mouse);
636
-
637
- // Gaming chair (racing style)
638
- buildGamingChair(group, 0, 0.7, rgbColor);
833
+ // Chair
834
+ buildGamingChair(group, 0, 0.70, rgbColor);
639
835
 
640
836
  S.furnitureGroup.add(group);
641
837
  S.deskMeshes.push({ group: group, screen: screen, screenMat: screenMat, index: index, x: x, z: z });
642
838
  }
643
839
 
644
- // ==================== GAMING CHAIR ====================
840
+ // ============================================================
841
+ // GAMING CHAIR
842
+ // ============================================================
645
843
  function buildGamingChair(parent, cx, cz, accentColor) {
646
844
  var chairGroup = new THREE.Group();
647
845
  chairGroup.position.set(cx, 0, cz);
648
-
649
- var baseMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.4, metalness: 0.3 });
650
- var seatMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.65 });
846
+ var baseMat = new THREE.MeshStandardMaterial({ color: 0x111111, roughness: 0.4, metalness: 0.3 });
847
+ var seatMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.65 });
651
848
  var accentMat = new THREE.MeshStandardMaterial({ color: accentColor, roughness: 0.5 });
652
-
653
849
  // 5-star base
654
- var baseHub = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.04, 12), baseMat);
655
- baseHub.position.y = 0.05;
656
- chairGroup.add(baseHub);
850
+ var hub = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.04, 12), baseMat);
851
+ hub.position.y = 0.05; chairGroup.add(hub);
657
852
  for (var i = 0; i < 5; i++) {
658
853
  var a = (i / 5) * Math.PI * 2;
659
- var arm = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.02, 0.03), baseMat);
854
+ var arm = new THREE.Mesh(new THREE.BoxGeometry(0.30, 0.02, 0.03), baseMat);
660
855
  arm.position.set(Math.cos(a) * 0.15, 0.04, Math.sin(a) * 0.15);
661
- arm.rotation.y = -a;
662
- chairGroup.add(arm);
663
- // Wheel
856
+ arm.rotation.y = -a; chairGroup.add(arm);
664
857
  var wheel = new THREE.Mesh(new THREE.SphereGeometry(0.025, 6, 4), baseMat);
665
858
  wheel.position.set(Math.cos(a) * 0.28, 0.025, Math.sin(a) * 0.28);
666
859
  chairGroup.add(wheel);
667
860
  }
668
-
669
- // Gas cylinder
670
861
  var cyl = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.35, 8), baseMat);
671
- cyl.position.y = 0.25;
672
- chairGroup.add(cyl);
673
-
674
- // Seat
675
- var seat = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.08, 0.42), seatMat);
676
- seat.position.y = 0.46;
677
- seat.castShadow = true;
678
- chairGroup.add(seat);
679
-
680
- // Backrest (tall, racing-style with wings)
862
+ cyl.position.y = 0.25; chairGroup.add(cyl);
863
+ var seat = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.08, 0.42), seatMat);
864
+ seat.position.y = 0.46; chairGroup.add(seat);
681
865
  var back = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.55, 0.06), seatMat);
682
- back.position.set(0, 0.78, 0.2);
683
- back.castShadow = true;
684
- chairGroup.add(back);
685
-
686
- // Headrest
687
- var headrest = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.1, 0.06), seatMat);
688
- headrest.position.set(0, 1.1, 0.2);
689
- chairGroup.add(headrest);
690
-
691
- // Accent stripes on backrest
692
- var stripe1 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.5, 0.005), accentMat);
693
- stripe1.position.set(-0.12, 0.78, 0.17);
694
- chairGroup.add(stripe1);
695
- var stripe2 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.5, 0.005), accentMat);
696
- stripe2.position.set(0.12, 0.78, 0.17);
697
- chairGroup.add(stripe2);
698
-
699
- // Armrests
866
+ back.position.set(0, 0.78, 0.20); chairGroup.add(back);
867
+ var headrest = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.10, 0.06), seatMat);
868
+ headrest.position.set(0, 1.10, 0.20); chairGroup.add(headrest);
869
+ var s1 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.50, 0.005), accentMat);
870
+ s1.position.set(-0.12, 0.78, 0.17); chairGroup.add(s1);
871
+ var s2 = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.50, 0.005), accentMat);
872
+ s2.position.set(0.12, 0.78, 0.17); chairGroup.add(s2);
700
873
  [-0.22, 0.22].forEach(function(ax) {
701
- var armPost = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.2, 0.03), baseMat);
702
- armPost.position.set(ax, 0.55, 0.05);
703
- chairGroup.add(armPost);
704
- var armPad = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.02, 0.2), seatMat);
705
- armPad.position.set(ax, 0.66, 0.05);
706
- chairGroup.add(armPad);
874
+ var ap = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.20, 0.03), baseMat);
875
+ ap.position.set(ax, 0.55, 0.05); chairGroup.add(ap);
876
+ var apd = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.02, 0.20), seatMat);
877
+ apd.position.set(ax, 0.66, 0.05); chairGroup.add(apd);
707
878
  });
708
-
709
879
  parent.add(chairGroup);
710
880
  }
711
881
 
712
- // ==================== MODERN CHAIR (for meeting rooms) ====================
713
- function buildModernChair(x, y, z, rotation, chromeMat) {
882
+ // ============================================================
883
+ // MODERN CHAIR (meeting rooms / mezzanine)
884
+ // ============================================================
885
+ function buildModernChair(x, y, z, rotation, matChrome) {
714
886
  var group = new THREE.Group();
715
887
  group.position.set(x, y, z);
716
888
  group.rotation.y = rotation;
717
-
718
- var seatMat = new THREE.MeshStandardMaterial({ color: 0x333340, roughness: 0.7 });
719
- var seat = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.05, 0.4), seatMat);
720
- seat.position.y = 0.45; seat.castShadow = true;
721
- group.add(seat);
722
- var back = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.4, 0.04), seatMat);
723
- back.position.set(0, 0.7, 0.18); back.castShadow = true;
724
- group.add(back);
725
- var post = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.4, 6), chromeMat);
726
- post.position.y = 0.22;
727
- group.add(post);
728
-
889
+ var seatMat = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.75 });
890
+ var seat = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.05, 0.40), seatMat);
891
+ seat.position.y = 0.45; group.add(seat);
892
+ var back = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.40, 0.04), seatMat);
893
+ back.position.set(0, 0.70, 0.18); group.add(back);
894
+ var post = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.02, 0.40, 6), matChrome);
895
+ post.position.y = 0.22; group.add(post);
729
896
  S.furnitureGroup.add(group);
730
897
  }
731
898
 
732
- // ==================== SOFA ====================
899
+ // ============================================================
900
+ // SOFA
901
+ // ============================================================
733
902
  function buildSofa(x, y, z) {
734
903
  var group = new THREE.Group();
735
904
  group.position.set(x, y, z);
736
-
737
- var sofaMat = new THREE.MeshStandardMaterial({ color: 0x2a2a3e, roughness: 0.75 });
738
- // Base
739
- var base = new THREE.Mesh(new THREE.BoxGeometry(3, 0.35, 0.9), sofaMat);
740
- base.position.y = 0.2; base.castShadow = true;
741
- group.add(base);
742
- // Backrest
743
- var backrest = new THREE.Mesh(new THREE.BoxGeometry(3, 0.5, 0.2), sofaMat);
744
- backrest.position.set(0, 0.55, -0.35); backrest.castShadow = true;
745
- group.add(backrest);
746
- // Armrests
747
- [-1.4, 1.4].forEach(function(ax) {
748
- var arm = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.3, 0.9), sofaMat);
749
- arm.position.set(ax, 0.4, 0); arm.castShadow = true;
750
- group.add(arm);
905
+ var sofaMat = new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.78 });
906
+ var cushionMat = new THREE.MeshStandardMaterial({ color: 0x1e2030, roughness: 0.82 });
907
+ var base = new THREE.Mesh(new THREE.BoxGeometry(3.2, 0.35, 0.9), sofaMat);
908
+ base.position.y = 0.20; group.add(base);
909
+ var backrest = new THREE.Mesh(new THREE.BoxGeometry(3.2, 0.50, 0.20), sofaMat);
910
+ backrest.position.set(0, 0.56, -0.36); group.add(backrest);
911
+ [-1.5, 1.5].forEach(function(ax) {
912
+ var arm = new THREE.Mesh(new THREE.BoxGeometry(0.20, 0.30, 0.90), sofaMat);
913
+ arm.position.set(ax, 0.40, 0); group.add(arm);
751
914
  });
752
- // Cushions
753
- var cushionMat = new THREE.MeshStandardMaterial({ color: 0x3a3a5e, roughness: 0.8 });
754
- [-0.8, 0, 0.8].forEach(function(cx2) {
755
- var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.1, 0.7), cushionMat);
756
- cushion.position.set(cx2, 0.42, 0.05);
757
- group.add(cushion);
915
+ [-0.9, 0, 0.9].forEach(function(cx2) {
916
+ var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.85, 0.10, 0.72), cushionMat);
917
+ cushion.position.set(cx2, 0.43, 0.05); group.add(cushion);
758
918
  });
759
-
760
919
  S.furnitureGroup.add(group);
761
920
  }
762
921
 
763
- // ==================== MANAGER'S OFFICE ====================
764
- function buildManagerOffice(x, z, glassMat, frameMat, walnutMat, leatherMat, chromeMat) {
765
- var offW = 8, offD = 7, wallH = 4;
922
+ // ============================================================
923
+ // MANAGER'S GLASS OFFICE
924
+ // ============================================================
925
+ function buildManagerOffice(x, z, matGlassCl, matGlassFr, matFrame, matWalnut, matLeather, matChrome) {
926
+ var offW = 14, offD = 10, wallH = 4.5;
766
927
  var group = new THREE.Group();
767
928
  group.position.set(x, 0, z);
768
929
 
769
- // --- Raised floor (dark walnut) ---
770
- var floorMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.5 });
771
- var floor = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, offD), floorMat);
772
- floor.position.y = 0.03; floor.receiveShadow = true;
773
- group.add(floor);
930
+ // Raised walnut floor
931
+ var floorMat = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.52 });
932
+ var offFloor = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.07, offD), floorMat);
933
+ offFloor.position.y = 0.035; group.add(offFloor);
774
934
 
775
- // --- Glass walls with frosted privacy strip ---
776
- var clearGlass = new THREE.MeshStandardMaterial({ color: 0xaaccee, transparent: true, opacity: 0.2, roughness: 0.05, metalness: 0.1, side: THREE.DoubleSide });
777
- var frostedGlass = new THREE.MeshStandardMaterial({ color: 0xd0d8e8, transparent: true, opacity: 0.5, roughness: 0.4, side: THREE.DoubleSide });
778
-
779
- // Front wall (with door gap in center)
935
+ // Glass walls (front with door, sides, back)
780
936
  var doorW = 1.2;
781
- // Left section of front wall
782
- var fwLeft = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), clearGlass);
937
+ var fwLeft = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), matGlassCl);
783
938
  fwLeft.position.set(-(offW + doorW) / 4, wallH / 2, -offD / 2);
784
939
  group.add(fwLeft);
785
- // Right section of front wall
786
- var fwRight = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), clearGlass);
940
+ var fwRight = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, wallH), matGlassCl);
787
941
  fwRight.position.set((offW + doorW) / 4, wallH / 2, -offD / 2);
788
942
  group.add(fwRight);
789
- // Frosted strip on front walls (waist-height privacy)
790
- var frostLeft = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, 0.8), frostedGlass);
791
- frostLeft.position.set(-(offW + doorW) / 4, 1.2, -offD / 2 + 0.01);
792
- group.add(frostLeft);
793
- var frostRight = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, 0.8), frostedGlass);
794
- frostRight.position.set((offW + doorW) / 4, 1.2, -offD / 2 + 0.01);
795
- group.add(frostRight);
796
-
797
- // Glass sliding door (animated)
798
- var doorGlass = new THREE.MeshStandardMaterial({ color: 0xbbddff, transparent: true, opacity: 0.3, roughness: 0.05, side: THREE.DoubleSide });
799
- var door = new THREE.Mesh(new THREE.PlaneGeometry(doorW, wallH - 0.2), doorGlass);
943
+ // Frosted privacy strips
944
+ [-(offW + doorW) / 4, (offW + doorW) / 4].forEach(function(fx) {
945
+ var frost = new THREE.Mesh(new THREE.PlaneGeometry((offW - doorW) / 2, 0.85), matGlassFr);
946
+ frost.position.set(fx, 1.25, -offD / 2 + 0.01);
947
+ group.add(frost);
948
+ });
949
+ // Sliding door
950
+ var doorGlassMat = new THREE.MeshStandardMaterial({ color: 0xbbddff, transparent: true, opacity: 0.28, roughness: 0.05, side: THREE.DoubleSide });
951
+ var door = new THREE.Mesh(new THREE.PlaneGeometry(doorW, wallH - 0.2), doorGlassMat);
800
952
  door.position.set(0, wallH / 2, -offD / 2);
801
953
  group.add(door);
802
- // Door handle (chrome bar)
803
- var handleMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.1, metalness: 0.8 });
804
- var handle = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.3, 0.04), handleMat);
954
+ var handle = new THREE.Mesh(new THREE.BoxGeometry(0.03, 0.30, 0.04), matChrome);
805
955
  handle.position.set(doorW / 2 - 0.1, 1.1, -offD / 2 + 0.03);
806
956
  group.add(handle);
807
- // Store door ref for animation
808
- S._managerDoor = door;
809
- S._managerDoorOpen = 0; // 0=closed, 1=open (lerp target)
957
+ S._managerDoor = door;
958
+ S._managerDoorOpen = 0;
810
959
  S._managerDoorLerp = 0;
811
960
  S._managerDoorClosedZ = -offD / 2;
812
-
813
- // Left glass wall
814
- var leftWall = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), clearGlass);
815
- leftWall.position.set(-offW / 2, wallH / 2, 0);
816
- leftWall.rotation.y = Math.PI / 2;
817
- group.add(leftWall);
818
- var frostLeftW = new THREE.Mesh(new THREE.PlaneGeometry(offD, 0.8), frostedGlass);
819
- frostLeftW.position.set(-offW / 2 + 0.01, 1.2, 0);
820
- frostLeftW.rotation.y = Math.PI / 2;
821
- group.add(frostLeftW);
822
-
823
- // Right glass wall
824
- var rightWall = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), clearGlass);
825
- rightWall.position.set(offW / 2, wallH / 2, 0);
826
- rightWall.rotation.y = -Math.PI / 2;
827
- group.add(rightWall);
828
- var frostRightW = new THREE.Mesh(new THREE.PlaneGeometry(offD, 0.8), frostedGlass);
829
- frostRightW.position.set(offW / 2 - 0.01, 1.2, 0);
830
- frostRightW.rotation.y = -Math.PI / 2;
831
- group.add(frostRightW);
832
-
833
- // Back glass wall
834
- var backWall = new THREE.Mesh(new THREE.PlaneGeometry(offW, wallH), clearGlass);
835
- backWall.position.set(0, wallH / 2, offD / 2);
836
- backWall.rotation.y = Math.PI;
837
- group.add(backWall);
838
- var frostBackW = new THREE.Mesh(new THREE.PlaneGeometry(offW, 0.8), frostedGlass);
839
- frostBackW.position.set(0, 1.2, offD / 2 - 0.01);
840
- frostBackW.rotation.y = Math.PI;
841
- group.add(frostBackW);
842
-
843
- // --- Chrome frame structure ---
844
- // Top beams (all 4 sides)
845
- // Front beam
846
- var beamFront = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, 0.06), frameMat);
847
- beamFront.position.set(0, wallH, -offD / 2); group.add(beamFront);
848
- // Back beam
849
- var beamBack = new THREE.Mesh(new THREE.BoxGeometry(offW, 0.06, 0.06), frameMat);
850
- beamBack.position.set(0, wallH, offD / 2); group.add(beamBack);
851
- // Left beam
852
- var beamLeft = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, offD), frameMat);
853
- beamLeft.position.set(-offW / 2, wallH, 0); group.add(beamLeft);
854
- // Right beam
855
- var beamRight = new THREE.Mesh(new THREE.BoxGeometry(0.06, 0.06, offD), frameMat);
856
- beamRight.position.set(offW / 2, wallH, 0); group.add(beamRight);
857
- // Vertical corner posts (all 4 corners)
858
- [[-offW / 2, -offD / 2], [-offW / 2, offD / 2], [offW / 2, -offD / 2], [offW / 2, offD / 2]].forEach(function(p) {
859
- var post = new THREE.Mesh(new THREE.BoxGeometry(0.06, wallH, 0.06), frameMat);
860
- post.position.set(p[0], wallH / 2, p[1]);
861
- group.add(post);
961
+ // Side walls
962
+ var leftW = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), matGlassCl);
963
+ leftW.position.set(-offW / 2, wallH / 2, 0); leftW.rotation.y = Math.PI / 2;
964
+ group.add(leftW);
965
+ var rightW = new THREE.Mesh(new THREE.PlaneGeometry(offD, wallH), matGlassCl);
966
+ rightW.position.set(offW / 2, wallH / 2, 0); rightW.rotation.y = -Math.PI / 2;
967
+ group.add(rightW);
968
+ var backW = new THREE.Mesh(new THREE.PlaneGeometry(offW, wallH), matGlassCl);
969
+ backW.position.set(0, wallH / 2, offD / 2); backW.rotation.y = Math.PI;
970
+ group.add(backW);
971
+
972
+ // Chrome frame structure
973
+ [[offW, 0.06, 0.06, 0, wallH, -offD / 2], [offW, 0.06, 0.06, 0, wallH, offD / 2],
974
+ [0.06, 0.06, offD, -offW / 2, wallH, 0], [0.06, 0.06, offD, offW / 2, wallH, 0]].forEach(function(b) {
975
+ var beam = new THREE.Mesh(new THREE.BoxGeometry(b[0], b[1], b[2]), matFrame);
976
+ beam.position.set(b[3], b[4], b[5]); group.add(beam);
862
977
  });
863
- // Door frame posts
864
- [-doorW / 2 - 0.03, doorW / 2 + 0.03].forEach(function(dx) {
865
- var doorPost = new THREE.Mesh(new THREE.BoxGeometry(0.06, wallH, 0.06), frameMat);
866
- doorPost.position.set(dx, wallH / 2, -offD / 2);
867
- group.add(doorPost);
978
+ [[-offW/2,-offD/2],[-offW/2,offD/2],[offW/2,-offD/2],[offW/2,offD/2]].forEach(function(p) {
979
+ var post = new THREE.Mesh(new THREE.BoxGeometry(0.06, wallH, 0.06), matFrame);
980
+ post.position.set(p[0], wallH / 2, p[1]); group.add(post);
868
981
  });
869
- // Door top beam
870
- var doorTopBeam = new THREE.Mesh(new THREE.BoxGeometry(doorW + 0.12, 0.06, 0.06), frameMat);
871
- doorTopBeam.position.set(0, wallH, -offD / 2);
872
- group.add(doorTopBeam);
873
982
 
874
- // --- L-shaped executive desk (walnut + marble top) ---
983
+ // Executive L-desk
875
984
  var marbleTopMat = new THREE.MeshStandardMaterial({ color: 0xf0ece4, roughness: 0.12, metalness: 0.05 });
876
- // Main section
877
- var deskMain = new THREE.Mesh(new THREE.BoxGeometry(2.8, 0.06, 1.2), walnutMat);
878
- deskMain.position.set(0, 0.78, 1.5); deskMain.castShadow = true;
879
- group.add(deskMain);
880
- var marbleTop1 = new THREE.Mesh(new THREE.BoxGeometry(2.82, 0.015, 1.22), marbleTopMat);
881
- marbleTop1.position.set(0, 0.82, 1.5);
882
- group.add(marbleTop1);
883
- // Side wing
884
- var deskWing = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.06, 0.8), walnutMat);
885
- deskWing.position.set(1.6, 0.78, 0.7); deskWing.castShadow = true;
886
- group.add(deskWing);
887
- var marbleTop2 = new THREE.Mesh(new THREE.BoxGeometry(1.22, 0.015, 0.82), marbleTopMat);
888
- marbleTop2.position.set(1.6, 0.82, 0.7);
889
- group.add(marbleTop2);
890
- // Desk legs (chrome, elegant)
891
- [[-1.2, 1], [-1.2, 2], [1.2, 2], [1.2, 1], [2.1, 0.4], [2.1, 1]].forEach(function(p) {
892
- var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.78, 8), chromeMat);
893
- leg.position.set(p[0], 0.39, p[1]);
894
- group.add(leg);
985
+ var deskMain = new THREE.Mesh(new THREE.BoxGeometry(3.2, 0.07, 1.3), matWalnut);
986
+ deskMain.position.set(0, 0.79, 1.8); group.add(deskMain);
987
+ var marbMain = new THREE.Mesh(new THREE.BoxGeometry(3.22, 0.016, 1.32), marbleTopMat);
988
+ marbMain.position.set(0, 0.835, 1.8); group.add(marbMain);
989
+ var deskWing = new THREE.Mesh(new THREE.BoxGeometry(1.3, 0.07, 1.0), matWalnut);
990
+ deskWing.position.set(1.8, 0.79, 0.9); group.add(deskWing);
991
+ var marbWing = new THREE.Mesh(new THREE.BoxGeometry(1.32, 0.016, 1.02), marbleTopMat);
992
+ marbWing.position.set(1.8, 0.835, 0.9); group.add(marbWing);
993
+ // Desk legs
994
+ [[-1.45,1.1],[-1.45,2.5],[1.45,2.5],[1.45,1.1],[2.35,0.5],[2.35,1.3]].forEach(function(p) {
995
+ var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.79, 8), matChrome);
996
+ leg.position.set(p[0], 0.395, p[1]); group.add(leg);
895
997
  });
896
- // Cable management panel (dark, under desk back)
897
- var cablePanelMat = new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.5 });
898
- var cablePanel = new THREE.Mesh(new THREE.BoxGeometry(2.6, 0.5, 0.04), cablePanelMat);
899
- cablePanel.position.set(0, 0.5, 0.9);
900
- group.add(cablePanel);
901
998
 
902
- // --- Single 47" ultrawide monitor (replaces dual setup per owner request) ---
999
+ // 47" monitor
903
1000
  var monMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
904
- // 47" = ~1.2m wide x 0.67m tall in world units (16:9 aspect ratio, scaled to desk)
905
1001
  var bigMon = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.67, 0.025), monMat);
906
- bigMon.position.set(0, 1.2, 1.05); bigMon.castShadow = true;
907
- group.add(bigMon);
908
- // Screen (in FRONT of bezel — z offset +0.013 so stand doesn't clip through)
909
- var scrMat = new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: 0x58a6ff, emissiveIntensity: 0.3, roughness: 0.1 });
910
- var bigScr = new THREE.Mesh(new THREE.PlaneGeometry(1.14, 0.61), scrMat);
911
- bigScr.position.set(0, 1.2, 1.063);
912
- group.add(bigScr);
913
- // Center stand (BEHIND screen — z offset -0.02 so it doesn't poke through)
914
- var stand = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.28, 8), chromeMat);
915
- stand.position.set(0, 0.92, 1.07);
916
- group.add(stand);
917
- // Stand base (BEHIND screen)
918
- var standBase = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.02, 0.2), chromeMat);
919
- standBase.position.set(0, 0.78, 1.07);
920
- group.add(standBase);
921
-
922
- // --- Keyboard + mouse ---
923
- var kbMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5 });
924
- var kb = new THREE.Mesh(new THREE.BoxGeometry(0.38, 0.015, 0.12), kbMat);
925
- kb.position.set(-0.15, 0.835, 1.8);
926
- group.add(kb);
927
- var mouse = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.015, 0.06), kbMat);
928
- mouse.position.set(0.35, 0.835, 1.8);
929
- group.add(mouse);
930
-
931
- // --- Premium leather executive chair ---
1002
+ bigMon.position.set(0, 1.22, 1.25); group.add(bigMon);
1003
+ var scrMat2 = new THREE.MeshStandardMaterial({ color: 0x1a2a4a, emissive: new THREE.Color(0x58a6ff), emissiveIntensity: 0.3, roughness: 0.1 });
1004
+ var bigScr = new THREE.Mesh(new THREE.PlaneGeometry(1.14, 0.61), scrMat2);
1005
+ bigScr.position.set(0, 1.22, 1.263); group.add(bigScr);
1006
+ var monStand = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.29, 8), matChrome);
1007
+ monStand.position.set(0, 0.94, 1.27); group.add(monStand);
1008
+ var monBase = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.02, 0.20), matChrome);
1009
+ monBase.position.set(0, 0.795, 1.27); group.add(monBase);
1010
+
1011
+ // Executive chair (leather)
932
1012
  var chairG = new THREE.Group();
933
- chairG.position.set(0, 0, 2.4);
934
- // 5-star base
935
- var baseMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3, metalness: 0.4 });
1013
+ chairG.position.set(0, 0, 2.7);
1014
+ var baseMat2 = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3, metalness: 0.4 });
936
1015
  for (var ci = 0; ci < 5; ci++) {
937
1016
  var ca = (ci / 5) * Math.PI * 2;
938
- var arm = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.02, 0.035), baseMat);
1017
+ var arm = new THREE.Mesh(new THREE.BoxGeometry(0.32, 0.02, 0.035), baseMat2);
939
1018
  arm.position.set(Math.cos(ca) * 0.16, 0.04, Math.sin(ca) * 0.16);
940
- arm.rotation.y = -ca;
941
- chairG.add(arm);
1019
+ arm.rotation.y = -ca; chairG.add(arm);
942
1020
  }
943
- var cylM = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.4, 8), chromeMat);
1021
+ var cylM = new THREE.Mesh(new THREE.CylinderGeometry(0.03, 0.03, 0.40, 8), matChrome);
944
1022
  cylM.position.y = 0.26; chairG.add(cylM);
945
- // Wide seat
946
- var seatM = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.1, 0.5), leatherMat);
947
- seatM.position.y = 0.5; seatM.castShadow = true; chairG.add(seatM);
948
- // Tall padded backrest
949
- var backM = new THREE.Mesh(new THREE.BoxGeometry(0.48, 0.7, 0.08), leatherMat);
950
- backM.position.set(0, 0.9, 0.24); backM.castShadow = true; chairG.add(backM);
951
- // Headrest
952
- var headM = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.12, 0.08), leatherMat);
953
- headM.position.set(0, 1.3, 0.24); chairG.add(headM);
954
- // Armrests
1023
+ var seatM = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.10, 0.50), matLeather);
1024
+ seatM.position.y = 0.50; chairG.add(seatM);
1025
+ var backM = new THREE.Mesh(new THREE.BoxGeometry(0.48, 0.70, 0.08), matLeather);
1026
+ backM.position.set(0, 0.92, 0.25); chairG.add(backM);
1027
+ var headM = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.12, 0.08), matLeather);
1028
+ headM.position.set(0, 1.32, 0.25); chairG.add(headM);
955
1029
  [-0.27, 0.27].forEach(function(ax) {
956
- var armPost = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.04), baseMat);
957
- armPost.position.set(ax, 0.6, 0.08); chairG.add(armPost);
958
- var armPad = new THREE.Mesh(new THREE.BoxGeometry(0.09, 0.03, 0.25), leatherMat);
959
- armPad.position.set(ax, 0.73, 0.08); chairG.add(armPad);
1030
+ var ap = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.25, 0.04), baseMat2);
1031
+ ap.position.set(ax, 0.60, 0.08); chairG.add(ap);
1032
+ var apd = new THREE.Mesh(new THREE.BoxGeometry(0.09, 0.03, 0.25), matLeather);
1033
+ apd.position.set(ax, 0.73, 0.08); chairG.add(apd);
960
1034
  });
961
1035
  group.add(chairG);
962
1036
 
963
- // --- Bookshelf (right wall, walnut) ---
964
- var shelfGroup = new THREE.Group();
965
- shelfGroup.position.set(3.2, 0, 0.5);
966
- var shelfBack = new THREE.Mesh(new THREE.BoxGeometry(0.06, 2.2, 1.4), walnutMat);
967
- shelfBack.position.y = 1.1; shelfBack.castShadow = true;
968
- shelfGroup.add(shelfBack);
969
- [0.05, 0.55, 1.1, 1.65, 2.15].forEach(function(sy) {
970
- var shelf = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.03, 1.4), walnutMat);
971
- shelf.position.set(0.12, sy, 0); shelf.receiveShadow = true;
972
- shelfGroup.add(shelf);
973
- });
974
- // Books
975
- var bookColors = [0xc0392b, 0x2980b9, 0x8e44ad, 0xd4a24e, 0x1abc9c, 0x2c3e50];
976
- [0.09, 0.59, 1.14, 1.69].forEach(function(sy, si) {
977
- var startZ = -0.55;
978
- for (var bi2 = 0; bi2 < 5; bi2++) {
979
- var bh = 0.32 + Math.sin(si + bi2) * 0.08;
980
- var bw = 0.04 + Math.sin(si * 3 + bi2) * 0.015;
981
- var bMat = new THREE.MeshStandardMaterial({ color: bookColors[(si * 3 + bi2) % bookColors.length], roughness: 0.8 });
982
- var book = new THREE.Mesh(new THREE.BoxGeometry(0.18, bh, bw), bMat);
983
- book.position.set(0.16, sy + bh / 2, startZ);
984
- shelfGroup.add(book);
985
- startZ += bw + 0.02;
986
- }
987
- });
988
- group.add(shelfGroup);
989
-
990
- // --- Small sofa + coffee table ---
991
- var sofaMat = new THREE.MeshStandardMaterial({ color: 0x2a1a0a, roughness: 0.7 });
992
- var sofaBase = new THREE.Mesh(new THREE.BoxGeometry(2, 0.3, 0.7), sofaMat);
993
- sofaBase.position.set(-2.5, 0.18, -0.5); sofaBase.castShadow = true;
994
- group.add(sofaBase);
995
- var sofaBack = new THREE.Mesh(new THREE.BoxGeometry(2, 0.4, 0.15), sofaMat);
996
- sofaBack.position.set(-2.5, 0.45, -0.85); sofaBack.castShadow = true;
997
- group.add(sofaBack);
998
- // Cushions
999
- var cushionMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.8 });
1000
- [-3.1, -2.5, -1.9].forEach(function(cx) {
1001
- var cushion = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.06, 0.55), cushionMat);
1002
- cushion.position.set(cx, 0.36, -0.5);
1003
- group.add(cushion);
1004
- });
1005
- // Coffee table (glass top, chrome legs)
1006
- var coffeeGlassMat = new THREE.MeshStandardMaterial({ color: 0xccddee, transparent: true, opacity: 0.35, roughness: 0.05 });
1007
- var coffeeTop = new THREE.Mesh(new THREE.BoxGeometry(1, 0.03, 0.5), coffeeGlassMat);
1008
- coffeeTop.position.set(-2.5, 0.45, 0.2);
1009
- group.add(coffeeTop);
1010
- [-0.4, 0.4].forEach(function(lx) {
1011
- [-0.18, 0.18].forEach(function(lz) {
1012
- var cLeg = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.42, 6), chromeMat);
1013
- cLeg.position.set(-2.5 + lx, 0.22, 0.2 + lz);
1014
- group.add(cLeg);
1015
- });
1037
+ // Gold accent art frame on back wall
1038
+ var goldMat2 = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.30, metalness: 0.70 });
1039
+ var artW = 2.0, artH = 1.3;
1040
+ [[artW + 0.08, 0.06, 0.06, 0, 3.4, offD/2-0.1],
1041
+ [artW + 0.08, 0.06, 0.06, 0, 2.1, offD/2-0.1],
1042
+ [0.06, (artH+0.12), 0.06, -(artW/2+0.04), 2.75, offD/2-0.1],
1043
+ [0.06, (artH+0.12), 0.06, (artW/2+0.04), 2.75, offD/2-0.1]].forEach(function(b) {
1044
+ var bar = new THREE.Mesh(new THREE.BoxGeometry(b[0], b[1], b[2]), goldMat2);
1045
+ bar.position.set(b[3], b[4], b[5]); group.add(bar);
1016
1046
  });
1017
-
1018
- // --- Luxury plant ---
1019
- var planterMat = new THREE.MeshStandardMaterial({ color: 0x2a2a3a, roughness: 0.5 });
1020
- var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.2, 0.5, 12), planterMat);
1021
- planter.position.set(3, 0.25, -2.5); planter.castShadow = true;
1022
- group.add(planter);
1023
- var leafMat = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.8 });
1024
- for (var pi = 0; pi < 6; pi++) {
1025
- var pa = (pi / 6) * Math.PI * 2;
1026
- var leaf = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 6), leafMat);
1027
- leaf.position.set(3 + Math.cos(pa) * 0.15, 0.6, -2.5 + Math.sin(pa) * 0.15);
1028
- group.add(leaf);
1029
- }
1030
- var topL = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), leafMat);
1031
- topL.position.set(3, 0.75, -2.5); group.add(topL);
1032
-
1033
- // --- Gold accent artwork frame on back wall ---
1034
- var goldMat = new THREE.MeshStandardMaterial({ color: 0xd4af37, roughness: 0.3, metalness: 0.7 });
1035
- // Frame
1036
- var artFrameTop = new THREE.Mesh(new THREE.BoxGeometry(1.6, 0.06, 0.06), goldMat);
1037
- artFrameTop.position.set(0, 3.2, offD / 2 - 0.08); group.add(artFrameTop);
1038
- var artFrameBot = new THREE.Mesh(new THREE.BoxGeometry(1.6, 0.06, 0.06), goldMat);
1039
- artFrameBot.position.set(0, 2.0, offD / 2 - 0.08); group.add(artFrameBot);
1040
- var artFrameL = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.26, 0.06), goldMat);
1041
- artFrameL.position.set(-0.8, 2.6, offD / 2 - 0.08); group.add(artFrameL);
1042
- var artFrameR = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.26, 0.06), goldMat);
1043
- artFrameR.position.set(0.8, 2.6, offD / 2 - 0.08); group.add(artFrameR);
1044
- // Canvas inside frame (dark elegant)
1045
1047
  var artMat = new THREE.MeshStandardMaterial({ color: 0x1a2a3a, roughness: 0.8 });
1046
- var art = new THREE.Mesh(new THREE.PlaneGeometry(1.5, 1.1), artMat);
1047
- art.position.set(0, 2.6, offD / 2 - 0.06);
1048
- art.rotation.y = Math.PI;
1049
- group.add(art);
1050
-
1051
- // --- Warm ambient lighting ---
1052
- var warmLight1 = new THREE.PointLight(0xffeedd, 0.4, 8);
1053
- warmLight1.position.set(0, 3.5, 1.5);
1054
- group.add(warmLight1);
1055
- var warmLight2 = new THREE.PointLight(0xffeedd, 0.2, 5);
1056
- warmLight2.position.set(-2, 2, 0);
1057
- group.add(warmLight2);
1058
-
1059
- // --- Pendant light (premium, gold accent) ---
1060
- var pendWire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 2), new THREE.MeshStandardMaterial({ color: 0x333333 }));
1061
- pendWire.position.set(0, wallH - 1, 1.5);
1062
- group.add(pendWire);
1063
- var pendShade = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.3, 0.2, 12, 1, true),
1048
+ var art = new THREE.Mesh(new THREE.PlaneGeometry(artW, artH), artMat);
1049
+ art.position.set(0, 2.75, offD / 2 - 0.08);
1050
+ art.rotation.y = Math.PI; group.add(art);
1051
+
1052
+ // Warm lighting
1053
+ var wl = new THREE.PointLight(0xffeedd, 0.40, 10);
1054
+ wl.castShadow = false; wl.position.set(0, 3.8, 1.8); group.add(wl);
1055
+ // Pendant
1056
+ var pendWire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 2.2, 4),
1057
+ new THREE.MeshStandardMaterial({ color: 0x2a2a2a }));
1058
+ pendWire.position.set(0, wallH - 1.1, 1.8); group.add(pendWire);
1059
+ var pendShade = new THREE.Mesh(new THREE.CylinderGeometry(0.16, 0.32, 0.22, 12, 1, true),
1064
1060
  new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.4, metalness: 0.3, side: THREE.DoubleSide }));
1065
- pendShade.position.set(0, wallH - 2.1, 1.5);
1066
- group.add(pendShade);
1067
- var pendRim = new THREE.Mesh(new THREE.TorusGeometry(0.3, 0.01, 6, 24), goldMat);
1068
- pendRim.position.set(0, wallH - 2.2, 1.5);
1061
+ pendShade.position.set(0, wallH - 2.3, 1.8); group.add(pendShade);
1062
+ var pendRim = new THREE.Mesh(new THREE.TorusGeometry(0.32, 0.012, 6, 24), goldMat2);
1069
1063
  pendRim.rotation.x = Math.PI / 2;
1070
- group.add(pendRim);
1064
+ pendRim.position.set(0, wallH - 2.42, 1.8); group.add(pendRim);
1071
1065
 
1072
- // --- "MANAGER" gold sign above door ---
1066
+ // MANAGER sign
1073
1067
  var signDiv = document.createElement('div');
1074
1068
  signDiv.textContent = 'MANAGER';
1075
1069
  signDiv.style.cssText = 'color:#d4af37;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:3px;text-shadow:0 0 6px rgba(212,175,55,0.4);';
1076
1070
  var sign = new CSS2DObject(signDiv);
1077
- sign.position.set(0, wallH + 0.3, -offD / 2);
1071
+ sign.position.set(0, wallH + 0.35, -offD / 2);
1078
1072
  group.add(sign);
1079
1073
 
1080
1074
  S.furnitureGroup.add(group);
1081
1075
  S._managerOfficeGroup = group;
1082
1076
  S._managerOfficePos = { x: x, z: z };
1083
1077
 
1084
- // Register manager desk in deskMeshes so monitor screen system works
1085
- // bigScr is the 47" monitor screen — used for click detection + iframe overlay
1078
+ // Register manager desk in deskMeshes
1086
1079
  var mgrDeskIdx = CAMPUS_DESKS.length - 1;
1087
- var mgrScreenMat = new THREE.MeshStandardMaterial({ color: 0x333333, emissive: 0x333333, emissiveIntensity: 0.1, roughness: 0.2 });
1088
- S.deskMeshes[mgrDeskIdx] = { group: group, screen: bigScr, screenMat: mgrScreenMat, index: mgrDeskIdx, x: x, z: z + 1.7 };
1080
+ var mgrScreenMat = new THREE.MeshStandardMaterial({ color: 0x333333, emissive: new THREE.Color(0x333333), emissiveIntensity: 0.1, roughness: 0.2 });
1081
+ S.deskMeshes[mgrDeskIdx] = { group: group, screen: bigScr, screenMat: mgrScreenMat, index: mgrDeskIdx, x: x, z: z + 1.9 };
1089
1082
  }
1090
1083
 
1091
- // ==================== DESIGNER STUDIO ====================
1092
- function buildDesignerStudio(x, z, walnutMat, chromeMat) {
1093
- // Mood board wall
1084
+ // ============================================================
1085
+ // DESIGNER STUDIO (left wing)
1086
+ // ============================================================
1087
+ function buildDesignerStudio(x, z, matWalnut, matChrome) {
1094
1088
  var boardMat = new THREE.MeshStandardMaterial({ color: 0x3a3a4a, roughness: 0.5 });
1095
- var board = new THREE.Mesh(new THREE.BoxGeometry(0.08, 2, 4), boardMat);
1096
- board.position.set(x - 5.5, 1.5, z);
1089
+ var board = new THREE.Mesh(new THREE.BoxGeometry(0.10, 2.2, 5), boardMat);
1090
+ board.position.set(x - 8, 1.6, z);
1097
1091
  board.castShadow = true;
1098
1092
  S.furnitureGroup.add(board);
1099
- // Colorful sticky notes on board
1100
1093
  var noteColors = [0xfbbf24, 0xf87171, 0x34d399, 0x60a5fa, 0xa78bfa, 0xfb923c];
1101
- for (var ni = 0; ni < 12; ni++) {
1094
+ for (var ni = 0; ni < 16; ni++) {
1102
1095
  var noteMat = new THREE.MeshStandardMaterial({ color: noteColors[ni % noteColors.length], roughness: 0.9 });
1103
- var note = new THREE.Mesh(new THREE.PlaneGeometry(0.3, 0.3), noteMat);
1104
- note.position.set(x - 5.44, 0.8 + Math.floor(ni / 4) * 0.5, z - 1.5 + (ni % 4) * 0.8);
1096
+ var note = new THREE.Mesh(new THREE.PlaneGeometry(0.30, 0.30), noteMat);
1097
+ note.position.set(x - 7.94, 0.8 + Math.floor(ni / 4) * 0.55, z - 2.0 + (ni % 4) * 1.0);
1105
1098
  note.rotation.y = Math.PI / 2;
1106
1099
  S.furnitureGroup.add(note);
1107
1100
  }
1108
-
1109
1101
  // Standing desk
1110
- var standDesk = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.06, 0.8), walnutMat);
1111
- standDesk.position.set(x - 2, 1.1, z + 3);
1102
+ var standDesk = new THREE.Mesh(new THREE.BoxGeometry(2.0, 0.06, 0.9), matWalnut);
1103
+ standDesk.position.set(x - 3, 1.05, z + 4);
1112
1104
  standDesk.castShadow = true;
1113
1105
  S.furnitureGroup.add(standDesk);
1114
- // Adjustable legs
1115
- [-0.7, 0.7].forEach(function(lx) {
1116
- var standLeg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.1, 0.06), chromeMat);
1117
- standLeg.position.set(x - 2 + lx, 0.55, z + 3);
1106
+ [-0.8, 0.8].forEach(function(lx) {
1107
+ var standLeg = new THREE.Mesh(new THREE.BoxGeometry(0.06, 1.05, 0.06), matChrome);
1108
+ standLeg.position.set(x - 3 + lx, 0.525, z + 4);
1118
1109
  S.furnitureGroup.add(standLeg);
1119
1110
  });
1120
-
1121
- // "DESIGN LAB" sign
1122
1111
  var signDiv = document.createElement('div');
1123
1112
  signDiv.textContent = 'DESIGN LAB';
1124
1113
  signDiv.style.cssText = 'color:#a855f7;font-size:9px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;';
1125
1114
  var sign = new CSS2DObject(signDiv);
1126
- sign.position.set(x, 3.5, z);
1115
+ sign.position.set(x, 4.0, z);
1127
1116
  S.furnitureGroup.add(sign);
1128
1117
  }
1129
1118
 
1130
- // ==================== BAR & CAFÉ ====================
1131
- function buildBar(x, z, walnutMat, chromeMat, neonBlueMat, neonPurpleMat) {
1132
- // Long bar counter
1133
- var barTop = new THREE.Mesh(new THREE.BoxGeometry(6, 0.08, 1.2), walnutMat);
1134
- barTop.position.set(x, 1.1, z); barTop.castShadow = true;
1119
+ // ============================================================
1120
+ // BAR & CAFÉ
1121
+ // ============================================================
1122
+ function buildBar(x, z, matWalnut, matChrome, matNeonBlue, matNeonPurple) {
1123
+ var barTop = new THREE.Mesh(new THREE.BoxGeometry(8, 0.09, 1.4), matWalnut);
1124
+ barTop.position.set(x, 1.12, z); barTop.castShadow = true;
1135
1125
  S.furnitureGroup.add(barTop);
1136
- var barFront = new THREE.Mesh(new THREE.BoxGeometry(6, 1.1, 0.1),
1137
- new THREE.MeshStandardMaterial({ color: 0x1a1a2e, roughness: 0.4 }));
1138
- barFront.position.set(x, 0.55, z + 0.55);
1139
- barFront.castShadow = true;
1126
+ var barFront = new THREE.Mesh(new THREE.BoxGeometry(8, 1.12, 0.12),
1127
+ new THREE.MeshStandardMaterial({ color: 0x1a1c22, roughness: 0.38 }));
1128
+ barFront.position.set(x, 0.56, z + 0.65); barFront.castShadow = true;
1140
1129
  S.furnitureGroup.add(barFront);
1141
-
1142
- // LED strip under bar counter
1143
- var barLed = new THREE.Mesh(new THREE.BoxGeometry(5.8, 0.02, 0.02), neonBlueMat);
1144
- barLed.position.set(x, 1.02, z + 0.58);
1130
+ var barLed = new THREE.Mesh(new THREE.BoxGeometry(7.8, 0.022, 0.022), matNeonBlue);
1131
+ barLed.position.set(x, 1.03, z + 0.68);
1145
1132
  S.furnitureGroup.add(barLed);
1146
-
1147
- // Bar stools (5)
1148
- for (var si = 0; si < 5; si++) {
1149
- var sx = x - 2 + si * 1;
1133
+ // Stools
1134
+ for (var si = 0; si < 6; si++) {
1150
1135
  var stoolGroup = new THREE.Group();
1151
- stoolGroup.position.set(sx, 0, z + 1.2);
1136
+ stoolGroup.position.set(x - 3 + si * 1.1, 0, z + 1.4);
1152
1137
  var stoolSeat = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.18, 0.06, 12),
1153
- new THREE.MeshStandardMaterial({ color: 0x333340, roughness: 0.6 }));
1154
- stoolSeat.position.y = 0.75;
1155
- stoolGroup.add(stoolSeat);
1156
- var stoolPost = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.7, 8), chromeMat);
1157
- stoolPost.position.y = 0.38;
1158
- stoolGroup.add(stoolPost);
1159
- var stoolBase = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.2, 0.04, 12), chromeMat);
1160
- stoolBase.position.y = 0.04;
1161
- stoolGroup.add(stoolBase);
1138
+ new THREE.MeshStandardMaterial({ color: 0x2a2d3a, roughness: 0.6 }));
1139
+ stoolSeat.position.y = 0.76; stoolGroup.add(stoolSeat);
1140
+ var stoolPost = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.72, 8), matChrome);
1141
+ stoolPost.position.y = 0.38; stoolGroup.add(stoolPost);
1142
+ var stoolBase = new THREE.Mesh(new THREE.CylinderGeometry(0.18, 0.20, 0.04, 12), matChrome);
1143
+ stoolBase.position.y = 0.04; stoolGroup.add(stoolBase);
1162
1144
  S.furnitureGroup.add(stoolGroup);
1163
1145
  }
1164
-
1165
- // Bottle shelf behind bar
1166
- var shelfMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.6 });
1146
+ // Shelves + bottles
1147
+ var shelfMat = new THREE.MeshStandardMaterial({ color: 0x3a2210, roughness: 0.58 });
1167
1148
  [1.5, 2.2, 2.9].forEach(function(sy) {
1168
- var shelf = new THREE.Mesh(new THREE.BoxGeometry(5.5, 0.04, 0.3), shelfMat);
1169
- shelf.position.set(x, sy, z - 0.9);
1149
+ var shelf = new THREE.Mesh(new THREE.BoxGeometry(7.5, 0.04, 0.32), shelfMat);
1150
+ shelf.position.set(x, sy, z - 1.0);
1170
1151
  S.furnitureGroup.add(shelf);
1171
1152
  });
1172
-
1173
- // Bottles on shelves
1174
1153
  var bottleColors = [0x2d8a4e, 0x8B4513, 0xd4af37, 0xcc3333, 0x1a5276, 0xf0f0f0];
1175
- for (var bi = 0; bi < 15; bi++) {
1176
- var bx = x - 2.5 + (bi % 5) * 1;
1177
- var by = 1.55 + Math.floor(bi / 5) * 0.7;
1154
+ for (var bi = 0; bi < 18; bi++) {
1155
+ var bx = x - 3.5 + (bi % 6) * 1.2;
1156
+ var by = 1.55 + Math.floor(bi / 6) * 0.7;
1178
1157
  var bottleMat = new THREE.MeshStandardMaterial({ color: bottleColors[bi % bottleColors.length], roughness: 0.3, metalness: 0.1 });
1179
- var bottle = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.25, 8), bottleMat);
1180
- bottle.position.set(bx, by + 0.12, z - 0.85);
1158
+ var bottle = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.26, 8), bottleMat);
1159
+ bottle.position.set(bx, by + 0.13, z - 0.94);
1181
1160
  S.furnitureGroup.add(bottle);
1182
- var bottleNeck = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.03, 0.1, 6), bottleMat);
1183
- bottleNeck.position.set(bx, by + 0.3, z - 0.85);
1184
- S.furnitureGroup.add(bottleNeck);
1161
+ var neck = new THREE.Mesh(new THREE.CylinderGeometry(0.02, 0.035, 0.10, 6), bottleMat);
1162
+ neck.position.set(bx, by + 0.31, z - 0.94);
1163
+ S.furnitureGroup.add(neck);
1185
1164
  }
1186
-
1187
1165
  // Coffee machine
1188
- var coffeeMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.3, metalness: 0.2 });
1189
- var coffee = new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.5, 0.3), coffeeMat);
1190
- coffee.position.set(x + 2.5, 1.4, z - 0.1);
1191
- coffee.castShadow = true;
1166
+ var coffee = new THREE.Mesh(new THREE.BoxGeometry(0.40, 0.50, 0.30),
1167
+ new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.28, metalness: 0.22 }));
1168
+ coffee.position.set(x + 3.5, 1.38, z - 0.12);
1192
1169
  S.furnitureGroup.add(coffee);
1193
-
1194
- // "BAR" neon sign
1170
+ // Sign
1195
1171
  var signDiv = document.createElement('div');
1196
1172
  signDiv.textContent = 'BAR & CAFÉ';
1197
1173
  signDiv.style.cssText = 'color:#a855f7;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;text-shadow:0 0 8px #a855f7;';
1198
1174
  var sign = new CSS2DObject(signDiv);
1199
- sign.position.set(x, 3.5, z - 1);
1175
+ sign.position.set(x, 4.0, z - 1.2);
1200
1176
  S.furnitureGroup.add(sign);
1201
1177
  }
1202
1178
 
1203
- // ==================== JUKEBOX (Wurlitzer 1015 style) ====================
1179
+ // ============================================================
1180
+ // JUKEBOX (Wurlitzer 1015 style)
1181
+ // ============================================================
1204
1182
  function buildJukebox(x, z) {
1205
1183
  var group = new THREE.Group();
1206
1184
  group.position.set(x, 0, z);
1207
-
1208
- // Main body rounded wooden cabinet
1209
- var bodyMat = new THREE.MeshStandardMaterial({ color: 0x8B4513, roughness: 0.5, metalness: 0.05 });
1210
- var body = new THREE.Mesh(new THREE.BoxGeometry(0.9, 1.5, 0.5), bodyMat);
1211
- body.position.y = 0.75; body.castShadow = true;
1212
- group.add(body);
1213
-
1214
- // Chrome trim top arch
1215
- var chromeMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.1, metalness: 0.8 });
1185
+ var bodyMat = new THREE.MeshStandardMaterial({ color: 0x8B4513, roughness: 0.50, metalness: 0.05 });
1186
+ var body = new THREE.Mesh(new THREE.BoxGeometry(0.90, 1.50, 0.50), bodyMat);
1187
+ body.position.y = 0.75; body.castShadow = true; group.add(body);
1188
+ var chromeMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, roughness: 0.10, metalness: 0.80 });
1216
1189
  var topArch = new THREE.Mesh(new THREE.CylinderGeometry(0.45, 0.45, 0.06, 16, 1, false, 0, Math.PI), chromeMat);
1217
- topArch.position.set(0, 1.53, 0);
1218
- topArch.rotation.z = Math.PI;
1219
- topArch.rotation.y = Math.PI / 2;
1190
+ topArch.position.set(0, 1.53, 0); topArch.rotation.z = Math.PI; topArch.rotation.y = Math.PI / 2;
1220
1191
  group.add(topArch);
1221
-
1222
- // Glass viewing panel (curved top section)
1223
- var glassMat = new THREE.MeshStandardMaterial({ color: 0xaaddff, transparent: true, opacity: 0.4, roughness: 0.05 });
1224
- var glassPanel = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.4, 0.08), glassMat);
1225
- glassPanel.position.set(0, 1.35, 0.22);
1226
- group.add(glassPanel);
1227
-
1228
- // Neon glow strips (sides) — animated via S._jukeboxNeon
1192
+ var glassMat2 = new THREE.MeshStandardMaterial({ color: 0xaaddff, transparent: true, opacity: 0.40, roughness: 0.05 });
1193
+ var glassPanel = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.40, 0.08), glassMat2);
1194
+ glassPanel.position.set(0, 1.35, 0.22); group.add(glassPanel);
1229
1195
  var neonColors = [0xff4488, 0xff8844, 0xffdd44, 0x44ff88, 0x4488ff, 0xaa44ff];
1230
- var neonMat = new THREE.MeshStandardMaterial({ color: 0xff4488, emissive: 0xff4488, emissiveIntensity: 0.8, roughness: 0.2 });
1231
- // Left neon strip
1232
- var neonL = new THREE.Mesh(new THREE.BoxGeometry(0.04, 1.2, 0.04), neonMat);
1233
- neonL.position.set(-0.42, 0.75, 0.23);
1234
- group.add(neonL);
1235
- // Right neon strip
1236
- var neonR = new THREE.Mesh(new THREE.BoxGeometry(0.04, 1.2, 0.04), neonMat);
1237
- neonR.position.set(0.42, 0.75, 0.23);
1238
- group.add(neonR);
1239
- // Top neon arc
1240
- var neonTop = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.04, 0.04), neonMat);
1241
- neonTop.position.set(0, 1.5, 0.23);
1242
- group.add(neonTop);
1243
-
1244
- // Bubble tube columns (sides)
1245
- var bubbleMat = new THREE.MeshStandardMaterial({ color: 0x66ccff, transparent: true, opacity: 0.5, emissive: 0x66ccff, emissiveIntensity: 0.3 });
1246
- var bubbleL = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.3, 8), bubbleMat);
1247
- bubbleL.position.set(-0.38, 0.75, 0.18);
1248
- group.add(bubbleL);
1249
- var bubbleR = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.3, 8), bubbleMat);
1250
- bubbleR.position.set(0.38, 0.75, 0.18);
1251
- group.add(bubbleR);
1252
-
1253
- // Chrome base
1254
- var baseMat = chromeMat;
1255
- var base = new THREE.Mesh(new THREE.BoxGeometry(1, 0.08, 0.55), baseMat);
1256
- base.position.y = 0.04;
1257
- group.add(base);
1258
-
1259
- // Chrome grille (speaker area — lower section)
1196
+ var neonMat = new THREE.MeshStandardMaterial({ color: 0xff4488, emissive: new THREE.Color(0xff4488), emissiveIntensity: 0.80, roughness: 0.20 });
1197
+ var neonL = new THREE.Mesh(new THREE.BoxGeometry(0.04, 1.20, 0.04), neonMat);
1198
+ neonL.position.set(-0.42, 0.75, 0.23); group.add(neonL);
1199
+ var neonR = new THREE.Mesh(new THREE.BoxGeometry(0.04, 1.20, 0.04), neonMat);
1200
+ neonR.position.set(0.42, 0.75, 0.23); group.add(neonR);
1201
+ var neonTop = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.04, 0.04), neonMat);
1202
+ neonTop.position.set(0, 1.50, 0.23); group.add(neonTop);
1203
+ var bubbleMat = new THREE.MeshStandardMaterial({ color: 0x66ccff, transparent: true, opacity: 0.50, emissive: new THREE.Color(0x66ccff), emissiveIntensity: 0.30 });
1204
+ var bubbleL = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.30, 8), bubbleMat);
1205
+ bubbleL.position.set(-0.38, 0.75, 0.18); group.add(bubbleL);
1206
+ var bubbleR = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1.30, 8), bubbleMat);
1207
+ bubbleR.position.set(0.38, 0.75, 0.18); group.add(bubbleR);
1208
+ var base = new THREE.Mesh(new THREE.BoxGeometry(1.00, 0.08, 0.55), chromeMat);
1209
+ base.position.y = 0.04; group.add(base);
1260
1210
  for (var gi = 0; gi < 6; gi++) {
1261
- var grille = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.01, 0.01), chromeMat);
1262
- grille.position.set(0, 0.15 + gi * 0.06, 0.26);
1263
- group.add(grille);
1211
+ var grille = new THREE.Mesh(new THREE.BoxGeometry(0.60, 0.01, 0.01), chromeMat);
1212
+ grille.position.set(0, 0.15 + gi * 0.06, 0.26); group.add(grille);
1264
1213
  }
1265
-
1266
- // Record selector buttons (front panel)
1267
1214
  var buttonMat = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.3, metalness: 0.5 });
1268
- for (var bi = 0; bi < 3; bi++) {
1215
+ for (var bi2 = 0; bi2 < 3; bi2++) {
1269
1216
  var btn = new THREE.Mesh(new THREE.CylinderGeometry(0.025, 0.025, 0.02, 8), buttonMat);
1270
- btn.position.set(-0.15 + bi * 0.15, 0.85, 0.26);
1271
- btn.rotation.x = Math.PI / 2;
1217
+ btn.position.set(-0.15 + bi2 * 0.15, 0.85, 0.26); btn.rotation.x = Math.PI / 2;
1272
1218
  group.add(btn);
1273
1219
  }
1274
-
1275
- // "NOW PLAYING" CSS2D label (hidden by default, shown when music plays)
1276
1220
  var labelDiv = document.createElement('div');
1277
1221
  labelDiv.className = 'jukebox-label';
1278
1222
  labelDiv.style.cssText = 'color:#ff4488;font-size:8px;font-weight:bold;font-family:monospace;text-shadow:0 0 6px #ff4488;text-align:center;pointer-events:none;opacity:0.9;';
1279
1223
  labelDiv.innerHTML = '<div style="color:#ffdd44;font-size:10px">JUKEBOX</div><div style="font-size:7px;color:#aaa">Press E to play</div>';
1280
1224
  var label = new CSS2DObject(labelDiv);
1281
- label.position.set(0, 1.8, 0);
1225
+ label.position.set(0, 1.80, 0);
1282
1226
  group.add(label);
1283
-
1284
- // Store references for interaction + animation
1285
- S._jukebox = {
1286
- group: group,
1287
- neonMat: neonMat,
1288
- neonColors: neonColors,
1289
- neonIndex: 0,
1290
- label: labelDiv,
1291
- pos: { x: x, z: z },
1292
- playing: false
1293
- };
1294
-
1227
+ S._jukebox = { group: group, neonMat: neonMat, neonColors: neonColors, neonIndex: 0, label: labelDiv, pos: { x: x, z: z }, playing: false };
1295
1228
  S.furnitureGroup.add(group);
1296
1229
  }
1297
1230
 
1298
- // ==================== RECREATION CENTER ====================
1299
- function buildRecCenter(x, z, walnutMat, chromeMat, carpetMat) {
1300
- // Carpet area
1301
- var recCarpet = new THREE.Mesh(new THREE.PlaneGeometry(10, 8), carpetMat);
1231
+ // ============================================================
1232
+ // RECREATION CENTER
1233
+ // ============================================================
1234
+ function buildRecCenter(x, z, matWalnut, matChrome, matFabric) {
1235
+ var recCarpet = new THREE.Mesh(new THREE.PlaneGeometry(16, 14), matFabric);
1302
1236
  recCarpet.rotation.x = -Math.PI / 2;
1303
1237
  recCarpet.position.set(x, 0.01, z);
1304
1238
  recCarpet.receiveShadow = true;
1305
1239
  S.furnitureGroup.add(recCarpet);
1306
-
1307
1240
  // Pool table
1308
1241
  var ptGroup = new THREE.Group();
1309
- ptGroup.position.set(x - 2, 0, z);
1310
- var ptTop = new THREE.Mesh(new THREE.BoxGeometry(2.4, 0.1, 1.3),
1242
+ ptGroup.position.set(x - 3, 0, z - 1);
1243
+ var ptTop = new THREE.Mesh(new THREE.BoxGeometry(2.4, 0.10, 1.3),
1311
1244
  new THREE.MeshStandardMaterial({ color: 0x006633, roughness: 0.9 }));
1312
- ptTop.position.y = 0.85; ptTop.castShadow = true;
1313
- ptGroup.add(ptTop);
1314
- var ptFrame = new THREE.Mesh(new THREE.BoxGeometry(2.5, 0.15, 1.4), walnutMat);
1315
- ptFrame.position.y = 0.78; ptFrame.castShadow = true;
1316
- ptGroup.add(ptFrame);
1317
- // Legs
1318
- [[-1.1, -0.55], [-1.1, 0.55], [1.1, -0.55], [1.1, 0.55]].forEach(function(p) {
1319
- var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.7, 8), walnutMat);
1320
- leg.position.set(p[0], 0.35, p[1]);
1321
- ptGroup.add(leg);
1245
+ ptTop.position.y = 0.85; ptGroup.add(ptTop);
1246
+ var ptFrame = new THREE.Mesh(new THREE.BoxGeometry(2.55, 0.15, 1.45), matWalnut);
1247
+ ptFrame.position.y = 0.78; ptGroup.add(ptFrame);
1248
+ [[-1.1,-0.55],[-1.1,0.55],[1.1,-0.55],[1.1,0.55]].forEach(function(p) {
1249
+ var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.70, 8), matWalnut);
1250
+ leg.position.set(p[0], 0.35, p[1]); ptGroup.add(leg);
1322
1251
  });
1323
1252
  S.furnitureGroup.add(ptGroup);
1324
-
1325
1253
  // Foosball table
1326
1254
  var fbGroup = new THREE.Group();
1327
- fbGroup.position.set(x + 2.5, 0, z);
1328
- var fbBody = new THREE.Mesh(new THREE.BoxGeometry(1.4, 0.2, 0.75),
1255
+ fbGroup.position.set(x + 3, 0, z - 1);
1256
+ var fbBody = new THREE.Mesh(new THREE.BoxGeometry(1.40, 0.20, 0.75),
1329
1257
  new THREE.MeshStandardMaterial({ color: 0x2a1a0a, roughness: 0.6 }));
1330
- fbBody.position.y = 0.85; fbBody.castShadow = true;
1331
- fbGroup.add(fbBody);
1332
- var fbField = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.02, 0.6),
1258
+ fbBody.position.y = 0.85; fbGroup.add(fbBody);
1259
+ var fbField = new THREE.Mesh(new THREE.BoxGeometry(1.20, 0.02, 0.60),
1333
1260
  new THREE.MeshStandardMaterial({ color: 0x006633, roughness: 0.8 }));
1334
- fbField.position.y = 0.96;
1335
- fbGroup.add(fbField);
1336
- // Legs
1337
- [[-0.6, -0.3], [-0.6, 0.3], [0.6, -0.3], [0.6, 0.3]].forEach(function(p) {
1338
- var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.75, 6), chromeMat);
1339
- leg.position.set(p[0], 0.38, p[1]);
1340
- fbGroup.add(leg);
1261
+ fbField.position.y = 0.96; fbGroup.add(fbField);
1262
+ [[-0.6,-0.3],[-0.6,0.3],[0.6,-0.3],[0.6,0.3]].forEach(function(p) {
1263
+ var leg = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 0.75, 6), matChrome);
1264
+ leg.position.set(p[0], 0.38, p[1]); fbGroup.add(leg);
1341
1265
  });
1342
- // Rods
1343
1266
  [-0.3, 0, 0.3].forEach(function(rz) {
1344
- var rod = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.9, 6), chromeMat);
1345
- rod.position.set(0, 0.98, rz);
1346
- rod.rotation.z = Math.PI / 2;
1347
- fbGroup.add(rod);
1267
+ var rod = new THREE.Mesh(new THREE.CylinderGeometry(0.012, 0.012, 0.90, 6), matChrome);
1268
+ rod.position.set(0, 0.98, rz); rod.rotation.z = Math.PI / 2; fbGroup.add(rod);
1348
1269
  });
1349
1270
  S.furnitureGroup.add(fbGroup);
1350
-
1351
1271
  // Beanbags
1352
1272
  var bbColors = [0xe53e3e, 0x3b82f6, 0x22c55e, 0xa855f7];
1353
- [{ x: -1, z: 3 }, { x: 1.5, z: 3.5 }, { x: 3, z: 2.5 }, { x: -2.5, z: 3.5 }].forEach(function(bp, bi) {
1273
+ [{ x: -1, z: 4 }, { x: 2, z: 4.5 }, { x: 4, z: 3.5 }, { x: -3, z: 4.5 }].forEach(function(bp, bi) {
1354
1274
  var bbMat = new THREE.MeshStandardMaterial({ color: bbColors[bi], roughness: 0.9 });
1355
1275
  var bot = new THREE.Mesh(new THREE.SphereGeometry(0.45, 16, 12), bbMat);
1356
1276
  bot.position.set(x + bp.x, 0.22, z + bp.z);
1357
1277
  bot.scale.set(1, 0.5, 1);
1358
- bot.castShadow = true;
1359
1278
  S.furnitureGroup.add(bot);
1360
1279
  });
1361
-
1362
- // Static decorative TV (smaller, no dashboard — main TV is at reception)
1280
+ // Decorative TV
1363
1281
  var tvMat2 = new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.2 });
1364
1282
  var tvBody = new THREE.Mesh(new THREE.BoxGeometry(2.5, 1.5, 0.08), tvMat2);
1365
- tvBody.position.set(x, 2.3, z - 3.8);
1366
- tvBody.castShadow = true;
1283
+ tvBody.position.set(x, 2.3, z - 6.5);
1367
1284
  S.furnitureGroup.add(tvBody);
1368
1285
  var tvScr = new THREE.Mesh(new THREE.PlaneGeometry(2.3, 1.3),
1369
- new THREE.MeshStandardMaterial({ color: 0x0a1520, emissive: 0x22c55e, emissiveIntensity: 0.15, roughness: 0.1 }));
1370
- tvScr.position.set(x, 2.3, z - 3.75);
1286
+ new THREE.MeshStandardMaterial({ color: 0x0a1520, emissive: new THREE.Color(0x22c55e), emissiveIntensity: 0.15, roughness: 0.1 }));
1287
+ tvScr.position.set(x, 2.3, z - 6.42);
1371
1288
  S.furnitureGroup.add(tvScr);
1372
-
1373
- // "REC ZONE" sign
1374
1289
  var signDiv = document.createElement('div');
1375
1290
  signDiv.textContent = 'REC ZONE';
1376
1291
  signDiv.style.cssText = 'color:#22c55e;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;text-shadow:0 0 8px #22c55e;';
@@ -1379,84 +1294,73 @@ function buildRecCenter(x, z, walnutMat, chromeMat, carpetMat) {
1379
1294
  S.furnitureGroup.add(sign);
1380
1295
  }
1381
1296
 
1382
- // ==================== GYM ====================
1383
- function buildGym(x, z, chromeMat, darkMat) {
1384
- // Rubber floor
1297
+ // ============================================================
1298
+ // GYM
1299
+ // ============================================================
1300
+ function buildGym(x, z, matChrome, matConcrete) {
1385
1301
  var rubberMat = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, roughness: 0.95 });
1386
- var gymFloor = new THREE.Mesh(new THREE.PlaneGeometry(8, 8), rubberMat);
1302
+ var gymFloor = new THREE.Mesh(new THREE.PlaneGeometry(12, 12), rubberMat);
1387
1303
  gymFloor.rotation.x = -Math.PI / 2;
1388
1304
  gymFloor.position.set(x, 0.01, z);
1389
1305
  gymFloor.receiveShadow = true;
1390
1306
  S.furnitureGroup.add(gymFloor);
1391
-
1392
1307
  // Treadmill
1393
1308
  var tmGroup = new THREE.Group();
1394
- tmGroup.position.set(x - 1.5, 0, z - 2);
1395
- var tmBase = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.15, 1.6), darkMat);
1396
- tmBase.position.y = 0.1; tmBase.castShadow = true;
1397
- tmGroup.add(tmBase);
1398
- var tmBelt = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.02, 1.3),
1309
+ tmGroup.position.set(x - 2, 0, z - 2);
1310
+ var tmBase = new THREE.Mesh(new THREE.BoxGeometry(0.70, 0.15, 1.60), matConcrete);
1311
+ tmBase.position.y = 0.10; tmGroup.add(tmBase);
1312
+ var tmBelt = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.02, 1.30),
1399
1313
  new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.8 }));
1400
- tmBelt.position.y = 0.19;
1401
- tmGroup.add(tmBelt);
1402
- // Handles
1403
- [-0.3, 0.3].forEach(function(hx) {
1404
- var handle = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 1, 6), chromeMat);
1405
- handle.position.set(hx, 0.7, -0.6);
1406
- tmGroup.add(handle);
1314
+ tmBelt.position.y = 0.19; tmGroup.add(tmBelt);
1315
+ [-0.30, 0.30].forEach(function(hx) {
1316
+ var handle = new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 1.00, 6), matChrome);
1317
+ handle.position.set(hx, 0.70, -0.60); tmGroup.add(handle);
1407
1318
  });
1408
- var console2 = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.25, 0.08), darkMat);
1409
- console2.position.set(0, 1.1, -0.65);
1410
- tmGroup.add(console2);
1319
+ var console2 = new THREE.Mesh(new THREE.BoxGeometry(0.50, 0.25, 0.08), matConcrete);
1320
+ console2.position.set(0, 1.10, -0.65); tmGroup.add(console2);
1411
1321
  S.furnitureGroup.add(tmGroup);
1412
-
1413
1322
  // Dumbbell rack
1414
- var rackBase = new THREE.Mesh(new THREE.BoxGeometry(2, 0.8, 0.4), chromeMat);
1415
- rackBase.position.set(x + 1.5, 0.4, z - 3);
1416
- rackBase.castShadow = true;
1323
+ var rackBase = new THREE.Mesh(new THREE.BoxGeometry(2.80, 0.80, 0.42), matChrome);
1324
+ rackBase.position.set(x + 2, 0.40, z - 4);
1417
1325
  S.furnitureGroup.add(rackBase);
1418
- // Dumbbells on rack
1419
- for (var di = 0; di < 5; di++) {
1326
+ for (var di = 0; di < 6; di++) {
1420
1327
  var dbMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5, metalness: 0.4 });
1421
- var db = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.3, 8), dbMat);
1422
- db.position.set(x + 0.7 + di * 0.35, 0.9, z - 3);
1328
+ var db = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 0.30, 8), dbMat);
1329
+ db.position.set(x + 0.8 + di * 0.40, 0.90, z - 4);
1423
1330
  db.rotation.z = Math.PI / 2;
1424
1331
  S.furnitureGroup.add(db);
1425
1332
  }
1426
-
1427
- // Yoga mat area
1333
+ // Yoga mats
1428
1334
  var yogaMat = new THREE.MeshStandardMaterial({ color: 0x7c3aed, roughness: 0.9 });
1429
- var mat = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.02, 1.8), yogaMat);
1430
- mat.position.set(x + 2, 0.02, z + 1);
1431
- S.furnitureGroup.add(mat);
1432
- var mat2 = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.02, 1.8),
1335
+ var mat1 = new THREE.Mesh(new THREE.BoxGeometry(0.80, 0.02, 1.80), yogaMat);
1336
+ mat1.position.set(x + 3, 0.02, z + 2);
1337
+ S.furnitureGroup.add(mat1);
1338
+ var mat2 = new THREE.Mesh(new THREE.BoxGeometry(0.80, 0.02, 1.80),
1433
1339
  new THREE.MeshStandardMaterial({ color: 0x06b6d4, roughness: 0.9 }));
1434
- mat2.position.set(x + 3, 0.02, z + 1);
1340
+ mat2.position.set(x + 4.2, 0.02, z + 2);
1435
1341
  S.furnitureGroup.add(mat2);
1436
-
1437
- // "FITNESS" sign
1438
1342
  var signDiv = document.createElement('div');
1439
1343
  signDiv.textContent = 'FITNESS';
1440
1344
  signDiv.style.cssText = 'color:#ef4444;font-size:10px;font-weight:bold;font-family:Inter,sans-serif;letter-spacing:2px;text-shadow:0 0 8px #ef4444;';
1441
1345
  var sign = new CSS2DObject(signDiv);
1442
- sign.position.set(x, 4, z);
1346
+ sign.position.set(x, 4.0, z);
1443
1347
  S.furnitureGroup.add(sign);
1444
1348
  }
1445
1349
 
1446
- // ==================== PLANTS ====================
1350
+ // ============================================================
1351
+ // PLANTS
1352
+ // ============================================================
1447
1353
  function buildCampusPlants() {
1448
1354
  var plantPositions = [
1449
- [-20, 8], [20, 8], [-20, -5], [20, -5],
1450
- [-8, 10], [8, 10], [-8, -8], [8, -8],
1451
- [0, 10], [-15, 5], [15, 5],
1452
- [-6, -10], [6, -10],
1355
+ [-38, 20], [38, 20], [-38, 0], [38, 0],
1356
+ [-12, 22], [12, 22], [-20, 10], [20, 10],
1357
+ [-20, -10], [20, -10], [0, 24],
1358
+ [-8, -20], [8, -20], [-28, -10], [28, -10],
1453
1359
  ];
1454
1360
  plantPositions.forEach(function(pos) {
1455
1361
  buildLuxuryPlant(pos[0], pos[1]);
1456
1362
  });
1457
-
1458
- // Indoor trees (taller, premium)
1459
- [[-18, 0], [18, 0], [0, -7]].forEach(function(pos) {
1363
+ [[-30, 5], [30, 5], [0, -12], [-15, 20], [15, 20]].forEach(function(pos) {
1460
1364
  buildIndoorTree(pos[0], pos[1]);
1461
1365
  });
1462
1366
  }
@@ -1464,62 +1368,52 @@ function buildCampusPlants() {
1464
1368
  function buildLuxuryPlant(x, z) {
1465
1369
  var group = new THREE.Group();
1466
1370
  group.position.set(x, 0, z);
1467
- // Concrete planter
1468
- var planterMat = new THREE.MeshStandardMaterial({ color: 0x4a4a5a, roughness: 0.6 });
1469
- var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.2, 0.5, 12), planterMat);
1470
- planter.position.y = 0.25; planter.castShadow = true;
1471
- group.add(planter);
1472
- // Lush greenery
1473
- var leafMat = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.8 });
1474
- for (var i = 0; i < 6; i++) {
1475
- var a = (i / 6) * Math.PI * 2;
1371
+ var planterMat = new THREE.MeshStandardMaterial({ color: 0x3a3a4a, roughness: 0.62 });
1372
+ var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.26, 0.20, 0.52, 12), planterMat);
1373
+ planter.position.y = 0.26; group.add(planter);
1374
+ var leafMat = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.80 });
1375
+ for (var i = 0; i < 7; i++) {
1376
+ var a = (i / 7) * Math.PI * 2;
1476
1377
  var leaf = new THREE.Mesh(new THREE.SphereGeometry(0.15, 8, 6), leafMat);
1477
- leaf.position.set(Math.cos(a) * 0.15, 0.6, Math.sin(a) * 0.15);
1478
- leaf.castShadow = true;
1378
+ leaf.position.set(Math.cos(a) * 0.16, 0.62, Math.sin(a) * 0.16);
1479
1379
  group.add(leaf);
1480
1380
  }
1481
1381
  var topLeaf = new THREE.Mesh(new THREE.SphereGeometry(0.12, 8, 6), leafMat);
1482
- topLeaf.position.y = 0.75;
1483
- group.add(topLeaf);
1382
+ topLeaf.position.y = 0.77; group.add(topLeaf);
1484
1383
  S.furnitureGroup.add(group);
1485
1384
  }
1486
1385
 
1487
1386
  function buildIndoorTree(x, z) {
1488
1387
  var group = new THREE.Group();
1489
1388
  group.position.set(x, 0, z);
1490
- // Large planter
1491
- var planterMat = new THREE.MeshStandardMaterial({ color: 0x3a3a4a, roughness: 0.5 });
1492
- var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.4, 0.35, 0.6, 12), planterMat);
1493
- planter.position.y = 0.3; planter.castShadow = true;
1494
- group.add(planter);
1495
- // Trunk
1496
- var trunkMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e, roughness: 0.8 });
1497
- var trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.1, 2.5, 8), trunkMat);
1498
- trunk.position.y = 1.85; trunk.castShadow = true;
1499
- group.add(trunk);
1500
- // Canopy (layered spheres)
1501
- var canopyMat = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.85 });
1502
- var canopyMat2 = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.85 });
1503
- [{ y: 2.8, r: 0.6 }, { y: 3.2, r: 0.5 }, { y: 3.5, r: 0.35 }].forEach(function(c, ci) {
1504
- var canopy = new THREE.Mesh(new THREE.SphereGeometry(c.r, 12, 10), ci % 2 === 0 ? canopyMat : canopyMat2);
1505
- canopy.position.y = c.y;
1506
- canopy.castShadow = true;
1507
- group.add(canopy);
1389
+ var planterMat = new THREE.MeshStandardMaterial({ color: 0x2a2d35, roughness: 0.52 });
1390
+ var planter = new THREE.Mesh(new THREE.CylinderGeometry(0.42, 0.36, 0.64, 12), planterMat);
1391
+ planter.position.y = 0.32; group.add(planter);
1392
+ var trunkMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e, roughness: 0.80 });
1393
+ var trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.11, 2.6, 8), trunkMat);
1394
+ trunk.position.y = 1.92; group.add(trunk);
1395
+ var cm1 = new THREE.MeshStandardMaterial({ color: 0x228B22, roughness: 0.85 });
1396
+ var cm2 = new THREE.MeshStandardMaterial({ color: 0x2d8a4e, roughness: 0.85 });
1397
+ [{ y: 2.9, r: 0.65 }, { y: 3.30, r: 0.52 }, { y: 3.60, r: 0.36 }].forEach(function(c, ci) {
1398
+ var canopy = new THREE.Mesh(new THREE.SphereGeometry(c.r, 12, 10), ci % 2 === 0 ? cm1 : cm2);
1399
+ canopy.position.y = c.y; group.add(canopy);
1508
1400
  });
1509
1401
  S.furnitureGroup.add(group);
1510
1402
  }
1511
1403
 
1512
- // ==================== PENDANT LIGHTS ====================
1404
+ // ============================================================
1405
+ // PENDANT LIGHTS
1406
+ // ============================================================
1513
1407
  function buildPendantLights() {
1514
- var lightPositions = [
1515
- [0, 2], [0, -1], [0, -4],
1516
- [-4.5, 2], [-4.5, -1], [-4.5, -4],
1517
- [4.5, 2], [4.5, -1], [4.5, -4],
1518
- [-12, 1], [-12, -2],
1519
- [12, 5],
1520
- [-14, -12], [0, -12], [14, -12],
1408
+ var positions = [
1409
+ [0, 6], [0, 10], [0, 14], [0, 18],
1410
+ [-8, 6], [-8, 14], [8, 6], [8, 14],
1411
+ [-28, 0], [28, 0],
1412
+ [-28, -18], [0, -18], [28, -18],
1413
+ [30, 10],
1414
+ [0, 24], [-10, 24], [10, 24],
1521
1415
  ];
1522
- lightPositions.forEach(function(pos) {
1416
+ positions.forEach(function(pos) {
1523
1417
  buildPendantLight(pos[0], pos[1]);
1524
1418
  });
1525
1419
  }
@@ -1527,52 +1421,51 @@ function buildPendantLights() {
1527
1421
  function buildPendantLight(x, z) {
1528
1422
  var group = new THREE.Group();
1529
1423
  group.position.set(x, 0, z);
1530
- // Wire
1531
- var wireMat = new THREE.MeshStandardMaterial({ color: 0x333333, roughness: 0.5 });
1532
- var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 1.5, 4), wireMat);
1533
- wire.position.y = WALL_H - 0.75;
1534
- group.add(wire);
1535
- // Shade (industrial style)
1536
- var shadeMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.5, metalness: 0.3, side: THREE.DoubleSide });
1537
- var shade = new THREE.Mesh(new THREE.ConeGeometry(0.25, 0.2, 12, 1, true), shadeMat);
1538
- shade.position.y = WALL_H - 1.55;
1539
- group.add(shade);
1540
- // Warm light
1541
- var light = new THREE.PointLight(0xffeedd, 0.25, 6);
1542
- light.position.set(0, WALL_H - 1.7, 0);
1543
- light.castShadow = false; // performance
1424
+ var wireMat = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, roughness: 0.5 });
1425
+ var wire = new THREE.Mesh(new THREE.CylinderGeometry(0.008, 0.008, 1.60, 4), wireMat);
1426
+ wire.position.y = WALL_H - 0.80; group.add(wire);
1427
+ var shadeMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, roughness: 0.5, metalness: 0.3, side: THREE.DoubleSide });
1428
+ var shade = new THREE.Mesh(new THREE.ConeGeometry(0.26, 0.22, 12, 1, true), shadeMat);
1429
+ shade.position.y = WALL_H - 1.70; group.add(shade);
1430
+ var light = new THREE.PointLight(0xffeedd, 0.22, 6);
1431
+ light.position.y = WALL_H - 1.85;
1432
+ light.castShadow = false;
1544
1433
  group.add(light);
1545
1434
  S.furnitureGroup.add(group);
1546
1435
  }
1547
1436
 
1548
- // ==================== GLASS PARTITIONS ====================
1549
- function buildGlassPartitions(glassMat, frameMat) {
1550
- // Between coder zone and rec area
1551
- var partition1 = new THREE.Mesh(new THREE.PlaneGeometry(14, 2.5), glassMat);
1552
- partition1.position.set(0, 1.25, -7);
1553
- S.furnitureGroup.add(partition1);
1554
- var frame1 = new THREE.Mesh(new THREE.BoxGeometry(14, 0.04, 0.04), frameMat);
1555
- frame1.position.set(0, 2.5, -7);
1556
- S.furnitureGroup.add(frame1);
1557
-
1558
- // Between designer area and main
1559
- var partition2 = new THREE.Mesh(new THREE.PlaneGeometry(10, 2.5), glassMat);
1560
- partition2.position.set(-8, 1.25, 0);
1561
- partition2.rotation.y = Math.PI / 2;
1562
- S.furnitureGroup.add(partition2);
1437
+ // ============================================================
1438
+ // GLASS PARTITIONS
1439
+ // ============================================================
1440
+ function buildGlassPartitions(matGlass, matFrame) {
1441
+ // Between workspace and zone areas
1442
+ var p1 = new THREE.Mesh(new THREE.PlaneGeometry(26, 2.8), matGlass);
1443
+ p1.position.set(0, 1.40, 3.0);
1444
+ S.furnitureGroup.add(p1);
1445
+ var f1 = new THREE.Mesh(new THREE.BoxGeometry(26, 0.04, 0.04), matFrame);
1446
+ f1.position.set(0, 2.82, 3.0);
1447
+ S.furnitureGroup.add(f1);
1448
+ // Between manager office and workspace
1449
+ var p2 = new THREE.Mesh(new THREE.PlaneGeometry(14, 2.8), matGlass);
1450
+ p2.position.set(22, 1.40, 8);
1451
+ p2.rotation.y = Math.PI / 2;
1452
+ S.furnitureGroup.add(p2);
1453
+ var f2 = new THREE.Mesh(new THREE.BoxGeometry(0.04, 0.04, 14), matFrame);
1454
+ f2.position.set(22, 2.82, 8);
1455
+ S.furnitureGroup.add(f2);
1563
1456
  }
1564
1457
 
1565
- // ==================== NEON SIGNS ====================
1566
- function buildNeonSign(text, x, y, z, neonMat) {
1567
- // Glow bar behind text
1568
- var glowBar = new THREE.Mesh(new THREE.BoxGeometry(text.length * 0.4, 0.4, 0.04), neonMat);
1458
+ // ============================================================
1459
+ // NEON SIGNS
1460
+ // ============================================================
1461
+ function buildNeonSign(text, x, y, z, matNeon) {
1462
+ var glowBar = new THREE.Mesh(new THREE.BoxGeometry(text.length * 0.44, 0.40, 0.04), matNeon);
1569
1463
  glowBar.position.set(x, y, z);
1570
1464
  S.furnitureGroup.add(glowBar);
1571
- // CSS label
1572
- var color = '#' + neonMat.color.getHexString();
1465
+ var color = '#' + matNeon.color.getHexString();
1573
1466
  var div = document.createElement('div');
1574
1467
  div.textContent = text;
1575
- div.style.cssText = 'color:' + color + ';font-size:12px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:4px;text-shadow:0 0 12px ' + color + ',0 0 24px ' + color + ';';
1468
+ div.style.cssText = 'color:' + color + ';font-size:13px;font-weight:900;font-family:Inter,sans-serif;letter-spacing:4px;text-shadow:0 0 14px ' + color + ',0 0 28px ' + color + ';';
1576
1469
  var label = new CSS2DObject(div);
1577
1470
  label.position.set(x, y, z + 0.05);
1578
1471
  S.furnitureGroup.add(label);