'use strict'; /** * cardRenderer — one card engine, per-template layouts. * * SVG rather than canvas: it is text, so it has no native dependency, it * diffs in review, and the numbers inside it are greppable — which matters when * the whole claim is that the numbers are real. A card whose contents cannot be * inspected without opening an image is a bad fit for a Truth-Law product. * * The card never formats its own facts. Every string arrives already rendered * and already gate-checked by contentEngine, so a caption and a card physically * cannot disagree. */ const BRAND = Object.freeze({ bg: '#05070A', panel: '#0A0E14', line: '#1A222E', green: '#00D4A0', // the R white: '#FFFFFF', // VYND dim: '#6B7A8D', amber: '#FFB347', mono: "ui-monospace, 'JetBrains Mono', 'SFMono-Regular', Menlo, monospace", }); const W = 1080; const H = 1350; const esc = (s) => String(s) .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"'); /** The slashed-Y wordmark: VYND white, R green. */ function wordmark(x, y, size = 34) { return ` VYND R `; } /** * Render a card model to SVG. * @param {object} card { kind, title, subtitle, lines[], footer } */ function toSvg(card = {}) { const lines = card.lines || []; let y = 300; const body = lines.map((l) => { const text = typeof l === 'string' ? l : l.text; const style = (typeof l === 'object' && l.style) || 'body'; let out = ''; if (style === 'rule') { out = ``; y += 26; return out; } const size = style === 'lead' ? 46 : style === 'stat' ? 40 : 30; const fill = style === 'stat' ? BRAND.green : style === 'dim' ? BRAND.dim : BRAND.white; const weight = style === 'body' ? 500 : 800; out = `${esc(text)}`; y += size + (style === 'lead' ? 26 : 18); return out; }).join('\n'); return ` ${Array.from({ length: 26 }, (_, i) => ``).join('')} ${wordmark(72, 128)} ${card.title ? `${esc(card.title)}` : ''} ${card.subtitle ? `${esc(card.subtitle)}` : ''} ${body} ${esc(card.footer || 'Every number here is measured. Nothing is projected.')} vyndr · built by Kevon Butler · Detroit `; } module.exports = { toSvg, BRAND, W, H };