
## Latency Percentiles

### Latency Percentiles

```svelte
<script lang="ts">
	import { EChartsAreaChart } from '$lib/components/evilcharts/charts/echarts-area-chart/index.js';
	import type { ChartConfig } from '$lib/components/evilcharts/ui/echarts-chart/index.js';

	const series = [
		{ key: 'p99', label: 'P99', color: '#d41f12', latest: 204 },
		{ key: 'p95', label: 'P95', color: '#f37a00', latest: 98 },
		{ key: 'p75', label: 'P75', color: '#62c9d4', latest: 46 },
		{ key: 'p50', label: 'P50', color: '#007292', latest: 21 }
	] as const;

	const data = [
		{ time: 'Today 13:06', p99: 188, p95: 92, p75: 44, p50: 20 },
		{ time: 'Today 13:07', p99: 196, p95: 95, p75: 46, p50: 21 },
		{ time: 'Today 13:08', p99: 181, p95: 89, p75: 43, p50: 20 },
		{ time: 'Today 13:09', p99: 192, p95: 94, p75: 47, p50: 22 },
		{ time: 'Today 13:10', p99: 205, p95: 99, p75: 45, p50: 21 },
		{ time: 'Today 13:11', p99: 187, p95: 91, p75: 44, p50: 20 },
		{ time: 'Today 13:12', p99: 179, p95: 88, p75: 42, p50: 19 },
		{ time: 'Today 13:13', p99: 198, p95: 96, p75: 46, p50: 21 },
		{ time: 'Today 13:14', p99: 210, p95: 101, p75: 48, p50: 22 },
		{ time: 'Today 13:15', p99: 194, p95: 93, p75: 45, p50: 21 },
		{ time: 'Today 13:16', p99: 202, p95: 97, p75: 47, p50: 22 },
		{ time: 'Today 13:17', p99: 215, p95: 104, p75: 49, p50: 23 },
		{ time: 'Today 13:18', p99: 231, p95: 112, p75: 52, p50: 23 },
		{ time: 'Today 13:19', p99: 278, p95: 131, p75: 57, p50: 24 },
		{ time: 'Today 13:20', p99: 306, p95: 142, p75: 61, p50: 25 },
		{ time: 'Today 13:21', p99: 289, p95: 135, p75: 58, p50: 24 },
		{ time: 'Today 13:22', p99: 247, p95: 118, p75: 53, p50: 23 },
		{ time: 'Today 13:23', p99: 216, p95: 105, p75: 49, p50: 22 },
		{ time: 'Today 13:24', p99: 201, p95: 97, p75: 46, p50: 21 },
		{ time: 'Today 13:25', p99: 193, p95: 94, p75: 45, p50: 21 },
		{ time: 'Today 13:26', p99: 186, p95: 90, p75: 44, p50: 20 },
		{ time: 'Today 13:27', p99: 199, p95: 96, p75: 46, p50: 21 },
		{ time: 'Today 13:28', p99: 207, p95: 100, p75: 47, p50: 22 },
		{ time: 'Today 13:29', p99: 191, p95: 92, p75: 45, p50: 20 },
		{ time: 'Today 13:30', p99: 184, p95: 89, p75: 43, p50: 20 },
		{ time: 'Today 13:31', p99: 196, p95: 95, p75: 46, p50: 21 },
		{ time: 'Today 13:32', p99: 209, p95: 101, p75: 48, p50: 22 },
		{ time: 'Today 13:33', p99: 198, p95: 96, p75: 46, p50: 21 },
		{ time: 'Today 13:34', p99: 204, p95: 98, p75: 46, p50: 21 }
	];

	const config = {
		p99: { label: 'P99', colors: { light: ['#f87171'], dark: ['#d41f12'] } },
		p95: { label: 'P95', colors: { light: ['#fbbf24'], dark: ['#f37a00'] } },
		p75: { label: 'P75', colors: { light: ['#60a5fa'], dark: ['#62c9d4'] } },
		p50: { label: 'P50', colors: { light: ['#93c5fd'], dark: ['#007292'] } }
	} satisfies ChartConfig;

	let selected = $state<string | null>(null);

	function toggle(key: string) {
		selected = selected === key ? null : key;
	}
</script>

<section class="flex h-full w-full flex-col p-4" data-block="latency-area-chart">
	<div class="grid grid-cols-2 gap-y-2 sm:grid-cols-4 sm:gap-y-4">
		{#each series as item (item.key)}
			<button
				type="button"
				onclick={() => toggle(item.key)}
				class={[
					'flex cursor-pointer flex-row items-center gap-1.5 border-border px-3 text-left transition-opacity sm:flex-col sm:items-start sm:gap-1.5 sm:px-4 sm:first:pl-1 sm:[&:not(:first-child)]:border-l [&:nth-child(even)]:border-l',
					selected !== null && selected !== item.key && 'opacity-40'
				]}
				aria-pressed={selected === item.key}
			>
				<span class="flex items-center gap-1.5 text-xs font-medium text-primary sm:gap-2">
					<span class="size-2 shrink-0 rounded-[2px]" style:background-color={item.color}></span>
					{item.label}
				</span>
				<span class="text-xs text-muted-foreground sm:hidden">–</span>
				<span class="leading-none">
					<span class="text-sm font-medium tracking-tight text-primary sm:text-xl"
						>{item.latest}</span
					>
					<span class="ml-0.5 text-xs font-light text-muted-foreground sm:ml-1 sm:text-sm">ms</span>
				</span>
			</button>
		{/each}
	</div>

	<EChartsAreaChart
		{data}
		{config}
		xDataKey="time"
		class="mt-4 min-h-0 w-full flex-1"
		curveType="linear"
		enableHoverHighlight
		selectedDataKey={selected}
		onSelectionChange={(key) => (selected = key)}
	>
		<EChartsAreaChart.Grid />
		<EChartsAreaChart.XAxis
			dataKey="time"
			label="Time (UTC)"
			tickFormatter={(value) => String(value).replace('Today ', '')}
		/>
		<EChartsAreaChart.YAxis />
		<EChartsAreaChart.Tooltip />
		<EChartsAreaChart.Area dataKey="p50" variant="gradient" strokeVariant="solid" isClickable />
		<EChartsAreaChart.Area dataKey="p75" variant="gradient" strokeVariant="solid" isClickable />
		<EChartsAreaChart.Area dataKey="p95" variant="gradient" strokeVariant="solid" isClickable />
		<EChartsAreaChart.Area dataKey="p99" variant="gradient" strokeVariant="solid" isClickable />
	</EChartsAreaChart>
</section>
```
### npm

```bash
npx shadcn-svelte@latest add @evilcharts/latency-echarts-area-chart
```

### yarn

```bash
yarn dlx shadcn-svelte@latest add @evilcharts/latency-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn-svelte@latest add @evilcharts/latency-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn-svelte@latest add @evilcharts/latency-echarts-area-chart
```

## Portfolio Comparison

### Portfolio Comparison

```svelte
<script lang="ts">
	import { EChartsAreaChart } from '$lib/components/evilcharts/charts/echarts-area-chart/index.js';
	import type { ChartConfig } from '$lib/components/evilcharts/ui/echarts-chart/index.js';

	const series = [
		{ key: 'robinhood', label: 'Robinhood', color: '#c3f000', pct: -4.41, delta: -2377.66 },
		{ key: 'coinbase', label: 'Coinbase', color: '#2f6bff', pct: 1.15, delta: 617.22 }
	] as const;
	const data = [
		{ date: 'Dec 22', robinhood: 53916, coinbase: 53670 },
		{ date: 'Dec 23', robinhood: 54380, coinbase: 53080 },
		{ date: 'Dec 24', robinhood: 54760, coinbase: 52460 },
		{ date: 'Dec 25', robinhood: 54480, coinbase: 51880 },
		{ date: 'Dec 26', robinhood: 54060, coinbase: 51340 },
		{ date: 'Dec 27', robinhood: 53820, coinbase: 50900 },
		{ date: 'Dec 28', robinhood: 53520, coinbase: 50520 },
		{ date: 'Dec 29', robinhood: 53240, coinbase: 50200 },
		{ date: 'Dec 30', robinhood: 53080, coinbase: 49960 },
		{ date: 'Dec 31', robinhood: 52840, coinbase: 49760 },
		{ date: 'Jan 1', robinhood: 52700, coinbase: 49640 },
		{ date: 'Jan 2', robinhood: 52520, coinbase: 49600 },
		{ date: 'Jan 3', robinhood: 52420, coinbase: 49720 },
		{ date: 'Jan 4', robinhood: 52340, coinbase: 49980 },
		{ date: 'Jan 5', robinhood: 52240, coinbase: 50360 },
		{ date: 'Jan 6', robinhood: 52140, coinbase: 50820 },
		{ date: 'Jan 7', robinhood: 52080, coinbase: 51340 },
		{ date: 'Jan 8', robinhood: 52020, coinbase: 51900 },
		{ date: 'Jan 9', robinhood: 51960, coinbase: 52440 },
		{ date: 'Jan 10', robinhood: 51900, coinbase: 52960 },
		{ date: 'Jan 11', robinhood: 51860, coinbase: 53420 },
		{ date: 'Jan 12', robinhood: 51800, coinbase: 53780 },
		{ date: 'Jan 13', robinhood: 51740, coinbase: 54040 },
		{ date: 'Jan 14', robinhood: 51700, coinbase: 54200 },
		{ date: 'Jan 15', robinhood: 51640, coinbase: 54300 },
		{ date: 'Jan 16', robinhood: 51580, coinbase: 54320 },
		{ date: 'Jan 17', robinhood: 51538, coinbase: 54287 }
	];
	const config = {
		robinhood: { label: 'Robinhood', colors: { light: ['#a6cc00'], dark: ['#c3f000'] } },
		coinbase: { label: 'Coinbase', colors: { light: ['#2f6bff'], dark: ['#4c86ff'] } }
	} satisfies ChartConfig;
	const money = (value: number) =>
		Math.abs(value).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
</script>

<section class="flex h-full w-full flex-col pt-4" data-block="portfolio-area-chart">
	<div class="grid grid-cols-2 gap-x-8 px-4">
		{#each series as item (item.key)}
			<div class="flex flex-col gap-1">
				<div class="flex items-center gap-2 text-sm text-muted-foreground">
					<span class="size-3 shrink-0 rounded-full border-2" style:border-color={item.color}
					></span>
					{item.label}
				</div>
				<div class="text-2xl font-semibold tracking-tight text-primary sm:text-3xl">
					{item.pct > 0 ? '+' : '−'}{Math.abs(item.pct).toFixed(2)}%
				</div>
				<div class={item.delta < 0 ? 'text-rose-500' : 'text-emerald-500'}>
					{item.delta < 0 ? '−' : '+'}${money(item.delta)}
				</div>
			</div>
		{/each}
	</div>
	<EChartsAreaChart
		{data}
		{config}
		xDataKey="date"
		class="mt-4 min-h-0 w-full flex-1"
		curveType="step"
		enableHoverReveal
		chartOptions={{
			grid: { left: 0, right: 0, top: 16, bottom: 0 },
			yAxis: { type: 'value', show: false, scale: true, boundaryGap: ['12%', '16%'] }
		}}
	>
		<EChartsAreaChart.Tooltip variant="frosted-glass" />
		<EChartsAreaChart.Area dataKey="robinhood" variant="dotted" strokeVariant="solid">
			<EChartsAreaChart.ActiveDot variant="ping" />
		</EChartsAreaChart.Area>
		<EChartsAreaChart.Area dataKey="coinbase" variant="dotted" strokeVariant="solid">
			<EChartsAreaChart.ActiveDot variant="ping" />
		</EChartsAreaChart.Area>
	</EChartsAreaChart>
</section>
```
### npm

```bash
npx shadcn-svelte@latest add @evilcharts/portfolio-echarts-area-chart
```

### yarn

```bash
yarn dlx shadcn-svelte@latest add @evilcharts/portfolio-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn-svelte@latest add @evilcharts/portfolio-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn-svelte@latest add @evilcharts/portfolio-echarts-area-chart
```

## Growth Benchmark

### Growth Benchmark

```svelte
<script lang="ts">
	import { EChartsAreaChart } from '$lib/components/evilcharts/charts/echarts-area-chart/index.js';
	import type { ChartConfig } from '$lib/components/evilcharts/ui/echarts-chart/index.js';

	const data = [
		{ date: 'Jan 1', actual: 240, target: 125 },
		{ date: 'Jan 8', actual: 240, target: 125 },
		{ date: 'Jan 15', actual: 175, target: 200 },
		{ date: 'Jan 22', actual: 175, target: 200 },
		{ date: 'Feb 1', actual: 175, target: 200 },
		{ date: 'Feb 8', actual: 260, target: 125 },
		{ date: 'Feb 15', actual: 260, target: 125 },
		{ date: 'Feb 22', actual: 260, target: 125 },
		{ date: 'Mar 1', actual: 260, target: 190 },
		{ date: 'Mar 8', actual: 335, target: 190 },
		{ date: 'Mar 15', actual: 335, target: 190 },
		{ date: 'Mar 22', actual: 335, target: 390 },
		{ date: 'Apr 1', actual: 335, target: 390 },
		{ date: 'Apr 8', actual: 335, target: 390 },
		{ date: 'Apr 15', actual: 190, target: 390 },
		{ date: 'Apr 22', actual: 215, target: 305 },
		{ date: 'May 1', actual: 215, target: 305 },
		{ date: 'May 8', actual: 215, target: 175 },
		{ date: 'May 15', actual: 215, target: 175 },
		{ date: 'May 22', actual: 275, target: 175 },
		{ date: 'Jun 1', actual: 275, target: 245 }
	];
	const config = {
		actual: { label: 'Actual', colors: { light: ['#0a0a0a'], dark: ['#ffffff'] } },
		target: { label: 'Target', colors: { light: ['#a1a1aa'], dark: ['#666666'] } }
	} satisfies ChartConfig;
	const latest = data.at(-1)!;
	const delta = ((latest.actual - latest.target) / latest.target) * 100;
</script>

<section class="flex h-full w-full flex-col p-4" data-block="benchmark-area-chart">
	<header class="flex items-start justify-between gap-4">
		<div class="flex flex-col gap-1">
			<span class="text-xs text-muted-foreground">Weekly signups</span>
			<div class="flex items-baseline gap-2">
				<span class="text-2xl font-semibold tracking-tight text-primary sm:text-3xl">
					{latest.actual}
				</span>
				<span class="text-sm font-medium text-emerald-500">+{delta.toFixed(1)}% vs target</span>
			</div>
		</div>
		<div class="flex flex-col items-end gap-1.5 pt-1">
			<div class="flex items-center gap-2 text-xs text-muted-foreground">
				<svg class="text-primary" width="18" height="2" viewBox="0 0 18 2" aria-hidden="true">
					<line x1="0" y1="1" x2="18" y2="1" stroke="currentColor" stroke-width="2" />
				</svg>
				Actual
			</div>
			<div class="flex items-center gap-2 text-xs text-muted-foreground">
				<svg width="18" height="2" viewBox="0 0 18 2" aria-hidden="true">
					<line
						x1="0"
						y1="1"
						x2="18"
						y2="1"
						stroke="currentColor"
						stroke-width="2"
						stroke-dasharray="4 3"
					/>
				</svg>
				Target
			</div>
		</div>
	</header>
	<EChartsAreaChart
		{data}
		{config}
		xDataKey="date"
		class="mt-4 min-h-0 w-full flex-1"
		curveType="smooth"
	>
		<EChartsAreaChart.Grid />
		<EChartsAreaChart.XAxis
			dataKey="date"
			tickFormatter={(value) =>
				String(value).split(' ')[1] === '1' ? String(value).split(' ')[0] : ''}
		/>
		<EChartsAreaChart.YAxis />
		<EChartsAreaChart.Tooltip />
		<EChartsAreaChart.Area dataKey="target" variant="none" strokeVariant="dashed" />
		<EChartsAreaChart.Area dataKey="actual" variant="lines" strokeVariant="solid" />
	</EChartsAreaChart>
</section>
```
### npm

```bash
npx shadcn-svelte@latest add @evilcharts/benchmark-echarts-area-chart
```

### yarn

```bash
yarn dlx shadcn-svelte@latest add @evilcharts/benchmark-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn-svelte@latest add @evilcharts/benchmark-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn-svelte@latest add @evilcharts/benchmark-echarts-area-chart
```

## Audience Growth

### Audience Growth

```svelte
<script lang="ts">
	import { EChartsAreaChart } from '$lib/components/evilcharts/charts/echarts-area-chart/index.js';
	import type { ChartConfig } from '$lib/components/evilcharts/ui/echarts-chart/index.js';

	const data = [
		{ month: 'Jan', listeners: 2980 },
		{ month: 'Feb', listeners: 3120 },
		{ month: 'Mar', listeners: 3460 },
		{ month: 'Apr', listeners: 3380 },
		{ month: 'May', listeners: 3720 },
		{ month: 'Jun', listeners: 4180 },
		{ month: 'Jul', listeners: 4560 }
	];
	const config = {
		listeners: {
			label: 'Listeners',
			colors: { light: ['#10b981', '#0ea5e9', '#8b5cf6'], dark: ['#34d399', '#38bdf8', '#a78bfa'] }
		}
	} satisfies ChartConfig;
	const total = data.reduce((sum, row) => sum + row.listeners, 0);
</script>

<section class="flex h-full w-full flex-col" data-block="audience-area-chart">
	<header class="flex items-start justify-between gap-4 px-4 pt-4">
		<div class="flex flex-col gap-1">
			<span class="text-base font-medium tracking-tight text-primary sm:text-lg">Listeners</span>
			<span class="max-w-[26ch] text-xs leading-snug text-muted-foreground">
				Monthly reach across every show and episode
			</span>
		</div>
		<div class="flex shrink-0 flex-col items-end gap-2">
			<span class="text-2xl font-semibold tracking-tight text-primary sm:text-4xl">
				{total.toLocaleString('en-US')}
			</span>
			<span class="text-xs text-muted-foreground">Total listeners</span>
		</div>
	</header>
	<div class="relative mt-2 min-h-0 w-full flex-1">
		<EChartsAreaChart
			{data}
			{config}
			xDataKey="month"
			class="h-full w-full"
			curveType="monotone"
			chartOptions={{
				grid: { left: 0, right: 0, top: 16, bottom: 0, outerBoundsMode: 'none' },
				yAxis: { type: 'value', show: false, scale: true, boundaryGap: ['16%', '20%'] }
			}}
		>
			<EChartsAreaChart.Tooltip variant="frosted-glass" />
			<EChartsAreaChart.Area
				dataKey="listeners"
				variant="gradient"
				strokeVariant="solid"
				strokeWidth={2.5}
			>
				<EChartsAreaChart.ActiveDot variant="ping" />
			</EChartsAreaChart.Area>
		</EChartsAreaChart>
		<div
			class="pointer-events-none absolute inset-x-0 bottom-0 flex justify-between px-4 pb-3 text-[10px] text-muted-foreground sm:text-xs"
		>
			{#each data as row (row.month)}<span>{row.month}</span>{/each}
		</div>
	</div>
</section>
```
### npm

```bash
npx shadcn-svelte@latest add @evilcharts/audience-echarts-area-chart
```

### yarn

```bash
yarn dlx shadcn-svelte@latest add @evilcharts/audience-echarts-area-chart
```

### bun

```bash
bunx --bun shadcn-svelte@latest add @evilcharts/audience-echarts-area-chart
```

### pnpm

```bash
pnpm dlx shadcn-svelte@latest add @evilcharts/audience-echarts-area-chart
```
