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.
187 lines
8.8 KiB
HTML
187 lines
8.8 KiB
HTML
{{define "title"}}Solitaire · {{.Room.Name}}{{end}}
|
||
|
||
{{define "main"}}
|
||
<div class="space-y-6" data-solitaire>
|
||
|
||
<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">Solitaire</h1>
|
||
</div>
|
||
<p class="text-sm text-muted">You buy the deck. Every card you get home buys some of it back.</p>
|
||
</div>
|
||
|
||
{{template "_chipbar" .}}
|
||
|
||
<!-- The felt. The board takes the room; the money lives in a rail down the
|
||
right of it, where the house rack can't collide with the foundations. -->
|
||
<section class="pete-felt pete-solitaire 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-6 lg:grid-cols-[minmax(0,1fr),auto] lg:gap-8">
|
||
|
||
<!-- The board. -->
|
||
<div class="min-w-0 space-y-4 sm:space-y-6">
|
||
|
||
<!-- The stock and the waste on the left, the foundations on the right,
|
||
exactly where a real layout puts them. -->
|
||
<div class="flex items-start justify-between gap-3">
|
||
<div class="flex items-start gap-2 sm:gap-3">
|
||
<button type="button" data-stock class="pete-slot pete-stock" aria-label="Turn over the stock">
|
||
<span data-stock-count class="pete-slot-count">0</span>
|
||
<span data-stock-recycle class="pete-slot-recycle hidden" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M3 12a9 9 0 0 1 15-6.7L21 8"></path><polyline points="21 3 21 8 16 8"></polyline>
|
||
<path d="M21 12a9 9 0 0 1-15 6.7L3 16"></path><polyline points="3 21 3 16 8 16"></polyline>
|
||
</svg>
|
||
</span>
|
||
</button>
|
||
<div data-waste class="pete-waste" aria-label="The waste"></div>
|
||
</div>
|
||
|
||
<div data-foundations class="flex items-start gap-1.5 sm:gap-2" aria-label="The foundations"></div>
|
||
</div>
|
||
|
||
<!-- The seven columns. -->
|
||
<div data-tableau class="pete-tableau" aria-label="The tableau"></div>
|
||
|
||
<!-- What just happened. -->
|
||
<div class="flex min-h-[2.75rem] items-center">
|
||
<p data-verdict class="hidden rounded-full bg-white/95 px-5 py-2 font-display text-lg font-bold text-[#2b2118] shadow-pete"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- The rail: the house's rack, what you've banked, and the meter that
|
||
reads it. Everything the money does happens between these two. -->
|
||
<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="pete-spot" data-spot>
|
||
<span class="pete-spot-label">Banked</span>
|
||
<div class="pete-stack" data-stack></div>
|
||
<span data-spot-total class="pete-spot-total hidden"></span>
|
||
</div>
|
||
|
||
<div class="pete-meter w-full justify-center" data-meter>
|
||
<span class="pete-meter-label">Home</span>
|
||
<span data-home class="pete-meter-value">0<span class="text-white/40">/{{.FullDeck}}</span></span>
|
||
</div>
|
||
|
||
<p class="text-center text-xs leading-relaxed text-white/55">
|
||
<span data-per-card class="font-bold text-white/85">—</span> a card<br>
|
||
<span data-break-even></span>
|
||
</p>
|
||
</aside>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Playing: shown while a board is live. -->
|
||
<section data-playing class="hidden rounded-3xl bg-card p-5 sm:p-6 shadow-pete border-2 border-ink/10">
|
||
|
||
<!-- The ordinary controls, while there's still a game to play. -->
|
||
<div data-play-controls class="flex flex-wrap items-center gap-3">
|
||
<button type="button" data-auto
|
||
class="rounded-full bg-ink/5 px-5 py-2.5 font-display font-bold border-2 border-ink/10
|
||
hover:bg-ink/10 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
|
||
Send home ⤴
|
||
</button>
|
||
|
||
<p class="text-xs text-muted">
|
||
Drag a card where it goes, or click it and then click the spot. Double-click sends it home.
|
||
</p>
|
||
|
||
<button type="button" data-cash
|
||
class="ml-auto rounded-full bg-accent px-6 py-2.5 font-display font-bold text-white shadow-pete
|
||
hover:brightness-105 active:translate-y-px disabled:opacity-40 disabled:pointer-events-none transition">
|
||
Cash the board · <span data-cash-amount class="tabular-nums">0</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- The board's won: every card's face up and the piles are drained, so
|
||
there's nothing left to decide. One press sends the lot home. -->
|
||
<div data-won-controls class="hidden">
|
||
<div class="flex flex-wrap items-center gap-3">
|
||
<button type="button" data-finish
|
||
class="pete-finish 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:pointer-events-none transition">
|
||
You've cracked it. Send them all home 🎉
|
||
</button>
|
||
<p class="text-xs text-muted">
|
||
The whole board's face up now, so the rest plays itself.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<p data-game-msg class="hidden mt-3 rounded-2xl bg-ink/5 px-4 py-2 text-sm font-semibold"></p>
|
||
</section>
|
||
|
||
<!-- Buying a deck: shown between games. -->
|
||
<section data-betting 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">Which deal?</div>
|
||
<div class="mt-2 grid gap-2 sm:grid-cols-3">
|
||
{{range .Deals}}
|
||
<button type="button" data-tier="{{.Slug}}"
|
||
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">{{printf "%.1f" .Base}}×</span>
|
||
</div>
|
||
<p class="mt-0.5 text-xs text-muted">{{.Blurb}}</p>
|
||
<p class="mt-1.5 text-xs font-semibold text-muted">
|
||
Square with the house at {{.BreakEven}} cards home.
|
||
</p>
|
||
</button>
|
||
{{end}}
|
||
</div>
|
||
|
||
<div class="mt-5 flex flex-wrap items-center gap-x-6 gap-y-4">
|
||
<div>
|
||
<div class="text-xs font-semibold uppercase tracking-wider text-muted">The deck costs</div>
|
||
<div class="font-display text-3xl font-bold tabular-nums"><span data-bet-amount>0</span></div>
|
||
</div>
|
||
|
||
<div class="flex flex-wrap items-center gap-2">
|
||
{{range .Denominations}}
|
||
<button type="button" data-chip="{{.}}" aria-label="Put {{.}} more down"
|
||
class="pete-chip pete-disc grid h-12 w-12 place-items-center font-display text-sm font-bold text-white">
|
||
<span>{{.}}</span>
|
||
</button>
|
||
{{end}}
|
||
<button type="button" data-bet-clear
|
||
class="rounded-full px-3 py-2 text-sm font-semibold text-muted hover:text-ink transition">Clear</button>
|
||
</div>
|
||
|
||
<button type="button" data-start
|
||
class="ml-auto 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">
|
||
Buy the deck
|
||
</button>
|
||
</div>
|
||
<p class="mt-3 text-xs text-muted">
|
||
The stake buys the deck outright, so it doesn't come back. What comes back is
|
||
whatever you get home, a fifty-second of the multiple at a time. Stop whenever
|
||
you like and keep it. There's no undo.
|
||
</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/solitaire.js"}}" defer></script>
|
||
{{end}}
|