| <script lang="ts"> |
| import type { PartnerData } from '$lib/data/partners'; |
| import { RESOURCE_GLYPH } from '$lib/data/partners'; |
| import { PARTNER_LOGOS } from '$lib/data/content'; |
| import Icon from '../Icon.svelte'; |
| import StampBadge from '../StampBadge.svelte'; |
| import TopoBackdrop from '../TopoBackdrop.svelte'; |
| import LinkRow from '../LinkRow.svelte'; |
| import ModelCard from './ModelCard.svelte'; |
| import CapabilityCard from './CapabilityCard.svelte'; |
| |
| interface Props { |
| data: PartnerData; |
| } |
| let { data }: Props = $props(); |
| |
| const isModels = $derived(data.type === 'models'); |
| const hasCatalog = $derived( |
| isModels ? !!data.models?.length : !!data.capabilities?.length |
| ); |
| const hasResources = $derived(!!data.resources?.length || !!data.starterSpace); |
| |
| |
| const labels = $derived.by(() => { |
| let n = 0; |
| const next = () => String(++n).padStart(2, '0'); |
| return { |
| offer: next(), |
| catalog: hasCatalog ? next() : '', |
| guide: data.guide?.length ? next() : '', |
| resources: hasResources ? next() : '', |
| prize: data.prize ? next() : '', |
| support: data.support?.length ? next() : '' |
| }; |
| }); |
| </script> |
|
|
| <svelte:head> |
| <title>{data.name} · Build Small</title> |
| </svelte:head> |
|
|
| <div class="app-root" style="--paccent:{data.accent}"> |
| |
| <nav class="pbar"> |
| <div class="wrap pbar__inner"> |
| <div class="crumb"> |
| <a href="/">Build Small</a> |
| <span class="crumb__sep">/</span> |
| <a href="/#partners">Partners</a> |
| <span class="crumb__sep">/</span> |
| <b>{data.name}</b> |
| </div> |
| <a |
| class="btn btn--accent" |
| href="/" |
| style="margin-left:auto;padding:8px 15px;font-size:13px;background:var(--paccent);border-color:var(--paccent)" |
| > |
| <Icon name="arrow" size={14} stroke="#fff" rotate={180} /> Back to hub |
| </a> |
| </div> |
| </nav> |
|
|
| |
| <header class="phead"> |
| <TopoBackdrop opacity={0.1} stroke="var(--paccent)" /> |
| <div class="wrap phead__grid" style="position:relative"> |
| <div> |
| <div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap"> |
| <div class="plogo"><img src={PARTNER_LOGOS[data.id]} alt={data.name} loading="lazy" /></div> |
| <span class="psize-chip"><Icon name="cube" size={14} stroke="var(--paccent)" /> {data.sizeRange}</span> |
| </div> |
| <h1 class="display" style="font-size:clamp(40px,6vw,72px);margin-top:22px">{data.name}</h1> |
| <p class="lede" style="margin-top:18px;max-width:52ch">{data.tagline}</p> |
| <LinkRow |
| links={data.links && Object.keys(data.links).length ? data.links : { website: data.url }} |
| accent="var(--paccent)" |
| style="margin-top:24px" |
| /> |
| </div> |
| {#if data.prize} |
| <div class="floaty"><StampBadge glyph="star" accent="var(--paccent)" size={132} /></div> |
| {/if} |
| </div> |
| </header> |
|
|
| |
| <section class="section section--tight"> |
| <div class="wrap"> |
| <div class="section-label"><span class="coord">{labels.offer}</span><span class="eyebrow">What they offer</span></div> |
| <p |
| class="display" |
| style="font-weight:600;font-stretch:106%;font-size:clamp(22px,3vw,32px);line-height:1.18;text-wrap:pretty;max-width:900px" |
| > |
| {data.offer} |
| </p> |
| </div> |
| </section> |
|
|
| |
| {#if hasCatalog} |
| <section class="section section--tight"> |
| <div class="wrap"> |
| <div class="section-label"><span class="coord">{labels.catalog}</span><span class="eyebrow">{isModels ? 'Models & products' : 'Capabilities'}</span></div> |
| <h2 class="display" style="font-size:clamp(28px,4vw,44px);margin-bottom:28px"> |
| {isModels ? 'The kit, sized small' : 'What you can run'} |
| </h2> |
| <div class="pcards"> |
| {#if isModels} |
| {#each data.models ?? [] as m (m.name)}<ModelCard {m} />{/each} |
| {:else} |
| {#each data.capabilities ?? [] as c (c.name)}<CapabilityCard {c} />{/each} |
| {/if} |
| </div> |
| </div> |
| </section> |
| {/if} |
|
|
| |
| {#if data.guide?.length} |
| <section class="section section--tight"> |
| <div class="wrap"> |
| <div class="section-label"><span class="coord">{labels.guide}</span><span class="eyebrow">Build this with us</span></div> |
| <h2 class="display" style="font-size:clamp(28px,4vw,44px);margin-bottom:28px">If you want to build…</h2> |
| <div class="guide" style="max-width:940px"> |
| {#each data.guide as g, i (i)} |
| <div class="guide__row"> |
| <div class="guide__want"><b>{g.want}</b></div> |
| <div class="guide__arrow"><Icon name="arrow" size={22} stroke="var(--paccent)" /></div> |
| <div class="guide__use"><b>{g.use}</b><span class="coord">{g.note}</span></div> |
| </div> |
| {/each} |
| </div> |
| </div> |
| </section> |
| {/if} |
|
|
| |
| {#if hasResources} |
| <section class="section section--tight"> |
| <div class="wrap"> |
| <div class="section-label"><span class="coord">{labels.resources}</span><span class="eyebrow">Getting started</span></div> |
| {#if data.resources?.length} |
| <div class="reslist"> |
| {#each data.resources as r (r.label)} |
| <a class="resitem" href={r.url} target="_blank" rel="noreferrer"> |
| <span class="resitem__glyph"><Icon name={RESOURCE_GLYPH[r.kind] || 'link'} size={19} stroke="var(--paccent)" /></span> |
| <span style="font-weight:600;font-family:var(--font-display);font-stretch:104%">{r.label}</span> |
| <span class="resitem__arrow"><Icon name="arrow" size={17} stroke="var(--ink-faint)" /></span> |
| </a> |
| {/each} |
| </div> |
| {/if} |
| {#if data.starterSpace} |
| <div class="starter" style="margin-top:18px"> |
| <StampBadge glyph="play" accent="var(--paccent)" size={72} spin={false} /> |
| <div style="flex:1"> |
| <div class="coord" style="color:var(--paccent)">STARTER SPACE</div> |
| <div class="display" style="font-size:21px;margin-top:4px;font-stretch:106%">{data.starterSpace.name}</div> |
| <p style="color:var(--ink-2);font-size:14.5px;margin:6px 0 0">{data.starterSpace.desc}</p> |
| </div> |
| <a |
| class="btn btn--accent" |
| href={data.starterSpace.url} |
| target="_blank" |
| rel="noreferrer" |
| style="background:var(--paccent);border-color:var(--paccent);flex:none" |
| > |
| Fork it <Icon name="arrow" size={16} stroke="#fff" /> |
| </a> |
| </div> |
| {/if} |
| </div> |
| </section> |
| {/if} |
|
|
| |
| {#if data.prize} |
| <section class="section section--tight"> |
| <div class="wrap"> |
| <div class="section-label"><span class="coord">{labels.prize}</span><span class="eyebrow">Eligible prize</span></div> |
| <div class="pprize"> |
| <TopoBackdrop opacity={0.07} stroke="rgba(51,49,43,.5)" /> |
| <div style="position:relative"><StampBadge glyph="star" accent="var(--amber)" size={104} /></div> |
| <div style="position:relative"> |
| <div class="coord" style="color:var(--amber-deep)">{data.name.toUpperCase()} · SPONSOR PRIZE</div> |
| <h3 class="display" style="font-size:clamp(26px,3.4vw,38px);margin-top:6px">{data.prize.title}</h3> |
| <p style="margin-top:12px;font-size:15.5px;color:var(--ink-2);max-width:52ch"> |
| <b style="font-family:var(--font-display)">To qualify · </b>{data.prize.criteria} |
| </p> |
| </div> |
| <div style="position:relative;text-align:right"> |
| <div class="display" style="font-size:clamp(30px,4vw,46px);color:var(--amber);line-height:1">{data.prize.award}</div> |
| <a href="/#prizes" class="chip" style="margin-top:12px;border-color:var(--ink)"> |
| All prizes <Icon name="arrow" size={14} /> |
| </a> |
| </div> |
| </div> |
| </div> |
| </section> |
| {/if} |
|
|
| |
| {#if data.support?.length} |
| <section class="section section--tight" style="padding-bottom:84px"> |
| <div class="wrap"> |
| <div class="section-label"><span class="coord">{labels.support}</span><span class="eyebrow">How to get help</span></div> |
| <h2 class="display" style="font-size:clamp(28px,4vw,44px);margin-bottom:28px">Stuck? Holler.</h2> |
| <div class="support"> |
| {#each data.support as s (s.channel + s.handle)} |
| <a class="supitem" href={s.url ?? '#'} target={s.url ? '_blank' : undefined} rel={s.url ? 'noreferrer' : undefined}> |
| <span class="supitem__glyph"><Icon name={s.glyph} size={22} stroke="#fff" /></span> |
| <span> |
| <div style="font-weight:600;font-family:var(--font-display);font-stretch:104%">{s.channel}</div> |
| <div class="supitem__handle">{s.handle}</div> |
| </span> |
| </a> |
| {/each} |
| </div> |
| </div> |
| </section> |
| {/if} |
|
|
| |
| <footer class="footer"> |
| <TopoBackdrop opacity={0.08} stroke="rgba(244,238,225,.4)" /> |
| <div |
| class="wrap" |
| style="position:relative;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:24px" |
| > |
| <div> |
| <div class="display" style="font-size:26px">BUILD SMALL</div> |
| <p style="color:rgba(244,238,225,.7);margin-top:8px;font-size:14px">Build something small, local, and yours.</p> |
| </div> |
| <div style="display:flex;gap:12px;flex-wrap:wrap"> |
| <a class="btn btn--ghost" href="/#partners" style="color:var(--paper);border-color:rgba(244,238,225,.4)">All partners</a> |
| <a class="btn btn--accent" href="/#recommender" style="background:var(--paccent);border-color:var(--paccent)"> |
| Find your kit <Icon name="arrow" size={16} stroke="#fff" /> |
| </a> |
| </div> |
| </div> |
| </footer> |
| </div> |
|
|