diff --git a/docs/challenge-explainers.md b/docs/challenge-explainers.md
index c3c55ee..95567ad 100644
--- a/docs/challenge-explainers.md
+++ b/docs/challenge-explainers.md
@@ -59,3 +59,27 @@ check the before/after story at phone width and desktop width; inspect a real
screenshot rather than relying on a build; test keyboard replay and reduced
motion; and keep the surrounding editorial to one observation, one invariant,
and one learner decision. The first pilots are Cache Pressure and Lossy Link.
+
+## Recovery and time batch
+
+These mechanisms should not inherit the cache tile composition. Bricolage
+states the consequence, Inter explains it in one sentence, and mono marks only
+literal keys, LSNs, ticks, and results. Layout stays left-aligned in the narrow
+problem pane.
+
+| Story | Surface and accent | Singular visual action |
+| --- | --- | --- |
+| Journal Recovery | Pale butter `#fff2c8`, ink `#282334`, verified mint `#a9e8cb`, torn orange `#ff784f` | A scan cursor reaches an incomplete third frame and stops; only the first two form the recovered prefix. |
+| LSM Compactor | Lilac `#d9cbff`, ink `#282334`, selected violet `#7954ef`, intersecting orange `#ff784f` | Two selected Level-0 ranges cast one union over Level 1, pulling in both overlapping runs. |
+| Stream Windows | Deep blue `#2141b4`, cyan `#53d9ff`, event orange `#ffad83`, final lime `#b7ff45` | A watermark reaches tick 10, emits and removes the left half-open window while the right remains live. |
+
+```text
+journal: [verified 1][verified 2][torn 3] scan stops before 3
+LSM: A [10------30] B [25-------------50]
+ X [0----20] Y [40------60]
+windows: [0,10) | [10,20) watermark = 10
+```
+
+The axes are conceptual but use the exact published numbers. Do not interpolate
+a fake performance result. In an editor split, keep critical labels legible
+without relying on viewport breakpoints.
diff --git a/src/lib/arena/statement.ts b/src/lib/arena/statement.ts
index ce1c89f..2fd471f 100644
--- a/src/lib/arena/statement.ts
+++ b/src/lib/arena/statement.ts
@@ -74,6 +74,9 @@ const conceptFigureProps = {
"systems.exchange": exchangeProps,
"systems.cache-story": z.object({ mode: z.enum(["lru", "scan"]) }).strict(),
"systems.lost-ack-story": z.object({}).strict(),
+ "systems.journal-story": z.object({}).strict(),
+ "systems.lsm-story": z.object({}).strict(),
+ "systems.window-story": z.object({}).strict(),
} as const;
const figureBlock = z.object({
@@ -84,6 +87,7 @@ const figureBlock = z.object({
"systems.snapshot", "systems.budget", "systems.frames",
"systems.ranges", "systems.exchange",
"systems.cache-story", "systems.lost-ack-story",
+ "systems.journal-story", "systems.lsm-story", "systems.window-story",
]),
props: z.record(z.string(), z.unknown()).optional(),
}).strict(),
diff --git a/src/packs/systems/index.ts b/src/packs/systems/index.ts
index 003a443..d659702 100644
--- a/src/packs/systems/index.ts
+++ b/src/packs/systems/index.ts
@@ -3,6 +3,7 @@
import { registerIllustration } from "../registry";
import { BudgetDiagram, ExchangeDiagram, FrameDiagram, RangeDiagram, SnapshotDiagram } from "./concept-illustrations";
import { FlowDiagram, StateDiagram, TimelineDiagram } from "./illustrations";
+import { JournalRecoveryStory, LsmOverlapStory, StreamWatermarkStory } from "./recovery-time-stories";
import { CacheStory, LostAckStory } from "./story-illustrations";
registerIllustration("systems.flow", FlowDiagram);
@@ -15,3 +16,6 @@ registerIllustration("systems.ranges", RangeDiagram);
registerIllustration("systems.exchange", ExchangeDiagram);
registerIllustration("systems.cache-story", CacheStory);
registerIllustration("systems.lost-ack-story", LostAckStory);
+registerIllustration("systems.journal-story", JournalRecoveryStory);
+registerIllustration("systems.lsm-story", LsmOverlapStory);
+registerIllustration("systems.window-story", StreamWatermarkStory);
diff --git a/src/packs/systems/recovery-time-stories.tsx b/src/packs/systems/recovery-time-stories.tsx
new file mode 100644
index 0000000..275211a
--- /dev/null
+++ b/src/packs/systems/recovery-time-stories.tsx
@@ -0,0 +1,216 @@
+"use client";
+
+import { cn } from "@/lib/utils";
+
+import { Replay, useVisibleStory } from "./story-illustrations";
+
+const JOURNAL_STEPS = [
+ "LSN 1 and 2 are synced, complete frames with valid checksums.",
+ "A third write stops before its payload and checksum are complete.",
+ "Recovery scans left to right and finds the incomplete tail.",
+ "Apply LSN 1 and 2 only. Report a torn tail.",
+] as const;
+
+export function JournalRecoveryStory() {
+ const { ref, stage, reducedMotion, replay } = useVisibleStory(3, 1100);
+
+ return (
+