diff --git a/.gitignore b/.gitignore
index f1331ae5..b61a91a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,3 +90,11 @@ lib/firebase_options.dart
# Local agent worktrees and execution ledgers.
/.worktrees/
/.superpowers/
+
+.codegraph/
+
+# Rendered previews of docs/*.svg — regenerate from the svg rather than
+# committing them. The svg IS the source; these are just what gets pasted
+# into Discord/issues, and they're ~750kb a piece.
+docs/*.gif
+docs/*.mp4
diff --git a/docs/calorie-heatmap-ios-dark.png b/docs/calorie-heatmap-ios-dark.png
new file mode 100644
index 00000000..169d2761
Binary files /dev/null and b/docs/calorie-heatmap-ios-dark.png differ
diff --git a/docs/calorie-heatmap-ios-light.png b/docs/calorie-heatmap-ios-light.png
new file mode 100644
index 00000000..c138f66a
Binary files /dev/null and b/docs/calorie-heatmap-ios-light.png differ
diff --git a/docs/calorie-heatmap-preview.svg b/docs/calorie-heatmap-preview.svg
new file mode 100644
index 00000000..0efae41b
--- /dev/null
+++ b/docs/calorie-heatmap-preview.svg
@@ -0,0 +1,419 @@
+
diff --git a/lib/data/local_repository.dart b/lib/data/local_repository.dart
index de857c8b..7fbf5c1f 100644
--- a/lib/data/local_repository.dart
+++ b/lib/data/local_repository.dart
@@ -54,8 +54,16 @@ abstract class LocalRepository {
throw UnimplementedError('re-layer: getSleep');
Future>> getStrain({int? from, int? to}) =>
throw UnimplementedError('re-layer: getStrain');
- Future>> getSessions({int? from, int? to}) =>
- throw UnimplementedError('re-layer: getSessions');
+ /// Saved sessions in the window, merged with unconfirmed auto-detected bouts.
+ ///
+ /// Pass `includeDetected: false` when only saved sessions are wanted: the
+ /// detected half has to read every recent day bundle to find them, and those
+ /// rows carry the full hr_curve/hypnogram/HRV payload.
+ Future>> getSessions({
+ int? from,
+ int? to,
+ bool includeDetected = true,
+ }) => throw UnimplementedError('re-layer: getSessions');
Future