// Mosaic Mobile — moodline + two card directions // Depends on globals from site-data.jsx (SENT_COLORS, NEUTRAL_PALETTE, FAVS) // and site-shared.jsx (SCollage, tickLabel). // Exports: Moodline, MiniMood, FramesChip, MFavRow, Banner, CardReader, CardPulse, // dominantColor, moodSegments function moodSegments(story, mode) { if (mode === "neutral") { const total = story.framings.reduce((s, f) => s + f.n, 0); return story.framings.map((f, i) => ({ c: NEUTRAL_PALETTE[i % NEUTRAL_PALETTE.length], pct: (f.n / total) * 100 })); } const sums = { neg: 0, mix: 0, pos: 0 }; story.framings.forEach((f) => { sums[f.lean] += f.n; }); const total = sums.neg + sums.mix + sums.pos; return [["neg", SENT_COLORS.neg], ["mix", SENT_COLORS.mix], ["pos", SENT_COLORS.pos]] .filter(([k]) => sums[k] > 0) .map(([k, c]) => ({ c, pct: (sums[k] / total) * 100 })); } function dominantColor(story, mode) { if (mode === "neutral") return NEUTRAL_PALETTE[0]; const sums = { neg: 0, mix: 0, pos: 0 }; story.framings.forEach((f) => { sums[f.lean] += f.n; }); const top = Object.entries(sums).sort((a, b) => b[1] - a[1])[0][0]; return SENT_COLORS[top]; } const Moodline = ({ story, mode, round = true }) => ( {moodSegments(story, mode).map((s, i) => )} ); const MiniMood = ({ story, mode, width = 28 }) => ( {moodSegments(story, mode).map((s, i) => )} ); const FramesChip = ({ story, mode, onOpen }) => ( ); const MFavRow = ({ ids, size = 18, max = 4 }) => { const all = [...new Set(ids)]; const extra = all.length - Math.min(all.length, max); return ( {all.slice(0, max).map((id, i) => ( ))} {extra > 0 && +{extra} more} ); }; // Use the real generated hero (story.hero, set by to_ui when assets/heroes/{id}.png exists) // when present; else the procedural SArt collage. Mirrors the desktop Art helper (site-feed.jsx) // so mobile no longer depends on the hardcoded HERO_MAP in SArt. const Banner = ({ story }) => ( {story.hero ? : } ); // ---------- Direction A: READER (airy, boxed, editorial) ---------- const CardReader = ({ story, mode, onOpen }) => (
{story.cat} · {story.upd}

{story.title}

{story.standfirst}

f.srcs)} size={19} max={4} ring="var(--bg-surface)" />
); // ---------- Direction B: PULSE (dense, edge-to-edge, data-forward) ---------- const CardPulse = ({ story, mode, onOpen }) => (
{story.cat} · {story.upd}

{story.title}

{tickLabel(story, mode)}

{story.standfirst}

f.srcs)} size={18} max={4} ring="var(--bg-primary)" />
); Object.assign(window, { Moodline, MiniMood, FramesChip, MFavRow, Banner, CardReader, CardPulse, dominantColor, moodSegments });