chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,53 @@
import { MODEL_CATALOG_PROVIDERS } from '@/app/(landing)/models/utils'
/**
* Luminance ceiling (Rec. 601, 0-255) above which a near-gray provider color is
* too light to read as a dot or bar on the landing's light background. OpenAI's
* near-white brand gray sits well above this, so it gets darkened to stay visible
* on the timeline and comparison charts.
*/
const MAX_LUMINANCE = 140
/**
* Only desaturated (near-gray) colors are darkened. Saturated brand colors stay
* readable on white thanks to their hue, so a bright yellow or orange is left
* exactly as the brand declares it.
*/
const MAX_GRAY_SATURATION = 0.15
/** Fallback when a provider declares no brand color: a readable mid-dark gray. */
const FALLBACK_COLOR = '#666666'
/** Darkens a too-light near-gray color toward the readable ceiling; passes others through. */
function clampToReadable(hex: string): string {
const match = /^#?([0-9a-f]{6})$/i.exec(hex)
if (!match) return hex
const value = Number.parseInt(match[1], 16)
const r = (value >> 16) & 0xff
const g = (value >> 8) & 0xff
const b = value & 0xff
const max = Math.max(r, g, b)
const saturation = max === 0 ? 0 : (max - Math.min(r, g, b)) / max
const luminance = 0.299 * r + 0.587 * g + 0.114 * b
if (luminance <= MAX_LUMINANCE || saturation > MAX_GRAY_SATURATION) return hex
const factor = MAX_LUMINANCE / luminance
const toHex = (channel: number) =>
Math.round(channel * factor)
.toString(16)
.padStart(2, '0')
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
}
const colorMap = new Map(
MODEL_CATALOG_PROVIDERS.flatMap(
(p): Array<[string, string]> => (p.color ? [[p.id, clampToReadable(p.color)]] : [])
)
)
/** Provider brand color, darkened when too light to read on the light background. */
export function getProviderColor(providerId: string): string {
return colorMap.get(providerId) ?? FALLBACK_COLOR
}
@@ -0,0 +1,242 @@
import type { ComponentType } from 'react'
import Link from 'next/link'
import { getProviderColor } from '@/app/(landing)/models/components/constants'
import type { CatalogModel } from '@/app/(landing)/models/utils'
import {
formatPrice,
formatTokenCount,
MODEL_CATALOG_PROVIDERS,
} from '@/app/(landing)/models/utils'
/** Flagship providers featured in the landing-page comparison, in display order. */
const FEATURED_COMPARISON_PROVIDER_IDS = ['anthropic', 'openai', 'google']
/** Max latest models pulled from each featured provider. */
const MAX_MODELS_PER_PROVIDER = 4
const PROVIDER_ICON_MAP: Record<string, ComponentType<{ className?: string }>> = (() => {
const map: Record<string, ComponentType<{ className?: string }>> = {}
for (const provider of MODEL_CATALOG_PROVIDERS) {
if (provider.icon) {
map[provider.id] = provider.icon
}
}
return map
})()
function selectComparisonModels(models: CatalogModel[]): CatalogModel[] {
const seen = new Set<string>()
const result: CatalogModel[] = []
for (const providerId of FEATURED_COMPARISON_PROVIDER_IDS) {
const providerModels = models
.filter((model) => model.providerId === providerId && !model.deprecated)
.sort((a, b) => (b.releaseDate ?? '').localeCompare(a.releaseDate ?? ''))
let takenForProvider = 0
for (const model of providerModels) {
if (takenForProvider >= MAX_MODELS_PER_PROVIDER) break
const nameKey = model.displayName.toLowerCase()
if (seen.has(nameKey)) continue
seen.add(nameKey)
result.push(model)
takenForProvider += 1
}
}
return result
}
interface ModelLabelProps {
model: CatalogModel
}
function ModelLabel({ model }: ModelLabelProps) {
const Icon = PROVIDER_ICON_MAP[model.providerId]
return (
<div className='flex w-[90px] shrink-0 items-center justify-end gap-1.5 sm:w-[140px] lg:w-[180px]'>
{Icon && <Icon className='size-3.5 shrink-0' />}
<span className='truncate text-[13px] text-[var(--text-primary)] leading-none tracking-[-0.01em]'>
{model.displayName}
</span>
</div>
)
}
interface ChartProps {
models: CatalogModel[]
}
function StackedCostChart({ models }: ChartProps) {
const entries = models
.reduce<Array<{ model: CatalogModel; input: number; output: number; total: number }>>(
(acc, model) => {
const total = model.pricing.input + model.pricing.output
if (total > 0) {
acc.push({ model, input: model.pricing.input, output: model.pricing.output, total })
}
return acc
},
[]
)
.sort((a, b) => a.total - b.total)
const maxTotal = entries.length > 0 ? Math.max(...entries.map((e) => e.total)) : 0
const data = { entries, maxTotal }
if (data.entries.length === 0) return null
return (
<div className='flex flex-col gap-3'>
<div className='flex flex-col gap-1'>
<h3 className='text-[20px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[24px]'>
Cost
</h3>
<span className='text-[var(--text-muted)] text-sm leading-[150%] tracking-[0.02em]'>
Per 1M tokens
</span>
</div>
<div className='flex flex-col gap-1.5'>
{data.entries.map(({ model, input, output, total }) => {
const totalPct = data.maxTotal > 0 ? (total / data.maxTotal) * 100 : 0
const inputPct = total > 0 ? (input / total) * 100 : 0
const color = getProviderColor(model.providerId)
return (
<Link
key={model.id}
href={model.href}
className='-mx-2 flex items-center gap-3 rounded-md px-2 transition-colors hover:bg-[var(--surface-hover)]'
>
<ModelLabel model={model} />
<div className='relative flex h-7 min-w-0 flex-1 items-center'>
<div
className='hidden h-full overflow-hidden rounded-r-[3px] sm:flex'
style={{ width: `${Math.max(totalPct, 3)}%` }}
>
<div
className='h-full'
style={{
width: `${inputPct}%`,
backgroundColor: color,
opacity: 0.8,
}}
/>
<div
className='h-full'
style={{
width: `${100 - inputPct}%`,
backgroundColor: color,
opacity: 0.35,
}}
/>
</div>
<span className='shrink-0 text-[11px] text-[var(--text-muted)] sm:ml-2.5 sm:text-xs'>
{formatPrice(input)} input / {formatPrice(output)} output
</span>
</div>
</Link>
)
})}
</div>
</div>
)
}
function ContextWindowChart({ models }: ChartProps) {
const entries = models
.map((model) => ({
model,
value: model.contextWindow,
}))
.filter((e): e is { model: CatalogModel; value: number } => e.value !== null && e.value > 0)
.sort((a, b) => a.value - b.value)
const maxValue = entries.length > 0 ? Math.max(...entries.map((e) => e.value)) : 0
const data = { entries, maxValue }
if (data.entries.length === 0) return null
return (
<div className='flex flex-col gap-3'>
<div className='flex flex-col gap-1'>
<h3 className='text-[20px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[24px]'>
Context window
</h3>
<span className='text-[var(--text-muted)] text-sm leading-[150%] tracking-[0.02em]'>
Max tokens
</span>
</div>
<div className='flex flex-col gap-1.5'>
{data.entries.map(({ model, value }) => {
const pct = data.maxValue > 0 ? (value / data.maxValue) * 100 : 0
const color = getProviderColor(model.providerId)
return (
<Link
key={model.id}
href={model.href}
className='-mx-2 flex items-center gap-3 rounded-md px-2 transition-colors hover:bg-[var(--surface-hover)]'
>
<ModelLabel model={model} />
<div className='relative flex h-7 min-w-0 flex-1 items-center'>
<div
className='h-full rounded-r-[3px]'
style={{
width: `${Math.max(pct, 3)}%`,
backgroundColor: color,
opacity: 0.8,
}}
/>
<span className='ml-2.5 shrink-0 text-[11px] text-[var(--text-muted)] sm:text-xs'>
{formatTokenCount(value)}
</span>
</div>
</Link>
)
})}
</div>
</div>
)
}
interface ModelComparisonChartsProps {
models: CatalogModel[]
}
export function ModelComparisonCharts({ models }: ModelComparisonChartsProps) {
const comparisonModels = selectComparisonModels(models)
return (
<section aria-labelledby='comparison-heading'>
<div className='px-6 pt-10 pb-4'>
<h2
id='comparison-heading'
className='mb-2 text-[20px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[24px]'
>
Compare models
</h2>
<p className='text-[var(--text-muted)] text-sm leading-[150%] tracking-[0.02em]'>
Side-by-side comparison of top models across key metrics.
</p>
</div>
<div className='h-px w-full bg-[var(--border)]' />
<div className='flex flex-col sm:flex-row'>
<div className='flex-1 p-6'>
<StackedCostChart models={comparisonModels} />
</div>
<div className='h-px w-full bg-[var(--border)] sm:h-auto sm:w-px' />
<div className='flex-1 p-6'>
<ContextWindowChart models={comparisonModels} />
</div>
</div>
</section>
)
}
@@ -0,0 +1,256 @@
'use client'
import { useMemo } from 'react'
import { ChipInput, Search } from '@sim/emcn'
import Link from 'next/link'
import { debounce, useQueryStates } from 'nuqs'
import { ChevronArrow } from '@/app/(landing)/components/chevron-arrow'
import { ProviderIcon } from '@/app/(landing)/models/components/model-primitives'
import { modelsParsers, modelsUrlKeys } from '@/app/(landing)/models/search-params'
import {
type CatalogModel,
type CatalogProvider,
formatPrice,
formatTokenCount,
MODEL_PROVIDERS_WITH_CATALOGS,
MODEL_PROVIDERS_WITH_DYNAMIC_CATALOGS,
} from '@/app/(landing)/models/utils'
/** Debounce window for writing the search term to the URL (filtering is instant). */
const SEARCH_DEBOUNCE_MS = 300
const PROVIDER_OPTIONS = MODEL_PROVIDERS_WITH_CATALOGS.map((provider) => ({
id: provider.id,
name: provider.name,
count: provider.modelCount,
}))
export function ModelDirectory() {
const [{ q: query, provider }, setParams] = useQueryStates(modelsParsers, modelsUrlKeys)
const activeProviderId = provider || null
const normalizedQuery = query.trim().toLowerCase()
const { filteredProviders, filteredDynamicProviders } = useMemo(() => {
const filteredProviders = MODEL_PROVIDERS_WITH_CATALOGS.map((provider) => {
const providerMatchesSearch =
normalizedQuery.length > 0 && provider.searchText.includes(normalizedQuery)
const providerMatchesFilter = !activeProviderId || provider.id === activeProviderId
if (!providerMatchesFilter) {
return null
}
const models =
normalizedQuery.length === 0
? provider.models
: provider.models.filter(
(model) =>
model.searchText.includes(normalizedQuery) ||
(providerMatchesSearch && normalizedQuery.length > 0)
)
if (!providerMatchesSearch && models.length === 0) {
return null
}
return {
...provider,
models: providerMatchesSearch && normalizedQuery.length > 0 ? provider.models : models,
}
}).filter((provider): provider is CatalogProvider => provider !== null)
const filteredDynamicProviders = MODEL_PROVIDERS_WITH_DYNAMIC_CATALOGS.filter((provider) => {
const providerMatchesFilter = !activeProviderId || provider.id === activeProviderId
if (!providerMatchesFilter) {
return false
}
if (!normalizedQuery) {
return true
}
return provider.searchText.includes(normalizedQuery)
})
return {
filteredProviders,
filteredDynamicProviders,
}
}, [activeProviderId, normalizedQuery])
const hasResults = filteredProviders.length > 0 || filteredDynamicProviders.length > 0
return (
<div>
<div className='mb-6 flex flex-col gap-4 px-6 sm:flex-row sm:items-center'>
<div className='max-w-[480px] flex-1'>
<ChipInput
icon={Search}
type='search'
placeholder='Search models, providers, or capabilities…'
value={query}
onChange={(event) =>
setParams(
{ q: event.target.value },
{ limitUrlUpdates: debounce(SEARCH_DEBOUNCE_MS) }
)
}
aria-label='Search AI models'
/>
</div>
</div>
<div className='mb-6 flex flex-wrap gap-2 px-6'>
<button
type='button'
onClick={() => setParams({ provider: '' })}
className={`rounded-[5px] border px-[9px] py-0.5 text-small transition-colors ${
activeProviderId === null
? 'border-[var(--border-1)] bg-[var(--surface-active)] text-[var(--text-primary)]'
: 'border-[var(--border-1)] text-[var(--text-primary)] hover:bg-[var(--surface-hover)]'
}`}
>
All
</button>
{PROVIDER_OPTIONS.map((provider) => (
<button
key={provider.id}
type='button'
onClick={() =>
setParams({ provider: activeProviderId === provider.id ? '' : provider.id })
}
className={`rounded-[5px] border px-[9px] py-0.5 text-small transition-colors ${
activeProviderId === provider.id
? 'border-[var(--border-1)] bg-[var(--surface-active)] text-[var(--text-primary)]'
: 'border-[var(--border-1)] text-[var(--text-primary)] hover:bg-[var(--surface-hover)]'
}`}
>
{provider.name}
</button>
))}
</div>
<div className='h-px w-full bg-[var(--border)]' />
{!hasResults ? (
<div className='px-6 py-12 text-center'>
<h3 className='text-[18px] text-[var(--text-primary)]'>No matches found</h3>
<p className='mt-2 text-[var(--text-muted)] text-sm leading-[150%]'>
Try a provider name like OpenAI or Anthropic, or search for capabilities like
&nbsp;structured outputs, reasoning, or deep research.
</p>
</div>
) : (
<div>
{filteredProviders.map((provider, index) => (
<section key={provider.id} aria-labelledby={`${provider.id}-heading`}>
{index > 0 && <div className='h-px w-full bg-[var(--border)]' />}
<Link
href={provider.href}
className='group/link flex items-center gap-3 px-6 py-4 transition-colors hover:bg-[var(--surface-hover)]'
>
<ProviderIcon
provider={provider}
className='size-8 rounded-xl'
iconClassName='size-4'
/>
<div className='min-w-0 flex-1'>
<h3
id={`${provider.id}-heading`}
className='text-[14px] text-[var(--text-primary)] leading-snug tracking-[-0.02em]'
>
{provider.name}
</h3>
<p className='line-clamp-1 hidden text-[12px] text-[var(--text-muted)] leading-[150%] sm:block'>
{provider.modelCount} models &middot; {provider.description}
</p>
</div>
<ChevronArrow />
</Link>
{provider.models.map((model) => (
<ModelRow key={model.id} provider={provider} model={model} />
))}
</section>
))}
{filteredDynamicProviders.length > 0 && (
<section aria-labelledby='dynamic-catalogs-heading'>
<div className='h-px w-full bg-[var(--border)]' />
<div className='px-6 pt-8 pb-6'>
<h3
id='dynamic-catalogs-heading'
className='text-[18px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[20px]'
>
Dynamic model catalogs
</h3>
<p className='mt-2 text-[var(--text-muted)] text-sm leading-[150%]'>
These providers load their model lists dynamically at runtime.
</p>
</div>
<div className='h-px w-full bg-[var(--border)]' />
<nav aria-label='Dynamic catalog providers' className='flex flex-col lg:flex-row'>
{filteredDynamicProviders.map((provider) => (
<div
key={provider.id}
className='flex flex-1 items-center gap-3 border-[var(--border)] border-t px-6 py-4 first:border-t-0 lg:border-t-0 lg:border-l lg:first:border-l-0'
>
<ProviderIcon
provider={provider}
className='size-8 rounded-xl'
iconClassName='size-4'
/>
<div className='min-w-0 flex-1'>
<h4 className='text-[14px] text-[var(--text-primary)] leading-snug'>
{provider.name}
</h4>
<p className='line-clamp-1 text-[12px] text-[var(--text-muted)] leading-[150%]'>
{provider.description}
</p>
</div>
</div>
))}
</nav>
</section>
)}
</div>
)}
</div>
)
}
function ModelRow({ provider, model }: { provider: CatalogProvider; model: CatalogModel }) {
return (
<>
<div className='h-px w-full bg-[var(--border)]' />
<Link
href={model.href}
className='group/link flex items-center gap-4 px-6 py-4 transition-colors hover:bg-[var(--surface-hover)]'
>
<ProviderIcon
provider={provider}
className='size-8 shrink-0 rounded-xl'
iconClassName='size-4'
/>
<div className='flex min-w-0 flex-1 flex-col gap-0.5'>
<h4 className='text-[14px] text-[var(--text-primary)] leading-snug tracking-[-0.02em]'>
{model.displayName}
</h4>
<p className='line-clamp-1 hidden text-[12px] text-[var(--text-muted)] leading-[150%] sm:block'>
{model.id} &middot; Input {formatPrice(model.pricing.input)}/1M &middot; Output{' '}
{formatPrice(model.pricing.output)}/1M
{model.contextWindow ? ` · ${formatTokenCount(model.contextWindow)} context` : ''}
</p>
</div>
<ChevronArrow />
</Link>
</>
)
}
@@ -0,0 +1,31 @@
import Link from 'next/link'
import { ProviderIcon } from '@/app/(landing)/models/components/model-primitives/provider-icon'
import type { CatalogModel, CatalogProvider } from '@/app/(landing)/models/utils'
export function FeaturedModelCard({
provider,
model,
}: {
provider: CatalogProvider
model: CatalogModel
}) {
return (
<Link
href={model.href}
className='group flex flex-1 flex-col gap-4 border-[var(--border)] border-t p-6 transition-colors first:border-t-0 hover:bg-[var(--surface-hover)] sm:border-t-0 sm:border-l sm:first:border-l-0'
>
<ProviderIcon provider={provider} className='size-10 rounded-xl' iconClassName='size-5' />
<div className='flex flex-col gap-2'>
<span className='text-[var(--text-muted)] text-xs uppercase tracking-[0.1em]'>
{provider.name}
</span>
<h3 className='text-[var(--text-primary)] text-lg leading-tight tracking-[-0.01em]'>
{model.displayName}
</h3>
<p className='line-clamp-2 text-[var(--text-muted)] text-sm leading-[150%]'>
{model.summary}
</p>
</div>
</Link>
)
}
@@ -0,0 +1,22 @@
import Link from 'next/link'
import { ProviderIcon } from '@/app/(landing)/models/components/model-primitives/provider-icon'
import type { CatalogProvider } from '@/app/(landing)/models/utils'
export function FeaturedProviderCard({ provider }: { provider: CatalogProvider }) {
return (
<Link
href={provider.href}
className='group flex flex-1 flex-col gap-4 border-[var(--border)] border-t p-6 transition-colors first:border-t-0 hover:bg-[var(--surface-hover)] sm:border-t-0 sm:border-l sm:first:border-l-0'
>
<ProviderIcon provider={provider} className='size-10 rounded-xl' iconClassName='size-5' />
<div className='flex flex-col gap-2'>
<h3 className='text-[var(--text-primary)] text-lg leading-tight tracking-[-0.01em]'>
{provider.name}
</h3>
<p className='line-clamp-2 text-[var(--text-muted)] text-sm leading-[150%]'>
{provider.description}
</p>
</div>
</Link>
)
}
@@ -0,0 +1,3 @@
export { FeaturedModelCard } from './featured-model-card'
export { FeaturedProviderCard } from './featured-provider-card'
export { ProviderIcon } from './provider-icon'
@@ -0,0 +1,31 @@
import { cn } from '@sim/emcn'
import type { CatalogProvider } from '@/app/(landing)/models/utils'
export function ProviderIcon({
provider,
className = 'size-12 rounded-xl',
iconClassName = 'size-6',
}: {
provider: Pick<CatalogProvider, 'icon' | 'name'>
className?: string
iconClassName?: string
}) {
const Icon = provider.icon
return (
<span
className={cn(
'flex items-center justify-center border border-[var(--border-1)] bg-[var(--bg)]',
className
)}
>
{Icon ? (
<Icon className={iconClassName} />
) : (
<span className='text-[14px] text-[var(--text-primary)]'>
{provider.name.slice(0, 2).toUpperCase()}
</span>
)}
</span>
)
}
@@ -0,0 +1,128 @@
import Link from 'next/link'
import { getProviderColor } from '@/app/(landing)/models/components/constants'
import type { CatalogModel } from '@/app/(landing)/models/utils'
const SHORT_DATE_FORMAT = new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
timeZone: 'UTC',
})
function formatShortDate(date: string): string {
try {
return SHORT_DATE_FORMAT.format(new Date(date))
} catch {
return date
}
}
interface ModelTimelineChartProps {
models: CatalogModel[]
providerId: string
}
const ITEM_WIDTH = 150
export function ModelTimelineChart({ models, providerId }: ModelTimelineChartProps) {
const entries = models
.filter((m) => m.releaseDate !== null)
.map((m) => ({
model: m,
date: new Date(m.releaseDate as string),
dateStr: m.releaseDate as string,
}))
.sort((a, b) => a.date.getTime() - b.date.getTime())
if (entries.length === 0) return null
const color = getProviderColor(providerId)
return (
<section aria-labelledby='timeline-heading'>
<div className='px-6 pt-10 pb-4'>
<h2
id='timeline-heading'
className='mb-2 text-[20px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[24px]'
>
Release timeline
</h2>
<p className='text-[var(--text-muted)] text-sm leading-[150%] tracking-[0.02em]'>
When each model was first publicly available.
</p>
</div>
<div className='overflow-x-auto px-6 pb-8'>
{/* Fixed height: top labels + line + bottom labels */}
<div
className='relative h-[140px]'
style={{ minWidth: `${entries.length * ITEM_WIDTH}px` }}
>
{/* Horizontal line - vertically centered */}
<div className='absolute top-[70px] right-0 left-0 h-px bg-[var(--border-1)]' />
{entries.map(({ model, dateStr }, i) => {
const left = i * ITEM_WIDTH + ITEM_WIDTH / 2
const isAbove = i % 2 === 0
return (
<Link
key={model.id}
href={model.href}
className='group absolute flex flex-col items-center'
style={{
left: `${left}px`,
width: `${ITEM_WIDTH}px`,
marginLeft: `${-ITEM_WIDTH / 2}px`,
top: 0,
height: '100%',
}}
>
<div
className='-translate-x-1/2 absolute top-[64px] left-1/2 size-[12px] rounded-full opacity-[0.85] transition-[opacity,transform] duration-150 group-hover:scale-110 group-hover:opacity-100'
style={{ backgroundColor: color }}
/>
{/* Stem + label above */}
{isAbove && (
<div className='-translate-x-1/2 absolute bottom-[74px] left-1/2 flex flex-col items-center'>
<div className='flex flex-col items-center gap-0.5 pb-1.5'>
<span className='whitespace-nowrap text-[12px] text-[var(--text-primary)] leading-none tracking-[-0.01em] transition-colors group-hover:text-[var(--text-primary)]'>
{model.displayName}
</span>
<span className='whitespace-nowrap text-[10px] text-[var(--text-muted)] leading-none'>
{formatShortDate(dateStr)}
</span>
</div>
<div
className='w-px'
style={{ height: '10px', backgroundColor: color, opacity: 0.2 }}
/>
</div>
)}
{/* Stem + label below */}
{!isAbove && (
<div className='-translate-x-1/2 absolute top-[75px] left-1/2 flex flex-col items-center'>
<div
className='w-px'
style={{ height: '10px', backgroundColor: color, opacity: 0.2 }}
/>
<div className='flex flex-col items-center gap-0.5 pt-1.5'>
<span className='whitespace-nowrap text-[12px] text-[var(--text-primary)] leading-none tracking-[-0.01em] transition-colors group-hover:text-[var(--text-primary)]'>
{model.displayName}
</span>
<span className='whitespace-nowrap text-[10px] text-[var(--text-muted)] leading-none'>
{formatShortDate(dateStr)}
</span>
</div>
</div>
)}
</Link>
)
})}
</div>
</div>
</section>
)
}