// Generative cover art for /writing. Inline SVG so it inherits the theme CSS
// variables (adapts to dark/light automatically). One cohesive abstract visual
// language across all articles, in the vein AI labs use for their own writing:
// soft gradient fields + thin geometric line work, seeded per topic.
//
// Public:
// --- deterministic pseudo-random, seeded by slug (so a cover never reshuffles) ---
function artHash(s) {
let h = 2166136261;
for (let i = 0; i < (s || "").length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); }
return h >>> 0;
}
function artRng(seed) {
let a = seed >>> 0;
return function () {
a = (a + 0x6d2b79f5) | 0;
let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
// motif + tone per article. tone: cool=accent blue, warm=amber, mix=both.
const ART_MAP = {
"predictions-2026": { m: "orbit", t: "mix" },
"what-stays-human": { m: "bloom", t: "warm" },
"future-of-jobs": { m: "strata", t: "warm" },
"on-intelligence": { m: "bloom", t: "cool" },
"turing-test": { m: "interfere", t: "cool" },
"world-simulators": { m: "grid", t: "cool" },
"responsibility-of-dialogue": { m: "interfere", t: "warm" },
"incompleteness-of-ethics": { m: "loop", t: "cool" },
"backpropagation-intuition": { m: "field", t: "cool" },
"the-analog-spark": { m: "bloom", t: "mix" },
"how-to-implement-ai-in-your-company": { m: "lattice", t: "cool" },
"cum-implementez-ai-in-companie-ghid": { m: "lattice", t: "cool" },
"ai-automation-for-business-what-is-worth-automating": { m: "field", t: "cool" },
"automatizari-ai-pentru-companii-ce-merita-automatizat": { m: "field", t: "cool" },
"ai-development-romania": { m: "lattice", t: "warm" },
"dezvoltare-ai-romania": { m: "lattice", t: "warm" },
"what-is-a-machine-learning-solution": { m: "strata", t: "cool" },
"ce-este-o-solutie-machine-learning": { m: "strata", t: "cool" },
};
const ART_MOTIFS = ["bloom", "orbit", "grid", "interfere", "loop", "field", "lattice", "strata"];
const ART_TONES = ["cool", "warm", "mix"];
function artMeta(slug) {
if (ART_MAP[slug]) return ART_MAP[slug];
const h = artHash(slug || "x");
return { m: ART_MOTIFS[h % ART_MOTIFS.length], t: ART_TONES[(h >> 5) % ART_TONES.length] };
}
function artColors(tone) {
if (tone === "warm") return { a: "var(--warm)", b: "var(--warm)" };
if (tone === "mix") return { a: "var(--accent)", b: "var(--warm)" };
return { a: "var(--accent)", b: "var(--accent)" };
}
// ---- motif renderers: each returns { defs, body } for the 600x375 canvas ----
function motifBloom(id, rnd, col) {
const orbs = [];
const n = 3;
for (let i = 0; i < n; i++) {
orbs.push({
cx: 120 + rnd() * 360, cy: 80 + rnd() * 215,
r: 150 + rnd() * 130, fill: i === 1 ? "b" : "a",
o: 0.5 + rnd() * 0.35,
});
}
const dots = [];
for (let i = 0; i < 22; i++) dots.push({ x: rnd() * 600, y: rnd() * 375, r: 0.7 + rnd() * 1.1 });
return {
defs: (
),
body: (
{orbs.map((o, i) => (
))}
{dots.map((d, i) => )}
),
};
}
function motifOrbit(id, rnd, col) {
const cx = 470, cy = 300;
const rings = [60, 120, 185, 255, 330];
const fan = [];
for (let i = 0; i < 13; i++) {
const ang = (Math.PI * 1.05) + (i / 12) * (Math.PI * 0.95);
fan.push({ x: cx + Math.cos(ang) * 360, y: cy + Math.sin(ang) * 360 });
}
const nodes = rings.map((r, i) => {
const ang = Math.PI + rnd() * Math.PI * 0.9;
return { x: cx + Math.cos(ang) * r, y: cy + Math.sin(ang) * r, big: i % 2 === 0 };
});
return {
defs: (
),
body: (
{fan.map((p, i) => )}
{rings.map((r, i) => )}
{nodes.map((nd, i) => )}
),
};
}
function motifGrid(id, rnd, col) {
const hzY = 150, vx = 300;
const verts = [];
for (let i = -7; i <= 7; i++) {
verts.push({ x1: vx + i * 22, y1: hzY, x2: vx + i * 95, y2: 400 });
}
const horis = [];
for (let i = 1; i <= 9; i++) {
const t = i / 10;
horis.push(hzY + Math.pow(t, 1.9) * 250);
}
return {
defs: (
),
body: (
{verts.map((v, i) => )}
{horis.map((y, i) => )}
),
};
}
function motifInterfere(id, rnd, col) {
const a = { x: 195, y: 165 }, b = { x: 410, y: 215 };
const ringsA = [], ringsB = [];
for (let i = 1; i <= 9; i++) { ringsA.push(i * 26); ringsB.push(i * 26); }
return {
defs: null,
body: (
{ringsA.map((r, i) => )}
{ringsB.map((r, i) => )}
),
};
}
function motifLoop(id, rnd, col) {
const cx = 300, cy = 188, r = 110;
// an almost-complete ring with a gap (the unclosed argument)
const gap = 0.42; // radians of opening
const start = -Math.PI / 2 + gap / 2;
const end = -Math.PI / 2 - gap / 2 + Math.PI * 2;
const x1 = cx + Math.cos(start) * r, y1 = cy + Math.sin(start) * r;
const x2 = cx + Math.cos(end) * r, y2 = cy + Math.sin(end) * r;
const d = "M " + x1 + " " + y1 + " A " + r + " " + r + " 0 1 1 " + x2 + " " + y2;
return {
defs: (
),
body: (
),
};
}
function motifField(id, rnd, col) {
// streamlines flowing down toward a basin (gradient descent)
const basin = { x: 330, y: 330 };
const lines = [];
for (let i = 0; i < 16; i++) {
const sx = i * 40 - 20;
const sy = 30 + rnd() * 40;
const c1x = sx + 60, c1y = sy + 120;
const ex = basin.x + (rnd() - 0.5) * 70;
const ey = basin.y - 6 - rnd() * 14;
lines.push("M " + sx + " " + sy + " C " + c1x + " " + c1y + " " + (ex - 60) + " " + (ey - 40) + " " + ex + " " + ey);
}
return {
defs: (
),
body: (
{lines.map((d, i) => )}
),
};
}
function motifLattice(id, rnd, col) {
const nodes = [];
for (let i = 0; i < 11; i++) nodes.push({ x: 60 + rnd() * 480, y: 50 + rnd() * 275 });
const edges = [];
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const dx = nodes[i].x - nodes[j].x, dy = nodes[i].y - nodes[j].y;
if (Math.sqrt(dx * dx + dy * dy) < 175) edges.push([i, j]);
}
}
return {
defs: (
),
body: (
{edges.map((e, i) => (
))}
{nodes.map((nd, i) => (
))}
),
};
}
function motifStrata(id, rnd, col) {
// stacked topographic contour bands
const bands = [];
const count = 6;
for (let b = 0; b < count; b++) {
const baseY = 70 + b * 48;
const amp = 16 + rnd() * 16;
const phase = rnd() * 6.28;
let d = "M 0 " + (baseY + Math.sin(phase) * amp);
for (let x = 60; x <= 600; x += 60) {
d += " Q " + (x - 30) + " " + (baseY + Math.sin(phase + x / 70) * amp) + " " + x + " " + (baseY + Math.sin(phase + x / 55) * amp);
}
bands.push({ d, b });
}
return {
defs: (
),
body: (
{bands.map((bd, i) => )}
),
};
}
const MOTIF_FN = {
bloom: motifBloom, orbit: motifOrbit, grid: motifGrid, interfere: motifInterfere,
loop: motifLoop, field: motifField, lattice: motifLattice, strata: motifStrata,
};
function CoverArt({ slug, variant }) {
const meta = artMeta(slug);
const col = artColors(meta.t);
const id = "ca-" + (slug || "x").replace(/[^a-z0-9]/gi, "").slice(0, 24);
const rnd = artRng(artHash(slug || "x"));
const fn = MOTIF_FN[meta.m] || motifBloom;
const { defs, body } = fn(id, rnd, col);
return (
);
}
Object.assign(window, { CoverArt });