'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 `
VYNDR`;
}
/**
* 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 ``;
}
module.exports = { toSvg, BRAND, W, H };