import type { AssetDepth, AssetLiquidation } from '@/lib/queries/depth'; import { fmtMoney, fmtNum, fmtPct, cn } from '@/lib/format'; import { Card, CardHeader, Table, th, td, tdNum, Badge, Unavailable, VsRiv } from '@/components/ui/primitives'; const fmtDays = (d: number | null) => (d === null ? null : d < 1 ? '< 1 day' : `~${Math.round(d)} day${Math.round(d) === 1 ? '' : 's'}`); /** * Market depth (§25), RareIndex spread (§26), days on market (§30), time-to-liquidate (§24) and the * fair buy / sell ladder (§213–§214). Server-rendered; every number comes from observed listings and * the published valuation band, and missing evidence is shown as such. */ export function DepthLiquidationCard({ depth, liquidation, riv, variantLabel }: { depth: AssetDepth | null; liquidation: AssetLiquidation | null; riv: number | null; variantLabel: string | null }) { const d = depth?.depth ?? null; const m = liquidation?.model ?? null; const f = liquidation?.fair ?? null; const bands = d ? [ { label: '±5 %', n: d.within5 }, { label: '±10 %', n: d.within10 }, { label: '±20 %', n: d.within20 }, ] : []; const max = d ? Math.max(1, d.asks) : 1; return (
{/* Depth */}

Market depth

{d && riv !== null ? ( <>
Asks
{fmtNum(d.asks)}
Below RIV
{fmtNum(d.belowRiv)}
Above RIV
{fmtNum(d.aboveRiv)}
    {bands.map((b) => (
  • {b.label} {b.n}
  • ))}
Best ask {d.lowestAsk === null ? : fmtMoney(d.lowestAsk)} Median ask {d.medianAsk === null ? : fmtMoney(d.medianAsk)} Spread
) : (

Not enough data — depth needs a transaction-based valuation for this variant.

)}
{/* Days on market + bands */}

Days on market{' '} {liquidation ? ( Model estimate · {liquidation.level === 'category' ? 'category-level' : 'this asset'} ) : null}

{m ? ( <>
{[ ['Sold', m.sold.n === 0 ? null : fmtNum(m.sold.n)], ['Median', fmtDays(m.sold.median)], ['P25 – P75', m.sold.p25 === null || m.sold.p75 === null ? null : `${Math.round(m.sold.p25)} – ${Math.round(m.sold.p75)} d`], ['Sell-through', m.sellThrough === null ? null : fmtPct(m.sellThrough, 0, false)], ].map(([k, v]) => (
{k}
{v ?? }
))}

{fmtNum(m.withdrawn.n)} withdrawn/expired without a sale{m.withdrawn.median !== null ? ` (median ${fmtDays(m.withdrawn.median)} listed)` : ''} · {fmtNum(liquidation!.lifecycles)} lifecycles observed

{m.bands.map((b) => ( ))}
Asking band Sold Median time to sale
{b.label} {b.n} {b.medianDays === null ? Not enough data : fmtDays(b.medianDays)}
) : (

Not enough data — no completed listing lifecycles observed for this asset or its category yet.

)}
{/* Fair buy / sell ladder */}

Fair buy · fair sell

{riv !== null && f ? ( {[ ['Aggressive buy', f.buyAggressive, null, 'RIV low band'], ['Fair buy', f.buyFair, null, 'RIV'], ['Fast sale', f.sellFast, f.sellFastDays, 'band with the shortest observed time to sale'], ['Typical sale', f.sellTypical, f.sellTypicalDays, 'RIV'], ['Patient sale', f.sellPatient, f.sellPatientDays, 'RIV high band'], ].map(([k, v, days, basis]) => ( ))}
{k} {v === null || v === undefined ? : fmtMoney(v as number)} {days === null || days === undefined ? '' : fmtDays(days as number)}
) : (

Not enough data — the ladder needs a published valuation.

)}

Prices come from the RIV low/high band; days from the observed asking bands above. Model analytics, not advice.

); }