Component library
Back to catalog

Evidence mosaic

Mixed proof in one hairline-ruled mosaic: an attributed result figure leads, and a quote, unnumbered results, the business's own facts (each with its basis) and case-study links keep their own shapes and tints, laid out from the mix. Every figure names its client or project; fewer than two items renders nothing. No icons, photos or cards.

metricsresultsevidencecase studiesmosaictypographiclight

Open preview in a new tab

Use this component

Three ways to get Evidence mosaic into one of your own projects. The first two carry the component's files with them, so nothing has to fetch this page.

Start here

Placement prompt

The whole thing: what to do, where it goes, what must not change, and every one of the component's 1 file appended underneath with the path each belongs at. Paste it into Claude Code inside the destination project — it fetches nothing and needs nothing else.

Read it first — the files are added underneath when you copy
Add Evidence mosaic from my Astro Component Library to this Astro project.

Library page, for reference only:
https://astro.baysixmedia.com/components/evidence-mosaic/

Place it in:
[ENTER PAGE, FILE, OR ROUTE]

Position:
[ENTER WHERE IT SHOULD APPEAR]

Every file you need is appended below, each fenced with the path it belongs at. Write them there, relative to the project root. Do not import anything from the deployed library at runtime, and do not fetch the library page — the files below are the source of truth.

Adapt its demo content, links, colors, typography, and images to this project through documented props where possible. Preserve its responsive behavior, accessibility, interaction logic, reduced-motion behavior, and support for multiple instances. Avoid changing unrelated project code.

Test it at desktop, tablet, and mobile widths. Run the project's available check and build commands. Report the files changed and any differences from the library version. Do not commit or deploy.

Install kit

The 1 file this component needs and nothing else, each fenced with the path it belongs at, followed by what the library's own verification covered and what it did not. No instructions attached — for placing it yourself, or handing it to something that has its own.

Read it first
Install kit: "Evidence mosaic"
Library page: https://astro.baysixmedia.com/components/evidence-mosaic/
Files: 1 required, 1 supporting

Write each block below to the path named in its header, relative to the project root. A block marked (required) is one the component does not work without; (supporting) is documentation. Nothing here needs downloading except a block that says so in as many words.

===== FILE (required): src/components/EvidenceMosaic.astro =====
---
/**
 * EvidenceMosaic (candidate, Step 36)
 *
 * Mixed proof in one bordered composition. The tiles are different kinds of
 * evidence, each with its own shape, and the grid follows the mix rather than
 * a fixed card size:
 *   - result     a figure (number set large, its unit or currency a step
 *                smaller) + one line on what it means + the named client or
 *                project it belongs to (optionally linked);
 *   - narrative  a result given without a number: the sentence, attributed;
 *   - quote      one short attributed quote (name, role, company);
 *   - fact       one of the business's own figures or facts, with its basis;
 *   - link       a case-study / evidence link, the whole tile one target.
 * The first result with a figure leads (the largest tile). The frame is one
 * hairline-ruled field: tiles share their borders, tinted by kind, no cards,
 * shadows, icons or photography.
 *
 * Truth: nothing renders that was not supplied. No default figure,
 * percentage, name, quote, year, client or claim. A result renders only with
 * its label AND its attribution (no anonymous figures); a quote only with its
 * words AND a name; a link only with a label and a safe href. Values are
 * shown exactly as supplied — the unit is set smaller for display, never
 * reworded. Fewer than 2 renderable evidence items → the section renders
 * nothing at all (a planner must not select it with less). At most 7 tiles;
 * one quote; two links.
 *
 * Two tones: the lead is the one solid accent field (its client named first,
 * the figure fitted to the tile); every other tile is one light panel and
 * its kind is carried by type alone — heavy display numerals for figures,
 * semibold display headlines for results given in words, lighter display
 * type inside quotation marks for the quote, accent text and a ringed arrow
 * for links. One provenance system: every source line (the header note, a
 * result's client, a quote's speaker, a fact's basis) sits under a hairline
 * at 15px in full ink. Hyphenated names and compounds ("Co-op", "60-panel")
 * are never split at the hyphen.
 *
 * Responsive (container queries on the section's own width), reading order
 * kept at every width (lead, quote, results and narratives, facts, links):
 * below 600px a composed column — results as ledger lines (a short figure
 * beside its meaning, client under both), consecutive facts paired two-up,
 * links as compact rows; never a hidden swipe row. 600–959px: the lead a
 * full-width banner (figure beside its meaning), then every other tile a
 * content-sized ledger row on one shared two-column grid (key — figure, value
 * or speaker — left; the evidence and its source right), consecutive facts
 * paired; no tile is stretched beside a taller neighbour. From 960px a
 * 12-column mosaic from the mix: the lead opens row one at 7/12, the tile
 * beside it settles to the row's foot; the rest follow in rows of three or
 * two (two-tile rows alternate their seam 7/5, 5/7). No tile spans two rows
 * and none is left alone in a closing row; a fact in a wide slot sets its
 * label beside its figure. Figure sizes follow each tile's own width (cqi).
 *
 * Hooks (read, never declared): --evm-bg, --evm-text, --evm-text-muted,
 * --evm-brand, --evm-brand-text, --evm-border, --evm-panel-bg, --evm-family,
 * --evm-display, --evm-grid-max, --evm-grid-gutter, --evm-heading-size.
 * No script; the only motion is the link tile's arrow on hover, off under
 * reduced motion.
 */

export interface EvidenceMosaicResult {
	/** The figure exactly as supplied ("38%", "$1.2M", "11 days"). Without one, the tile is a narrative. */
	value?: string;
	/** What the figure means, or the result sentence when there is no figure. */
	label: string;
	/** The named client or project the result belongs to. Required: no anonymous results. */
	attribution: string;
	/** Where the result can be read or checked; links the attribution. */
	href?: string;
}

export interface EvidenceMosaicQuote {
	quote: string;
	name: string;
	role?: string;
	company?: string;
}

export interface EvidenceMosaicFact {
	/** The figure or date exactly as supplied ("2011", "412"). Optional. */
	value?: string;
	/** What it is ("Serving the county since", "Installs completed in 2025"). */
	label: string;
	/** Where it comes from ("Company job records"). */
	basis?: string;
}

export interface EvidenceMosaicLink {
	label: string;
	href: string;
	/** One short line on what the linked page shows. */
	note?: string;
}

export interface EvidenceMosaicProps {
	heading: string;
	headingLevel?: "h2" | "h3";
	eyebrow?: string;
	/** One or two sentences beside the heading. */
	intro?: string;
	/** Provenance note under the intro — where the evidence came from. */
	note?: string;
	results?: readonly EvidenceMosaicResult[];
	quote?: EvidenceMosaicQuote | null;
	facts?: readonly EvidenceMosaicFact[];
	links?: readonly EvidenceMosaicLink[];
	fontStylesheet?: string;
	class?: string;
	id?: string;
}

interface Props extends EvidenceMosaicProps {}

const {
	heading,
	headingLevel = "h2",
	eyebrow = "",
	intro = "",
	note = "",
	results = [],
	quote = null,
	facts = [],
	links = [],
	fontStylesheet = "https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600&family=Schibsted+Grotesk:wght@500;600;700&display=swap",
	class: className,
	id,
} = Astro.props;

const MAX_TILES = 7;
const MAX_LINKS = 2;

const text = (v: unknown) => (typeof v === "string" ? v.trim() : "");
const safeHref = (v: unknown) => {
	const href = text(v);
	if (!href) return "";
	if (/^(https?:|mailto:|tel:)/i.test(href)) return /\s/.test(href) ? "" : href;
	if (/^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith("//")) return "";
	return href.startsWith("/") || href.startsWith("#") || href.startsWith("./") || href.startsWith("../") ? href : "";
};
const external = (href: string) => /^https?:/i.test(href);

/*
 * Display split only: a short non-digit prefix ("$", "£", "+") and a short
 * word/unit suffix ("%", "M", " days") are set smaller. The characters are
 * never changed or reordered; anything that does not fit this shape is set
 * whole.
 */
const splitFigure = (value: string) => {
	const m = value.match(/^([^\d\s]{0,2})(\d[\d.,:/]*)(.*)$/);
	if (!m) return { pre: "", num: value, post: "" };
	const [, pre, num, post] = m;
	if (post.length > 8 || /\d/.test(post)) return { pre: "", num: value, post: "" };
	return { pre, num, post };
};

type Kind = "lead" | "result" | "narrative" | "quote" | "fact" | "link";
interface Tile {
	kind: Kind;
	value: string;
	label: string;
	by: string;
	href: string;
	role: string;
	basis: string;
	fig: { pre: string; num: string; post: string };
}
const blank = { value: "", label: "", by: "", href: "", role: "", basis: "", fig: { pre: "", num: "", post: "" } };

const resultTiles: Tile[] = (results ?? [])
	.filter((r) => r && text(r.label) && text(r.attribution))
	.map((r) => {
		const value = text(r.value);
		return { ...blank, kind: value ? "result" : "narrative", value, label: text(r.label), by: text(r.attribution), href: safeHref(r.href), fig: value ? splitFigure(value) : blank.fig };
	});
const leadIndex = resultTiles.findIndex((t) => t.kind === "result");
const lead = leadIndex >= 0 ? [{ ...resultTiles[leadIndex], kind: "lead" as Kind }] : [];
const restResults = resultTiles.filter((_, i) => i !== leadIndex);

const quoteTile: Tile[] =
	quote && text(quote.quote) && text(quote.name)
		? [{ ...blank, kind: "quote", label: text(quote.quote), by: text(quote.name), role: [text(quote.role), text(quote.company)].filter(Boolean).join(", ") }]
		: [];

const factTiles: Tile[] = (facts ?? [])
	.filter((f) => f && text(f.label))
	.map((f) => ({ ...blank, kind: "fact", value: text(f.value), label: text(f.label), basis: text(f.basis) }));

const linkTiles: Tile[] = (links ?? [])
	.filter((l) => l && text(l.label) && safeHref(l.href))
	.slice(0, MAX_LINKS)
	.map((l) => ({ ...blank, kind: "link", label: text(l.label), href: safeHref(l.href), basis: text(l.note) }));

/* Reading order: lead, quote, the other results and narratives as supplied, facts, links. */
const tiles: Tile[] = [...lead, ...quoteTile, ...restResults, ...factTiles, ...linkTiles].slice(0, MAX_TILES);
const count = tiles.length;

/* ---------- desktop placement (12 columns), computed from the mix ---------- */
const WEIGHT: Record<Kind, number> = { lead: 3, quote: 3, narrative: 2.4, result: 2, fact: 2, link: 1.6 };
interface Place { c: number; s: number; r: number; rs: number }
const place: Place[] = new Array(count);

/* Split `width` columns among row items by weight, every tile at least 3 columns. */
const fillRow = (indices: number[], start: number, width: number, row: number) => {
	const total = indices.reduce((sum, i) => sum + WEIGHT[tiles[i].kind], 0);
	let spans = indices.map((i) => Math.max(3, Math.round((width * WEIGHT[tiles[i].kind]) / total)));
	let diff = width - spans.reduce((a, b) => a + b, 0);
	/* Settle rounding on the heaviest (or lightest) tiles. */
	const order = indices.map((_, k) => k).sort((a, b) => WEIGHT[tiles[indices[b]].kind] - WEIGHT[tiles[indices[a]].kind]);
	for (let k = 0; diff !== 0 && k < 24; k++) {
		const j = diff > 0 ? order[k % order.length] : order[order.length - 1 - (k % order.length)];
		if (diff < 0 && spans[j] <= 3) continue;
		spans[j] += diff > 0 ? 1 : -1;
		diff += diff > 0 ? -1 : 1;
	}
	let c = start;
	indices.forEach((i, k) => {
		place[i] = { c, s: spans[k], r: row, rs: 1 };
		c += spans[k];
	});
};

const hasLead = lead.length > 0;
/* A row of two: offset the seam row by row (7/5, then 5/7) so the field reads as a mosaic, not a table. */
const offsetSeam = (idx: number[], row: number, firstWide: boolean) => {
	if (idx.length !== 2) return;
	const first = firstWide ? 7 : 5;
	place[idx[0]] = { ...place[idx[0]], s: first };
	place[idx[1]] = { ...place[idx[1]], c: 1 + first, s: 12 - first };
};
const fillRows = (sizes: number[], from: number, firstRow: number) => {
	let i = from;
	sizes.forEach((size, k) => {
		const idx = Array.from({ length: size }, (_, j) => i + j);
		fillRow(idx, 1, 12, firstRow + k);
		offsetSeam(idx, firstRow + k, (firstRow + k) % 2 === 1);
		i += size;
	});
};
if (hasLead) {
	/*
	 * The lead opens the first row beside the next tile (the quote, when there is one), both
	 * sized by their content — no tile is stretched over two rows, so none carries a dead band.
	 * The rest follow in rows of two or three; a tile is never left alone in a closing row.
	 */
	if (count === 3) {
		place[0] = { c: 1, s: 6, r: 1, rs: 1 };
		fillRow([1, 2], 7, 6, 1);
	} else {
		place[0] = { c: 1, s: 7, r: 1, rs: 1 };
		place[1] = { c: 8, s: 5, r: 1, rs: 1 };
		const rest: Record<number, number[]> = { 2: [], 4: [2], 5: [3], 6: [2, 2], 7: [3, 2] };
		fillRows(rest[count] ?? [], 2, 2);
	}
} else if (count === 2) {
	fillRow([0, 1], 1, 12, 1);
} else {
	const sizes: Record<number, number[]> = { 3: [3], 4: [2, 2], 5: [2, 3], 6: [3, 3], 7: [3, 4] };
	fillRows(sizes[count] ?? [count], 0, 1);
}

/* ---------- phone placement (2 columns): consecutive facts pair up; everything else spans ---------- */
const phoneSpan: number[] = tiles.map(() => 2);
for (let i = 0; i < count - 1; i++) {
	if (tiles[i].kind === "fact" && tiles[i + 1].kind === "fact" && phoneSpan[i] === 2) {
		phoneSpan[i] = 1;
		phoneSpan[i + 1] = 1;
		i++;
	}
}

/* ---------- tablet placement (2 columns): the lead and every other tile span as ledger rows; consecutive facts pair ---------- */
const tabletSpan = phoneSpan;

const styleOf = (i: number) => {
	const p = place[i] ?? { c: 1, s: 12, r: i + 1, rs: 1 };
	return `--evm-c:${p.c};--evm-s:${p.s};--evm-r:${p.r};--evm-rs:${p.rs};--evm-ts:${tabletSpan[i]};--evm-ms:${phoneSpan[i]}`;
};
/* Hyphenated compounds and names ("Co-op", "60-panel") never break at the hyphen. Display only. */
const parts = (value: string) =>
	value
		.split(/(\S*[^\s-]-[^\s-]\S*)/)
		.filter(Boolean)
		.map((t) => ({ t, keep: /[^\s-]-[^\s-]/.test(t) }));

/* Fit the lead figure to its tile: cqi per em-width estimate of the supplied value (display only). */
const fitOf = (t: Tile) => {
	const em = t.fig.num.length * 0.58 + (t.fig.pre.length + t.fig.post.length) * 0.3 + 0.1;
	return Math.max(14, Math.min(58, Math.round(100 / em)));
};
const longFig = (t: Tile) => t.fig.num.length + t.fig.post.length > 7;
const bandOf = (i: number) => (place[i]?.s ?? 0) >= 9 && tiles[i].kind !== "lead";
const ledgerOf = (i: number) => tabletSpan[i] === 2 && tiles[i].kind !== "lead" && tiles[i].kind !== "link";

const Heading = headingLevel;
---

{count >= 2 && (
<>
{fontStylesheet && <link rel="stylesheet" href={fontStylesheet} />}
<section class:list={["evm", className]} id={id} data-count={count}>
	<div class="evm-inner">
		<header class="evm-head">
			<div class="evm-head-main">
				{text(eyebrow) && <p class="evm-eyebrow">{text(eyebrow)}</p>}
				{text(heading) && <Heading class="evm-title">{text(heading)}</Heading>}
			</div>
			{(text(intro) || text(note)) && (
				<div class="evm-head-aside">
					{text(intro) && <p class="evm-intro">{text(intro)}</p>}
					{text(note) && <p class="evm-note">{text(note)}</p>}
				</div>
			)}
		</header>

		<ul class="evm-grid" role="list">
			{tiles.map((t, i) => (
				<li class:list={["evm-tile", `evm-tile--${t.kind}`, { "evm-tile--band": bandOf(i), "evm-tile--ledger": ledgerOf(i), "evm-tile--companion": hasLead && i === 1, "evm-tile--longfig": t.kind === "result" && longFig(t), "evm-tile--widefact": t.kind === "fact" && !bandOf(i) && (place[i]?.s ?? 0) >= 7 }]} style={styleOf(i)}>
					{t.kind === "lead" && (
						<>
							<p class="evm-by evm-by--head">
								{t.href ? (
									<a class="evm-by-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
										{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}
										{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
									</a>
								) : (
									t.by
								)}
							</p>
							<div class="evm-main">
								<p class="evm-fig" style={`--evm-fit:${fitOf(t)}`}>{t.fig.pre && <span class="evm-fig-pre">{t.fig.pre}</span>}<span class="evm-fig-num">{t.fig.num}</span>{t.fig.post && <span class="evm-fig-post">{t.fig.post}</span>}</p>
								<p class="evm-meaning">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</div>
						</>
					)}

					{t.kind === "result" && (
						<>
							<div class="evm-main">
								<p class="evm-fig">{t.fig.pre && <span class="evm-fig-pre">{t.fig.pre}</span>}<span class="evm-fig-num">{t.fig.num}</span>{t.fig.post && <span class="evm-fig-post">{t.fig.post}</span>}</p>
								<p class="evm-meaning">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</div>
							<p class="evm-by">
								{t.href ? (
									<a class="evm-by-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
										{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}
										{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
									</a>
								) : (
									t.by
								)}
							</p>
						</>
					)}

					{t.kind === "narrative" && (
						<>
							<p class="evm-story">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							<p class="evm-by">
								{t.href ? (
									<a class="evm-by-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
										{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}
										{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
									</a>
								) : (
									t.by
								)}
							</p>
						</>
					)}

					{t.kind === "quote" && (
						<figure class="evm-quote">
							<blockquote class="evm-quote-text">
								<p>{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</blockquote>
							<figcaption class="evm-quote-by">
								<span class="evm-quote-name">{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>
								{t.role && <span class="evm-quote-role">{parts(t.role).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>}
							</figcaption>
						</figure>
					)}

					{t.kind === "fact" && (
						<>
							<div class="evm-main">
								{t.value && <p class="evm-fact-value">{t.value}</p>}
								<p class:list={["evm-fact-label", { "evm-fact-label--solo": !t.value }]}>{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</div>
							{t.basis && <p class="evm-basis">{parts(t.basis).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>}
						</>
					)}

					{t.kind === "link" && (
						<a class="evm-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
							<span class="evm-main">
								<span class="evm-link-label">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>
								{t.basis && <span class="evm-link-note">{parts(t.basis).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>}
							</span>
							<span class="evm-link-arrow" aria-hidden="true">
								<svg viewBox="0 0 24 24" width="22" height="22" focusable="false"><path d="M5 12h13M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="square" /></svg>
							</span>
							{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
						</a>
					)}
				</li>
			))}
		</ul>
	</div>
</section>
</>
)}

<style>
	.evm {
		--evm-surface: var(--evm-bg, #e8ebe6);
		--evm-ink: var(--evm-text, #15191c);
		--evm-soft: var(--evm-text-muted, #3f464c);
		--evm-accent: var(--evm-brand, #1f4a63);
		--evm-accent-ink: var(--evm-brand-text, #ffffff);
		--evm-line: var(--evm-border, #bcc3bb);
		--evm-panel: var(--evm-panel-bg, #fbfbf8);
		--evm-font: var(--evm-family, "Instrument Sans", ui-sans-serif, system-ui, sans-serif);
		--evm-font-display: var(--evm-display, "Schibsted Grotesk", ui-sans-serif, system-ui, sans-serif);

		container-type: inline-size;
		container-name: evm;
		background: var(--evm-surface);
		color: var(--evm-ink);
		font-family: var(--evm-font);
		padding-block: clamp(64px, 9vw, 120px);
		hyphens: manual;
	}

	.evm *,
	.evm *::before,
	.evm *::after {
		box-sizing: border-box;
	}

	/* A hyphenated compound or name is never split at its hyphen. */
	.evm-keep {
		white-space: nowrap;
	}

	.evm-inner {
		max-width: var(--evm-grid-max, 1232px);
		margin-inline: auto;
		padding-inline: 20px;
	}

	/* ---------- header ---------- */
	.evm-head {
		display: grid;
		gap: 18px;
		margin-bottom: clamp(32px, 5vw, 56px);
	}
	.evm-eyebrow {
		margin: 0 0 14px;
		font-size: 15px;
		font-weight: 500;
		line-height: 1.4;
		color: var(--evm-accent);
	}
	.evm-title {
		margin: 0;
		max-width: 18ch;
		font-family: var(--evm-font-display);
		font-size: var(--evm-heading-size, clamp(32px, 4vw, 48px));
		font-weight: 600;
		line-height: 1.06;
		letter-spacing: -0.028em;
		text-wrap: balance;
		overflow-wrap: anywhere;
	}
	.evm-intro {
		margin: 0;
		max-width: 46ch;
		font-size: 17px;
		line-height: 1.55;
		color: var(--evm-soft);
		overflow-wrap: anywhere;
	}

	/*
	 * One provenance system: every source line — the header note, each
	 * result's client, each quote's speaker, each fact's basis — is set the
	 * same way: a hairline above, 15px, full ink. Where evidence comes from is
	 * read as part of the evidence, not as fine print.
	 */
	.evm-note,
	.evm-by,
	.evm-quote-by,
	.evm-basis {
		margin: 0;
		padding-top: 12px;
		border-top: 1px solid var(--evm-line);
		font-size: 15px;
		line-height: 1.45;
		color: var(--evm-ink);
		overflow-wrap: anywhere;
	}
	.evm-note {
		margin-top: 16px;
		max-width: 46ch;
		font-weight: 500;
	}
	.evm-by,
	.evm-quote-name {
		font-weight: 600;
	}
	.evm-basis {
		font-weight: 500;
	}

	/* ---------- the mosaic frame: tiles share hairlines ---------- */
	.evm-grid {
		list-style: none;
		margin: 0;
		padding: 0;
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 1px;
		background: var(--evm-line);
		border: 1px solid var(--evm-line);
	}

	.evm-tile {
		container-type: inline-size;
		grid-column: span var(--evm-ms, 2);
		min-width: 0;
		display: flex;
		flex-direction: column;
		justify-content: space-between;
		gap: 22px;
		padding: 22px 20px;
		background: var(--evm-panel);
	}

	/*
	 * Two tones: the lead is the one solid accent field; every other tile is
	 * the light panel, and its kind is carried by type alone — figures in
	 * heavy display numerals, results-in-words as semibold display headlines,
	 * the quote in lighter display type inside its own quotation marks, links
	 * in the accent colour with a ringed arrow.
	 */
	.evm-tile--lead {
		background: var(--evm-accent);
		color: var(--evm-accent-ink);
	}
	.evm-tile--link {
		padding: 0;
	}

	.evm-main {
		display: flex;
		flex-direction: column;
		gap: 10px;
		min-width: 0;
	}

	/* ---------- result: figure + meaning + attribution ---------- */
	.evm-fig {
		margin: 0;
		font-family: var(--evm-font-display);
		font-weight: 600;
		font-size: clamp(40px, 18cqi, 84px);
		line-height: 0.92;
		letter-spacing: -0.045em;
		font-variant-numeric: lining-nums;
		overflow-wrap: anywhere;
		color: var(--evm-ink);
	}
	.evm-tile--lead .evm-fig {
		font-size: clamp(64px, calc(var(--evm-fit, 30) * 0.8cqi), 132px);
		letter-spacing: -0.055em;
		color: var(--evm-accent-ink);
	}
	.evm-fig-pre,
	.evm-fig-post {
		font-size: 0.46em;
		letter-spacing: -0.01em;
		font-weight: 500;
	}
	.evm-fig-pre {
		vertical-align: 0.92em;
		margin-right: 0.06em;
	}
	.evm-fig-post {
		margin-left: 0.05em;
	}

	.evm-meaning {
		margin: 0;
		max-width: 34ch;
		font-size: 16px;
		line-height: 1.45;
		overflow-wrap: anywhere;
	}
	.evm-tile--lead .evm-meaning {
		font-size: clamp(17px, 1.6vw, 20px);
		max-width: 30ch;
	}
	.evm-tile--lead {
		justify-content: flex-start;
	}
	.evm-tile--lead > .evm-main {
		flex: 1;
		justify-content: flex-end;
	}
	/* The lead names its client first, as the tile's header. */
	.evm-tile--lead .evm-by {
		padding-top: 0;
		padding-bottom: 12px;
		border-top: 0;
		border-bottom: 1px solid color-mix(in srgb, var(--evm-accent-ink) 38%, transparent);
		color: var(--evm-accent-ink);
	}

	.evm-by-link {
		color: inherit;
		text-decoration: underline;
		text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
		text-decoration-thickness: 1px;
		text-underline-offset: 4px;
		display: inline-block;
		padding-block: 12px;
		margin-block: -12px;
	}
	.evm-by-link:hover {
		text-decoration-color: currentColor;
		text-decoration-thickness: 2px;
	}
	.evm-by-link:focus-visible {
		outline: 2px solid currentColor;
		outline-offset: 3px;
		border-radius: 2px;
	}

	/* ---------- narrative: a result in words, set as a headline ---------- */
	.evm-story {
		margin: 0;
		max-width: 30ch;
		font-family: var(--evm-font-display);
		font-size: clamp(21px, 7cqi, 28px);
		font-weight: 600;
		line-height: 1.18;
		letter-spacing: -0.022em;
		text-wrap: pretty;
		overflow-wrap: anywhere;
	}

	/* ---------- quote: the words in their own marks, and a name ---------- */
	.evm-quote {
		margin: 0;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 26px;
		height: 100%;
	}
	.evm-quote-text {
		margin: 0;
	}
	.evm-quote-text p {
		margin: 0;
		max-width: 34ch;
		font-family: var(--evm-font-display);
		font-size: clamp(19px, 8cqi, 31px);
		font-weight: 400;
		line-height: 1.3;
		letter-spacing: -0.012em;
		text-wrap: pretty;
		overflow-wrap: anywhere;
		text-indent: -0.42em;
	}
	.evm-quote-text p::before {
		content: "\201C";
	}
	.evm-quote-text p::after {
		content: "\201D";
	}
	.evm-quote-by {
		display: flex;
		flex-direction: column;
		gap: 2px;
	}
	.evm-quote-role {
		color: var(--evm-soft);
		font-weight: 500;
	}

	/* ---------- fact ---------- */
	.evm-fact-value {
		margin: 0;
		font-family: var(--evm-font-display);
		font-size: clamp(36px, 17cqi, 64px);
		font-weight: 600;
		line-height: 1;
		letter-spacing: -0.04em;
		font-variant-numeric: lining-nums;
		overflow-wrap: anywhere;
	}
	.evm-fact-label {
		margin: 0;
		max-width: 30ch;
		font-size: 16px;
		font-weight: 500;
		line-height: 1.4;
		overflow-wrap: anywhere;
	}
	.evm-fact-label--solo {
		font-family: var(--evm-font-display);
		font-size: clamp(19px, 6.6cqi, 23px);
		line-height: 1.28;
		letter-spacing: -0.012em;
	}

	/* ---------- link tile: the whole tile is the target ---------- */
	.evm-link {
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: 20px;
		height: 100%;
		min-height: 96px;
		padding: 22px 20px;
		color: var(--evm-accent);
		text-decoration: none;
		background: var(--evm-panel);
		transition: background-color 0.2s ease;
	}
	.evm-link-label {
		font-family: var(--evm-font-display);
		font-size: clamp(19px, 6.4cqi, 23px);
		font-weight: 600;
		line-height: 1.22;
		letter-spacing: -0.012em;
		text-decoration: underline;
		text-decoration-thickness: 1px;
		text-underline-offset: 5px;
		text-decoration-color: color-mix(in srgb, var(--evm-accent) 40%, transparent);
		overflow-wrap: anywhere;
	}
	.evm-link-note {
		font-size: 15px;
		line-height: 1.45;
		color: var(--evm-ink);
		overflow-wrap: anywhere;
	}
	.evm-link-arrow {
		display: inline-grid;
		place-items: center;
		flex: none;
		width: 48px;
		height: 48px;
		border: 1.5px solid var(--evm-accent);
		border-radius: 50%;
		color: var(--evm-accent);
		transition: transform 0.2s ease, background-color 0.2s ease, color 0.2s ease;
	}
	.evm-link:hover {
		background: color-mix(in srgb, var(--evm-accent) 7%, var(--evm-panel));
	}
	.evm-link:hover .evm-link-label {
		text-decoration-color: currentColor;
	}
	.evm-link:hover .evm-link-arrow {
		transform: translateX(4px);
		background: var(--evm-accent);
		color: var(--evm-accent-ink);
	}
	.evm-link:focus-visible {
		outline: 3px solid var(--evm-accent);
		outline-offset: -8px;
	}

	.evm-sr {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
		border: 0;
	}

	/* ---------- phone: a composed column, not a plain stack ---------- */
	@container evm (max-width: 599.98px) {
		/* Results read as ledger lines: a short figure beside its meaning, the client under both. */
		.evm-tile--result:not(.evm-tile--longfig) > .evm-main {
			display: grid;
			grid-template-columns: max-content minmax(0, 1fr);
			align-items: baseline;
			column-gap: 22px;
		}
		.evm-tile--result:not(.evm-tile--longfig) .evm-fig {
			white-space: nowrap;
			font-size: clamp(34px, 12cqi, 44px);
		}
		.evm-tile--result .evm-meaning {
			font-size: 15px;
		}
		/* Paired facts: tighter, figure first. */
		.evm-tile--fact {
			gap: 16px;
		}
		.evm-tile--fact .evm-fact-value {
			font-size: clamp(32px, 20cqi, 48px);
		}
		.evm-tile--fact .evm-fact-label {
			font-size: 15px;
		}
		.evm-quote-text p {
			font-size: clamp(19px, 6.6cqi, 24px);
		}
	}

	/* ---------- tablet and up ---------- */
	@container evm (min-width: 600px) {
		.evm-inner {
			padding-inline: 32px;
		}
		.evm-tile {
			grid-column: span var(--evm-ts, 2);
			padding: 28px;
			gap: 28px;
		}
		.evm-tile--link {
			padding: 0;
		}
		.evm-link {
			padding: 28px;
		}
	}

	/*
	 * Tablet: the lead as a full-width banner, then every other tile as a
	 * content-sized ledger row on one shared two-column grid — the key (figure,
	 * value or speaker) on the left, the evidence and its source on the right —
	 * so no tile is stretched beside a taller neighbour. Consecutive facts pair.
	 */
	@container evm (min-width: 600px) and (max-width: 959.98px) {
		.evm-tile--lead > .evm-main {
			flex-direction: row;
			align-items: flex-end;
			justify-content: space-between;
			gap: 32px;
		}
		.evm-tile--lead .evm-fig {
			font-size: clamp(72px, 17cqi, 112px);
		}
		.evm-tile--lead .evm-meaning {
			max-width: 28ch;
			padding-bottom: 6px;
		}

		.evm-tile--ledger {
			display: grid;
			grid-template-columns: minmax(0, 12fr) minmax(0, 21fr);
			column-gap: 32px;
			row-gap: 14px;
			align-items: start;
			align-content: start;
		}
		.evm-tile--ledger > .evm-main,
		.evm-tile--ledger > .evm-quote {
			display: contents;
		}
		.evm-tile--ledger .evm-fig,
		.evm-tile--ledger .evm-fact-value {
			grid-column: 1;
			grid-row: 1 / span 2;
			font-size: clamp(44px, 9cqi, 72px);
		}
		.evm-tile--ledger .evm-meaning,
		.evm-tile--ledger .evm-fact-label,
		.evm-tile--ledger .evm-story,
		.evm-tile--ledger .evm-quote-text {
			grid-column: 2;
			grid-row: 1;
		}
		.evm-tile--ledger .evm-fact-label--solo {
			grid-column: 1 / -1;
		}
		.evm-tile--ledger > .evm-by,
		.evm-tile--ledger > .evm-basis {
			grid-column: 2;
			grid-row: 2;
		}
		/* Text-only results and the quote name their source in the key column. */
		.evm-tile--narrative.evm-tile--ledger > .evm-by,
		.evm-tile--ledger .evm-quote-by {
			grid-column: 1;
			grid-row: 1;
		}
		.evm-tile--ledger .evm-quote-text p {
			font-size: clamp(20px, 3.6cqi, 26px);
			max-width: 38ch;
		}
		.evm-tile--link .evm-link {
			min-height: 0;
		}
	}

	/* ---------- desktop: the 12-column mosaic ---------- */
	@container evm (min-width: 960px) {
		.evm-inner {
			padding-inline: var(--evm-grid-gutter, 48px);
		}
		.evm-head {
			grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
			align-items: end;
			column-gap: 48px;
		}
		.evm-head-aside {
			padding-bottom: 6px;
		}
		.evm-grid {
			grid-template-columns: repeat(12, minmax(0, 1fr));
		}
		.evm-tile {
			grid-column: var(--evm-c) / span var(--evm-s);
			grid-row: var(--evm-r) / span var(--evm-rs);
			padding: 32px;
			min-height: 212px;
		}
		.evm-tile--lead {
			padding: 36px 40px 40px;
		}
		.evm-tile--lead .evm-fig {
			font-size: clamp(88px, calc(var(--evm-fit, 34) * 1cqi), 168px);
		}
		.evm-tile--lead > .evm-main {
			gap: 18px;
		}
		.evm-tile--link {
			padding: 0;
		}
		.evm-link {
			flex-direction: column;
			align-items: stretch;
			padding: 32px;
			min-height: 0;
		}
		.evm-link-arrow {
			align-self: flex-start;
		}
		/* A fact given a wide slot sets its label beside the figure. */
		.evm-tile--widefact > .evm-main {
			flex-direction: row;
			align-items: baseline;
			gap: 28px;
		}
		.evm-tile--widefact .evm-fact-label {
			max-width: 34ch;
		}

		.evm[data-count="2"] .evm-tile {
			min-height: 340px;
		}
		/* The tile beside the lead settles to the row's foot, level with the lead's caption. */
		.evm-tile--companion {
			justify-content: flex-end;
		}
	}

	@media (prefers-reduced-motion: reduce) {
		.evm-link,
		.evm-link-arrow {
			transition: none;
		}
		.evm-link:hover .evm-link-arrow {
			transform: none;
		}
	}

	@media (forced-colors: active) {
		.evm-grid {
			background: CanvasText;
		}
		.evm-tile {
			border: 1px solid CanvasText;
		}
		.evm-link:focus-visible,
		.evm-by-link:focus-visible {
			outline-color: Highlight;
		}
	}
</style>

===== END src/components/EvidenceMosaic.astro =====

===== FILE (supporting): src/components/EvidenceMosaic.md =====
# Evidence mosaic

Mixed proof in one hairline-ruled mosaic: an attributed result figure leads, and a quote, unnumbered results, the business's own facts (each with its basis) and case-study links keep their own shapes and tints, laid out from the mix. Every figure names its client or project; fewer than two items renders nothing. No icons, photos or cards.

## Integration

- A section heading (h2 by default). Designed for 2–7 evidence items (ideal 5): at most one quote and two link tiles; anything past 7 tiles is not shown.
- Truth: no default figure, percentage, name, quote, year or claim. A result renders only with a label and an attribution (no anonymous figures); a quote only with its words and a name. Fewer than 2 renderable items renders nothing, and the planner must not select the component with less.
- Evidence paths: the input schema has no attributed-result record. Results bind `evidence.statistics` (value, label) and take their attribution from `evidence.logos` (clients the business may name) — the closest existing verified path. The pairing is made by the builder from supplied evidence; with statistics alone (no named client) results do not render. Facts also read `evidence.statistics` (the business's own figures). The quote is `evidence.testimonials[0]`. No schema was added.
- `facts[].basis` is a short copy slot naming the record a figure comes from, written only from what was supplied with it; leave it out otherwise.
- Values render exactly as supplied; a short currency prefix or unit suffix is only set smaller.
- Container queries on the section's own width: one column in reading order below 600px (no swipe row); two columns from 600px; a 12-column mosaic computed from the mix from 960px. A tile alone in a row becomes a horizontal band.
- Link tiles and attribution links go through a safe-href check (http(s), mailto, tel, #, relative); http(s) links open in a new tab and say so.
- Reads --evm-bg, --evm-text, --evm-text-muted, --evm-brand, --evm-brand-text, --evm-border, --evm-panel-bg, --evm-family, --evm-display, --evm-grid-max, --evm-grid-gutter and --evm-heading-size.
- Loads Schibsted Grotesk and Instrument Sans from Google Fonts by default. Pass fontStylesheet="" on a site that loads its own fonts.

## Provenance

Staged as candidate `evidence-mosaic` (Step 28) and admitted through the candidate pipeline. Source type: original. References (direction only): none. Quality history: quality/candidates/records/evidence-mosaic.json.

===== END src/components/EvidenceMosaic.md =====

--- VERIFIED IN THE COMPONENT LIBRARY ---
Enrolled for verification but never run.
No verification run has been recorded.
No checks have passed yet.
No usable source fingerprint, so these results cannot be tied to the files below.

--- NOT PROVEN ---
- ClientRouter: not applicable — this component ships no script.

--- WHAT THIS DOES NOT CERTIFY ---
Verification was performed on the unmodified component, in an isolated fixture, in the
library. It says nothing about this project's CSS, routes, layout or data, and nothing
about the component once you adapt it. A verified component is a good starting point,
not a guarantee about the installation you are about to build.

--- TEST IN THIS PROJECT AFTER INSTALLING OR CUSTOMISING ---
- Horizontal overflow at 1440, 1280, 1024, 834, 390 and 320px.
- Keyboard operation end to end, including visible focus.
- The no-JavaScript render.
- prefers-reduced-motion: reduce.
- Two instances on one page, if this project renders more than one.
- If this project uses <ClientRouter />: teardown and reinitialisation across a real navigation.

Component reference

A fragment for a request you write yourself, e.g. “I want to add [paste] to Section 3 of my homepage.” It names the component, links its page, and fixes what must not change — the design build and its effects — while leaving branding and content open. It does not carry the code.

Read it first
“Evidence mosaic” from my Astro Component Library:
https://astro.baysixmedia.com/components/evidence-mosaic/

Use the existing library component as the implementation source. Preserve its design structure, layout, proportions, spacing system, responsive behavior, visual effects, animations, transitions, hover states, interactions, and accessibility.

The copy, colors, typography, images, and other content may be adapted to match the destination website. Do not redesign, simplify, or reinterpret the component’s underlying design build or effects.

Component readiness

Judged on recorded evidence, not on how it looks in the frame above. A description of a component is not a result.

Interactivity
Static
Profile
core only
Installation files
1 required + 1 supporting
Verified
Never
Client navigation
Not applicable — ships no script
Source fingerprint
No fingerprint recorded
Production ready
Not yet

Not yet verified

Enrolled for verification, but never run. Silence is reported as silence rather than assumed to be fine.

Open the verification fixture

src/components/library/EvidenceMosaic.astro

one fileNo imports, no companion files, no packages. Copy EvidenceMosaic.astro into any Astro project's components folder and render it.

---
/**
 * EvidenceMosaic (candidate, Step 36)
 *
 * Mixed proof in one bordered composition. The tiles are different kinds of
 * evidence, each with its own shape, and the grid follows the mix rather than
 * a fixed card size:
 *   - result     a figure (number set large, its unit or currency a step
 *                smaller) + one line on what it means + the named client or
 *                project it belongs to (optionally linked);
 *   - narrative  a result given without a number: the sentence, attributed;
 *   - quote      one short attributed quote (name, role, company);
 *   - fact       one of the business's own figures or facts, with its basis;
 *   - link       a case-study / evidence link, the whole tile one target.
 * The first result with a figure leads (the largest tile). The frame is one
 * hairline-ruled field: tiles share their borders, tinted by kind, no cards,
 * shadows, icons or photography.
 *
 * Truth: nothing renders that was not supplied. No default figure,
 * percentage, name, quote, year, client or claim. A result renders only with
 * its label AND its attribution (no anonymous figures); a quote only with its
 * words AND a name; a link only with a label and a safe href. Values are
 * shown exactly as supplied — the unit is set smaller for display, never
 * reworded. Fewer than 2 renderable evidence items → the section renders
 * nothing at all (a planner must not select it with less). At most 7 tiles;
 * one quote; two links.
 *
 * Two tones: the lead is the one solid accent field (its client named first,
 * the figure fitted to the tile); every other tile is one light panel and
 * its kind is carried by type alone — heavy display numerals for figures,
 * semibold display headlines for results given in words, lighter display
 * type inside quotation marks for the quote, accent text and a ringed arrow
 * for links. One provenance system: every source line (the header note, a
 * result's client, a quote's speaker, a fact's basis) sits under a hairline
 * at 15px in full ink. Hyphenated names and compounds ("Co-op", "60-panel")
 * are never split at the hyphen.
 *
 * Responsive (container queries on the section's own width), reading order
 * kept at every width (lead, quote, results and narratives, facts, links):
 * below 600px a composed column — results as ledger lines (a short figure
 * beside its meaning, client under both), consecutive facts paired two-up,
 * links as compact rows; never a hidden swipe row. 600–959px: the lead a
 * full-width banner (figure beside its meaning), then every other tile a
 * content-sized ledger row on one shared two-column grid (key — figure, value
 * or speaker — left; the evidence and its source right), consecutive facts
 * paired; no tile is stretched beside a taller neighbour. From 960px a
 * 12-column mosaic from the mix: the lead opens row one at 7/12, the tile
 * beside it settles to the row's foot; the rest follow in rows of three or
 * two (two-tile rows alternate their seam 7/5, 5/7). No tile spans two rows
 * and none is left alone in a closing row; a fact in a wide slot sets its
 * label beside its figure. Figure sizes follow each tile's own width (cqi).
 *
 * Hooks (read, never declared): --evm-bg, --evm-text, --evm-text-muted,
 * --evm-brand, --evm-brand-text, --evm-border, --evm-panel-bg, --evm-family,
 * --evm-display, --evm-grid-max, --evm-grid-gutter, --evm-heading-size.
 * No script; the only motion is the link tile's arrow on hover, off under
 * reduced motion.
 */

export interface EvidenceMosaicResult {
	/** The figure exactly as supplied ("38%", "$1.2M", "11 days"). Without one, the tile is a narrative. */
	value?: string;
	/** What the figure means, or the result sentence when there is no figure. */
	label: string;
	/** The named client or project the result belongs to. Required: no anonymous results. */
	attribution: string;
	/** Where the result can be read or checked; links the attribution. */
	href?: string;
}

export interface EvidenceMosaicQuote {
	quote: string;
	name: string;
	role?: string;
	company?: string;
}

export interface EvidenceMosaicFact {
	/** The figure or date exactly as supplied ("2011", "412"). Optional. */
	value?: string;
	/** What it is ("Serving the county since", "Installs completed in 2025"). */
	label: string;
	/** Where it comes from ("Company job records"). */
	basis?: string;
}

export interface EvidenceMosaicLink {
	label: string;
	href: string;
	/** One short line on what the linked page shows. */
	note?: string;
}

export interface EvidenceMosaicProps {
	heading: string;
	headingLevel?: "h2" | "h3";
	eyebrow?: string;
	/** One or two sentences beside the heading. */
	intro?: string;
	/** Provenance note under the intro — where the evidence came from. */
	note?: string;
	results?: readonly EvidenceMosaicResult[];
	quote?: EvidenceMosaicQuote | null;
	facts?: readonly EvidenceMosaicFact[];
	links?: readonly EvidenceMosaicLink[];
	fontStylesheet?: string;
	class?: string;
	id?: string;
}

interface Props extends EvidenceMosaicProps {}

const {
	heading,
	headingLevel = "h2",
	eyebrow = "",
	intro = "",
	note = "",
	results = [],
	quote = null,
	facts = [],
	links = [],
	fontStylesheet = "https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600&family=Schibsted+Grotesk:wght@500;600;700&display=swap",
	class: className,
	id,
} = Astro.props;

const MAX_TILES = 7;
const MAX_LINKS = 2;

const text = (v: unknown) => (typeof v === "string" ? v.trim() : "");
const safeHref = (v: unknown) => {
	const href = text(v);
	if (!href) return "";
	if (/^(https?:|mailto:|tel:)/i.test(href)) return /\s/.test(href) ? "" : href;
	if (/^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith("//")) return "";
	return href.startsWith("/") || href.startsWith("#") || href.startsWith("./") || href.startsWith("../") ? href : "";
};
const external = (href: string) => /^https?:/i.test(href);

/*
 * Display split only: a short non-digit prefix ("$", "£", "+") and a short
 * word/unit suffix ("%", "M", " days") are set smaller. The characters are
 * never changed or reordered; anything that does not fit this shape is set
 * whole.
 */
const splitFigure = (value: string) => {
	const m = value.match(/^([^\d\s]{0,2})(\d[\d.,:/]*)(.*)$/);
	if (!m) return { pre: "", num: value, post: "" };
	const [, pre, num, post] = m;
	if (post.length > 8 || /\d/.test(post)) return { pre: "", num: value, post: "" };
	return { pre, num, post };
};

type Kind = "lead" | "result" | "narrative" | "quote" | "fact" | "link";
interface Tile {
	kind: Kind;
	value: string;
	label: string;
	by: string;
	href: string;
	role: string;
	basis: string;
	fig: { pre: string; num: string; post: string };
}
const blank = { value: "", label: "", by: "", href: "", role: "", basis: "", fig: { pre: "", num: "", post: "" } };

const resultTiles: Tile[] = (results ?? [])
	.filter((r) => r && text(r.label) && text(r.attribution))
	.map((r) => {
		const value = text(r.value);
		return { ...blank, kind: value ? "result" : "narrative", value, label: text(r.label), by: text(r.attribution), href: safeHref(r.href), fig: value ? splitFigure(value) : blank.fig };
	});
const leadIndex = resultTiles.findIndex((t) => t.kind === "result");
const lead = leadIndex >= 0 ? [{ ...resultTiles[leadIndex], kind: "lead" as Kind }] : [];
const restResults = resultTiles.filter((_, i) => i !== leadIndex);

const quoteTile: Tile[] =
	quote && text(quote.quote) && text(quote.name)
		? [{ ...blank, kind: "quote", label: text(quote.quote), by: text(quote.name), role: [text(quote.role), text(quote.company)].filter(Boolean).join(", ") }]
		: [];

const factTiles: Tile[] = (facts ?? [])
	.filter((f) => f && text(f.label))
	.map((f) => ({ ...blank, kind: "fact", value: text(f.value), label: text(f.label), basis: text(f.basis) }));

const linkTiles: Tile[] = (links ?? [])
	.filter((l) => l && text(l.label) && safeHref(l.href))
	.slice(0, MAX_LINKS)
	.map((l) => ({ ...blank, kind: "link", label: text(l.label), href: safeHref(l.href), basis: text(l.note) }));

/* Reading order: lead, quote, the other results and narratives as supplied, facts, links. */
const tiles: Tile[] = [...lead, ...quoteTile, ...restResults, ...factTiles, ...linkTiles].slice(0, MAX_TILES);
const count = tiles.length;

/* ---------- desktop placement (12 columns), computed from the mix ---------- */
const WEIGHT: Record<Kind, number> = { lead: 3, quote: 3, narrative: 2.4, result: 2, fact: 2, link: 1.6 };
interface Place { c: number; s: number; r: number; rs: number }
const place: Place[] = new Array(count);

/* Split `width` columns among row items by weight, every tile at least 3 columns. */
const fillRow = (indices: number[], start: number, width: number, row: number) => {
	const total = indices.reduce((sum, i) => sum + WEIGHT[tiles[i].kind], 0);
	let spans = indices.map((i) => Math.max(3, Math.round((width * WEIGHT[tiles[i].kind]) / total)));
	let diff = width - spans.reduce((a, b) => a + b, 0);
	/* Settle rounding on the heaviest (or lightest) tiles. */
	const order = indices.map((_, k) => k).sort((a, b) => WEIGHT[tiles[indices[b]].kind] - WEIGHT[tiles[indices[a]].kind]);
	for (let k = 0; diff !== 0 && k < 24; k++) {
		const j = diff > 0 ? order[k % order.length] : order[order.length - 1 - (k % order.length)];
		if (diff < 0 && spans[j] <= 3) continue;
		spans[j] += diff > 0 ? 1 : -1;
		diff += diff > 0 ? -1 : 1;
	}
	let c = start;
	indices.forEach((i, k) => {
		place[i] = { c, s: spans[k], r: row, rs: 1 };
		c += spans[k];
	});
};

const hasLead = lead.length > 0;
/* A row of two: offset the seam row by row (7/5, then 5/7) so the field reads as a mosaic, not a table. */
const offsetSeam = (idx: number[], row: number, firstWide: boolean) => {
	if (idx.length !== 2) return;
	const first = firstWide ? 7 : 5;
	place[idx[0]] = { ...place[idx[0]], s: first };
	place[idx[1]] = { ...place[idx[1]], c: 1 + first, s: 12 - first };
};
const fillRows = (sizes: number[], from: number, firstRow: number) => {
	let i = from;
	sizes.forEach((size, k) => {
		const idx = Array.from({ length: size }, (_, j) => i + j);
		fillRow(idx, 1, 12, firstRow + k);
		offsetSeam(idx, firstRow + k, (firstRow + k) % 2 === 1);
		i += size;
	});
};
if (hasLead) {
	/*
	 * The lead opens the first row beside the next tile (the quote, when there is one), both
	 * sized by their content — no tile is stretched over two rows, so none carries a dead band.
	 * The rest follow in rows of two or three; a tile is never left alone in a closing row.
	 */
	if (count === 3) {
		place[0] = { c: 1, s: 6, r: 1, rs: 1 };
		fillRow([1, 2], 7, 6, 1);
	} else {
		place[0] = { c: 1, s: 7, r: 1, rs: 1 };
		place[1] = { c: 8, s: 5, r: 1, rs: 1 };
		const rest: Record<number, number[]> = { 2: [], 4: [2], 5: [3], 6: [2, 2], 7: [3, 2] };
		fillRows(rest[count] ?? [], 2, 2);
	}
} else if (count === 2) {
	fillRow([0, 1], 1, 12, 1);
} else {
	const sizes: Record<number, number[]> = { 3: [3], 4: [2, 2], 5: [2, 3], 6: [3, 3], 7: [3, 4] };
	fillRows(sizes[count] ?? [count], 0, 1);
}

/* ---------- phone placement (2 columns): consecutive facts pair up; everything else spans ---------- */
const phoneSpan: number[] = tiles.map(() => 2);
for (let i = 0; i < count - 1; i++) {
	if (tiles[i].kind === "fact" && tiles[i + 1].kind === "fact" && phoneSpan[i] === 2) {
		phoneSpan[i] = 1;
		phoneSpan[i + 1] = 1;
		i++;
	}
}

/* ---------- tablet placement (2 columns): the lead and every other tile span as ledger rows; consecutive facts pair ---------- */
const tabletSpan = phoneSpan;

const styleOf = (i: number) => {
	const p = place[i] ?? { c: 1, s: 12, r: i + 1, rs: 1 };
	return `--evm-c:${p.c};--evm-s:${p.s};--evm-r:${p.r};--evm-rs:${p.rs};--evm-ts:${tabletSpan[i]};--evm-ms:${phoneSpan[i]}`;
};
/* Hyphenated compounds and names ("Co-op", "60-panel") never break at the hyphen. Display only. */
const parts = (value: string) =>
	value
		.split(/(\S*[^\s-]-[^\s-]\S*)/)
		.filter(Boolean)
		.map((t) => ({ t, keep: /[^\s-]-[^\s-]/.test(t) }));

/* Fit the lead figure to its tile: cqi per em-width estimate of the supplied value (display only). */
const fitOf = (t: Tile) => {
	const em = t.fig.num.length * 0.58 + (t.fig.pre.length + t.fig.post.length) * 0.3 + 0.1;
	return Math.max(14, Math.min(58, Math.round(100 / em)));
};
const longFig = (t: Tile) => t.fig.num.length + t.fig.post.length > 7;
const bandOf = (i: number) => (place[i]?.s ?? 0) >= 9 && tiles[i].kind !== "lead";
const ledgerOf = (i: number) => tabletSpan[i] === 2 && tiles[i].kind !== "lead" && tiles[i].kind !== "link";

const Heading = headingLevel;
---

{count >= 2 && (
<>
{fontStylesheet && <link rel="stylesheet" href={fontStylesheet} />}
<section class:list={["evm", className]} id={id} data-count={count}>
	<div class="evm-inner">
		<header class="evm-head">
			<div class="evm-head-main">
				{text(eyebrow) && <p class="evm-eyebrow">{text(eyebrow)}</p>}
				{text(heading) && <Heading class="evm-title">{text(heading)}</Heading>}
			</div>
			{(text(intro) || text(note)) && (
				<div class="evm-head-aside">
					{text(intro) && <p class="evm-intro">{text(intro)}</p>}
					{text(note) && <p class="evm-note">{text(note)}</p>}
				</div>
			)}
		</header>

		<ul class="evm-grid" role="list">
			{tiles.map((t, i) => (
				<li class:list={["evm-tile", `evm-tile--${t.kind}`, { "evm-tile--band": bandOf(i), "evm-tile--ledger": ledgerOf(i), "evm-tile--companion": hasLead && i === 1, "evm-tile--longfig": t.kind === "result" && longFig(t), "evm-tile--widefact": t.kind === "fact" && !bandOf(i) && (place[i]?.s ?? 0) >= 7 }]} style={styleOf(i)}>
					{t.kind === "lead" && (
						<>
							<p class="evm-by evm-by--head">
								{t.href ? (
									<a class="evm-by-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
										{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}
										{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
									</a>
								) : (
									t.by
								)}
							</p>
							<div class="evm-main">
								<p class="evm-fig" style={`--evm-fit:${fitOf(t)}`}>{t.fig.pre && <span class="evm-fig-pre">{t.fig.pre}</span>}<span class="evm-fig-num">{t.fig.num}</span>{t.fig.post && <span class="evm-fig-post">{t.fig.post}</span>}</p>
								<p class="evm-meaning">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</div>
						</>
					)}

					{t.kind === "result" && (
						<>
							<div class="evm-main">
								<p class="evm-fig">{t.fig.pre && <span class="evm-fig-pre">{t.fig.pre}</span>}<span class="evm-fig-num">{t.fig.num}</span>{t.fig.post && <span class="evm-fig-post">{t.fig.post}</span>}</p>
								<p class="evm-meaning">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</div>
							<p class="evm-by">
								{t.href ? (
									<a class="evm-by-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
										{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}
										{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
									</a>
								) : (
									t.by
								)}
							</p>
						</>
					)}

					{t.kind === "narrative" && (
						<>
							<p class="evm-story">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							<p class="evm-by">
								{t.href ? (
									<a class="evm-by-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
										{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}
										{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
									</a>
								) : (
									t.by
								)}
							</p>
						</>
					)}

					{t.kind === "quote" && (
						<figure class="evm-quote">
							<blockquote class="evm-quote-text">
								<p>{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</blockquote>
							<figcaption class="evm-quote-by">
								<span class="evm-quote-name">{parts(t.by).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>
								{t.role && <span class="evm-quote-role">{parts(t.role).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>}
							</figcaption>
						</figure>
					)}

					{t.kind === "fact" && (
						<>
							<div class="evm-main">
								{t.value && <p class="evm-fact-value">{t.value}</p>}
								<p class:list={["evm-fact-label", { "evm-fact-label--solo": !t.value }]}>{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>
							</div>
							{t.basis && <p class="evm-basis">{parts(t.basis).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</p>}
						</>
					)}

					{t.kind === "link" && (
						<a class="evm-link" href={t.href} {...(external(t.href) ? { target: "_blank", rel: "noopener" } : {})}>
							<span class="evm-main">
								<span class="evm-link-label">{parts(t.label).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>
								{t.basis && <span class="evm-link-note">{parts(t.basis).map((p) => (p.keep ? <span class="evm-keep">{p.t}</span> : p.t))}</span>}
							</span>
							<span class="evm-link-arrow" aria-hidden="true">
								<svg viewBox="0 0 24 24" width="22" height="22" focusable="false"><path d="M5 12h13M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="square" /></svg>
							</span>
							{external(t.href) && <span class="evm-sr"> (opens in a new tab)</span>}
						</a>
					)}
				</li>
			))}
		</ul>
	</div>
</section>
</>
)}

<style>
	.evm {
		--evm-surface: var(--evm-bg, #e8ebe6);
		--evm-ink: var(--evm-text, #15191c);
		--evm-soft: var(--evm-text-muted, #3f464c);
		--evm-accent: var(--evm-brand, #1f4a63);
		--evm-accent-ink: var(--evm-brand-text, #ffffff);
		--evm-line: var(--evm-border, #bcc3bb);
		--evm-panel: var(--evm-panel-bg, #fbfbf8);
		--evm-font: var(--evm-family, "Instrument Sans", ui-sans-serif, system-ui, sans-serif);
		--evm-font-display: var(--evm-display, "Schibsted Grotesk", ui-sans-serif, system-ui, sans-serif);

		container-type: inline-size;
		container-name: evm;
		background: var(--evm-surface);
		color: var(--evm-ink);
		font-family: var(--evm-font);
		padding-block: clamp(64px, 9vw, 120px);
		hyphens: manual;
	}

	.evm *,
	.evm *::before,
	.evm *::after {
		box-sizing: border-box;
	}

	/* A hyphenated compound or name is never split at its hyphen. */
	.evm-keep {
		white-space: nowrap;
	}

	.evm-inner {
		max-width: var(--evm-grid-max, 1232px);
		margin-inline: auto;
		padding-inline: 20px;
	}

	/* ---------- header ---------- */
	.evm-head {
		display: grid;
		gap: 18px;
		margin-bottom: clamp(32px, 5vw, 56px);
	}
	.evm-eyebrow {
		margin: 0 0 14px;
		font-size: 15px;
		font-weight: 500;
		line-height: 1.4;
		color: var(--evm-accent);
	}
	.evm-title {
		margin: 0;
		max-width: 18ch;
		font-family: var(--evm-font-display);
		font-size: var(--evm-heading-size, clamp(32px, 4vw, 48px));
		font-weight: 600;
		line-height: 1.06;
		letter-spacing: -0.028em;
		text-wrap: balance;
		overflow-wrap: anywhere;
	}
	.evm-intro {
		margin: 0;
		max-width: 46ch;
		font-size: 17px;
		line-height: 1.55;
		color: var(--evm-soft);
		overflow-wrap: anywhere;
	}

	/*
	 * One provenance system: every source line — the header note, each
	 * result's client, each quote's speaker, each fact's basis — is set the
	 * same way: a hairline above, 15px, full ink. Where evidence comes from is
	 * read as part of the evidence, not as fine print.
	 */
	.evm-note,
	.evm-by,
	.evm-quote-by,
	.evm-basis {
		margin: 0;
		padding-top: 12px;
		border-top: 1px solid var(--evm-line);
		font-size: 15px;
		line-height: 1.45;
		color: var(--evm-ink);
		overflow-wrap: anywhere;
	}
	.evm-note {
		margin-top: 16px;
		max-width: 46ch;
		font-weight: 500;
	}
	.evm-by,
	.evm-quote-name {
		font-weight: 600;
	}
	.evm-basis {
		font-weight: 500;
	}

	/* ---------- the mosaic frame: tiles share hairlines ---------- */
	.evm-grid {
		list-style: none;
		margin: 0;
		padding: 0;
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 1px;
		background: var(--evm-line);
		border: 1px solid var(--evm-line);
	}

	.evm-tile {
		container-type: inline-size;
		grid-column: span var(--evm-ms, 2);
		min-width: 0;
		display: flex;
		flex-direction: column;
		justify-content: space-between;
		gap: 22px;
		padding: 22px 20px;
		background: var(--evm-panel);
	}

	/*
	 * Two tones: the lead is the one solid accent field; every other tile is
	 * the light panel, and its kind is carried by type alone — figures in
	 * heavy display numerals, results-in-words as semibold display headlines,
	 * the quote in lighter display type inside its own quotation marks, links
	 * in the accent colour with a ringed arrow.
	 */
	.evm-tile--lead {
		background: var(--evm-accent);
		color: var(--evm-accent-ink);
	}
	.evm-tile--link {
		padding: 0;
	}

	.evm-main {
		display: flex;
		flex-direction: column;
		gap: 10px;
		min-width: 0;
	}

	/* ---------- result: figure + meaning + attribution ---------- */
	.evm-fig {
		margin: 0;
		font-family: var(--evm-font-display);
		font-weight: 600;
		font-size: clamp(40px, 18cqi, 84px);
		line-height: 0.92;
		letter-spacing: -0.045em;
		font-variant-numeric: lining-nums;
		overflow-wrap: anywhere;
		color: var(--evm-ink);
	}
	.evm-tile--lead .evm-fig {
		font-size: clamp(64px, calc(var(--evm-fit, 30) * 0.8cqi), 132px);
		letter-spacing: -0.055em;
		color: var(--evm-accent-ink);
	}
	.evm-fig-pre,
	.evm-fig-post {
		font-size: 0.46em;
		letter-spacing: -0.01em;
		font-weight: 500;
	}
	.evm-fig-pre {
		vertical-align: 0.92em;
		margin-right: 0.06em;
	}
	.evm-fig-post {
		margin-left: 0.05em;
	}

	.evm-meaning {
		margin: 0;
		max-width: 34ch;
		font-size: 16px;
		line-height: 1.45;
		overflow-wrap: anywhere;
	}
	.evm-tile--lead .evm-meaning {
		font-size: clamp(17px, 1.6vw, 20px);
		max-width: 30ch;
	}
	.evm-tile--lead {
		justify-content: flex-start;
	}
	.evm-tile--lead > .evm-main {
		flex: 1;
		justify-content: flex-end;
	}
	/* The lead names its client first, as the tile's header. */
	.evm-tile--lead .evm-by {
		padding-top: 0;
		padding-bottom: 12px;
		border-top: 0;
		border-bottom: 1px solid color-mix(in srgb, var(--evm-accent-ink) 38%, transparent);
		color: var(--evm-accent-ink);
	}

	.evm-by-link {
		color: inherit;
		text-decoration: underline;
		text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
		text-decoration-thickness: 1px;
		text-underline-offset: 4px;
		display: inline-block;
		padding-block: 12px;
		margin-block: -12px;
	}
	.evm-by-link:hover {
		text-decoration-color: currentColor;
		text-decoration-thickness: 2px;
	}
	.evm-by-link:focus-visible {
		outline: 2px solid currentColor;
		outline-offset: 3px;
		border-radius: 2px;
	}

	/* ---------- narrative: a result in words, set as a headline ---------- */
	.evm-story {
		margin: 0;
		max-width: 30ch;
		font-family: var(--evm-font-display);
		font-size: clamp(21px, 7cqi, 28px);
		font-weight: 600;
		line-height: 1.18;
		letter-spacing: -0.022em;
		text-wrap: pretty;
		overflow-wrap: anywhere;
	}

	/* ---------- quote: the words in their own marks, and a name ---------- */
	.evm-quote {
		margin: 0;
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: 26px;
		height: 100%;
	}
	.evm-quote-text {
		margin: 0;
	}
	.evm-quote-text p {
		margin: 0;
		max-width: 34ch;
		font-family: var(--evm-font-display);
		font-size: clamp(19px, 8cqi, 31px);
		font-weight: 400;
		line-height: 1.3;
		letter-spacing: -0.012em;
		text-wrap: pretty;
		overflow-wrap: anywhere;
		text-indent: -0.42em;
	}
	.evm-quote-text p::before {
		content: "\201C";
	}
	.evm-quote-text p::after {
		content: "\201D";
	}
	.evm-quote-by {
		display: flex;
		flex-direction: column;
		gap: 2px;
	}
	.evm-quote-role {
		color: var(--evm-soft);
		font-weight: 500;
	}

	/* ---------- fact ---------- */
	.evm-fact-value {
		margin: 0;
		font-family: var(--evm-font-display);
		font-size: clamp(36px, 17cqi, 64px);
		font-weight: 600;
		line-height: 1;
		letter-spacing: -0.04em;
		font-variant-numeric: lining-nums;
		overflow-wrap: anywhere;
	}
	.evm-fact-label {
		margin: 0;
		max-width: 30ch;
		font-size: 16px;
		font-weight: 500;
		line-height: 1.4;
		overflow-wrap: anywhere;
	}
	.evm-fact-label--solo {
		font-family: var(--evm-font-display);
		font-size: clamp(19px, 6.6cqi, 23px);
		line-height: 1.28;
		letter-spacing: -0.012em;
	}

	/* ---------- link tile: the whole tile is the target ---------- */
	.evm-link {
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: 20px;
		height: 100%;
		min-height: 96px;
		padding: 22px 20px;
		color: var(--evm-accent);
		text-decoration: none;
		background: var(--evm-panel);
		transition: background-color 0.2s ease;
	}
	.evm-link-label {
		font-family: var(--evm-font-display);
		font-size: clamp(19px, 6.4cqi, 23px);
		font-weight: 600;
		line-height: 1.22;
		letter-spacing: -0.012em;
		text-decoration: underline;
		text-decoration-thickness: 1px;
		text-underline-offset: 5px;
		text-decoration-color: color-mix(in srgb, var(--evm-accent) 40%, transparent);
		overflow-wrap: anywhere;
	}
	.evm-link-note {
		font-size: 15px;
		line-height: 1.45;
		color: var(--evm-ink);
		overflow-wrap: anywhere;
	}
	.evm-link-arrow {
		display: inline-grid;
		place-items: center;
		flex: none;
		width: 48px;
		height: 48px;
		border: 1.5px solid var(--evm-accent);
		border-radius: 50%;
		color: var(--evm-accent);
		transition: transform 0.2s ease, background-color 0.2s ease, color 0.2s ease;
	}
	.evm-link:hover {
		background: color-mix(in srgb, var(--evm-accent) 7%, var(--evm-panel));
	}
	.evm-link:hover .evm-link-label {
		text-decoration-color: currentColor;
	}
	.evm-link:hover .evm-link-arrow {
		transform: translateX(4px);
		background: var(--evm-accent);
		color: var(--evm-accent-ink);
	}
	.evm-link:focus-visible {
		outline: 3px solid var(--evm-accent);
		outline-offset: -8px;
	}

	.evm-sr {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0 0 0 0);
		white-space: nowrap;
		border: 0;
	}

	/* ---------- phone: a composed column, not a plain stack ---------- */
	@container evm (max-width: 599.98px) {
		/* Results read as ledger lines: a short figure beside its meaning, the client under both. */
		.evm-tile--result:not(.evm-tile--longfig) > .evm-main {
			display: grid;
			grid-template-columns: max-content minmax(0, 1fr);
			align-items: baseline;
			column-gap: 22px;
		}
		.evm-tile--result:not(.evm-tile--longfig) .evm-fig {
			white-space: nowrap;
			font-size: clamp(34px, 12cqi, 44px);
		}
		.evm-tile--result .evm-meaning {
			font-size: 15px;
		}
		/* Paired facts: tighter, figure first. */
		.evm-tile--fact {
			gap: 16px;
		}
		.evm-tile--fact .evm-fact-value {
			font-size: clamp(32px, 20cqi, 48px);
		}
		.evm-tile--fact .evm-fact-label {
			font-size: 15px;
		}
		.evm-quote-text p {
			font-size: clamp(19px, 6.6cqi, 24px);
		}
	}

	/* ---------- tablet and up ---------- */
	@container evm (min-width: 600px) {
		.evm-inner {
			padding-inline: 32px;
		}
		.evm-tile {
			grid-column: span var(--evm-ts, 2);
			padding: 28px;
			gap: 28px;
		}
		.evm-tile--link {
			padding: 0;
		}
		.evm-link {
			padding: 28px;
		}
	}

	/*
	 * Tablet: the lead as a full-width banner, then every other tile as a
	 * content-sized ledger row on one shared two-column grid — the key (figure,
	 * value or speaker) on the left, the evidence and its source on the right —
	 * so no tile is stretched beside a taller neighbour. Consecutive facts pair.
	 */
	@container evm (min-width: 600px) and (max-width: 959.98px) {
		.evm-tile--lead > .evm-main {
			flex-direction: row;
			align-items: flex-end;
			justify-content: space-between;
			gap: 32px;
		}
		.evm-tile--lead .evm-fig {
			font-size: clamp(72px, 17cqi, 112px);
		}
		.evm-tile--lead .evm-meaning {
			max-width: 28ch;
			padding-bottom: 6px;
		}

		.evm-tile--ledger {
			display: grid;
			grid-template-columns: minmax(0, 12fr) minmax(0, 21fr);
			column-gap: 32px;
			row-gap: 14px;
			align-items: start;
			align-content: start;
		}
		.evm-tile--ledger > .evm-main,
		.evm-tile--ledger > .evm-quote {
			display: contents;
		}
		.evm-tile--ledger .evm-fig,
		.evm-tile--ledger .evm-fact-value {
			grid-column: 1;
			grid-row: 1 / span 2;
			font-size: clamp(44px, 9cqi, 72px);
		}
		.evm-tile--ledger .evm-meaning,
		.evm-tile--ledger .evm-fact-label,
		.evm-tile--ledger .evm-story,
		.evm-tile--ledger .evm-quote-text {
			grid-column: 2;
			grid-row: 1;
		}
		.evm-tile--ledger .evm-fact-label--solo {
			grid-column: 1 / -1;
		}
		.evm-tile--ledger > .evm-by,
		.evm-tile--ledger > .evm-basis {
			grid-column: 2;
			grid-row: 2;
		}
		/* Text-only results and the quote name their source in the key column. */
		.evm-tile--narrative.evm-tile--ledger > .evm-by,
		.evm-tile--ledger .evm-quote-by {
			grid-column: 1;
			grid-row: 1;
		}
		.evm-tile--ledger .evm-quote-text p {
			font-size: clamp(20px, 3.6cqi, 26px);
			max-width: 38ch;
		}
		.evm-tile--link .evm-link {
			min-height: 0;
		}
	}

	/* ---------- desktop: the 12-column mosaic ---------- */
	@container evm (min-width: 960px) {
		.evm-inner {
			padding-inline: var(--evm-grid-gutter, 48px);
		}
		.evm-head {
			grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
			align-items: end;
			column-gap: 48px;
		}
		.evm-head-aside {
			padding-bottom: 6px;
		}
		.evm-grid {
			grid-template-columns: repeat(12, minmax(0, 1fr));
		}
		.evm-tile {
			grid-column: var(--evm-c) / span var(--evm-s);
			grid-row: var(--evm-r) / span var(--evm-rs);
			padding: 32px;
			min-height: 212px;
		}
		.evm-tile--lead {
			padding: 36px 40px 40px;
		}
		.evm-tile--lead .evm-fig {
			font-size: clamp(88px, calc(var(--evm-fit, 34) * 1cqi), 168px);
		}
		.evm-tile--lead > .evm-main {
			gap: 18px;
		}
		.evm-tile--link {
			padding: 0;
		}
		.evm-link {
			flex-direction: column;
			align-items: stretch;
			padding: 32px;
			min-height: 0;
		}
		.evm-link-arrow {
			align-self: flex-start;
		}
		/* A fact given a wide slot sets its label beside the figure. */
		.evm-tile--widefact > .evm-main {
			flex-direction: row;
			align-items: baseline;
			gap: 28px;
		}
		.evm-tile--widefact .evm-fact-label {
			max-width: 34ch;
		}

		.evm[data-count="2"] .evm-tile {
			min-height: 340px;
		}
		/* The tile beside the lead settles to the row's foot, level with the lead's caption. */
		.evm-tile--companion {
			justify-content: flex-end;
		}
	}

	@media (prefers-reduced-motion: reduce) {
		.evm-link,
		.evm-link-arrow {
			transition: none;
		}
		.evm-link:hover .evm-link-arrow {
			transform: none;
		}
	}

	@media (forced-colors: active) {
		.evm-grid {
			background: CanvasText;
		}
		.evm-tile {
			border: 1px solid CanvasText;
		}
		.evm-link:focus-visible,
		.evm-by-link:focus-visible {
			outline-color: Highlight;
		}
	}
</style>