zebra-spaces: cameras column truly hides when empty + timeline reflows

Switched from 'reserve 220px even when empty' to actually dropping the
column track when no camera is live. Uses :has() so the page grid
template flips between four states based on which side columns are
present:

  cams + controls : 220px 1fr min(360px, 50vw)    timeline col 2
  cams only       : 220px 1fr                     timeline col 2
  controls only   : 1fr   min(360px, 50vw)        timeline col 1
  alone           : 1fr                           timeline col 1

Timeline gets an explicit grid-column reassignment in the no-cameras
branches so it doesn't land in the wrong slot. Removed the
'display: grid !important' override on .cameras-col so the global
.hidden util can naturally drop it from the layout.
This commit is contained in:
Russell Ballestrini 2026-06-02 09:17:13 -04:00
parent eed5348567
commit a2d5071b71
No known key found for this signature in database

View file

@ -105,6 +105,16 @@
* them going display:none can't cause the others to slide. Controls
* column is also clamped to ≤50vw so a very wide window doesn't let
* the side panel eat the screen-share area. */
/* Page grid switches its template based on which columns are live.
* .cameras-col.hidden (no cameras) and .page.controls-collapsed
* (hide-panel) each remove a track; the timeline reassigns its
* grid-column for each combination so it always lands in the
* remaining slot. Four states:
* cams + ctrls : 220 1fr min(360,50vw) timeline col 2
* cams : 220 1fr timeline col 2
* ctrls only : 1fr min(360,50vw) timeline col 1
* alone : 1fr timeline col 1
*/
.page {
display: grid;
grid-template-columns: 220px minmax(0, 1fr) minmax(0, min(360px, 50vw));
@ -117,35 +127,41 @@
.cameras-col {
min-width: 0;
border-right: 1px solid #ddd; padding-right: 1.25rem;
display: grid !important; /* beat the global .hidden util */
display: grid;
grid-auto-rows: max-content;
gap: 0.5rem;
}
/* the cameras-col uses the .hidden class as a content-empty signal —
* keep the column visible (track reserved) but hide its h2 + grid
* and drop the right-border/padding so the empty column reads as
* dead space rather than a divider for nothing */
.cameras-col.hidden h2,
.cameras-col.hidden #cameras { display: none; }
.cameras-col.hidden { border-right: none; padding-right: 0; }
/* hide-panel = focus mode: both side columns fold away, screen+timeline
* fills the full viewport. Cameras and controls both go to display:none
* so the screen-share / game iframe has nothing competing with it. */
.page.controls-collapsed {
/* state: no cameras (column track gone, timeline + controls take 2-col) */
.page:has(> .cameras-col.hidden) {
grid-template-columns: minmax(0, 1fr) minmax(0, min(360px, 50vw));
}
.page:has(> .cameras-col.hidden) > .timeline { grid-column: 1; }
.page:has(> .cameras-col.hidden) > .controls { grid-column: 2; }
/* state: controls collapsed (right column gone) */
.page.controls-collapsed > .controls { display: none; }
.page.controls-collapsed:not(:has(> .cameras-col.hidden)) {
grid-template-columns: 220px minmax(0, 1fr);
}
/* state: controls collapsed AND no cameras → timeline owns the viewport */
.page.controls-collapsed:has(> .cameras-col.hidden) {
grid-template-columns: minmax(0, 1fr);
gap: 0;
}
.page.controls-collapsed > .controls,
.page.controls-collapsed > .cameras-col { display: none; }
.page.controls-collapsed > .timeline { border-right: none; padding-right: 0; }
/* honour the pre-paint pref so first-paint matches saved state */
html.controls-collapsed-pref .page {
grid-template-columns: minmax(0, 1fr);
gap: 0;
.page.controls-collapsed:has(> .cameras-col.hidden) > .timeline {
grid-column: 1; border-right: none; padding-right: 0;
}
/* pre-paint pref so a 'hidden' reload doesn't flash the panel */
html.controls-collapsed-pref .page > .controls { display: none; }
html.controls-collapsed-pref .page:not(:has(> .cameras-col.hidden)) {
grid-template-columns: 220px minmax(0, 1fr);
}
html.controls-collapsed-pref .page:has(> .cameras-col.hidden) {
grid-template-columns: minmax(0, 1fr); gap: 0;
}
html.controls-collapsed-pref .page:has(> .cameras-col.hidden) > .timeline {
grid-column: 1; border-right: none; padding-right: 0;
}
html.controls-collapsed-pref .page > .controls,
html.controls-collapsed-pref .page > .cameras-col { display: none; }
html.controls-collapsed-pref .page > .timeline { border-right: none; padding-right: 0; }
.timeline {
border-right: 1px solid #ddd; padding-right: 1.25rem; min-height: 60vh;
min-width: 0; display: grid; grid-auto-rows: max-content;
@ -2285,8 +2301,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-02</span><br>
md5 <span class="stamp-md5">c58b4a17d9536b098d707ff650fbb9d3</span><br>
sha256 <span class="stamp-sha">4a105138a9b6e25c913dbbf9217a94a65912eee678caaebb28a07e96877bf35f</span><br>
md5 <span class="stamp-md5">ddf174a681416af767243cb652f4c946</span><br>
sha256 <span class="stamp-sha">3c1db79787ea6e2c0cac7ea75e20bb59dc2d9c47f628b4964d65af58fa1f415f</span><br>
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
</footer>