From ff35e8ef8e7a3e86c184f399e1d9dba730a86936 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Tue, 15 Sep 2026 23:19:59 -0400 Subject: [PATCH] The table fits the window: the hand stays in reach, the maze takes the height MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a desktop the game grid is now exactly the window's height. The board and the paper rail share the top row, the rail scrolling in place, and the hand with its End turn sit pinned beneath — nothing a first turn needs is ever below the fold. The board's window fills whatever height the row allows instead of a fixed subtraction. A wide, short window — a laptop in landscape — stands the hand beside the board on its own until the player chooses a side, since a tall maze under a bottom hand left a postage stamp. A short window shrinks the bottom hand's cards a little. The eyes toggle floats over the board's corner so it costs no height, and the masthead's "invite friends" hides on phones where it wrapped. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG --- packages/web/src/App.svelte | 57 +++++++++++++++++++++++--------- packages/web/src/prefs.svelte.ts | 6 +++- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 427beab..a9f6d21 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -468,6 +468,13 @@ const wideQuery = typeof matchMedia === "undefined" ? null : matchMedia("(min-width: 901px)"); let wideScreen = $state(wideQuery?.matches ?? true); wideQuery?.addEventListener("change", (e) => (wideScreen = e.matches)); + /** A wide, short screen — a laptop in landscape — stands the hand beside + * the board unless the player has said otherwise: a tall maze under a + * bottom hand leaves a postage stamp, and a first turn deserves better. */ + const shortWideQuery = typeof matchMedia === "undefined" ? null : matchMedia("(min-width: 1180px) and (max-height: 820px)"); + let shortWide = $state(shortWideQuery?.matches ?? false); + shortWideQuery?.addEventListener("change", (e) => (shortWide = e.matches)); + const handLeftOn = $derived(prefs.handLeftChosen ? prefs.handLeft : shortWide); const castPanelOn = $derived(wideScreen && !local.active && (selectedCard != null || (youMustRespond && !!view?.stack))); @@ -497,7 +504,7 @@ * width, capped so the full hand still fits without a scrollbar. */ let handEl = $state(null); function fitHandCards(el: HTMLDivElement) { - if (!prefs.handLeft) { el.style.removeProperty("--card-zoom"); return; } + if (!handLeftOn) { el.style.removeProperty("--card-zoom"); return; } // Card.svelte is the source of truth for the card's box: 8.2rem wide, // 11.4rem tall. 5rem covers the masthead and the column's padding. const rem = 16; @@ -518,7 +525,7 @@ }); $effect(() => { void view?.yourHand.length; - void prefs.handLeft; + void handLeftOn; const el = handEl; if (el) requestAnimationFrame(() => fitHandCards(el)); }); @@ -1665,7 +1672,7 @@ {:else if net.roomId} {#if net.audience > 0} 👁 {net.audience} @@ -1902,9 +1909,9 @@ Instant replay: notable chronicle lines offer a 👁 that relives the turn