diff --git a/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml b/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml index 4d47fce..ea2314b 100644 --- a/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml +++ b/JoshHeaps.Net/Pages/Casino/TexasHoldem.cshtml @@ -7,18 +7,23 @@
+ @* Six equally-weighted buttons used to sit here in two rows, and on a 390px + phone the felt did not start until 225px down — a quarter of the screen spent + before the dealer saw the table. Only two of the six are per-hand controls. + The rest have gone where they belong rather than into a "more" menu, which + would have relocated the crowding rather than removed it: Reset Table to the + Session panel beside Clear history and the save/load pair, Quick Split down + with Common rulings where the other "open something else" link already lives, + and the way back to the site up top where a back link goes. *@
+ ← Casino

Texas Hold'em Dealer

-

Seat your players, track the action, split the pot.

Hand #1
- Quick Split - - ← Casino
@@ -37,6 +42,7 @@

+ Quick Split

@@ -52,7 +58,11 @@
-
+ @* Not `open` in the markup any more. Whether this panel starts open is a + question about the table, so main.js answers it after the state has + loaded: open on a table nobody has set up yet, shut on one already + half-way through a night. *@ +
Table Setup
diff --git a/JoshHeaps.Net/wwwroot/css/casino/holdem.css b/JoshHeaps.Net/wwwroot/css/casino/holdem.css index 5b528d5..feec269 100644 --- a/JoshHeaps.Net/wwwroot/css/casino/holdem.css +++ b/JoshHeaps.Net/wwwroot/css/casino/holdem.css @@ -288,10 +288,31 @@ button, background: linear-gradient(90deg, var(--gold), var(--gold-deep)); } -.holdemSubtitle { - margin: var(--sp-1) 0 0; - color: var(--text-muted); - font-size: var(--fs-md); +/* The way back to the site. It used to be a button in the action row, the same size + and weight as Resolve Pot; navigation is not an action, and putting it in that row + cost a sixth of the row's width to say "← Casino". Up here it is where a back link + goes, and it reads as one. */ +.holdemBackLink { + /* Reads as a quiet link, hits like a control. As bare text it drew a 56x14 target + — the audit caught it at every viewport — and a back link a thumb misses is + worse than the button it replaced. The padding is the hit area; the negative + margins give most of it back to the layout, so 44px of reachable link costs the + header about eight. */ + display: inline-flex; + align-items: center; + min-height: 44px; + padding: 0 var(--sp-2); + margin: calc(var(--sp-3) * -1) calc(var(--sp-2) * -1) calc(var(--sp-2) * -1); + color: var(--text-dim); + font-size: var(--fs-sm); + text-decoration: none; + letter-spacing: 0.03em; +} + +@media (hover: hover) { + .holdemBackLink:hover { + color: var(--gold); + } } .handCounterBadge { @@ -713,13 +734,38 @@ button, color: #aeaeae; } +/* Two lines, not an ellipsis. + A 96px puck fits about nine characters on a line, so "Christopher" came out as + "Chri…" and "Mike from work" as "Mike fro…" — and "from work" is the half a dealer + typed to tell two Mikes apart, so the ellipsis was eating exactly the part that + carried the information. Wrapping keeps both, and a name is the one thing on the + felt read at a glance rather than looked up. */ .seatPuckName { + /* Not shrunk to buy a line. Dropping a step did fit another character and a half, + and still didn't get "Christopher" onto one line on the button seat, where the + dealer chip takes 22px of the row — so it bought nothing and cost legibility on + the one label a dealer reads at a glance across a table. The name stays the size + it was; the wrap deals with the length. */ font-size: var(--fs-sm); font-weight: 600; color: var(--text-bright); - white-space: nowrap; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; overflow: hidden; - text-overflow: ellipsis; + /* A single word longer than the puck has nothing to break on. `anywhere` was the + first try and it gave "Christo / pher", which reads as a rendering fault; with + `break-word` the browser only breaks a word that cannot fit on a line of its + own, and `hyphens` puts a hyphen in when it does. */ + overflow-wrap: break-word; + hyphens: auto; + line-height: 1.2; + /* Deliberately no reserved second line. Reserving one made every puck the height of + the tallest — measured, 69-73px became a uniform 88 — and 15px added to all nine + seats is fifteen more pixels of felt spent on chrome, which is the thing this + round is trying to give back. A ragged ring is cheaper than a crowded one, and + the ring was already ragged by 4px before any of this. */ margin-top: var(--sp-2); } @@ -962,9 +1008,17 @@ button, padding: 3px 4px; } +/* One line again once the ring is crowded. A second line is 14px the tight layout + hasn't got — it already gives up the hole cards to fit — and a puck that grew taller + here would push its neighbours into each other, which is the one thing the tight + layout exists to stop. */ .seatsTight .seatPuckName { margin-top: var(--sp-1); font-size: var(--fs-2xs); + -webkit-line-clamp: 1; + line-clamp: 1; + min-height: 0; + text-overflow: ellipsis; } .seatsTight .seatPuckStack { @@ -1829,11 +1883,14 @@ button, min-width: 0; } +/* No border. The panel already has a lighter background than the page behind it and a + shadow lifting it off — three ways of drawing the same edge, where the background + alone does the job. Putting a line round every box is what makes an interface read + like a spreadsheet: the eye spends itself on the lines rather than the contents. */ .sidebarPanel { background: linear-gradient(160deg, #212121, var(--surface-1)); - border: 1px solid var(--surface-3); border-radius: var(--r-md); - padding: var(--sp-1) var(--sp-4); + padding: var(--sp-2) var(--sp-4); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } @@ -1985,14 +2042,17 @@ button, container-type: inline-size; } +/* A rule under every row turned the standings into ruled paper — seven lines on a + six-player table, none of which said anything the row spacing wasn't already saying. + The rows are separated by the space between them; the header keeps its rule, because + that one marks a real change of kind. */ .standingsRow { display: grid; grid-template-columns: minmax(4rem, 1fr) auto auto; gap: var(--sp-2); align-items: baseline; - padding: var(--sp-1) var(--sp-0); + padding: var(--sp-2) var(--sp-0); font-size: var(--fs-sm); - border-bottom: 1px solid var(--surface-2); } .standingsRow > span:not(:first-child) { @@ -2130,7 +2190,10 @@ button, font-size: var(--fs-xs); letter-spacing: 0.04em; text-transform: uppercase; - border-bottom-color: var(--line); + /* The one rule kept in this table: it separates the column names from the players, + which is a change of kind rather than just the next row down. Stated in full + now the rows themselves have none to inherit a colour from. */ + border-bottom: 1px solid var(--line); position: sticky; top: 0; background: #1b1b1b; @@ -3363,10 +3426,6 @@ body.hasActionBar #toastHost { font-size: var(--fs-xl); } - .holdemSubtitle { - font-size: var(--fs-base); - } - #holdemHeaderActions { width: 100%; gap: var(--sp-2); @@ -3739,10 +3798,6 @@ body.hasActionBar #toastHost { white-space: nowrap; } - .holdemSubtitle { - display: none; - } - /* There's no vertical room left, but plenty either side of a table this narrow, so the toast moves out of the middle and sits beside the felt instead of on top of it. Centred, it was covering up to six seats at once — and it carries @@ -3820,10 +3875,6 @@ body.hasActionBar #toastHost { font-size: var(--fs-lg); padding-bottom: var(--sp-1); } - - .holdemSubtitle { - display: none; - } } /* ---------- Keyboard focus ---------- diff --git a/JoshHeaps.Net/wwwroot/js/casino/holdem/main.js b/JoshHeaps.Net/wwwroot/js/casino/holdem/main.js index bc7edf2..f5b0a34 100644 --- a/JoshHeaps.Net/wwwroot/js/casino/holdem/main.js +++ b/JoshHeaps.Net/wwwroot/js/casino/holdem/main.js @@ -257,6 +257,22 @@ document.addEventListener("DOMContentLoaded", () => { Render.mount(state, hooks); + // Table Setup used to ship open, so the tool's first screen was its most crowded + // one: seat count, blind direction, dedicated dealer, both blinds, starting stack + // and two sub-panels, all under a felt the dealer had come here to look at. It is + // the right panel to have open on a table nobody has touched, and the wrong one to + // have open on the fourth hand of a night. + // + // Decided once, here, rather than on every render — a panel that reopened itself + // whenever the table emptied would fight the dealer who had just shut it. + (() => { + const panel = document.getElementById("tableSetupPanel"); + if (!panel) return; + const fresh = (Number(state.handNumber) || 1) === 1 && + !state.seats.some(seat => HoldemState.seatInPlay(seat)); + panel.open = fresh; + })(); + const openPotWizard = () => { // Chips still in front of players belong in the pot being priced, so bank them // before the wizard reads anyone's contribution. diff --git a/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js b/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js index d275926..15d5daf 100644 --- a/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js +++ b/JoshHeaps.Net/wwwroot/js/casino/holdem/render.js @@ -88,6 +88,19 @@ const Render = { hint.textContent = this.canDragSeats() ? "Tap a seat to edit · double-tap to fold · press and hold for quick actions · drag to swap seats." : "Tap a seat to edit · double-tap to fold · press and hold for quick actions, including moving a seat."; + + // But only on the first hand. Two lines of instructions under the felt are worth + // having on a table nobody has dealt at yet, and worth nothing on the fifth hand + // of the night — by then the dealer either knows the gestures or is not going to + // learn them from a caption they have scrolled past forty times. + // + // Keyed to the hand number rather than to whether a hand is currently out, on + // purpose. `bettingOpen` was the obvious test and it is the wrong one: the hint + // would vanish at every deal and come back at every payout, so a two-line block + // under the felt would appear and disappear twice a hand, shunting the rulings + // link and the whole panel stack up and down all night. This way it goes once, + // at the first deal of the session, and stays gone. + hint.hidden = (Number(this.state.handNumber) || 1) > 1 || !!this.state.bettingOpen; }, renderNotes() { @@ -553,8 +566,21 @@ const Render = { // font down keeps every digit, which matters: a dealer misreading a stack is a // worse outcome than a small one, so this shrinks rather than abbreviating. fitAmount(el) { - const length = (el.textContent || "").length; + const formatted = el.textContent || ""; + // The separators go before the type size does. "$1,234,567" is ten characters, + // which used to drop the puck to 7px — and 7px is not a size anybody reads a + // stack off a felt at, so the shrink-rather-than-abbreviate rule above was + // buying precision the dealer then couldn't see. Without its commas the same + // number is eight characters, and eight fits at 10px with every digit intact. + // Grouping is a reading aid for a column of figures; this is one number on a + // 64px puck, and the aid costs more than it gives. + const text = formatted.length >= 10 ? formatted.replace(/,/g, "") : formatted; + if (text !== formatted) el.textContent = text; + + const length = text.length; el.classList.toggle("puckAmountLong", length >= 8 && length < 10); + // Only a stack of $100,000,000 or more still lands here. It is an overflow + // guard rather than a size anybody is expected to read. el.classList.toggle("puckAmountHuge", length >= 10); },