diff --git a/HANDOFF.md b/HANDOFF.md index 396b53b..53b6cc3 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -442,6 +442,14 @@ 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. +**Sixth batch, same day — the live card-tuning session (Oliver in the preview pane).** Settled by +looking, together: the card is `CARD_PAD` 20 with the icon boxed to 46×44 and `CARD_TOP` 88 (flush +under the flash row). And the RESUMING countdown left the band entirely: **Bangers 16, gold, +shadowed, floating just above the net tape — no panel, no border.** His call: the countdown +belongs to the court the pause is holding, and the shout face survives any backdrop, which retires +the panel that was added for clay-crowd readability two batches ago. The changeover still names +itself in the same line ('CHANGEOVER · RESUMING IN n'). + **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 diff --git a/index.html b/index.html index b702b14..158b2fb 100644 --- a/index.html +++ b/index.html @@ -359,6 +359,6 @@ - + diff --git a/render.js b/render.js index 8947428..91c4044 100644 --- a/render.js +++ b/render.js @@ -1190,7 +1190,8 @@ const AD_TOP = () => Math.round(proj(0, 0, -6).y) - AD_H; // top of the courtsid // or not — the icon sits LEFT of the text instead of stacking above it. Stacked, an icon card ran // 102 px tall and its lower third hung over live court; side-by-side it is 62 px flat, sits fully // over the crowd, and the art actually draws BIGGER than the stacked layout gave it. -const CARD_TOP = 96, CARD_H = 62, CARD_PAD = 14; // px of side padding inside the band +const CARD_TOP = 88, CARD_H = 62, CARD_PAD = 20; // px of side padding inside the band; top sits + // flush under the flash row (ends ~88) function drawCard(title, sub1, sub2, color, icon) { const img = icon && SPR[icon]; @@ -1199,7 +1200,7 @@ function drawCard(title, sub1, sub2, color, icon) { // it — centred type next to a left icon read as two unrelated things sharing a band let tx = CARD_PAD; if (img) { // vertically centred, boxed so a wide tin can't reach the type - const k = Math.min((CARD_H - 10) / img.height, 58 / img.width); + const k = Math.min((CARD_H - 18) / img.height, 46 / img.width); const iw = Math.round(img.width * k), ih = Math.round(img.height * k); ctx.imageSmoothingEnabled = false; ctx.drawImage(img, CARD_PAD, Math.round(CARD_TOP + (CARD_H - ih) / 2), iw, ih); @@ -1784,22 +1785,15 @@ export function render() { : Math.max(G.changeover > 0 ? G.changeover : 0, G.phase === 'over' ? G.overT : 0, G.interlude ? G.interlude.introT : 0); - // A live card owns the band and IS the announcement of the pause — the countdown yields to it. - if (waitT > 0.5 && !(G.card && G.card.t > 0 && !(G.card.wait > 0))) { - // In the message band with the rest of the family, and on its OWN PANEL: bare shadowed text - // was barely readable over the sunlit clay crowd (Oliver flagged it every court since). - // Plain Silkscreen on purpose — a countdown is information, not an announcement. - // the changeover names itself here — its old separate flash was the same word, twice + if (waitT > 0.5) { + // OVER THE NET, in the shout face, no box — Oliver's call after trying it panelled in the + // band: the countdown belongs to the COURT the pause is holding, and Bangers + shadowText is + // the register that survives any backdrop (it is what every flash already rides). + // The changeover names itself here — its old separate flash was the same word, twice. const txt = (G.changeover > 0 ? 'CHANGEOVER · ' : '') + 'RESUMING IN ' + Math.ceil(waitT); - const y = Math.min(FLASH_Y + 26, AD_TOP() - 18); - ctx.font = F(10); - const tw = Math.ceil(ctx.measureText(txt).width); - ctx.lineWidth = 1; - // panel top hugs the glyphs: Silkscreen carries its own top leading under baseline 'top', - // so a symmetric-looking box needs the frame to start a px above the text, not four - panel(W / 2 - tw / 2 - 7, y - 1, tw + 14, 15, 3, 'rgba(8,26,18,0.85)', hexA(UI.warm, 0.4)); + ctx.font = B(16); ctx.textBaseline = 'top'; - shadowText(txt, W / 2, y, hexA(UI.warm, 0.95)); + shadowText(txt, W / 2, Math.round(proj(0, COURT.netH, COURT.netZ).y) - 26, hexA(UI.warm, 0.95)); } if (G.phase === 'pretoss' && score.server === 'P' && G.mode !== 'eaten' && !G.horde && !(G.mgT > 0) && !G.aim && waitT <= 0.5) { // while pulling, the draw meter owns this slot ctx.font = F(8);