diff --git a/web/src/lib/components/PlaylistPlaceholderCard.svelte b/web/src/lib/components/PlaylistPlaceholderCard.svelte
new file mode 100644
index 00000000..6ba05146
--- /dev/null
+++ b/web/src/lib/components/PlaylistPlaceholderCard.svelte
@@ -0,0 +1,40 @@
+
+
+
diff --git a/web/src/lib/components/PlaylistPlaceholderCard.test.ts b/web/src/lib/components/PlaylistPlaceholderCard.test.ts
new file mode 100644
index 00000000..8d62310c
--- /dev/null
+++ b/web/src/lib/components/PlaylistPlaceholderCard.test.ts
@@ -0,0 +1,34 @@
+import { describe, test, expect } from 'vitest';
+import { render, screen } from '@testing-library/svelte';
+import PlaylistPlaceholderCard from './PlaylistPlaceholderCard.svelte';
+
+describe('PlaylistPlaceholderCard', () => {
+ test('renders label', () => {
+ render(PlaylistPlaceholderCard, { props: { label: 'For You', variant: 'pending' } });
+ expect(screen.getByText('For You')).toBeInTheDocument();
+ });
+
+ test('building variant shows building copy and pulses', () => {
+ const { container } = render(PlaylistPlaceholderCard, { props: { label: 'For You', variant: 'building' } });
+ expect(screen.getByText(/Building your daily mix/i)).toBeInTheDocument();
+ const cover = container.querySelector('[data-variant="building"] .aspect-square');
+ expect(cover?.className).toMatch(/animate-pulse/);
+ });
+
+ test('failed variant shows failure copy and does NOT pulse', () => {
+ const { container } = render(PlaylistPlaceholderCard, { props: { label: 'For You', variant: 'failed' } });
+ expect(screen.getByText(/Couldn't build this mix/i)).toBeInTheDocument();
+ const cover = container.querySelector('[data-variant="failed"] .aspect-square');
+ expect(cover?.className).not.toMatch(/animate-pulse/);
+ });
+
+ test('seed-needed variant shows variety copy', () => {
+ render(PlaylistPlaceholderCard, { props: { label: 'Songs like…', variant: 'seed-needed' } });
+ expect(screen.getByText(/Listen to more variety/i)).toBeInTheDocument();
+ });
+
+ test('pending variant (default) shows the within-24h copy', () => {
+ render(PlaylistPlaceholderCard, { props: { label: 'For You' } });
+ expect(screen.getByText(/within 24h/i)).toBeInTheDocument();
+ });
+});