diff --git a/docs/superpowers/plans/2026-05-24-nle-editor-react-polish-phase-1-2-3.md b/docs/superpowers/plans/2026-05-24-nle-editor-react-polish-phase-1-2-3.md
new file mode 100644
index 0000000..5d95d38
--- /dev/null
+++ b/docs/superpowers/plans/2026-05-24-nle-editor-react-polish-phase-1-2-3.md
@@ -0,0 +1,797 @@
+# NLE Editor: React SPA Polish — Phases 1–3 Implementation Plan
+
+> **Date:** 2026-05-24
+> **Status:** Draft — awaiting user review before execution
+> **Reference spec:** `docs/superpowers/specs/2026-05-18-editor-growing-file-features-design.md`
+> **Architecture note:** The original NLE editor plan (`2026-05-18-nle-editor.md`) was written for standalone HTML pages. Commit `6322b61` deleted those pages — the React SPA is now the only entry. This plan adapts Phase 1 for the React SPA architecture. Phases 2–3 follow the original design spec.
+
+---
+
+## Architectural Context
+
+**Backend (complete, no changes needed):**
+- `003-editor-sequences.sql` migration applied — `sequences` + `sequence_clips` tables live
+- `routes/sequences.js` — full CRUD, clip sync (`PUT /:id/clips`), EDL export — registered in `index.js`
+
+**Frontend (React SPA):**
+- `screens-editor.jsx` — skeleton with all features disabled behind "In Development" overlay
+- `shell.jsx` — Editor nav item with `DEV` badge
+- `app.jsx` — `case 'editor': content = `
+- `data.jsx` — **no** sequence API helpers exist
+- `js/timecode.js` — 59.94 DF timecode (vanilla JS, unused by SPA)
+- `js/timeline.js` — DOM timeline engine (vanilla JS, unused by SPA)
+
+**Other services:**
+- `services/editor/` — OpenReel Video (separate WebCodecs/WebGPU NLE, proxied at `/editor/`). Not part of this plan's scope — the React SPA editor is the primary in-app NLE.
+- `services/premiere-plugin/` — Premiere Pro CEP panel (external integration, not in scope)
+
+---
+
+## File Map
+
+### Phase 1 — Core Editor
+
+| Action | Path | Description |
+|--------|------|-------------|
+| MODIFY | `services/web-ui/public/data.jsx` | Add `getSequences`, `createSequence`, `getSequence`, `updateSequence`, `deleteSequence`, `syncSequenceClips`, `exportSequenceEDL` |
+| MODIFY | `services/web-ui/public/screens-editor.jsx` | Full rewrite: source monitor, media panel, timeline, program monitor, sequence management, auto-save, undo/redo, EDL export |
+| MODIFY | `services/web-ui/public/shell.jsx` | Remove `DEV` badge from Editor nav item |
+| NO CHANGE | `services/web-ui/public/js/timecode.js` | Imported by screens-editor.jsx via `
+```
+
+For the React component, create a thin wrapper:
+```js
+// Inside screens-editor.jsx, before the Editor component:
+function useTimecode() {
+ // Ensure TC is loaded
+ if (typeof TC === 'undefined') {
+ console.warn('timecode.js not loaded — timecode functions unavailable');
+ }
+ return {
+ framesToTC: (f) => TC ? TC.framesToTC(f) : String(f),
+ tcToFrames: (tc) => TC ? TC.tcToFrames(tc) : 0,
+ secondsToFrames: (s) => TC ? TC.secondsToFrames(s) : Math.round(s * 60),
+ framesToSeconds: (f) => TC ? TC.framesToSeconds(f) : f / 60,
+ FPS: TC ? TC.FPS : 59.94,
+ };
+}
+```
+
+**Verify:** In browser console — `TC.framesToTC(3596)` → `"00:01:00;00"`
+
+---
+
+### Task 1.3: React Timeline Component
+
+**Files:** MODIFY `services/web-ui/public/screens-editor.jsx` (add components inline, same file pattern as other screens)
+
+Build a `TimelinePanel` React component that wraps the existing `timeline.js` engine:
+
+```js
+function TimelinePanel({ clips, onClipsChanged, playheadFrames, onPlayheadMoved, fps }) {
+ const containerRef = React.useRef(null);
+ const engineRef = React.useRef(null);
+
+ React.useEffect(() => {
+ if (!containerRef.current || engineRef.current) return;
+ engineRef.current = window.Timeline;
+ window.Timeline.init(containerRef.current, {
+ fps: fps || 59.94,
+ scale: 100,
+ onClipsChanged: onClipsChanged,
+ onPlayheadMoved: onPlayheadMoved,
+ });
+ return () => { /* cleanup */ };
+ }, []);
+
+ React.useEffect(() => {
+ if (engineRef.current && clips) {
+ engineRef.current.render(clips, { fps: fps || 59.94 });
+ }
+ }, [clips, fps]);
+
+ React.useEffect(() => {
+ if (engineRef.current) {
+ engineRef.current.setPlayhead(playheadFrames || 0);
+ }
+ }, [playheadFrames]);
+
+ return (
+
+
+ {/* Tool buttons: Select(V), Razor(C), Hand(H) */}
+ {/* Undo/Redo */}
+ {/* Save status indicator */}
+
+
+
+ );
+}
+```
+
+The component mounts the timeline once, then pushes clip/playhead updates via `render()` and `setPlayhead()`. No rebuild needed on every frame.
+
+**Tool toolbar state:**
+- Three tool buttons (V/C/H) that call `Timeline.setTool(name)`
+- Active tool highlighted — sync with keyboard shortcuts
+- Undo/redo buttons call the editor's history stack (not timeline.js's — timeline.js doesn't have an undo stack, the editor page in the original plan managed it in the app state)
+
+**Verify:** Timeline renders 4 track rows (V1, V2, A1, A2) with ruler, playhead at frame 0, no clips. Tool buttons switch cursor mode.
+
+---
+
+### Task 1.4: Rewrite screens-editor.jsx
+
+**Files:** MODIFY `services/web-ui/public/screens-editor.jsx`
+
+Full rewrite preserving the `Editor` component on `window.Editor`. The new structure:
+
+```
+┌─ SOURCE MONITOR ───────┬─ PROGRAM MONITOR ───────┐
+│ [video preview] │ [video preview] │
+│ TC: 00:00:00;00 │ TC: 00:00:00;00 │
+│ [=====scrub=========] │ [=====scrub=========] │
+│ [In] [Out] [Insert] │ [▶] [⏮] [⏭] │
+├─ MEDIA PANEL ──────────┴─ TIMELINE ─────────────┤
+│ [sequence picker ▼] [+]│ [V] [C] [H] | [↩] [↪] │
+│ [filter by bin ▼] │ ruler: 00:00 — 00:05 … │
+│ [asset list] │ V1 ░░░░[clip]░░░░ │
+│ │ V2 │
+│ │ A1 ░░░░[clip]░░░░ │
+│ │ A2 │
+└────────────────────────┴─────────────────────────┘
+```
+
+**App state (React hooks):**
+```js
+const [projectId, setProjectId] = React.useState(null);
+const [sequences, setSequences] = React.useState([]);
+const [currentSeq, setCurrentSeq] = React.useState(null); // { id, name, clips: [...] }
+const [assets, setAssets] = React.useState([]); // from ZAMPP_DATA.ASSETS
+const [sourceAsset, setSourceAsset] = React.useState(null);
+const [srcIn, setSrcIn] = React.useState(null);
+const [srcOut, setSrcOut] = React.useState(null);
+const [playing, setPlaying] = React.useState(false);
+const [playheadFrames, setPlayheadFrames] = React.useState(0);
+const [saveStatus, setSaveStatus] = React.useState('');
+const [history, setHistory] = React.useState([[]]);
+const [historyIdx, setHistoryIdx] = React.useState(0);
+```
+
+**Source monitor:**
+- `