`, ``, `
`. One step, 5 checkpoints using `element-exists` and `element-text` rules. Starter file is a bare `\n\n`.
+
+- [ ] **Step 3: Write the first challenge JSON**
+
+Create `src/lessons/data/challenge-01-simple-page.json` with `allowFileOps: true`, starter files `index.html` + `style.css`, and checkpoints for h1, p, background-color.
+
+- [ ] **Step 4: Write the registry**
+
+```ts
+// src/lessons/data/index.ts
+import type { LessonData } from "../types";
+import html01 from "./html-01-structure.json";
+import challenge01 from "./challenge-01-simple-page.json";
+
+export const LESSONS: readonly LessonData[] = [html01 as LessonData];
+export const CHALLENGES: readonly LessonData[] = [challenge01 as LessonData];
+export const ALL_CONTENT: readonly LessonData[] = [...LESSONS, ...CHALLENGES];
+
+export function getLessonById(id: string): LessonData | undefined {
+ return ALL_CONTENT.find((l) => l.id === id);
+}
+```
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/lessons/types.ts src/lessons/data/
+git commit -m "feat(lessons): add data types, first lesson and challenge content"
+```
+
+---
+
+### Task 5: Lesson store + progress persistence
+
+**Files:**
+
+- Create: `src/lessons/lessonStore.ts`
+- Create: `src/lessons/progressPersistence.ts`
+- Test: `src/lessons/lessonStore.test.ts`
+
+- [ ] **Step 1: Write the lesson store**
+
+Zustand store with `activeLessonId`, `checkpointStates`, `startLesson(id)`, `completeCheckpoint(id)`, `resetLesson(id)`, `exitLesson()`. `startLesson` hydrates VFS with starter files and sets `useIdeStore.view` to `"lesson"`.
+
+- [ ] **Step 2: Write progress persistence**
+
+`loadProgress()` and `saveProgress()` using `persistedStore("wecode.progress.json")`, same pattern as `src/projects/persistence.ts`.
+
+- [ ] **Step 3: Write tests for the store**
+
+Test `startLesson` sets `activeLessonId`, `completeCheckpoint` transitions status, `exitLesson` clears state and sets view to `"home"`.
+
+- [ ] **Step 4: Run tests, commit**
+
+```bash
+git add src/lessons/lessonStore.ts src/lessons/progressPersistence.ts src/lessons/lessonStore.test.ts
+git commit -m "feat(lessons): add lesson store and progress persistence"
+```
+
+---
+
+### Task 6: Home store + rail routing
+
+**Files:**
+
+- Create: `src/home/homeStore.ts`
+- Modify: `src/home/rail/HomeNav.tsx`
+- Modify: `src/home/HomeShell.tsx`
+
+- [ ] **Step 1: Create home store**
+
+```ts
+// src/home/homeStore.ts
+import { create } from "zustand";
+
+export type HomeTab = "accueil" | "lessons" | "challenges";
+
+interface HomeState {
+ tab: HomeTab;
+ setTab: (tab: HomeTab) => void;
+}
+
+export const useHomeStore = create((set) => ({
+ tab: "accueil",
+ setTab: (tab) => set({ tab }),
+}));
+```
+
+- [ ] **Step 2: Update HomeNav with routable items + counters**
+
+Add Leçons item (with `X / Y` counter from progress store) and Challenges item. Accueil is the default active. Items like Projets, Cheatsheets, Settings stay disabled.
+
+- [ ] **Step 3: Update HomeShell to route based on tab**
+
+```tsx
+// HomeShell main zone switches on useHomeStore.tab:
+// "accueil" → current content (AccueilView extracted from current HomeShell)
+// "lessons" →
+// "challenges" →
+```
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add src/home/homeStore.ts src/home/rail/HomeNav.tsx src/home/HomeShell.tsx
+git commit -m "feat(home): add rail tab routing for lessons and challenges"
+```
+
+---
+
+### Task 7: Lessons & Challenges list views
+
+**Files:**
+
+- Create: `src/home/sections/LessonCard.tsx`
+- Create: `src/home/sections/LessonsListView.tsx`
+- Create: `src/home/sections/ChallengesListView.tsx`
+
+- [ ] **Step 1: Create LessonCard component**
+
+Displays: status badge (terminé/en cours/pas commencé), estimated time, title, description, optional progress bar. Clicking calls `useLessonStore.startLesson(id)`.
+
+- [ ] **Step 2: Create LessonsListView**
+
+Imports `LESSONS` from registry, reads progress from `useLessonStore`, renders a grid of `LessonCard` components with the heading "Toutes les leçons" and a completion counter.
+
+- [ ] **Step 3: Create ChallengesListView**
+
+Same structure but for challenges. Status is `✓ réussi` or `pas tenté`. No progress bar.
+
+- [ ] **Step 4: Add CSS for lesson cards and list views**
+
+New `.lesson-card`, `.lesson-card--in-progress`, `.lesson-card--done` classes in `global.css`. Follow the existing card pattern (`.home-proj` style) with status badges using `--ok` for done and `--accent` for in-progress.
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add src/home/sections/LessonCard.tsx src/home/sections/LessonsListView.tsx src/home/sections/ChallengesListView.tsx src/styles/global.css
+git commit -m "feat(home): add lessons and challenges list views with cards"
+```
+
+---
+
+### Task 8: LessonProvider context
+
+**Files:**
+
+- Create: `src/lessons/LessonProvider.tsx`
+- Create: `src/lessons/useLessonContext.ts`
+
+- [ ] **Step 1: Create the context and provider**
+
+`LessonProvider` wraps children, reads `activeLessonId` from `useLessonStore`, loads the lesson data via `getLessonById`, subscribes to VFS changes (debounced 300 ms) to run `validateCheckpoints`, and exposes the `LessonContextValue` interface from the spec.
+
+- [ ] **Step 2: Create the consumer hook**
+
+```ts
+// src/lessons/useLessonContext.ts
+import { useContext } from "react";
+import { LessonContext } from "./LessonProvider";
+
+export function useLessonContext() {
+ return useContext(LessonContext);
+}
+```
+
+Returns `LessonContextValue | null`. Components check for null to know if they're in lesson mode.
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/lessons/LessonProvider.tsx src/lessons/useLessonContext.ts
+git commit -m "feat(lessons): add LessonProvider context with validation wiring"
+```
+
+---
+
+### Task 9: Dock UI rewrite
+
+**Files:**
+
+- Modify: `src/ide/dock/LessonDock.tsx`
+- Modify: `src/styles/global.css`
+
+- [ ] **Step 1: Rewrite LessonDock to read context**
+
+Replace mock data imports with `useLessonContext()`. If context is null, return null (hides dock in project mode). Render the design-faithful layout: `.dock-head` with chevron + lesson-chip + progress, `.dock-body` grid with `.lesson-text` (left) and `.checkpoints` (right). Checkpoint rows use `.cp`, `.cp.active`, `.cp.done` classes based on `checkpoints[i].status`.
+
+- [ ] **Step 2: Update CSS to match the Claude Design handoff**
+
+Port the CSS from `WeCode IDE.html:504–610` into `global.css`. Key classes: `.dock`, `.dock-head`, `.lesson-chip`, `.progress`, `.progress-ring`, `.dock-body`, `.lesson-text`, `.checkpoints`, `.cp`, `.cp.done`, `.cp.active`. All using existing design tokens.
+
+- [ ] **Step 3: Remove mockLesson.ts**
+
+Delete `src/ide/dock/mockLesson.ts` — no longer needed.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add src/ide/dock/LessonDock.tsx src/ide/dock/mockLesson.ts src/styles/global.css
+git commit -m "feat(dock): rewrite lesson dock to use context and match design"
+```
+
+---
+
+### Task 10: IDE shell adaptations
+
+**Files:**
+
+- Modify: `src/ide/tree/FileTree.tsx`
+- Modify: `src/ide/shell/Toolbar.tsx`
+- Modify: `src/ide/shell/StatusBar.tsx`
+- Modify: `src/App.tsx`
+- Modify: `src/state/ideStore.ts`
+
+- [ ] **Step 1: Add "lesson" to the View type**
+
+In `src/state/ideStore.ts`, extend `View = "home" | "ide"` to `View = "home" | "ide" | "lesson"`.
+
+- [ ] **Step 2: FileTree — hide file-ops buttons in lesson mode**
+
+Read `useLessonContext()`. If `context?.fileOpsLocked`, hide the "new file" button and suppress the create/rename/delete context menu items.
+
+- [ ] **Step 3: Toolbar — brand click exits lesson**
+
+Read `useLessonContext()`. If context exists, brand click calls `context.exitLesson()` instead of `setView("home")`.
+
+- [ ] **Step 4: StatusBar — show checkpoint progress**
+
+Read `useLessonContext()`. If context exists, replace the version chip with a checkpoint counter (`2 / 4 checkpoints`).
+
+- [ ] **Step 5: App.tsx — render LessonProvider when view is "lesson"**
+
+```tsx
+{
+ view === "lesson" ? (
+
+
+
+ ) : view === "home" ? (
+
+ ) : (
+
+ );
+}
+```
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add src/state/ideStore.ts src/ide/tree/FileTree.tsx src/ide/shell/Toolbar.tsx src/ide/shell/StatusBar.tsx src/App.tsx
+git commit -m "feat(ide): adapt shell components for lesson mode via context"
+```
+
+---
+
+### Task 11: Continue card adaptation
+
+**Files:**
+
+- Modify: `src/home/sections/ContinueSection.tsx`
+
+- [ ] **Step 1: Read lesson progress alongside project data**
+
+If an in-progress lesson exists (at least one checkpoint done, not all done), show it in the continue card instead of the last project. Priority: lesson in progress > last project.
+
+- [ ] **Step 2: Adapt continue card display for lessons**
+
+Show lesson title, step name, checkpoint progress (`2 / 4 checkpoints`), "Continuer" button calls `startLesson(id)`.
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add src/home/sections/ContinueSection.tsx
+git commit -m "feat(home): continue card shows in-progress lesson"
+```
+
+---
+
+### Task 12: Integration verification
+
+- [ ] **Step 1: Run full CI**
+
+```bash
+npm run typecheck
+npm run lint
+npm run format:check
+npm test
+npm run build
+cargo check --manifest-path src-tauri/Cargo.toml
+cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings
+```
+
+- [ ] **Step 2: Manual smoke test**
+
+Following the spec verification checklist:
+
+1. Home → rail → click Leçons → grid appears
+2. Click lesson card → IDE opens with starter files, dock shows instructions
+3. Edit code to pass checkpoints → validation runs, checkpoints animate to "done"
+4. Complete all → lesson marked complete
+5. Brand click → back to Home, lesson shows ✓
+6. Challenges → click challenge → IDE with file ops enabled
+7. Quit mid-lesson → reopen → progress restored
+
+- [ ] **Step 3: Final commit + push**
+
+```bash
+git push
+```
diff --git a/docs/superpowers/specs/2026-04-26-lessons-system-design.md b/docs/superpowers/specs/2026-04-26-lessons-system-design.md
new file mode 100644
index 0000000..9e00175
--- /dev/null
+++ b/docs/superpowers/specs/2026-04-26-lessons-system-design.md
@@ -0,0 +1,306 @@
+# Lessons & Challenges System — Design Spec
+
+## Context
+
+WeCode is a desktop IDE that teaches HTML and CSS. The app currently has two views: Home (project picker) and IDE (editor + preview). This spec adds a third pillar: **structured learning** through guided lessons and free-form challenges, both reusing the existing IDE shell.
+
+The system ships as milestone 3. Scope for v1: the full engine (navigation, validation, progression, dock UI) plus **1 lesson + 1 challenge** as proof of concept.
+
+## Architecture
+
+### View routing
+
+```
+useIdeStore.view: "home" | "ide" | "lesson"
+```
+
+- `"home"` — Home shell with rail-routed sub-views
+- `"ide"` — Free project editing (unchanged)
+- `"lesson"` — IDE shell wrapped in a `LessonProvider` context
+
+### Home sub-views (rail as router)
+
+The Home rail gains clickable items that swap the main content zone:
+
+```
+useHomeStore.tab: "accueil" | "lessons" | "challenges"
+```
+
+| Rail item | Tab value | What the main zone shows |
+| ---------- | -------------- | ------------------------------------------------------------------------ |
+| Accueil | `"accueil"` | Current home content (search, continue card, recent projects, templates) |
+| Leçons | `"lessons"` | Grid of all lessons with progress indicators |
+| Challenges | `"challenges"` | Grid of all challenges with completion status |
+
+Items not yet wired (Projets, Cheatsheets, Settings) remain visible but disabled.
+
+The rail shows a progress counter next to Leçons: `3 / 12` (completed / total).
+
+### LessonProvider (React Context)
+
+When `view === "lesson"`, `App.tsx` renders:
+
+```tsx
+
+
+
+```
+
+The context exposes:
+
+```ts
+interface LessonContextValue {
+ lesson: LessonData;
+ checkpoints: CheckpointState[]; // { id, status: "todo"|"active"|"done" }
+ currentStepIndex: number;
+ fileOpsLocked: boolean; // true for lessons, false for challenges
+ progress: number; // 0–1 ratio
+ validateNow: () => void;
+ exitLesson: () => void;
+}
+```
+
+Components that adapt their behavior in lesson mode:
+
+- **FileTree** — hides create/delete/rename buttons when `fileOpsLocked`
+- **Toolbar** — brand click calls `exitLesson()` instead of `setView("home")`
+- **LessonDock** — reads `lesson`, `checkpoints`, `currentStepIndex` from context
+- **StatusBar** — shows checkpoint progress instead of version chip
+
+When the context is absent (free project mode), all components behave as today.
+
+### Stores
+
+**`useLessonStore`** (new Zustand store):
+
+- `activeLessonId: string | null`
+- `checkpointStates: Record`
+- `startLesson(id): void` — hydrates VFS with starter files, sets view to `"lesson"`
+- `completeCheckpoint(id): void`
+- `resetLesson(id): void`
+
+**`useHomeStore`** (new Zustand store):
+
+- `tab: "accueil" | "lessons" | "challenges"`
+- `setTab(tab): void`
+
+**Global progression** — persisted via `tauri-plugin-store` as `wecode.progress.json`:
+
+```ts
+interface ProgressStore {
+ version: 1;
+ completed: Record; // lessonId → timestamp
+ checkpoints: Record; // lessonId → completed checkpoint ids
+}
+```
+
+## Lesson data format
+
+Each lesson/challenge is a JSON file in `src/lessons/data/`:
+
+```ts
+interface LessonData {
+ id: string;
+ type: "lesson" | "challenge";
+ title: string;
+ description: string;
+ difficulty: "débutant" | "intermédiaire" | "avancé";
+ estimatedMinutes: number;
+ tags: string[];
+ allowFileOps: boolean; // false for lessons, true for challenges
+ starterFiles: Record; // VFS path → content
+ steps: LessonStep[];
+}
+
+interface LessonStep {
+ heading: string;
+ paragraphs: LessonParagraph[];
+ checkpoints: CheckpointDef[];
+}
+
+interface LessonParagraph {
+ kind: "text" | "code";
+ content: string; // text may contain `backtick` for inline code
+}
+
+interface CheckpointDef {
+ id: string;
+ label: string;
+ rule: ValidationRule;
+}
+```
+
+## Validation engine
+
+### Rule types
+
+| Type | Fields | What it checks |
+| -------------------- | -------------------------------------------------- | ------------------------------------------------------------------------ |
+| `element-exists` | `selector`, `file` | `querySelector(selector)` returns ≥1 match |
+| `element-count` | `selector`, `file`, `min?`, `max?` | Match count within range |
+| `element-text` | `selector`, `file`, `text`, `match` | Element text content matches (`contains`, `exact`, `regex`, `not-empty`) |
+| `attribute-exists` | `selector`, `file`, `attribute` | Element has the attribute |
+| `attribute-value` | `selector`, `file`, `attribute`, `value?`, `match` | Attribute value check |
+| `attribute-count` | `selector`, `file`, `minAttributes` | Element has ≥N attributes |
+| `css-property` | `selector`, `file`, `property`, `match` | CSS property exists on selector |
+| `css-property-value` | `selector`, `file`, `property`, `value`, `match` | CSS property value check (`exact`, `contains`, `gte`, `lte`) |
+| `file-contains` | `file`, `text` | Raw source contains string |
+| `file-not-contains` | `file`, `text` | Raw source does NOT contain string |
+| `file-regex` | `file`, `pattern` | Raw source matches regex |
+| `nesting` | `parent`, `child`, `direct?`, `file` | Child is descendant (or direct child) of parent |
+| `element-order` | `selectors[]`, `within`, `file` | Elements appear in document order |
+| `sibling` | `first`, `then`, `file` | Two elements are adjacent siblings |
+| `indent-style` | `file`, `style` (`spaces`\|`tabs`), `size?` | Source follows indentation convention |
+| `composite` | `operator` (`and`\|`or`), `rules[]` | Combines multiple rules |
+
+### Match modes
+
+`exact`, `contains`, `starts-with`, `ends-with`, `regex`, `not-empty`, `exists`, `gte`, `lte`.
+
+### Execution
+
+- **Trigger**: VFS `change` event, debounced 300 ms.
+- **Method**: `DOMParser` parses the HTML source into a DOM. `querySelector` / `querySelectorAll` run the selectors. CSS rules are checked by parsing `