Same fix as MoM v1.74: in-game footer (.status-strip .rules-link) and setup-screen version-line links now use var(--ink) at 85% opacity by default, brightening to var(--accent) + underline on hover. Explicit :visited rule prevents post-click purple. The previous var(--muted) (#8a93a6) was hard to read on the dark panel.
v1.1.3 — bug fixes from Benjamin's smoke test
Dice animation now plays in online mode. The v1.1 online refactor added a runDieAnimation() function that gets fired from two places: directly via the host's await runDieAnimation(...) inside hostProceedToDieRoll, and indirectly via renderOverlaysForPhase() when state.phase==='die'. Bug: hostProceedToDieRoll was calling render() BEFORE its await — render() triggered the animation via renderOverlaysForPhase, which claimed the dieAnimating guard. The host's explicit await then early-returned via if (dieAnimating) return;, so the host raced past the animation to discardPlayedCards() and hostNextRound(). Net effect: dice overlay flashed for one frame, no spin, immediately moved on. Fix: drop the redundant render() from hostProceedToDieRoll — the explicit await is now the only animation path.
Played No cards recycle. Previously a played No card went into state.discard, which only reshuffles into the battle deck (where No cards are unplayable as regular cards). Net effect: the 2-card No pool drained to zero permanently after both were used, and 5/6 die rolls produced "pool empty, no card" forever after. Now: played No cards go straight back into noPool, available for re-acquisition via future 5/6 rolls. Reported by Benjamin: "No cards get spent when you use them. They should go to a pile to be used again."
Dice numeric backup. The unicode dice glyphs (U+2680-2685) need a font that has them — 'Apple Color Emoji' on macOS, 'Segoe UI Emoji' on Windows. On Chromebooks/Linux Chromium, neither is installed and the cascade through sans-serif doesn't always pick up a Symbols font. Belt-and-suspenders fix: render the numeric value (1-6) in monospace below the glyph, so even if the unicode glyph fails the player always sees the result. Cosmetic only; not the cause of the v1.1.2 dice bug.
Rules page updated: "Once both are spent, that's it" → "Played No cards recycle back into the pool".
v1.1.2 — quick play + more mobile fixes
⚡ Quick play matchmaking. New button on the Online setup tab: auto-matches you with anyone waiting in a Card Battle room, or hosts a new one if no one's waiting. Reuses MoM's existing /api/online/quickjoin endpoint, now extended with a gameType filter so CB players don't accidentally land in an MoM open room. Same shared games table, just different rows.
Server-side merge for join + quickjoin. The shared /api/online/{join,quickjoin} previously REPLACED state.players.p2 with whatever the client sent. That clobbered CB's host-pre-dealt p2 hand. Now it MERGES ({ ...existingP2, ...playerObject }) so the host's deal is preserved. MoM behaviour unchanged because MoM's existing p2 is empty pre-join.
More mobile portrait fixes. The v1.1.1 fix should have worked at 430 px width but wasn't enough on Benjamin's iPhone. Belt-and-suspenders this round:
Body uses 100dvh (dynamic viewport) with 100vh fallback — handles iOS Safari URL bar.
viewport-fit=cover + env(safe-area-inset-bottom/left/right) padding so the home indicator and notch don't overlap content.
Hand rows now have overflow-x: auto as a last-resort scroll fallback if 5 cards still don't fit.
Status strip wraps to two rows on narrow viewports instead of clipping.
New (max-width: 380px) breakpoint for very small phones — cards drop to 52 × 72.
v1.1.1 — mobile responsive
Phone layout fix. On iPhone Pro Max portrait, the 5×80 px hand cards + gaps overflowed the ~430 px viewport — cards got clipped on the right. In landscape, the stacked rows (header + opponent hand 110 + battle 160 + player hand 110 + status) totalled more than the ~430 px height, and body { overflow: hidden } clipped the bottom off. Added two media-query breakpoints:
(max-width: 500px) — phones in portrait. Cards shrink to 64×88, battle cards to 78×108, plus tighter spacing/padding throughout.
(orientation: landscape) and (max-height: 500px) — phones in landscape. Cards shrink further to 50×70, battle cards 62×86, minimal gaps. Whole game fits in ~370 px tall.
Reported by Benjamin: portrait cuts off cards on the right; landscape cuts off the bottom.
v1.1 — online multiplayer
"← home" link in setup-screen and in-game footer per Benjamin's uplinked feature request (2026-05-03): "there should be a button to send you back to the homepage". Sits next to the rules / changelog / backlog row.
Online mode. Setup screen now has a vs AI / Online tab toggle. Host a private room (you get a 4-char code), share with a friend, they join with the code from another device. Reuses the existing Supabase backend that Myth-o-Magic uses (single shared games table, distinguished by a gameType: 'card-battle' field).
Host-orchestrated model. The host (room creator, p1) is the trusted server: they shuffle the deck, pre-deal both hands at create time, generate die rolls, and drive every phase transition. The guest (p2) only sends two kinds of input: card picks (during 'pick') and No-card decisions (during 'no-prompt' when they're the loser). Everything else flows back via state sync. This avoids races on the shared row.
Room badge in the in-game footer shows the active room code (so you can re-share if your friend lost it).
Forfeit on leave. Hitting "leave" mid-game in online mode pushes a forfeitedBy flag so the other side gets a definitive game-over instead of being stuck waiting forever.
Same dice animation, same No-card prompt as vs AI — just driven by remote state instead of local logic.
Not yet: quick-play matchmaking, online scoreboard, resume-on-refresh, "play again" without re-hosting. Logged in BACKLOG.
v1.0 — first playable
Initial scaffold from Benjamin's spec.
Setup screen shows the per-game version ("Card Battle v1.0"), not the site build number. Setup + in-game footer have rules/changelog/backlog links.
Changelog/backlog links resolve to locally-hosted HTML pages (the repo is private, so GitHub-rendered markdown isn't visible to visitors).
47-card deck: 15 Red, 15 Black, 15 Royal in a rock-paper-scissors beats cycle, plus 2 "No" cards in a separate pool.
Single-player vs heuristic AI: first to 7 points wins.
Round flow: both pick a card → reveal → winner rolls a 6-sided die → 1-4 = +1 point, 5-6 = pick up a No card (if any left in pool).
Loser of a battle is prompted to play a No card to block the winner's roll. AI plays a No card at 70% probability when it has one and just lost.
Same-suit ties: no roll, both cards discarded, next round.
Auto-reshuffle of discard back into deck when deck is empty.
Stalemate handling: if either side can't draw any battle cards, game ends; whoever has more points wins.
Setup screen with player name + AI name (persisted in localStorage).