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 +36 -0
- src/lib/actions/reveal.ts +0 -25
- src/lib/components/BigIdea.svelte +1 -2
- src/lib/components/Icon.svelte +4 -1
- src/lib/components/Nav.svelte +50 -3
- src/lib/components/PartnersOverview.svelte +5 -18
- src/lib/components/Prizes.svelte +2 -3
- src/lib/components/Recommender.svelte +3 -6
- src/lib/components/Rules.svelte +1 -2
- src/lib/components/SubmitTrail.svelte +1 -2
- src/lib/components/Tracks.svelte +2 -3
- src/lib/components/partner/CapabilityCard.svelte +1 -2
- src/lib/components/partner/ModelCard.svelte +1 -2
- src/lib/components/partner/PartnerPage.svelte +5 -10
- src/lib/components/prizes/SponsorCard.svelte +2 -1
- src/lib/data/content.ts +19 -5
- src/lib/styles/global.css +79 -13
- src/routes/+page.svelte +28 -15
- static/partners/blackforest.png +3 -0
- static/partners/cohere.svg +24 -0
- static/partners/jetbrains.png +3 -0
- static/partners/modal.png +3 -0
- static/partners/nvidia.png +3 -0
- static/partners/openai.png +3 -0
- static/partners/openbmb.png +3 -0
.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
|
| 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"
|
| 15 |
<StampBadge glyph="trees" accent="var(--forest)" size={32} spin={false} />
|
| 16 |
-
<span class="display
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 18 |
-
<a class="
|
| 19 |
-
<div
|
| 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="
|
| 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
|
| 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
|
| 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="
|
| 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"
|
| 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="
|
| 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="
|
| 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
|
| 19 |
-
<div class="
|
| 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="
|
| 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="
|
| 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="
|
| 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="
|
| 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="
|
| 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"
|
| 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: '
|
| 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
|
| 284 |
-
{ q: 'Do I have to use a sponsor’s model?', a: 'No
|
|
|
|
|
|
|
| 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
|
| 287 |
-
{ q: 'Can one project win multiple prizes?', a: 'Yes
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 >
|
| 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 >
|
| 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;
|
| 471 |
-
|
| 472 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 {
|
| 535 |
-
|
| 536 |
-
|
|
|
|
| 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
|
|
|
|
|
|
|
| 18 |
$effect(() => {
|
| 19 |
const ids = ['top', ...NAV.map((n) => n.id)];
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
static/partners/cohere.svg
ADDED
|
|
static/partners/jetbrains.png
ADDED
|
Git LFS Details
|
static/partners/modal.png
ADDED
|
Git LFS Details
|
static/partners/nvidia.png
ADDED
|
Git LFS Details
|
static/partners/openai.png
ADDED
|
Git LFS Details
|
static/partners/openbmb.png
ADDED
|
Git LFS Details
|