From 15f9c07743daeeedd9b03139652d1d19dcffa909 Mon Sep 17 00:00:00 2001 From: Hashem Aldhaheri <158606+aenawi@users.noreply.github.com> Date: Sun, 13 Sep 2026 07:21:44 +0400 Subject: [PATCH] Measure the fourth side too, so the verdict clears a bottom-docked card MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HUD overlay insets itself by whatever the furniture is covering on three sides and by a flat 12px on the fourth. Since #79 gave a theme a bottom dock, that fourth side has something under it: on Warroom the verdict panel was drawn 30px behind the tally card and the transcript — the one line on the board that is a command rather than a rendering of a number — was inside it completely. --pad-b could not simply be added to the padding. It is measured against the window, because the page verdict and the view controls have to clear the rung ladder, and the ladder sits below the viewport rather than over it. The overlay is laid out inside the viewport, so reading --pad-b there clears the ladder a second time and lifts the whole bottom stack a ladder higher than the card it only had to clear by a gap. So publish the number the camera already gets — what covers the board, not what is occupied at the foot of the window — as --pad-b-canvas, and read that. The other three sides need no second token: the viewport starts at the top-left corner of the window, so there the two rectangles agree. Measured in Chrome 147 at 1440x760 on 2026-09-13, Warroom, after a probe: before after tally card #hud-verdict 393-514 393-514 597-700 #hud-transcript 670-698 (inside) 557-585 overlay pad-b 12px 125px The default page is untouched: with nothing docked --pad-b-canvas is 0px and the padding stays at 12px, the transcript at 670-698 exactly as before. replay.html gets the same treatment, where the thing below the board is the transport rather than a card. The gauges do not move. The top row is an `auto` row above a `1fr`, so the padding comes out of the gap; at 625px — one pixel above the height where the layout floats and the question stops applying — the overlay already overflows with the old rule and both rules put the bottom stack in the same place. Closes #81 --- lab/control/ui/app.js | 18 ++++++++++++++++++ lab/control/ui/replay-app.js | 5 +++++ lab/control/ui/style.css | 10 ++++++++++ 3 files changed, 33 insertions(+) diff --git a/lab/control/ui/app.js b/lab/control/ui/app.js index 49d2f1e..74d4a59 100644 --- a/lab/control/ui/app.js +++ b/lab/control/ui/app.js @@ -391,6 +391,24 @@ root.setProperty("--pad-t", Math.round(covered.top) + "px"); /* The floating panels clear the ladder; the camera, above, does not. */ root.setProperty("--pad-b", Math.round(padBottom) + "px"); + /* The bottom edge is the only one that needs saying twice. --pad-l, -r + and -t are the same number against either rectangle, because the + viewport starts at the top-left corner of the window and the rails are + drawn over it. The bottom is where the two part company, and which one + a rule wants depends on what it is positioned inside: + + --pad-b against the window. The page verdict and anything + else that sits in .app, which the ladder is in. + --pad-b-canvas against the board. The HUD overlay is laid out + inside .viewport, whose bottom edge is already above + the ladder — so clearing the ladder a second time + lifts the verdict a whole ladder higher than the card + it only had to clear by a gap. + + Same number the camera is handed, for the same reason: it is what + covers the board rather than what is occupied at the foot of the + window. */ + root.setProperty("--pad-b-canvas", Math.round(covered.bottom) + "px"); if (board && board.setViewInset) board.setViewInset(covered); } diff --git a/lab/control/ui/replay-app.js b/lab/control/ui/replay-app.js index d9715da..e763e9b 100644 --- a/lab/control/ui/replay-app.js +++ b/lab/control/ui/replay-app.js @@ -218,6 +218,11 @@ root.setProperty("--pad-r", Math.round(covered.right) + "px"); root.setProperty("--pad-t", Math.round(covered.top) + "px"); root.setProperty("--pad-b", Math.round(padBottom) + "px"); + /* The camera's answer, published, because the HUD overlay is laid out + inside the viewport and so wants the same one: what covers the *board* + — here the transport and whatever the docks hide — and not the ladder + below it. Why only this edge needs a second token is in app.js. */ + root.setProperty("--pad-b-canvas", Math.round(covered.bottom) + "px"); if (board && board.setViewInset) board.setViewInset(covered); } diff --git a/lab/control/ui/style.css b/lab/control/ui/style.css index 26eed98..d49745d 100644 --- a/lab/control/ui/style.css +++ b/lab/control/ui/style.css @@ -613,6 +613,16 @@ pre.out.live { max-height: 38vh; overflow-y: auto; } position: absolute; inset: 0; pointer-events: none; padding: 12px calc(var(--pad-r, 0px) + 12px) 12px calc(var(--pad-l, 0px) + 12px); padding-top: calc(var(--pad-t, 0px) + 12px); + /* The fourth side, and it reads a different token from the other three. + Those are the same number against the window or against the board; this + one is not, because the rung ladder is below the viewport rather than + over it. --pad-b clears the ladder — right for .verdict, which is in + .app — and this box is inside .viewport, whose bottom edge is already + above the ladder. --pad-b-canvas is the number measured there, so the + verdict and the transcript stop on top of a bottom-docked card rather + than under it, and without a whole ladder of dead air when nothing is + docked at all. */ + padding-bottom: calc(var(--pad-b-canvas, 0px) + 12px); display: grid; grid-template-rows: auto 1fr auto; gap: 8px; transition: padding .18s ease; }