diff --git a/AGENTS.md b/AGENTS.md
index b9a67ce17..878329442 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -83,6 +83,7 @@ This section applies to any agent (Claude, Codex, Cursor, opencode, ...) working
- Keep UI and analysis engine decoupled through shared contracts.
- Prefer minimal, test-first changes for production code.
- Prefer practical, friendly, rehearsal-first wording over academic or authority-heavy language.
+- Workspace components stay on `docs/design-system/component-contract.md` and Figma node 19-239. Storybook covers ConfidenceBadge, PracticeProgress, RehearsalCallout, RoleSwitcher, WorkspaceStates, SectionRoadmap, and the extracted OverlapWarningList.
- Do not reduce the product to a chord analyzer when form, timing, player coordination, playable ranges, simplification, and setup cues are the real rehearsal blockers.
- Do not frame usability as a reason to accept weak analysis quality; BandScope should aim for both easy use and high accuracy.
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index ca0df5ac4..c76373a41 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -6,6 +6,7 @@ Last updated: 2026-03-11
- Product identity, UX tone, copy rules, and prioritization tie-breakers live in `docs/brand-story.md`.
- Future PRDs, TRDs, onboarding copy, empty states, error messages, and marketing copy should use that document as the single brand source of truth.
+- Workspace Storybook stories cite Figma 19-239 and `docs/design-system/component-contract.md`. OverlapWarningList is the shared clash list for Section Roadmap and Ranges.
## Security source
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b6f7e784..176682b98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
### Added
+- Storybook stories and workspace tokens for ConfidenceBadge (Figma 19-239), PracticeProgress, RehearsalCallout, RoleSwitcher, WorkspaceStates, SectionRoadmap, and the extracted OverlapWarningList. ConfidenceBadge now exposes the Figma Compact and Default size states for every confidence level while preserving Compact for existing call sites. OverlapWarningList is the shared clash list for Roadmap and Ranges. The RehearsalCallout story keeps the first practice action enabled.
- Name tonight's first playable range on the ready rehearsal map and tell the player to check that span on their instrument before the section.
- Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace.
- 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함.
@@ -14,6 +15,7 @@
### Fixed
+- Keep shared overlap warnings readable on both dark rehearsal surfaces and light Ranges cards by selecting surface-specific shared tokens and exposing both contexts in Storybook.
- Upgraded the local score PDF parser to `pdfjs-dist` 6.2.108, pinned Undici 7.29.0 across the workspace, and constrained PDF loading to copied in-memory bytes with a same-origin bundled worker and npm-generated lock provenance.
## [0.1.3] - 2026-04-29
diff --git a/CLAUDE.md b/CLAUDE.md
index b5a34c1fa..34c8d6755 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -51,7 +51,7 @@ BandScope is a local-first desktop app for rehearsal prep: it turns a song into
Three layers, decoupled through shared contracts:
-- `apps/desktop` — Tauri 2 + Vite + React 19 shell (Tailwind 4, Base UI, Storybook). Feature screens live in `src/features/` (home, workspace, chords, ranges, player, settings). The ready workspace names tonight's first playable range and the next instrument check. `src/lib/analysis.ts` and `src/lib/job_runner.ts` call typed Tauri IPC commands, with a browser fallback that serves demo data when not running inside Tauri.
+- `apps/desktop` — Tauri 2 + Vite + React 19 shell (Tailwind 4, Base UI, Storybook). Feature screens live in `src/features/` (home, workspace, chords, ranges, player, settings). The ready workspace names tonight's first playable range and the next instrument check. Workspace Storybook stories cite Figma 19-239 and `docs/design-system/component-contract.md`; OverlapWarningList is the shared clash list for Section Roadmap and Ranges. `src/lib/analysis.ts` and `src/lib/job_runner.ts` call typed Tauri IPC commands, with a browser fallback that serves demo data when not running inside Tauri.
- `apps/desktop/src-tauri/src/main.rs` — the Rust orchestration boundary. Tauri commands (`start_analysis_job`, `get_analysis_job_status`, `select_local_audio_source`, `import_youtube_url`) validate untrusted input (project IDs, file paths, URLs) and spawn the Python engine as a subprocess. There is no loopback HTTP listener and no network path for local analysis.
- `services/analysis-engine` — Python package `bandscope_analysis` (librosa/numpy). Entry point `cli.py` reads a JSON job request on stdin and prints a structured job-status JSON envelope on stdout (`--progress-jsonl` streams progress lines). `api.py` orchestrates the pipeline across the `separation`, `sections`, `roles`, `chords`, `ranges`, `temporal`, `transcription`, and `youtube` modules.
diff --git a/apps/desktop/src/features/ranges/index.test.tsx b/apps/desktop/src/features/ranges/index.test.tsx
index 585c1f4af..3bdd54509 100644
--- a/apps/desktop/src/features/ranges/index.test.tsx
+++ b/apps/desktop/src/features/ranges/index.test.tsx
@@ -70,7 +70,7 @@ describe("RangesFeature", () => {
it("renders overlap warnings", () => {
render();
- expect(screen.getByText("⚠️ Clashing notes with Role 2")).toBeInTheDocument();
+ expect(screen.getByText("Clashing notes with Role 2")).toBeInTheDocument();
});
it("renders transcription count when transcription exists", () => {
diff --git a/apps/desktop/src/features/ranges/index.tsx b/apps/desktop/src/features/ranges/index.tsx
index 1cbb020b4..716fcafab 100644
--- a/apps/desktop/src/features/ranges/index.tsx
+++ b/apps/desktop/src/features/ranges/index.tsx
@@ -1,6 +1,7 @@
+import { OverlapWarningList } from "../workspace/OverlapWarningList";
import type { RehearsalSong } from "@bandscope/shared-types";
-/** Documented. */
+/** Render per-role range, overlap, and transcription summaries for the loaded rehearsal song. */
export function RangesFeature(props: { title: string; song?: RehearsalSong | null }) {
const { title, song } = props;
@@ -37,25 +38,7 @@ export function RangesFeature(props: { title: string; song?: RehearsalSong | nul
- )}
+
diff --git a/apps/desktop/src/features/workspace/WorkspaceStates.stories.tsx b/apps/desktop/src/features/workspace/WorkspaceStates.stories.tsx
new file mode 100644
index 000000000..618c482b4
--- /dev/null
+++ b/apps/desktop/src/features/workspace/WorkspaceStates.stories.tsx
@@ -0,0 +1,26 @@
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { EmptyState, ErrorState, LoadingState } from "./WorkspaceStates";
+
+/** Storybook metadata for the workspace empty, loading, and failed states. */
+const meta = {
+ title: "Workspace/WorkspaceStates",
+ parameters: { layout: "padded" },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+/** Empty workspace before a rehearsal source has been selected. */
+export const Empty: Story = {
+ render: () => ,
+};
+
+/** Workspace while local analysis is still running. */
+export const Loading: Story = {
+ render: () => ,
+};
+
+/** Failed workspace state with actionable decode-error copy. */
+export const Failed: Story = {
+ render: () => ,
+};
diff --git a/apps/desktop/src/index.css b/apps/desktop/src/index.css
index e64ea931a..db9497b9e 100644
--- a/apps/desktop/src/index.css
+++ b/apps/desktop/src/index.css
@@ -1,6 +1,7 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "@fontsource-variable/geist";
+@import "./styles/workspace-tokens.css";
/* stylelint-disable-next-line scss/at-rule-no-unknown -- Tailwind v4 directive is compiled by the Vite plugin. */
@custom-variant dark (&:is(.dark *));
diff --git a/apps/desktop/src/styles/workspace-tokens.css b/apps/desktop/src/styles/workspace-tokens.css
new file mode 100644
index 000000000..cfd5c6e51
--- /dev/null
+++ b/apps/desktop/src/styles/workspace-tokens.css
@@ -0,0 +1,39 @@
+/* Workspace tokens mapped from Figma 19-239 Confidence Badge and page 31. */
+:root {
+ --bandscope-confidence-compact-height: 1.375rem;
+ --bandscope-confidence-default-height: 1.625rem;
+
+ /* Canonical semantic status colors from the published BandScope Figma library. */
+ --bandscope-status-danger: #fda4af;
+ --bandscope-status-danger-bg: #fda4af14;
+ --bandscope-status-danger-border: #fda4af4d;
+ --bandscope-status-warning: #fcd34d;
+ --bandscope-status-warning-bg: #fcd34d12;
+ --bandscope-status-warning-border: #fcd34d33;
+ --bandscope-status-success: #6ee7b7;
+ --bandscope-status-success-bg: #6ee7b712;
+ --bandscope-status-success-border: #6ee7b733;
+
+ /* Confidence levels consume reusable status semantics instead of inventing a parallel palette. */
+ --bandscope-confidence-low-bg: var(--bandscope-status-danger-bg);
+ --bandscope-confidence-low-fg: var(--bandscope-status-danger);
+ --bandscope-confidence-low-border: var(--bandscope-status-danger-border);
+ --bandscope-confidence-medium-bg: var(--bandscope-status-warning-bg);
+ --bandscope-confidence-medium-fg: var(--bandscope-status-warning);
+ --bandscope-confidence-medium-border: var(--bandscope-status-warning-border);
+ --bandscope-confidence-high-bg: var(--bandscope-status-success-bg);
+ --bandscope-confidence-high-fg: var(--bandscope-status-success);
+ --bandscope-confidence-high-border: var(--bandscope-status-success-border);
+ --bandscope-overlap-bg: rgb(251 113 133 / 0.08);
+ --bandscope-overlap-border: rgb(253 164 175 / 0.2);
+ --bandscope-overlap-fg: rgb(255 228 230);
+ --bandscope-overlap-light-bg: #fff1f2;
+ --bandscope-overlap-light-border: #fecdd3;
+ --bandscope-overlap-light-fg: #9f1239;
+ --bandscope-callout-bg: rgb(34 211 238 / 0.08);
+ --bandscope-callout-border: rgb(34 211 238 / 0.25);
+ --bandscope-callout-fg: rgb(207 250 254);
+ --bandscope-practice-track: rgb(15 23 42 / 0.5);
+ --bandscope-practice-fill-from: rgb(99 102 241);
+ --bandscope-practice-fill-to: rgb(34 211 238);
+}
\ No newline at end of file
diff --git a/docs/design-system/component-contract.md b/docs/design-system/component-contract.md
index 22602c313..17b13acb1 100644
--- a/docs/design-system/component-contract.md
+++ b/docs/design-system/component-contract.md
@@ -4,7 +4,7 @@ This contract connects the BandScope Figma design system to production React com
Figma file: https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk
-The authoritative Figma view is `31 Component Contract Catalog`. This file mirrors that page for review only.
+The live design-system authority was re-verified on 2026-08-30. Page `45:86` (`31 Component Contract Catalog`) contains the current reusable `OverlapWarningItem` component set at node `254:81`, with `Surface=Dark` and `Surface=Light` variants. Contract frame `254:82` shows both variants against their intended host surfaces and records the no-warning state; live metadata confirms the older `252:19` node no longer exists and must not be used as design-gate evidence. Older component links below remain traceability unless their specific node is freshly verified.
## Canonical Components
@@ -26,7 +26,9 @@ The authoritative Figma view is `31 Component Contract Catalog`. This file mirro
| Console Panel | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=18-632 | `apps/desktop/src/components/ui/card.tsx` | Use `Card`, `CardHeader`, `CardTitle`, and `CardContent`; `size="sm"` is the compact state. |
| BandScope Mark | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=19-163 | `apps/desktop/src/App.tsx` | Feature-local `BandScopeMark()` currently has no props; Figma size variants are visual guidance only. |
| Metric Card | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=19-216 | `apps/desktop/src/App.tsx` | Feature-local `MetricCard({ icon, label, value, detail, accent? })`. Metrics follow source controls on mobile. |
-| Confidence Badge | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=19-239 | `apps/desktop/src/features/workspace/ConfidenceBadge.tsx` | Use `level: ConfidenceLevel`; no `score` or `label` prop exists in current code. |
+| Confidence Badge | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=19-239 | `apps/desktop/src/features/workspace/ConfidenceBadge.tsx` | Use `level: ConfidenceLevel` plus optional `size` (`"compact"` or `"default"`); the compact size remains the backward-compatible default. No `score` or `label` prop exists. |
+| Overlap Warning List | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=254-81 | `apps/desktop/src/features/workspace/OverlapWarningList.tsx` | Current reusable `OverlapWarningItem` component set. Pass `warnings`; default dark surface is for Roadmap and `surface="light"` is required on light cards such as Ranges. |
+| Rehearsal Callout | workspace next-action pattern | `apps/desktop/src/features/workspace/RehearsalCallout.tsx` | First practice action after analysis; keep a visible button. |
| Status Pill | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=19-283 | `apps/desktop/src/features/workspace/Workspace.tsx` | Design pattern only. Current code uses `formatStatusLabel(status)` inside local badge-like markup. |
| Role Switcher | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=19-337 | `apps/desktop/src/features/workspace/RoleSwitcher.tsx` | Use `roles`, `activeRole`, and `onRoleChange`; `null` means all roles. |
| Section Roadmap Card | https://www.figma.com/design/zthWmqfNKUgJBECvv002Qk/Bandscope-Design-System-v1?node-id=19-402 | `apps/desktop/src/features/workspace/SectionRoadmap.tsx` | Use `song`, `activeRole`, and optional `onSongUpdate`; avoid rebuilding its internal card layout. |
@@ -57,8 +59,11 @@ The authoritative Figma view is `31 Component Contract Catalog`. This file mirro
### Badge And Confidence
- Use `Badge` for general metadata and `ConfidenceBadge` for confidence status.
+- Historical Figma node `19:239` had six variants: `Level=Low|Medium|High` crossed with `Size=Compact|Default`; re-verify that node before using it as current evidence.
- Use `ConfidenceBadge` with `level` from shared types only: `low`, `medium`, `high`.
-- Do not pass `score` or `label` to `ConfidenceBadge`; those props do not exist in the current runtime component.
+- Omit `size` to preserve the existing compact rendering; pass `size="default"` when the 26 px state is required. Compact and default heights are workspace tokens corresponding to the prior 22 px and 26 px Figma variants at the default root font size.
+- Do not pass `score` or `label` to `ConfidenceBadge`; those props do not exist in the runtime component.
+- Storybook must retain both size states for every confidence level so design review can compare the complete variant matrix.
- Keep badges short enough to avoid wrapping inside dense cards.
### Tabs
@@ -73,18 +78,27 @@ The authoritative Figma view is `31 Component Contract Catalog`. This file mirro
- Tone-specific colors belong on `ProgressIndicator` or scoped child selectors.
- Provide adjacent live text when progress reflects an active asynchronous job.
+### Overlap Warnings
+
+- Current Figma authority is the `OverlapWarningItem` component set `254:81` on page `45:86`; contract frame `254:82` is the current exact-state handoff. The former `252:19`/`252:2` references are stale and are not design-gate evidence.
+- `OverlapWarningList` defaults to `surface="dark"` so Section Roadmap keeps the established dark-workspace token set.
+- Ranges cards have a white background and must pass `surface="light"` so compact warning copy uses the dedicated light-surface tokens.
+- Storybook must retain both dark and light populated states plus the empty state; do not copy the warning markup into a feature-specific implementation.
+- Figma and Storybook both show the two surface states. Runtime empty state intentionally renders no node.
+- Accessibility rationale and current exact-value mapping are recorded in `docs/doctoring/overlap-warning-surface-contrast.md`.
+
### Workspace States
-- Figma page `34 Workspace State Matrix` maps `EmptyState`, `LoadingState`, `ErrorState`, ready `Workspace`, `GrooveMap`, and Source Control Stack substates.
+- Historical Figma page `34 Workspace State Matrix` mapped `EmptyState`, `LoadingState`, `ErrorState`, ready `Workspace`, `GrooveMap`, and Source Control Stack substates. Re-verify that specific state authority before changing workspace routing.
- `App.tsx` must preserve the current routing order: `jobError` -> `ErrorState`, `analysisInFlight || isStarting` -> `LoadingState`, `jobResult` -> `Workspace`, otherwise `EmptyState`.
- `LoadingState` keeps `role="status"`, `aria-live="polite"`, `aria-atomic="true"`, and `aria-busy="true"`.
- `ErrorState` keeps `role="alert"`, `aria-live="assertive"`, and visible safe error detail copy.
- `EmptyState` must remain an actionable state card, not a blank placeholder panel.
-- If a new workspace state is added in code, update Figma page 34 and page 33 audit evidence before merging.
+- If a new workspace state is added in code, update current Figma state authority before merging.
## Pattern Backlog
-These Figma patterns are valid visual guidance but are not yet extracted as standalone code components. Use the existing feature markup and open a follow-up extraction task when reuse appears twice.
+Historical Figma patterns remain implementation traceability unless freshly verified. Use existing feature markup and open a follow-up extraction task when reuse appears twice.
| Figma pattern | Current implementation home | Extraction trigger |
| --- | --- | --- |
@@ -97,9 +111,9 @@ These Figma patterns are valid visual guidance but are not yet extracted as stan
## PR Review Rules
-- New UI should cite the matching Figma node and code path in the PR description when it implements a design-system component.
-- A new component variant must update this file, the relevant component tests, and the Figma component notes.
+- New UI should cite a current matching Figma node and code path in the PR description when it implements a design-system component; historical or missing nodes are traceability only.
+- A new component variant must update this file, the relevant component tests, Storybook, and the current Figma component notes before merge.
- A new Figma-only pattern must enter the Pattern Backlog before being reused.
- Any deliberate visual divergence from Figma should state whether the repo contract or accessibility requirement caused it.
-- Workspace state changes must cite page `34 Workspace State Matrix` or explain why Figma was updated first.
+- Workspace state changes must cite current Figma state authority or explain the design-authority repair before merge.
- Do not add Code Connect, Figma token, or Figma publish requirements to CI.
diff --git a/docs/doctoring/overlap-warning-surface-contrast.md b/docs/doctoring/overlap-warning-surface-contrast.md
new file mode 100644
index 000000000..8c3442a4b
--- /dev/null
+++ b/docs/doctoring/overlap-warning-surface-contrast.md
@@ -0,0 +1,48 @@
+# Overlap Warning Surface Contrast
+
+## Customer-facing defect
+
+`OverlapWarningList` is shared by the dark rehearsal workspace and the light Ranges cards. The component previously applied the dark-workspace foreground token in both contexts, so a warning rendered on a white Ranges card could lose readable text contrast even though the same component was legible on the dark workspace.
+
+## Repair contract
+
+The shared component keeps one semantic warning primitive and selects a surface contract instead of duplicating markup:
+
+- `surface="dark"` remains the default for the rehearsal workspace;
+- `surface="light"` uses `--bandscope-overlap-light-bg`, `--bandscope-overlap-light-border`, and `--bandscope-overlap-light-fg` for light cards;
+- the Ranges feature explicitly selects the light surface;
+- Storybook exposes both surface contexts; and
+- component tests require the light-surface token contract so a later refactor cannot silently reuse dark-only text colors.
+
+The light warning foreground/background pair is chosen to satisfy WCAG 2.2 Success Criterion 1.4.3 for normal text. WCAG 2.2 requires at least 4.5:1 contrast for ordinary text; large text has a separate 3:1 threshold. This component renders compact warning copy and therefore uses the normal-text requirement.
+
+## Design authority status
+
+The configured BandScope Figma file was freshly re-verified on 2026-08-30. Page `45:86` (`31 Component Contract Catalog`) contains the reusable `OverlapWarningItem` component set at node `254:81` with `Surface=Dark` and `Surface=Light` variants. Contract frame `254:82` renders real instances of the same component set against the dark rehearsal surface (`254:85`) and white Ranges-card surface (`254:96`), while `254:107` records the intentional `NoClashes` state in which the runtime renders no warning list. Fresh metadata also confirms that the former `252:19` reference is no longer present in the file, so it is historical/stale traceability and is not acceptable as design-gate evidence.
+
+The Figma values match the runtime/Storybook contract:
+
+- Dark: background `rgb(251 113 133 / 0.08)`, border `rgb(253 164 175 / 0.2)`, foreground `#FFE4E6`.
+- Light: background `#FFF1F2`, border `#FECDD3`, foreground `#9F1239`.
+
+Older component node references in the broader BandScope catalog remain historical traceability unless their specific node is freshly verified. No Code Connect or Figma publishing requirement is added to CI; Storybook remains the executable design-review boundary and Figma remains the visual/component authority.
+
+## Security Notes
+
+This slice changes React presentation, Storybook coverage, shared CSS tokens, design-traceability documentation, and a static Figma design reference only. It does not add or widen filesystem handling, URL fetching, subprocess execution, IPC, WebView authority, updater behavior, model downloads, export paths, credential handling, or runtime network access. The light/dark warning selection operates only on already-provided warning strings and does not create a new trust boundary.
+
+## Verification
+
+Before merge, verify the unchanged exact head with:
+
+- focused `OverlapWarningList` component tests;
+- Storybook rendering for dark, light, and empty states;
+- the repository's configured Storybook accessibility checks;
+- current live Figma metadata and screenshot evidence for component set `254:81`, contract frame `254:82`, dark preview `254:85`, light preview `254:96`, and empty-state note `254:107`; and
+- every required repository and organization check plus qualifying independent human review.
+
+Queued, skipped-required, stale, predecessor-head, model-only, self/author, synthetic, or administrative-bypass evidence is not sufficient.
+
+## Reference
+
+World Wide Web Consortium. (2024, December 12). *Web Content Accessibility Guidelines (WCAG) 2.2*. https://www.w3.org/TR/WCAG22/