# Files changed · PR \#133 · flambo/atlantis

[View on GitCafe](https://git.cafe/flambo/atlantis/pull/133/diffs)

Repository: [flambo/atlantis](https://git.cafe/flambo/atlantis)

Visibility: public

[perf\(web\): reduce dashboard rendering cost and spectrum payloads](https://git.cafe/flambo/atlantis/pull/133.md)

Version: 0

Head commit: c8222023a3dee70187b8f5625726609facf9cf67

Base commit: ab5316e0786e50a468fd0cefd926f8cbf9c4f6f8

Comparison base: ab5316e0786e50a468fd0cefd926f8cbf9c4f6f8

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/web/src/lib/components/charts/BreakdownChart.svelte

Status: modified; +17; −6

```diff
@@ -104,6 +104,7 @@
 		props.granularity ?? config.defaultGranularity
 	);
 
+	let addressType = $derived(props.addressType ?? 'sa');
 	const ipVersion = $derived<MaadIpVersion>(props.ipVersion ?? DEFAULT_MAAD_IP_VERSION);
 	type FilterInputs = {
 		startDate: string;
@@ -113,6 +114,7 @@
 		direction: FlowDirection;
 		ipVersion?: MaadIpVersion;
 		measure?: MaadMeasure;
+		addressSide?: 'source' | 'destination';
 	};
 
 	const filters = $derived<FilterInputs>({
@@ -126,7 +128,10 @@
 					: []
 				: deriveSelectedRouters(props.routers),
 		direction: props.direction ?? 'all',
-		...(config.usesMaad ? { ipVersion, measure: props.measure } : {})
+		...(config.usesMaad ? { ipVersion, measure: props.measure } : {}),
+		...(props.kind === 'spectrum'
+			? { addressSide: addressType === 'sa' ? ('source' as const) : ('destination' as const) }
+			: {})
 	});
 	const requestKey = $derived(
 		JSON.stringify({
@@ -159,7 +164,6 @@
 				? settled.error
 				: null
 	);
-	let addressType = $derived(props.addressType ?? 'sa');
 	const selectedSide = $derived<DimensionSide | null>(
 		props.kind === 'spectrum'
 			? addressType
@@ -221,6 +225,11 @@
 	const bucketStarts = $derived(
 		[...new Set(buckets.map((bucket) => bucket.bucketStart))].sort((a, b) => a - b)
 	);
+	const bucketLabels = $derived(
+		new Map(
+			bucketStarts.map((start) => [start, formatTemporalBucketLabel(start, currentGranularity)])
+		)
+	);
 	const series = $derived.by((): PlotSeries[] => {
 		if (props.kind === 'spectrum')
 			return [
@@ -232,7 +241,7 @@
 							x: bucket.bucketStart,
 							y: point.alpha,
 							f: point.f,
+							label: bucketLabels.get(bucket.bucketStart),
-							label: formatTemporalBucketLabel(bucket.bucketStart, currentGranularity),
 							coverage: bucket.coverage
 						}))
 					)
@@ -264,7 +273,7 @@
 							return {
 								x: start,
 								y: readLineMetric((bucket?.data as LineBucketData | null) ?? null, metric.key),
-								label: formatTemporalBucketLabel(start, currentGranularity),
+								label: bucketLabels.get(start),
 								coverage: getChartBucketCoverage(bucket) ?? {
 									state: 'unknown',
 									observedUnits: 0,
@@ -281,7 +290,8 @@
 		plotBounds(series, groupByBucketDurationMs(IP_TO_GROUP_BY[currentGranularity]) / 1000)
 	);
 	const chartOptions = $derived.by((): PlotOptions => {
+		const points =
-		const points = series.flatMap((item) => item.data).filter(finitePoint);
+			props.kind === 'spectrum' ? series.flatMap((item) => item.data).filter(finitePoint) : [];
 		let minAlpha = Infinity;
 		let maxAlpha = -Infinity;
 		let minF = Infinity;
@@ -342,7 +352,8 @@
 			routers: filters.routers.join(','),
 			direction: filters.direction,
 			...(filters.ipVersion !== undefined ? { ipVersion: String(filters.ipVersion) } : {}),
+			...(filters.measure !== undefined ? { measure: filters.measure } : {}),
-			...(filters.measure !== undefined ? { measure: filters.measure } : {})
+			...(filters.addressSide ? { addressSide: filters.addressSide } : {})
 		});
 		const cacheKey = `${config.endpoint}?${params}`;
 		await ensureCachedWindow<CachedBreakdownBucket>({
```

## apps/web/src/lib/components/charts/ChartPlot.svelte

Status: modified; +74; −92

```diff
@@ -1,8 +1,10 @@
 <script lang="ts">
+	import { SvelteSet } from 'svelte/reactivity';
+	import { untrack } from 'svelte';
-	import { SvelteSet, SvelteMap } from 'svelte/reactivity';
+	import { mountChart } from '@tanstack/charts/dom';
+	import { defineChart } from '@tanstack/charts/scene';
 	import type { ChartPoint, ChartRenderContext } from '@tanstack/charts';
 	import {
-		Chart,
 		buildChartDefinition,
 		finitePoint,
 		plotTooltip,
@@ -11,6 +13,9 @@
 		type PlotSeries
 	} from './chart-registry';
 	import { paintLegendSwatch } from './legend-swatch';
+	import { paintSpectrumCloud } from './spectrum-cloud';
+	import { renderCoverageSvg } from './coverage-marks';
+	import { plotObservations, positionedScenePoints } from './chart-observations';
 	import { createChartContract } from './chart-contract';
 	import { MIN_DRAG_PIXELS, findNearestValueIndex } from './chart-utils';
 	import { crosshairStore } from '#lib/stores/crosshair.ts';
@@ -37,8 +42,8 @@
 		emptyCopy?: string;
 		retainEmptySurface?: boolean;
 	} = $props();
+	const generatedId = $props.id();
 	const hidden = new SvelteSet<string>();
-	let size = $state({ width: 800, height: 320 });
 	let context: ChartRenderContext<PlotPoint, number, number> | null = null;
 	let contract: ReturnType<typeof createChartContract> | null = null;
 	let contractSurface: HTMLElement | SVGElement | null = null;
@@ -51,25 +56,32 @@
 	let selection: HTMLDivElement | null = null;
 	let externalCrosshair: HTMLDivElement | null = null;
 	const hasData = $derived(series.some((item) => item.data.some(finitePoint)));
-	const timeline = $derived.by(() => {
-		const unique = new SvelteMap<number, PlotPoint>();
-		for (const item of series) {
-			if (hidden.has(item.label)) continue;
-			for (const point of item.data) if (finitePoint(point)) unique.set(point.x, point);
+	const observationData = $derived(plotObservations(series, hidden));
+	let observations: ReturnType<typeof plotObservations> | null = null;
+	const definition = $derived.by(() => {
-		}
+		const currentSeries = series;
+		const currentOptions = options;
+		const currentHidden = new Set(hidden);
+		const formatter = formatTooltip;
+		const normal = buildChartDefinition(
+			currentSeries,
+			{ ...currentOptions, compact: false },
+			currentHidden,
+			toggle,
+			formatter
+		);
+		return defineChart({
+			chart: ({ width }) =>
+				currentOptions.compact && width < 560
+					? buildChartDefinition(currentSeries, currentOptions, currentHidden, toggle, formatter)
+					: normal,
+			focus: normal.focus,
+			tooltip: normal.tooltip,
+			motion: false,
+			svgAnimation: false,
+			keyboard: true
+		});
-		return [...unique.values()].sort((left, right) => left.x - right.x);
 	});
-	const timestamps = $derived(timeline.map((point) => point.x));
-	const labeledPoints = $derived(new SvelteMap(timeline.map((point) => [point.label, point])));
-	const definition = $derived(
-		buildChartDefinition(
-			series,
-			{ ...options, compact: options.compact && size.width < 560 },
-			hidden,
-			toggle,
-			formatTooltip
-		)
-	);
 
 	function toggle(visible: readonly string[]) {
 		for (const item of series) {
@@ -78,37 +90,42 @@
 		}
 	}
 
+	function attachPlot(node: HTMLDivElement) {
+		const hostOptions = () => ({
+			definition,
+			idPrefix: `atlantis-chart-${generatedId.replaceAll(/[^a-zA-Z0-9_-]/g, '')}`,
+			ariaLabel: name,
+			renderSvg: options.kind === 'coverage' ? renderCoverageSvg : undefined,
+			onRender: publish,
+			onFocusGroupChange: focusGroup,
+			onSelect: select
-	function attachSize(node: HTMLDivElement) {
-		const observer = new ResizeObserver(() => {
-			const width = node.clientWidth;
-			const height = node.clientHeight;
-			if (width > 0 && height > 0 && (width !== size.width || height !== size.height))
-				size = { width, height };
 		});
+		let host: ReturnType<typeof mountChart<PlotPoint, number, number>> | null = null;
+		$effect(() => {
-		observer.observe(node);
+			const current = hostOptions();
+			const currentObservations = observationData;
+			untrack(() => {
+				observations = currentObservations;
+				if (host) host.update(current);
+				else host = mountChart(node, current);
+			});
+		});
 		return () => {
-			observer.disconnect();
 			cleanupRender();
 			contract?.destroy();
+			host?.destroy();
 			contract = null;
+			contractSurface = null;
 			context = null;
+			observations = null;
 			if (chartId && crosshairStore.sourceChartId === chartId) crosshairStore.clearHover();
 			if (chartId && rangeSelection.selection?.sourceChartId === chartId) rangeSelection.clear();
 		};
 	}
 
-	function attachLifetime() {
-		return () => {
-			cleanupRender();
-			contract?.destroy();
-			contract = null;
-			contractSurface = null;
-			context = null;
-		};
-	}
-
 	function publish(next: ChartRenderContext<PlotPoint, number, number>) {
 		cleanupRender();
+		paintSpectrumCloud(next, series);
 		context = next;
 		const surface = next.surface.element;
 		if (!(surface instanceof HTMLElement || surface instanceof SVGElement))
@@ -118,27 +135,7 @@
 			contract = createChartContract(surface);
 			contractSurface = surface;
 		}
-		const seen = new SvelteSet<PlotPoint>();
-		const positioned = new SvelteMap<string, ChartPoint<PlotPoint, number, number>[]>();
-		for (const point of next.scene.points) {
-			if (
-				seen.has(point.datum) ||
-				!finitePoint(point.datum) ||
-				!Number.isFinite(point.x) ||
-				!Number.isFinite(point.y)
-			)
-				continue;
-			if (
-				point.x < next.scene.chart.x - 0.01 ||
-				point.x > next.scene.chart.x + next.scene.chart.width + 0.01
+		const positioned = positionedScenePoints(next.scene);
-			)
-				continue;
-			seen.add(point.datum);
-			const markId = point.markId.replace(/-(complete|partial|run-\d+)$/, '');
-			const points = positioned.get(markId) ?? [];
-			points.push(point);
-			positioned.set(markId, points);
-		}
 		const buttons = Array.from(
 			next.container.querySelectorAll<HTMLButtonElement>('[data-chart-legend-value]')
 		);
@@ -146,12 +143,14 @@
 			const item = series.find((item) => item.label === button.dataset.chartLegendValue);
 			if (item) paintLegendSwatch(button, item, options.kind);
 		}
+		const bounds = surface.getBoundingClientRect();
-
 		contract?.render({
 			name,
 			kind: options.kind === 'coverage' ? 'line' : (options.kind ?? 'line'),
 			axes:
-				options.compact && size.width < 560 ? [] : [options.xTitle, options.yTitle].filter(Boolean),
+				options.compact && next.scene.width < 560
+					? []
+					: [options.xTitle, options.yTitle].filter(Boolean),
 			series: series.map((item, index) => {
 				const visible = !hidden.has(item.label);
 				const points = positioned.get(`series-${index}`) ?? [];
@@ -173,11 +172,8 @@
 				const finite = values.filter(
 					(value): value is number => typeof value === 'number' && Number.isFinite(value)
 				);
-				const button = Array.from(
+				const button = buttons.find((button) => button.dataset.chartLegendValue === item.label);
-					next.container.querySelectorAll<HTMLButtonElement>('[data-chart-legend-value]')
-				).find((button) => button.dataset.chartLegendValue === item.label);
 				const box = button?.getBoundingClientRect();
-				const bounds = surface.getBoundingClientRect();
 				const target = points[0];
 				return {
 					label: item.label,
@@ -248,8 +244,11 @@
 	function atPixel(x: number): PlotPoint | null {
 		if (!context) return null;
 		const value = context.scene.scales.x.invert?.(x);
+		const index =
+			typeof value === 'number'
+				? findNearestValueIndex(observations?.timestamps ?? [], value)
+				: null;
+		return index === null ? null : (observations?.timeline[index] ?? null);
-		const index = typeof value === 'number' ? findNearestValueIndex(timestamps, value) : null;
-		return index === null ? null : timeline[index];
 	}
 
 	function pointerPosition(event: MouseEvent) {
@@ -341,8 +340,8 @@
 	function paintSelection() {
 		if (!selection || !context) return;
 		const mirrored = rangeSelection.selection;
+		const from = observations?.labeledPoints.get(mirrored?.startLabel);
-		const from = labeledPoints.get(mirrored?.startLabel);
+		const to = observations?.labeledPoints.get(mirrored?.endLabel);
-		const to = labeledPoints.get(mirrored?.endLabel);
 		const left = rangeStart ?? (from ? context.scene.scales.x.map(from.x) : null);
 		const right = rangeEnd ?? (to ? context.scene.scales.x.map(to.x) : null);
 		selection.hidden = left === null || right === null || Math.abs(right - left) < MIN_DRAG_PIXELS;
@@ -353,7 +352,7 @@
 	function paintExternalHover() {
 		if (!externalCrosshair || !context) return;
 		const label = chartId ? crosshairStore.getExternalLabel(chartId) : null;
-		const point = label ? labeledPoints.get(label) : null;
+		const point = label ? observations?.labeledPoints.get(label) : null;
 		externalCrosshair.hidden = !point;
 		if (point)
 			externalCrosshair.style.cssText = `left:${context.scene.scales.x.map(point.x)}px;top:${context.scene.chart.y}px;height:${context.scene.chart.height}px`;
@@ -397,7 +396,7 @@
 		if (!event.shiftKey || !['ArrowLeft', 'ArrowRight'].includes(event.key)) return;
 		event.preventDefault();
 		event.stopImmediatePropagation();
-		const points = timeline;
+		const points = observations?.timeline ?? [];
 		const index = Math.max(
 			0,
 			points.findIndex((point) => point.x === focused?.x)
@@ -421,27 +420,10 @@
 	}
 </script>
 
+<div class="relative h-full w-full min-w-0">
-<div class="relative h-full w-full min-w-0" {@attach attachSize}>
 	{#if hasData || retainEmptySurface}
+		<div class="ts-chart-host h-full">
-		<div class="h-full" {@attach attachLifetime}>
-			<Chart
-				{definition}
-				ariaLabel={name}
-				height={size.height}
-				onRender={publish}
-				onFocusGroupChange={focusGroup}
-				onSelect={select}
-			>
-				{#snippet tooltipBody({ points, defaultBody })}
-					{#if options.kind === 'scatter'}
-						<div role="tooltip" class="whitespace-pre-line">
-							{plotTooltip(points, formatTooltip)}
-						</div>
-					{:else}
-						{@render defaultBody()}
+			<div class="ts-chart-surface h-full w-full" {@attach attachPlot}></div>
-					{/if}
-				{/snippet}
-			</Chart>
 		</div>
 		{#if !hasData}<p
 				class="text-muted-foreground pointer-events-none absolute inset-0 flex items-center justify-center px-4 text-center text-sm"
```

## apps/web/src/lib/components/charts/chart-observations.ts

Status: added; +42; −0

```diff
@@ -1,0 +1,42 @@
+import type { ChartPoint, ChartScene } from '@tanstack/charts';
+import { finitePoint, type PlotPoint, type PlotSeries } from './chart-registry';
+
+export function plotTimeline(series: PlotSeries[], hidden: ReadonlySet<string>) {
+	const unique = new Map<number, PlotPoint>();
+	for (const item of series) {
+		if (hidden.has(item.label)) continue;
+		for (const point of item.data) if (finitePoint(point)) unique.set(point.x, point);
+	}
+	return [...unique.values()].sort((left, right) => left.x - right.x);
+}
+
+export function plotObservations(series: PlotSeries[], hidden: ReadonlySet<string>) {
+	const timeline = plotTimeline(series, hidden);
+	return {
+		timeline,
+		timestamps: timeline.map((point) => point.x),
+		labeledPoints: new Map(timeline.map((point) => [point.label, point]))
+	};
+}
+
+export function positionedScenePoints(scene: ChartScene<PlotPoint, number, number>) {
+	const seen = new Set<PlotPoint>();
+	const positioned = new Map<string, ChartPoint<PlotPoint, number, number>[]>();
+	for (const point of scene.points) {
+		if (
+			seen.has(point.datum) ||
+			!finitePoint(point.datum) ||
+			!Number.isFinite(point.x) ||
+			!Number.isFinite(point.y) ||
+			point.x < scene.chart.x - 0.01 ||
+			point.x > scene.chart.x + scene.chart.width + 0.01
+		)
+			continue;
+		seen.add(point.datum);
+		const markId = point.markId.replace(/-(complete|partial|run-\d+)$/, '');
+		const points = positioned.get(markId) ?? [];
+		points.push(point);
+		positioned.set(markId, points);
+	}
+	return positioned;
+}
```

## apps/web/src/lib/components/charts/chart-registry.ts

Status: modified; +76; −9

```diff
@@ -1,4 +1,4 @@
-import { defineChart } from '@tanstack/charts';
+import { defineChart } from '@tanstack/charts/scene';
 import { lineY } from '@tanstack/charts/line';
 import { areaY } from '@tanstack/charts/area';
 import { rect } from '@tanstack/charts/rect';
@@ -14,10 +14,66 @@
 import type { ChartMark, ChartPoint } from '@tanstack/charts';
 import { temporalTicks } from './temporal-ticks';
 import { temporalGrid } from './temporal-grid';
+import { spectrumCloud } from './spectrum-cloud';
 import { coverageLineRuns } from './coverage-line-style';
 import type { ChartCoverage } from './chart-utils';
 
+const spectrumTooltip: typeof tooltip = {
+	...tooltip,
+	create(context) {
+		const instance = tooltip.create(context);
+		return {
+			...instance,
+			paint(input) {
+				instance.paint(input);
+				const element = context.container.querySelector<HTMLElement>('.ts-chart-tooltip');
+				if (element) {
+					element.setAttribute('role', 'tooltip');
+					element.style.whiteSpace = 'pre-line';
+				}
+			}
+		};
+	}
+};
+
+const focusRings: ChartMark<never, never, never> = {
+	initialize: () => ({
+		id: 'focus-rings',
+		focusGuideOnly: true,
+		channels: {},
+		render: ({ chart, surface }) => ({
+			nodes: [],
+			focusGuides: [
+				{
+					key: 'focus-rings',
+					markId: 'focus-rings',
+					chart,
+					surface,
+					resolve: ({ focus }) =>
+						focus
+							? {
+									kind: 'group',
+									key: 'focus-rings',
+									ariaHidden: true,
+									children: [focus.primary].map((point) => ({
+										kind: 'dot',
+										key: point.key,
+										x: point.x,
+										y: point.y,
+										radius: 5,
+										style: {
+											fill: 'var(--ts-chart-focus-fill, Canvas)',
+											stroke: point.color,
+											strokeWidth: 2.5
+										}
+									}))
+								}
+							: undefined
+				}
+			]
+		})
-export { Chart } from '@tanstack/charts/svelte';
+	})
+};
 
 export type PlotPoint = {
 	x: number;
@@ -124,11 +180,13 @@
 		let displayedData = item.data;
 		if (options.kind === 'scatter') {
 			marks.push(
-				dot(item.data, {
-					...channels,
-					color: options.colorDomain ? (point) => point.f ?? 0 : channels.color,
-					r: item.radius ?? 1
+				options.colorDomain && item.data.length > 2048
+					? spectrumCloud(item.data, id, item.label)
+					: dot(item.data, {
+							...channels,
+							color: options.colorDomain ? (point) => point.f ?? 0 : channels.color,
+							r: item.radius ?? 1
-				})
+						})
 			);
 		} else if (options.kind === 'coverage') {
 			const track = options.xDomain ?? plotBounds(series);
@@ -260,6 +318,7 @@
 		);
 	}
 	marks.push(
+		focusRings,
 		crosshair({ y: false, stroke: 'currentColor', strokeOpacity: 0.65, strokeDasharray: '3 3' })
 	);
 	const xDomain = options.xDomain ?? plotBounds(series);
@@ -282,7 +341,14 @@
 			? scaleLinear
 			: scaleLinear().domain([minY, maxY === minY ? maxY + 1 : maxY]);
 	const labels = series.map((item) => item.label);
+	const formattedTicks = new Map<number, string>();
+	const formatter = options.xFormat;
+	const formatX = formatter
+		? (value: number) => {
+				if (!formattedTicks.has(value)) formattedTicks.set(value, formatter(value));
-	const formatX = options.xFormat;
+				return formattedTicks.get(value) ?? '';
+			}
+		: undefined;
 	const timeAxis = Boolean(options.xTicks && formatX);
 	const xTicks =
 		options.xTicks && formatX
@@ -368,9 +434,10 @@
 									height: (count, context) => Math.min(108, legend.height(count, context))
 								}
 				},
+		focusRing: false,
 		focus: options.kind === 'scatter' ? undefined : focusGroupX,
 		tooltip: {
-			use: tooltip,
+			use: options.kind === 'scatter' ? spectrumTooltip : tooltip,
 			formatGroup: (points) => plotTooltip(points, formatTooltip),
 			motion: false
 		},
```

## apps/web/src/lib/components/charts/coverage-marks.ts

Status: added; +32; −0

```diff
@@ -1,0 +1,32 @@
+import { renderChartSvg } from '@tanstack/charts/svg';
+import type { SceneNode } from '@tanstack/charts';
+
+export const renderCoverageSvg: typeof renderChartSvg = (scene, options) => {
+	function batch(nodes: readonly SceneNode[]): SceneNode[] {
+		return nodes.map((node) => {
+			if (node.kind !== 'group') return node;
+			if (node.className !== 'ts-chart__rect') return { ...node, children: batch(node.children) };
+			const rectangles = node.children.filter((child) => child.kind === 'rect');
+			return {
+				...node,
+				children: rectangles.length
+					? [
+							{
+								kind: 'area',
+								key: `${node.key}-segments`,
+								points: [],
+								style: rectangles[0].style,
+								path: rectangles
+									.map(({ x, y, width, height }) => {
+										const round = (value: number) => Math.round(value * 100) / 100;
+										return `M${round(x)},${round(y)}h${round(width)}v${round(height)}h${-round(width)}Z`;
+									})
+									.join('')
+							}
+						]
+					: []
+			};
+		});
+	}
+	return renderChartSvg({ ...scene, nodes: batch(scene.nodes) }, options);
+};
```

## apps/web/src/lib/components/charts/spectrum-cloud.ts

Status: added; +95; −0

```diff
@@ -1,0 +1,95 @@
+import type { ChartMark, ChartPoint, ChartRenderContext } from '@tanstack/charts';
+import type { PlotPoint, PlotSeries } from './chart-registry';
+
+export function spectrumCloud(
+	data: PlotPoint[],
+	id: string,
+	label: string
+): ChartMark<PlotPoint, number, number> {
+	return {
+		initialize: () => ({
+			id,
+			channels: {
+				x: { scale: 'x', values: data.map((point) => point.x) },
+				y: { scale: 'y', values: data.flatMap((point) => (point.y === null ? [] : [point.y])) },
+				color: { scale: 'color', values: data.map((point) => point.f ?? 0) }
+			},
+			render: ({ scales, color }) => {
+				const points: ChartPoint<PlotPoint, number, number>[] = [];
+				for (const [datumIndex, datum] of data.entries()) {
+					if (datum.y === null) continue;
+					const x = scales.x.map(datum.x);
+					const y = scales.y.map(datum.y);
+					if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
+					points.push({
+						key: `${id}:${datumIndex}`,
+						markId: id,
+						group: label,
+						groupLabel: label,
+						datum,
+						datumIndex,
+						xValue: datum.x,
+						yValue: datum.y,
+						x,
+						y,
+						color: color(datum.f ?? 0)
+					});
+				}
+				return {
+					points,
+					nodes: [
+						{
+							kind: 'group',
+							key: id,
+							className: 'atlantis-spectrum-cloud',
+							ariaHidden: true,
+							children: []
+						}
+					]
+				};
+			}
+		})
+	};
+}
+
+export function paintSpectrumCloud(
+	context: ChartRenderContext<PlotPoint, number, number>,
+	series: PlotSeries[]
+) {
+	for (const group of context.svg.querySelectorAll<SVGGElement>('.atlantis-spectrum-cloud')) {
+		const { width, height } = context.scene;
+		const ratio = context.container.ownerDocument.defaultView?.devicePixelRatio ?? 1;
+		const layer = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
+		layer.setAttribute('x', '0');
+		layer.setAttribute('y', '0');
+		layer.setAttribute('width', String(width));
+		layer.setAttribute('height', String(height));
+		layer.setAttribute('pointer-events', 'none');
+		const canvas = document.createElement('canvas');
+		canvas.setAttribute('aria-hidden', 'true');
+		canvas.width = Math.ceil(width * ratio);
+		canvas.height = Math.ceil(height * ratio);
+		canvas.style.width = `${width}px`;
+		canvas.style.height = `${height}px`;
+		const painter = canvas.getContext('2d');
+		if (!painter) throw new Error('Spectrum canvas rendering is unavailable');
+		painter.scale(ratio, ratio);
+		const markId = group.getAttribute('data-ts-key');
+		const radius = series.find((_, index) => markId === `series-${index}`)?.radius ?? 1;
+		for (const point of context.scene.points) {
+			if (point.markId !== markId) continue;
+			painter.fillStyle = point.color;
+			painter.beginPath();
+			painter.arc(
+				Math.round(point.x * 100) / 100,
+				Math.round(point.y * 100) / 100,
+				radius,
+				0,
+				Math.PI * 2
+			);
+			painter.fill();
+		}
+		layer.append(canvas);
+		group.replaceChildren(layer);
+	}
+}
```

## apps/web/src/lib/components/netflow/NetflowFileDetailPage.svelte

Status: modified; +0; −5

```diff
@@ -23,7 +23,6 @@
 		epochToPSTComponents,
 		formatTimestampAsPST
 	} from '#lib/utils/timezone.ts';
-	import { onMount } from 'svelte';
 
 	type NetflowFileDetailData = {
 		dataset: string;
@@ -123,10 +122,6 @@
 		);
 	}
 
-	onMount(() => {
-		syncLoader();
-	});
-
 	afterNavigate(() => {
 		syncLoader();
 	});
```

## apps/web/src/lib/datasets.ts

Status: modified; +6; −6

```diff
@@ -1,20 +1,20 @@
-import { z } from 'zod';
+import { z } from 'zod/mini';
 import type { DatasetSummariesResponse, DatasetSummary } from '#lib/types/types.ts';
 
 export type { DatasetSummariesResponse, DatasetSummary } from '#lib/types/types.ts';
 
 const datasetSummarySchema = z.object({
-	datasetId: z.string().min(1),
+	datasetId: z.string().check(z.minLength(1)),
+	label: z.string().check(z.minLength(1)),
-	label: z.string().min(1),
 	defaultStartDate: z.iso.date(),
-	discoveryMode: z.string().min(1),
+	discoveryMode: z.string().check(z.minLength(1)),
 	hasLocality: z.boolean(),
 	isDefault: z.boolean()
 });
 
 const datasetSummariesResponseSchema = z.object({
-	data: z.array(datasetSummarySchema).nullable(),
+	data: z.nullable(z.array(datasetSummarySchema)),
+	error: z.nullable(z.string())
-	error: z.string().nullable()
 });
 
 let cachedDatasetSummaries: DatasetSummary[] | null = null;
```

## apps/web/src/lib/schemas.ts

Status: modified; +14; −11

```diff
@@ -9,7 +9,7 @@
 	type MaadIpVersion,
 	type MaadMeasure
 } from '#lib/types/types.ts';
-import { z } from 'zod';
+import { z } from 'zod/mini';
 
 export type DateRangeSearch = {
 	startDate: string;
@@ -37,17 +37,20 @@
 	today = new Date().toJSON().slice(0, 10)
 ) {
 	const schema = z.object({
+		startDate: z.catch(z.iso.date(), defaultStartDate),
+		endDate: z.catch(z.iso.date(), today),
+		groupBy: z.catch(z.enum(GROUP_BY_OPTIONS), 'date'),
+		direction: z.catch(z.enum(FLOW_DIRECTIONS), 'all'),
+		ipVersion: z.catch(
+			z.pipe(
+				z.enum(IP_VERSION_PARAMS),
+				z.transform((value) => Number(value) as MaadIpVersion)
+			),
+			DEFAULT_MAAD_IP_VERSION
+		),
-		startDate: z.iso.date().catch(defaultStartDate),
-		endDate: z.iso.date().catch(today),
-		groupBy: z.enum(GROUP_BY_OPTIONS).catch('date'),
-		direction: z.enum(FLOW_DIRECTIONS).catch('all'),
-		ipVersion: z
-			.enum(IP_VERSION_PARAMS)
-			.transform((value) => Number(value) as MaadIpVersion)
-			.catch(DEFAULT_MAAD_IP_VERSION),
-		measure: z.enum(MAAD_MEASURES).catch(DEFAULT_MAAD_MEASURE)
+		measure: z.catch(z.enum(MAAD_MEASURES), DEFAULT_MAAD_MEASURE)
 	});
+	const keys = z.keyof(schema).options;
-	const keys = schema.keyof().options;
 	const defaults: DateRangeSearch = schema.parse({});
 
 	function parse(searchParams: SearchParamsReader): DateRangeSearch {
```

## apps/web/src/routes/api/netflow/spectrum-stats/\+server.ts

Status: modified; +6; −2

```diff
@@ -27,6 +27,10 @@
 	const params = parseMaadStatsParams(url);
 	if ('error' in params) {
 		return Response.json({ error: params.error }, { status: params.status });
+	}
+	const addressSide = url.searchParams.get('addressSide');
+	if (addressSide !== null && addressSide !== 'source' && addressSide !== 'destination') {
+		return Response.json({ error: 'Invalid addressSide' }, { status: 400 });
 	}
 	const measureError = spectrumMeasureError(params.measure);
 	if (measureError) {
@@ -55,8 +59,8 @@
 					${sourceColumn} AS router,
 					bucket_start AS bucketStart,
 					MAX(bucket_end) AS bucketEnd,
-					MAX(CASE WHEN address_side = 'source' THEN spectrum END) AS saSpectrum,
-					MAX(CASE WHEN address_side = 'destination' THEN spectrum END) AS daSpectrum
+					${addressSide === 'destination' ? 'NULL' : "MAX(CASE WHEN address_side = 'source' THEN spectrum END)"} AS saSpectrum,
+					${addressSide === 'source' ? 'NULL' : "MAX(CASE WHEN address_side = 'destination' THEN spectrum END)"} AS daSpectrum
 				FROM ${tableName}
 				WHERE granularity = ?
 					AND ${sourceColumn} IN (${placeholders(routers)})
```

## apps/web/tests/e2e/README.md

Status: modified; +1; −1

```diff
@@ -23,7 +23,7 @@
 | `data-testid="chart-card-state"`       | Card lifecycle: `loading`, `error`, `no-metrics`, `empty`, `unavailable`, or `ready`. Card `ready` means its content branch is active; the renderer summary proves finite data actually rendered.                                                                                                          |
 | `data-testid="coverage-strip-card"`    | Coverage card retains its test ID and publishes `loading`, `error`, `empty`, or `ready`.                                                                                                                                                                                                                   |
 
-`chart-contract.ts` publishes a renderer-independent DOM summary with memory proportional to series and axes. `ChartPlot.svelte` publishes from the official TanStack Svelte adapter after layout and rendering. All charts use SVG after comparing SVG and canvas costs on real v5 points. Do not synthesize readiness solely from an API response or from a mounted graphics element. The existing spectrum tooltip and loading/empty/unavailable copy remain user-facing contracts.
+`chart-contract.ts` publishes a renderer-independent DOM summary with memory proportional to series and axes. `ChartPlot.svelte` mounts TanStack through its DOM API in a Svelte attachment and publishes after layout and painting. Sparse charts use SVG. Dense time-spectrum marks paint every observation into a canvas inside the SVG plot group; axes, focus overlays and interactions remain in TanStack. Coverage paths batch their SVG output while retaining native rectangle hit geometry and each bucket observation. Do not synthesize readiness solely from an API response or from a mounted graphics element. The existing spectrum tooltip and loading/empty/unavailable copy remain user-facing contracts.
 
 ## Numerical and interaction assertions
 
```

## apps/web/tests/e2e/chart-fidelity.spec.ts

Status: modified; +52; −1

```diff
@@ -1,4 +1,4 @@
-import { expect, test } from '@playwright/test';
+import { expect, test, type Page } from '@playwright/test';
 import { activateChart, expectRendered } from './chart-helpers';
 
 test('time axes keep midnight and noon labels with bounded grid geometry', async ({ page }) => {
@@ -74,3 +74,54 @@
 		await expect(structure.locator('text').filter({ hasText: /^4\.1$/ })).toBeAttached();
 	});
 }
+
+async function expectLocalClipPaths(page: Page) {
+	const clips = await page.getByTestId('chart-surface').evaluateAll((surfaces) =>
+		surfaces.flatMap((surface) =>
+			Array.from(surface.querySelectorAll('g[clip-path]')).map((group) => {
+				const id = group.getAttribute('clip-path')?.match(/^url\(#(.+)\)$/)?.[1];
+				const local = Array.from(surface.querySelectorAll('clipPath')).find(
+					(clip) => clip.id === id
+				);
+				return {
+					id,
+					local: Boolean(local),
+					resolvedLocally: Boolean(local && document.getElementById(id ?? '')?.isSameNode(local))
+				};
+			})
+		)
+	);
+	expect(clips.length).toBeGreaterThan(1);
+	expect(new Set(clips.map((clip) => clip.id)).size).toBe(clips.length);
+	for (const clip of clips) {
+		expect(clip.local).toBe(true);
+		expect(clip.resolvedLocally).toBe(true);
+	}
+}
+
+test('each dashboard and file chart resolves its own clipping rectangle', async ({ page }) => {
+	await page.goto('/datasets/playwright?startDate=2025-03-01&endDate=2025-03-02&groupBy=10min');
+	for (const id of [
+		'dashboard',
+		'characteristics',
+		'ports',
+		'ip',
+		'protocol',
+		'dimensions',
+		'spectrum',
+		'coverage'
+	]) {
+		const card = await activateChart(page, id);
+		await expectRendered(card, id === 'characteristics' ? 2 : 1);
+	}
+	await expectLocalClipPaths(page);
+	await page.getByRole('button', { name: 'Switch to dark mode' }).click();
+	await expect(page.locator('html')).toHaveClass(/dark/);
+	await expectLocalClipPaths(page);
+	await page.goto('/netflow/files/202503010200?dataset=playwright');
+	await expect(page.getByTestId('chart-surface').first()).toHaveAttribute(
+		'data-chart-rendered',
+		'true'
+	);
+	await expectLocalClipPaths(page);
+});
```

## apps/web/tests/e2e/chart-migration.spec.ts

Status: modified; +35; −0

```diff
@@ -239,3 +239,38 @@
 	await expect(card.getByTestId('chart-axis').filter({ hasText: '10 Minutes' })).toBeAttached();
 	await expect(tcp).toHaveAttribute('data-total', '100');
 });
+
+test('filter transitions release detached chart DOM and listeners', async ({ page }) => {
+	await page.goto(FIXTURE_DASHBOARD);
+	for (const id of [
+		'dashboard',
+		'characteristics',
+		'ports',
+		'ip',
+		'protocol',
+		'dimensions',
+		'spectrum',
+		'coverage'
+	]) {
+		await activateChart(page, id);
+	}
+	const direction = page.getByRole('group', { name: 'Traffic direction' });
+	async function change(name: 'Ingress' | 'All') {
+		await direction.getByRole('button', { name, exact: true }).click();
+		await page.waitForLoadState('networkidle');
+	}
+	await change('Ingress');
+	await change('All');
+	const cdp = await page.context().newCDPSession(page);
+	async function retainedDOM() {
+		await cdp.send('HeapProfiler.collectGarbage');
+		return cdp.send('Memory.getDOMCounters');
+	}
+	const before = await retainedDOM();
+	for (let cycle = 0; cycle < 6; cycle++) {
+		await change(cycle % 2 ? 'All' : 'Ingress');
+	}
+	const after = await retainedDOM();
+	expect(after.nodes).toBeLessThanOrEqual(before.nodes + 100);
+	expect(after.jsEventListeners).toBeLessThanOrEqual(before.jsEventListeners + 3);
+});
```

## apps/web/tests/e2e/coverage-strip.spec.ts

Status: modified; +18; −0

```diff
@@ -21,3 +21,21 @@
 
 	expect(heightAfterHover).toBe(heightBeforeHover);
 });
+
+test('wide coverage intervals retain hover throughout their interiors', async ({ page }) => {
+	await page.goto('/datasets/playwright?startDate=2025-03-01&endDate=2025-03-01');
+	const card = page.locator('[data-chart-id="coverage"]');
+	await card.scrollIntoViewIfNeeded();
+	const surface = card.getByTestId('chart-surface');
+	await expect(surface).toHaveAttribute('data-chart-rendered', 'true');
+	const target = card.locator('[data-testid="chart-series"][data-point-x]').first();
+	const x = Number(await target.getAttribute('data-point-x'));
+	const y = Number(await target.getAttribute('data-point-y'));
+	const box = await surface.boundingBox();
+	if (!box) throw new Error('Missing coverage surface');
+	for (const offset of [-100, 0, 100]) {
+		await surface.hover({ position: { x: x * box.width + offset, y: y * box.height } });
+		await expect(card.getByTestId('chart-tooltip')).toContainText('2025-03-01');
+		await expect(card.getByTestId('chart-tooltip')).toContainText('fixture-router');
+	}
+});
```

## apps/web/tests/e2e/navigation-baseline.spec.ts

Status: modified; +10; −0

```diff
@@ -57,12 +57,17 @@
 });
 
 test('file next navigation keeps filters and explains absent MAAD analyses', async ({ page }) => {
+	const requests: string[] = [];
+	page.on('request', (request) => {
+		if (/\/api\/netflow\/files\/.*\/details\?/.test(request.url())) requests.push(request.url());
+	});
 	await page.goto('/netflow/files/202503010200?dataset=playwright&measure=bytes');
 	await expect(page.getByRole('button', { name: 'Bytes', exact: true })).toHaveAttribute(
 		'aria-pressed',
 		'true'
 	);
 	await expectRendered(page.locator('main'), 2);
+	await expect.poll(() => requests.length).toBe(1);
 	await expect(
 		page.getByText('The spectrum is only computed for the addresses measure.')
 	).toBeVisible();
@@ -71,8 +76,13 @@
 	await expect(page.getByText('No source structure data.', { exact: true })).toBeVisible();
 	await expect(page.getByText('No destination structure data.', { exact: true })).toBeVisible();
 	await expect(page.getByRole('button', { name: 'Reload', exact: true })).toHaveCount(2);
+	await expect.poll(() => requests.length).toBe(2);
 	await page.goBack();
 	await expectRendered(page.locator('main'), 2);
+	await expect.poll(() => requests.length).toBe(3);
+	await page.reload();
+	await expectRendered(page.locator('main'), 2);
+	await expect.poll(() => requests.length).toBe(4);
 });
 
 test('file summary request errors recover through Retry Summary', async ({ page }) => {
```

## apps/web/tests/lib/coverage-marks.test.ts

Status: added; +69; −0

```diff
@@ -1,0 +1,69 @@
+import { expect, it } from 'vitest';
+import { rect } from '@tanstack/charts/rect';
+import { createChartScene, defineChart, findNearestPoint } from '@tanstack/charts/scene';
+import { scaleLinear } from '@tanstack/charts/scales/linear';
+import { renderCoverageSvg } from '#lib/components/charts/coverage-marks.ts';
+import type { PlotPoint } from '#lib/components/charts/chart-registry.ts';
+
+it('retains each coverage interval and ratio when batching the painted segments', () => {
+	const data: PlotPoint[] = [
+		{
+			x: 0,
+			x2: 10,
+			y: 0,
+			value: 1,
+			coverage: { state: 'complete', observedUnits: 10, expectedUnits: 10 }
+		},
+		{
+			x: 10,
+			x2: 20,
+			y: 0,
+			value: 1,
+			coverage: { state: 'complete', observedUnits: 10, expectedUnits: 10 }
+		}
+	];
+	const scene = createChartScene(
+		defineChart({
+			marks: [
+				rect(data, {
+					id: 'coverage',
+					x: 'x',
+					x1: 'x',
+					x2: 'x2',
+					y: 'y',
+					y1: () => -0.1,
+					y2: () => 0.1,
+					fill: 'green',
+					inset: 0
+				})
+			],
+			scales: {
+				x: { scale: scaleLinear().domain([0, 20]) },
+				y: { scale: scaleLinear().domain([-1, 1]) }
+			},
+			focusRing: false
+		}),
+		{ width: 400, height: 100 }
+	);
+	const svg = renderCoverageSvg(scene, { ariaLabel: 'Coverage' });
+	expect(svg).toContain('coverage-segments');
+	expect(svg).not.toContain('<rect');
+	expect(scene.points).toHaveLength(2);
+	for (const point of scene.points) {
+		for (const fraction of [0.01, 0.5, 0.99]) {
+			const x =
+				scene.scales.x.map(Number(point.x1Value)) +
+				(scene.scales.x.map(Number(point.x2Value)) - scene.scales.x.map(Number(point.x1Value))) *
+					fraction;
+			expect(findNearestPoint(scene, x, point.y, 48)).toBe(point);
+		}
+	}
+	expect(scene.points.map((point) => [point.x1Value, point.x2Value, point.datum.value])).toEqual([
+		[0, 10, 1],
+		[10, 20, 1]
+	]);
+	expect(scene.points[0].datum).toBe(data[0]);
+	expect(scene.points[1].datum).toBe(data[1]);
+	expect(scene.points[0].x).toBeCloseTo(scene.chart.x + scene.chart.width / 4);
+	expect(scene.points[1].x).toBeCloseTo(scene.chart.x + (scene.chart.width * 3) / 4);
+});
```

## apps/web/tests/lib/schemas.test.ts

Status: modified; +17; −0

```diff
@@ -32,6 +32,23 @@
 		});
 	});
 
+	it('preserves Gregorian date validation, including leap years and four-digit years', () => {
+		for (const date of [
+			'2025-02-29',
+			'2024-02-30',
+			'2025-04-31',
+			'2025-13-01',
+			'2025-00-01',
+			'2025-01-00',
+			'2025-1-01'
+		]) {
+			expect(search.parse(new URLSearchParams({ startDate: date })).startDate).toBe('2025-01-01');
+		}
+		for (const date of ['2024-02-29', '2000-02-29', '0000-01-01', '9999-12-31']) {
+			expect(search.parse(new URLSearchParams({ startDate: date })).startDate).toBe(date);
+		}
+	});
+
 	it('accepts every dashboard grouping including 10 minutes', () => {
 		for (const groupBy of ['date', 'hour', '30min', '10min', '5min']) {
 			expect(search.parse(new URLSearchParams({ groupBy })).groupBy).toBe(groupBy);
```

## apps/web/tests/lib/server/db/drivers.test.ts

Status: modified; +71; −0

```diff
@@ -1,4 +1,5 @@
 import Database from 'better-sqlite3';
+import type { SpectrumStatsResponse } from '#lib/types/spectrum-stats.ts';
 import { afterEach, describe, expect, it, vi } from 'vitest';
 import {
 	cleanupPlaywrightDatabase,
@@ -133,6 +134,76 @@
 		}
 	});
 
+	it.each(['sqlite', 'd1'] as const)(
+		'preserves selected-side spectra, half-open buckets and coverage in %s projections',
+		async (driver) => {
+			const databasePath = seedDatabase();
+			const fixture = new Database(databasePath);
+			try {
+				fixture
+					.prepare(
+						"UPDATE address_maad_stats SET spectrum = ? WHERE address_side = 'destination' AND measure = 'addresses'"
+					)
+					.run(Buffer.from(Float32Array.from([3, 4]).buffer));
+				const row = fixture
+					.prepare(
+						"SELECT * FROM address_maad_stats WHERE address_side = 'destination' AND ip_version = 4 AND measure = 'addresses' AND granularity = '5m' AND src_locality = 'all' AND dst_locality = 'all' LIMIT 1"
+					)
+					.get() as Record<string, unknown>;
+				row.bucket_start = 1740823500;
+				row.bucket_end = 1740823800;
+				const columns = Object.keys(row);
+				fixture
+					.prepare(
+						`INSERT OR REPLACE INTO address_maad_stats (${columns.join(',')}) VALUES (${columns.map(() => '?').join(',')})`
+					)
+					.run(...Object.values(row));
+			} finally {
+				fixture.close();
+			}
+			const route = () => import('../../../../src/routes/api/netflow/spectrum-stats/+server.ts');
+			const url =
+				'http://localhost/api/netflow/spectrum-stats?dataset=playwright&routers=fixture-router&granularity=5m&startDate=1740822900&endDate=1740823800';
+			const full = await callRoute(driver, databasePath, route, url);
+			expect(full.status).toBe(200);
+			const body = full.body as SpectrumStatsResponse;
+			const buckets = body.timelines[0].buckets;
+			expect(buckets.map((bucket) => bucket.bucketStart)).toEqual([
+				1740822900, 1740823200, 1740823500
+			]);
+			expect(buckets[1].data?.spectrumSa).not.toEqual(buckets[1].data?.spectrumDa);
+			expect(buckets[2].data?.spectrumSa).toEqual([]);
+			expect(buckets[2].data?.spectrumDa).toEqual([{ alpha: 3, f: 4 }]);
+			for (const addressSide of ['source', 'destination']) {
+				const projected = await callRoute(
+					driver,
+					databasePath,
+					route,
+					`${url}&addressSide=${addressSide}`
+				);
+				expect(projected.status).toBe(200);
+				expect(projected.body).toEqual({
+					...body,
+					timelines: body.timelines.map((timeline) => ({
+						...timeline,
+						buckets: timeline.buckets.map((bucket) => ({
+							...bucket,
+							data:
+								bucket.data === null
+									? null
+									: {
+											spectrumSa: addressSide === 'source' ? bucket.data.spectrumSa : [],
+											spectrumDa: addressSide === 'destination' ? bucket.data.spectrumDa : []
+										}
+						}))
+					}))
+				});
+			}
+			const invalid = await callRoute(driver, databasePath, route, `${url}&addressSide=invalid`);
+			expect(invalid.status).toBe(400);
+		}
+	);
+
 	it('export the same driver interface', async () => {
 		vi.resetModules();
 		const [{ default: sqlite }, { default: d1 }] = await Promise.all([
```

## apps/web/tests/lib/spectrum-cloud.test.ts

Status: added; +41; −0

```diff
@@ -1,0 +1,41 @@
+import { describe, expect, it } from 'vitest';
+import { createChartScene, defineChart } from '@tanstack/charts/scene';
+import { scaleLinear } from '@tanstack/charts/scales/linear';
+import { scaleSequential } from 'd3-scale';
+import { spectrumCloud } from '#lib/components/charts/spectrum-cloud.ts';
+
+describe('dense spectrum scene', () => {
+	it('keeps every finite observation, color, bucket identity and position without SVG point geometry', () => {
+		const data = Array.from({ length: 3000 }, (_, index) => ({
+			x: index % 3,
+			y: index / 3000,
+			f: index / 3000,
+			label: `bucket-${index % 3}`
+		}));
+		const scene = createChartScene(
+			defineChart({
+				marks: [
+					spectrumCloud([...data, { x: 0, y: NaN, f: 0, label: 'invalid' }], 'series-0', 'Spectrum')
+				],
+				scales: {
+					x: { scale: scaleLinear().domain([0, 2]) },
+					y: { scale: scaleLinear().domain([0, 1]) }
+				},
+				color: { scale: scaleSequential((value) => `rgb(${value * 255},0,0)`).domain([0, 1]) },
+				focusRing: false
+			}),
+			{ width: 600, height: 300 }
+		);
+		expect(scene.points).toHaveLength(3000);
+		expect(new Set(scene.points.map((point) => point.key)).size).toBe(3000);
+		for (const index of [0, 1499, 2999]) {
+			const point = scene.points[index];
+			expect(point.datum).toBe(data[index]);
+			expect(point.xValue).toBe(index % 3);
+			expect(point.yValue).toBe(index / 3000);
+			expect(point.color).toBe(`rgb(${(index / 3000) * 255},0,0)`);
+			expect(point.x).toBeCloseTo(scene.chart.x + ((index % 3) / 2) * scene.chart.width);
+			expect(point.y).toBeCloseTo(scene.chart.y + (1 - index / 3000) * scene.chart.height);
+		}
+	});
+});
```

## docs/code/architecture.md

Status: modified; +7; −1

```diff
@@ -67,7 +67,13 @@
 - Astro 6
 - TypeScript
 - Tailwind CSS 4
-- TanStack Charts 1.0.0 through its official Svelte adapter, using SVG renderers
+- TanStack Charts 1.0.0 through its DOM API and Svelte attachments, using SVG with a canvas layer for dense time spectra
 - Rust 1.97.1
 - SQLite and Cloudflare D1
 - Bun 1.3.11
+
+## Chart rendering and spectrum requests
+
+ChartPlot mounts each chart once and updates its DOM host when data or options change. It publishes accessibility summaries after painting, using positioned scene observations. The attachment effect owns an interaction-observation snapshot before passing it to DOM callbacks, so derived dependencies detach with the chart. Dense time spectra retain every point, color and bucket identity in the scene while painting the marks into a canvas at the device pixel ratio. Axes and focus rings remain SVG. Focus rings are generated only for the active observation. Coverage SVG output uses one disconnected path per state and lane. The scene retains its native rectangles for interval hit testing, each bucket's ratio and hover identity.
+
+The spectrum-stats endpoint accepts optional `addressSide=source` or `addressSide=destination`. Omitting it returns both spectra as before. A projection returns the requested spectrum unchanged and the other side as an empty array. It retains the full query's row membership, bucket ends, coverage and empty or null buckets. The dashboard scopes its requests and window-cache keys to the selected side. This changes response shaping only; stored data, aggregation and half-open windows are unchanged.
```
