zebra-spaces: games move to left column as persistent tiles — cameras+screens push them down

Per fox: 'make unmario and cake murder adventure as tiles on the left
which always push down as stuff joins. this will allow people to play
games during a meeting.'

Layout change:
- game-tabs + iframe moved out of <main class="timeline"> and into
  <aside id="sec-cameras"> as a third group (cameras → screens-thumbs
  → games), DOM order = stacking order, so a new camera or screen
  thumbnail naturally pushes the games further down the column
- iframe sized to fit the 220px column (aspect-ratio: 4/3, ~165px tall),
  tabs stack vertically as full-width buttons
- removed the timeline:has(> #sec-spotlight:not(.hidden)) → hide-games
  rules — no longer needed since games aren't in the timeline anymore
- sec-cameras is always visible now (games live there permanently);
  the 'cameras' h2 hides when no live cameras to avoid an empty header
- :has(> .cameras-col.hidden) page-grid rules stay as harmless dead code

Games + screen-share + cameras now coexist: someone can play unmario
in the corner while a colleague shares their screen at full size in
the middle.
This commit is contained in:
Russell Ballestrini 2026-06-02 11:20:15 -04:00
parent 44ac92984c
commit a2dda2d586
No known key found for this signature in database

View file

@ -26,7 +26,7 @@
html.theme-dark .status-line { color: #888; }
html.theme-dark .timeline,
html.theme-dark .cameras-col { border-right-color: #222; }
html.theme-dark .timeline-frame { background: #000; border-color: #222; }
html.theme-dark .game-tile-frame { background: #000; border-color: #222; }
/* buttons + inputs */
html.theme-dark button,
html.theme-dark .game-tabs button,
@ -176,19 +176,25 @@
position: relative;
}
.controls { min-width: 0; }
/* games live in the LEFT column now (under cameras + screens-thumbs)
* so people can play casually during a meeting without competing
* with screen-shares for the middle slot. The tabs wrap if the
* column is narrow; the iframe is sized for the column. */
.games-h2 { margin-top: 0.5rem; }
.game-tabs {
display: grid; grid-auto-flow: column; grid-auto-columns: max-content;
gap: 0.4rem; margin-bottom: 0.5rem;
display: grid; grid-auto-columns: minmax(0, 1fr); grid-auto-flow: row;
gap: 0.25rem; margin-bottom: 0.35rem;
}
.game-tabs button {
background: #fff; color: #000; border: 1px solid #000;
padding: 0.3rem 0.8rem; font-family: monospace; font-size: 0.8rem; cursor: pointer;
padding: 0.25rem 0.5rem; font-family: monospace; font-size: 0.72rem;
cursor: pointer; text-align: left;
}
.game-tabs button.active { background: #000; color: #fff; }
.game-tabs button:hover:not(.active) { background: #f0f0f0; }
.timeline-frame {
min-height: 80vh; width: 100%;
border: 1px solid #ddd; background: #fff;
.game-tile-frame {
width: 100%; aspect-ratio: 4 / 3;
border: 1px solid #ddd; background: #fff; display: block;
}
@media (max-width: 800px) {
body { padding: 1.25rem; }
@ -280,8 +286,7 @@
}
.screen-tile.needs-tap .tap-play { display: grid; }
/* spotlight active → hide the game iframe so the spotlight tile owns the column */
.timeline:has(> #sec-spotlight:not(.hidden)) > .game-tabs,
.timeline:has(> #sec-spotlight:not(.hidden)) > #game-frame { display: none; }
/* (games live in sec-cameras now — no timeline-side hide rule needed) */
/* camera tiles live in the LEFT column now — single column, stacked
* vertically. Each tile keeps a 16:9 letterbox so faces don't squash. */
@ -497,14 +502,28 @@ try {
<div class="page">
<!-- left column: cameras stacked vertically so face-cams stay visible
during a screen share without dwarfing the screen itself. Hidden
when nobody has a camera on. -->
<aside id="sec-cameras" class="cameras-col hidden">
<!-- left column: cameras at top, then screen-thumbs, then games at the
bottom. Games are persistent (always visible) so the column is
always on; cameras + screens push the games further down as people
join + share. People can play during the meeting without the games
ever competing with a live screen-share for the middle slot. -->
<aside id="sec-cameras" class="cameras-col">
<h2>cameras</h2>
<div id="cameras"></div>
<h2 class="screens-thumbs-h2 hidden">screens</h2>
<div id="screens-thumbs"></div>
<h2 class="games-h2">games</h2>
<div class="game-tabs" id="game-tabs">
<button type="button" class="active" data-game="unmario"
data-src="https://unmario.com/">unmario</button>
<button type="button" data-game="cake"
data-src="https://cuppcb.com/games/cake-murder-adventure/">cake murder adventure</button>
</div>
<iframe id="game-frame" class="game-tile-frame"
src="https://unmario.com/"
title="game"
loading="lazy"
referrerpolicy="no-referrer"></iframe>
</aside>
<main class="timeline">
@ -514,22 +533,6 @@ try {
<section id="sec-spotlight" class="hidden">
<div id="spotlight"></div>
</section>
<!-- placeholder content until the v0.3 event timeline lands: a game switcher
framing an external playable. Iframes load unsandboxed so the game JS can
run; if a target refuses framing (X-Frame-Options / CSP frame-ancestors)
the iframe goes blank — no impact on the controls column. -->
<div class="game-tabs" id="game-tabs">
<button type="button" class="active" data-game="unmario"
data-src="https://unmario.com/">unmario</button>
<button type="button" data-game="cake"
data-src="https://cuppcb.com/games/cake-murder-adventure/">cake murder adventure</button>
</div>
<iframe id="game-frame" class="timeline-frame"
src="https://unmario.com/"
title="game"
loading="lazy"
referrerpolicy="no-referrer"></iframe>
</main>
<aside class="controls">
@ -1546,9 +1549,14 @@ function logSpotlightChange(uuid, key){
function updateContainerVisibility(){
$('sec-spotlight').classList.toggle('hidden', !spotlight);
/* sec-cameras is ALWAYS visible now — the games tiles live there as
* persistent residents that get pushed down by arriving cameras +
* screens. Only the cameras-header and screens-header toggle. */
const anyCam = cameraVideos.size > 0;
const anyScreen = screenVideos.size > 0;
$('sec-cameras').classList.toggle('hidden', !(anyCam || anyScreen));
/* hide the 'cameras' h2 when no live cameras to avoid an empty header */
const camH2 = document.querySelector('#sec-cameras > h2:first-of-type');
if (camH2) camH2.classList.toggle('hidden', !anyCam);
document.querySelector('.screens-thumbs-h2').classList.toggle('hidden', !anyScreen);
}
@ -3218,8 +3226,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">5b160894e031e4a9362e8fdb09bc6905</span><br>
sha256 <span class="stamp-sha">80bdc123f6e3b3297af300b3a2076498a389bbff63042926373aa9eae7fa0a27</span><br>
md5 <span class="stamp-md5">947c4c514376d2995517d8576ac3e3a4</span><br>
sha256 <span class="stamp-sha">583d56867cce3995325c8f35b83388dcbd6fb14584f64dfcac3a7dcb28556f1c</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>