{"version":"5d4d92a48b14","source":"src/model-hub.jsx","code":"({ h: createElement, Fragment: __Fragment, useState, useEffect, useRef, useMemo, fetchData, now: clock = Date.now }) => {\n  // Text sets its quotes and apostrophes as curly ones, the way Mintlify sets\n  // page text. Code keeps them as typed.\n  const AS_TYPED = new Set(['code', 'pre', 'kbd', 'samp', 'textarea']);\n  const typeset = text => text\n    .replace(/(\\w)'(?=\\w)/g, '$1\\u2019')\n    .replace(/(s)'(?=$|[\\s.,;:!?)\\]])/g, '$1\\u2019')\n    .replace(/(^|[\\s([{\\u2013\\u2014])'/g, '$1\\u2018')\n    .replace(/'/g, '\\u2019')\n    .replace(/(^|[\\s([{\\u2013\\u2014])\"/g, '$1\\u201c')\n    .replace(/\"/g, '\\u201d');\n  const __jsx = (type, props, ...kids) => (type === __Fragment || (typeof type === 'string' && !AS_TYPED.has(type))\n    ? createElement(type, props, ...kids.map(kid => (typeof kid === 'string' && /['\"]/.test(kid) ? typeset(kid) : kid)))\n    : createElement(type, props, ...kids));\n\n  /* ------------------------------------------------------------ data */\n\n  const CATALOG_PATH = '/data/model-catalog.json';\n  const API = 'https://api.venice.ai/api/v1';\n  const store = { catalog: null, promise: null };\n\n  const loadCatalog = () => {\n    if (store.catalog) return Promise.resolve(store.catalog);\n    if (store.promise) return store.promise;\n    store.promise = fetchData(CATALOG_PATH)\n      .then(catalog => { store.catalog = catalog; return catalog; })\n      .catch(err => { store.promise = null; throw err; });\n    return store.promise;\n  };\n\n  const useCatalog = () => {\n    const [state, setState] = useState({ catalog: store.catalog, error: null });\n    useEffect(() => {\n      if (state.catalog) return undefined;\n      let alive = true;\n      loadCatalog()\n        .then(catalog => alive && setState({ catalog, error: null }))\n        .catch(error => alive && setState({ catalog: null, error }));\n      return () => { alive = false; };\n    }, []);\n    return state;\n  };\n\n  const useNow = () => useState(() => (typeof clock === 'function' ? clock() : clock))[0];\n\n  const readParams = () => (typeof window === 'undefined' ? new URLSearchParams() : new URLSearchParams(window.location.search));\n  const listParam = (params, key) => (params.get(key) ? params.get(key).split(',').filter(Boolean) : []);\n  const writeParams = updates => {\n    if (typeof window === 'undefined') return;\n    const params = readParams();\n    Object.entries(updates).forEach(([key, value]) => {\n      const empty = value == null || value === '' || value === false || (Array.isArray(value) && !value.length);\n      if (empty) params.delete(key);\n      else params.set(key, Array.isArray(value) ? value.join(',') : String(value));\n    });\n    const qs = params.toString();\n    window.history.replaceState(window.history.state, '', `${window.location.pathname}${qs ? `?${qs}` : ''}${window.location.hash}`);\n  };\n\n  // Links from API data open only as http(s); anything else, such as a\n  // javascript: URL, is dropped.\n  const safeUrl = value => {\n    try {\n      const url = new URL(String(value || ''));\n      return url.protocol === 'https:' || url.protocol === 'http:' ? url.href : null;\n    } catch (e2) {\n      return null;\n    }\n  };\n\n  const copyText = text => {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) return navigator.clipboard.writeText(text).catch(() => {});\n    return Promise.resolve();\n  };\n\n  /* ------------------------------------------------------------ vocabulary */\n\n  const MODALITIES = [\n    { key: 'text', label: 'Text', path: '/models/text' },\n    { key: 'image', label: 'Image', path: '/models/image' },\n    { key: 'video', label: 'Video', path: '/models/video' },\n    { key: 'audio', label: 'Audio', path: '/models/text-to-speech' },\n    { key: 'embedding', label: 'Embeddings', path: '/models/embeddings' }\n  ];\n  // The explorer's modality facet, checkboxes as in Gordon's design; none\n  // checked shows every modality. Each family has one key: audio splits by\n  // task and decision models stand apart from chat.\n  const SCOPES = [\n    { key: 'text', label: 'Text' }, { key: 'image', label: 'Image' }, { key: 'video', label: 'Video' },\n    { key: 'tts', label: 'Text to speech' }, { key: 'stt', label: 'Speech to text' }, { key: 'music', label: 'Music & sound effects' },\n    { key: 'embedding', label: 'Embeddings' }, { key: 'decision', label: 'Decisions' }\n  ];\n  const AUDIO_SCOPES = ['tts', 'stt', 'music'];\n  const familyScope = family => {\n    if (family.modality === 'text') return family.task === 'decision' ? 'decision' : 'text';\n    if (family.modality === 'audio') return family.task === 'sfx' ? 'music' : family.task;\n    return family.modality;\n  };\n  const initialScope = (modality, task) => (modality === 'all' ? null : modality === 'audio' ? task || 'tts' : modality);\n  // `m` names the chosen modality; `all` or nothing valid means All. Links from\n  // when several could be checked open on the first one they list.\n  const parseScope = value => String(value || '').split(',').find(key => SCOPES.some(s => s.key === key)) || null;\n  const VIEWS = ['list', 'table', 'grid'];\n  const PAGE_SIZE = 60;\n  const defaultView = modality => (modality === 'image' || modality === 'video' ? 'grid' : 'list');\n  const TASK_LABELS = {\n    chat: 'Chat', decision: 'Decisions', 'image-generation': 'Image generation', 'image-edit': 'Image editing',\n    'image-upscale': 'Upscaling', 'background-removal': 'Background removal', video: 'Video',\n    tts: 'Text to speech', stt: 'Speech to text', music: 'Music', sfx: 'Sound effects', embedding: 'Embeddings'\n  };\n  const VARIANT_LABELS = {\n    standard: 'Standard', fast: 'Fast', e2ee: 'E2EE', tee: 'TEE', generate: 'Generate', edit: 'Edit', upscale: 'Upscale',\n    'background-removal': 'Background removal', t2v: 'Text to video', i2v: 'Image to video', r2v: 'Reference to video',\n    flf: 'First & last frame', v2v: 'Video edit', motion: 'Motion control', transition: 'Transition', 'multi-angle': 'Multi-angle'\n  };\n  const MODE_SHORT = { t2v: 'T2V', i2v: 'I2V', r2v: 'R2V', flf: 'Keyframes', v2v: 'Edit', motion: 'Motion', transition: 'Transition', 'multi-angle': 'Multi-angle', upscale: 'Upscale' };\n  const PRIVACY = {\n    e2ee: { label: 'E2EE', long: 'End-to-end encrypted', icon: 'lock', desc: 'Prompts are encrypted on your device and only decrypted inside an attested hardware enclave. Neither Venice nor the GPU provider can read them.' },\n    tee: { label: 'TEE', long: 'Trusted execution', icon: 'shield', desc: 'Runs inside a hardware-secured enclave with cryptographic attestation. No prompt data is stored or accessible outside the enclave.' },\n    private: { label: 'Private', long: 'Zero data retention', icon: 'eyeOff', desc: 'Zero data retention, enforced by contract. Prompts and outputs are processed for inference only and not retained after the request completes.' },\n    anonymized: { label: 'Anonymized', long: 'Anonymized proxy', icon: 'mask', desc: 'Proxied to the upstream provider without your identity. The provider may retain prompt data; use a Private, TEE or E2EE model for sensitive work.' }\n  };\n  const PRIVACY_ORDER = ['e2ee', 'tee', 'private', 'anonymized'];\n  const TEXT_CAPS = [\n    { key: 'tools', label: 'Function calling', short: 'Tools', icon: 'tool', desc: 'Tool and function calls, including tool_choice' },\n    { key: 'structured', label: 'Structured outputs', short: 'JSON', icon: 'braces', desc: 'response_format with a JSON schema' },\n    { key: 'reasoning', label: 'Reasoning', short: 'Reasoning', icon: 'brain', desc: 'Thinks before answering' },\n    { key: 'effort', label: 'Reasoning effort control', short: 'Effort', icon: 'gauge', desc: 'Adjustable with reasoning_effort' },\n    { key: 'vision', label: 'Image input', short: 'Vision', icon: 'eye', desc: 'Accepts images in messages' },\n    { key: 'videoInput', label: 'Video input', short: 'Video in', icon: 'film', desc: 'Accepts video in messages' },\n    { key: 'audioInput', label: 'Audio input', short: 'Audio in', icon: 'mic', desc: 'Accepts audio in messages' },\n    { key: 'webSearch', label: 'Web search', short: 'Web', icon: 'globe', desc: 'Venice web search and citations' },\n    { key: 'xSearch', label: 'X search', short: 'X', icon: 'at', desc: 'Search posts on X' },\n    { key: 'caching', label: 'Prompt caching', short: 'Cache', icon: 'zap', desc: 'Discounted cache reads' },\n    { key: 'logprobs', label: 'Log probabilities', short: 'Logprobs', icon: 'activity', desc: 'Token logprobs in responses' },\n    { key: 'code', label: 'Optimized for code', short: 'Code', icon: 'code', desc: 'Tuned for software engineering' }\n  ];\n  const QUANT_LABELS = { fp4: 'FP4', fp8: 'FP8', fp16: 'FP16', bf16: 'BF16', int4: 'INT4' };\n  const QUANT_NOTES = {\n    fp4: '4-bit floating point weights. Lowest memory and cost; small accuracy loss on some tasks.',\n    int4: '4-bit integer weights. Lowest memory and cost; small accuracy loss on some tasks.',\n    fp8: '8-bit floating point. Near-lossless against BF16 on most benchmarks.',\n    bf16: '16-bit brain float, the precision most labs publish weights in.',\n    fp16: '16-bit floating point, full published precision.'\n  };\n\n  /* ------------------------------------------------------------ format */\n\n  const usd = n => {\n    if (n == null || !Number.isFinite(n)) return '—';\n    if (n === 0) return 'Free';\n    const a = Math.abs(n);\n    if (a >= 1000) return `$${Math.round(n).toLocaleString('en-US')}`;\n    if (a >= 100) return `$${n.toFixed(0)}`;\n    if (a >= 1) return `$${n.toFixed(2)}`;\n    // Sub-dollar prices keep up to four decimals, so $0.1725 isn't shown as $0.17.\n    if (a >= 0.0001) return `$${n.toFixed(4).replace(/0+$/, '').replace(/\\.(\\d?)$/, (m, d) => `.${d.padEnd(2, '0')}`)}`;\n    return `$${Number(n.toPrecision(2))}`;\n  };\n  // 128,000 is a multiple of 1024 too, so decimal sizes are checked first.\n  const tokens = n => {\n    if (!n) return '—';\n    if (n >= 1e6) return `${n % 1e6 && n % 1048576 === 0 ? n / 1048576 : Math.round(n / 1e4) / 100}M`;\n    if (n >= 1e3) return `${n % 1e3 && n % 1024 === 0 ? n / 1024 : Math.round(n / 1e3)}K`;\n    return String(n);\n  };\n  const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n  const fmtDate = ts => {\n    if (!ts) return '—';\n    const d = new Date(ts * 1000);\n    return `${MONTHS[d.getUTCMonth()]} ${d.getUTCDate()}, ${d.getUTCFullYear()}`;\n  };\n  const shortDate = ts => {\n    if (!ts) return '—';\n    const d = new Date(ts * 1000);\n    return `${MONTHS[d.getUTCMonth()]} ${d.getUTCFullYear()}`;\n  };\n  const isNew = (ts, now) => Boolean(now && ts && (now - ts * 1000) / 86400000 <= 30);\n  const plural = (n, word) => `${n.toLocaleString('en-US')} ${n === 1 ? word : word.endsWith('y') ? `${word.slice(0, -1)}ies` : `${word}s`}`;\n  const cls = (...parts) => parts.filter(Boolean).join(' ');\n\n  /* ------------------------------------------------------------ model helpers */\n\n  const privacyTiers = list => PRIVACY_ORDER.filter(tier => (list || []).includes(tier));\n  const hasCap = (model, key) => {\n    const text = model.text || {};\n    const caps = text.caps || {};\n    if (key === 'reasoning') return Boolean(text.reasoning);\n    if (key === 'effort') return Boolean(text.reasoning && text.reasoning.effort && text.reasoning.effort.length);\n    if (key === 'caching') return model.pricing && model.pricing.cacheRead != null;\n    return Boolean(caps[key]);\n  };\n  // E2EE models also run in TEE-only mode under the same ID.\n  const offersTier = (m, tier) => m.privacy === tier || (tier === 'tee' && Boolean(m.text && m.text.caps && m.text.caps.tee));\n  const maxVideoHeight = model => Math.max(0, ...((model.video && model.video.resolutions) || []).map(r => r.height || 0));\n  const maxVideoSeconds = model => Math.max(0, ...((model.video && model.video.durations) || []).map(d => d.seconds || 0));\n  const minVideoSeconds = model => {\n    const list = ((model.video && model.video.durations) || []).map(d => d.seconds).filter(Boolean);\n    return list.length ? Math.min(...list) : 0;\n  };\n  // Clip lengths in seconds, sorted, with the spacing between them when it's even.\n  const durationSteps = durations => {\n    const secs = (durations || []).map(d => d.seconds).filter(Boolean).sort((a, b) => a - b);\n    const step = secs.length > 2 ? secs[1] - secs[0] : null;\n    return { secs, step: step && secs.every((x, i) => i === 0 || x - secs[i - 1] === step) ? step : null };\n  };\n  // A long, evenly spaced list of lengths reads as a range.\n  const durationText = durations => {\n    const { secs, step } = durationSteps(durations);\n    if (secs.length > 6 && step) return `${secs[0]}–${secs[secs.length - 1]}s, ${step === 1 ? 'any whole second' : `in ${step}s steps`}`;\n    return (durations || []).map(d => (d.seconds ? `${d.seconds}s` : d.value)).join(', ');\n  };\n  const upperFirst = text => text.charAt(0).toUpperCase() + text.slice(1);\n  // Upscalers take no prompt. Seedance's edit model reads its source clip\n  // from reference_video_urls instead of video_url.\n  const videoInputList = video => {\n    const inputs = (video && video.inputs) || {};\n    return [\n      video && video.mode !== 'upscale' && 'prompt',\n      inputs.image && 'image',\n      inputs.endImage && 'end frame',\n      inputs.reference && 'reference images',\n      (inputs.video || inputs.referenceVideo) && 'video',\n      inputs.audio && 'audio'\n    ].filter(Boolean);\n  };\n  // Price rows for the video calculator and the static text when a model has\n  // more than six lengths: the shortest, 5s, 10s and the longest, led by a\n  // per-second row when every length costs the same rate per second.\n  const clipTable = (model, cols, audioKey) => {\n    const p = model.pricing || {};\n    const quotes = p.quotes || {};\n    const durs = ((model.video && model.video.durations) || []).filter(d => d.seconds).sort((a, b) => a.seconds - b.seconds);\n    if (durs.length <= 6) return null;\n    const first = durs[0];\n    const last = durs[durs.length - 1];\n    const rates = cols.map(c => {\n      const points = durs.map(d => [d.seconds, quotes[`${c.value}|${d.value}|${audioKey}`]]).filter(([, q]) => q != null);\n      if (points.length < 2) return null;\n      const fitted = points.reduce((sum, [sec, q]) => sum + sec * q, 0) / points.reduce((sum, [sec]) => sum + sec * sec, 0);\n      // Quotes are rounded to the cent.\n      if (!points.every(([sec, q]) => Math.abs(q - fitted * sec) <= 0.01)) return null;\n      return (p.perSecond || {})[`${c.value}|${audioKey}`] || fitted;\n    });\n    const perSecond = rates.every(rate => rate != null) ? rates : null;\n    const { step } = durationSteps(durs);\n    const span = step\n      ? `Any length from ${first.seconds} to ${last.seconds} seconds${step > 1 ? ` in ${step}-second steps` : ''}`\n      : `${durs.length} lengths from ${first.seconds} to ${last.seconds} seconds`;\n    return {\n      rows: durs.filter(d => d === first || d === last || d.seconds === 5 || d.seconds === 10),\n      rates: perSecond,\n      note: `${span}${perSecond ? ', priced per second' : ''}.`\n    };\n  };\n  const imageMaxRes = model => {\n    const list = (model.image && model.image.resolutions) || [];\n    if (list.includes('4K')) return '4K';\n    if (list.includes('2K')) return '2K';\n    if (list.includes('1K')) return '1K';\n    return list.length ? list[list.length - 1] : null;\n  };\n  const heightLabel = h => (h >= 2160 ? '4K' : h >= 1440 ? '1440p' : h ? `${h}p` : '—');\n  const listText = items => (items.length > 1 ? `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}` : items[0] || '');\n\n  const IMAGE_TASK_WORDS = { 'image-generation': 'generation', 'image-edit': 'editing', 'image-upscale': 'upscaling' };\n  // What a family does across its variants, e.g. \"Image generation and editing\".\n  const familyTask = (family, models) => {\n    if (family.task === 'chat') return null;\n    if (family.modality === 'video') return 'Video generation';\n    if (family.modality === 'image') {\n      const words = [...new Set(models.map(m => IMAGE_TASK_WORDS[m.task]).filter(Boolean))];\n      return words.length ? `Image ${listText(words)}` : TASK_LABELS[family.task] || null;\n    }\n    return TASK_LABELS[family.task] || null;\n  };\n\n  // Video prices are quote-derived at build time. A model page asks for a\n  // resolution, duration and audio setting; the closest supported option is\n  // used and flagged when it differs from the request.\n  const videoPrice = (model, lens) => {\n    const p = model.pricing || {};\n    if (p.status !== 'quoted') return { status: p.status || 'unavailable' };\n    const video = model.video || {};\n    const res = (video.resolutions || []).filter(r => r.height).sort((a, b) => a.height - b.height);\n    const pick = res.find(r => r.height === lens.h) || res.find(r => r.height > lens.h) || res[res.length - 1] || null;\n    const resKey = pick ? pick.value : '-';\n    const audioKey = video.audio === 'optional' ? (lens.a === 'on' ? 'on' : 'off') : (video.audio === 'native' ? 'on' : 'off');\n    const durs = (video.durations || []).map(d => d.seconds).filter(Boolean);\n    const dur = durs.includes(lens.s) ? lens.s : durs.reduce((best, d) => (Math.abs(d - lens.s) < Math.abs(best - lens.s) ? d : best), durs[0]);\n    const clip = p.quotes ? p.quotes[`${resKey}|${dur}s|${audioKey}`] : null;\n    let perSecond = p.perSecond ? p.perSecond[`${resKey}|${audioKey}`] : null;\n    if (perSecond == null && clip != null && dur) perSecond = clip / dur;\n    return {\n      status: 'quoted', perSecond, clip, seconds: dur, resolution: pick ? pick.label : null,\n      exactRes: !pick || pick.height === lens.h, exactDur: dur === lens.s,\n      audio: audioKey === 'on', forcedAudio: video.audio === 'native' && lens.a !== 'on', noAudio: video.audio === 'none' && lens.a === 'on'\n    };\n  };\n  // A model with duration options accepts only those lengths, one per tier.\n  const tierLabel = (t, fixed) => (fixed || t.from === t.upTo ? `${t.upTo}s` : t.from ? `${t.from}–${t.upTo}s` : `Up to ${t.upTo}s`);\n\n  // The explorer prices a model at its cheapest option, or, once the\n  // resolution, length or audio filter is set, at that setting. `from` marks\n  // the lowest of several prices and `breakdown` lists them.\n  const spread = (options, unit) => {\n    const values = options.map(([, value]) => value);\n    const varies = new Set(values).size > 1;\n    return {\n      value: values.length ? Math.min(...values) : null,\n      unit,\n      from: varies,\n      breakdown: varies ? options.map(([label, value]) => [label, `${usd(value)} ${unit}`]) : null\n    };\n  };\n  const IMAGE_RESOLUTIONS = ['1K', '2K', '4K'];\n  // Per second at the lowest resolution at or above the filter, with or\n  // without audio as filtered; a length filter adds the shortest clip that\n  // long.\n  const videoListPrice = (model, f) => {\n    const p = model.pricing || {};\n    if (p.status !== 'quoted') return { value: null, unit: '/ sec', status: p.status || 'unavailable' };\n    const video = model.video || {};\n    const h = Number(f.res) || 0;\n    const res = (video.resolutions || []).filter(r => r.height).sort((a, b) => a.height - b.height);\n    const shown = h ? res.filter(r => r.height >= h).slice(0, 1) : res;\n    const audio = video.audio !== 'optional' ? [video.audio === 'native' ? 'on' : 'off']\n      : f.audio === 'audio' ? ['on'] : f.audio === 'silent' ? ['off'] : ['off', 'on'];\n    const options = (shown.length ? shown : [{ value: '-', label: null }])\n      .flatMap(r => audio.map(a => ({ r, a, value: (p.perSecond || {})[`${r.value}|${a}`] })))\n      .filter(o => o.value != null);\n    if (!options.length) return { value: null, unit: '/ sec', status: 'unavailable' };\n    const label = o => {\n      const text = [o.r.label, audio.length > 1 ? (o.a === 'on' ? 'with audio' : 'silent') : null].filter(Boolean).join(' · ');\n      return text.charAt(0).toUpperCase() + text.slice(1);\n    };\n    const best = options.reduce((min, o) => (o.value < min.value ? o : min));\n    const out = { ...spread(options.map(o => [label(o), o.value]), '/ sec'), status: 'quoted' };\n    if (h && best.r.height && best.r.height !== h) out.note = `No ${heightLabel(h)}; priced at ${best.r.label}.`;\n    const s = Number(f.dur) || 0;\n    if (s) {\n      out.seconds = (video.durations || []).map(d => d.seconds).filter(Boolean).sort((a, b) => a - b).find(d => d >= s) || null;\n      out.clip = out.seconds && p.quotes ? p.quotes[`${best.r.value}|${out.seconds}s|${best.a}`] : null;\n    }\n    return out;\n  };\n  const listPrice = (model, f) => {\n    const p = model.pricing || {};\n    if (model.modality === 'video') return videoListPrice(model, f);\n    if (model.task === 'image-generation' && p.byResolution && Object.keys(p.byResolution).length) {\n      if (f.res && p.byResolution[f.res] != null) return { value: p.byResolution[f.res], unit: '/ image' };\n      return spread(IMAGE_RESOLUTIONS.filter(r => p.byResolution[r] != null).map(r => [r, p.byResolution[r]]), '/ image');\n    }\n    if (p.kind === 'tiered' && (p.tiers || []).length) return spread(p.tiers.map(t => [tierLabel(t, model.audio && model.audio.durationOptions), t.usd]), '/ track');\n    return { value: model.headline ? model.headline.value : null, unit: headlineUnit(model) };\n  };\n  // The unit a model is billed in, as the pricing section's note reads it.\n  const priceUnit = model => {\n    if (model.modality === 'text' || model.modality === 'embedding') return model.modality === 'embedding' ? 'per 1M input tokens' : 'per 1M tokens';\n    if (model.modality === 'video') return 'per clip';\n    if (model.task === 'tts') return 'per 1M characters';\n    if (model.task === 'stt') return 'per second of audio';\n    if (model.modality === 'audio') {\n      if (model.pricing && model.pricing.kind === 'tiered') return 'per track, by length';\n      return model.headline && model.headline.basis === 'track' ? 'per track' : 'per minute of audio';\n    }\n    if (model.task === 'image-edit') return 'per edit';\n    if (model.task === 'image-upscale') return 'per upscale';\n    return 'per image';\n  };\n\n  const headlineUnit = model => {\n    if (model.task === 'decision') return '/ 1M input tok';\n    if (model.modality === 'video') return '/ sec';\n    if (model.modality === 'text' && model.task === 'chat') return '/ 1M tok';\n    if (model.modality === 'embedding') return '/ 1M tok';\n    if (model.task === 'tts') return '/ 1M chars';\n    if (model.task === 'stt') return '/ audio hr';\n    if (model.modality === 'audio') return model.headline && model.headline.basis === 'track' ? '/ track' : '/ min';\n    if (model.task === 'image-edit') return '/ edit';\n    if (model.task === 'image-upscale') return '/ 2x';\n    if (model.modality === 'image') return '/ image';\n    return '';\n  };\n\n  // One line per variant, as the variant chips and the page's static text show it.\n  const priceSummary = m => (m.modality === 'text' && m.pricing ? `${usd(m.pricing.input)} / ${usd(m.pricing.output)} per 1M`\n    : m.modality === 'video' ? (m.pricing && m.pricing.status === 'quoted' ? `from ${usd(m.pricing.fromPerSecond)} / sec` : 'priced by source')\n      : m.headline && m.headline.value != null ? `${usd(m.headline.value)} ${headlineUnit(m)}` : '');\n\n  // The reference clips on a video model page.\n  const DRAFT_CLIP = { h: 720, s: 5, a: 'off' };\n  const PRODUCTION_CLIP = { h: 1080, s: 10, a: 'on' };\n\n  const searchText = (family, models, providers) => [\n    family.name, family.slug, providers[family.provider] && providers[family.provider].name, family.task,\n    ...models.map(m => m.id), ...(family.sets || []), ...(family.traits || [])\n  ].filter(Boolean).join(' ').toLowerCase();\n\n  const matchesQuery = (text, query) => query.toLowerCase().split(/\\s+/).filter(Boolean).every(token => text.includes(token));\n\n  /* ------------------------------------------------------------ icons */\n\n  const ICONS = {\n    search: __jsx(__Fragment, null, __jsx('circle', { cx: \"11\", cy: \"11\", r: \"7\",} ), __jsx('path', { d: \"m20 20-3.5-3.5\" ,} )),\n    x: __jsx('path', { d: \"M18 6 6 18M6 6l12 12\"     ,} ),\n    check: __jsx('path', { d: \"M20 6 9 17l-5-5\"   ,} ),\n    minus: __jsx('path', { d: \"M5 12h14\" ,} ),\n    plus: __jsx('path', { d: \"M12 5v14M5 12h14\"  ,} ),\n    copy: __jsx(__Fragment, null, __jsx('rect', { x: \"9\", y: \"9\", width: \"13\", height: \"13\", rx: \"2\",} ), __jsx('path', { d: \"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"                ,} )),\n    arrowRight: __jsx('path', { d: \"M5 12h14m-6-6 6 6-6 6\"    ,} ),\n    external: __jsx('path', { d: \"M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"                   ,} ),\n    chevronDown: __jsx('path', { d: \"m6 9 6 6 6-6\"    ,} ),\n    chevronRight: __jsx('path', { d: \"m9 18 6-6-6-6\"  ,} ),\n    sliders: __jsx('path', { d: \"M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6\"         ,} ),\n    grid: __jsx(__Fragment, null, __jsx('rect', { x: \"3\", y: \"3\", width: \"7\", height: \"7\", rx: \"1.5\",} ), __jsx('rect', { x: \"14\", y: \"3\", width: \"7\", height: \"7\", rx: \"1.5\",} ), __jsx('rect', { x: \"3\", y: \"14\", width: \"7\", height: \"7\", rx: \"1.5\",} ), __jsx('rect', { x: \"14\", y: \"14\", width: \"7\", height: \"7\", rx: \"1.5\",} )),\n    rows: __jsx('path', { d: \"M3 6h18M3 12h18M3 18h18\"   ,} ),\n    columns: __jsx(__Fragment, null, __jsx('rect', { x: \"3\", y: \"4\", width: \"7\", height: \"16\", rx: \"1.5\",} ), __jsx('rect', { x: \"14\", y: \"4\", width: \"7\", height: \"16\", rx: \"1.5\",} )),\n    tool: __jsx('path', { d: \"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.8-3.8a6 6 0 0 1-7.9 7.9l-6.9 6.9a2.1 2.1 0 0 1-3-3l6.9-6.9a6 6 0 0 1 7.9-7.9z\"                             ,} ),\n    braces: __jsx('path', { d: \"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5a2 2 0 0 0 2 2h1M16 21h1a2 2 0 0 0 2-2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1\"                                            ,} ),\n    brain: __jsx('path', { d: \"M12 5a3 3 0 1 0-6 .1 4 4 0 0 0-2.5 5.8 4 4 0 0 0 .6 6.6A4 4 0 1 0 12 18zM12 5a3 3 0 1 1 6 .1 4 4 0 0 1 2.5 5.8 4 4 0 0 1-.6 6.6A4 4 0 1 1 12 18zM12 5v13\"                                                    ,} ),\n    gauge: __jsx('path', { d: \"m12 14 4-4M3.3 19a10 10 0 1 1 17.4 0\"         ,} ),\n    eye: __jsx(__Fragment, null, __jsx('path', { d: \"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z\"        ,} ), __jsx('circle', { cx: \"12\", cy: \"12\", r: \"3\",} )),\n    eyeOff: __jsx('path', { d: \"M9.9 4.2A9 9 0 0 1 12 4c7 0 10 8 10 8a13 13 0 0 1-1.7 2.7M6.6 6.6A13 13 0 0 0 2 12s3 8 10 8a9 9 0 0 0 5.4-1.6M2 2l20 20M9.9 9.9a3 3 0 0 0 4.2 4.2\"                                         ,} ),\n    mask: __jsx('path', { d: \"M2 10c0-3 4-5 10-5s10 2 10 5c0 5-4 9-7 9-2 0-2-2-3-2s-1 2-3 2c-3 0-7-4-7-9zM7 11h3M14 11h3\"               ,} ),\n    film: __jsx(__Fragment, null, __jsx('rect', { x: \"2\", y: \"3\", width: \"20\", height: \"18\", rx: \"2\",} ), __jsx('path', { d: \"M7 3v18M17 3v18M2 8h5M2 16h5M17 8h5M17 16h5\"      ,} )),\n    mic: __jsx('path', { d: \"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3zM19 10v2a7 7 0 0 1-14 0v-2M12 19v3\"                       ,} ),\n    globe: __jsx(__Fragment, null, __jsx('circle', { cx: \"12\", cy: \"12\", r: \"10\",} ), __jsx('path', { d: \"M2 12h20M12 2a15 15 0 0 1 4 10 15 15 0 0 1-4 10 15 15 0 0 1-4-10 15 15 0 0 1 4-10z\"                         ,} )),\n    at: __jsx(__Fragment, null, __jsx('circle', { cx: \"12\", cy: \"12\", r: \"4\",} ), __jsx('path', { d: \"M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8\"            ,} )),\n    activity: __jsx('path', { d: \"M22 12h-4l-3 9L9 3l-3 9H2\"    ,} ),\n    zap: __jsx('path', { d: \"M13 2 3 14h9l-1 8 10-12h-9z\"     ,} ),\n    code: __jsx('path', { d: \"m16 18 6-6-6-6M8 6l-6 6 6 6\"      ,} ),\n    lock: __jsx(__Fragment, null, __jsx('rect', { x: \"4\", y: \"11\", width: \"16\", height: \"10\", rx: \"2\",} ), __jsx('path', { d: \"M8 11V7a4 4 0 0 1 8 0v4\"       ,} )),\n    shield: __jsx('path', { d: \"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"        ,} ),\n    shieldCheck: __jsx('path', { d: \"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10zM9 12l2 2 4-4\"           ,} ),\n    sparkles: __jsx('path', { d: \"m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9zM19 16l.7 1.8 1.8.7-1.8.7L19 21l-.7-1.8-1.8-.7 1.8-.7z\"            ,} ),\n    clock: __jsx(__Fragment, null, __jsx('circle', { cx: \"12\", cy: \"12\", r: \"10\",} ), __jsx('path', { d: \"M12 6v6l4 2\"  ,} )),\n    image: __jsx(__Fragment, null, __jsx('rect', { x: \"3\", y: \"3\", width: \"18\", height: \"18\", rx: \"2\",} ), __jsx('circle', { cx: \"9\", cy: \"9\", r: \"2\",} ), __jsx('path', { d: \"m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21\"       ,} )),\n    video: __jsx(__Fragment, null, __jsx('path', { d: \"m16 13 5.2 3.1a.5.5 0 0 0 .8-.4V8.3a.5.5 0 0 0-.8-.4L16 11\"           ,} ), __jsx('rect', { x: \"2\", y: \"6\", width: \"14\", height: \"12\", rx: \"2\",} )),\n    music: __jsx(__Fragment, null, __jsx('path', { d: \"M9 18V5l12-2v13\" ,} ), __jsx('circle', { cx: \"6\", cy: \"18\", r: \"3\",} ), __jsx('circle', { cx: \"18\", cy: \"16\", r: \"3\",} )),\n    text: __jsx('path', { d: \"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\"                  ,} ),\n    layers: __jsx('path', { d: \"m12 2 10 5-10 5L2 7zM2 17l10 5 10-5M2 12l10 5 10-5\"           ,} ),\n    info: __jsx(__Fragment, null, __jsx('circle', { cx: \"12\", cy: \"12\", r: \"10\",} ), __jsx('path', { d: \"M12 16v-4M12 8h.01\"  ,} )),\n    play: __jsx('path', { d: \"M7 4.5v15l12.5-7.5z\" , fill: \"currentColor\", stroke: \"none\",} ),\n    pause: __jsx('path', { d: \"M7 4h3.5v16H7zM13.5 4H17v16h-3.5z\"  , fill: \"currentColor\", stroke: \"none\",} ),\n    volume: __jsx('path', { d: \"M11 5 6 9H2v6h4l5 4zM15.5 8.5a5 5 0 0 1 0 7M19 5a10 10 0 0 1 0 14\"                  ,} ),\n    mute: __jsx('path', { d: \"M11 5 6 9H2v6h4l5 4zM22 9l-6 6M16 9l6 6\"        ,} ),\n    link: __jsx('path', { d: \"M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.7 1.7M14 11a5 5 0 0 0-7.5-.5l-3 3a5 5 0 0 0 7 7l1.7-1.7\"                       ,} ),\n    trophy: __jsx('path', { d: \"M6 9H4.5a2.5 2.5 0 0 1 0-5H6M18 9h1.5a2.5 2.5 0 0 0 0-5H18M4 22h16M10 14.7V17c0 .6-.5 1-1 1.2C7.9 18.8 7 20.2 7 22M14 14.7V17c0 .6.5 1 1 1.2 1.1.6 2 2 2 3.8M18 2H6v7a6 6 0 0 0 12 0z\"                                       ,} ),\n    flask: __jsx('path', { d: \"M9 3h6M10 9V3M14 9V3M10 9 4.5 19a2 2 0 0 0 1.8 3h11.4a2 2 0 0 0 1.8-3L14 9\"                  ,} ),\n    server: __jsx(__Fragment, null, __jsx('rect', { x: \"2\", y: \"3\", width: \"20\", height: \"8\", rx: \"2\",} ), __jsx('rect', { x: \"2\", y: \"13\", width: \"20\", height: \"8\", rx: \"2\",} ), __jsx('path', { d: \"M6 7h.01M6 17h.01\"  ,} )),\n    terminal: __jsx('path', { d: \"m4 17 6-6-6-6M12 19h8\"   ,} ),\n    book: __jsx('path', { d: \"M4 19.5A2.5 2.5 0 0 1 6.5 17H20V2H6.5A2.5 2.5 0 0 0 4 4.5zM4 19.5A2.5 2.5 0 0 0 6.5 22H20v-5\"                    ,} ),\n    maximize: __jsx('path', { d: \"M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3\"                        ,} ),\n    dollar: __jsx('path', { d: \"M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6\"              ,} ),\n    bolt: __jsx('path', { d: \"M13 2 3 14h9l-1 8 10-12h-9z\"     ,} ),\n    wand: __jsx('path', { d: \"m15 4 5 5M4 20 16 8M18 2v4M20 4h-4M8 2v2M9 3H7\"          ,} )\n  };\n  const Icon = ({ name, size = 16, className }) => (\n    __jsx('svg', { className: cls('vx-icon', className), width: size, height: size, viewBox: \"0 0 24 24\"   , fill: \"none\", stroke: \"currentColor\", strokeWidth: \"1.8\", strokeLinecap: \"round\", strokeLinejoin: \"round\", 'aria-hidden': \"true\",}\n      , ICONS[name] || null\n    )\n  );\n\n  /* ------------------------------------------------------------ atoms */\n\n  const ProviderLogo = ({ provider, size = 32 }) => (\n    __jsx('span', { className: cls('vx-logo', size <= 24 && 'vx-logo-sm'), style: { width: size, height: size }, 'aria-hidden': \"true\",}\n      , __jsx('span', { className: \"vx-logo-mask\", style: { '--vx-logo': `url('${(provider && provider.logo) || '/images/icons/models/text.svg'}')` },} )\n    )\n  );\n\n  const PrivacyBadge = ({ tier }) => {\n    const meta = PRIVACY[tier];\n    if (!meta) return null;\n    return __jsx('span', { className: cls('vx-badge', `vx-privacy-${tier}`), title: `${meta.long}. ${meta.desc}`,}, meta.label);\n  };\n\n  const PrivacyStack = ({ tiers }) => (\n    __jsx('span', { className: \"vx-badges\",}\n      , privacyTiers(tiers).map(tier => __jsx(PrivacyBadge, { key: tier, tier: tier,} ))\n    )\n  );\n\n  const Tag = ({ tone, children, title }) => (\n    __jsx('span', { className: cls('vx-badge', tone && `vx-badge-${tone}`), title: title,}, children)\n  );\n\n  const StatusTags = ({ item, now }) => (\n    __jsx(__Fragment, null\n      , item.appDefault ? __jsx(Tag, { tone: \"app-default\", title: \"The default model used by the Venice app.\"       ,}, \"App Default\" ) : null\n      , isNew(item.created, now) ? __jsx(Tag, { tone: \"new\",}, \"New\") : null\n      , item.beta ? __jsx(Tag, { tone: \"beta\", title: \"Experimental. May change or be removed without notice.\"       ,}, \"Beta\") : null\n      , item.deprecated || item.deprecation ? __jsx(Tag, { tone: \"deprecated\", title: \"Scheduled for removal. See Deprecations.\"    ,}, \"Deprecated\") : null\n    )\n  );\n\n  const CopyButton = ({ text, label, className, iconOnly, small }) => {\n    const [done, setDone] = useState(false);\n    const onClick = event => {\n      event.preventDefault();\n      event.stopPropagation();\n      copyText(text).then(() => {\n        setDone(true);\n        setTimeout(() => setDone(false), 1400);\n      });\n    };\n    return (\n      __jsx('button', { type: \"button\", className: cls(iconOnly ? 'vx-icon-btn' : 'vx-btn', small && 'vx-btn-sm', done && 'is-done', className), onClick: onClick, 'aria-label': label || `Copy ${text}`, title: iconOnly ? (label || 'Copy') : undefined,}\n        , __jsx(Icon, { name: done ? 'check' : 'copy', size: 14,} )\n        , iconOnly ? __jsx('span', { className: \"vx-sr-only\", 'aria-live': \"polite\",}, done ? 'Copied' : '') : __jsx('span', { 'aria-live': \"polite\",}, done ? 'Copied' : (label || 'Copy'))\n      )\n    );\n  };\n\n  const ModelId = ({ id }) => {\n    const [done, setDone] = useState(false);\n    const copy = () => copyText(id).then(() => {\n      setDone(true);\n      setTimeout(() => setDone(false), 1400);\n    });\n    return (\n      __jsx('span', { className: \"vx-id\",}\n        , __jsx('code', { title: id,}, id)\n        , __jsx('button', { type: \"button\", className: cls('vx-id-copy', done && 'is-done'), onClick: copy, 'aria-label': `Copy model ID ${id}`,}\n          , __jsx('span', { 'aria-live': \"polite\",}, done ? 'Copied' : 'Copy')\n        )\n      )\n    );\n  };\n\n  const Segmented = ({ options, value, onChange, ariaLabel }) => (\n    __jsx('div', { className: \"vx-seg\", role: \"radiogroup\", 'aria-label': ariaLabel,}\n      , options.map(option => (\n        __jsx('button', { key: String(option.value), type: \"button\", role: \"radio\", 'aria-checked': value === option.value,\n          className: cls('vx-seg-btn', value === option.value && 'is-active'), disabled: option.disabled,\n          onClick: () => onChange(option.value), title: option.title, 'aria-label': option.label ? undefined : option.title,}\n          , option.icon ? __jsx(Icon, { name: option.icon, size: 14,} ) : null\n          , option.label\n        )\n      ))\n    )\n  );\n\n  const CapIcons = ({ model }) => (\n    __jsx('span', { className: \"vx-caps\",}\n      , TEXT_CAPS.filter(cap => ['tools', 'reasoning', 'vision', 'structured', 'webSearch'].includes(cap.key) && hasCap(model, cap.key)).map(cap => (\n        __jsx('span', { key: cap.key, className: \"vx-cap\", title: cap.label,}, __jsx(Icon, { name: cap.icon, size: 15,} ), __jsx('span', { className: \"vx-sr-only\",}, cap.label))\n      ))\n    )\n  );\n\n  const Stat = ({ label, value, sub, last }) => (\n    __jsx('div', { className: cls('vx-stat', last && 'is-last'),}\n      , __jsx('div', { className: \"vx-stat-label\",}, label)\n      , __jsx('div', { className: \"vx-stat-value\",}, value)\n      , sub ? __jsx('div', { className: \"vx-stat-sub\",}, sub) : null\n    )\n  );\n\n  const Empty = ({ title, children }) => (\n    __jsx('div', { className: \"vx-empty\", role: \"status\",}\n      , __jsx('div', { className: \"vx-empty-title\",}, title)\n      , children ? __jsx('div', { className: \"vx-empty-body\",}, children) : null\n    )\n  );\n\n  const CatalogSkeleton = () => (\n    __jsx('div', { className: \"vx-skeleton\", 'aria-hidden': \"true\",}\n      , Array.from({ length: 8 }, (_, i) => __jsx('div', { key: i, className: \"vx-skel-row\",}, __jsx('span', null ), __jsx('span', null ), __jsx('span', null ), __jsx('span', null )))\n    )\n  );\n\n  /* ------------------------------------------------------------ media */\n\n  const sampleFor = (model, preferred) => {\n    const media = model.media || [];\n    return media.find(m => m.prompt === preferred) || media[0] || null;\n  };\n\n  // Sources attach only as a tile nears the viewport, so a gallery doesn't\n  // open dozens of video requests at once; a skeleton holds the tile until\n  // the first frame paints.\n  const HoverVideo = ({ src, poster, className, autoPlay }) => {\n    const ref = useRef(null);\n    const [near, setNear] = useState(false);\n    const [ready, setReady] = useState(false);\n    useEffect(() => {\n      const el = ref.current;\n      if (!el || near) return undefined;\n      if (typeof IntersectionObserver === 'undefined') { setNear(true); return undefined; }\n      const observer = new IntersectionObserver(entries => {\n        if (entries.some(entry => entry.isIntersecting)) { setNear(true); observer.disconnect(); }\n      }, { rootMargin: '300px 0px' });\n      observer.observe(el);\n      return () => observer.disconnect();\n    }, [near]);\n    const play = () => { if (ref.current && near) ref.current.play().catch(() => {}); };\n    const stop = () => { if (ref.current && !autoPlay) { ref.current.pause(); } };\n    const show = () => setReady(true);\n    // Without a poster, the #t fragment makes browsers seek and paint a first frame.\n    const url = poster || /#t=/.test(src) ? src : `${src}#t=0.1`;\n    return (\n      __jsx(__Fragment, null\n        , ready ? null : __jsx('span', { className: \"vx-media-skeleton\", 'aria-hidden': \"true\",} )\n        , __jsx('video', { ref: ref, className: cls(className, !ready && 'is-loading'), src: near ? url : undefined, poster: poster, muted: true, loop: true, playsInline: true, preload: \"metadata\",\n          autoPlay: autoPlay, onLoadedData: show, onError: show, onMouseEnter: play, onMouseLeave: stop, onFocus: play, onBlur: stop,} )\n      )\n    );\n  };\n\n  // venice.ai serves its samples through its image optimizer at these widths.\n  const SAMPLE_WIDTHS = [256, 384, 640, 828];\n  const SAMPLE_PATH = /^https:\\/\\/venice\\.ai(\\/samples\\/[^?#]+)$/;\n  const sampleSrcSet = url => {\n    const match = SAMPLE_PATH.exec(url);\n    if (!match) return undefined;\n    const path = encodeURIComponent(match[1]);\n    return SAMPLE_WIDTHS.map(w => `https://venice.ai/_next/image?url=${path}&w=${w}&q=75 ${w}w`).join(', ');\n  };\n  // Tiles run two to a row on phones and about 280px wide elsewhere.\n  const CARD_SIZES = '(max-width: 700px) calc(50vw - 22px), 280px';\n\n  const MediaFallback = ({ label = 'No reference render yet' }) => (\n    __jsx('div', { className: \"vx-media-fallback\",}, __jsx('span', null, label))\n  );\n\n  const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), video[controls], [tabindex]:not([tabindex=\"-1\"])';\n\n  // Modal: focus moves to Close, Tab cycles inside the dialog, and focus\n  // returns to the opener when it closes.\n  const Lightbox = ({ item, onClose }) => {\n    const closeRef = useRef(null);\n    const dialogRef = useRef(null);\n    const onCloseRef = useRef(onClose);\n    onCloseRef.current = onClose;\n    useEffect(() => {\n      if (!item) return undefined;\n      const previous = document.activeElement;\n      if (closeRef.current) closeRef.current.focus();\n      const onKey = e => {\n        if (e.key === 'Escape') onCloseRef.current();\n        if (e.key !== 'Tab' || !dialogRef.current) return;\n        const items = [...dialogRef.current.querySelectorAll(FOCUSABLE)];\n        if (!items.length) return;\n        const first = items[0];\n        const last = items[items.length - 1];\n        const inside = dialogRef.current.contains(document.activeElement);\n        if (e.shiftKey && (document.activeElement === first || !inside)) { e.preventDefault(); last.focus(); }\n        else if (!e.shiftKey && (document.activeElement === last || !inside)) { e.preventDefault(); first.focus(); }\n      };\n      window.addEventListener('keydown', onKey);\n      return () => {\n        window.removeEventListener('keydown', onKey);\n        if (previous && previous.focus) previous.focus();\n      };\n    }, [item]);\n    if (!item) return null;\n    return (\n      __jsx('div', { ref: dialogRef, className: \"vx-lightbox\", role: \"dialog\", 'aria-modal': \"true\", 'aria-label': item.title, onClick: onClose,}\n        , __jsx('div', { className: \"vx-lightbox-inner\", onClick: e => e.stopPropagation(),}\n          , __jsx('button', { ref: closeRef, type: \"button\", className: \"vx-icon-btn vx-lightbox-close\" , onClick: onClose, 'aria-label': \"Close\",}, __jsx(Icon, { name: \"x\", size: 18,} ))\n          , __jsx('div', { className: \"vx-lightbox-media\",}\n            , item.type === 'video'\n              ? __jsx('video', { src: item.url, controls: true, autoPlay: true, loop: true, playsInline: true,} )\n              : __jsx('img', { src: item.url, alt: item.caption || item.title,} )\n          )\n          , __jsx('div', { className: \"vx-lightbox-meta\",}\n            , __jsx('div', { className: \"vx-lightbox-title\",}, item.title)\n            , __jsx('p', null, item.caption)\n          )\n        )\n      )\n    );\n  };\n\n  const MediaGallery = ({ model }) => {\n    const [open, setOpen] = useState(null);\n    const media = model.media || [];\n    const isVideo = model.modality === 'video';\n    if (!media.length) {\n      return __jsx('p', { className: \"vx-text\",}, \"This variant has not been rendered on the reference prompts yet.\"          );\n    }\n    return (\n      __jsx(__Fragment, null\n        , __jsx('div', { className: cls('vx-gallery', isVideo ? 'vx-gallery-video' : 'vx-gallery-image', !isVideo && media.length === 4 && 'is-four'),}\n          , media.map(item => (\n            __jsx('figure', { key: item.url, className: \"vx-shot\",}\n              , __jsx('button', { type: \"button\", className: \"vx-shot-media\", onClick: () => setOpen(item), 'aria-label': `Open ${item.title} sample`,}\n                , item.type === 'video'\n                  ? __jsx(HoverVideo, { src: item.url, className: \"vx-shot-el\",} )\n                  : __jsx('img', { className: \"vx-shot-el\", src: item.url, alt: item.caption || item.title, loading: \"lazy\",} )\n              )\n              , __jsx('figcaption', null\n                , __jsx('span', { className: \"vx-shot-title\",}, item.title)\n                , item.caption ? __jsx('span', { className: \"vx-shot-caption\", title: item.caption,}, item.caption) : null\n              )\n            )\n          ))\n        )\n        , __jsx(Lightbox, { item: open, onClose: () => setOpen(null),} )\n      )\n    );\n  };\n\n  /* ------------------------------------------------------------ explorer */\n\n  const PAGE_TITLES = {\n    all: 'Models', text: 'Text Models', image: 'Image Models', video: 'Video Models', embedding: 'Embedding Models',\n    tts: 'Text-to-Speech Models', stt: 'Speech-to-Text Models', music: 'Music & Sound Effects Models', decision: 'Decision Models'\n  };\n  const UNIT_NOTES = { text: 'prices per 1M tokens' };\n  // The setting the filters price image and video at, for the count line.\n  const settingNote = (modality, f) => {\n    if (modality === 'image' && f.res) return `prices at ${f.res}`;\n    if (modality !== 'video') return null;\n    const h = Number(f.res) || 0;\n    const parts = [h ? `${heightLabel(h)}${h < 2160 ? ' or higher' : ''}` : null, f.audio === 'audio' ? 'with audio' : f.audio === 'silent' ? 'silent' : null].filter(Boolean);\n    return parts.length ? `prices at ${parts.join(', ')}` : null;\n  };\n  const SCOPE_BLURBS = {\n    all: 'Every model on the Venice API across text, image, video, audio and embeddings, with normalized prices, limits and privacy tiers.',\n    text: 'Chat, reasoning, coding and agent models, priced per 1M tokens.',\n    image: 'Image generation, editing, upscaling and background removal, priced per image.',\n    video: 'Text, image and reference to video, video editing and upscaling, priced per clip from quotes.',\n    tts: 'Speech synthesis with multilingual voices, priced per character.',\n    stt: 'Transcription with timestamps, priced per second of audio.',\n    music: 'Songs, instrumental tracks and sound effects, priced per track or minute.',\n    embedding: 'Vectors for search and retrieval, priced per 1M input tokens.',\n    decision: 'Typed judgments instead of generated text, priced per 1M tokens.'\n  };\n\n  const EMPTY_FILTERS = { providers: [], privacy: [], caps: [], modes: [], flags: [], quant: [], ctx: '', price: '', res: '', audio: '', dur: '' };\n\n  const filtersFromParams = params => ({\n    providers: listParam(params, 'provider'), privacy: listParam(params, 'privacy'), caps: listParam(params, 'cap'),\n    modes: listParam(params, 'mode'), flags: listParam(params, 'flag'), quant: listParam(params, 'quant'),\n    ctx: params.get('ctx') || '', price: params.get('price') || '', res: params.get('res') || '', audio: params.get('audio') || '', dur: params.get('dur') || ''\n  });\n  const filtersToParams = f => ({\n    provider: f.providers, privacy: f.privacy, cap: f.caps, mode: f.modes, flag: f.flags, quant: f.quant,\n    ctx: f.ctx, price: f.price, res: f.res, audio: f.audio, dur: f.dur\n  });\n  const activeFilterCount = f => ['providers', 'privacy', 'caps', 'modes', 'flags', 'quant'].reduce((n, k) => n + f[k].length, 0) +\n    ['ctx', 'price', 'res', 'audio', 'dur'].filter(k => f[k]).length;\n\n  // A family matches when any of its variants matches; the first matching\n  // variant becomes the row's display variant so prices follow the filters.\n  const variantMatches = (model, f, catalog) => {\n    if (f.privacy.length && !f.privacy.some(tier => offersTier(model, tier))) return false;\n    if (f.caps.length) {\n      if (model.modality === 'text' && !f.caps.every(key => hasCap(model, key))) return false;\n      if (model.modality === 'image' && f.caps.includes('webSearch') && !(model.image && model.image.webSearch)) return false;\n      if (model.modality === 'image' && f.caps.includes('multiImage') && !((model.image && model.image.maxInputImages) > 1)) return false;\n    }\n    if (f.modes.length && model.modality === 'video' && !f.modes.includes(model.variant)) return false;\n    if (f.modes.length && model.modality === 'image') {\n      const map = { generate: 'image-generation', edit: 'image-edit', upscale: 'image-upscale', 'background-removal': 'background-removal' };\n      if (!f.modes.some(mode => map[mode] === model.task)) return false;\n    }\n    if (f.quant.length && model.modality === 'text') {\n      const q = (model.text && model.text.quantization) || 'none';\n      if (!f.quant.includes(q)) return false;\n    }\n    if (f.ctx && model.modality === 'text' && !((model.text && model.text.context) >= Number(f.ctx))) return false;\n    if (f.price && model.modality === 'text' && !(model.pricing && model.pricing.input != null && model.pricing.input <= Number(f.price))) return false;\n    if (f.res && model.modality === 'image' && !((model.image && model.image.resolutions) || []).includes(f.res)) return false;\n    if (f.res && model.modality === 'video' && !(maxVideoHeight(model) >= Number(f.res))) return false;\n    if (f.audio && model.modality === 'video') {\n      const audio = model.video && model.video.audio;\n      if (f.audio === 'audio' && audio === 'none') return false;\n      if (f.audio === 'silent' && audio === 'native') return false;\n    }\n    if (f.dur && model.modality === 'video' && !(maxVideoSeconds(model) >= Number(f.dur))) return false;\n    return true;\n  };\n\n  const modelsOf = (catalog, family) => family.variants.map(id => catalog.models[id]).filter(Boolean);\n  // Groups whose checked options must all hold; in the others any may.\n  const ALL_OF = ['caps', 'flags'];\n\n  const familyMatches = (family, models, f, now) => {\n    if (f.providers.length && !f.providers.includes(family.provider)) return false;\n    if (f.flags.includes('app-default') && !family.appDefault) return false;\n    if (f.flags.includes('new') && !isNew(family.updated || family.created, now)) return false;\n    if (f.flags.includes('open') && !family.openWeights) return false;\n    if (f.flags.includes('uncensored') && !family.uncensored) return false;\n    return models.some(model => variantMatches(model, f));\n  };\n\n  const SORTS = {\n    name: { label: 'Name A–Z', dir: 'ascending', fn: (a, b) => a.family.name.localeCompare(b.family.name) },\n    newest: { label: 'Newest', dir: 'descending', fn: (a, b) => (b.family.updated || 0) - (a.family.updated || 0) || a.family.name.localeCompare(b.family.name) },\n    price: { label: 'Price, low to high', dir: 'ascending', fn: (a, b) => (a.price == null ? 1e9 : a.price) - (b.price == null ? 1e9 : b.price) },\n    context: { label: 'Context, largest', dir: 'descending', fn: (a, b) => ((b.display.text && b.display.text.context) || 0) - ((a.display.text && a.display.text.context) || 0) },\n    input: { label: 'Input price, low to high', dir: 'ascending', fn: (a, b) => ((a.display.pricing && a.display.pricing.input) || 1e9) - ((b.display.pricing && b.display.pricing.input) || 1e9) },\n    output: { label: 'Output price, low to high', dir: 'ascending', fn: (a, b) => ((a.display.pricing && a.display.pricing.output) || 1e9) - ((b.display.pricing && b.display.pricing.output) || 1e9) }\n  };\n  const defaultSort = modality => (modality === 'all' ? 'newest' : 'name');\n\n  const FilterGroup = ({ title, children, defaultOpen = true, role }) => {\n    const [open, setOpen] = useState(defaultOpen);\n    return (\n      __jsx('div', { className: cls('vx-fgroup', open && 'is-open'),}\n        , __jsx('button', { type: \"button\", className: \"vx-fgroup-head\", onClick: () => setOpen(!open), 'aria-expanded': open,}\n          , __jsx('span', null, title)\n          , __jsx(Icon, { name: \"chevronDown\", size: 14,} )\n        )\n        , open ? __jsx('div', { className: \"vx-fgroup-body\", role: role, 'aria-label': role ? title : undefined,}, children) : null\n      )\n    );\n  };\n\n  const Check = ({ checked, onChange, label, count, title, disabled }) => (\n    __jsx('label', { className: cls('vx-check', checked && 'is-checked', disabled && 'is-disabled'), title: title,}\n      , __jsx('input', { type: \"checkbox\", checked: checked, onChange: onChange, disabled: disabled,} )\n      , __jsx('span', { className: \"vx-check-box\",}, __jsx(Icon, { name: \"check\", size: 11,} ))\n      , __jsx('span', { className: \"vx-check-label\",}, label)\n      , count != null ? __jsx('span', { className: \"vx-check-count\",}, count) : null\n    )\n  );\n\n  const Radio = ({ name, checked, onChange, label, count }) => (\n    __jsx('label', { className: cls('vx-check vx-radio', checked && 'is-checked'),}\n      , __jsx('input', { type: \"radio\", name: name, checked: checked, onChange: onChange,} )\n      , __jsx('span', { className: \"vx-check-box\",} )\n      , __jsx('span', { className: \"vx-check-label\",}, label)\n      , __jsx('span', { className: \"vx-check-count\",}, count)\n    )\n  );\n\n  // The filter column; below 1024px it opens as a drawer from the toolbar.\n  const DRAWER_MAX_WIDTH = 1024;\n  const FilterRail = ({ modality, task, scope, scopeCounts, onScope, filters, setFilters, facetCount, providerCounts, providers, onClear, open, onClose }) => {\n    const toggleIn = (key, value) => setFilters(prev => ({ ...prev, [key]: prev[key].includes(value) ? prev[key].filter(v => v !== value) : [...prev[key], value] }));\n    // An option nothing in scope matches is disabled unless already checked.\n    const option = (key, value, label, title) => {\n      const checked = filters[key].includes(value);\n      const count = facetCount(key, value);\n      return __jsx(Check, { key: value, checked: checked, onChange: () => toggleIn(key, value), label: label, title: title, count: count, disabled: !checked && count === 0,} );\n    };\n    // Single-choice filters are segmented tracks, as in Gordon's column.\n    const range = (key, title, options) => (\n      __jsx(FilterGroup, { title: title,}\n        , __jsx(Segmented, { ariaLabel: title, value: filters[key], onChange: value => setFilters(prev => ({ ...prev, [key]: value })),\n          options: options.map(([value, label, hint]) => ({ value, label, title: hint })),} )\n      )\n    );\n    const [showAll, setShowAll] = useState(false);\n    const providerName = slug => (providers[slug] && providers[slug].name) || slug;\n    const providerList = Object.entries(providerCounts).sort((a, b) => b[1] - a[1] || providerName(a[0]).localeCompare(providerName(b[0])));\n    // The short list is the top ten that would leave results, plus any checked\n    // provider wherever it ranks; Show all adds the rest, disabled.\n    const visibleProviders = showAll ? providerList : providerList.filter(([slug, n], i) => (i < 10 && n > 0) || filters.providers.includes(slug));\n    const count = activeFilterCount(filters);\n    return (\n      __jsx('aside', { id: \"vx-filters\", className: cls('vx-side vx-filters', open && 'is-open'), 'aria-label': \"Filters\",\n        role: open ? 'dialog' : undefined, 'aria-modal': open ? 'true' : undefined,}\n        , __jsx('div', { className: \"vx-side-inner\",}\n          , __jsx('div', { className: \"vx-rail-head\",}\n            , __jsx('span', null, \"Filters\")\n            , count ? __jsx('button', { type: \"button\", className: \"vx-link-btn\", onClick: onClear,}, \"Clear all (\"  , count, \")\") : null\n            , __jsx('button', { type: \"button\", className: \"vx-btn vx-btn-sm vx-rail-done\"  , onClick: onClose,}, \"Done\")\n          )\n\n          , __jsx(FilterGroup, { title: \"Modality\", role: \"radiogroup\",}\n            , __jsx(Radio, { name: \"vx-modality\", checked: !scope, onChange: () => onScope(null), label: \"All\", count: Object.values(scopeCounts).reduce((n, c) => n + c, 0),} )\n            , SCOPES.filter(s => scopeCounts[s.key]).map(s => (\n              __jsx(Radio, { key: s.key, name: \"vx-modality\", checked: scope === s.key, onChange: () => onScope(s.key), label: s.label, count: scopeCounts[s.key],} )\n            ))\n          )\n\n          , __jsx(FilterGroup, { title: \"Privacy\",}\n            , PRIVACY_ORDER.map(tier => option('privacy', tier, PRIVACY[tier].long, PRIVACY[tier].desc))\n          )\n\n          , modality === 'text' ? (\n            __jsx(__Fragment, null\n              , __jsx(FilterGroup, { title: \"Capabilities\",}\n                , TEXT_CAPS.filter(c => !['logprobs', 'xSearch', 'audioInput'].includes(c.key)).map(cap => option('caps', cap.key, cap.label, cap.desc))\n              )\n              , range('ctx', 'Context length', [['', 'Any'], ['128000', '128K', '128K tokens or more'], ['256000', '256K', '256K tokens or more'], ['1000000', '1M', '1M tokens or more']])\n              , range('price', 'Input price (per 1M tokens)', [['', 'Any'], ['0.5', '≤$0.5', 'Up to $0.50'], ['2', '≤$2', 'Up to $2'], ['5', '≤$5', 'Up to $5']])\n              , __jsx(FilterGroup, { title: \"Served precision\" , defaultOpen: false,}\n                , [['bf16', 'BF16'], ['fp16', 'FP16'], ['fp8', 'FP8'], ['fp4', 'FP4'], ['int4', 'INT4'], ['none', 'Not disclosed']].map(([value, label]) => option('quant', value, label, QUANT_NOTES[value]))\n              )\n            )\n          ) : null\n\n          , modality === 'image' ? (\n            __jsx(__Fragment, null\n              , __jsx(FilterGroup, { title: \"Task\",}\n                , [['generate', 'Generate'], ['edit', 'Edit'], ['upscale', 'Upscale'], ['background-removal', 'Remove background']].map(([value, label]) => option('modes', value, label))\n              )\n              , range('res', 'Output resolution', [['', 'Any'], ['2K', '2K', '2K available'], ['4K', '4K', '4K available']])\n              , __jsx(FilterGroup, { title: \"Features\",}\n                , option('caps', 'webSearch', 'Web-grounded generation')\n                , option('caps', 'multiImage', 'Multi-image editing')\n              )\n            )\n          ) : null\n\n          , modality === 'video' ? (\n            __jsx(__Fragment, null\n              , __jsx(FilterGroup, { title: \"Mode\",}\n                , ['t2v', 'i2v', 'r2v', 'v2v', 'flf', 'motion', 'transition'].map(mode => option('modes', mode, VARIANT_LABELS[mode]))\n              )\n              , range('audio', 'Audio', [['', 'Any'], ['audio', 'Audio', 'Generates audio'], ['silent', 'Silent', 'Can be silent']])\n              , range('res', 'Resolution', [['', 'Any'], ['720', '720p', '720p or higher'], ['1080', '1080p', '1080p or higher'], ['1440', '1440p', '1440p or higher'], ['2160', '4K']])\n              , range('dur', 'Clip length', [['', 'Any'], ['10', '10s', '10 seconds or longer'], ['15', '15s', '15 seconds or longer'], ['20', '20s', '20 seconds or longer'], ['30', '30s', '30 seconds or longer']])\n            )\n          ) : null\n\n          , __jsx(FilterGroup, { title: \"Attributes\",}\n            , option('flags', 'app-default', 'App Default', 'The default model used by the Venice app.')\n            , option('flags', 'open', 'Open weights')\n            , option('flags', 'uncensored', 'Uncensored')\n            , option('flags', 'new', 'Added in the last 30 days')\n          )\n\n          , __jsx(FilterGroup, { title: \"Provider\",}\n            , visibleProviders.map(([slug, n]) => {\n              const checked = filters.providers.includes(slug);\n              return __jsx(Check, { key: slug, checked: checked, onChange: () => toggleIn('providers', slug), label: providerName(slug), count: n, disabled: !checked && n === 0,} );\n            })\n            , providerList.length > 10 ? (\n              __jsx('button', { type: \"button\", className: \"vx-link-btn vx-more\" , onClick: () => setShowAll(!showAll),}, showAll ? 'Show fewer' : `Show all ${providerList.length}`)\n            ) : null\n          )\n        )\n      )\n    );\n  };\n\n  // A \"from\" price lists its options on hover, focus or tap. The trigger is a\n  // button described by the list, so screen readers announce the options with\n  // the price. The list is fixed to the viewport so table and card overflow\n  // can't clip it.\n  let tipCount = 0;\n  const PriceTip = ({ breakdown, children }) => {\n    const ref = useRef(null);\n    const [tipId] = useState(() => `vx-tip-${(tipCount += 1)}`);\n    const [pos, setPos] = useState(null);\n    useEffect(() => {\n      if (!pos) return undefined;\n      const close = () => setPos(null);\n      const closeOutside = e => { if (ref.current && !ref.current.contains(e.target)) close(); };\n      window.addEventListener('scroll', close, true);\n      window.addEventListener('resize', close);\n      document.addEventListener('pointerdown', closeOutside);\n      return () => {\n        window.removeEventListener('scroll', close, true);\n        window.removeEventListener('resize', close);\n        document.removeEventListener('pointerdown', closeOutside);\n      };\n    }, [pos]);\n    if (!breakdown) return children;\n    const open = () => {\n      const r = ref.current.getBoundingClientRect();\n      const below = window.innerHeight - r.bottom > breakdown.length * 20 + 32;\n      const left = r.left < window.innerWidth / 2;\n      setPos({\n        top: below ? r.bottom + 6 : null,\n        bottom: below ? null : window.innerHeight - r.top + 6,\n        left: left ? r.left : null,\n        right: left ? null : window.innerWidth - r.right\n      });\n    };\n    return (\n      __jsx('button', { ref: ref, type: \"button\", className: \"vx-tip-anchor\", 'aria-describedby': tipId,\n        onMouseEnter: open, onMouseLeave: () => setPos(null), onFocus: open, onBlur: () => setPos(null), onClick: open,\n        onKeyDown: e => { if (e.key === 'Escape' && pos) { e.stopPropagation(); setPos(null); } },}\n        , children\n        , __jsx('span', { id: tipId, role: \"tooltip\", className: \"vx-tip\", style: pos || undefined, hidden: !pos,}\n          , breakdown.map(([label, text]) => __jsx('span', { key: label, className: \"vx-tip-row\",}, __jsx('span', null, label), __jsx('span', null, text)))\n        )\n      )\n    );\n  };\n\n  const PriceCell = ({ value, unit, note, strong, from, breakdown }) => (\n    __jsx(PriceTip, { breakdown: breakdown,}\n      , __jsx('span', { className: cls('vx-price', strong && 'is-strong'),}\n        , from ? __jsx('span', { className: \"vx-price-from\",}, \"from \" ) : null\n        , __jsx('span', { className: \"vx-price-value\",}, usd(value))\n        , unit && value != null && value !== 0 ? __jsx('span', { className: \"vx-price-unit\",}, unit) : null\n        , note ? __jsx('span', { className: \"vx-price-note\", title: note,}, \"*\") : null\n      )\n    )\n  );\n  const OfferPrice = ({ offer, strong }) => (\n    __jsx(PriceCell, { value: offer.value, unit: offer.unit, note: offer.note, from: offer.from, breakdown: offer.breakdown, strong: strong,} )\n  );\n\n  const modelHref = (family, id) => `/models/${family.slug}${id !== family.primary ? `?v=${encodeURIComponent(id)}` : ''}`;\n  const privacySummary = tiers => privacyTiers(tiers).map(tier => (PRIVACY[tier] ? PRIVACY[tier].label : tier)).join(', ');\n\n  const ModelCell = ({ row, providers, now }) => {\n    const { family, display } = row;\n    const provider = providers[family.provider];\n    const extra = family.variants.length - 1;\n    return (\n      __jsx('div', { className: \"vx-mcell\",}\n        , __jsx(ProviderLogo, { provider: provider, size: 32,} )\n        , __jsx('div', { className: \"vx-mcell-body\",}\n          , __jsx('div', { className: \"vx-mcell-top\",}\n            , __jsx('a', { className: \"vx-mcell-name\", href: modelHref(family, display.id),}, family.name)\n            , __jsx(StatusTags, { item: family, now: now,} )\n          )\n          , __jsx('div', { className: \"vx-mcell-sub\",}\n            , __jsx('code', { className: \"vx-mcell-id\", title: display.id,}, display.id)\n            , __jsx(CopyButton, { text: display.id, iconOnly: true, label: `Copy model ID ${display.id}`,} )\n            , extra > 0 ? __jsx('span', { className: \"vx-mcell-more\", title: family.variants.join('\\n'),}, \"+\", extra, \" \" , extra === 1 ? 'variant' : 'variants') : null\n          )\n          , __jsx('div', { className: \"vx-mcell-mobile\",}, [keySpec(display), privacySummary(family.privacy)].filter(Boolean).join(' · '))\n        )\n      )\n    );\n  };\n\n  const modeChips = (family, catalog) => (family.variants || []).map(id => catalog.models[id]).filter(Boolean).map(m => m.variant);\n\n  const ModeList = ({ modes, active, labels }) => (\n    __jsx('span', { className: \"vx-modes\",}\n      , modes.map(v => __jsx('span', { key: v, className: v === active ? 'is-active' : undefined,}, labels[v] || v))\n    )\n  );\n\n  // `hide` drops a column below a breakpoint (lg 1280px, md 1024px, sm 700px);\n  // the model cell repeats the key spec and privacy tier on small screens.\n  const explorerColumns = (modality, task, filters, catalog) => {\n    const privacy = { key: 'privacy', label: 'Privacy', hide: 'sm', render: row => __jsx(PrivacyStack, { tiers: row.family.privacy,} ) };\n    const added = { key: 'added', label: 'Added', sort: 'newest', align: 'right', hide: 'lg', render: row => __jsx('span', { className: \"vx-muted vx-nowrap\" ,}, shortDate(row.family.updated || row.family.created)) };\n    if (modality === 'text') {\n      return [\n        { key: 'context', label: 'Context', sort: 'context', align: 'right', hide: 'sm', render: row => __jsx('span', { className: \"vx-num\",}, tokens(row.display.text && row.display.text.context)) },\n        { key: 'input', label: 'Input', sort: 'input', align: 'right', hide: 'md', render: row => __jsx(PriceCell, { value: row.display.pricing && row.display.pricing.input,} ) },\n        { key: 'output', label: 'Output', sort: 'output', align: 'right', hide: 'md', render: row => __jsx(PriceCell, { value: row.display.pricing && row.display.pricing.output,} ) },\n        { key: 'cache', label: 'Cached', align: 'right', hide: 'lg', hint: 'Cached input (prompt cache read)', render: row => __jsx(PriceCell, { value: row.display.pricing && row.display.pricing.cacheRead,} ) },\n        { key: 'blended', label: 'Blended', sort: 'price', align: 'right', hint: 'Blended = (3 × input + output) / 4, the mix Artificial Analysis uses.', render: row => __jsx(PriceCell, { value: row.price, strong: true,} ) },\n        { key: 'caps', label: 'Capabilities', hide: 'md', render: row => __jsx(CapIcons, { model: row.display,} ) },\n        privacy\n      ];\n    }\n    if (modality === 'image') {\n      return [\n        { key: 'tasks', label: 'Tasks', hide: 'md', render: row => __jsx(ModeList, { modes: [...new Set(modeChips(row.family, catalog))], labels: VARIANT_LABELS,} ) },\n        { key: 'price', label: filters.res ? `Price at ${filters.res}` : 'Price', sort: 'price', align: 'right', render: row => __jsx(OfferPrice, { offer: row.offer, strong: true,} ) },\n        { key: 'res', label: 'Max output', align: 'right', hide: 'sm', render: row => __jsx('span', { className: \"vx-num\",}, imageMaxRes(row.display) || '—') },\n        { key: 'ar', label: 'Aspect ratios', align: 'right', hide: 'lg', render: row => __jsx('span', { className: \"vx-num\",}, ((row.display.image && row.display.image.aspectRatios) || []).filter(a => a !== 'auto').length || '—') },\n        privacy\n      ];\n    }\n    if (modality === 'video') {\n      return [\n        { key: 'modes', label: 'Modes', hide: 'md', render: row => __jsx(ModeList, { modes: modeChips(row.family, catalog), active: row.display.variant, labels: MODE_SHORT,} ) },\n        { key: 'persec', label: 'Per second', sort: 'price', align: 'right', render: row => (row.offer.status === 'quoted'\n          ? __jsx(OfferPrice, { offer: row.offer, strong: true,} )\n          : __jsx('span', { className: \"vx-muted\", title: \"Priced by the length of the source video. Use /video/quote.\"         ,}, \"By source\" )) },\n        filters.dur ? { key: 'clip', label: `${filters.dur}s clip`, align: 'right', hide: 'sm', render: row => (row.offer.clip != null\n          ? __jsx(PriceCell, { value: row.offer.clip, unit: row.offer.seconds !== Number(filters.dur) ? `${row.offer.seconds}s` : null,} )\n          : __jsx('span', { className: \"vx-muted\",}, \"—\")) } : null,\n        { key: 'max', label: 'Max', align: 'right', hide: 'lg', render: row => __jsx('span', { className: \"vx-num\",}, heightLabel(maxVideoHeight(row.display)), \" · \"  , maxVideoSeconds(row.display) ? `${maxVideoSeconds(row.display)}s` : 'source') },\n        { key: 'audio', label: 'Audio', hide: 'md', render: row => {\n          const a = row.display.video && row.display.video.audio;\n          return __jsx('span', { className: a === 'native' || a === 'optional' ? undefined : 'vx-muted',}, a === 'native' ? 'Native' : a === 'optional' ? 'Optional' : 'None');\n        } },\n        privacy\n      ].filter(Boolean);\n    }\n    if (modality === 'audio') {\n      if (task === 'stt') {\n        return [\n          { key: 'hour', label: 'Per audio hour', sort: 'price', align: 'right', render: row => __jsx(PriceCell, { value: row.display.pricing && row.display.pricing.perHour, strong: true,} ) },\n          { key: 'k', label: 'Per 1K minutes', align: 'right', hide: 'sm', render: row => __jsx(PriceCell, { value: row.display.pricing && row.display.pricing.perMinute != null ? row.display.pricing.perMinute * 1000 : null,} ) },\n          privacy\n        ];\n      }\n      if (task === 'music') {\n        return [\n          { key: 'type', label: 'Type', hide: 'md', render: row => TASK_LABELS[row.display.task] },\n          { key: 'price', label: 'Price', sort: 'price', align: 'right', render: row => __jsx(OfferPrice, { offer: row.offer, strong: true,} ) },\n          { key: 'len', label: 'Max length', align: 'right', hide: 'sm', render: row => __jsx('span', { className: \"vx-num\",}, row.display.audio && row.display.audio.maxDuration ? `${Math.round(row.display.audio.maxDuration / 60 * 10) / 10} min` : '—') },\n          { key: 'lyrics', label: 'Lyrics', hide: 'lg', render: row => (row.display.audio && row.display.audio.lyrics ? 'Yes' : __jsx('span', { className: \"vx-muted\",}, \"No\")) },\n          privacy\n        ];\n      }\n      return [\n        { key: 'chars', label: 'Per 1M chars', sort: 'price', align: 'right', render: row => __jsx(PriceCell, { value: row.price, strong: true,} ) },\n        { key: 'min', label: 'Per minute', align: 'right', hide: 'sm', hint: 'At about 825 characters per minute of speech.', render: row => __jsx(PriceCell, { value: row.display.pricing && row.display.pricing.perMinute,} ) },\n        { key: 'voices', label: 'Voices', align: 'right', hide: 'md', render: row => __jsx('span', { className: \"vx-num\",}, (row.display.audio && row.display.audio.voiceCount) || '—') },\n        privacy\n      ];\n    }\n    if (modality === 'embedding') {\n      return [\n        { key: 'dims', label: 'Dimensions', align: 'right', hide: 'sm', render: row => __jsx('span', { className: \"vx-num\",}, (row.display.embedding && row.display.embedding.dimensions) ? row.display.embedding.dimensions.toLocaleString('en-US') : '—') },\n        { key: 'max', label: 'Max input', align: 'right', hide: 'md', render: row => __jsx('span', { className: \"vx-num\",}, tokens(row.display.embedding && row.display.embedding.maxInputTokens)) },\n        { key: 'price', label: 'Per 1M tokens', sort: 'price', align: 'right', render: row => __jsx(PriceCell, { value: row.price, strong: true,} ) },\n        privacy\n      ];\n    }\n    return [\n      { key: 'type', label: 'Type', hide: 'sm', render: row => __jsx('span', { className: \"vx-nowrap\",}, TASK_LABELS[row.display.task] || row.family.modality) },\n      { key: 'price', label: 'Price', align: 'right', hint: 'Blended per 1M tokens for text; per image, second, character or minute elsewhere.', render: row => __jsx(OfferPrice, { offer: row.offer, strong: true,} ) },\n      { key: 'spec', label: 'Key spec', hide: 'md', render: row => __jsx('span', { className: \"vx-muted vx-nowrap\" ,}, keySpec(row.display)) },\n      privacy, added\n    ];\n  };\n\n  const keySpec = model => {\n    if (model.modality === 'text' && model.text) return `${tokens(model.text.context)} context`;\n    if (model.modality === 'image') return imageMaxRes(model) ? `Up to ${imageMaxRes(model)}` : VARIANT_LABELS[model.variant] || '';\n    if (model.modality === 'video') return `${heightLabel(maxVideoHeight(model))} · ${maxVideoSeconds(model) ? `up to ${maxVideoSeconds(model)}s` : 'source length'}`;\n    if (model.task === 'tts') return `${(model.audio && model.audio.voiceCount) || 0} voices`;\n    if (model.modality === 'embedding' && model.embedding) return `${model.embedding.dimensions || '—'} dims`;\n    return TASK_LABELS[model.task] || '';\n  };\n\n  const hideCls = col => (col.hide ? `vx-hide-${col.hide}` : null);\n\n  const HeaderLabel = ({ col }) => (\n    __jsx(__Fragment, null\n      , col.label\n      , col.sub ? __jsx('span', { className: \"vx-th-sub\",}, col.sub) : null\n    )\n  );\n\n  const ExplorerTable = ({ rows, columns, sort, setSort, providers, now }) => (\n    __jsx('div', { className: \"vx-table-wrap\",}\n      , __jsx('table', { className: \"vx-table\",}\n        , __jsx('thead', null\n          , __jsx('tr', null\n            , __jsx('th', { className: \"vx-th-model\", 'aria-sort': sort === 'name' ? 'ascending' : undefined,}\n              , __jsx('button', { type: \"button\", className: cls('vx-th-btn', sort === 'name' && 'is-sorted'), onClick: () => setSort('name'),}, \"Model\"\n                , __jsx(Icon, { name: \"chevronDown\", size: 12, className: \"vx-sort-icon is-up\" ,} )\n              )\n            )\n            , columns.map(col => (\n              __jsx('th', { key: col.key, className: cls(col.align && `vx-al-${col.align}`, hideCls(col)), title: col.hint,\n                'aria-sort': col.sort && sort === col.sort ? SORTS[col.sort].dir : undefined,}\n                , col.sort ? (\n                  __jsx('button', { type: \"button\", className: cls('vx-th-btn', sort === col.sort && 'is-sorted'), onClick: () => setSort(col.sort),}\n                    , __jsx(HeaderLabel, { col: col,} )\n                    , __jsx(Icon, { name: \"chevronDown\", size: 12, className: cls('vx-sort-icon', SORTS[col.sort].dir === 'ascending' && 'is-up'),} )\n                  )\n                ) : __jsx(HeaderLabel, { col: col,} )\n              )\n            ))\n          )\n        )\n        , __jsx('tbody', null\n          , rows.map(row => (\n            __jsx('tr', { key: row.family.slug,}\n              , __jsx('td', { className: \"vx-td-model\",}, __jsx(ModelCell, { row: row, providers: providers, now: now,} ))\n              , columns.map(col => __jsx('td', { key: col.key, className: cls(col.align && `vx-al-${col.align}`, hideCls(col)),}, col.render(row)))\n            )\n          ))\n        )\n      )\n    )\n  );\n\n  const ExplorerCard = ({ row, now, catalog }) => {\n    const { family, display } = row;\n    const isVideo = family.modality === 'video';\n    const media = sampleFor(display, isVideo ? 'cinematic-landscape' : 'photoreal') ||\n      family.variants.map(id => catalog.models[id]).map(m => sampleFor(m, isVideo ? 'cinematic-landscape' : 'photoreal')).find(Boolean);\n    const { offer } = row;\n    const href = modelHref(family, display.id);\n    return (\n      __jsx('article', { className: \"vx-card\",}\n        , __jsx('a', { className: cls('vx-card-media', isVideo ? 'is-video' : 'is-image'), href: href, tabIndex: -1, 'aria-hidden': \"true\",}\n          , media\n            ? (media.type === 'video' ? __jsx(HoverVideo, { src: media.url, className: \"vx-card-el\",} ) : __jsx('img', { className: \"vx-card-el\", src: media.url, srcSet: sampleSrcSet(media.url), sizes: CARD_SIZES, alt: \"\", loading: \"lazy\", decoding: \"async\",} ))\n            : __jsx(MediaFallback, null )\n          , isVideo && media ? __jsx('span', { className: \"vx-card-play\",}, __jsx(Icon, { name: \"play\", size: 10,} )) : null\n        )\n        , __jsx('div', { className: \"vx-card-body\",}\n          , __jsx('div', { className: \"vx-card-top\",}\n            , __jsx('a', { className: \"vx-card-name\", href: href, title: family.name,}, family.name)\n            , __jsx(StatusTags, { item: family, now: now,} )\n          )\n          , __jsx('div', { className: \"vx-card-foot\",}\n            , isVideo && offer.status !== 'quoted' ? __jsx('span', { className: \"vx-card-price vx-muted\" ,}, \"Priced by source length\"   ) : (\n              __jsx(PriceTip, { breakdown: offer.breakdown,}\n                , __jsx('span', { className: \"vx-card-price\",}\n                  , offer.from ? 'from ' : null, __jsx('strong', null, usd(offer.value)), \" \" , offer.unit\n                  , offer.clip != null ? __jsx('span', { className: \"vx-muted\",}, \" · \"  , usd(offer.clip), \" per \"  , offer.seconds, \"s\") : null\n                )\n              )\n            )\n            , __jsx(PrivacyStack, { tiers: family.privacy,} )\n          )\n          , isVideo ? __jsx(ModeList, { modes: modeChips(family, catalog), active: display.variant, labels: MODE_SHORT,} ) : null\n        )\n      )\n    );\n  };\n\n  // List view: logo, name and price; the description when the\n  // catalog has one; then the model ID, type (when the results mix types),\n  // privacy and key specs on one line. Capabilities are left to the filters\n  // and the table.\n  const TYPE_LABELS = {\n    chat: 'Text', decision: 'Decision', 'image-generation': 'Image', 'image-edit': 'Image edit', 'image-upscale': 'Upscale',\n    'background-removal': 'Background removal', video: 'Video', tts: 'Text to speech', stt: 'Speech to text', music: 'Music', sfx: 'Sound effects', embedding: 'Embedding'\n  };\n\n  const rowPrice = row => {\n    const { display, offer } = row;\n    const p = display.pricing || {};\n    if (display.task === 'chat') return p.input != null ? __jsx(__Fragment, null, usd(p.input), \" / \"  , usd(p.output), __jsx('span', { className: \"vx-muted\",}, \" per 1M\"  )) : null;\n    if (display.modality === 'video' && offer.status !== 'quoted') return __jsx('span', { className: \"vx-muted\",}, \"By source\" );\n    return offer.value != null ? __jsx(OfferPrice, { offer: offer,} ) : null;\n  };\n\n  // The price already leads the row, so text lists limits rather than\n  // repeating it.\n  const rowSummary = model => {\n    if (model.task !== 'chat' || !model.text) return keySpec(model);\n    const p = model.pricing || {};\n    return [\n      model.text.context ? `${tokens(model.text.context)} context` : null,\n      model.text.maxOutput ? `${tokens(model.text.maxOutput)} max output` : null,\n      p.cacheRead != null ? `${usd(p.cacheRead)}/M cached input` : null\n    ].filter(Boolean).join(' · ');\n  };\n\n  const IdChip = ({ id }) => {\n    const [done, setDone] = useState(false);\n    return (\n      __jsx('button', { type: \"button\", className: cls('vx-idchip', done && 'is-done'), title: id, 'aria-label': `Copy model ID ${id}`,\n        onClick: () => copyText(id).then(() => { setDone(true); setTimeout(() => setDone(false), 1400); }),}\n        , __jsx('span', null, id), done ? __jsx(Icon, { name: \"check\", size: 12,} ) : null\n      )\n    );\n  };\n\n  const ExplorerList = ({ rows, providers, now, showType }) => (\n    __jsx('ul', { className: \"vx-list\",}\n      , rows.map(row => {\n        const { family, display } = row;\n        const provider = providers[family.provider];\n        const extra = family.variants.length - 1;\n        const description = display.description || family.description;\n        const type = TYPE_LABELS[display.task] || family.modality;\n        const spec = rowSummary(display);\n        const details = [showType ? type : null, privacySummary(family.privacy), spec === type || spec === TASK_LABELS[display.task] ? null : spec].filter(Boolean).join(' · ');\n        return (\n          __jsx('li', { key: family.slug, className: \"vx-list-row\",}\n            , __jsx('div', { className: \"vx-list-top\",}\n              , __jsx(ProviderLogo, { provider: provider, size: 24,} )\n              , __jsx('a', { className: \"vx-list-name\", href: modelHref(family, display.id),}, family.name)\n              , __jsx(StatusTags, { item: family, now: now,} )\n              , __jsx('span', { className: \"vx-list-price\",}, rowPrice(row))\n            )\n            , description ? __jsx('p', { className: \"vx-list-desc\",}, description) : null\n            , __jsx('div', { className: \"vx-list-sum\",}\n              , __jsx(IdChip, { id: display.id,} )\n              , extra > 0 ? __jsx('span', { className: \"vx-list-more\", title: family.variants.join('\\n'),}, \"+\", extra, \" \" , extra === 1 ? 'variant' : 'variants') : null\n              , __jsx('span', null, details)\n            )\n          )\n        );\n      })\n    )\n  );\n\n  const hasRenders = (family, catalog) => family.variants.some(id => ((catalog.models[id] || {}).media || []).length);\n\n  // Families without reference renders go in a table under the gallery rather\n  // than as empty tiles, so the grid only shows real outputs.\n  const Gallery = ({ rows, columns, sort, setSort, providers, now, catalog, modality }) => {\n    const rendered = rows.filter(row => hasRenders(row.family, catalog));\n    const pending = rows.filter(row => !hasRenders(row.family, catalog));\n    return (\n      __jsx(__Fragment, null\n        , rendered.length ? (\n          __jsx('div', { className: cls('vx-cards', modality === 'video' && 'is-video'),}\n            , rendered.map(row => __jsx(ExplorerCard, { key: row.family.slug, row: row, now: now, catalog: catalog,} ))\n          )\n        ) : null\n        , pending.length ? (\n          __jsx('section', { className: \"vx-unrendered\", 'aria-labelledby': \"vx-unrendered-title\",}\n            , __jsx('h3', { id: \"vx-unrendered-title\", className: \"vx-h3\",}, \"No reference renders yet \"    , __jsx('span', { className: \"vx-muted\",}, plural(pending.length, 'family')))\n            , __jsx(ExplorerTable, { rows: pending, columns: columns, sort: sort, setSort: setSort, providers: providers, now: now,} )\n          )\n        ) : null\n      )\n    );\n  };\n\n  const ModelExplorer = ({ modality: initialModality = 'all', task: initialTask }) => {\n    const { catalog, error } = useCatalog();\n    const now = useNow();\n    const startScope = initialScope(initialModality, initialTask);\n    const startSort = defaultSort(initialModality);\n    // A link's state applies on the first render, so a shared view opens as\n    // it was.\n    const [linked] = useState(() => {\n      const params = readParams();\n      // `m=audio&t=stt` is the shape of links from before the facet.\n      const m = params.get('m') === 'audio' ? params.get('t') || 'tts' : params.get('m') || (initialModality === 'audio' && params.get('t'));\n      const scope = m === 'all' ? null : parseScope(m) || startScope;\n      return {\n        scope,\n        query: params.get('q') || '',\n        sort: params.get('sort') && SORTS[params.get('sort')] ? params.get('sort') : startSort,\n        // Links leave the view out when it is the modality's default.\n        view: VIEWS.includes(params.get('view')) ? params.get('view') : defaultView(scope),\n        filters: { ...EMPTY_FILTERS, ...filtersFromParams(params) }\n      };\n    });\n    const [scope, setScope] = useState(linked.scope);\n    const [query, setQuery] = useState(linked.query);\n    const [filters, setFilters] = useState(linked.filters);\n    const [sort, setSort] = useState(linked.sort);\n    const [view, setView] = useState(linked.view);\n    const [railOpen, setRailOpen] = useState(false);\n    const [limit, setLimit] = useState(PAGE_SIZE);\n    const searchRef = useRef(null);\n    // A chosen modality brings its columns, filters and price lenses; All\n    // shows the cross-modality view.\n    const modality = AUDIO_SCOPES.includes(scope) ? 'audio' : scope && scope !== 'decision' ? scope : 'all';\n    const task = AUDIO_SCOPES.includes(scope) ? scope : 'tts';\n\n    useEffect(() => {\n      // \"/\" jumps to the search unless the reader is typing somewhere or the\n      // filter drawer holds focus.\n      const typing = el => Boolean(el && (el.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName)));\n      const onKey = e => {\n        if (e.key !== '/' || e.defaultPrevented || e.metaKey || e.ctrlKey || e.altKey) return;\n        if (typing(e.target) || typing(document.activeElement) || document.querySelector('#vx-filters[aria-modal]')) return;\n        e.preventDefault();\n        if (searchRef.current) searchRef.current.focus();\n      };\n      window.addEventListener('keydown', onKey);\n      return () => window.removeEventListener('keydown', onKey);\n    }, []);\n\n    useEffect(() => {\n      writeParams({\n        m: scope === startScope ? null : scope || 'all',\n        q: query || null,\n        sort: sort !== startSort ? sort : null,\n        view: view !== defaultView(modality) ? view : null,\n        ...filtersToParams(filters)\n      });\n    }, [scope, query, sort, view, filters]);\n\n    useEffect(() => { setLimit(PAGE_SIZE); }, [scope, filters, query, sort, view]);\n\n    useEffect(() => {\n      if (!railOpen) return undefined;\n      // The drawer is modal: focus moves into it on open and back to the\n      // toggle on close, Tab stays inside it and the page under it holds still.\n      const drawer = document.getElementById('vx-filters');\n      const done = drawer && drawer.querySelector('.vx-rail-done');\n      if (done) done.focus();\n      const root = document.documentElement;\n      const overflow = root.style.overflow;\n      root.style.overflow = 'hidden';\n      const tabbable = () => (drawer ? [...drawer.querySelectorAll(FOCUSABLE)].filter(el => el.offsetParent !== null) : []);\n      const onKey = e => {\n        if (e.key === 'Escape') { setRailOpen(false); return; }\n        if (e.key !== 'Tab') return;\n        const items = tabbable();\n        if (!items.length) return;\n        const first = items[0];\n        const last = items[items.length - 1];\n        const inside = drawer.contains(document.activeElement);\n        if (e.shiftKey && (!inside || document.activeElement === first)) { e.preventDefault(); last.focus(); }\n        else if (!e.shiftKey && (!inside || document.activeElement === last)) { e.preventDefault(); first.focus(); }\n      };\n      // Widening past the drawer breakpoint turns it back into the column.\n      const wide = window.matchMedia(`(min-width: ${DRAWER_MAX_WIDTH + 1}px)`);\n      const onWide = () => { if (wide.matches) setRailOpen(false); };\n      window.addEventListener('keydown', onKey);\n      wide.addEventListener('change', onWide);\n      return () => {\n        window.removeEventListener('keydown', onKey);\n        wide.removeEventListener('change', onWide);\n        root.style.overflow = overflow;\n        const toggle = document.querySelector('[aria-controls=\"vx-filters\"]');\n        if (toggle && drawer && drawer.contains(document.activeElement)) toggle.focus();\n      };\n    }, [railOpen]);\n\n    // Filters for one modality and the provider list reset with the facet,\n    // so no checked filter is left out of view.\n    const chooseScope = key => {\n      setScope(key);\n      setFilters(prev => ({ ...EMPTY_FILTERS, providers: [], privacy: prev.privacy, flags: prev.flags }));\n      setView(defaultView(key));\n      if (key !== 'text' && !['newest', 'name', 'price'].includes(sort)) setSort(startSort);\n    };\n\n    const providers = catalog ? catalog.providers : {};\n\n    const scoped = useMemo(() => (catalog ? catalog.families.filter(family => !scope || familyScope(family) === scope) : []), [catalog, scope]);\n\n    const scopeCounts = useMemo(() => {\n      const out = {};\n      (catalog ? catalog.families : []).forEach(family => { const key = familyScope(family); out[key] = (out[key] || 0) + 1; });\n      return out;\n    }, [catalog]);\n\n    const searched = useMemo(() => (catalog && query ? scoped.filter(family => matchesQuery(searchText(family, modelsOf(catalog, family), providers), query)) : scoped), [catalog, scoped, query, providers]);\n\n    // An option's count is the families it would leave, run through the same\n    // test as the filter so the two never disagree: the search and every\n    // other group apply, and within its own group the option joins the\n    // checked ones where all must hold, or stands alone where any may.\n    const facetCount = useMemo(() => {\n      const cache = new Map();\n      return (key, value) => {\n        const id = `${key}:${value}`;\n        if (!cache.has(id)) {\n          const f = { ...filters, [key]: ALL_OF.includes(key) ? [...new Set([...filters[key], value])] : [value] };\n          cache.set(id, searched.filter(family => familyMatches(family, modelsOf(catalog, family), f, now)).length);\n        }\n        return cache.get(id);\n      };\n    }, [catalog, searched, filters, now]);\n\n    const rows = useMemo(() => {\n      if (!catalog) return [];\n      const list = [];\n      for (const family of searched) {\n        const models = modelsOf(catalog, family);\n        if (!familyMatches(family, models, filters, now)) continue;\n        const display = models.find(m => variantMatches(m, filters)) || catalog.models[family.primary] || models[0];\n        const offer = listPrice(display, filters);\n        list.push({ family, models, display, offer, price: offer.value });\n      }\n      const sorter = SORTS[sort] || SORTS[startSort];\n      return list.sort(sorter.fn);\n    }, [catalog, searched, filters, sort, now]);\n\n    // Providers count the same way, in one pass. Every provider in scope, and\n    // any checked from a link, stays listed so it can show as disabled or be\n    // unchecked.\n    const providerCounts = useMemo(() => {\n      const counts = Object.fromEntries([...scoped.map(family => family.provider), ...filters.providers].map(slug => [slug, 0]));\n      const others = { ...filters, providers: [] };\n      searched.forEach(family => { if (familyMatches(family, modelsOf(catalog, family), others, now)) counts[family.provider] += 1; });\n      return counts;\n    }, [catalog, scoped, searched, filters, now]);\n\n    const modelCount = rows.reduce((n, row) => n + row.family.variants.length, 0);\n    const page = rows.slice(0, limit);\n    const remaining = rows.length - page.length;\n    const columns = catalog ? explorerColumns(modality, task, filters, catalog) : [];\n    const canGrid = modality === 'image' || modality === 'video';\n    // A gallery link opened with text or audio in scope falls back to the list.\n    const shownView = view === 'grid' && !canGrid ? 'list' : view;\n    const filterCount = activeFilterCount(filters);\n    const scopeKey = scope || 'all';\n    const unitNote = settingNote(modality, filters) || (shownView !== 'grid' ? UNIT_NOTES[scopeKey] : null);\n\n    const updated = catalog && catalog.generatedAt ? fmtDate(Math.floor(new Date(catalog.generatedAt).getTime() / 1000)) : null;\n\n    return (\n      __jsx('div', { className: \"vx vx-page vx-explorer not-prose\"   ,}\n        , __jsx('div', { className: \"vx-page-grid\",}\n          , catalog ? (\n            __jsx(FilterRail, { modality: modality, task: task, scope: scope, scopeCounts: scopeCounts, onScope: chooseScope,\n              filters: filters, setFilters: setFilters, facetCount: facetCount, providerCounts: providerCounts, providers: providers,\n              onClear: () => setFilters(EMPTY_FILTERS), open: railOpen, onClose: () => setRailOpen(false),} )\n          ) : __jsx('aside', { className: \"vx-side vx-filters\" , 'aria-hidden': \"true\",} )\n          , railOpen ? __jsx('div', { className: \"vx-filters-backdrop\", onClick: () => setRailOpen(false), 'aria-hidden': \"true\",} ) : null\n\n          , __jsx('div', { className: \"vx-page-main\",}\n            , __jsx('header', { className: \"vx-hero\",}\n              , __jsx('div', { className: \"vx-hero-text\",}\n                , __jsx('h1', { className: \"vx-h1\",}, PAGE_TITLES[scopeKey] || PAGE_TITLES.all)\n                , __jsx('p', { className: \"vx-lede\",}, SCOPE_BLURBS[scopeKey] || SCOPE_BLURBS.all)\n              )\n              , __jsx('div', { className: \"vx-hero-actions\",}\n                , __jsx('a', { className: \"vx-btn vx-btn-tertiary\" , href: \"/api-reference/endpoint/models/list\",}, \"Models API\" )\n              )\n            )\n\n            , __jsx('div', { className: \"vx-toolbar\",}\n              , __jsx('button', { type: \"button\", className: cls('vx-btn vx-rail-toggle', filterCount && 'vx-btn-active'), onClick: () => setRailOpen(!railOpen), 'aria-expanded': railOpen, 'aria-controls': \"vx-filters\", disabled: !catalog,}, \"Filters\"\n                , filterCount ? __jsx('span', { className: \"vx-btn-count\",}, filterCount) : null\n              )\n              , __jsx('div', { className: \"vx-search\",}\n                , __jsx(Icon, { name: \"search\", size: 16,} )\n                , __jsx('input', { ref: searchRef, type: \"search\", value: query, onChange: e => setQuery(e.target.value),\n                  placeholder: \"Search by name, ID or provider\"     , 'aria-label': \"Search models\" ,} )\n                , query ? __jsx('button', { type: \"button\", className: \"vx-icon-btn\", onClick: () => setQuery(''), 'aria-label': \"Clear search\" ,}, __jsx(Icon, { name: \"x\", size: 14,} )) : __jsx('kbd', { 'aria-hidden': \"true\",}, \"/\")\n              )\n              , __jsx('select', { className: \"vx-select\", value: sort, onChange: e => setSort(e.target.value), 'aria-label': \"Sort\",}\n                , ['name', 'newest', 'price', ...(modality === 'text' ? ['context', 'input', 'output'] : [])].map(key => __jsx('option', { key: key, value: key,}, SORTS[key].label))\n              )\n              , __jsx(Segmented, { ariaLabel: \"View\", value: shownView, onChange: setView,\n                options: [{ value: 'list', label: 'List' }, { value: 'table', label: 'Table' }, ...(canGrid ? [{ value: 'grid', label: 'Gallery' }] : [])],} )\n            )\n\n            , !catalog ? (\n              error\n                ? __jsx('div', { className: \"vx-error\", role: \"alert\",}, \"The model catalog could not load. Reload the page to try again.\"           )\n                : __jsx(__Fragment, null, __jsx('span', { className: \"vx-sr-only\", role: \"status\",}, \"Loading models\" ), __jsx(CatalogSkeleton, null ))\n            ) : (\n              __jsx('section', { className: \"vx-results\", 'aria-label': \"Models\",}\n                , __jsx('div', { className: \"vx-countline\",}\n                  , __jsx('span', { className: \"vx-result-count\", 'aria-live': \"polite\",}\n                    , __jsx('strong', null, plural(rows.length, 'family')), \" · \"  , plural(modelCount, 'model ID'), unitNote ? ` · ${unitNote}` : ''\n                  )\n                  , updated ? __jsx('span', { className: \"vx-source\",}, \"Updated \" , updated) : null\n                )\n\n                , rows.length === 0 ? (\n                  __jsx(Empty, { title: \"No models match\"  ,}\n                    , query ? __jsx(__Fragment, null, \"Nothing matches “\"  , query, \"”\", filterCount ? ' with the current filters' : '', \". \" ) : 'Try removing a filter. '\n                    , __jsx('button', { type: \"button\", className: \"vx-btn vx-btn-sm\" , onClick: () => { setFilters(EMPTY_FILTERS); setQuery(''); },}, \"Clear \" , query && filterCount ? 'search and filters' : query ? 'search' : 'filters')\n                  )\n                ) : shownView === 'grid' ? (\n                  __jsx(Gallery, { rows: page, columns: columns, sort: sort, setSort: setSort, providers: providers, now: now, catalog: catalog, modality: modality,} )\n                ) : shownView === 'table' ? (\n                  __jsx(ExplorerTable, { rows: page, columns: columns, sort: sort, setSort: setSort, providers: providers, now: now,} )\n                ) : (\n                  __jsx(ExplorerList, { rows: page, providers: providers, now: now, showType: new Set(rows.map(row => row.display.task)).size > 1,} )\n                )\n\n                , remaining > 0 ? (\n                  __jsx('div', { className: \"vx-more-row\",}\n                    , __jsx('button', { type: \"button\", className: \"vx-btn vx-more-btn\" , onClick: () => setLimit(limit + PAGE_SIZE),}\n                      , remaining > PAGE_SIZE ? `Show ${PAGE_SIZE} more · ${remaining} remaining` : `Show the last ${remaining}`\n                    )\n                  )\n                ) : null\n\n                , __jsx('footer', { className: \"vx-explorer-foot\",}\n                  , __jsx('nav', { 'aria-label': \"Related pages\" ,}\n                    , __jsx('a', { href: \"/models/methodology\",}, \"Methodology\")\n                    , __jsx('a', { href: \"/overview/deprecations\",}, \"Deprecations\")\n                    , __jsx('a', { href: \"/overview/beta-models\",}, \"Beta models\" )\n                  )\n                )\n              )\n            )\n          )\n        )\n      )\n    );\n  };\n\n  /* ------------------------------------------------------------ code samples */\n\n  const VARIANT_PROMPTS = {\n    chat: 'Explain TEE attestation in two sentences.',\n    image: 'A retro travel poster with the bold headline \"VENICE\" in large condensed serif type, sunset color palette',\n    video: 'Slow aerial drone shot over a misty mountain valley at golden hour',\n    videoEdit: 'Turn the scene into a snowy winter morning',\n    videoExtend: 'Continue the drone shot as it rises above the clouds into clear sky',\n    videoRecast: 'Recast the person in the video as the person in the reference image',\n    videoMotion: 'The character follows the movements in the source video',\n    music: 'Warm lo-fi hip hop beat with soft piano and vinyl crackle',\n    sfx: 'Heavy wooden door creaking open in a stone hallway'\n  };\n\n  const codeSamples = (model, endpointId) => {\n    const id = model.id;\n    const key = '$VENICE_API_KEY';\n    const auth = `-H \"Authorization: Bearer ${key}\" \\\\\\n  -H \"Content-Type: application/json\"`;\n    const pyHead = 'import os\\nfrom openai import OpenAI\\n\\nclient = OpenAI(\\n    api_key=os.environ[\"VENICE_API_KEY\"],\\n    base_url=\"https://api.venice.ai/api/v1\",\\n)\\n';\n    const tsHead = 'import OpenAI from \"openai\";\\n\\nconst client = new OpenAI({\\n  apiKey: process.env.VENICE_API_KEY,\\n  baseURL: \"https://api.venice.ai/api/v1\",\\n});\\n';\n    const reqHead = 'import os, requests\\n\\nAPI = \"https://api.venice.ai/api/v1\"\\nHEADERS = {\"Authorization\": f\"Bearer {os.environ[\\'VENICE_API_KEY\\']}\"}\\n';\n    const fetchHead = 'const API = \"https://api.venice.ai/api/v1\";\\nconst headers = {\\n  Authorization: `Bearer ${process.env.VENICE_API_KEY}`,\\n  \"Content-Type\": \"application/json\",\\n};\\n';\n    const text = model.text || {};\n    const effort = text.reasoning && text.reasoning.effort && text.reasoning.effort.length\n      ? (text.reasoning.defaultEffort && text.reasoning.effort.includes(text.reasoning.defaultEffort) ? text.reasoning.defaultEffort : text.reasoning.effort[text.reasoning.effort.length - 1])\n      : null;\n\n    if (model.task === 'chat' && endpointId === 'responses') {\n      return {\n        curl: `curl ${API}/responses \\\\\\n  ${auth} \\\\\\n  -d '{\\n    \"model\": \"${id}\",\\n    \"input\": \"${VARIANT_PROMPTS.chat}\"${effort ? `,\\n    \"reasoning\": { \"effort\": \"${effort}\" }` : ''}\\n  }'`,\n        python: `${pyHead}\\nresponse = client.responses.create(\\n    model=\"${id}\",\\n    input=\"${VARIANT_PROMPTS.chat}\",${effort ? `\\n    reasoning={\"effort\": \"${effort}\"},` : ''}\\n)\\nprint(response.output_text)`,\n        typescript: `${tsHead}\\nconst response = await client.responses.create({\\n  model: \"${id}\",\\n  input: \"${VARIANT_PROMPTS.chat}\",${effort ? `\\n  reasoning: { effort: \"${effort}\" },` : ''}\\n});\\nconsole.log(response.output_text);`\n      };\n    }\n    if (model.task === 'chat') {\n      const opts = effort ? `(${text.reasoning.effort.join(' | ')})` : '';\n      return {\n        curl: `curl ${API}/chat/completions \\\\\\n  ${auth} \\\\\\n  -d '{\\n    \"model\": \"${id}\",\\n    \"messages\": [{ \"role\": \"user\", \"content\": \"${VARIANT_PROMPTS.chat}\" }]${effort ? `,\\n    \"reasoning_effort\": \"${effort}\"` : ''}\\n  }'`,\n        python: `${pyHead}\\nresponse = client.chat.completions.create(\\n    model=\"${id}\",\\n    messages=[{\"role\": \"user\", \"content\": \"${VARIANT_PROMPTS.chat}\"}],${effort ? `\\n    reasoning_effort=\"${effort}\",  # ${opts}` : ''}\\n)\\nprint(response.choices[0].message.content)`,\n        typescript: `${tsHead}\\nconst response = await client.chat.completions.create({\\n  model: \"${id}\",\\n  messages: [{ role: \"user\", content: \"${VARIANT_PROMPTS.chat}\" }],${effort ? `\\n  reasoning_effort: \"${effort}\",` : ''}\\n});\\nconsole.log(response.choices[0].message.content);`\n      };\n    }\n    if (model.task === 'image-generation') {\n      const image = model.image || {};\n      const res = image.defaultResolution ? `,\\n    \"resolution\": \"${image.defaultResolution}\"` : '';\n      const ar = image.aspectRatios && image.aspectRatios.length ? `,\\n    \"aspect_ratio\": \"${image.defaultAspectRatio || image.aspectRatios[0]}\"` : '';\n      const body = `{\\n    \"model\": \"${id}\",\\n    \"prompt\": \"${VARIANT_PROMPTS.image.replace(/\"/g, '\\\\\"')}\"${res}${ar}\\n  }`;\n      return {\n        curl: `curl ${API}/image/generate \\\\\\n  ${auth} \\\\\\n  -d '${body.replace(/'/g, \"'\\\\''\")}'`,\n        python: `import base64\\n${reqHead}\\nres = requests.post(f\"{API}/image/generate\", headers=HEADERS, json={\\n    \"model\": \"${id}\",\\n    \"prompt\": \"A retro travel poster with the headline VENICE\",${image.defaultResolution ? `\\n    \"resolution\": \"${image.defaultResolution}\",` : ''}\\n})\\nres.raise_for_status()\\nwith open(\"image.webp\", \"wb\") as f:\\n    f.write(base64.b64decode(res.json()[\"images\"][0]))`,\n        typescript: `${fetchHead}\\nconst res = await fetch(\\`\\${API}/image/generate\\`, {\\n  method: \"POST\",\\n  headers,\\n  body: JSON.stringify({\\n    model: \"${id}\",\\n    prompt: \"A retro travel poster with the headline VENICE\",${image.defaultResolution ? `\\n    resolution: \"${image.defaultResolution}\",` : ''}\\n  }),\\n});\\nconst { images } = await res.json(); // base64-encoded`\n      };\n    }\n    if (model.task === 'image-edit' || model.task === 'image-upscale' || model.task === 'background-removal') {\n      const path = model.task === 'image-edit' ? '/image/edit' : model.task === 'image-upscale' ? '/image/upscale' : '/image/background-remove';\n      const extra = model.task === 'image-edit' ? `,\\n    \"prompt\": \"Make it a rainy night scene\"` : model.task === 'image-upscale' ? ',\\n    \"scale\": 2' : '';\n      const pyExtra = model.task === 'image-edit' ? '\\n    \"prompt\": \"Make it a rainy night scene\",' : model.task === 'image-upscale' ? '\\n    \"scale\": 2,' : '';\n      return {\n        curl: `curl ${API}${path} \\\\\\n  ${auth} \\\\\\n  -d '{\\n    \"model\": \"${id}\",\\n    \"image\": \"<base64-encoded image>\"${extra}\\n  }' --output result.png`,\n        python: `import base64\\n${reqHead}\\nwith open(\"photo.jpg\", \"rb\") as f:\\n    image = base64.b64encode(f.read()).decode()\\n\\nres = requests.post(f\"{API}${path}\", headers=HEADERS, json={\\n    \"model\": \"${id}\",\\n    \"image\": image,${pyExtra}\\n})\\nres.raise_for_status()\\nwith open(\"result.png\", \"wb\") as f:\\n    f.write(res.content)`,\n        typescript: `${fetchHead}\\nimport { readFile, writeFile } from \"node:fs/promises\";\\n\\nconst image = (await readFile(\"photo.jpg\")).toString(\"base64\");\\nconst res = await fetch(\\`\\${API}${path}\\`, {\\n  method: \"POST\",\\n  headers,\\n  body: JSON.stringify({ model: \"${id}\", image${model.task === 'image-edit' ? ', prompt: \"Make it a rainy night scene\"' : model.task === 'image-upscale' ? ', scale: 2' : ''} }),\\n});\\nawait writeFile(\"result.png\", Buffer.from(await res.arrayBuffer()));`\n      };\n    }\n    if (model.modality === 'video') {\n      const video = model.video || {};\n      const inputs = video.inputs || {};\n      const upscale = video.mode === 'upscale';\n      // Source-length models list only \"Auto\", which /video/queue rejects; they take no duration.\n      const timed = (video.durations || []).filter(d => d.seconds);\n      const dur = timed.find(d => d.seconds === 5) || timed[0];\n      const res = upscale ? null : (video.resolutions || []).find(r => r.height === 720) || (video.resolutions || [])[0];\n      const prompt = upscale ? null\n        : video.mode === 'motion' ? VARIANT_PROMPTS.videoMotion\n        : video.mode !== 'v2v' ? VARIANT_PROMPTS.video\n        : inputs.reference ? VARIANT_PROMPTS.videoRecast\n        : /extend/.test(id) ? VARIANT_PROMPTS.videoExtend\n        : VARIANT_PROMPTS.videoEdit;\n      const fields = [\n        `\"model\": \"${id}\"`,\n        prompt ? `\"prompt\": \"${prompt}\"` : null,\n        dur ? `\"duration\": \"${dur.value}\"` : null,\n        res ? `\"resolution\": \"${res.value}\"` : null,\n        upscale ? '\"upscale_factor\": 2' : null,\n        video.aspectRatios && video.aspectRatios.length ? `\"aspect_ratio\": \"${video.aspectRatios[0]}\"` : null,\n        video.audio === 'optional' ? '\"audio\": true' : null,\n        inputs.image ? `\"image_url\": \"https://example.com/${video.mode === 'motion' ? 'character' : 'first-frame'}.jpg\"` : null,\n        inputs.endImage ? '\"end_image_url\": \"https://example.com/last-frame.jpg\"' : null,\n        inputs.reference ? '\"reference_image_urls\": [\"https://example.com/character.jpg\"]' : null,\n        inputs.video ? '\"video_url\": \"https://example.com/source.mp4\"' : null,\n        inputs.referenceVideo ? '\"reference_video_urls\": [\"https://example.com/source.mp4\"]' : null\n      ].filter(Boolean);\n      const pyFields = fields.map(f => f.replace(/\": true/, '\": True'));\n      return {\n        curl: `# 1. Queue the job (returns a queue_id)\\ncurl ${API}/video/queue \\\\\\n  ${auth} \\\\\\n  -d '{\\n    ${fields.join(',\\n    ')}\\n  }'\\n\\n# 2. Poll until the MP4 is ready\\ncurl ${API}/video/retrieve \\\\\\n  ${auth} \\\\\\n  -d '{ \"model\": \"${id}\", \"queue_id\": \"<queue_id>\" }' --output video.mp4`,\n        python: `import time\\n${reqHead}\\njob = requests.post(f\"{API}/video/queue\", headers=HEADERS, json={\\n    ${pyFields.join(',\\n    ')},\\n}).json()\\n\\nwhile True:\\n    res = requests.post(f\"{API}/video/retrieve\", headers=HEADERS,\\n                        json={\"model\": \"${id}\", \"queue_id\": job[\"queue_id\"]})\\n    if res.headers.get(\"content-type\", \"\").startswith(\"video/\"):\\n        open(\"video.mp4\", \"wb\").write(res.content)\\n        break\\n    time.sleep(5)  # still processing`,\n        typescript: `${fetchHead}\\nconst job = await fetch(\\`\\${API}/video/queue\\`, {\\n  method: \"POST\",\\n  headers,\\n  body: JSON.stringify({\\n    ${fields.map(f => f.replace(/^\"([a-z_]+)\":/, '$1:')).join(',\\n    ')},\\n  }),\\n}).then(r => r.json());\\n\\nlet res;\\ndo {\\n  await new Promise(r => setTimeout(r, 5000));\\n  res = await fetch(\\`\\${API}/video/retrieve\\`, {\\n    method: \"POST\",\\n    headers,\\n    body: JSON.stringify({ model: \"${id}\", queue_id: job.queue_id }),\\n  });\\n} while (!res.headers.get(\"content-type\")?.startsWith(\"video/\"));`\n      };\n    }\n    if (model.task === 'tts' && !(model.audio && model.audio.async)) {\n      const voice = (model.audio && model.audio.voices && model.audio.voices[0]) || 'default';\n      return {\n        curl: `curl ${API}/audio/speech \\\\\\n  ${auth} \\\\\\n  -d '{\\n    \"model\": \"${id}\",\\n    \"input\": \"Private AI for everyone.\",\\n    \"voice\": \"${voice}\",\\n    \"response_format\": \"mp3\"\\n  }' --output speech.mp3`,\n        python: `${pyHead}\\nwith client.audio.speech.with_streaming_response.create(\\n    model=\"${id}\",\\n    voice=\"${voice}\",\\n    input=\"Private AI for everyone.\",\\n) as response:\\n    response.stream_to_file(\"speech.mp3\")`,\n        typescript: `${tsHead}\\nimport { writeFile } from \"node:fs/promises\";\\n\\nconst speech = await client.audio.speech.create({\\n  model: \"${id}\",\\n  voice: \"${voice}\",\\n  input: \"Private AI for everyone.\",\\n});\\nawait writeFile(\"speech.mp3\", Buffer.from(await speech.arrayBuffer()));`\n      };\n    }\n    if (model.task === 'stt') {\n      return {\n        curl: `curl ${API}/audio/transcriptions \\\\\\n  -H \"Authorization: Bearer ${key}\" \\\\\\n  -F model=${id} \\\\\\n  -F file=@meeting.mp3`,\n        python: `${pyHead}\\nwith open(\"meeting.mp3\", \"rb\") as audio:\\n    transcript = client.audio.transcriptions.create(model=\"${id}\", file=audio)\\nprint(transcript.text)`,\n        typescript: `${tsHead}\\nimport fs from \"node:fs\";\\n\\nconst transcript = await client.audio.transcriptions.create({\\n  model: \"${id}\",\\n  file: fs.createReadStream(\"meeting.mp3\"),\\n});\\nconsole.log(transcript.text);`\n      };\n    }\n    if (model.modality === 'audio') {\n      const prompt = model.task === 'sfx' ? VARIANT_PROMPTS.sfx : VARIANT_PROMPTS.music;\n      const isTts = model.task === 'tts';\n      const input = isTts ? '\"prompt\": \"Private AI for everyone.\"' : `\"prompt\": \"${prompt}\"`;\n      const a = model.audio || {};\n      const seconds = a.durationOptions ? a.durationOptions[0] : a.defaultDuration ? Math.max(a.minDuration || 1, Math.min(30, a.defaultDuration)) : null;\n      const duration = !isTts && seconds ? `,\\n    \"duration_seconds\": ${seconds}` : '';\n      return {\n        curl: `# 1. Queue the job\\ncurl ${API}/audio/queue \\\\\\n  ${auth} \\\\\\n  -d '{\\n    \"model\": \"${id}\",\\n    ${input}${duration}\\n  }'\\n\\n# 2. Retrieve when ready\\ncurl ${API}/audio/retrieve \\\\\\n  ${auth} \\\\\\n  -d '{ \"model\": \"${id}\", \"queue_id\": \"<queue_id>\" }' --output audio.mp3`,\n        python: `import time\\n${reqHead}\\njob = requests.post(f\"{API}/audio/queue\", headers=HEADERS, json={\\n    \"model\": \"${id}\",\\n    ${input}${duration.replace(/\\n    /, '\\n    ')},\\n}).json()\\n\\nwhile True:\\n    res = requests.post(f\"{API}/audio/retrieve\", headers=HEADERS,\\n                        json={\"model\": \"${id}\", \"queue_id\": job[\"queue_id\"]})\\n    if res.headers.get(\"content-type\", \"\").startswith(\"audio/\"):\\n        open(\"audio.mp3\", \"wb\").write(res.content)\\n        break\\n    time.sleep(3)`,\n        typescript: `${fetchHead}\\nconst job = await fetch(\\`\\${API}/audio/queue\\`, {\\n  method: \"POST\",\\n  headers,\\n  body: JSON.stringify({ model: \"${id}\", ${isTts ? 'prompt: \"Private AI for everyone.\"' : `prompt: \"${prompt}\"`} }),\\n}).then(r => r.json());\\n// Poll POST /audio/retrieve with { model, queue_id: job.queue_id }`\n      };\n    }\n    if (model.modality === 'embedding') {\n      return {\n        curl: `curl ${API}/embeddings \\\\\\n  ${auth} \\\\\\n  -d '{\\n    \"model\": \"${id}\",\\n    \"input\": \"Private AI for everyone.\"\\n  }'`,\n        python: `${pyHead}\\nresult = client.embeddings.create(model=\"${id}\", input=\"Private AI for everyone.\")\\nvector = result.data[0].embedding  # ${model.embedding && model.embedding.dimensions ? `${model.embedding.dimensions} dimensions` : 'list of floats'}`,\n        typescript: `${tsHead}\\nconst result = await client.embeddings.create({\\n  model: \"${id}\",\\n  input: \"Private AI for everyone.\",\\n});\\nconst vector = result.data[0].embedding;`\n      };\n    }\n    return {\n      curl: `curl ${API}/decisions \\\\\\n  ${auth} \\\\\\n  -d '{\\n    \"model\": \"${id}\",\\n    \"state\": \"Help! My payouts have been failing for 3 days.\",\\n    \"questions\": {\\n      \"is_urgent\": { \"type\": \"noul\", \"instructions\": \"Does this need a reply within the hour?\" }\\n    }\\n  }'`,\n      python: `${reqHead}\\nres = requests.post(f\"{API}/decisions\", headers=HEADERS, json={\\n    \"model\": \"${id}\",\\n    \"state\": \"Help! My payouts have been failing for 3 days.\",\\n    \"questions\": {\\n        \"is_urgent\": {\"type\": \"noul\", \"instructions\": \"Does this need a reply within the hour?\"},\\n    },\\n})\\nprint(res.json()[\"answers\"][\"is_urgent\"][\"noul\"])  # 0 (no) to 1 (yes)`,\n      typescript: `${fetchHead}\\nconst res = await fetch(\\`\\${API}/decisions\\`, {\\n  method: \"POST\",\\n  headers,\\n  body: JSON.stringify({\\n    model: \"${id}\",\\n    state: \"Help! My payouts have been failing for 3 days.\",\\n    questions: { is_urgent: { type: \"noul\", instructions: \"Does this need a reply within the hour?\" } },\\n  }),\\n});\\nconst { answers } = await res.json();`\n    };\n  };\n\n  // Arrow keys, Home and End move between options in a tablist or radio\n  // group; only the selected option is in the tab order.\n  const rovingKeys = (keys, current, select) => e => {\n    const i = keys.indexOf(current);\n    const next = { ArrowRight: i + 1, ArrowDown: i + 1, ArrowLeft: i - 1, ArrowUp: i - 1, Home: 0, End: keys.length - 1 }[e.key];\n    if (next === undefined) return;\n    e.preventDefault();\n    const key = keys[(next + keys.length) % keys.length];\n    select(key);\n    const target = e.currentTarget.querySelector(`[data-key=\"${CSS.escape(String(key))}\"]`);\n    if (target) target.focus();\n  };\n\n  const CodeTabs = ({ samples }) => {\n    const [lang, setLang] = useState('curl');\n    const langs = [['curl', 'cURL'], ['python', 'Python'], ['typescript', 'TypeScript']];\n    return (\n      __jsx('div', { className: \"vx-code\",}\n        , __jsx('div', { className: \"vx-code-head\",}\n          , __jsx('div', { className: \"vx-code-tabs\", role: \"tablist\", 'aria-label': \"Language\", onKeyDown: rovingKeys(langs.map(([key]) => key), lang, setLang),}\n            , langs.map(([key, label]) => (\n              __jsx('button', { key: key, id: `vx-code-tab-${key}`, 'data-key': key, type: \"button\", role: \"tab\", 'aria-selected': lang === key, 'aria-controls': \"vx-code-panel\",\n                tabIndex: lang === key ? 0 : -1, className: cls('vx-code-tab', lang === key && 'is-active'), onClick: () => setLang(key),}, label)\n            ))\n          )\n          , __jsx(CopyButton, { text: samples[lang], label: \"Copy\", small: true,} )\n        )\n        , __jsx('pre', { id: \"vx-code-panel\", className: \"vx-code-body\", role: \"tabpanel\", 'aria-labelledby': `vx-code-tab-${lang}`, tabIndex: 0,}, __jsx('code', null, samples[lang]))\n      )\n    );\n  };\n\n  /* ------------------------------------------------------------ model page */\n\n  const pageSections = (model, { variants = 1, related = false, examples = false, capabilities = false } = {}) => {\n    const list = [['overview', 'Overview'], ['pricing', 'Pricing']];\n    if (examples) list.push(['examples', 'Examples']);\n    if (model.task === 'tts' && model.audio && model.audio.voices && model.audio.voices.length) list.push(['voices', 'Voices']);\n    list.push(['specs', 'Specs']);\n    if (capabilities) list.push(['capabilities', 'Capabilities']);\n    list.push(['api', 'API']);\n    if (PRIVACY_TIERS.some(([tier]) => tier === model.privacy)) list.push(['privacy', 'Privacy']);\n    if (variants > 1) list.push(['variants', 'Variants']);\n    if (related) list.push(['related', 'Related models']);\n    return list;\n  };\n\n  const useScrollSpy = ids => {\n    const [active, setActive] = useState(ids[0]);\n    const key = ids.join('|');\n    useEffect(() => {\n      if (typeof IntersectionObserver === 'undefined') return undefined;\n      const seen = new Map();\n      const observer = new IntersectionObserver(entries => {\n        entries.forEach(entry => seen.set(entry.target.id, entry.isIntersecting));\n        const first = ids.find(id => seen.get(id));\n        if (first) setActive(first);\n      }, { rootMargin: '-96px 0px -55% 0px' });\n      ids.forEach(id => { const el = document.getElementById(id); if (el) observer.observe(el); });\n      return () => observer.disconnect();\n    }, [key]);\n    return active;\n  };\n\n  const Section = ({ id, title, note, actions, children }) => (\n    __jsx('section', { id: id, className: \"vx-section\", 'aria-labelledby': `${id}-title`,}\n      , __jsx('div', { className: \"vx-section-head\",}\n        , __jsx('h2', { id: `${id}-title`, className: \"vx-h2\",}, title)\n        , note ? __jsx('span', { className: \"vx-section-note\",}, note) : null\n        , actions ? __jsx('div', { className: \"vx-section-actions\",}, actions) : null\n      )\n      , children\n    )\n  );\n\n  // Three cards a row, or four when a model has only four facts. Empty cells\n  // fill the last row so its hairlines stay unbroken.\n  const StatGrid = ({ items }) => {\n    const cells = items.filter(Boolean).slice(0, 6);\n    if (!cells.length) return null;\n    const cols = cells.length <= 4 ? cells.length : 3;\n    const empty = (cols - (cells.length % cols)) % cols;\n    return (\n      __jsx('div', { className: cls('vx-stats', cells.length % 2 && 'is-odd'), style: { '--vx-cols': cols },}\n        , cells.map((item, i) => __jsx(Stat, { key: item.label, ...item, last: i === cells.length - 1,} ))\n        , Array.from({ length: empty }, (_, i) => __jsx('span', { key: `empty-${i}`, className: \"vx-stat is-empty\" , 'aria-hidden': \"true\",} ))\n      )\n    );\n  };\n\n  const KeyStats = ({ model, models }) => {\n    const p = model.pricing || {};\n    if (model.modality === 'text' && model.task === 'chat') {\n      const text = model.text || {};\n      const caps = text.caps || {};\n      const r = text.reasoning;\n      const levels = (r && r.effort) || [];\n      return (\n        __jsx(StatGrid, { items: [\n          { label: 'Input / output', value: `${usd(p.input)} / ${usd(p.output)}`, sub: 'per 1M tokens' },\n          { label: 'Context', value: tokens(text.context), sub: text.maxOutput ? `${tokens(text.maxOutput)} max output` : 'max output not published' },\n          {\n            label: 'Reasoning',\n            value: !r ? 'None' : levels.length ? levels.join(', ') : 'On',\n            sub: !r ? 'answers directly' : r.defaultEffort ? `default ${r.defaultEffort}` : levels.length ? 'reasoning_effort' : 'fixed effort'\n          },\n          p.cacheRead != null\n            ? { label: 'Cached input', value: usd(p.cacheRead), sub: p.input ? `${Math.round((1 - p.cacheRead / p.input) * 100)}% off input` : 'per 1M tokens' }\n            : { label: 'Cached input', value: '—', sub: 'no prompt caching' },\n          { label: 'Modalities', value: ['Text', caps.vision && 'image', caps.videoInput && 'video', caps.audioInput && 'audio'].filter(Boolean).join(', '), sub: 'out: text' }\n        ],} )\n      );\n    }\n    if (model.task === 'decision') {\n      const d = model.decision || {};\n      return (\n        __jsx(StatGrid, { items: [\n          { label: 'Input / output', value: `${usd(p.input)} / ${usd(p.output)}`, sub: 'per 1M tokens' },\n          d.maxStateTokens ? { label: 'Max state', value: tokens(d.maxStateTokens), sub: 'tokens' } : null,\n          d.maxTotalTokens ? { label: 'Max total', value: tokens(d.maxTotalTokens), sub: 'tokens per request' } : null\n        ],} )\n      );\n    }\n    if (model.modality === 'image') {\n      const image = model.image || {};\n      const prices = p.byResolution ? Object.values(p.byResolution).filter(v => v != null) : [];\n      const lo = prices.length ? Math.min(...prices) : p.perImage;\n      const hi = prices.length ? Math.max(...prices) : p.perImage;\n      const unit = model.task === 'image-edit' ? 'edit' : model.task === 'image-upscale' ? '2x upscale' : 'image';\n      const resolutions = image.resolutions || [];\n      const ratios = (image.aspectRatios || []).filter(a => a !== 'auto');\n      const editor = model.task === 'image-edit' ? model : models.find(m => m.task === 'image-edit');\n      const refs = editor && editor.image && editor.image.maxInputImages;\n      return (\n        __jsx(StatGrid, { items: [\n          { label: `Price per ${unit}`, value: lo === hi ? usd(lo) : `${usd(lo)} – ${usd(hi)}`, sub: resolutions.length > 1 ? `${resolutions[0]} to ${resolutions[resolutions.length - 1]}` : resolutions[0] || null },\n          model.task === 'image-upscale' && p.upscale && p.upscale['4x'] != null ? { label: '4x upscale', value: usd(p.upscale['4x']), sub: 'per image' } : null,\n          resolutions.length ? { label: 'Resolution', value: resolutions.join(' · '), sub: image.defaultResolution ? `default ${image.defaultResolution}` : null } : null,\n          ratios.length ? { label: 'Aspect ratios', value: plural(ratios.length, 'ratio'), sub: image.defaultAspectRatio ? `default ${image.defaultAspectRatio}` : null } : null,\n          refs ? { label: 'Reference images', value: `Up to ${refs}`, sub: editor.image.combineImages ? 'combined in one edit' : 'per edit' } : null,\n          image.promptLimit ? { label: 'Prompt limit', value: image.promptLimit.toLocaleString('en-US'), sub: 'characters' } : null,\n          p.extraInputImage != null ? { label: 'Extra input image', value: usd(p.extraInputImage), sub: 'per additional image' } : null\n        ],} )\n      );\n    }\n    if (model.modality === 'video') {\n      const draft = videoPrice(model, DRAFT_CLIP);\n      const prod = videoPrice(model, PRODUCTION_CLIP);\n      const video = model.video || {};\n      return (\n        __jsx(StatGrid, { items: [\n          { label: 'From', value: p.status === 'quoted' ? `${usd(p.fromPerSecond)} / sec` : 'By source', sub: p.status === 'quoted' ? `up to ${usd(p.toPerSecond)} / sec` : 'priced per second of input' },\n          { label: 'Draft clip', value: draft.status === 'quoted' ? usd(draft.clip) : '—', sub: draft.status === 'quoted' ? `${draft.seconds}s · ${draft.resolution || 'default'}${draft.audio ? ' · audio' : ''}` : null },\n          { label: 'Production clip', value: prod.status === 'quoted' ? usd(prod.clip) : '—', sub: prod.status === 'quoted' ? `${prod.seconds}s · ${prod.resolution || 'default'}${prod.audio ? ' · audio' : ''}` : null },\n          { label: 'Max resolution', value: heightLabel(maxVideoHeight(model)), sub: plural((video.resolutions || []).length || 1, 'option') },\n          { label: 'Duration', value: minVideoSeconds(model) ? `${minVideoSeconds(model)}–${maxVideoSeconds(model)}s` : 'Source length', sub: plural((video.durations || []).length, 'option') },\n          { label: 'Audio', value: video.audio === 'native' ? 'Native' : video.audio === 'optional' ? 'Optional' : 'None', sub: video.audio === 'optional' ? 'toggle with audio' : null }\n        ],} )\n      );\n    }\n    if (model.task === 'tts') {\n      const audio = model.audio || {};\n      return (\n        __jsx(StatGrid, { items: [\n          { label: 'Per 1M characters', value: usd(p.per1MChars) },\n          { label: 'Per minute', value: usd(p.perMinute), sub: '≈ 825 characters' },\n          { label: 'Per hour', value: usd(p.perHour), sub: 'of generated speech' },\n          { label: 'Voices', value: (audio.voices && audio.voices.length) || '—', sub: audio.customVoice ? 'plus custom voice IDs' : null },\n          { label: 'Formats', value: (audio.formats && audio.formats.length) || '—', sub: audio.formats ? audio.formats.slice(0, 4).join(', ') : null }\n        ],} )\n      );\n    }\n    if (model.task === 'stt') {\n      return (\n        __jsx(StatGrid, { items: [\n          { label: 'Per audio second', value: usd(p.perSecond) },\n          { label: 'Per minute', value: usd(p.perMinute) },\n          { label: 'Per hour', value: usd(p.perHour) },\n          { label: 'Per 1K minutes', value: usd(p.perMinute != null ? p.perMinute * 1000 : null), sub: 'for batch planning' }\n        ],} )\n      );\n    }\n    if (model.modality === 'audio') {\n      const audio = model.audio || {};\n      return (\n        __jsx(StatGrid, { items: [\n          { label: 'Price', value: p.perMinute != null ? usd(p.perMinute) : usd(p.perTrack), sub: p.perMinute != null ? 'per minute of audio' : 'per track' },\n          { label: 'Length', value: audio.maxDuration ? `${audio.minDuration || 1}s–${Math.round(audio.maxDuration / 60 * 10) / 10}m` : '—' },\n          { label: 'Lyrics', value: audio.lyrics ? 'Supported' : 'No' },\n          { label: 'Instrumental', value: audio.instrumental ? 'Forceable' : '—' }\n        ],} )\n      );\n    }\n    if (model.modality === 'embedding') {\n      const e = model.embedding || {};\n      return (\n        __jsx(StatGrid, { items: [\n          { label: 'Per 1M tokens', value: usd(p.input) },\n          { label: 'Dimensions', value: e.dimensions ? e.dimensions.toLocaleString('en-US') : '—' },\n          { label: 'Max input', value: tokens(e.maxInputTokens), sub: 'tokens per item' },\n          { label: '1M docs × 500 tokens', value: usd(p.input != null ? p.input * 500 : null), sub: 'to embed once' }\n        ],} )\n      );\n    }\n    return null;\n  };\n\n  // A ruled table of what a model takes and does: supported values read in\n  // the success color, the rest muted.\n  const CapTable = ({ title, rows }) => (\n    __jsx('div', { className: \"vx-captable\",}\n      , __jsx('div', { className: \"vx-captable-head\",}, title)\n      , __jsx('dl', null\n        , rows.map(([label, value]) => (\n          __jsx('div', { key: label, className: \"vx-captable-row\",}\n            , __jsx('dt', null, label)\n            , __jsx('dd', { className: value ? 'is-on' : 'is-off',}, value || 'Not supported')\n          )\n        ))\n      )\n    )\n  );\n\n  const hasCapabilities = (model, models) => model.task === 'chat' || (model.modality === 'image' && models.some(m => m.task === 'image-generation'));\n\n  const CapabilityMatrix = ({ model, models }) => {\n    if (model.modality === 'image') {\n      const image = model.image || {};\n      const editor = models.find(m => m.task === 'image-edit');\n      const refs = editor && editor.image && editor.image.maxInputImages;\n      const upscale = model.pricing && model.pricing.upscale ? Object.keys(model.pricing.upscale).filter(k => model.pricing.upscale[k] != null) : [];\n      return (\n        __jsx('div', { className: \"vx-capgrid\",}\n          , __jsx(CapTable, { title: \"Modalities\", rows: [\n            ['Text', 'Input'],\n            ['Image', editor ? 'Input and output' : 'Output']\n          ],} )\n          , __jsx(CapTable, { title: \"Features\", rows: [\n            ['Image editing', editor ? __jsx('code', null, editor.id) : null],\n            ['Multi-image editing', refs > 1 ? `up to ${refs} images` : null],\n            ['Web-grounded generation', image.webSearch ? 'Supported' : null],\n            ['Style references', image.styleReferences ? 'Supported' : null],\n            ['Upscale', upscale.length ? listText(upscale) : null]\n          ],} )\n        )\n      );\n    }\n    const text = model.text || {};\n    const caps = text.caps || {};\n    const r = text.reasoning;\n    const levels = (r && r.effort) || [];\n    const per = (on, n) => (on ? `Input only${n ? ` · up to ${n}` : ''}` : null);\n    return (\n      __jsx('div', { className: \"vx-capgrid\",}\n        , __jsx(CapTable, { title: \"Modalities\", rows: [\n          ['Text', 'Input and output'],\n          ['Image', per(caps.vision, caps.maxImages)],\n          ['Audio', per(caps.audioInput)],\n          ['Video', per(caps.videoInput, caps.maxVideos)]\n        ],} )\n        , __jsx(CapTable, { title: \"Features\", rows: [\n          ['Streaming (SSE)', 'Supported'],\n          ['Function calling', caps.tools ? 'Supported' : null],\n          ['Structured output (json_schema)', caps.structured ? 'Supported' : null],\n          ['Reasoning effort', !r ? null : levels.length ? levels.join(', ') : 'Not adjustable'],\n          ['Prompt caching', hasCap(model, 'caching') ? 'Supported' : null],\n          ['Web search', caps.webSearch ? 'venice_parameters' : null],\n          ['X search', caps.xSearch ? 'venice_parameters' : null],\n          ['Log probabilities', caps.logprobs ? 'Supported' : null],\n          ['Fine-tuning', null],\n          ['Image generation', null]\n        ],} )\n      )\n    );\n  };\n\n  const count = n => n.toLocaleString('en-US');\n  const ratioShapes = (ratios, active) => __jsx('span', { className: \"vx-ratios\",}, ratios.map(ar => __jsx(AspectShape, { key: ar, ratio: ar, active: ar === active,} )));\n\n  // The rows each modality adds to Specs, between the IDs and the dates.\n  const modalitySpecs = model => {\n    if (model.task === 'chat') {\n      const t = model.text || {};\n      const r = t.reasoning;\n      const levels = (r && r.effort) || [];\n      const q = t.quantization;\n      const sampling = t.sampling && Object.keys(t.sampling).length ? Object.entries(t.sampling).map(([k, v]) => `${k} ${v}`).join(' · ') : null;\n      return [\n        ['Context window', t.context ? `${count(t.context)} tokens` : 'Not published'],\n        ['Max output', t.maxOutput ? `${count(t.maxOutput)} tokens` : 'Not published'],\n        ['Reasoning effort', !r ? 'No separate reasoning phase' : levels.length\n          ? __jsx(__Fragment, null, __jsx('code', null, \"reasoning_effort: \" , levels.join(' | ')), r.defaultEffort ? __jsx('span', { className: \"vx-muted\",}, \" · default \"   , r.defaultEffort) : null, \" · \"  , __jsx('a', { href: \"/guides/features/reasoning-models#reasoning-effort\",}, \"guide\"))\n          : __jsx(__Fragment, null, \"On at a fixed effort; send \"      , __jsx('code', null, '\"reasoning\": { \"enabled\": false }'), \" to turn it off where the model allows · \"          , __jsx('a', { href: \"/guides/features/reasoning-models\",}, \"guide\"))],\n        ['Served precision', q ? QUANT_LABELS[q] || q : model.privacy === 'anonymized' ? 'Set by the upstream provider' : 'Not disclosed'],\n        sampling ? ['Default sampling', sampling] : null\n      ];\n    }\n    if (model.task === 'decision') {\n      const d = model.decision || {};\n      return [\n        d.maxStateTokens ? ['Max state', `${count(d.maxStateTokens)} tokens`] : null,\n        d.maxTotalTokens ? ['Max total', `${count(d.maxTotalTokens)} tokens`] : null\n      ];\n    }\n    if (model.modality === 'image') {\n      const image = model.image || {};\n      return [\n        image.resolutions && image.resolutions.length ? ['Resolutions', `${image.resolutions.join(', ')}${image.defaultResolution ? ` (default ${image.defaultResolution})` : ''}`] : null,\n        image.aspectRatios && image.aspectRatios.length ? ['Aspect ratios', ratioShapes(image.aspectRatios, image.defaultAspectRatio)] : null,\n        image.promptLimit ? ['Prompt limit', `${count(image.promptLimit)} characters`] : null,\n        image.steps ? ['Steps', `default ${image.steps.default}, max ${image.steps.max}`] : null,\n        image.maxInputImages ? ['Input images', `Up to ${image.maxInputImages}${image.combineImages ? ', combined into one edit' : ''}`] : null\n      ];\n    }\n    if (model.modality === 'video') {\n      const video = model.video || {};\n      return [\n        ['Mode', VARIANT_LABELS[model.variant] || model.variant],\n        ['Inputs', upperFirst(videoInputList(video).join(', '))],\n        video.resolutions && video.resolutions.length ? ['Resolutions', video.resolutions.map(r => r.label).join(', ')] : null,\n        video.durations && video.durations.length ? ['Durations', durationText(video.durations)] : null,\n        video.aspectRatios && video.aspectRatios.length ? ['Aspect ratios', ratioShapes(video.aspectRatios)] : null,\n        ['Audio', video.audio === 'native' ? 'Always generated' : video.audio === 'optional' ? __jsx(__Fragment, null, \"Optional; set \"  , __jsx('code', null, \"\\\"audio\\\": true\" )) : 'Silent output'],\n        video.promptLimit ? ['Prompt limit', `${count(video.promptLimit)} characters`] : null\n      ];\n    }\n    if (model.task === 'tts') {\n      const a = model.audio || {};\n      return [\n        a.voices && a.voices.length ? ['Voices', `${a.voices.length}${a.customVoice ? ', plus custom voice IDs' : ''}`] : null,\n        a.formats && a.formats.length ? ['Formats', `${a.formats.join(', ')}${a.defaultFormat ? ` (default ${a.defaultFormat})` : ''}`] : null\n      ];\n    }\n    if (model.modality === 'audio' && model.task !== 'stt') {\n      const a = model.audio || {};\n      return [\n        a.durationOptions ? ['Duration', `${a.durationOptions.join(', ')} seconds${a.defaultDuration ? ` (default ${a.defaultDuration})` : ''}`]\n          : a.maxDuration ? ['Duration', `${a.minDuration || 1}–${a.maxDuration} seconds${a.defaultDuration ? ` (default ${a.defaultDuration})` : ''}`] : null,\n        ['Lyrics', a.lyricsRequired ? 'Required' : a.lyrics ? 'Supported' : 'Not supported'],\n        a.instrumental ? ['Instrumental', 'Can be forced'] : null,\n        a.loop ? ['Seamless loop', 'Supported'] : null,\n        a.formats && a.formats.length ? ['Formats', a.formats.join(', ')] : null,\n        a.promptLimit ? ['Prompt limit', `${count(a.promptLimit)} characters`] : null\n      ];\n    }\n    if (model.modality === 'embedding') {\n      const e = model.embedding || {};\n      return [\n        e.dimensions ? ['Dimensions', count(e.dimensions)] : null,\n        e.maxInputTokens ? ['Max input', `${count(e.maxInputTokens)} tokens per item`] : null\n      ];\n    }\n    return [];\n  };\n\n  const Specs = ({ model, models }) => {\n    const endpoint = (model.endpoints || []).find(e => e.recommended) || (model.endpoints || [])[0];\n    const editor = model.task === 'image-generation' ? models.find(m => m.task === 'image-edit') : null;\n    const rows = [\n      ['Model ID', __jsx('code', null, model.id)],\n      editor ? ['Edit model ID', __jsx('code', null, editor.id)] : null,\n      endpoint ? ['Endpoint', __jsx('code', null, endpoint.method, \" /api/v1\" , endpoint.path)] : null,\n      ...modalitySpecs(model),\n      ['Added', fmtDate(model.created)],\n      model.openWeights ? ['Weights', `Open${model.license ? ` · ${model.license}` : ''}`] : null\n    ].filter(Boolean);\n    return (\n      __jsx('dl', { className: \"vx-kv vx-specs\" ,}\n        , rows.map(([label, value]) => (\n          __jsx('div', { key: label, className: \"vx-kv-row\",}, __jsx('dt', null, label), __jsx('dd', null, value))\n        ))\n      )\n    );\n  };\n\n  // Lifecycle in words, for the page's static text.\n  const statusText = model => {\n    const d = model.deprecation;\n    if (d) {\n      const date = d.removesAt && Number.isFinite(Date.parse(d.removesAt)) ? fmtDate(Math.floor(Date.parse(d.removesAt) / 1000)) : null;\n      return `Deprecated${date ? `, retiring ${date}` : ''}${d.replacement ? `; use ${d.replacement}` : ''}`;\n    }\n    return model.beta ? 'Beta' : 'Stable';\n  };\n  // The maker is the upstream for proprietary models; an open-weight model's\n  // host isn't known, so it isn't named.\n  const upstreamName = (model, provider) => (!model.openWeights && provider ? provider.name : null);\n\n  // Venice's four privacy modes, weakest first. Each builds on the one before,\n  // so lower tiers are included in the current one.\n  const PRIVACY_TIERS = [['anonymized', 'Anonymized'], ['private', 'Private'], ['tee', 'TEE'], ['e2ee', 'End-to-end encrypted']];\n  const tierText = (tier, by) => ({\n    anonymized: `Venice hides your identity and stores no prompts. ${by || 'The provider'} still sees the content.`,\n    private: \"Zero data retention, enforced by contract. Prompts and outputs aren't stored.\",\n    tee: 'Hardware-isolated inference with remote attestation.',\n    e2ee: 'Your client encrypts the prompt, and only the verified enclave decrypts it.'\n  })[tier];\n\n  const PrivacyTiers = ({ model, models, provider, onSelect }) => {\n    const rank = PRIVACY_TIERS.findIndex(([tier]) => tier === model.privacy);\n    return (\n      __jsx('div', { className: \"vx-tiers\",}\n        , PRIVACY_TIERS.map(([tier, label], i) => {\n          const other = i > rank ? models.find(m => m.id !== model.id && offersTier(m, tier)) : null;\n          return (\n            __jsx('div', { key: tier, className: cls('vx-tier', i === rank && 'is-current'), 'aria-current': i === rank ? 'true' : undefined,}\n              , __jsx('div', { className: \"vx-tier-num\",}, \"Tier \" , i + 1, i === rank ? ' · this model' : '')\n              , __jsx('div', { className: \"vx-tier-name\",}, label)\n              , i === rank ? __jsx('p', { className: \"vx-tier-text\",}, tierText(tier, upstreamName(model, provider)))\n                : i < rank ? __jsx('p', { className: \"vx-tier-text\",}, \"Included\")\n                  : other ? __jsx('button', { type: \"button\", className: \"vx-tier-link\", onClick: () => onSelect(other.id),}, \"Available as \"  , __jsx('code', null, other.id))\n                    : __jsx('p', { className: \"vx-tier-text\",}, \"Not available\" )\n            )\n          );\n        })\n      )\n    );\n  };\n\n  // One card per billed unit, four a row, on the same hairline gaps as the\n  // key numbers.\n  const PriceCards = ({ items }) => {\n    const cells = items.filter(Boolean);\n    if (!cells.length) return null;\n    const cols = Math.min(cells.length, 4);\n    const empty = (cols - (cells.length % cols)) % cols;\n    return (\n      __jsx('div', { className: cls('vx-prices', cells.length % 2 && 'is-odd'), style: { '--vx-cols': cols },}\n        , cells.map((item, i) => (\n          __jsx('div', { key: item.label, className: cls('vx-price-card', i === cells.length - 1 && 'is-last'),}\n            , __jsx('div', { className: \"vx-price-card-label\",}, item.label, item.hint ? __jsx('span', { className: \"vx-muted\",}, \" \" , item.hint) : null)\n            , __jsx('div', { className: \"vx-price-card-value\",}, item.value)\n            , item.sub ? __jsx('div', { className: \"vx-price-card-sub\",}, item.sub) : null\n          )\n        ))\n        , Array.from({ length: empty }, (_, i) => __jsx('span', { key: `empty-${i}`, className: \"vx-price-card is-empty\" , 'aria-hidden': \"true\",} ))\n      )\n    );\n  };\n\n  // This model's prices against similar models on Venice. The bar scales one\n  // column across the rows; it repeats the figures, so it's hidden from\n  // assistive tech.\n  const SimilarPrices = ({ columns, bar, rows }) => {\n    const shown = rows.filter(row => row.current || columns.some(col => col.get(row) != null));\n    if (shown.length < 2) return null;\n    const values = shown.map(bar.get).filter(v => v != null && v > 0);\n    const max = values.length ? Math.max(...values) : 0;\n    return (\n      __jsx('div', { className: \"vx-table-wrap vx-ruled vx-ptable-wrap\"  ,}\n        , __jsx('table', { className: \"vx-ptable\",}\n          , __jsx('thead', null\n            , __jsx('tr', null\n              , __jsx('th', { scope: \"col\",}, \"Similar models\" )\n              , columns.map(col => __jsx('th', { key: col.label, scope: \"col\", className: \"vx-al-right\",}, col.label))\n              , __jsx('th', { scope: \"col\", className: \"vx-ptable-bar-head vx-hide-sm\" ,}, bar.label)\n            )\n          )\n          , __jsx('tbody', null\n            , shown.map(row => {\n              const value = bar.get(row);\n              return (\n                __jsx('tr', { key: row.key, className: cls(row.current && 'is-current'),}\n                  , __jsx('th', { scope: \"row\",}, row.href ? __jsx('a', { href: row.href,}, row.name) : row.name)\n                  , columns.map(col => __jsx('td', { key: col.label, className: \"vx-al-right vx-num\" ,}, usd(col.get(row))))\n                  , __jsx('td', { className: \"vx-ptable-bar vx-hide-sm\" , 'aria-hidden': \"true\",}\n                    , __jsx('span', { className: \"vx-ptable-track\",}, __jsx('span', { style: { width: value && max ? `${Math.max(1, (value / max) * 100)}%` : 0 },} ))\n                  )\n                )\n              );\n            })\n          )\n        )\n      )\n    );\n  };\n\n  const similarRows = (related, current) => [\n    current,\n    ...((related && related.similar) || []).filter(item => item.pricing).map(item => ({ key: item.slug, name: item.name, href: `/models/${item.slug}`, ...item.pricing }))\n  ];\n\n  const TextPricing = ({ model, family, related }) => {\n    const p = model.pricing || {};\n    const ext = p.extended;\n    const above = value => (ext && value != null ? `${usd(value)} above ${tokens(ext.threshold)}` : null);\n    return (\n      __jsx(__Fragment, null\n        , __jsx(PriceCards, { items: [\n          { label: 'Input', value: usd(p.input), sub: above(ext && ext.input) },\n          p.cacheRead != null ? { label: 'Cached input', value: usd(p.cacheRead), sub: above(ext && ext.cacheRead) } : null,\n          p.cacheWrite != null ? { label: 'Cache write', value: usd(p.cacheWrite), sub: above(ext && ext.cacheWrite) } : null,\n          { label: 'Output', hint: model.text && model.text.reasoning ? 'incl. reasoning' : null, value: usd(p.output), sub: above(ext && ext.output) }\n        ],} )\n        , __jsx(SimilarPrices, {\n          columns: [{ label: 'Input', get: row => row.input }, { label: 'Output', get: row => row.output }],\n          bar: { label: 'Output price, relative', get: row => row.output },\n          rows: similarRows(related, { key: model.id, name: family.name, current: true, input: p.input, output: p.output }),} )\n        , p.cacheRead != null ? __jsx('p', { className: \"vx-footnote\",}, \"Prompt caching is automatic; see \"     , __jsx('a', { href: \"/guides/features/prompt-caching\",}, \"Prompt caching\" ), \".\") : null\n      )\n    );\n  };\n\n  const VideoCalculator = ({ model }) => {\n    const video = model.video || {};\n    const p = model.pricing || {};\n    const res = (video.resolutions || []).filter(r => r.height).sort((a, b) => a.height - b.height);\n    const durs = (video.durations || []).filter(d => d.seconds);\n    const [h, setH] = useState(() => ((res.find(r => r.height === 720) || res[0] || {}).height || 720));\n    const [s, setS] = useState(() => ((durs.find(d => d.seconds === 5) || durs[0] || {}).seconds || 5));\n    const [a, setA] = useState(video.audio === 'native' ? 'on' : 'off');\n    useEffect(() => {\n      setH((res.find(r => r.height === 720) || res[0] || {}).height || 720);\n      setS((durs.find(d => d.seconds === 5) || durs[0] || {}).seconds || 5);\n      setA(video.audio === 'native' ? 'on' : 'off');\n    }, [model.id]);\n    if (p.status === 'input-dependent') {\n      return __jsx('p', { className: \"vx-text\",}, \"This mode bills by the length of the source video you upload. Call \"             , __jsx('code', null, \"POST /video/quote\" ), \" with your \"   , __jsx('code', null, \"video_url\"), \" for an exact price before queueing.\"      );\n    }\n    if (p.status !== 'quoted') return __jsx('p', { className: \"vx-text\",}, \"Use \" , __jsx('code', null, \"POST /video/quote\" ), \" for this model's price.\"    );\n    const vp = videoPrice(model, { h, s, a });\n    // The selected clip's own rate, not the median across lengths.\n    const perSecond = vp.clip != null && vp.seconds ? vp.clip / vp.seconds : null;\n    const clipNote = `${vp.seconds}s${vp.resolution ? ` · ${vp.resolution}` : ''} · ${vp.audio ? 'with audio' : 'silent'}`;\n    const audioKey = video.audio === 'optional' ? a : (video.audio === 'native' ? 'on' : 'off');\n    const resKeys = res.length ? res : [{ value: '-', label: 'Default', height: 0 }];\n    const table = clipTable(model, resKeys, audioKey) || { rows: durs, rates: null, note: null };\n    // With per-second pricing the card shows the rate every length shares.\n    const shared = table.rates ? table.rates[Math.max(0, resKeys.findIndex(r => r.height === h))] : null;\n    const rate = shared != null ? shared : perSecond;\n    // Many evenly spaced lengths get a slider rather than a button each.\n    const lengths = durationSteps(durs);\n    const slider = durs.length > 6 && lengths.step ? lengths : null;\n    return (\n      __jsx('div', { className: \"vx-calc\",}\n        , __jsx('div', { className: \"vx-calc-controls\",}\n          , res.length > 1 ? (\n            __jsx('div', { className: \"vx-control\",}, __jsx('span', { className: \"vx-control-label\",}, \"Resolution\")\n              , __jsx(Segmented, { ariaLabel: \"Resolution\", value: h, onChange: setH, options: res.map(r => ({ value: r.height, label: r.label })),} )\n            )\n          ) : null\n          , durs.length > 1 ? (\n            __jsx('div', { className: \"vx-control\",}, __jsx('span', { className: \"vx-control-label\",}, \"Duration\")\n              , slider ? (\n                __jsx('span', { className: \"vx-range\",}\n                  , __jsx('input', { type: \"range\", min: slider.secs[0], max: slider.secs[slider.secs.length - 1], step: slider.step, value: s,\n                    'aria-label': \"Duration\", 'aria-valuetext': `${s} seconds`, onChange: e => setS(Number(e.target.value)),} )\n                  , __jsx('span', { className: \"vx-range-value\",}, s, \"s\")\n                )\n              ) : (\n                __jsx(Segmented, { ariaLabel: \"Duration\", value: s, onChange: setS, options: durs.map(d => ({ value: d.seconds, label: `${d.seconds}s` })),} )\n              )\n            )\n          ) : null\n          , video.audio === 'optional' ? (\n            __jsx('div', { className: \"vx-control\",}, __jsx('span', { className: \"vx-control-label\",}, \"Audio\")\n              , __jsx(Segmented, { ariaLabel: \"Audio\", value: a, onChange: setA, options: [{ value: 'off', label: 'Silent' }, { value: 'on', label: 'With audio' }],} )\n            )\n          ) : null\n        )\n        , __jsx('div', { className: \"vx-estimate\", 'aria-live': \"polite\",}\n          , __jsx(PriceCards, { items: [\n            { label: 'Per clip', value: usd(vp.clip), sub: clipNote },\n            { label: 'Per second', value: usd(rate), sub: shared != null ? 'any length' : 'this clip' },\n            // A minute only means something when one clip can run that long.\n            maxVideoSeconds(model) >= 60 ? { label: 'Per minute', value: usd(rate != null ? rate * 60 : null), sub: 'at this rate' } : null\n          ],} )\n        )\n        , __jsx('div', { className: \"vx-table-wrap vx-ruled\" ,}\n          , __jsx('table', { className: \"vx-dtable vx-matrix\" ,}\n            , __jsx('caption', { className: \"vx-sr-only\",}, \"Clip price by duration and resolution\"     , video.audio === 'optional' ? (a === 'on' ? ', with audio' : ', silent') : '')\n            , __jsx('thead', null\n              , __jsx('tr', null, __jsx('th', null, \"Duration\"), resKeys.map(r => __jsx('th', { key: r.value, className: \"vx-al-right\",}, r.label)))\n            )\n            , __jsx('tbody', null\n              , table.rates ? (\n                __jsx('tr', { className: \"vx-rate-row\",}\n                  , __jsx('th', { scope: \"row\",}, \"Per second\" )\n                  , table.rates.map((rate, i) => __jsx('td', { key: resKeys[i].value, className: \"vx-al-right\",}, usd(rate)))\n                )\n              ) : null\n              , table.rows.map(d => (\n                __jsx('tr', { key: d.value,}\n                  , __jsx('th', { scope: \"row\",}, d.seconds, \"s\")\n                  , resKeys.map(r => {\n                    const q = p.quotes && p.quotes[`${r.value}|${d.value}|${audioKey}`];\n                    const active = d.seconds === s && (r.height === h || !res.length);\n                    return (\n                      __jsx('td', { key: r.value, className: \"vx-al-right\",}\n                        , __jsx('button', { type: \"button\", className: cls('vx-cell-btn', active && 'is-active'), 'aria-pressed': active, disabled: q == null,\n                          onClick: () => { setS(d.seconds); if (r.height) setH(r.height); },}, usd(q))\n                      )\n                    );\n                  })\n                )\n              ))\n            )\n          )\n        )\n        , __jsx('p', { className: \"vx-footnote\",}, table.note ? `${table.note} ` : '', \"Quoted from \"  , __jsx('code', null, \"POST /video/quote\" ), p.quotedAt ? ` on ${fmtDate(Math.floor(new Date(p.quotedAt).getTime() / 1000))}` : '', \".\")\n      )\n    );\n  };\n\n  const ImagePricing = ({ model, models, family, related }) => {\n    const p = model.pricing || {};\n    const verb = model.task === 'image-edit' ? 'edit' : 'generation';\n    const editor = model.task === 'image-generation' ? models.find(m => m.task === 'image-edit') : null;\n    const editPrice = editor && editor.pricing ? (editor.pricing.edit != null ? editor.pricing.edit : editor.pricing.perImage) : null;\n    const upscale = p.upscale && model.task !== 'image-upscale' ? Object.entries(p.upscale).filter(([, v]) => v != null) : [];\n    const resolutions = p.byResolution ? Object.keys(p.byResolution) : [];\n    const top = resolutions[resolutions.length - 1];\n    const at = (row, res) => (row.byResolution ? row.byResolution[res] : res === resolutions[0] ? row.perImage : null);\n    const qualityRes = p.byQuality ? Object.keys(p.byQuality) : [];\n    const levels = ['low', 'medium', 'high'].filter(level => qualityRes.some(res => p.byQuality[res][level] != null));\n    return (\n      __jsx(__Fragment, null\n        , __jsx(PriceCards, { items: [\n          ...(p.byResolution\n            ? Object.entries(p.byResolution).map(([res, value]) => ({ label: `${res} ${verb}`, value: usd(value), sub: p.defaultQuality ? `${p.defaultQuality} quality` : null }))\n            : [{ label: model.task === 'image-edit' ? 'Edit' : model.task === 'image-upscale' ? '2x upscale' : 'Per image', value: usd(p.perImage) }]),\n          model.task === 'image-upscale' && p.upscale && p.upscale['4x'] != null ? { label: '4x upscale', value: usd(p.upscale['4x']) } : null,\n          editPrice != null ? { label: 'Edit', value: usd(editPrice) } : null,\n          p.extraInputImage != null ? { label: 'Extra input image', value: usd(p.extraInputImage) } : null\n        ],} )\n        , levels.length ? (\n          __jsx(__Fragment, null\n            , __jsx('div', { className: \"vx-table-wrap vx-ruled\" ,}\n              , __jsx('table', { className: \"vx-dtable\",}\n                , __jsx('caption', { className: \"vx-sr-only\",}, \"Price per \"  , verb === 'edit' ? 'edit' : 'image', \" by quality and resolution\"    )\n                , __jsx('thead', null\n                  , __jsx('tr', null, __jsx('th', null, \"Quality\"), qualityRes.map(res => __jsx('th', { key: res, className: \"vx-al-right\",}, res)))\n                )\n                , __jsx('tbody', null\n                  , levels.map(level => (\n                    __jsx('tr', { key: level,}\n                      , __jsx('th', { scope: \"row\",}, upperFirst(level), level === p.defaultQuality ? __jsx('span', { className: \"vx-muted\",}, \" · default\"  ) : null)\n                      , qualityRes.map(res => __jsx('td', { key: res, className: \"vx-al-right vx-num\" ,}, usd(p.byQuality[res][level])))\n                    )\n                  ))\n                )\n              )\n            )\n            , __jsx('p', { className: \"vx-footnote\",}\n              , model.task === 'image-edit'\n                ? __jsx(__Fragment, null, __jsx('code', null, \"POST /image/edit\" ), \" runs at the default quality. To pick another, send \"          , __jsx('code', null, \"quality\"), \" to \"  , __jsx('code', null, \"POST /image/multi-edit\" ), \".\")\n                : __jsx(__Fragment, null, \"Send \" , __jsx('code', null, \"quality\"), \" to pick a tier\"    , p.defaultQuality ? `; requests without it run at ${p.defaultQuality}` : '', \".\")\n            )\n          )\n        ) : null\n        , model.task === 'image-generation' && resolutions.length ? (\n          __jsx(SimilarPrices, {\n            columns: [...resolutions.map(res => ({ label: res, get: row => at(row, res) })), { label: 'Edit', get: row => row.edit }],\n            bar: { label: `${top}, relative`, get: row => at(row, top) },\n            rows: similarRows(related, { key: model.id, name: family.name, current: true, byResolution: p.byResolution, perImage: p.perImage, edit: editPrice }),} )\n        ) : null\n        , upscale.length ? __jsx('p', { className: \"vx-footnote\",}, \"Optional upscale: \"  , listText(upscale.map(([k, v]) => `${usd(v)} at ${k}`)), \".\") : null\n      )\n    );\n  };\n\n  const AudioPricing = ({ model }) => {\n    const p = model.pricing || {};\n    if (model.task === 'tts') {\n      return (\n        __jsx(__Fragment, null\n          , __jsx(PriceCards, { items: [\n            { label: '1M characters', hint: 'billed unit', value: usd(p.per1MChars) },\n            { label: '1 minute', value: usd(p.perMinute), sub: '≈ 825 characters' },\n            { label: '1 hour', value: usd(p.perHour) },\n            { label: '2,000-word article', value: usd(p.per1MChars != null ? p.per1MChars * 0.012 : null), sub: '≈ 12K characters' }\n          ],} )\n          , __jsx('p', { className: \"vx-footnote\",}, \"Minute and hour figures assume 150 words per minute.\"        )\n        )\n      );\n    }\n    if (model.task === 'stt') {\n      return (\n        __jsx(PriceCards, { items: [\n          { label: '1 second', hint: 'billed unit', value: usd(p.perSecond) },\n          { label: '1 minute', value: usd(p.perMinute) },\n          { label: '1 hour', value: usd(p.perHour) },\n          { label: '1,000 minutes', value: usd(p.perMinute != null ? p.perMinute * 1000 : null) }\n        ],} )\n      );\n    }\n    if (p.kind === 'tiered' && p.tiers) {\n      const fixed = model.audio && model.audio.durationOptions;\n      return (\n        __jsx(__Fragment, null\n          , __jsx('div', { className: \"vx-table-wrap vx-ruled\" ,}\n            , __jsx('table', { className: \"vx-dtable\",}\n              , __jsx('thead', null, __jsx('tr', null, __jsx('th', null, \"Track length\" ), __jsx('th', { className: \"vx-al-right\",}, \"Price\")))\n              , __jsx('tbody', null\n                , p.tiers.map(t => (\n                  __jsx('tr', { key: t.upTo,}, __jsx('td', null, tierLabel(t, fixed)), __jsx('td', { className: \"vx-al-right vx-num\" ,}, usd(t.usd)))\n                ))\n              )\n            )\n          )\n          , __jsx('p', { className: \"vx-footnote\",}, fixed\n            ? __jsx(__Fragment, null, \"Send one of these lengths as \"      , __jsx('code', null, \"duration_seconds\"), \".\")\n            : __jsx(__Fragment, null, \"Billed by duration bucket. Use \"     , __jsx('code', null, \"POST /audio/quote\" ), \" for an exact price.\"    ))\n        )\n      );\n    }\n    return __jsx('p', { className: \"vx-text\",}, p.kind === 'per-second' ? `${usd(p.perSecond)} per second of generated audio.` : p.kind === 'per-track' ? `${usd(p.perTrack)} per generated track.` : 'Use POST /audio/quote for this model.');\n  };\n\n  const AspectShape = ({ ratio, active }) => {\n    const [w, h] = String(ratio).split(':').map(Number);\n    if (!w || !h) return __jsx('span', { className: \"vx-ratio\",}, __jsx('span', null, ratio));\n    const scale = 22 / Math.max(w, h);\n    return (\n      __jsx('span', { className: cls('vx-ratio', active && 'is-active'), title: ratio,}\n        , __jsx('span', { className: \"vx-ratio-shape\", style: { width: Math.max(6, w * scale), height: Math.max(6, h * scale) },} )\n        , __jsx('span', null, ratio)\n      )\n    );\n  };\n\n  const VOICE_LIMIT = 48;\n\n  const Voices = ({ model }) => {\n    const [q, setQ] = useState('');\n    const [all, setAll] = useState(false);\n    const voices = (model.audio && model.audio.voices) || [];\n    const matches = voices.filter(v => v.toLowerCase().includes(q.toLowerCase()));\n    const shown = all || q ? matches : matches.slice(0, VOICE_LIMIT);\n    return (\n      __jsx('div', { className: \"vx-voices\",}\n        , __jsx('div', { className: \"vx-voices-head\",}\n          , __jsx('div', { className: \"vx-search vx-search-sm\" ,}\n            , __jsx(Icon, { name: \"search\", size: 14,} )\n            , __jsx('input', { type: \"search\", value: q, onChange: e => setQ(e.target.value), placeholder: `Filter ${voices.length} voices`, 'aria-label': \"Filter voices\" ,} )\n          )\n        )\n        , shown.length ? (\n          __jsx('div', { className: \"vx-voice-grid\",}\n            , shown.map(v => __jsx(VoiceChip, { key: v, voice: v,} ))\n          )\n        ) : __jsx('p', { className: \"vx-muted\",}, \"No voice matches “\"   , q, \"”.\")\n        , !q && matches.length > VOICE_LIMIT ? (\n          __jsx('button', { type: \"button\", className: \"vx-link-btn vx-more\" , onClick: () => setAll(!all),}, all ? 'Show fewer' : `Show all ${matches.length} voices`)\n        ) : null\n      )\n    );\n  };\n\n  const VoiceChip = ({ voice }) => {\n    const [done, setDone] = useState(false);\n    return (\n      __jsx('button', { type: \"button\", className: cls('vx-voice', done && 'is-done'), 'aria-label': `Copy voice ID ${voice}`,\n        onClick: () => copyText(voice).then(() => { setDone(true); setTimeout(() => setDone(false), 1200); }),}\n        , done ? __jsx(Icon, { name: \"check\", size: 12,} ) : null, voice\n      )\n    );\n  };\n\n  const MethodPath = ({ method, path }) => (\n    __jsx('span', { className: \"vx-endpoint\",}, __jsx('span', { className: \"vx-method\",}, method), __jsx('code', null, \"/api/v1\", path))\n  );\n\n  const Endpoints = ({ model, endpointId, setEndpointId }) => {\n    const list = model.endpoints || [];\n    const active = list.find(e => e.id === endpointId) || list[0];\n    if (!active) return null;\n    const isFlow = list.some(e => /-(retrieve|quote)$/.test(e.id));\n    return (\n      __jsx('div', { className: \"vx-endpoints\",}\n        , list.length > 1 ? (\n          __jsx(Segmented, { ariaLabel: \"Endpoint\", value: active.id, onChange: setEndpointId,\n            options: list.map(e => ({ value: e.id, label: e.name, disabled: e.supported === false, title: e.supported === false ? (e.note || 'Not supported for this model') : undefined })),} )\n        ) : null\n        , __jsx('div', { className: \"vx-endpoint-line\",}\n          , __jsx(MethodPath, { method: active.method, path: active.path,} )\n          , active.recommended && list.length > 1 && !isFlow ? __jsx(Tag, { tone: \"accent\",}, \"Recommended\") : null\n        )\n        , active.recommendation ? __jsx('p', { className: \"vx-endpoint-note\",}, active.recommendation) : null\n      )\n    );\n  };\n\n  const VariantsTable = ({ models, activeId, onSelect }) => {\n    const first = models[0];\n    const isText = first.modality === 'text' && first.task === 'chat';\n    const isVideo = first.modality === 'video';\n    const showEffort = isText && models.some(m => m.text && m.text.reasoning && m.text.reasoning.effort && m.text.reasoning.effort.length);\n    const showPrecision = isText && models.some(m => m.text && m.text.quantization);\n    return (\n      __jsx('div', { className: \"vx-table-wrap vx-ruled\" ,}\n        , __jsx('table', { className: \"vx-dtable vx-variants-table\" ,}\n          , __jsx('thead', null\n            , __jsx('tr', null\n              , __jsx('th', null, \"Variant\"), __jsx('th', null, \"Model ID\" ), __jsx('th', { className: \"vx-hide-sm\",}, \"Privacy\")\n              , isText ? __jsx(__Fragment, null, __jsx('th', { className: \"vx-al-right vx-hide-sm\" ,}, \"Context\"), __jsx('th', { className: \"vx-al-right vx-hide-md\" ,}, \"Max output\" ), __jsx('th', { className: \"vx-al-right\",}, \"Input / output\"  ), showEffort ? __jsx('th', { className: \"vx-hide-md\",}, \"Effort\") : null, showPrecision ? __jsx('th', { className: \"vx-hide-lg\",}, \"Precision\") : null) : null\n              , isVideo ? __jsx(__Fragment, null, __jsx('th', { className: \"vx-hide-md\",}, \"Inputs\"), __jsx('th', { className: \"vx-al-right\",}, \"From\"), __jsx('th', { className: \"vx-al-right vx-hide-sm\" ,}, \"Max\")) : null\n              , !isText && !isVideo ? __jsx('th', { className: \"vx-al-right\",}, \"Price\") : null\n              , __jsx('th', { className: \"vx-al-right vx-hide-md\" ,}, \"Added\")\n            )\n          )\n          , __jsx('tbody', null\n            , models.map(m => (\n              __jsx('tr', { key: m.id, className: cls(m.id === activeId && 'is-current', 'is-clickable'), onClick: () => onSelect(m.id),}\n                , __jsx('td', null, __jsx('button', { type: \"button\", className: \"vx-row-btn\", 'aria-pressed': m.id === activeId, onClick: e => { e.stopPropagation(); onSelect(m.id); },}, VARIANT_LABELS[m.variant] || m.variant))\n                , __jsx('td', null, __jsx('span', { className: \"vx-id vx-id-inline\" , title: m.id,}, __jsx('code', null, m.id), __jsx(CopyButton, { text: m.id, iconOnly: true, label: `Copy model ID ${m.id}`,} )))\n                , __jsx('td', { className: \"vx-hide-sm\",}, PRIVACY[m.privacy] ? PRIVACY[m.privacy].label : m.privacy)\n                , isText ? (\n                  __jsx(__Fragment, null\n                    , __jsx('td', { className: \"vx-al-right vx-num vx-hide-sm\"  ,}, tokens(m.text && m.text.context))\n                    , __jsx('td', { className: \"vx-al-right vx-num vx-hide-md\"  ,}, tokens(m.text && m.text.maxOutput))\n                    , __jsx('td', { className: \"vx-al-right vx-num\" ,}, usd(m.pricing && m.pricing.input), \" / \"  , usd(m.pricing && m.pricing.output))\n                    , showEffort ? __jsx('td', { className: \"vx-hide-md vx-wrap\" ,}, m.text && m.text.reasoning && m.text.reasoning.effort && m.text.reasoning.effort.length ? m.text.reasoning.effort.join(', ') : __jsx('span', { className: \"vx-muted\",}, \"—\")) : null\n                    , showPrecision ? __jsx('td', { className: \"vx-hide-lg\",}, m.text && m.text.quantization ? QUANT_LABELS[m.text.quantization] || m.text.quantization : __jsx('span', { className: \"vx-muted\",}, \"Not disclosed\" )) : null\n                  )\n                ) : null\n                , isVideo ? (\n                  __jsx(__Fragment, null\n                    , __jsx('td', { className: \"vx-hide-md\",}, videoInputList(m.video).map(upperFirst).join(', '))\n                    , __jsx('td', { className: \"vx-al-right vx-num\" ,}, m.pricing && m.pricing.status === 'quoted' ? `${usd(m.pricing.fromPerSecond)} / sec` : 'By source')\n                    , __jsx('td', { className: \"vx-al-right vx-num vx-hide-sm\"  ,}, heightLabel(maxVideoHeight(m)))\n                  )\n                ) : null\n                , !isText && !isVideo ? __jsx('td', { className: \"vx-al-right vx-num\" ,}, usd(m.headline && m.headline.value), \" \" , __jsx('span', { className: \"vx-muted\",}, headlineUnit(m))) : null\n                , __jsx('td', { className: \"vx-al-right vx-muted vx-hide-md\"  ,}, shortDate(m.created))\n              )\n            ))\n          )\n        )\n      )\n    );\n  };\n\n  // Text lists input and output, as the price comparison does, rather than\n  // the blended figure the catalog sorts by.\n  const relatedPrice = item => {\n    const p = item.pricing;\n    if (p && p.input != null && p.output != null) return __jsx(__Fragment, null, usd(p.input), \" / \"  , usd(p.output), __jsx('span', { className: \"vx-muted\",}, \" per 1M\"  ));\n    if (item.modality === 'video' && item.videoFrom != null) return __jsx(__Fragment, null, \"from \" , usd(item.videoFrom), __jsx('span', { className: \"vx-muted\",}, \" / sec\"  ));\n    if (item.headline && item.headline.value != null) return __jsx(__Fragment, null, usd(item.headline.value), __jsx('span', { className: \"vx-muted\",}, \" \" , item.headline.unit));\n    return null;\n  };\n\n  const RelatedList = ({ items, providers, title }) => {\n    if (!items || !items.length) return null;\n    return (\n      __jsx('div', { className: \"vx-related\",}\n        , __jsx('h3', { className: \"vx-related-head\",}, title)\n        , __jsx('ul', { className: \"vx-related-list\",}\n          , items.map(item => (\n            __jsx('li', { key: item.slug,}\n              , __jsx(ProviderLogo, { provider: providers[item.provider], size: 20,} )\n              , __jsx('span', { className: \"vx-related-name\",}\n                , __jsx('a', { href: `/models/${item.slug}`,}, item.name)\n                , __jsx('span', { className: \"vx-muted\",}, (providers[item.provider] && providers[item.provider].name) || item.provider)\n              )\n              , __jsx('span', { className: \"vx-related-price\",}, relatedPrice(item))\n            )\n          ))\n        )\n      )\n    );\n  };\n\n  const ModelPage = ({ data }) => {\n    const { family, models, related, providers } = data;\n    // ?v= names the variant. It's read on the first render, so the primary\n    // never flashes, and again on Back and Forward.\n    const variantFromUrl = () => {\n      const v = readParams().get('v');\n      return v && models.some(m => m.id === v) ? v : family.primary;\n    };\n    const [variantId, setVariantId] = useState(variantFromUrl);\n    const [endpointId, setEndpointId] = useState(null);\n    const now = useNow();\n\n    useEffect(() => {\n      const onPop = () => {\n        setVariantId(variantFromUrl());\n        setEndpointId(null);\n      };\n      window.addEventListener('popstate', onPop);\n      return () => window.removeEventListener('popstate', onPop);\n    }, []);\n\n    const model = models.find(m => m.id === variantId) || models[0];\n    const provider = providers[family.provider] || providers[model.provider];\n    const recommended = (model.endpoints || []).find(e => e.recommended) || (model.endpoints || [])[0];\n    const activeEndpoint = (model.endpoints || []).find(e => e.id === endpointId && e.supported !== false) || recommended;\n    const hasRelated = Boolean(related && ((related.versions || []).length || (related.similar || []).length));\n    const hasExamples = (model.modality === 'image' || model.modality === 'video') && models.some(m => (m.media || []).length);\n    const sections = pageSections(model, { variants: models.length, related: hasRelated, examples: hasExamples, capabilities: hasCapabilities(model, models) });\n    const activeSection = useScrollSpy(sections.map(([id]) => id));\n    const has = id => sections.some(([key]) => key === id);\n\n    const selectVariant = id => {\n      setVariantId(id);\n      setEndpointId(null);\n      writeParams({ v: id === family.primary ? null : id });\n    };\n\n    return (\n      __jsx('div', { className: \"vx vx-page not-prose\"  ,}\n        , __jsx('div', { className: \"vx-page-grid\",}\n          , __jsx('aside', { className: \"vx-side\",}\n            , __jsx('div', { className: \"vx-side-inner\",}\n              , __jsx('nav', { className: \"vx-crumbs\", 'aria-label': \"Breadcrumb\",}\n                , __jsx('a', { href: \"/models/overview\",}, \"Models\")\n                , __jsx('span', { 'aria-hidden': \"true\",}, \"/\")\n                , __jsx('span', { 'aria-current': \"page\",}, family.name)\n              )\n              , __jsx('nav', { className: \"vx-side-nav\", 'aria-label': \"On this page\"  ,}\n                , sections.map(([id, label]) => (\n                  __jsx('a', { key: id, href: `#${id}`, className: cls(activeSection === id && 'is-active'), 'aria-current': activeSection === id ? 'location' : undefined,}, label)\n                ))\n              )\n              , safeUrl(model.source) ? (\n                __jsx('div', { className: \"vx-side-actions\",}\n                  , __jsx('a', { className: \"vx-side-link\", href: safeUrl(model.source), target: \"_blank\", rel: \"noopener noreferrer\" ,}, \"Source\", __jsx(Icon, { name: \"external\", size: 12,} ))\n                )\n              ) : null\n            )\n          )\n\n          , __jsx('div', { className: \"vx-page-main\",}\n            , __jsx('header', { id: \"overview\", className: \"vx-model-head\",}\n              , __jsx('p', { className: \"vx-model-meta\",}\n                , [\n                  provider ? provider.name : family.provider,\n                  `Added ${fmtDate(model.created)}`,\n                  familyTask(family, models),\n                  PRIVACY[model.privacy] ? PRIVACY[model.privacy].label : null,\n                  model.openWeights ? 'Open weights' : null,\n                  model.license,\n                  model.uncensored ? 'Uncensored' : null,\n                  isNew(model.created, now) ? 'New' : null\n                ].filter(Boolean).map(item => __jsx('span', { key: item, className: \"vx-meta-item\",}, item))\n                , model.appDefault ? __jsx('span', { className: \"vx-meta-item\", title: \"The default model used by the Venice app.\"       ,}, \"App Default\" ) : null\n                , model.beta ? __jsx('span', { className: \"vx-meta-item is-warn\" , title: \"Experimental. May change or be removed without notice.\"       ,}, \"Beta\") : null\n                , model.deprecation ? __jsx('span', { className: \"vx-meta-item is-warn\" , title: \"Scheduled for removal. See Deprecations.\"    ,}, \"Deprecated\") : null\n              )\n              , __jsx('h1', { className: \"vx-h1\",}, family.name)\n              , model.description || family.description ? __jsx('p', { className: \"vx-description\",}, model.description || family.description) : null\n\n              , models.length > 1 ? (\n                __jsx('div', { className: \"vx-variants\", role: \"radiogroup\", 'aria-label': \"Variant\", onKeyDown: rovingKeys(models.map(m => m.id), model.id, selectVariant),}\n                  , models.map(m => (\n                    __jsx('button', { key: m.id, 'data-key': m.id, type: \"button\", role: \"radio\", 'aria-checked': m.id === model.id, tabIndex: m.id === model.id ? 0 : -1,\n                      className: cls('vx-variant', m.id === model.id && 'is-active'), onClick: () => selectVariant(m.id),}\n                      , __jsx('span', { className: \"vx-variant-name\",}, VARIANT_LABELS[m.variant] || m.variant)\n                      , __jsx('span', { className: \"vx-variant-sub\",}, [m.privacy !== m.variant && PRIVACY[m.privacy] ? PRIVACY[m.privacy].label : null, priceSummary(m)].filter(Boolean).join(' · '))\n                    )\n                  ))\n                )\n              ) : null\n\n              , __jsx('div', { className: \"vx-idbar\",}\n                , __jsx(ModelId, { id: model.id,} )\n                , recommended ? __jsx('span', { className: \"vx-endpoint-url\",}, recommended.method, \" \" , API, recommended.path) : null\n              )\n            )\n\n            , __jsx(KeyStats, { model: model, models: models,} )\n\n            , __jsx(Section, { id: \"pricing\", title: \"Pricing\", note: `USD ${priceUnit(model)}`,}\n              , model.modality === 'text' && model.task === 'chat' ? __jsx(TextPricing, { model: model, family: family, related: related,} ) : null\n              , model.modality === 'image' ? __jsx(ImagePricing, { model: model, models: models, family: family, related: related,} ) : null\n              , model.modality === 'video' ? __jsx(VideoCalculator, { model: model,} ) : null\n              , model.modality === 'audio' ? __jsx(AudioPricing, { model: model,} ) : null\n              , model.modality === 'embedding' ? (\n                __jsx(PriceCards, { items: [\n                  { label: 'Input', value: usd(model.pricing && model.pricing.input) },\n                  { label: '1M documents', value: usd(model.pricing && model.pricing.input != null ? model.pricing.input * 500 : null), sub: '500 tokens each, embedded once' }\n                ],} )\n              ) : null\n              , model.task === 'decision' ? (\n                __jsx(PriceCards, { items: [\n                  { label: 'State tokens in', value: usd(model.pricing && model.pricing.input) },\n                  { label: 'Decision tokens out', value: usd(model.pricing && model.pricing.output) }\n                ],} )\n              ) : null\n            )\n\n            , has('examples') ? (\n              __jsx(Section, { id: \"examples\", title: \"Examples\",}\n                , __jsx(MediaGallery, { model: model.media && model.media.length ? model : (models.find(m => m.media && m.media.length) || model),} )\n              )\n            ) : null\n\n            , has('voices') ? (\n              __jsx(Section, { id: \"voices\", title: \"Voices\",}\n                , __jsx(Voices, { model: model,} )\n              )\n            ) : null\n\n            , __jsx(Section, { id: \"specs\", title: \"Specs\",}\n              , __jsx(Specs, { model: model, models: models,} )\n            )\n\n            , has('capabilities') ? (\n              __jsx(Section, { id: \"capabilities\", title: \"Capabilities\",}\n                , __jsx(CapabilityMatrix, { model: model, models: models,} )\n              )\n            ) : null\n\n            , __jsx(Section, { id: \"api\", title: \"API\", actions: __jsx('a', { className: \"vx-link-btn\", href: \"https://venice.ai/settings/api\", target: \"_blank\", rel: \"noopener\",}, \"Get an API key\"   ),}\n              , __jsx(Endpoints, { model: model, endpointId: activeEndpoint && activeEndpoint.id, setEndpointId: setEndpointId,} )\n              , model.privacy === 'e2ee' ? __jsx('p', { className: \"vx-text\",}, \"E2EE requests encrypt the prompt on your device and send attestation headers; follow the \"              , __jsx('a', { href: \"/guides/features/tee-e2ee-models\",}, \"TEE and E2EE guide\"   ), \". The samples show the plain request shape.\"       ) : null\n              , __jsx(CodeTabs, { samples: codeSamples(model, activeEndpoint && activeEndpoint.id),} )\n            )\n\n            , has('privacy') ? (\n              __jsx(Section, { id: \"privacy\", title: \"Privacy\",}\n                , __jsx(PrivacyTiers, { model: model, models: models, provider: provider, onSelect: selectVariant,} )\n              )\n            ) : null\n\n            , has('variants') ? (\n              __jsx(Section, { id: \"variants\", title: \"Variants\",}\n                , __jsx(VariantsTable, { models: models, activeId: model.id, onSelect: selectVariant,} )\n              )\n            ) : null\n\n            , has('related') ? (\n              __jsx(Section, { id: \"related\", title: \"Related models\" ,}\n                , __jsx('div', { className: \"vx-related-grid\",}\n                  , __jsx(RelatedList, { title: \"Other versions\" , items: related.versions, providers: providers,} )\n                  , __jsx(RelatedList, { title: \"Similar price and capability\"   , items: related.similar, providers: providers,} )\n                )\n              )\n            ) : null\n          )\n        )\n      )\n    );\n  };\n\n  // scripts/build-model-catalog.js writes each page's static text with these,\n  // so it reads the same as the page.\n  const helpers = { usd, tokens, fmtDate, listText, heightLabel, VARIANT_LABELS, PRIVACY, videoPrice, DRAFT_CLIP, priceSummary, statusText, clipTable, durationText };\n\n  // Hands over a catalog that loaded before the explorer's first render, so\n  // the view opens on its results instead of a loading skeleton.\n  const setCatalog = catalog => { store.catalog = catalog; };\n\n  return { ModelExplorer, ModelPage, codeSamples, helpers, setCatalog };\n}"}