1import Link from 'next/link';2import type { MarketRow } from '@/lib/queries/markets';3import { fmtNum, cn } from '@/lib/format';4import { Thumb } from '@/components/market/bits';5import { Badge } from '@/components/ui/primitives';67/** Browse-by-category grid: thumbnail of a representative asset, live counts, coverage state. */8export function CategoryGrid({ rows, thumbs, className, limit }: { rows: MarketRow[]; thumbs: Map<string, string>; className?: string; limit?: number }) {9 const sorted = [...rows].sort((a, b) => b.counts.assets - a.counts.assets || a.node.sortOrder - b.node.sortOrder);10 const shown = limit ? sorted.slice(0, limit) : sorted;11 return (12 <div className={cn('grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6', className)}>13 {shown.map(({ node, counts, snapshot }) => {14 const empty = counts.assets === 0;15 return (16 <Link key={node.slug} href={`/markets/${node.slug}`} className={cn('card flex items-center gap-2.5 p-2.5 transition-colors hover:border-border-strong', empty && 'opacity-60')}>17 <Thumb src={thumbs.get(node.slug)} alt="" size={44} rounded="rounded-md" />18 <span className="min-w-0 flex-1">19 <span className="block truncate text-[13px] font-medium text-fg">{node.name}</span>20 <span className="num block truncate text-[11px] text-muted">{empty ? 'no data yet' : `${fmtNum(counts.assets, { compact: true })} assets${counts.sales ? ` · ${fmtNum(counts.sales, { compact: true })} sales` : ''}`}</span>21 <span className="mt-0.5 flex items-center gap-1 text-[10px]">22 {counts.priced ? <Badge tone="gain">{fmtNum(counts.priced, { compact: true })} priced</Badge> : !empty ? <Badge>catalog</Badge> : null}23 {snapshot?.indexValue != null ? <Badge tone="index">index</Badge> : null}24 </span>25 </span>26 </Link>27 );28 })}29 </div>30 );31}32