PageSourceSearch

https://stateloop.ai/src/home.js

js stateloop.ai collected 2026-10-05 15:24:58 UTC 7,760 bytes, 202 lines download raw bytes

1import { HOME, esc } from "./data.js";
2import { siteFooterHTML } from "./footer.js";
3import { figureHTML } from "./figures.js";
4import { mountHeroArena } from "./hero-arena.js";
5import { bindMobileNav } from "./mobile-nav.js";
6import { bindEarlyAccess } from "./early-access.js";
7import { logoHTML } from "./logo.js";
8import {
9  MENU_ICON,
10  bindReveal,
11  bindStickyNav,
12  cardGridHTML,
13  closingHTML,
14  heroHTML,
15  specStripHTML,
16} from "./page-shell.js";
17
18/* Lucide at stroke 1.5, square-cornered to match the blueprint world. */
19const ICONS = {
20  database: `<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14a9 3 0 0 0 18 0V5"/><path d="M3 12a9 3 0 0 0 18 0"/>`,
21  chip: `<rect x="4" y="4" width="16" height="16"/><rect x="9" y="9" width="6" height="6"/><path d="M9 2v2"/><path d="M15 2v2"/><path d="M9 20v2"/><path d="M15 20v2"/><path d="M2 9h2"/><path d="M2 15h2"/><path d="M20 9h2"/><path d="M20 15h2"/>`,
22  clearance: `<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M15 2v5h5"/><path d="m9 15 2 2 4-4"/>`,
23};
24
25const icon = (name, size = 18) =>
26  `<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${ICONS[name]}</svg>`;
27
28function sectionHTML(section, i) {
29  const copy = `
30<div class="cap-copy reveal">
31  <span class="cap-eyebrow">${icon(section.icon)}${esc(section.eyebrow)}</span>
32  <h2>${esc(section.title)}</h2>
33  <p>${esc(section.body)}</p>
34  ${section.link ? `<a class="cap-link" href="${esc(section.link.href)}">${esc(section.link.label)}</a>` : ""}
35</div>`;
36  const fig = figureHTML(section);
37  const flipped = i % 2 === 1;
38  return `
39<section id="${esc(section.id)}" class="cap${flipped ? " is-flipped" : ""}">
40  ${flipped ? fig + copy : copy + fig}
41</section>`;
42}
43
44/** Split a sentence into words that fade up in reading order. */
45function staggered(text) {
46  return text
47    .split(" ")
48    .map((word, i) => `<span class="wd" style="transition-delay:${40 + i * 34}ms">${esc(word)}</span>`)
49    .join(" ");
50}
51
52/** Company homepage (physical AI / foundation models). */
53export function mountHome(root) {
54  let arenaCleanup = null;
55  let unbindReveal = () => {};
56  let unbindNav = () => {};
57  let unbindMobile = () => {};
58  let unbindArenaMq = () => {};
59  let unbindEarlyAccess = () => {};
60  const arenaMq = window.matchMedia("(min-width:901px)");
61
62  function syncArena() {
63    if (arenaCleanup) { arenaCleanup(); arenaCleanup = null; }
64    if (!arenaMq.matches) return;
65    const mount = root.querySelector("[data-hero-arena]");
66    if (mount) arenaCleanup = mountHeroArena(mount);
67  }
68
69  function bindArenaMq() {
70    const onChange = () => syncArena();
71    arenaMq.addEventListener("change", onChange);
72    return () => arenaMq.removeEventListener("change", onChange);
73  }
74
75  function paint() {
76    if (arenaCleanup) { arenaCleanup(); arenaCleanup = null; }
77    unbindReveal();
78    unbindNav();
79    unbindMobile();
80    unbindArenaMq();
81    unbindEarlyAccess();
82
83    root.innerHTML = `
84<div class="home">
85  <!-- .stl-topbar first: the system bar supplies the wordmark's box and its
86       distance from the top of the bar, so it is identical here and on the
87       legal pages. .home-top now only adds what is genuinely marketing
88       behaviour -- sticky, translucent, and the scroll-triggered .is-top
89       transparent state. Before this the two were parallel components and
90       the wordmark came out 20px tall at +24 here against 36px at +16 there. -->
91  <header class="stl-topbar home-top is-top">
92    <div class="wrap home-top-inner">
93      <a class="stl-brand" href="/" aria-label="Stateloop home">
94        ${logoHTML()}
95      </a>
96      <nav class="navlinks home-nav">
97        <a class="lp-link" href="#vision">Vision</a>
98        <a class="lp-link" href="#company">Company</a>
99        <a class="lp-link" href="/careers/senior-ai-engineer/">Careers</a>
100      </nav>
101      <div class="home-top-end">
102        <button type="button" class="btn btn-primary home-top-cta" data-act="early-access">Request early access</button>
103        <button type="button" class="stl-nav-toggle" data-nav-toggle aria-expanded="false" aria-controls="home-mobile-nav" aria-label="Open menu">${MENU_ICON}</button>
104      </div>
105    </div>
106    <nav class="stl-mobile-nav" id="home-mobile-nav" data-nav-panel hidden>
107      <a href="#vision">Vision</a>
108      <a href="#company">Company</a>
109      <a href="/careers/senior-ai-engineer/">Careers</a>
110      <button type="button" data-act="early-access">Request early access</button>
111    </nav>
112  </header>
113
114  ${specStripHTML({
115    left: "Stateloop · Sheet A-01",
116    center: "Grid 24 / 120 · Scale 1:1",
117    right: "Ver 0.1",
118  })}
119
120  ${heroHTML({
121    titleHTML: `
122        <span>Supplying the right data and model</span>
123        <span>for every <em>physical AI</em> task</span>`,
124    lede: "We provide specialized datasets and domain-specific foundation models for physical AI.",
125    actionsHTML: `
126        <button type="button" class="btn btn-primary" data-act="early-access">Request early access</button>`,
127    belowHTML: `
128      <div class="hero-arena reveal" style="transition-delay:240ms" data-hero-arena></div>`,
129  })}
130
131  <section id="vision" class="vision">
132    <div class="sheet vision-inner">
133      <p class="reveal words">${staggered(HOME.vision[1].body)}</p>
134      <p class="reveal words">${staggered(HOME.vision[2].body)}</p>
135    </div>
136  </section>
137
138  <div class="sheet">
139    ${HOME.sections.map(sectionHTML).join("")}
140  </div>
141
142  <section class="teams">
143    <div class="sheet">
144      ${cardGridHTML(HOME.teams.cards, { heading: HOME.teams.heading, headingClass: "teams-head" })}
145    </div>
146  </section>
147
148  <section id="company" class="company">
149    <div class="sheet company-inner">
150      <div class="company-head stl-band stl-prose reveal">
151        <h2>${esc(HOME.company.eyebrow)}</h2>
152        <p>${esc(HOME.company.blurb)}</p>
153      </div>
154      <ul class="company-team reveal" style="transition-delay:90ms">
155        ${HOME.company.team.map((m) => `
156          <li class="person">
157            <span class="person-plate blueprint">
158              <i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
159              <img src="${esc(m.photo)}" alt="${esc(m.name)}" width="96" height="96" loading="lazy" />
160            </span>
161            <span class="person-name">${esc(m.name)}</span>
162            <span class="person-title">${esc(m.title)}</span>
163          </li>`).join("")}
164      </ul>
165      <div class="hiring blueprint reveal" style="transition-delay:140ms">
166        <i class="corner tl"></i><i class="corner tr"></i><i class="corner bl"></i><i class="corner br"></i>
167        <div class="hiring-copy">
168          <h3>${esc(HOME.company.hiring.title)}</h3>
169          <span class="hiring-loc">${esc(HOME.company.hiring.location)}</span>
170        </div>
171        <a class="btn btn-ghost" href="${esc(HOME.company.hiring.href)}">${esc(HOME.company.hiring.cta)} →</a>
172      </div>
173    </div>
174  </section>
175
176  ${closingHTML({
177    title: "Incorporate the right data and foundation models for your physical AI task.",
178    ctaHTML: `<button type="button" class="btn btn-primary" data-act="early-access">Request early access →</button>`,
179  })}
180
181  ${siteFooterHTML()}
182</div>`;
183
184    syncArena();
185    unbindArenaMq = bindArenaMq();
186    unbindReveal = bindReveal(root);
187    unbindNav = bindStickyNav(root);
188    unbindMobile = bindMobileNav(root);
189    unbindEarlyAccess = bindEarlyAccess(root);
190  }
191
192  paint();
193
194  return () => {
195    if (arenaCleanup) { arenaCleanup(); arenaCleanup = null; }
196    unbindReveal();
197    unbindNav();
198    unbindMobile();
199    unbindArenaMq();
200    unbindEarlyAccess();
201  };
202}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.