diff --git a/HANDOFF.md b/HANDOFF.md
index 33b6207..520857d 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -343,7 +343,13 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
`perfectMs` window, the slingshot earns it by STOPPING in `pullFull..pullFull*perfectBand`. The
draw clicks brighter when you enter that band and goes quiet when you overshoot — without that
audible target it would be invisible skill. The word itself rotates through `PRAISE` (cycled, not
- random: the same word twice running reads as a stuck HUD element, not as praise).
+ random: the same word twice running reads as a stuck HUD element, not as praise) and sits behind
+ `TUNE.praiseEvery`, the same shared-cooldown model the umpire's asides use — you LEARN to hit the
+ band, and then a per-serve word is noise again whatever it says. The click is the feedback for a
+ good pull; the word is garnish.
+- **The free-floating slot is 18px, not card-sized.** Half of what lands in it (the praise words,
+ `RETURNED`, `DOUBLE KILL`) is a footnote to something you already watched happen. Sharing the loud
+ FACE with the milestone cards is right; sharing their SIZE made every footnote read as a milestone.
## Session log: the sidestep, and two type registers (2026-08-03)
diff --git a/config.js b/config.js
index e808c4d..3ac4231 100644
--- a/config.js
+++ b/config.js
@@ -195,6 +195,10 @@ export const TUNE = {
// needs the pull to STOP in the band pullFull..pullFull*this (130-166 px), which
// is a target you can miss. The pull clicks when you enter it (see input.js) and
// goes quiet when you overshoot, so the window is learnable by feel.
+ praiseEvery: 14, // s between praise words, on the umpire's-asides model (asideEvery below). The
+ // band alone was not enough: you learn to hit it, and then the HUD talks on
+ // every other serve again. The CLICK is the reliable feedback for a good pull —
+ // the word is garnish, and garnish on every plate is just food.
aimCone: 0.10, // lane half-width added per meter of range. This is what makes the pull's ANGLE
// mean something: at 0.45 the cone was ±10 m at the far baseline — the whole
// court — so every downward pull picked the same body and the angle felt inert.
diff --git a/gameplay.js b/gameplay.js
index dd74e9c..73da091 100644
--- a/gameplay.js
+++ b/gameplay.js
@@ -43,7 +43,14 @@ export const pullPerfect = mag => mag >= TUNE.pullFull && mag <= TUNE.pullFull *
// running is what made one word read as a stuck HUD element rather than as praise.
const PRAISE = ['PERFECT', 'TEXTBOOK', 'IMMACULATE', 'OH, LOVELY', 'UNPLAYABLE', 'SHOT OF THE DAY'];
let praiseN = 0;
-const praise = () => { G.flash = { text: PRAISE[praiseN++ % PRAISE.length], t: 0.7 }; };
+// ...and he does not say it every time. Same cooldown model as the asides (aside()): once you have
+// learned the band you hit it constantly, and a HUD that talks on every other serve is noise
+// whatever the word is. The bright click in the pull is the reliable feedback; this is the garnish.
+function praise() {
+ if (G.praiseT > 0) return;
+ G.praiseT = TUNE.praiseEvery;
+ G.flash = { text: PRAISE[praiseN++ % PRAISE.length], t: 0.7 };
+}
export function flickTarget(ch, aim) { // exported: render draws the live reticle with it
const q = pullQuality(aim.mag), f = pullFrac(aim.mag);
@@ -1213,6 +1220,7 @@ export function update(dt) {
crowd.cheer = Math.max(0, crowd.cheer - dt);
G.idleT += dt;
if (G.asideT > 0) G.asideT -= dt; // the asides' shared cooldown (see aside())
+ if (G.praiseT > 0) G.praiseT -= dt; // ...and the praise words' (see praise())
// the fired band unravels behind the ball. It also goes the moment the ball does — a shambler
// eating the shot ends the band with the splat, which is a cut you want.
if (G.trail && ((G.trail.t += dt) >= G.trail.T || !ball.active)) G.trail = null;
diff --git a/index.html b/index.html
index ac0fbd8..ff69d58 100644
--- a/index.html
+++ b/index.html
@@ -184,6 +184,6 @@
-
+