Files
prosolis a15d67e210 web: the muted text that was never muted, and the stylesheet fetched every time
Every text-[color:var(--ink)]/50, border-[color:var(--ink)]/10 and
bg-[color:var(--ink)]/5 in the templates compiled to nothing. Tailwind v3
cannot put an opacity on an arbitrary var() colour, so all 538 of them were
dropped from output.css without a word. Meta text rendered at full ink, card
borders fell back to Tailwind's default grey (bright white rings at night),
and the washes, backdrops and hover pills simply weren't there, on the news
site, the casino and the adventure pages alike.

The palette is now written once per phase and casino room as bare rgb
channels (--ink-rgb and friends), the plain vars are built from them, and
tailwind.config.js exposes ink, muted, accent, warn and card as real colours.
The classes are text-ink/80, border-ink/10, bg-accent/20 now, and they compile.

Restoring the intended opacities as-is would have traded flat for illegible:
ink at 50% measures 2.7 to 2.9:1 on the lit phases. So secondary text gets
its own colour, --muted, per phase and per casino room, taken as faint as it
can go while still clearing 4.5:1 on both the card and the page. Everything
that was ink at 35 to 70% is text-muted. The 20-30% ones are separators and
empty-cell dashes and stay faint.

The bg-theme-* chips carry white text everywhere they appear, and eight of
them measured 2.5 to 3.2:1. Same hues, taken just dark enough to pass (4.6 to
4.7:1). eu, lego and adventure already passed and are unchanged, and the
underline and border colours keep the old pastels since they carry no text.

Static files came out of the embedded FS with no Last-Modified, no ETag and
no Cache-Control, so every page view downloaded the stylesheet and every
script in full, and the service worker's "cheap 304" never happened. Each
file is hashed once now. Templates link through {{asset}}, which appends
?v=<hash>; a request naming the current hash is served immutable for a year,
and everything else gets the hash as its ETag and revalidates to a 304. The
service worker serves versioned URLs cache-first, drops older versions of the
same file as new ones land, and moves to cache v6.
2026-09-22 22:54:50 -07:00

235 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "title"}}Hold'em · {{.Room.Name}}{{end}}
{{define "main"}}
<div class="space-y-6" data-holdem>
<div class="flex flex-wrap items-center justify-between gap-3">
<div class="flex items-center gap-3 min-w-0">
<a href="/games" class="grid h-10 w-10 shrink-0 place-items-center rounded-full bg-card shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition" title="Back to the casino">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5" aria-hidden="true">
<path d="M19 12H5"></path><polyline points="12 19 5 12 12 5"></polyline>
</svg>
<span class="sr-only">Back to the casino</span>
</a>
<h1 class="font-display text-3xl font-bold">Texas Hold'em</h1>
</div>
<p class="text-sm text-muted">Buy in, play as long as you like, leave with what's in front of you</p>
</div>
{{template "_chipbar" .}}
<!-- The felt. Seats along the top, the board and the pot in the middle, you at
the bottom. Every seat has its own bet spot, and every chip on this table is
travelling between one of those and the pot — which is the whole difference
between poker and every other game in the room, where the chips only ever
move between you and the house. -->
<section class="pete-felt pete-poker relative overflow-hidden rounded-3xl p-4 sm:p-6 lg:p-8 shadow-pete-lg border-2 border-ink/10">
<div class="grid gap-5 lg:grid-cols-[minmax(0,1fr),auto] lg:gap-8">
<div class="min-w-0">
<div data-seats class="pete-poker-seats" aria-label="The other players"></div>
<div class="pete-poker-middle">
<div data-board class="pete-poker-board" aria-label="The board"></div>
<div class="pete-poker-pot">
<!-- The chips get a box of their own. .pete-stack is absolutely
positioned over whatever contains it, so a pile sharing a box with
the number under it paints straight over the number. -->
<div class="pete-poker-pot-pile">
<div class="pete-stack" data-pot-stack></div>
</div>
<span class="pete-poker-pot-label">Pot</span>
<span data-pot-total class="pete-poker-pot-total tabular-nums">0</span>
<span data-side class="pete-poker-side hidden"></span>
</div>
<div class="flex min-h-[2.75rem] items-center justify-center">
<p data-verdict class="pete-poker-verdict hidden" aria-live="polite"></p>
</div>
</div>
<div class="pete-poker-you flex flex-col items-center" data-you></div>
</div>
<!-- The rail. This table has no corner free for the house's rack — the seats
run along the top and your hand is under the board — so it takes
solitaire's rail and sits off the felt entirely. -->
<aside class="pete-rail">
<div class="pete-rack" data-at="rail" data-house aria-hidden="true">
<span data-chip="500" style="--stack: 5"></span>
<span data-chip="100" style="--stack: 7"></span>
<span data-chip="25" style="--stack: 4"></span>
<span data-chip="5" style="--stack: 6"></span>
</div>
<div class="text-center">
<div class="pete-meter" data-meter>
<span class="pete-meter-label">Blinds</span>
<span data-blinds class="pete-meter-value">—</span>
</div>
<p data-table-name class="mt-1.5 text-xs font-bold uppercase tracking-wider text-white/40"></p>
</div>
</aside>
</div>
</section>
<!-- Acting: shown when the hand is yours to play. -->
<section data-acting class="hidden rounded-3xl bg-card p-5 sm:p-6 shadow-pete border-2 border-ink/10">
<div class="flex flex-wrap items-center gap-3">
<button type="button" data-move="fold"
class="rounded-full border-2 border-ink/15 bg-card px-6 py-3 font-display text-lg font-bold shadow-pete
hover:bg-ink/5 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
Fold
</button>
<button type="button" data-move="check"
class="rounded-full border-2 border-ink/15 bg-card px-6 py-3 font-display text-lg font-bold shadow-pete
hover:bg-ink/5 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
Check
</button>
<button type="button" data-move="call"
class="rounded-full bg-accent px-6 py-3 font-display text-lg font-bold text-white shadow-pete
hover:brightness-105 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
Call <span data-call-amount class="tabular-nums"></span>
</button>
</div>
<div data-raise-row class="mt-4 flex flex-wrap items-center gap-3 border-t-2 border-ink/5 pt-4">
<div class="pete-raise">
<input type="range" data-raise-slider aria-label="How much to raise to">
<span data-raise-to class="pete-raise-to">0</span>
</div>
<div class="flex flex-wrap items-center gap-1.5">
<button type="button" data-raise-preset="0.5" class="rounded-full border-2 border-ink/10 px-3 py-1.5 text-xs font-bold hover:bg-ink/5 transition">½ pot</button>
<button type="button" data-raise-preset="1" class="rounded-full border-2 border-ink/10 px-3 py-1.5 text-xs font-bold hover:bg-ink/5 transition">Pot</button>
<button type="button" data-raise-preset="max" class="rounded-full border-2 border-ink/10 px-3 py-1.5 text-xs font-bold hover:bg-ink/5 transition">Max</button>
</div>
<button type="button" data-move="raise"
class="ml-auto rounded-full bg-accent px-7 py-3 font-display text-lg font-bold text-white shadow-pete
hover:brightness-105 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
<span data-raise-verb>Raise to</span> <span data-raise-label class="tabular-nums"></span>
</button>
</div>
<p data-game-msg class="hidden mt-3 rounded-2xl bg-ink/5 px-4 py-2 text-sm font-semibold"></p>
</section>
<!-- Between hands: deal the next one, put more chips out, or get up. -->
<section data-between class="hidden rounded-3xl bg-card p-5 sm:p-6 shadow-pete border-2 border-ink/10">
<div class="flex flex-wrap items-center gap-3">
<div class="min-w-0">
<div class="text-xs font-semibold uppercase tracking-wider text-muted">In front of you</div>
<div class="font-display text-3xl font-bold tabular-nums" data-table-stack>0</div>
<p class="mt-0.5 text-xs text-muted">
Bought in for <span data-bought-in class="tabular-nums">0</span> · the house has taken <span data-session-rake class="tabular-nums">0</span> in rake
</p>
</div>
<div class="ml-auto flex flex-wrap items-center gap-2">
<button type="button" data-topup
class="rounded-full border-2 border-ink/15 bg-card px-5 py-3 font-display text-base font-bold shadow-pete
hover:bg-ink/5 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
Top up
</button>
<button type="button" data-leave
class="rounded-full border-2 border-ink/15 bg-card px-5 py-3 font-display text-base font-bold shadow-pete
hover:bg-ink/5 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
Get up
</button>
<button type="button" data-deal
class="rounded-full bg-accent px-8 py-3 font-display text-lg font-bold text-white shadow-pete
hover:brightness-105 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
Next hand
</button>
</div>
</div>
<p data-between-msg class="hidden mt-3 rounded-2xl bg-ink/5 px-4 py-2 text-sm font-semibold"></p>
</section>
<!-- The rail talk. Chat lives at the felt and does not leave it for Matrix;
every line is stamped with the hand it was said during. Shown while you're
seated. -->
<section data-chat class="hidden rounded-3xl bg-card p-4 shadow-pete border-2 border-ink/10">
<div data-chat-log class="flex flex-col gap-1 max-h-40 overflow-y-auto pr-1 text-sm" aria-live="polite" aria-label="Table chat"></div>
<form data-chat-form class="mt-3 flex items-center gap-2">
<input data-chat-input type="text" maxlength="240" autocomplete="off" placeholder="Say something to the table…"
class="flex-1 min-w-0 rounded-full border-2 border-ink/15 bg-[color:var(--bg)] px-4 py-2 text-sm focus:outline-none focus:border-accent">
<button type="submit"
class="rounded-full bg-accent px-5 py-2 font-display text-sm font-bold text-white shadow-pete hover:brightness-105 active:translate-y-px transition">
Say
</button>
</form>
</section>
<!-- Sitting down: shown when you aren't at a table. -->
<section data-sitting class="rounded-3xl bg-card p-5 sm:p-6 shadow-pete border-2 border-ink/10">
<div class="text-xs font-semibold uppercase tracking-wider text-muted">What are you playing for?</div>
<div class="mt-2 grid gap-2 sm:grid-cols-3">
{{range .Stakes}}
<button type="button" data-tier="{{.Slug}}"
data-min="{{.MinBuy}}" data-max="{{.MaxBuy}}" data-bb="{{.BB}}"
class="pete-tier rounded-2xl border-2 p-3 text-left transition">
<div class="flex items-baseline justify-between gap-2">
<span class="font-display text-lg font-bold">{{.Name}}</span>
<span class="font-display text-lg font-bold tabular-nums text-accent">{{.SB}}/{{.BB}}</span>
</div>
<p class="mt-0.5 text-xs text-muted">{{.Blurb}}</p>
<p class="mt-1.5 text-xs font-semibold text-muted">
Buy in {{.MinBuy}}–{{.MaxBuy}}
</p>
</button>
{{end}}
</div>
<div class="mt-5 grid gap-5 sm:grid-cols-2">
<div>
<div class="text-xs font-semibold uppercase tracking-wider text-muted">How many of them?</div>
<div class="mt-2 flex flex-wrap gap-1.5" data-bots>
<button type="button" data-bot-count="1" class="pete-tier rounded-xl border-2 px-4 py-2 text-sm font-bold transition">1</button>
<button type="button" data-bot-count="2" class="pete-tier rounded-xl border-2 px-4 py-2 text-sm font-bold transition">2</button>
<button type="button" data-bot-count="3" class="pete-tier rounded-xl border-2 px-4 py-2 text-sm font-bold transition">3</button>
<button type="button" data-bot-count="4" class="pete-tier rounded-xl border-2 px-4 py-2 text-sm font-bold transition">4</button>
<button type="button" data-bot-count="5" class="pete-tier rounded-xl border-2 px-4 py-2 text-sm font-bold transition">5</button>
</div>
<p class="mt-1.5 text-xs text-muted" data-bots-note></p>
</div>
<div>
<div class="text-xs font-semibold uppercase tracking-wider text-muted">Buying in for</div>
<div class="mt-2 flex items-center gap-3">
<input type="range" data-buyin-slider class="flex-1 min-w-0" aria-label="How much to buy in for">
<span data-buyin class="font-display text-2xl font-bold tabular-nums">0</span>
</div>
<p class="mt-1.5 text-xs text-muted" data-buyin-note></p>
</div>
</div>
<button type="button" data-sit
class="mt-5 w-full rounded-full bg-accent px-8 py-3 font-display text-lg font-bold text-white shadow-pete
hover:brightness-105 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
Sit down
</button>
<!-- Or pull up a chair at a table someone else has already opened. Bots keep
every open table full, so there is always a seat to take. -->
<div data-lobby-wrap class="mt-6 hidden">
<div class="text-xs font-semibold uppercase tracking-wider text-muted">Or join a table in progress</div>
<div data-lobby class="mt-2 grid gap-2"></div>
</div>
<p class="mt-3 text-center text-xs text-muted">
The bots are trained, not scripted. The house takes {{.RakePct}}% of a pot that sees a flop, capped at three big blinds, and nothing at all from a hand that doesn't.
</p>
<p data-table-msg class="hidden mt-3 rounded-2xl bg-ink/5 px-4 py-2 text-sm font-semibold"></p>
</section>
</div>
{{end}}
{{define "scripts"}}
<script src="{{asset "/static/js/casino-fx.js"}}" defer></script>
<script src="{{asset "/static/js/casino-cards.js"}}" defer></script>
<script src="{{asset "/static/js/games.js"}}" defer></script>
<script src="{{asset "/static/js/holdem.js"}}" defer></script>
{{end}}