diff --git a/HANDOFF.md b/HANDOFF.md
index c8ea542..e9f39e4 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -442,6 +442,22 @@ the countdown panel ('CHANGEOVER · RESUMING IN n'). He kept the burst-arm weapo
praise words. **A bottom/above-rack message slot was proposed and REJECTED**: that is thumb
territory — covered by the hand mid-pull and outside where the eyes are.
+**Fifth batch, same day — THE BATTERY PASS.** "Drains battery like there is no tomorrow." Measured
+first: update() 0.01 ms, render() 0.15 ms per frame — the JS is nothing. The bill is fill-rate ×
+frame-rate (a 3x iPhone pushes a ~1080×2400 canvas through the compositor 60×/s) plus 60 Hz wakeups
+on static screens. Three levers shipped:
+- **The canvas is OPAQUE now** (`dom.js`: `getContext('2d', {alpha:false, desynchronized:true})`)
+ — render() clears the whole frame to a solid colour anyway, so the compositor no longer alpha-
+ blends 2.5 Mpx sixty times a second. Free.
+- **Calm states run slow** (`calmFps()` in main.js): a SETTLED modal (world frozen, only the blink
+ moves) runs at 20 fps, the death summary once faded at 24. Live play stays at TUNE.fps — the 60
+ was a feel decision and the governor deliberately never touches anything that moves.
+- **`TUNE.lowPower`** (>= 0.5, T-panel-flippable live): caps RES at 3 — −44% of pixels painted per
+ frame, THE big lever. `RES` is `export let` now, re-derived in fitViewport; main.js re-fits when
+ the knob flips. Sprites are 224px sources so RES 3 draws them at-or-below 1:1: softer, never
+ mushy. OFF by default — whether the sharpness trade is worth it is Oliver's phone-call, and if he
+ wants it player-facing it should become a settings toggle, not a TUNE knob.
+
**Fourth batch, same day — the card went HORIZONTAL, and nothing draws under a modal.** The
stacked icon card ran 102 px tall and its lower third hung over live court; the icon sits LEFT of
the text now (boxed to 58×52 so a wide tin cannot reach the type — and it draws BIGGER than the
diff --git a/config.js b/config.js
index 7d6e976..98c882e 100644
--- a/config.js
+++ b/config.js
@@ -312,6 +312,9 @@ export const TUNE = {
barrelHp: 1, // anything pops it: ball, pellet, bomb, flame, swish. It is a target, not a body
barrelH: 1.15, // drawn height (a body is 1.8)
fps: 60, // frame cap — 30 halves battery/heat but Oliver felt it; smoothness wins (0 = uncapped)
+ lowPower: 0, // >= 0.5 caps RES at 3 (−44% of pixels painted per frame) — the battery-saver
+ // switch, flippable live from the T panel. Play speed and fps are untouched:
+ // the trade is sharpness only, and 224px sprites keep it "softer", never mushy.
runnerSpeed: 3.2, // runner ('f') speed multiplier over the shamble
runnerVMax: 4.2, // hard runner speed cap — the late-game ramp must stay humanly reactable
shamblerHp: 2, // splat-quality hits a classic shambler ('a'/'b') soaks before it dies — only the runner ('f') is still one-shot glass
diff --git a/dom.js b/dom.js
index 07b5bf9..8cc4bbb 100644
--- a/dom.js
+++ b/dom.js
@@ -1,3 +1,6 @@
// Canvas handles — imported by everyone, imports nothing (keeps module cycles harmless).
export const cv = document.getElementById('c');
-export const ctx = cv.getContext('2d');
+// alpha:false — render() clears the whole frame to an opaque colour anyway, so declaring the canvas
+// opaque lets the compositor skip blending a 2.5-megapixel layer 60 times a second (battery, not
+// looks). desynchronized is a further no-op-where-unsupported compositor shortcut (Chrome).
+export const ctx = cv.getContext('2d', { alpha: false, desynchronized: true });
diff --git a/index.html b/index.html
index 388343c..1aca1cf 100644
--- a/index.html
+++ b/index.html
@@ -359,6 +359,6 @@
-
+