diff --git a/frontend/src/lib/components/DistributionBar.svelte b/frontend/src/lib/components/DistributionBar.svelte
new file mode 100644
index 00000000..d0e52292
--- /dev/null
+++ b/frontend/src/lib/components/DistributionBar.svelte
@@ -0,0 +1,54 @@
+
+
+
+
+
{outCount} von {shortSenderName}
+ 
+
{inCount} von {shortReceiverName}
+ 
+
+
+
diff --git a/frontend/src/lib/components/DistributionBar.svelte.spec.ts b/frontend/src/lib/components/DistributionBar.svelte.spec.ts
new file mode 100644
index 00000000..3af8bca0
--- /dev/null
+++ b/frontend/src/lib/components/DistributionBar.svelte.spec.ts
@@ -0,0 +1,58 @@
+import { describe, it, expect, afterEach } from 'vitest';
+import { cleanup, render } from 'vitest-browser-svelte';
+
+import DistributionBar from './DistributionBar.svelte';
+
+afterEach(() => {
+ cleanup();
+});
+
+describe('DistributionBar', () => {
+ it('renders both counts and short names, and the two-tone fill bar', async () => {
+ render(DistributionBar, {
+ outCount: 3,
+ inCount: 7,
+ senderName: 'Hans Müller',
+ receiverName: 'Anna Schmidt'
+ });
+
+ const container = document.querySelector('[role="img"]') as HTMLElement;
+ expect(container).toBeTruthy();
+ expect(container.getAttribute('aria-label')).toContain('3 von Hans Müller');
+ expect(container.getAttribute('aria-label')).toContain('7 von Anna Schmidt');
+
+ expect(container.textContent).toContain('3 von Hans');
+ expect(container.textContent).toContain('7 von Anna');
+
+ // 3/10 → 30% / 70% split on the two segments
+ const segments = container.querySelectorAll('[data-testid="dist-bar-segment"]');
+ expect(segments).toHaveLength(2);
+ expect((segments[0] as HTMLElement).style.width).toBe('30%');
+ expect((segments[1] as HTMLElement).style.width).toBe('70%');
+ });
+
+ it('falls back to the full name when it has no space to split', async () => {
+ render(DistributionBar, {
+ outCount: 1,
+ inCount: 0,
+ senderName: 'SingleWord',
+ receiverName: 'Another'
+ });
+
+ const container = document.querySelector('[role="img"]') as HTMLElement;
+ expect(container.textContent).toContain('1 von SingleWord');
+ });
+
+ it('renders a zero-percent left segment when outCount is zero', async () => {
+ render(DistributionBar, {
+ outCount: 0,
+ inCount: 4,
+ senderName: 'Hans',
+ receiverName: 'Anna'
+ });
+
+ const segments = document.querySelectorAll('[data-testid="dist-bar-segment"]');
+ expect((segments[0] as HTMLElement).style.width).toBe('0%');
+ expect((segments[1] as HTMLElement).style.width).toBe('100%');
+ });
+});