diff --git a/JoshHeaps.Net/wwwroot/css/casino/holdem.css b/JoshHeaps.Net/wwwroot/css/casino/holdem.css index 2ffd186..8fe6162 100644 --- a/JoshHeaps.Net/wwwroot/css/casino/holdem.css +++ b/JoshHeaps.Net/wwwroot/css/casino/holdem.css @@ -308,9 +308,22 @@ button, #pokerTableWrap { position: relative; + /* The shape is stated once, and both the height and the cap below are derived from + it. It used to be a `padding-bottom: 72%` box, which has a trap in it: percentage + padding resolves against the *containing block's* width, not the element's own, + so narrowing the wrap left it exactly as tall as before. Capping the width was + therefore no way to shorten the table — measured, after trying it: the felt went + 822px wide to 746px and stayed 617px tall, and the bottom seat sat as far into + the action bar as it had. aspect-ratio ties the two together properly. */ + --feltAspect: 0.72; width: 100%; - padding-bottom: 72%; + aspect-ratio: 1 / var(--feltAspect); margin: 0 auto; + /* Capping the width is how the felt is told to stay above the action bar. + --feltRoomPx is published by actionBar.js with the bar's real height and the + bottom seat's overhang already taken out; the fallback leaves the table its full + size if that ever fails to arrive. */ + max-width: calc(var(--feltRoomPx, 100vh) / var(--feltAspect)); } @@ -2126,6 +2139,23 @@ body.hasActionBar #holdemSidebar { margin-top: 0.1rem; } +/* "ALL IN" takes the stack line's place, so it has to not read as a stack. Wider + letters and a warm colour separate it from the grey money above it at a glance — + which is the whole job, because the dealer is scanning the ring for who can still + be bet at. */ +.seatPuckStack.seatPuckAllIn { + color: #ffcf6b; + font-weight: 700; + letter-spacing: 0.06em; +} + +/* And the seat itself carries a hint of it, so the mark survives being read out of + the corner of an eye. Deliberately quieter than .seatActing — all-in is a standing + fact about a player, not a request for the dealer's attention. */ +.seat.seatAllIn { + border-color: rgba(255, 207, 107, 0.45); +} + /* Tournament-sized amounts. A number has no spaces to break on, so an amount too wide for the 64px puck neither wraps nor ellipsises — it draws straight out over the seat next to it. These step the size down so every digit stays on its own @@ -2504,6 +2534,16 @@ body.hasActionBar #toastHost { } @media (max-width: 700px) { + /* The narrow layout already had an answer for the bar: it reserves the bar's height + as scroll room below the table, and a phone is a thing you scroll. Capping the + felt here as well was measured and rejected — at 360x640 it took the table from + 321px to 226px, which fixed the six-seat ring but broke the nine-seat one, two + covered seats traded for two seats whose centre landed on a neighbour. The cap is + for the wide layout, which never consulted the bar's height at all. */ + #pokerTableWrap { + max-width: none; + } + /* Stacked, with the action that ends the hand furthest from a stray thumb. */ .potWizardActions { flex-direction: column-reverse; @@ -2519,8 +2559,9 @@ body.hasActionBar #toastHost { grid-template-columns: 1fr; } + /* One column, so the table has the full width and can afford to be round. */ #pokerTableWrap { - padding-bottom: 100%; + --feltAspect: 1; } .seat { @@ -2845,7 +2886,11 @@ body.hasActionBar #toastHost { width: 100%; height: var(--feltRoom); min-height: 7rem; - padding-bottom: 0; + /* This branch sets its own height outright, so neither the aspect ratio nor the + cap derived from it has anything to say here — and the cap would only narrow + a table that is already as short as the screen allows. */ + aspect-ratio: auto; + max-width: none; } /* The header ate 148px of a 390px-tall window, which was most of the reason the diff --git a/JoshHeaps.Net/wwwroot/js/casino/holdem/actionBar.js b/JoshHeaps.Net/wwwroot/js/casino/holdem/actionBar.js index 2befb48..eee933b 100644 --- a/JoshHeaps.Net/wwwroot/js/casino/holdem/actionBar.js +++ b/JoshHeaps.Net/wwwroot/js/casino/holdem/actionBar.js @@ -29,6 +29,7 @@ const ActionBar = { const visible = !host.hidden; const height = visible ? host.getBoundingClientRect().height : 0; document.documentElement.style.setProperty("--actionBarHeight", `${Math.round(height)}px`); + this.publishFeltRoom(height); }; if (typeof ResizeObserver === "function") { @@ -40,6 +41,36 @@ const ActionBar = { publish(); }, + // Half a seat, the gap left under the lowest one, and the smallest felt worth + // drawing. A seat is about 73px tall at every width — the puck is text-sized, not + // table-sized — so the overhang is a constant rather than something read back off + // the layout this is about to change. Measuring it would mean shrinking the felt, + // moving the seats, and getting a different answer on the next pass. + SEAT_OVERHANG_PX: 37, + FELT_GAP_PX: 8, + FELT_MIN_PX: 220, + + // How tall the felt can be and still leave the whole ring of seats above the bar. + // + // Publishing the bar's height was only half the job. The wide layout never used it: + // the felt sizes itself from its own width — a box 72% as tall as it is wide — and + // so has no idea what is underneath it. The bar meanwhile grows with the hand, from + // 117px to 172px the moment there's a side pot to show, which is exactly when the + // dealer most needs to see who is still in. Measured with two side pots, the bottom + // seat sat 22px into the bar at 1280x900, 54px at 1600x1000, 23px at 768x1024. + // + // The wrap's offset is taken in document space rather than viewport space, so + // scrolling the page doesn't resize the table under the dealer's hands. + publishFeltRoom(barHeight) { + const wrap = document.getElementById("pokerTableWrap"); + if (!wrap) return; + const topInDocument = wrap.getBoundingClientRect().top + window.scrollY; + const room = window.innerHeight - barHeight - topInDocument - + this.SEAT_OVERHANG_PX - this.FELT_GAP_PX; + document.documentElement.style.setProperty( + "--feltRoomPx", `${Math.max(this.FELT_MIN_PX, Math.round(room))}px`); + }, + // Anything to act on? A hand with nobody dealt in doesn't get a bar — and that // still holds during setup, because a seated player is a live seat whether or not // the betting has been opened. An empty table stays uncluttered.