import { Suspense } from 'react'; import Link from 'next/link'; import { Hero } from '@/components/home/hero'; import { IndexTape } from '@/components/market/index-strip'; import { AssetRail, LatestTransactions, LotsRail, MarketInsights, RadarRail, SalesRail } from '@/components/home/sections'; import { CategoryGrid } from '@/components/market/category-grid'; import { TileSkeleton } from '@/components/market/asset-tile'; import { attachGuidePrices, getCategoryThumbnails, getLatestObservationsFeed, rankedAssets } from '@/lib/queries/assets'; import { fmtNum } from '@/lib/format'; import { Card, Delta, Skeleton, TableSkeleton } from '@/components/ui/primitives'; import { getLatestSales, getSiteStats } from '@/lib/queries/site'; import { getIndexSeries, listIndices } from '@/lib/queries/indices'; import { getCategorySparklines, getMarketRows } from '@/lib/queries/markets'; import { getTopSales, listLots, listRadar } from '@/lib/queries/market-lists'; import { LineChart } from '@/components/charts/line-chart'; export const revalidate = 120; export default async function HomePage() { const [stats, indices] = await Promise.all([getSiteStats(), listIndices()]); return (
{/* §195 hierarchy: hero + search → live stats (in hero) → RARE tape → global index → market snapshot → trending → record sales → Rare Radar → value opportunities → ending auctions → latest sales → categories → long tail */}
}> }> }> }> }> }>
); } function RailFallback() { return (
{[0, 1].map((k) => (
{Array.from({ length: 8 }).map((_, i) => ( ))}
))}
); } async function Categories() { const markets = await getMarketRows(); const families = markets.filter((r) => r.counts.assets > 0); const [thumbs, sparklines] = await Promise.all([getCategoryThumbnails(markets.map((r) => r.node.slug)), getCategorySparklines(markets.map((r) => r.node.slug), 90)]); return (

Browse by category

Live counts from the canonical tables · 90-day category index where published

{fmtNum(families.length)} of {fmtNum(markets.length)} families with data →
); } async function FlagshipPanel() { const indices = await listIndices(); const rare = indices.find((i) => i.isFlagship); const subs = indices.filter((i) => !i.isFlagship); const published = subs.filter((i) => i.latest); const building = subs.filter((i) => !i.latest).sort((a, b) => b.pricedAssets / b.minConstituents - a.pricedAssets / a.minConstituents); const series = rare ? await getIndexSeries(rare.id, 365) : []; return (

RARE · Global Collectibles Index

{rare?.latest ? (

{rare.latest.value.toFixed(2)} 1D 30D

) : (

Transaction-weighted across published subindices · base 1,000 on 2024-01-01 · methodology

)}
Index detail →
({ x: p.date, y: p.value })) }]} emptyLabel={rare ? `Index building — publishes once ${rare.minConstituents} priced constituents exist (${rare.pricedAssets} today)` : 'Index not configured'} />

Subindices

All indices →
{published.length ? ( ) : null} {building.length ? (

In development · priced constituents vs. minimumWhy →

    {building.slice(0, 8).map((i) => { const pct = Math.min(100, Math.round((i.pricedAssets / Math.max(1, i.minConstituents)) * 100)); return (
  • {i.ticker.replace('RARE-', '')} {i.pricedAssets}/{i.minConstituents}
  • ); })}
) : null}
); } /** Market snapshot (§195): the category table right under the global index. */ async function Snapshot() { const markets = await getMarketRows(); return ; } /** Long tail below the fold: catalogue additions and community signals. */ async function LongTail() { const [newest, watched] = await Promise.all([rankedAssets('newest', { limit: 8 }), rankedAssets('watched', { limit: 8 })]); await attachGuidePrices(newest); return ( <> {watched.length ? : null} ); } async function Discovery() { const [trending, movers, losers, records, radar, lots, opportunities, newestPriced, documented, withSales] = await Promise.all([ rankedAssets('trending', { limit: 8 }), rankedAssets('gainers', { limit: 8, window: '7d' }), rankedAssets('losers', { limit: 8, window: '7d' }), getTopSales(8), listRadar({ limit: 8 }), listLots({ endingWithinHours: 72, status: null, limit: 8 }), rankedAssets('opportunity', { limit: 8 }), rankedAssets('newest_priced', { limit: 8 }), rankedAssets('documented', { limit: 8 }), rankedAssets('with_sales', { limit: 8 }), ]); await Promise.all([attachGuidePrices(documented), attachGuidePrices(trending), attachGuidePrices(newestPriced)]); // Honest fallbacks while valuations are still being computed: each rail states what it shows. const trendingRail = trending.length ? { title: 'Trending Now', subtitle: 'Price × volume × listing momentum', items: trending, metric: 'trending' as const, href: '/trending' } : newestPriced.length ? { title: 'Recently valued', subtitle: 'Newest RareIndex Valuations — trending publishes once momentum history exists', items: newestPriced, metric: 'riv' as const, href: '/explore?has=valuation&sort=riv' } : { title: 'Best-documented assets', subtitle: 'Images plus sales or guide prices', items: documented, metric: 'guide' as const, href: '/explore?has=observations' }; const moversItems = movers.length ? movers : losers; const moversRail = moversItems.length ? { title: 'Biggest Movers', subtitle: '7-day change in RareIndex Valuation', items: moversItems, metric: 'change7d' as const, href: '/explore?sort=change7d' } : withSales.length ? { title: 'Latest sold assets', subtitle: 'Most recent verified sale per asset — movers need a 7-day valuation history', items: withSales, metric: 'latestSale' as const, href: '/sales' } : null; return ( <> {moversRail ? : null} {opportunities.length ? : null} ); } async function Latest() { const items = await getLatestSales(12); const observations = items.length ? [] : await getLatestObservationsFeed(12); return ; }