pngwn Claude Opus 4.8 (1M context) commited on
Commit
560169d
·
1 Parent(s): 58d9edc

Add partner logos, nav popout menu, FAQ updates; fix scroll-spy

Browse files

- Replace generic partner labels with brand logos (LFS-tracked PNGs + Cohere SVG)
- Add responsive nav popout menu; fix top-nav scroll-spy jumping
- Expand FAQ content; remove buggy scroll-reveal animation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

.gitattributes ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.png filter=lfs diff=lfs merge=lfs -text
23
+ *.pt filter=lfs diff=lfs merge=lfs -text
24
+ *.pth filter=lfs diff=lfs merge=lfs -text
25
+ *.rar filter=lfs diff=lfs merge=lfs -text
26
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
27
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
29
+ *.tar filter=lfs diff=lfs merge=lfs -text
30
+ *.tflite filter=lfs diff=lfs merge=lfs -text
31
+ *.tgz filter=lfs diff=lfs merge=lfs -text
32
+ *.wasm filter=lfs diff=lfs merge=lfs -text
33
+ *.xz filter=lfs diff=lfs merge=lfs -text
34
+ *.zip filter=lfs diff=lfs merge=lfs -text
35
+ *.zst filter=lfs diff=lfs merge=lfs -text
36
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
src/lib/actions/reveal.ts DELETED
@@ -1,25 +0,0 @@
1
- /* Scroll-reveal action — adds `.in` when the element enters the viewport.
2
- Apply alongside the `reveal` class, e.g. <div class="reveal" use:reveal>. */
3
- import type { Action } from 'svelte/action';
4
-
5
- export const reveal: Action<HTMLElement, { delay?: number } | undefined> = (node, params) => {
6
- const delay = params?.delay ?? 0;
7
- if (delay) node.style.transitionDelay = `${delay}ms`;
8
-
9
- const io = new IntersectionObserver(
10
- ([entry]) => {
11
- if (entry.isIntersecting) {
12
- node.classList.add('in');
13
- io.disconnect();
14
- }
15
- },
16
- { threshold: 0.12, rootMargin: '0px 0px -8% 0px' }
17
- );
18
- io.observe(node);
19
-
20
- return {
21
- destroy() {
22
- io.disconnect();
23
- }
24
- };
25
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/components/BigIdea.svelte CHANGED
@@ -1,13 +1,12 @@
1
  <script lang="ts">
2
  import { EVENT } from '$lib/data/content';
3
  import TopoBackdrop from './TopoBackdrop.svelte';
4
- import { reveal } from '$lib/actions/reveal';
5
  </script>
6
 
7
  <section class="section on-dark" id="idea">
8
  <TopoBackdrop opacity={0.1} stroke="rgba(244,238,225,.5)" />
9
  <div class="wrap" style="position:relative">
10
- <div class="reveal" use:reveal>
11
  <span class="eyebrow">The big idea</span>
12
  <p
13
  class="display"
 
1
  <script lang="ts">
2
  import { EVENT } from '$lib/data/content';
3
  import TopoBackdrop from './TopoBackdrop.svelte';
 
4
  </script>
5
 
6
  <section class="section on-dark" id="idea">
7
  <TopoBackdrop opacity={0.1} stroke="rgba(244,238,225,.5)" />
8
  <div class="wrap" style="position:relative">
9
+ <div>
10
  <span class="eyebrow">The big idea</span>
11
  <p
12
  class="display"
src/lib/components/Icon.svelte CHANGED
@@ -30,7 +30,8 @@
30
  Smile,
31
  X,
32
  GitBranch,
33
- Plus
 
34
  } from '@lucide/svelte';
35
 
36
  interface Props {
@@ -70,6 +71,8 @@
70
  grid: LayoutGrid,
71
  clock: Clock,
72
  plus: Plus,
 
 
73
  // social / external — neutral generic marks (no brand logos)
74
  globe: Globe,
75
  hug: Smile,
 
30
  Smile,
31
  X,
32
  GitBranch,
33
+ Plus,
34
+ Menu
35
  } from '@lucide/svelte';
36
 
37
  interface Props {
 
71
  grid: LayoutGrid,
72
  clock: Clock,
73
  plus: Plus,
74
+ menu: Menu,
75
+ close: X,
76
  // social / external — neutral generic marks (no brand logos)
77
  globe: Globe,
78
  hug: Smile,
src/lib/components/Nav.svelte CHANGED
@@ -7,21 +7,68 @@
7
  active?: string;
8
  }
9
  let { active = 'top' }: Props = $props();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  </script>
11
 
12
  <nav class="nav">
13
  <div class="wrap nav__inner">
14
- <a href="#top" style="display:flex;align-items:center;gap:10px">
15
  <StampBadge glyph="trees" accent="var(--forest)" size={32} spin={false} />
16
- <span class="display" style="font-size:19px;font-stretch:112%">Build Small</span>
17
  </a>
 
18
  <div class="nav__links">
19
  {#each NAV as n (n.id)}
20
  <a href={'#' + n.id} class="nav__link" data-active={active === n.id}>{n.label}</a>
21
  {/each}
22
  </div>
23
- <a class="btn btn--accent" href="#submit" style="padding:9px 16px;font-size:13px">
 
 
 
 
 
 
 
 
 
 
 
24
  Submit <Icon name="arrow" size={15} stroke="#fff" />
25
  </a>
26
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  </nav>
 
7
  active?: string;
8
  }
9
  let { active = 'top' }: Props = $props();
10
+
11
+ let open = $state(false);
12
+ const close = () => (open = false);
13
+
14
+ // close the popout on Escape, and when the viewport grows back to the wide layout
15
+ $effect(() => {
16
+ if (!open) return;
17
+ const onKey = (e: KeyboardEvent) => {
18
+ if (e.key === 'Escape') close();
19
+ };
20
+ const onResize = () => {
21
+ if (window.innerWidth > 1000) close();
22
+ };
23
+ window.addEventListener('keydown', onKey);
24
+ window.addEventListener('resize', onResize);
25
+ return () => {
26
+ window.removeEventListener('keydown', onKey);
27
+ window.removeEventListener('resize', onResize);
28
+ };
29
+ });
30
  </script>
31
 
32
  <nav class="nav">
33
  <div class="wrap nav__inner">
34
+ <a href="#top" class="nav__brand" onclick={close}>
35
  <StampBadge glyph="trees" accent="var(--forest)" size={32} spin={false} />
36
+ <span class="display nav__brand-text">Build Small</span>
37
  </a>
38
+
39
  <div class="nav__links">
40
  {#each NAV as n (n.id)}
41
  <a href={'#' + n.id} class="nav__link" data-active={active === n.id}>{n.label}</a>
42
  {/each}
43
  </div>
44
+
45
+ <button
46
+ class="nav__toggle"
47
+ aria-label={open ? 'Close menu' : 'Open menu'}
48
+ aria-expanded={open}
49
+ aria-controls="nav-menu"
50
+ onclick={() => (open = !open)}
51
+ >
52
+ <Icon name={open ? 'close' : 'menu'} size={22} stroke="var(--ink)" />
53
+ </button>
54
+
55
+ <a class="btn btn--accent nav__submit" href="#submit" onclick={close}>
56
  Submit <Icon name="arrow" size={15} stroke="#fff" />
57
  </a>
58
  </div>
59
+
60
+ {#if open}
61
+ <!-- click-away scrim -->
62
+ <button class="nav__scrim" aria-hidden="true" tabindex="-1" onclick={close}></button>
63
+ <div class="nav__menu" id="nav-menu">
64
+ {#each NAV as n (n.id)}
65
+ <a
66
+ href={'#' + n.id}
67
+ class="nav__menu-link"
68
+ data-active={active === n.id}
69
+ onclick={close}>{n.label}</a
70
+ >
71
+ {/each}
72
+ </div>
73
+ {/if}
74
  </nav>
src/lib/components/PartnersOverview.svelte CHANGED
@@ -1,8 +1,7 @@
1
  <script lang="ts">
2
- import { PARTNERS } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import Icon from './Icon.svelte';
5
- import { reveal } from '$lib/actions/reveal';
6
  </script>
7
 
8
  <section class="section" id="partners">
@@ -14,20 +13,9 @@
14
  lede="Models, tools and compute from across the small-AI world. Tap any one for its full kit and support channels."
15
  />
16
  <div class="partner-grid" style="margin-top:44px">
17
- {#each PARTNERS as p, i (p.id)}
18
- <a class="reveal partner-card" use:reveal={{ delay: i * 50 }} href={'/partners/' + p.id}>
19
- <div style="display:flex;justify-content:space-between;align-items:flex-start">
20
- <div class="partner-logo" style="background:{p.accent}">{p.name.split(' · ')[0]}</div>
21
- {#if p.sponsors}
22
- <span
23
- class="coord"
24
- title="Sponsors a prize"
25
- style="display:inline-flex;align-items:center;gap:5px;color:var(--amber-deep)"
26
- >
27
- <Icon name="star" size={13} stroke="var(--amber-deep)" /> sponsor
28
- </span>
29
- {/if}
30
- </div>
31
  <p style="margin-top:16px;color:var(--ink-2);font-size:14.5px;min-height:44px;text-wrap:pretty">
32
  {p.one}
33
  </p>
@@ -38,8 +26,7 @@
38
  </a>
39
  {/each}
40
  <div
41
- class="reveal partner-card"
42
- use:reveal={{ delay: 350 }}
43
  style="display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:10px;background:var(--kraft)"
44
  >
45
  <span class="coord">FIND THE RIGHT ONE</span>
 
1
  <script lang="ts">
2
+ import { PARTNERS, PARTNER_LOGOS } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import Icon from './Icon.svelte';
 
5
  </script>
6
 
7
  <section class="section" id="partners">
 
13
  lede="Models, tools and compute from across the small-AI world. Tap any one for its full kit and support channels."
14
  />
15
  <div class="partner-grid" style="margin-top:44px">
16
+ {#each PARTNERS as p (p.id)}
17
+ <a class="partner-card" href={'/partners/' + p.id}>
18
+ <div class="partner-logo"><img src={PARTNER_LOGOS[p.id]} alt={p.name} loading="lazy" /></div>
 
 
 
 
 
 
 
 
 
 
 
19
  <p style="margin-top:16px;color:var(--ink-2);font-size:14.5px;min-height:44px;text-wrap:pretty">
20
  {p.one}
21
  </p>
 
26
  </a>
27
  {/each}
28
  <div
29
+ class="partner-card"
 
30
  style="display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:10px;background:var(--kraft)"
31
  >
32
  <span class="coord">FIND THE RIGHT ONE</span>
src/lib/components/Prizes.svelte CHANGED
@@ -5,7 +5,6 @@
5
  import TrackPodium from './prizes/TrackPodium.svelte';
6
  import SponsorCard from './prizes/SponsorCard.svelte';
7
  import BonusBadge from './prizes/BonusBadge.svelte';
8
- import { reveal } from '$lib/actions/reveal';
9
 
10
  let filter = $state('all');
11
 
@@ -62,7 +61,7 @@
62
  <div class="coord" style="margin-bottom:16px">SPONSOR PRIZES · OWN CRITERIA PER PRIZE</div>
63
  <div class="sponsor-grid">
64
  {#each SPONSOR_PRIZES as s (s.id)}
65
- <div class="reveal" use:reveal><SponsorCard {s} /></div>
66
  {/each}
67
  </div>
68
  </div>
@@ -78,7 +77,7 @@
78
  </div>
79
  <div class="badge-grid">
80
  {#each BONUS_BADGES as b (b.id)}
81
- <div class="reveal" use:reveal><BonusBadge {b} /></div>
82
  {/each}
83
  </div>
84
  </div>
 
5
  import TrackPodium from './prizes/TrackPodium.svelte';
6
  import SponsorCard from './prizes/SponsorCard.svelte';
7
  import BonusBadge from './prizes/BonusBadge.svelte';
 
8
 
9
  let filter = $state('all');
10
 
 
61
  <div class="coord" style="margin-bottom:16px">SPONSOR PRIZES · OWN CRITERIA PER PRIZE</div>
62
  <div class="sponsor-grid">
63
  {#each SPONSOR_PRIZES as s (s.id)}
64
+ <div><SponsorCard {s} /></div>
65
  {/each}
66
  </div>
67
  </div>
 
77
  </div>
78
  <div class="badge-grid">
79
  {#each BONUS_BADGES as b (b.id)}
80
+ <div><BonusBadge {b} /></div>
81
  {/each}
82
  </div>
83
  </div>
src/lib/components/Recommender.svelte CHANGED
@@ -1,8 +1,7 @@
1
  <script lang="ts">
2
- import { BUILD_NEEDS, PARTNERS } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import Icon from './Icon.svelte';
5
- import { reveal } from '$lib/actions/reveal';
6
 
7
  let need = $state('image');
8
  const active = $derived(BUILD_NEEDS.find((n) => n.id === need)!);
@@ -18,7 +17,7 @@
18
  lede="Tell us the shape of your idea and we’ll point you at the partners and models worth reaching for. Then dig into their pages for the full guide."
19
  />
20
 
21
- <div class="reveal rec" use:reveal style="margin-top:44px">
22
  <div class="rec__needs">
23
  {#each BUILD_NEEDS as n, i (n.id)}
24
  <button class="rec__need" data-on={need === n.id} onclick={() => (need = n.id)}>
@@ -42,9 +41,7 @@
42
  {#each active.matches as m (m.partner + m.model)}
43
  {@const p = partnerById[m.partner]}
44
  <div class="rec__match">
45
- <div class="partner-logo" style="background:{p.accent}">
46
- {p.name.split(' · ')[0]}
47
- </div>
48
  <div style="min-width:0">
49
  <div class="rec__matchmeta">
50
  <span class="display rec__matchname">{m.model}</span>
 
1
  <script lang="ts">
2
+ import { BUILD_NEEDS, PARTNERS, PARTNER_LOGOS } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import Icon from './Icon.svelte';
 
5
 
6
  let need = $state('image');
7
  const active = $derived(BUILD_NEEDS.find((n) => n.id === need)!);
 
17
  lede="Tell us the shape of your idea and we’ll point you at the partners and models worth reaching for. Then dig into their pages for the full guide."
18
  />
19
 
20
+ <div class="rec" style="margin-top:44px">
21
  <div class="rec__needs">
22
  {#each BUILD_NEEDS as n, i (n.id)}
23
  <button class="rec__need" data-on={need === n.id} onclick={() => (need = n.id)}>
 
41
  {#each active.matches as m (m.partner + m.model)}
42
  {@const p = partnerById[m.partner]}
43
  <div class="rec__match">
44
+ <div class="partner-logo"><img src={PARTNER_LOGOS[p.id]} alt={p.name} loading="lazy" /></div>
 
 
45
  <div style="min-width:0">
46
  <div class="rec__matchmeta">
47
  <span class="display rec__matchname">{m.model}</span>
src/lib/components/Rules.svelte CHANGED
@@ -2,7 +2,6 @@
2
  import { RULES } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import Icon from './Icon.svelte';
5
- import { reveal } from '$lib/actions/reveal';
6
  </script>
7
 
8
  <section class="section section--tight" id="rules">
@@ -13,7 +12,7 @@
13
  title="Entry criteria at a glance"
14
  lede="The things every submission needs. Tick them off and you’re on the board."
15
  />
16
- <div class="reveal rules" use:reveal style="margin-top:40px">
17
  {#each RULES as r, i (r.t)}
18
  <div class="rule">
19
  <div class="rule__check"><Icon name="check" size={15} sw={2.4} /></div>
 
2
  import { RULES } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import Icon from './Icon.svelte';
 
5
  </script>
6
 
7
  <section class="section section--tight" id="rules">
 
12
  title="Entry criteria at a glance"
13
  lede="The things every submission needs. Tick them off and you’re on the board."
14
  />
15
+ <div class="rules" style="margin-top:40px">
16
  {#each RULES as r, i (r.t)}
17
  <div class="rule">
18
  <div class="rule__check"><Icon name="check" size={15} sw={2.4} /></div>
src/lib/components/SubmitTrail.svelte CHANGED
@@ -2,7 +2,6 @@
2
  import { SUBMIT_STEPS } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import TopoBackdrop from './TopoBackdrop.svelte';
5
- import { reveal } from '$lib/actions/reveal';
6
  </script>
7
 
8
  <section class="section on-dark" id="submit">
@@ -16,7 +15,7 @@
16
  />
17
  <div class="trail" style="margin-top:48px;max-width:720px">
18
  {#each SUBMIT_STEPS as s, i (s.t)}
19
- <div class="reveal trail__step" use:reveal={{ delay: i * 70 }}>
20
  <div class="trail__dot" style="background:var(--amber);border-color:var(--amber);color:#fff">
21
  {i + 1}
22
  </div>
 
2
  import { SUBMIT_STEPS } from '$lib/data/content';
3
  import SectionHeader from './SectionHeader.svelte';
4
  import TopoBackdrop from './TopoBackdrop.svelte';
 
5
  </script>
6
 
7
  <section class="section on-dark" id="submit">
 
15
  />
16
  <div class="trail" style="margin-top:48px;max-width:720px">
17
  {#each SUBMIT_STEPS as s, i (s.t)}
18
+ <div class="trail__step">
19
  <div class="trail__dot" style="background:var(--amber);border-color:var(--amber);color:#fff">
20
  {i + 1}
21
  </div>
src/lib/components/Tracks.svelte CHANGED
@@ -3,7 +3,6 @@
3
  import SectionHeader from './SectionHeader.svelte';
4
  import TopoBackdrop from './TopoBackdrop.svelte';
5
  import Icon from './Icon.svelte';
6
- import { reveal } from '$lib/actions/reveal';
7
  </script>
8
 
9
  <section class="section" id="tracks">
@@ -15,8 +14,8 @@
15
  lede="Solve a real problem, or wander somewhere weird. Both are equally celebrated — and carry the same prize pool."
16
  />
17
  <div class="tracks" style="margin-top:44px">
18
- {#each TRACKS as tr, i (tr.id)}
19
- <div class="reveal track" use:reveal={{ delay: i * 90 }}>
20
  <TopoBackdrop opacity={0.06} />
21
  <div style="position:relative">
22
  <div
 
3
  import SectionHeader from './SectionHeader.svelte';
4
  import TopoBackdrop from './TopoBackdrop.svelte';
5
  import Icon from './Icon.svelte';
 
6
  </script>
7
 
8
  <section class="section" id="tracks">
 
14
  lede="Solve a real problem, or wander somewhere weird. Both are equally celebrated — and carry the same prize pool."
15
  />
16
  <div class="tracks" style="margin-top:44px">
17
+ {#each TRACKS as tr (tr.id)}
18
+ <div class="track">
19
  <TopoBackdrop opacity={0.06} />
20
  <div style="position:relative">
21
  <div
src/lib/components/partner/CapabilityCard.svelte CHANGED
@@ -1,7 +1,6 @@
1
  <script lang="ts">
2
  import type { PartnerCapability } from '$lib/data/partners';
3
  import Icon from '../Icon.svelte';
4
- import { reveal } from '$lib/actions/reveal';
5
 
6
  interface Props {
7
  c: PartnerCapability;
@@ -9,7 +8,7 @@
9
  let { c }: Props = $props();
10
  </script>
11
 
12
- <div class="reveal pcard" use:reveal>
13
  <div class="capglyph"><Icon name={c.glyph} size={24} stroke="#fff" /></div>
14
  <div class="pcard__name" style="font-size:19px">{c.name}</div>
15
  <p style="color:var(--ink-2);font-size:14.5px;margin-top:10px;text-wrap:pretty">{c.desc}</p>
 
1
  <script lang="ts">
2
  import type { PartnerCapability } from '$lib/data/partners';
3
  import Icon from '../Icon.svelte';
 
4
 
5
  interface Props {
6
  c: PartnerCapability;
 
8
  let { c }: Props = $props();
9
  </script>
10
 
11
+ <div class="pcard">
12
  <div class="capglyph"><Icon name={c.glyph} size={24} stroke="#fff" /></div>
13
  <div class="pcard__name" style="font-size:19px">{c.name}</div>
14
  <p style="color:var(--ink-2);font-size:14.5px;margin-top:10px;text-wrap:pretty">{c.desc}</p>
src/lib/components/partner/ModelCard.svelte CHANGED
@@ -1,7 +1,6 @@
1
  <script lang="ts">
2
  import type { PartnerModel } from '$lib/data/partners';
3
  import Icon from '../Icon.svelte';
4
- import { reveal } from '$lib/actions/reveal';
5
 
6
  interface Props {
7
  m: PartnerModel;
@@ -11,7 +10,7 @@
11
  const pct = $derived(m.size !== undefined ? Math.max(4, Math.round((m.size / 32) * 100)) : 0);
12
  </script>
13
 
14
- <div class="reveal pcard" use:reveal>
15
  <div class="pcard__head">
16
  <div class="pcard__name">{m.name}</div>
17
  {#if m.url}
 
1
  <script lang="ts">
2
  import type { PartnerModel } from '$lib/data/partners';
3
  import Icon from '../Icon.svelte';
 
4
 
5
  interface Props {
6
  m: PartnerModel;
 
10
  const pct = $derived(m.size !== undefined ? Math.max(4, Math.round((m.size / 32) * 100)) : 0);
11
  </script>
12
 
13
+ <div class="pcard">
14
  <div class="pcard__head">
15
  <div class="pcard__name">{m.name}</div>
16
  {#if m.url}
src/lib/components/partner/PartnerPage.svelte CHANGED
@@ -1,13 +1,13 @@
1
  <script lang="ts">
2
  import type { PartnerData } from '$lib/data/partners';
3
  import { RESOURCE_GLYPH } from '$lib/data/partners';
 
4
  import Icon from '../Icon.svelte';
5
  import StampBadge from '../StampBadge.svelte';
6
  import TopoBackdrop from '../TopoBackdrop.svelte';
7
  import LinkRow from '../LinkRow.svelte';
8
  import ModelCard from './ModelCard.svelte';
9
  import CapabilityCard from './CapabilityCard.svelte';
10
- import { reveal } from '$lib/actions/reveal';
11
 
12
  interface Props {
13
  data: PartnerData;
@@ -66,13 +66,8 @@
66
  <div class="wrap phead__grid" style="position:relative">
67
  <div>
68
  <div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap">
69
- <div class="plogo">{data.name}</div>
70
  <span class="psize-chip"><Icon name="cube" size={14} stroke="var(--paccent)" /> {data.sizeRange}</span>
71
- {#if data.prize}
72
- <span class="psize-chip" style="border-color:var(--amber);color:var(--amber-deep)">
73
- <Icon name="star" size={14} stroke="var(--amber-deep)" /> prize sponsor
74
- </span>
75
- {/if}
76
  </div>
77
  <h1 class="display" style="font-size:clamp(40px,6vw,72px);margin-top:22px">{data.name}</h1>
78
  <p class="lede" style="margin-top:18px;max-width:52ch">{data.tagline}</p>
@@ -126,7 +121,7 @@
126
  <div class="wrap">
127
  <div class="section-label"><span class="coord">{labels.guide}</span><span class="eyebrow">Build this with us</span></div>
128
  <h2 class="display" style="font-size:clamp(28px,4vw,44px);margin-bottom:28px">If you want to build…</h2>
129
- <div class="reveal guide" use:reveal style="max-width:940px">
130
  {#each data.guide as g, i (i)}
131
  <div class="guide__row">
132
  <div class="guide__want"><b>{g.want}</b></div>
@@ -156,7 +151,7 @@
156
  </div>
157
  {/if}
158
  {#if data.starterSpace}
159
- <div class="reveal starter" use:reveal style="margin-top:18px">
160
  <StampBadge glyph="play" accent="var(--paccent)" size={72} spin={false} />
161
  <div style="flex:1">
162
  <div class="coord" style="color:var(--paccent)">STARTER SPACE</div>
@@ -183,7 +178,7 @@
183
  <section class="section section--tight">
184
  <div class="wrap">
185
  <div class="section-label"><span class="coord">{labels.prize}</span><span class="eyebrow">Eligible prize</span></div>
186
- <div class="reveal pprize" use:reveal>
187
  <TopoBackdrop opacity={0.07} stroke="rgba(51,49,43,.5)" />
188
  <div style="position:relative"><StampBadge glyph="star" accent="var(--amber)" size={104} /></div>
189
  <div style="position:relative">
 
1
  <script lang="ts">
2
  import type { PartnerData } from '$lib/data/partners';
3
  import { RESOURCE_GLYPH } from '$lib/data/partners';
4
+ import { PARTNER_LOGOS } from '$lib/data/content';
5
  import Icon from '../Icon.svelte';
6
  import StampBadge from '../StampBadge.svelte';
7
  import TopoBackdrop from '../TopoBackdrop.svelte';
8
  import LinkRow from '../LinkRow.svelte';
9
  import ModelCard from './ModelCard.svelte';
10
  import CapabilityCard from './CapabilityCard.svelte';
 
11
 
12
  interface Props {
13
  data: PartnerData;
 
66
  <div class="wrap phead__grid" style="position:relative">
67
  <div>
68
  <div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap">
69
+ <div class="plogo"><img src={PARTNER_LOGOS[data.id]} alt={data.name} loading="lazy" /></div>
70
  <span class="psize-chip"><Icon name="cube" size={14} stroke="var(--paccent)" /> {data.sizeRange}</span>
 
 
 
 
 
71
  </div>
72
  <h1 class="display" style="font-size:clamp(40px,6vw,72px);margin-top:22px">{data.name}</h1>
73
  <p class="lede" style="margin-top:18px;max-width:52ch">{data.tagline}</p>
 
121
  <div class="wrap">
122
  <div class="section-label"><span class="coord">{labels.guide}</span><span class="eyebrow">Build this with us</span></div>
123
  <h2 class="display" style="font-size:clamp(28px,4vw,44px);margin-bottom:28px">If you want to build…</h2>
124
+ <div class="guide" style="max-width:940px">
125
  {#each data.guide as g, i (i)}
126
  <div class="guide__row">
127
  <div class="guide__want"><b>{g.want}</b></div>
 
151
  </div>
152
  {/if}
153
  {#if data.starterSpace}
154
+ <div class="starter" style="margin-top:18px">
155
  <StampBadge glyph="play" accent="var(--paccent)" size={72} spin={false} />
156
  <div style="flex:1">
157
  <div class="coord" style="color:var(--paccent)">STARTER SPACE</div>
 
178
  <section class="section section--tight">
179
  <div class="wrap">
180
  <div class="section-label"><span class="coord">{labels.prize}</span><span class="eyebrow">Eligible prize</span></div>
181
+ <div class="pprize">
182
  <TopoBackdrop opacity={0.07} stroke="rgba(51,49,43,.5)" />
183
  <div style="position:relative"><StampBadge glyph="star" accent="var(--amber)" size={104} /></div>
184
  <div style="position:relative">
src/lib/components/prizes/SponsorCard.svelte CHANGED
@@ -1,5 +1,6 @@
1
  <script lang="ts">
2
  import type { SponsorPrize } from '$lib/data/content';
 
3
  import Icon from '../Icon.svelte';
4
 
5
  interface Props {
@@ -15,7 +16,7 @@
15
  <div class="sponsor-card">
16
  <div style="display:flex;justify-content:space-between;align-items:flex-start;gap:14px">
17
  <div>
18
- <div class="partner-logo" style="background:{accent};font-size:13px;height:30px">{s.sponsor}</div>
19
  <h4 class="display" style="font-size:23px;margin-top:14px;font-stretch:108%">{s.title}</h4>
20
  </div>
21
  <Icon name="star" size={22} stroke={accent} class="sponsor-card__star" />
 
1
  <script lang="ts">
2
  import type { SponsorPrize } from '$lib/data/content';
3
+ import { PARTNER_LOGOS } from '$lib/data/content';
4
  import Icon from '../Icon.svelte';
5
 
6
  interface Props {
 
16
  <div class="sponsor-card">
17
  <div style="display:flex;justify-content:space-between;align-items:flex-start;gap:14px">
18
  <div>
19
+ <div class="partner-logo partner-logo--sm"><img src={PARTNER_LOGOS[s.id]} alt={s.sponsor} loading="lazy" /></div>
20
  <h4 class="display" style="font-size:23px;margin-top:14px;font-stretch:108%">{s.title}</h4>
21
  </div>
22
  <Icon name="star" size={22} stroke={accent} class="sponsor-card__star" />
src/lib/data/content.ts CHANGED
@@ -19,7 +19,7 @@ export const NAV = [
19
  export const EVENT = {
20
  name: 'Build Small',
21
  tagline: 'Build something small, local, and yours.',
22
- deadline: 'Aug 4, 2026',
23
  deadlineNote: 'final submission',
24
  org: 'huggingface.co/BuildSmall',
25
  paramCap: '32B',
@@ -206,6 +206,17 @@ export const PARTNERS: PartnerSummary[] = [
206
  { id: 'cohere', name: 'Cohere Labs', accent: '#9c5a2b', one: 'Cohere Transcribe (ASR) and Tiny Aya multilingual models.', sponsors: false, size: 'rich', tag: 'ASR · multilingual' }
207
  ];
208
 
 
 
 
 
 
 
 
 
 
 
 
209
  export type BuildNeed = {
210
  id: string;
211
  label: string;
@@ -280,11 +291,14 @@ export const SUBMIT_STEPS = [
280
  ];
281
 
282
  export const FAQS = [
283
- { q: 'What does “under 32B” actually mean?', a: 'Every model your project depends on must have under 32B total parameters (not just active parameters). You can combine several small models, but none may exceed the cap.' },
284
- { q: 'Do I have to use a sponsor’s model?', a: 'No the general track prizes have no additional model criteria. Sponsor prizes each require their own model or platform, listed on every prize.' },
 
 
285
  { q: 'Is there a GPU limit?', a: 'If you rely on the provided Zero GPU resources, you’re limited to 10 Zero GPU apps per user. Otherwise, run on consumer hardware or use Modal credits.' },
286
- { q: 'Can I use a hosted API instead of running locally?', a: 'Yes. Several partners (like OpenBMB) provide free hosted API access to their models, and most models also run locally via llama.cpp or transformers. Small, open models are the spirit of the jam.' },
287
- { q: 'Can one project win multiple prizes?', a: 'Yes. A single Space can land a track prize, a sponsor prize, and one or more bonus badges all at once.' },
 
288
  { q: 'How do I submit?', a: 'Ship a Gradio Space in the org, record a demo video, post on social, and update your README with links and frontmatter tags. See the trail map above for the full sequence.' }
289
  ];
290
 
 
19
  export const EVENT = {
20
  name: 'Build Small',
21
  tagline: 'Build something small, local, and yours.',
22
+ deadline: 'June 15, 2026',
23
  deadlineNote: 'final submission',
24
  org: 'huggingface.co/BuildSmall',
25
  paramCap: '32B',
 
206
  { id: 'cohere', name: 'Cohere Labs', accent: '#9c5a2b', one: 'Cohere Transcribe (ASR) and Tiny Aya multilingual models.', sponsors: false, size: 'rich', tag: 'ASR · multilingual' }
207
  ];
208
 
209
+ // Brand logos, keyed by partner id (shared across PARTNERS, SPONSOR_PRIZES and partner pages).
210
+ export const PARTNER_LOGOS: Record<string, string> = {
211
+ openbmb: '/partners/openbmb.png',
212
+ blackforest: '/partners/blackforest.png',
213
+ openai: '/partners/openai.png',
214
+ nvidia: '/partners/nvidia.png',
215
+ modal: '/partners/modal.png',
216
+ jetbrains: '/partners/jetbrains.png',
217
+ cohere: '/partners/cohere.svg'
218
+ };
219
+
220
  export type BuildNeed = {
221
  id: string;
222
  label: string;
 
291
  ];
292
 
293
  export const FAQS = [
294
+ { q: 'What does “under 32B” actually mean?', a: 'Every model your project depends on must have under 32B total parameters (not just active parameters). You can freely combine several models — say a 14B text model, a 7B speech model, and a 12B image model — as long as each one individually stays under the cap.' },
295
+ { q: 'Do I have to use a sponsor’s model?', a: 'No. For the general tracks you can use any model you like, including ones from non-sponsoring orgs, as long as it stays under the 32B cap. Sponsor prizes each require their own model or platform and have their own criteria, listed on every prize.' },
296
+ { q: 'Do I need to exclusively use a sponsor’s models to win their prize?', a: 'No — you can use other providers’ models too. But the sponsor’s models must form a core part of the experience, since each sponsor is looking for the most interesting use of their own models.' },
297
+ { q: 'Am I eligible for the OpenAI Codex prize if I didn’t get free Codex credits?', a: 'Yes. The credits given to the first 1,000 registrants aren’t tied to the prize. As long as you use Codex during development and have Codex-attributed commits in the Space’s history or a linked GitHub repo, you’re eligible.' },
298
  { q: 'Is there a GPU limit?', a: 'If you rely on the provided Zero GPU resources, you’re limited to 10 Zero GPU apps per user. Otherwise, run on consumer hardware or use Modal credits.' },
299
+ { q: 'Can I use a hosted API instead of running locally?', a: 'Yes. Several partners (like OpenBMB) provide free hosted API access, and Hugging Face inference providers offer plenty more options. That said, not every model is available via a cloud API, and some prizes require running locally to be eligible.' },
300
+ { q: 'Can one project win multiple prizes?', a: 'Yes awards stack. One Space could place in a general track (say Backyard AI), win sponsor prizes (like OpenBMB’s Best MiniCPM Build, the OpenAI Codex prize, and Best Use of Modal), and still pick up bonus badges such as Tiny Titan if every model is ≤ 4B — all at once.' },
301
+ { q: 'Can I submit multiple apps?', a: 'Yes — submit as many apps as you like, and each one will be considered.' },
302
  { q: 'How do I submit?', a: 'Ship a Gradio Space in the org, record a demo video, post on social, and update your README with links and frontmatter tags. See the trail map above for the full sequence.' }
303
  ];
304
 
src/lib/styles/global.css CHANGED
@@ -206,11 +206,7 @@ h1,h2,h3,h4 { font-family: var(--font-display); margin: 0; line-height: 1.02; }
206
  /* small motif: dotted route */
207
  .route { stroke-dasharray: 1 7; stroke-linecap: round; }
208
 
209
- /* reveal on scroll */
210
- .reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s ease; }
211
- .reveal.in { opacity: 1; transform: none; }
212
  @media (prefers-reduced-motion: reduce) {
213
- .reveal { opacity: 1; transform: none; transition: none; }
214
  html { scroll-behavior: auto; }
215
  }
216
 
@@ -222,15 +218,77 @@ h1,h2,h3,h4 { font-family: var(--font-display); margin: 0; line-height: 1.02; }
222
  border-bottom: 2px solid var(--ink);
223
  }
224
  .nav__inner { display: flex; align-items: center; gap: 20px; height: 64px; }
 
 
225
  .nav__links { display: flex; gap: 4px; margin-left: auto; }
226
  .nav__link {
227
  font-family: var(--font-mono); font-size: 12.5px; letter-spacing: .02em;
228
  padding: 7px 12px; border-radius: 0; color: var(--ink-soft);
 
229
  transition: all .15s ease; cursor: pointer;
230
  }
231
  .nav__link:hover { color: var(--ink); background: color-mix(in srgb, var(--kraft) 60%, transparent); }
232
  .nav__link[data-active="true"] { color: var(--ink); background: var(--kraft); }
233
- @media (max-width: 880px){ .nav__links { display: none; } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
235
  /* dark section */
236
  .on-dark { --bg: var(--forest-ink); background: var(--forest-ink); color: var(--paper); }
@@ -324,7 +382,7 @@ h1,h2,h3,h4 { font-family: var(--font-display); margin: 0; line-height: 1.02; }
324
  @media (max-width: 760px){ .podium { grid-template-columns: 1fr; } }
325
 
326
  .sponsor-grid { display: grid; grid-template-columns: repeat(2,1fr); gap: 18px; grid-auto-rows: 1fr; }
327
- .sponsor-grid > .reveal { display: grid; }
328
  .sponsor-card { position: relative; border: 2px solid var(--ink); border-radius: 0;
329
  background: var(--paper); padding: 24px; overflow: hidden;
330
  box-shadow: 6px 6px 0 var(--ink);
@@ -357,7 +415,7 @@ h1,h2,h3,h4 { font-family: var(--font-display); margin: 0; line-height: 1.02; }
357
 
358
  /* bonus badge grid */
359
  .badge-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; grid-auto-rows: 1fr; }
360
- .badge-grid > .reveal { display: grid; }
361
  @media (max-width: 700px){ .badge-grid { grid-template-columns: repeat(2,1fr); } }
362
  @media (max-width: 460px){ .badge-grid { grid-template-columns: 1fr; } }
363
  .bonus-card { border: 2px solid var(--ink); border-radius: 0; background: var(--paper);
@@ -467,9 +525,16 @@ h1,h2,h3,h4 { font-family: var(--font-display); margin: 0; line-height: 1.02; }
467
  padding: 22px; box-shadow: 6px 6px 0 var(--ink);
468
  transition: transform .15s ease, box-shadow .15s ease; display: block; }
469
  .partner-card:hover { transform: translate(-2px,-2px); box-shadow: 8px 8px 0 var(--ink); }
470
- .partner-logo { height: 38px; border-radius: 0; display: flex; align-items: center; padding: 0 12px;
471
- font-family: var(--font-display); font-weight: 800; font-stretch: 112%; color: #fff; width: fit-content;
472
- letter-spacing: -0.01em; font-size: 15px; }
 
 
 
 
 
 
 
473
  @media (max-width: 900px){ .partner-grid { grid-template-columns: repeat(2,1fr); } }
474
  @media (max-width: 480px){ .partner-grid { grid-template-columns: 1fr; } }
475
 
@@ -531,9 +596,10 @@ h1,h2,h3,h4 { font-family: var(--font-display); margin: 0; line-height: 1.02; }
531
  background:
532
  linear-gradient(180deg, color-mix(in srgb, var(--paccent) 9%, var(--bg)) 0%, var(--bg) 78%); }
533
  .phead__grid { display: grid; grid-template-columns: 1fr auto; gap: 36px; align-items: center; }
534
- .plogo { font-family: var(--font-display); font-weight: 800; font-stretch: 112%;
535
- color: #fff; background: var(--paccent); padding: 9px 18px; border-radius: 0;
536
- font-size: 20px; width: fit-content; letter-spacing: -0.01em; box-shadow: 3px 3px 0 var(--ink); }
 
537
  .psize-chip { font-family: var(--font-mono); font-size: 12px; letter-spacing: .04em;
538
  border: 2px solid var(--ink); color: var(--paccent); padding: 6px 13px;
539
  border-radius: 0; box-shadow: 2px 2px 0 var(--ink);
 
206
  /* small motif: dotted route */
207
  .route { stroke-dasharray: 1 7; stroke-linecap: round; }
208
 
 
 
 
209
  @media (prefers-reduced-motion: reduce) {
 
210
  html { scroll-behavior: auto; }
211
  }
212
 
 
218
  border-bottom: 2px solid var(--ink);
219
  }
220
  .nav__inner { display: flex; align-items: center; gap: 20px; height: 64px; }
221
+ .nav__brand { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
222
+ .nav__brand-text { font-size: 19px; font-stretch: 112%; white-space: nowrap; }
223
  .nav__links { display: flex; gap: 4px; margin-left: auto; }
224
  .nav__link {
225
  font-family: var(--font-mono); font-size: 12.5px; letter-spacing: .02em;
226
  padding: 7px 12px; border-radius: 0; color: var(--ink-soft);
227
+ white-space: nowrap;
228
  transition: all .15s ease; cursor: pointer;
229
  }
230
  .nav__link:hover { color: var(--ink); background: color-mix(in srgb, var(--kraft) 60%, transparent); }
231
  .nav__link[data-active="true"] { color: var(--ink); background: var(--kraft); }
232
+
233
+ /* hamburger toggle — hidden until the inline links no longer fit */
234
+ .nav__toggle {
235
+ display: none;
236
+ margin-left: auto;
237
+ align-items: center; justify-content: center;
238
+ width: 40px; height: 40px; padding: 0;
239
+ background: var(--paper);
240
+ border: 2px solid var(--ink);
241
+ box-shadow: var(--press-sm);
242
+ cursor: pointer;
243
+ transition: transform .1s ease, box-shadow .1s ease;
244
+ }
245
+ .nav__toggle:hover { transform: translate(2px,2px); box-shadow: 0 0 0 var(--ink); }
246
+ .nav__toggle:active { transform: translate(2px,2px); box-shadow: 0 0 0 var(--ink); }
247
+
248
+ /* submit button sized to match the toggle (40px tall, 2px stamp shadow) */
249
+ .nav__submit {
250
+ height: 40px; padding: 0 16px;
251
+ font-size: 13px; gap: 8px;
252
+ box-shadow: 2px 2px 0 var(--forest-ink);
253
+ }
254
+ .nav__submit:hover { transform: translate(2px,2px); box-shadow: 0 0 0 var(--forest-ink); }
255
+ .nav__submit:active { transform: translate(2px,2px); box-shadow: 0 0 0 var(--forest-ink); }
256
+
257
+ /* click-away scrim behind the popout */
258
+ .nav__scrim {
259
+ display: none;
260
+ position: fixed; inset: 0; z-index: 40;
261
+ background: transparent; border: 0; padding: 0; margin: 0; cursor: default;
262
+ }
263
+
264
+ /* popout menu panel */
265
+ .nav__menu {
266
+ display: none;
267
+ position: absolute; top: calc(100% + 8px); right: 28px; z-index: 60;
268
+ flex-direction: column;
269
+ width: min(260px, calc(100vw - 56px));
270
+ padding: 8px;
271
+ background: var(--paper);
272
+ border: 2px solid var(--ink);
273
+ box-shadow: var(--press);
274
+ }
275
+ .nav__menu-link {
276
+ font-family: var(--font-mono); font-size: 14px; letter-spacing: .02em;
277
+ padding: 11px 14px; color: var(--ink-soft);
278
+ transition: background .15s ease, color .15s ease;
279
+ }
280
+ .nav__menu-link:hover { color: var(--ink); background: color-mix(in srgb, var(--kraft) 60%, transparent); }
281
+ .nav__menu-link[data-active="true"] { color: var(--ink); background: var(--kraft); }
282
+
283
+ /* below ~1000px the eight inline links stop fitting — switch to the popout */
284
+ @media (max-width: 1000px){
285
+ .nav__links { display: none; }
286
+ .nav__toggle { display: inline-flex; }
287
+ .nav__menu { display: flex; }
288
+ .nav__scrim { display: block; }
289
+ }
290
+ /* tiny screens: drop the wordmark, keep the stamp badge */
291
+ @media (max-width: 400px){ .nav__brand-text { display: none; } }
292
 
293
  /* dark section */
294
  .on-dark { --bg: var(--forest-ink); background: var(--forest-ink); color: var(--paper); }
 
382
  @media (max-width: 760px){ .podium { grid-template-columns: 1fr; } }
383
 
384
  .sponsor-grid { display: grid; grid-template-columns: repeat(2,1fr); gap: 18px; grid-auto-rows: 1fr; }
385
+ .sponsor-grid > div { display: grid; }
386
  .sponsor-card { position: relative; border: 2px solid var(--ink); border-radius: 0;
387
  background: var(--paper); padding: 24px; overflow: hidden;
388
  box-shadow: 6px 6px 0 var(--ink);
 
415
 
416
  /* bonus badge grid */
417
  .badge-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; grid-auto-rows: 1fr; }
418
+ .badge-grid > div { display: grid; }
419
  @media (max-width: 700px){ .badge-grid { grid-template-columns: repeat(2,1fr); } }
420
  @media (max-width: 460px){ .badge-grid { grid-template-columns: 1fr; } }
421
  .bonus-card { border: 2px solid var(--ink); border-radius: 0; background: var(--paper);
 
525
  padding: 22px; box-shadow: 6px 6px 0 var(--ink);
526
  transition: transform .15s ease, box-shadow .15s ease; display: block; }
527
  .partner-card:hover { transform: translate(-2px,-2px); box-shadow: 8px 8px 0 var(--ink); }
528
+ .partner-logo { height: 38px; border-radius: 0; display: inline-flex; align-items: center; justify-content: center;
529
+ padding: 7px 12px; background: #fff; border: 2px solid var(--ink); width: fit-content; }
530
+ .partner-logo img { max-height: 100%; max-width: 100%; width: auto; object-fit: contain; display: block; }
531
+ .partner-logo--sm { height: 30px; padding: 5px 10px; }
532
+ /* OpenAI wordmark has a descending “p” — nudge down so the caps optically centre */
533
+ .partner-logo img[src*="openai"],
534
+ .plogo img[src*="openai"] { transform: translateY(8%); }
535
+ /* Cohere lockup reads heavier/larger than the others — shrink for even visual weight */
536
+ .partner-logo img[src*="cohere"],
537
+ .plogo img[src*="cohere"] { max-height: 74%; }
538
  @media (max-width: 900px){ .partner-grid { grid-template-columns: repeat(2,1fr); } }
539
  @media (max-width: 480px){ .partner-grid { grid-template-columns: 1fr; } }
540
 
 
596
  background:
597
  linear-gradient(180deg, color-mix(in srgb, var(--paccent) 9%, var(--bg)) 0%, var(--bg) 78%); }
598
  .phead__grid { display: grid; grid-template-columns: 1fr auto; gap: 36px; align-items: center; }
599
+ .plogo { display: inline-flex; align-items: center; justify-content: center; height: 52px;
600
+ padding: 10px 18px; background: #fff; border: 2px solid var(--ink); border-radius: 0;
601
+ width: fit-content; box-shadow: 3px 3px 0 var(--ink); }
602
+ .plogo img { max-height: 100%; max-width: 220px; width: auto; object-fit: contain; display: block; }
603
  .psize-chip { font-family: var(--font-mono); font-size: 12px; letter-spacing: .04em;
604
  border: 2px solid var(--ink); color: var(--paccent); padding: 6px 13px;
605
  border-radius: 0; box-shadow: 2px 2px 0 var(--ink);
src/routes/+page.svelte CHANGED
@@ -14,23 +14,36 @@
14
 
15
  let active = $state('top');
16
 
17
- // scroll-spy: highlight the nav item for the section in view
 
 
18
  $effect(() => {
19
  const ids = ['top', ...NAV.map((n) => n.id)];
20
- const io = new IntersectionObserver(
21
- (entries) => {
22
- const visible = entries
23
- .filter((e) => e.isIntersecting)
24
- .sort((a, b) => b.intersectionRatio - a.intersectionRatio);
25
- if (visible[0]) active = visible[0].target.id;
26
- },
27
- { rootMargin: '-45% 0px -50% 0px', threshold: [0, 0.25, 0.5] }
28
- );
29
- for (const id of ids) {
30
- const el = document.getElementById(id);
31
- if (el) io.observe(el);
32
- }
33
- return () => io.disconnect();
 
 
 
 
 
 
 
 
 
 
 
34
  });
35
  </script>
36
 
 
14
 
15
  let active = $state('top');
16
 
17
+ // scroll-spy: highlight the nav item for the last section whose top has
18
+ // crossed a line ~40% down the viewport. Position-based so it tracks scroll
19
+ // monotonically and never flips back and forth between sections.
20
  $effect(() => {
21
  const ids = ['top', ...NAV.map((n) => n.id)];
22
+ let frame = 0;
23
+
24
+ const update = () => {
25
+ frame = 0;
26
+ const line = window.innerHeight * 0.4;
27
+ let current = ids[0];
28
+ for (const id of ids) {
29
+ const el = document.getElementById(id);
30
+ if (el && el.getBoundingClientRect().top <= line) current = id;
31
+ }
32
+ active = current;
33
+ };
34
+
35
+ const onScroll = () => {
36
+ if (!frame) frame = requestAnimationFrame(update);
37
+ };
38
+
39
+ update();
40
+ window.addEventListener('scroll', onScroll, { passive: true });
41
+ window.addEventListener('resize', onScroll);
42
+ return () => {
43
+ if (frame) cancelAnimationFrame(frame);
44
+ window.removeEventListener('scroll', onScroll);
45
+ window.removeEventListener('resize', onScroll);
46
+ };
47
  });
48
  </script>
49
 
static/partners/blackforest.png ADDED

Git LFS Details

  • SHA256: c2bc9640c5dfdce8d0d5eca3e9dbffff569437f1e0463913a1974d4b8196557e
  • Pointer size: 130 Bytes
  • Size of remote file: 10.8 kB
static/partners/cohere.svg ADDED
static/partners/jetbrains.png ADDED

Git LFS Details

  • SHA256: 12891548bd431cac3e923c353bcc9034b7f993da99d3760d2175c78ad79551bc
  • Pointer size: 130 Bytes
  • Size of remote file: 16 kB
static/partners/modal.png ADDED

Git LFS Details

  • SHA256: 146601e53d42d995a68e07ed30a006e60f9148409880cea813afa6b2e49442ea
  • Pointer size: 131 Bytes
  • Size of remote file: 273 kB
static/partners/nvidia.png ADDED

Git LFS Details

  • SHA256: c144397c640467e57bc069a676cb977ffc8f2e03772ef289400716259665e72e
  • Pointer size: 130 Bytes
  • Size of remote file: 29.2 kB
static/partners/openai.png ADDED

Git LFS Details

  • SHA256: c0b77a6bc329c15eb67052c99027f6fc8002ba6ba47d936fe9015336a2e3409f
  • Pointer size: 130 Bytes
  • Size of remote file: 26.5 kB
static/partners/openbmb.png ADDED

Git LFS Details

  • SHA256: 35fd2ce4054d0f9721e87bb13869e342a6bc026fad1a8f05370d278dfbb75a2c
  • Pointer size: 130 Bytes
  • Size of remote file: 10.8 kB