Tuned live, together: the card breathes, the countdown floats the net

- card: padding 20, smaller icon (46x44 box), band up to 88
- RESUMING IN: Bangers over the net tape, no panel — the countdown
  belongs to the court the pause is holding

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-08 21:32:33 +02:00
1 parent f69fc12742
commit dc35b802dc
3 files changed
+19 -17

No files matched your search

+10 -16
View File
@@ -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);