Efradeca commited on
Commit
d2a711c
·
verified ·
1 Parent(s): 9c7ca7c

chore: deploy private lightloom build

Browse files
frontend/css/immersive.css CHANGED
@@ -313,3 +313,31 @@ body.in-world .controls .ctl:nth-child(5) { animation-delay: 0.32s; }
313
  .status--work::after, .mic.is-recording { animation: none !important; }
314
  *, *::before, *::after { transition-duration: 0.01ms !important; }
315
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  .status--work::after, .mic.is-recording { animation: none !important; }
314
  *, *::before, *::after { transition-duration: 0.01ms !important; }
315
  }
316
+
317
+ /* ---- IF-TIME polish: wordmark · title-card · entrance fades · contrast ---- */
318
+ /* 8 · the wordmark reads as a logo: gilded LOOM + a glowing projector dot */
319
+ .hud__brand { display: inline-flex; align-items: center; letter-spacing: 0.2em; opacity: 0.96; }
320
+ .hud__brand b { color: var(--amber); font-weight: 700; }
321
+ .hud__brand::before { content: ""; width: 6px; height: 6px; border-radius: 50%; margin-right: 9px; flex: none;
322
+ background: var(--amber); box-shadow: 0 0 9px var(--amber), 0 0 3px var(--amber); }
323
+
324
+ /* 9 · the Director NAMING your world is a cinematic title card: tracking-in + a growing gold filet */
325
+ .world-title { letter-spacing: 0.3em; transition: opacity 0.8s ease, transform 0.8s ease, letter-spacing 0.8s var(--ease); }
326
+ .world-title.is-on { letter-spacing: 0.015em; }
327
+ .world-title::after { content: ""; display: block; height: 1px; width: 0; margin: 7px auto 0;
328
+ background: linear-gradient(90deg, transparent, var(--amber), transparent); transition: width 0.8s var(--ease) 0.12s; }
329
+ .world-title.is-on::after { width: 130px; }
330
+
331
+ /* 11 · keepsake + explore cross-fade in (the panel already pops); stagger the keepsake text */
332
+ .keepsake, .explore3d { animation: llFade 0.42s ease both; }
333
+ .keepsake__title { animation: llRise 0.5s var(--ease) both 0.12s; }
334
+ .keepsake__caption { animation: llRise 0.5s var(--ease) both 0.2s; }
335
+ .keepsake__notes { animation: llRise 0.5s var(--ease) both 0.28s; }
336
+
337
+ /* 13 · idle orchestra labels crisp on a big projector, not muddy */
338
+ .orch { color: rgba(241, 234, 217, 0.52); }
339
+
340
+ @media (prefers-reduced-motion: reduce) {
341
+ .keepsake, .explore3d, .keepsake__title, .keepsake__caption, .keepsake__notes {
342
+ animation: none !important; opacity: 1 !important; transform: none !important; }
343
+ }
frontend/index.html CHANGED
@@ -17,6 +17,9 @@
17
  name="description"
18
  content="Speak, and your world unrolls ahead of you. Lightloom turns your voice into an endless, explorable 3D world — tiny local models listen, direct, paint and give depth to every scene in real time."
19
  />
 
 
 
20
  <link rel="stylesheet" href="/frontend/css/tokens.css" />
21
  <link rel="stylesheet" href="/frontend/css/immersive.css" />
22
  <script type="importmap">
@@ -31,7 +34,7 @@
31
 
32
  <!-- Top HUD: brand + the live orchestra (which tiny model is working now) + counter. -->
33
  <header id="hud" class="hud">
34
- <span class="hud__brand">LIGHTLOOM</span>
35
  <nav class="orchestra" aria-label="Model orchestra">
36
  <span class="orch" id="orch-asr"><i></i>Voice</span>
37
  <span class="orch" id="orch-director"><i></i>Director</span>
 
17
  name="description"
18
  content="Speak, and your world unrolls ahead of you. Lightloom turns your voice into an endless, explorable 3D world — tiny local models listen, direct, paint and give depth to every scene in real time."
19
  />
20
+ <!-- Preload the self-hosted hero fonts so the title card / wordmark never FOUT-flash Georgia first. -->
21
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/Fraunces-Variable.woff2" />
22
+ <link rel="preload" as="font" type="font/woff2" crossorigin href="/assets/fonts/Inter-Variable.woff2" />
23
  <link rel="stylesheet" href="/frontend/css/tokens.css" />
24
  <link rel="stylesheet" href="/frontend/css/immersive.css" />
25
  <script type="importmap">
 
34
 
35
  <!-- Top HUD: brand + the live orchestra (which tiny model is working now) + counter. -->
36
  <header id="hud" class="hud">
37
+ <span class="hud__brand">LIGHT<b>LOOM</b></span>
38
  <nav class="orchestra" aria-label="Model orchestra">
39
  <span class="orch" id="orch-asr"><i></i>Voice</span>
40
  <span class="orch" id="orch-director"><i></i>Director</span>
frontend/js/controller.js CHANGED
@@ -700,7 +700,17 @@ const controller = {
700
 
701
  _about(show) {
702
  const el = document.getElementById("about");
703
- if (el) el.hidden = !show;
 
 
 
 
 
 
 
 
 
 
704
  },
705
 
706
  setReducedMotion(on, fromOS) {
@@ -712,20 +722,12 @@ const controller = {
712
  if (!fromOS) try { localStorage.setItem("ll.rm", this.reducedMotion ? "1" : "0"); } catch (_) {}
713
  },
714
 
715
- // English-only app (the hackathon is judged in English): the language is locked to "en".
716
- setLang() {
717
- this.lang = "en";
718
- this._applyLang("en");
719
- },
720
-
721
  _applyLang(lang) {
722
  document.documentElement.setAttribute("lang", lang);
723
  document.querySelectorAll("[data-en],[data-es]").forEach((n) => {
724
  if (n.hasAttribute("data-en") && !n.hasAttribute("data-es")) n.hidden = lang !== "en";
725
  else if (n.hasAttribute("data-es") && !n.hasAttribute("data-en")) n.hidden = lang !== "es";
726
  });
727
- const lb = document.getElementById("lang-btn");
728
- if (lb) lb.textContent = lang.toUpperCase();
729
  this._setCount();
730
  },
731
 
 
700
 
701
  _about(show) {
702
  const el = document.getElementById("about");
703
+ if (!el) return;
704
+ el.hidden = !show;
705
+ if (show) {
706
+ this._aboutReturn = document.activeElement;
707
+ const c = document.getElementById("about-close"); if (c) { try { c.focus(); } catch (_) {} }
708
+ this._aboutKey = (e) => { if (e.key === "Escape") this._about(false); };
709
+ document.addEventListener("keydown", this._aboutKey);
710
+ } else {
711
+ if (this._aboutKey) { document.removeEventListener("keydown", this._aboutKey); this._aboutKey = null; }
712
+ const r = this._aboutReturn; if (r && r.focus) { try { r.focus(); } catch (_) {} } // restore focus to the opener
713
+ }
714
  },
715
 
716
  setReducedMotion(on, fromOS) {
 
722
  if (!fromOS) try { localStorage.setItem("ll.rm", this.reducedMotion ? "1" : "0"); } catch (_) {}
723
  },
724
 
 
 
 
 
 
 
725
  _applyLang(lang) {
726
  document.documentElement.setAttribute("lang", lang);
727
  document.querySelectorAll("[data-en],[data-es]").forEach((n) => {
728
  if (n.hasAttribute("data-en") && !n.hasAttribute("data-es")) n.hidden = lang !== "en";
729
  else if (n.hasAttribute("data-es") && !n.hasAttribute("data-en")) n.hidden = lang !== "es";
730
  });
 
 
731
  this._setCount();
732
  },
733