privateboard 0.1.37 → 0.1.40

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 (76) hide show
  1. package/dist/boot.js +1415 -91
  2. package/dist/boot.js.map +1 -1
  3. package/dist/cli.js +1415 -91
  4. package/dist/cli.js.map +1 -1
  5. package/dist/server.js +1271 -81
  6. package/dist/server.js.map +1 -1
  7. package/dist/version.d.ts +1 -1
  8. package/dist/version.js +1 -1
  9. package/dist/version.js.map +1 -1
  10. package/package.json +1 -1
  11. package/public/__avatar3d_test.html +156 -0
  12. package/public/adjourn-overlay.css +2 -2
  13. package/public/agent-overlay.css +27 -15
  14. package/public/agent-overlay.js +3 -1
  15. package/public/agent-profile.css +331 -41
  16. package/public/agent-profile.js +499 -75
  17. package/public/app-updater.css +1 -1
  18. package/public/app.js +2090 -547
  19. package/public/avatar-3d-snap.js +205 -0
  20. package/public/avatar-3d.js +792 -0
  21. package/public/avatar-customizer.html +274 -0
  22. package/public/avatar3d-editor.css +240 -0
  23. package/public/avatar3d-editor.js +481 -0
  24. package/public/avatars/3d/chair.png +0 -0
  25. package/public/avatars/3d/first-principles.png +0 -0
  26. package/public/avatars/3d/historian.png +0 -0
  27. package/public/avatars/3d/long-horizon.png +0 -0
  28. package/public/avatars/3d/phenomenologist.png +0 -0
  29. package/public/avatars/3d/socrates.png +0 -0
  30. package/public/avatars/3d/user-empathy.png +0 -0
  31. package/public/avatars/3d/value-investor.png +0 -0
  32. package/public/core-avatars.js +86 -0
  33. package/public/home-3d-loader.js +15 -4
  34. package/public/home-3d-mock.js +18 -7
  35. package/public/home.html +80 -18
  36. package/public/i18n.js +279 -4
  37. package/public/icons/avatar_1779855104027.glb +0 -0
  38. package/public/icons/logo.png +0 -0
  39. package/public/icons/new-style.glb +0 -0
  40. package/public/icons/new-style2.glb +0 -0
  41. package/public/icons/new-style3.glb +0 -0
  42. package/public/icons/new-style4.glb +0 -0
  43. package/public/icons/new-style5.glb +0 -0
  44. package/public/icons/office.glb +0 -0
  45. package/public/icons/stuff.glb +0 -0
  46. package/public/index.html +203 -182
  47. package/public/mention-picker.js +1 -1
  48. package/public/new-agent.css +7 -7
  49. package/public/new-agent.js +46 -20
  50. package/public/office-viewer.html +340 -0
  51. package/public/onboarding.css +5 -5
  52. package/public/quote-cta.css +5 -4
  53. package/public/quote-cta.js +50 -5
  54. package/public/room-settings.css +24 -9
  55. package/public/stuff-viewer.html +330 -0
  56. package/public/thread.css +1211 -0
  57. package/public/user-settings.css +16 -19
  58. package/public/user-settings.js +86 -78
  59. package/public/vendor/BufferGeometryUtils.js +1434 -0
  60. package/public/vendor/DRACOLoader.js +739 -0
  61. package/public/vendor/GLTFLoader.js +4860 -0
  62. package/public/vendor/RoomEnvironment.js +185 -0
  63. package/public/vendor/SkeletonUtils.js +496 -0
  64. package/public/vendor/draco/draco_decoder.js +34 -0
  65. package/public/vendor/draco/draco_decoder.wasm +0 -0
  66. package/public/vendor/draco/draco_encoder.js +33 -0
  67. package/public/vendor/draco/draco_wasm_wrapper.js +117 -0
  68. package/public/vendor/meshopt_decoder.module.js +196 -0
  69. package/public/voice-3d-banner.js +12 -0
  70. package/public/voice-3d.js +1407 -432
  71. package/public/voice-clone.css +875 -0
  72. package/public/voice-clone.js +1351 -0
  73. package/public/voice-replay.css +3 -3
  74. package/public/voice-replay.js +21 -0
  75. package/public/avatar-skill.js +0 -629
  76. package/public/icons/folded-sidebar.png +0 -0
@@ -41,13 +41,12 @@ const CAST = [
41
41
  { id: "phenomenologist", name: "Phenomenologist", avatarPath: "/avatars/phenomenologist.svg", roleKind: "director" },
42
42
  ];
43
43
 
44
- /* The 5 tone-keyed wall variants in voice-3d.js. "brainstorm" gives
45
- us the red-brick + stone-band + moss painted mural — the most
46
- characterful of the five, and the one the user explicitly asked
47
- for as the homepage scene. Painted brick reads warm against the
48
- page's dark theme, the moss + plants ground the room visually,
49
- and the green floor (#5E6B47) gives the table a planted feel
50
- instead of the cleaner library look "research" had. */
44
+ /* Which of the five tone-keyed rooms (voice-3d.js) the homepage hero
45
+ shows. This is the FINAL homepage scene · "brainstorm" — the cozy
46
+ modern interior (daylight windows + framed art on cream walls, a real
47
+ 3D oak chest + sage sofa) on the warm-oak plank floor (set on
48
+ `.hero-3d-stage` in home.html). All five rooms were redesigned +
49
+ signed off; brainstorm is the one we showcase here. */
51
50
  const DEFAULT_MODE = "brainstorm";
52
51
 
53
52
  /* Director rotation cadence. Long enough for the eye to register
@@ -123,6 +122,18 @@ export function startMockDriver() {
123
122
  const VS3D = window.VoiceStage3D;
124
123
  if (!VS3D || typeof VS3D.update !== "function") return () => {};
125
124
 
125
+ // Decorate CAST with the canonical avatar3d configs · without this
126
+ // the chair + directors render as RNG-drawn random faces instead of
127
+ // the seeded looks the user sees in the in-app voice room. Source:
128
+ // window.PB_CORE_AVATARS (public/core-avatars.js, loaded by home.html
129
+ // before this driver). Idempotent · re-walks on every driver start.
130
+ if (window.PB_CORE_AVATARS) {
131
+ for (const m of CAST) {
132
+ const cfg = window.PB_CORE_AVATARS[m.id];
133
+ if (cfg && !m.avatar3d) m.avatar3d = cfg;
134
+ }
135
+ }
136
+
126
137
  const positions = computeSeatPositions(CAST);
127
138
  // Director ids only · the chair never speaks in this rotation
128
139
  // (chairs in the real app intervene at round boundaries; the
package/public/home.html CHANGED
@@ -659,8 +659,8 @@
659
659
  height: 64px;
660
660
  image-rendering: pixelated;
661
661
  image-rendering: crisp-edges;
662
- background: var(--panel-2);
663
- border: 0.5px solid var(--line);
662
+ background: transparent;
663
+ border: 0;
664
664
  flex-shrink: 0;
665
665
  margin-bottom: 4px;
666
666
  }
@@ -1382,8 +1382,13 @@
1382
1382
  the brainstorm vars directly onto the stage host here. SVG
1383
1383
  data URI is copied verbatim from
1384
1384
  index.html:8674 (brainstorm tile). */
1385
- --floor-bg: #5E6B47;
1386
- --floor-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='128' height='128' shape-rendering='crispEdges'><rect width='128' height='128' fill='%235E6B47'/><rect x='0' y='8' width='20' height='16' fill='%234F5C3D'/><rect x='104' y='20' width='20' height='20' fill='%234F5C3D'/><rect x='88' y='64' width='24' height='16' fill='%234F5C3D'/><rect x='0' y='64' width='12' height='20' fill='%234F5C3D'/><rect x='44' y='64' width='16' height='8' fill='%234F5C3D'/><rect x='72' y='8' width='12' height='8' fill='%236E7A52'/><rect x='120' y='80' width='8' height='12' fill='%236E7A52'/><rect x='48' y='84' width='8' height='8' fill='%236E7A52'/><rect x='48' y='24' width='32' height='32' fill='%235C4838'/><rect x='44' y='28' width='4' height='24' fill='%235C4838'/><rect x='80' y='28' width='4' height='24' fill='%235C4838'/><rect x='52' y='20' width='24' height='4' fill='%235C4838'/><rect x='52' y='56' width='20' height='4' fill='%235C4838'/><rect x='56' y='16' width='12' height='4' fill='%235C4838'/><rect x='56' y='32' width='8' height='8' fill='%234A3A28'/><rect x='68' y='40' width='4' height='8' fill='%234A3A28'/><rect x='60' y='24' width='4' height='4' fill='%236E5A48'/><rect x='68' y='48' width='4' height='4' fill='%236E5A48'/><rect x='58' y='44' width='2' height='2' fill='%236B6258'/><rect x='72' y='32' width='2' height='2' fill='%236B6258'/><rect x='50' y='38' width='2' height='2' fill='%236B6258'/><rect x='16' y='96' width='24' height='20' fill='%235C4838'/><rect x='12' y='100' width='4' height='12' fill='%235C4838'/><rect x='40' y='100' width='4' height='12' fill='%235C4838'/><rect x='20' y='92' width='16' height='4' fill='%235C4838'/><rect x='22' y='104' width='8' height='4' fill='%234A3A28'/><rect x='20' y='100' width='4' height='4' fill='%236E5A48'/><rect x='24' y='108' width='2' height='2' fill='%236B6258'/><rect x='32' y='98' width='2' height='2' fill='%236B6258'/><rect x='8' y='40' width='1' height='2' fill='%238FA068'/><rect x='24' y='72' width='1' height='2' fill='%238FA068'/><rect x='104' y='56' width='1' height='2' fill='%238FA068'/><rect x='120' y='104' width='1' height='2' fill='%238FA068'/><rect x='88' y='44' width='1' height='2' fill='%238FA068'/><rect x='4' y='88' width='1' height='2' fill='%238FA068'/><rect x='64' y='88' width='1' height='2' fill='%238FA068'/><rect x='92' y='24' width='1' height='2' fill='%238FA068'/><rect x='44' y='62' width='1' height='2' fill='%238FA068'/><rect x='80' y='120' width='1' height='2' fill='%238FA068'/><rect x='12' y='12' width='1' height='2' fill='%238FA068'/><rect x='112' y='60' width='1' height='2' fill='%238FA068'/></svg>");
1385
+ /* FINAL · matched to DEFAULT_MODE "brainstorm" in home-3d-mock.js.
1386
+ The warm-oak plank floor (mirrors index.html's
1387
+ `.roundtable-stage[data-floor="brainstorm"]`) under the cozy
1388
+ modern-interior walls — directors in a warm furnished lounge.
1389
+ This is the homepage's showcase scene. */
1390
+ --floor-bg: #C8A877;
1391
+ --floor-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='128' height='64' shape-rendering='crispEdges'><rect width='128' height='64' fill='%23C8A877'/><rect y='16' width='128' height='16' fill='%23BE9E6C'/><rect y='48' width='128' height='16' fill='%23BE9E6C'/><rect y='0' width='128' height='1' fill='%23D8BC8C'/><rect y='16' width='128' height='1' fill='%23D8BC8C'/><rect y='32' width='128' height='1' fill='%23D8BC8C'/><rect y='48' width='128' height='1' fill='%23D8BC8C'/><rect y='15' width='128' height='1' fill='%239C7E50'/><rect y='31' width='128' height='1' fill='%239C7E50'/><rect y='47' width='128' height='1' fill='%239C7E50'/><rect y='63' width='128' height='1' fill='%239C7E50'/><rect x='40' y='0' width='1' height='16' fill='%239C7E50'/><rect x='96' y='16' width='1' height='16' fill='%239C7E50'/><rect x='20' y='32' width='1' height='16' fill='%239C7E50'/><rect x='72' y='48' width='1' height='16' fill='%239C7E50'/><rect x='10' y='6' width='14' height='1' fill='%23B8945E'/><rect x='60' y='22' width='18' height='1' fill='%23B8945E'/><rect x='100' y='40' width='14' height='1' fill='%23B8945E'/><rect x='30' y='56' width='16' height='1' fill='%23B8945E'/></svg>");
1387
1392
  }
1388
1393
  .hero-3d-stage canvas {
1389
1394
  width: 100% !important;
@@ -1414,12 +1419,55 @@
1414
1419
  }
1415
1420
  .hero-3d-hint.is-visible { opacity: 0.85; }
1416
1421
 
1422
+ /* Loading veil · covers the stage while the 3D bundle downloads +
1423
+ mounts. The static poster is the intended cover for that gap, but
1424
+ when it's absent the section would sit blank during the ~500KB
1425
+ download — so the veil fills it with a spinner + caption. Revealed
1426
+ + torn down by home-3d-loader.js; only shown on devices that
1427
+ actually load 3D (no perpetual spinner on skip). z-index above the
1428
+ poster/stage so it covers both until `[data-mounted]` fades it. */
1429
+ .hero-3d-loading {
1430
+ position: absolute;
1431
+ inset: 0;
1432
+ z-index: 3;
1433
+ display: flex;
1434
+ flex-direction: column;
1435
+ align-items: center;
1436
+ justify-content: center;
1437
+ gap: 14px;
1438
+ background: var(--panel-2);
1439
+ transition: opacity 0.4s ease-out;
1440
+ }
1441
+ .hero-3d-loading[hidden] { display: none; }
1442
+ .hero-3d[data-mounted] .hero-3d-loading {
1443
+ opacity: 0;
1444
+ pointer-events: none;
1445
+ }
1446
+ .hero-3d-spinner {
1447
+ width: 26px;
1448
+ height: 26px;
1449
+ border: 2px solid var(--line-bright);
1450
+ border-top-color: var(--lime);
1451
+ border-radius: 50%;
1452
+ animation: hero-3d-spin 0.8s linear infinite;
1453
+ }
1454
+ @keyframes hero-3d-spin { to { transform: rotate(360deg); } }
1455
+ .hero-3d-loading-label {
1456
+ font-family: var(--mono, "Inter", system-ui, sans-serif);
1457
+ font-size: 10px;
1458
+ letter-spacing: 0.16em;
1459
+ text-transform: uppercase;
1460
+ color: var(--text-faint);
1461
+ }
1462
+
1417
1463
  /* `prefers-reduced-motion` · the loader honours this and never
1418
1464
  mounts the scene · the poster stays. Skip CSS transitions too so
1419
1465
  nothing animates if the loader fails halfway and toggles classes. */
1420
1466
  @media (prefers-reduced-motion: reduce) {
1421
1467
  .hero-3d-poster,
1422
- .hero-3d-hint { transition: none; }
1468
+ .hero-3d-hint,
1469
+ .hero-3d-loading { transition: none; }
1470
+ .hero-3d-spinner { animation: none; }
1423
1471
  }
1424
1472
 
1425
1473
  /* Narrow viewports (phone) · the round table compresses into a
@@ -1622,7 +1670,7 @@
1622
1670
 
1623
1671
  <div class="hero-actions">
1624
1672
  <a href="#install" class="big-btn primary">[ ◆ Convene a Room ]</a>
1625
- <a href="https://github.com/kaysaith1900/privateboard/releases/download/v0.1.36/PrivateBoard-0.1.36-arm64.dmg"
1673
+ <a href="https://github.com/kaysaith1900/privateboard/releases/download/v0.1.40/PrivateBoard-0.1.40-arm64.dmg"
1626
1674
  class="big-btn secondary"
1627
1675
  download
1628
1676
  aria-label="Download PrivateBoard for macOS (Apple Silicon)"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 3v12"/><polyline points="6 11 12 17 18 11"/><path d="M5 21h14"/></svg> Download for Mac</a>
@@ -1636,7 +1684,7 @@
1636
1684
  <div class="cast-card" data-agent="socrates">
1637
1685
  <div class="cast-card-frame">
1638
1686
  <span class="cc-corner cc-tl"></span><span class="cc-corner cc-br"></span>
1639
- <img src="avatars/socrates.svg" alt="Socrates">
1687
+ <img src="avatars/3d/socrates.png" alt="Socrates">
1640
1688
  </div>
1641
1689
  <div class="cast-card-name">Socrates</div>
1642
1690
  <div class="cast-card-tag">// skeptic</div>
@@ -1644,7 +1692,7 @@
1644
1692
  <div class="cast-card" data-agent="first-principles">
1645
1693
  <div class="cast-card-frame">
1646
1694
  <span class="cc-corner cc-tl"></span><span class="cc-corner cc-br"></span>
1647
- <img src="avatars/first-principles.svg" alt="First Principles">
1695
+ <img src="avatars/3d/first-principles.png" alt="First Principles">
1648
1696
  </div>
1649
1697
  <div class="cast-card-name">First Principles</div>
1650
1698
  <div class="cast-card-tag">// physicist</div>
@@ -1652,7 +1700,7 @@
1652
1700
  <div class="cast-card" data-agent="value-investor">
1653
1701
  <div class="cast-card-frame">
1654
1702
  <span class="cc-corner cc-tl"></span><span class="cc-corner cc-br"></span>
1655
- <img src="avatars/value-investor.svg" alt="Value Investor">
1703
+ <img src="avatars/3d/value-investor.png" alt="Value Investor">
1656
1704
  </div>
1657
1705
  <div class="cast-card-name">Value Investor</div>
1658
1706
  <div class="cast-card-tag">// patterns</div>
@@ -1660,7 +1708,7 @@
1660
1708
  <div class="cast-card" data-agent="user-empathy">
1661
1709
  <div class="cast-card-frame">
1662
1710
  <span class="cc-corner cc-tl"></span><span class="cc-corner cc-br"></span>
1663
- <img src="avatars/user-empathy.svg" alt="User-Empathy">
1711
+ <img src="avatars/3d/user-empathy.png" alt="User-Empathy">
1664
1712
  </div>
1665
1713
  <div class="cast-card-name">User-Empathy</div>
1666
1714
  <div class="cast-card-tag">// advocate</div>
@@ -1668,7 +1716,7 @@
1668
1716
  <div class="cast-card" data-agent="long-horizon">
1669
1717
  <div class="cast-card-frame">
1670
1718
  <span class="cc-corner cc-tl"></span><span class="cc-corner cc-br"></span>
1671
- <img src="avatars/long-horizon.svg" alt="Long Horizon">
1719
+ <img src="avatars/3d/long-horizon.png" alt="Long Horizon">
1672
1720
  </div>
1673
1721
  <div class="cast-card-name">Long Horizon</div>
1674
1722
  <div class="cast-card-tag">// strategist</div>
@@ -1676,7 +1724,7 @@
1676
1724
  <div class="cast-card" data-agent="phenomenologist">
1677
1725
  <div class="cast-card-frame">
1678
1726
  <span class="cc-corner cc-tl"></span><span class="cc-corner cc-br"></span>
1679
- <img src="avatars/phenomenologist.svg" alt="Phenomenologist">
1727
+ <img src="avatars/3d/phenomenologist.png" alt="Phenomenologist">
1680
1728
  </div>
1681
1729
  <div class="cast-card-name">Phenomenologist</div>
1682
1730
  <div class="cast-card-tag">// observer</div>
@@ -1816,6 +1864,14 @@
1816
1864
  </picture>
1817
1865
  <div class="hero-3d-stage" data-hero-3d-stage hidden></div>
1818
1866
  <div class="hero-3d-hint" data-hero-3d-hint hidden>drag to look around</div>
1867
+ <!-- Loading veil · home-3d-loader.js reveals this the moment it
1868
+ commits to downloading the 3D bundle (~500KB three.js +
1869
+ voice-3d) and tears it down on mount / failure. Hidden by
1870
+ default so devices that skip 3D never see a spinner. -->
1871
+ <div class="hero-3d-loading" data-hero-3d-loading hidden aria-hidden="true">
1872
+ <span class="hero-3d-spinner"></span>
1873
+ <span class="hero-3d-loading-label">Rendering the round table…</span>
1874
+ </div>
1819
1875
  </div>
1820
1876
  </section>
1821
1877
 
@@ -1833,42 +1889,42 @@
1833
1889
  <!-- LEFT · the standing six in a 3×2 grid (display-only) -->
1834
1890
  <div class="cast-directors">
1835
1891
  <div class="bento-cell bento-director" data-agent="socrates">
1836
- <img class="bento-director-img" src="avatars/socrates.svg" alt="Socrates">
1892
+ <img class="bento-director-img" src="avatars/3d/socrates.png" alt="Socrates">
1837
1893
  <div class="bento-director-tag">// skeptic</div>
1838
1894
  <div class="bento-director-name">Socrates</div>
1839
1895
  <div class="bento-director-desc">Three layers deep on every assumption.</div>
1840
1896
  <div class="bento-director-model">claude-opus-4.7</div>
1841
1897
  </div>
1842
1898
  <div class="bento-cell bento-director" data-agent="first-principles">
1843
- <img class="bento-director-img" src="avatars/first-principles.svg" alt="First Principles">
1899
+ <img class="bento-director-img" src="avatars/3d/first-principles.png" alt="First Principles">
1844
1900
  <div class="bento-director-tag">// physicist</div>
1845
1901
  <div class="bento-director-name">First Principles</div>
1846
1902
  <div class="bento-director-desc">Strips problems to their primitives.</div>
1847
1903
  <div class="bento-director-model">gpt-5.5-pro</div>
1848
1904
  </div>
1849
1905
  <div class="bento-cell bento-director" data-agent="value-investor">
1850
- <img class="bento-director-img" src="avatars/value-investor.svg" alt="Value Investor">
1906
+ <img class="bento-director-img" src="avatars/3d/value-investor.png" alt="Value Investor">
1851
1907
  <div class="bento-director-tag">// patterns</div>
1852
1908
  <div class="bento-director-name">Value Investor</div>
1853
1909
  <div class="bento-director-desc">Reads every judgment through a ten-year lens.</div>
1854
1910
  <div class="bento-director-model">gemini-3.1-pro</div>
1855
1911
  </div>
1856
1912
  <div class="bento-cell bento-director" data-agent="user-empathy">
1857
- <img class="bento-director-img" src="avatars/user-empathy.svg" alt="User-Empathy">
1913
+ <img class="bento-director-img" src="avatars/3d/user-empathy.png" alt="User-Empathy">
1858
1914
  <div class="bento-director-tag">// advocate</div>
1859
1915
  <div class="bento-director-name">User-Empathy</div>
1860
1916
  <div class="bento-director-desc">Asks why anyone would actually use this.</div>
1861
1917
  <div class="bento-director-model">grok-4.3</div>
1862
1918
  </div>
1863
1919
  <div class="bento-cell bento-director" data-agent="long-horizon">
1864
- <img class="bento-director-img" src="avatars/long-horizon.svg" alt="Long Horizon">
1920
+ <img class="bento-director-img" src="avatars/3d/long-horizon.png" alt="Long Horizon">
1865
1921
  <div class="bento-director-tag">// strategist</div>
1866
1922
  <div class="bento-director-name">Long Horizon</div>
1867
1923
  <div class="bento-director-desc">Reads everything on a hundred-year scale.</div>
1868
1924
  <div class="bento-director-model">deepseek-v4-pro</div>
1869
1925
  </div>
1870
1926
  <div class="bento-cell bento-director" data-agent="phenomenologist">
1871
- <img class="bento-director-img" src="avatars/phenomenologist.svg" alt="Phenomenologist">
1927
+ <img class="bento-director-img" src="avatars/3d/phenomenologist.png" alt="Phenomenologist">
1872
1928
  <div class="bento-director-tag">// observer</div>
1873
1929
  <div class="bento-director-name">Phenomenologist</div>
1874
1930
  <div class="bento-director-desc">Begins from experience itself.</div>
@@ -2226,6 +2282,12 @@
2226
2282
  `voice-3d.js` (which downloads three.module.min.js + OrbitControls,
2227
2283
  ~488 KB total). Visitors who can't or shouldn't run WebGL stay on
2228
2284
  the poster and never download the 3D bundle. -->
2285
+ <!-- Canonical chair + director avatar3d configs · populated onto
2286
+ window.PB_CORE_AVATARS so the dynamically-imported home-3d-mock.js
2287
+ can decorate its CAST with the same look the in-app voice room
2288
+ and `src/seed/*.ts` ship. Classic-script defer · evaluates before
2289
+ the ESM home-3d-loader's `import('/home-3d-mock.js')` resolves. -->
2290
+ <script src="core-avatars.js" defer></script>
2229
2291
  <script type="module" src="home-3d-loader.js" defer></script>
2230
2292
 
2231
2293
  </body>