Commit Graph
57 Commits
Author SHA1 Message Date
Eric WagonerandClaude Fable 5 0ebae03b1c The discard pile lies face-up, and the chronicle names what falls
At the table the discards sit face-up for anyone to leaf through. The
view now carries the pile (deck order stays secret), the "discards N"
count in the rail became a link, and it opens a booklet-styled reader:
every cast-away card newest first, tap any to enlarge. And the
chronicle stops mumbling "discards 1 card(s)" — it names names, so a
wraith's theft reads "bob discards Fireball," exactly what a table
would see as the card hits the pile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 13:28:55 -04:00
Eric WagonerandClaude Fable 5 d1eace9530 Rules rev 4: a creature's blow can be counteracted
The WRAITH card assumes the window exists — "REFLECTIONs used on the
wraith's touch will damage the wraith" — but creature damage applied
instantly, so BLUNT had nothing to catch, as playtesting found. Under
revision 4 every creature blow against a wizard opens the same
counteraction stack a spell does: the wraith's entry touch, the
democratic monster's claw, and commanded troll/skeleton/shadow
attacks. The blow's damage rides the stack; BLUNT halves it (round
up); reflections work by name against creatures — half back for
REFLECTION, the whole blow for FULL REFLECTION — landing on the
creature, not its controller; FULL SHIELD correctly does nothing
("does not stop any physical attack"); the wraith's card theft is a
secondary effect that lands only if damage does. No aim-miss rolls
against invisible or shrunk defenders — the creature is already in
the square. The attack fanfare shows the creature's own card.

Earlier revisions keep the instant touch so every stored game — and
the live one that found this — replays unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 13:25:45 -04:00
Eric WagonerandClaude Fable 5 3f3e6d9853 The table survives synthetic spell ids — fixes the blank screen
The Sticky Wand attaches its webs as a sustained effect with the
engine-internal id "sticky-web", which is not a printed card — and
cardDef throws on unknown ids. The scoresheet's new spell chips and
the chronicle's settles-over/wears-off lines both called it on
sustained ids, so the first webbing killed every render: mid-game the
actions bar died, and after a reload the whole table came up blank.
A spellName lookup now names synthetic effects properly ("Sticky
Wand webs") and falls back to the raw id rather than ever throwing,
and the chip peek only opens ids the card library can actually show.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 13:11:06 -04:00
Eric WagonerandClaude Fable 5 45803a2664 Live interrupts announce themselves
Interrupt and Opportunity Fire were fully playable out of turn — tap
the card while another wizard acts — but nothing said so, and cards
are otherwise inert out of turn, so playtesters never tried. Now,
whenever the moment is live (someone else's turn, no attack or chaos
pending, past round one, not hotseat), those cards wear a pulsing
golden glow in your hand and the rail says to tap one. The glow
respects prefers-reduced-motion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 13:07:22 -04:00
Eric WagonerandClaude Fable 5 7e3e00bf97 Rules rev 3: Ward is a choice, Chaos honors the shield
WARD's text says "you may play at that time" — under rules revision 3
the choice is made by setting the trap: an armWard command toggles it
on your own turn (your secret; a private wardSet event and a rail
note), and the spring fires only while set, consuming the card and
the arming together. Async games keep their agency without an
interrupt window, exactly as the ambush system solved this before.

CHAOS gains its printed interactions: "FULL SHIELD removes a player
from participation" — a defender's shield sits them out instead of
stopping the spell, and after the counter chain every bystander gets
a shield window in seat order (chaosPending; queue head owes a
response in lobby summaries, the rail says whose hand hangs in the
balance) before the pile forms among the unshielded. "REFLECTIONS
have no effect" — both reflections are refused as counters. ABSORB
SPELL still eats the whole thing through the existing absorb path.

Both changes are frozen behind deckRev 3 so every stored game — and
the live one — replays byte-for-byte under its own rules. Five new
tests cover armed/unarmed wards, shielded defenders and bystanders,
the reflection refusal, and the legacy auto-ward.

Not deployed — a live game is in progress.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 12:55:58 -04:00
Eric WagonerandClaude Fable 5 41c384274d Walls and doors fall to sustained assault
"It is possible, though time-consuming, to punch a wall down. A wall
takes 20 points of damage to destroy; a door takes 15. Any attack
against an inanimate object counts as your one attack for the turn."
Damage accumulates per edge in wallDamage (remapped through sector
rotations, public in the view), fed two ways: a punchWall command for
the bare-fisted (1 point, from a square touching the edge) and attack
spells cast at an edge target — LOS to the wall for L.O.S. cards,
touching it for same-square cards, amplify and power-attack honored,
wand charges spent, no counteractions since stonework plays none.
Thrown daggers and rocks clatter to the floor at the foot of the
wall. At the threshold the edge opens through the same override path
destroy-wall uses.

On the table: damaged walls wear spreading cracks, an attack card's
hint offers "or a wall line to batter it" with the edge layer live,
and a "Punch a wall…" stamp arms a click-the-wall mode. The chronicle
counts the blows: "alice batters the wall with bare fists — 3/20."

Not deployed — a live game is in progress.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 12:48:49 -04:00
Eric WagonerandClaude Fable 5 695307daa8 Credibility pass: one voice, no scars
A three-reviewer sweep for tells of piecemeal machine generation,
every finding verified against the code before touching it. No
behavior changes; the full suite passes unchanged (plus two
strengthened pins).

Engine: removed four void-silenced fossils (a parseEdgeKey call
voided where it stood, stoneEffect's ignored cardId parameter, the
actualTarget remnant in doCast, a voided loop variable in shadow
upkeep); fixed the initialize-then-overwrite narration in
spawnCreature; replaced a filter(() => false) no-op; waterwall now
rides waveFromEdge instead of carrying its own verbatim copy (and the
single-caller washBack wrapper went with it); blind wall-bumps and
LOS blockers each collapsed to one implementation; the wand-id list
and the "permanent" duration sentinel became named constants; the
ambush number local no longer shadows the imported numberValue
function; assorted reviewer-aimed phrasings rewritten as the
constraints they guard.

Server/deploy: the protocol header now documents all eleven message
types; dropped an eslint pragma with no eslint, a test script with no
tests, and an rsync exclude anchored at a path that never existed
(the real data/ dir now excluded); the Caddy vhost has one source of
truth; stale "pending DNS" note removed — the record resolves.

Web: ~90 lines of CSS swallowed verbatim into a mobile media query
deduplicated; the reduced-motion guard on the board now actually
stops the marked-cell pulse; one shared color module replaces two
drifted palettes; an orphaned doc comment rejoined its function.

Tests: the ten-times-pasted helper block became test/helpers.ts;
wave-numbered files renamed for the behaviors they pin; deliberation
comments and void-ed corpses of unwritten assertions deleted; silent
seed-dependent early-returns now fail loudly; one assertion that
compared a value to itself now pins the home-translation it meant to;
the stored-log single-number command form gained the explicit
compatibility test it deserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:56:55 -04:00
Eric WagonerandClaude Fable 5 85831828d7 The tally: a booklet tab counting the love
A new tab in the help booklet answers "how much is this getting
played?" from the ledgers themselves: games chronicled (begun and
fought to a finish), distinct wizards seated, every spell, step, and
punch recorded, time at the table estimated by the clock between
moves (gaps over ten minutes don't count — async games tell the
truth), victories split by treasure-theft versus last standing, the
longest game, the fullest table, and the date the first game was
dealt. Computed server-side over the in-memory rooms with a 60-second
cache, fetched over the socket when the tab opens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:31:14 -04:00
Eric WagonerandClaude Fable 5 ced4dc5749 Link previews and a wizard in the tab
Sharing the game's URL now unfurls a proper card: Open Graph and
Twitter meta tags with a 1200x630 preview composed from the physical
set's own counter art — WIZ-WAR in tall caps over the parchment, the
tagline in script, a row of wizard, troll, and treasure tokens, and
the fan-project line beneath. The favicon becomes the purple wizard
in his black hat, cropped from the counter sheet; the your-turn
variant wears a red badge, replacing the old lettered SVGs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:28:22 -04:00
Eric WagonerandClaude Fable 5 da4ab03135 The reveal remembers the hand a wizard fell holding
Elimination destroys the evidence: a killed wizard's cards pass to
the killer, a treasure-lost wizard's go to the discard — so the
post-game reveal showed "empty-handed" for exactly the player whose
hand everyone wants to see. Both elimination paths now snapshot the
hand at the moment of death (finalHand), the finished view serves it,
and the gallery marks those rows "as they fell" in script. Because
rooms replay from the command log, already-finished games show their
fallen hands too. Test kills a wizard holding a Fireball and a 6 and
finds them in the reveal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:20:10 -04:00
Eric WagonerandClaude Fable 5 e7b17f7643 The finished table lays every hand face-up
Once the game is won, the view stops keeping secrets: revealedHands
carries every player's cards, and the table replaces your hand row
with a face-up gallery — each wizard's name (trophy on the winner)
beside their final hand, tap any card to enlarge, "empty-handed"
written in script for those who died with nothing. In hotseat this is
the thing the winner passes around after dismissing the fanfare; in
async games it greets each player when they open the finished game.
Redaction still guards every phase before "finished."

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:17:07 -04:00
Eric WagonerandClaude Fable 5 13aeeea0ef Victory gets its moment: a gold-ringed proclamation
Winning showed only as a small slip in the rail — anticlimactic, as
playtesting put it. The engine now records HOW the game was won
(winReason: treasures carried home, or last wizard standing) alongside
the winner, the view carries it, and the finish opens a gold-ringed
modal: trophy, the winner's name in tall caps, and the manner of
victory in a handwritten line. "Behold the final board" dismisses it
to the finished table. In hotseat the one screen proclaims to the
whole table at once; in async games each player is greeted by it when
they next open the game.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:11:24 -04:00
Eric WagonerandClaude Fable 5 8f7beb5ef3 The card-naming field suggests as you type
Illusionary Attack, Deja-Vu, Card Erasure, and the other name-a-card
spells asked for exact spell names from memory. The field is now a
native datalist: type a few letters and matching card names appear,
tuned per card — Illusionary Attack offers only attack spells (it can
only pretend to be one), Drop Object offers only the movable objects
plus Treasure, and the rest offer the whole playable pool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:04:14 -04:00
Eric WagonerandClaude Fable 5 33b36feb8b Illusion Wall is castable: it was missing from the edge-target list
The engine has always accepted it — edge target, line of sight to the
edge, open corridor only — but the client's EDGE_CARDS list never
included illusion-wall, so selecting it offered no clickable wall
lines and the card was simply uncastable. The LOS dimming that
appears alongside is correct and stays: placement requires sight of
the wall line, so the lit squares show where the shimmering can go.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:58:32 -04:00
Eric WagonerandClaude Fable 5 db1fbf7099 Duration spells show themselves: chips on the sheet, size on the board
view.sustained was never rendered — every duration spell in play was
invisible, Big Man included, as playtesting found. Now each player's
scoresheet row lists the spells on them as violet chips (name and
turns remaining, tap to read the card), beside their displayed cards.
And on the board itself, Big Man makes the wizard token tower half
again as large, Shrink dwindles it — the exact at-a-glance "I have
become big" the table owes you.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:55:45 -04:00
Eric WagonerandClaude Fable 5 7ad3e7acc8 Selecting a creature opens its card, with living stats
Tapping a creature you command now does both things at once: selects
it for orders AND opens its card in the inspector corner, with a
stats line pinned beneath — hits left, moves left, attack ready or
spent (or barred on the turn it appears; the shadow reports itself
unharmed by ordinary damage). The same stats ride along on any
hold-to-peek of a creature, and they are derived live, so marching
the monster updates the numbers as you spend its moves. While
commanding, the hint strip carries the same line beside the hand,
with a "done" to dismiss. Deselecting closes the card.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:51:46 -04:00
Eric WagonerandClaude Fable 5 6e6691c6c0 Creatures step through board-edge warps by click, like wizards
The engine always warped a creature marched off a lettered opening —
verified headless — but the table offered no way to ask for it: cell
clicks for a selected creature matched only geometric neighbors, and
the warp arrows moved only your wizard. Now a selected creature
standing on an opening lights the arrow (and, per the existing cue,
its destination), the arrow marches the creature through, and
clicking the far mouth directly does the same.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:46:35 -04:00
Eric WagonerandClaude Fable 5 c4dafe1df7 Creatures with orders left announce themselves in the rail
Summoned monsters act during their controller's turn, but nothing
reminded you — a democratic monster in play was invisibly waiting on
every single player. Now each creature you may command this turn gets
a moss-green slip in the paper rail: name, moves remaining, whether
its attack is ready (respecting no-attack-on-creation and the
democratic monster's one-attack-per-round), and the tap-to-command
hint. The democratic monster reminds every player on their turn,
since all players get to move it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:43:13 -04:00
Eric WagonerandClaude Fable 5 97a3305d07 Every token on the board can be read as its card
"What does a rosebush do again?" now has an in-game answer: press and
hold any square (450ms) to open the card behind whatever occupies it —
creature, terrain, dropped object, warp token, or boobytrap marker —
in the view-only inspector corner. A plain click that claims no game
action peeks too: out of turn, every tap reads; on your turn, taps
beyond your reach read instead of doing nothing, and clicking an
opponent's creature with nothing selected reads its card. Priority
when a square holds several stories: creature, then terrain, then the
topmost dropped object.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:39:56 -04:00
Eric WagonerandClaude Fable 5 a9d40d73ac Face-up cards show under each name on the scoresheet
At the table, a displayed card sits in front of its wizard for all to
see — Master Key, Wizardblade, the stones, wands. The view always
carried every player's displayed cards, but only your own hand marked
them. Now each scoresheet row lists that player's face-up cards as
parchment chips, wands with their remaining charges as pips, and
tapping a chip enlarges the card in the inspector corner, view-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:34:28 -04:00
Eric WagonerandClaude Fable 5 4f3083c490 Targeting dims out-of-reach squares; Speedstone pays out immediately
Selecting an L.O.S. card now drops every square you cannot see into
shadow, leaving eligible targets lit — computed by the engine's own
hasLineOfSight over the player's VIEW, so illusion walls you still
believe in block your aim, stone and bushes and the Big Man block
everyone's, and Around The Corner widens the light one bend when
attached. ADJACENT cards light just your square and its four
neighbors. The shade paints over tokens and wizards alike, and
switches off while arming an ambush, since ambushes aim at the
future. A new sightedCellsFor lives in the engine view module so the
client can never disagree with the rules.

Speedstone, meanwhile, only took effect at next turn's allowance
recompute — displaying it mid-turn granted nothing, as playtesting
found. It now bumps the current allowance by one on display (a delta,
so number-card movement already played survives), unless SLOW or a
zeroed allowance says otherwise. Test covers the immediate grant and
the persistence into later turns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:33:02 -04:00
Eric WagonerandClaude Fable 5 a0d862ca10 Attacks announce themselves; the response moves beside the hand
Being attacked was announced only by a slip in the paper rail, easy
to miss mid-turn — the playtest complaint was real. Now an attack on
you (or a counter to your spell) opens a modal: the attacker's name
in red Oswald caps, the actual card face enlarged, the number backing
it if any, a bare 👊 for a punch, and a single "To arms!" stamp.
Dismissing it leaves the actionable prompt in the hint strip beside
your hand — where the counteraction cards you would tap actually
live — with the "let it resolve" escape. The rail keeps a one-line
reminder. Each new decision point in a counter-chain re-announces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:26:58 -04:00
Eric WagonerandClaude Fable 5 f9eff3341b Dimensional warps get the edge-warp treatment
Placing the two tokens worked, but using them asked too much: the
only affordance was a "Step through the warp" button in the actions
row, easy to miss and far from the board. Now standing on a warp
token pulses its twin with the same green dashed ring the lettered
edge openings use, and clicking the twin steps you through — one
gesture, learned once, working for both kinds of warp. The button
stays for those who found it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:21:27 -04:00
Eric WagonerandClaude Fable 5 57bd036417 The first square of a two-square spell now shows on the board
Dimensional Warp, Trader, Redirection, and Relocate Sector all take
two clicks, but only the hint bar acknowledged the first — the board
itself sat unchanged until the second click landed. The first chosen
square now wears a pulsing amber dashed ring (same rhythm as the warp
destination ring, still under prefers-reduced-motion) until the spell
completes or is cancelled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 10:17:57 -04:00
Eric WagonerandClaude Fable 5 789d927122 Daggers, rocks, blades, and wands can be set down too
The rulebook's movable-object list is wider than the stones: "magic
stones, treasure chests, the dagger, the large rock, and the
wizardblade" — and the expansion expects wands to change hands, since
"its remaining charges go with it." A new isMovableObject helper in
the engine names that full set, doDropObject honors it, and the hint
bar's "Drop it here" button follows suit. Charges are keyed by card
instance, so a dropped wand carries them to whoever picks it up —
covered by new tests, along with the refusal to drop spell cards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 09:59:09 -04:00
Eric WagonerandClaude Fable 5 c04ed1e0ee Objects can be set down and picked back up
The engine always knew how — dropObject and pickUpObject were in the
command set from the start — but the table never offered a hand for
it. Now selecting a magic stone (or any object card) on your turn
shows a "Drop it here" button in the hint bar, and standing on a
square with objects lying on it offers a "Pick up <name>" stamp per
object, disabled once your actions have ended (picking anything up
ends them, per the rulebook). The chronicle narrates both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 09:55:04 -04:00
Eric WagonerandClaude Fable 5 7a231cbe1b About: the story of the game, told on the box lid
A handwritten "about this game — a labor of love" link on the box lid
opens a new About tab in the booklet: what Wiz-War is, that Tom Jolly
created it in 1983, that this is a faithful reproduction of the
out-of-print Chessex sixth edition (with its one expansion) that Eric
and his friends played for countless hours in the early nineties —
rebuilt from that very copy, card by photographed card, wall by
verified wall, with the original hand-inked token art. It names Steve
Jackson Games as the current rightsholder, states plainly that this
is an unofficial, non-commercial fan work made with love and
admiration, and closes by pointing anyone the game delights toward
the edition currently in print and a real table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 09:43:09 -04:00
Eric WagonerandClaude Fable 5 45666bca16 Ambushes: the async-native form of Interrupt and Opportunity Fire
Eric's diagnosis: "in the moment" interruption cards are worthless in
correspondence play — there is no moment. The answer was already in
the game: WARD is a contingency card, and ambushes generalize it. On
your turn, playing Interrupt or Opportunity Fire now arms an ambush:
commit it with an attack from your hand (plus an optional number
card) and a trigger — an opponent entering your line of sight, coming
beside you, or grabbing a treasure — and it springs automatically
when the condition occurs, whether you are watching or asleep. The
sprung attack opens the normal counteraction stack, so the victim
gets their defense (asynchronously, like any attack). Committed cards
leave your hand until the trap springs or you disarm it; ambushes are
invisible to everyone but their owner (view-level redaction), die
with their owner, stay armed if the shot is momentarily illegal, and
honor the no-combat first round. Live play in the moment still works
too — and the client now actually offers it (the old UI never let
you click those cards out of turn). The rail shows your armed traps
with a disarm control; the chronicle announces AMBUSH! when one
springs. 124 tests passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:36:53 -04:00
Eric WagonerandClaude Fable 5 295ad2ad55 Catch-up replays and named attention for async games
"While you were away": returning to a game with unseen moves shows a
banner — "You missed N moves. Watch what happened" — that opens a
replay reel. The server rebuilds the game and captures a redacted
per-move view for the viewer (own hand only, capped at the last 200
moves); the client plays the reel on a full board with the actor and
humanized events captioned per step, auto-advancing with pause,
step-back/forward, arrow-key control, and skip-to-now. Seen progress
is tracked per room in the browser (every state broadcast now carries
the log sequence), so the banner only appears when there is genuinely
something to watch.

Attention between turns is now named, not just signaled: game
summaries carry WHY a game waits on you — your turn, counteract
(you're being attacked mid-someone-else's-turn), forced discard, or a
pending interruption — the ledger prints it ("UNDER ATTACK —
respond!"), and browser notifications say "you are under attack in
GNSK!" rather than a generic your-turn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:26:27 -04:00
Eric WagonerandClaude Fable 5 4cbf5a013e Lobby standee picker for online rooms
Waiting rooms now show the six wizard standees: tap to claim yours,
claimed ones gray out with the claimant named for screen readers, the
roster shows each player's chosen standee (or "choosing..."), and
conflicts are refused first-come. Choices sync live to everyone in
the room, resolve to first-free defaults for the undecided when the
host flips the boards, persist in the start line of the room log (so
restored games keep their colors), and flow through the same engine
config field the hotseat picker uses. Also fixed: the static-serving
realpath guard crashed the dev server when no client build exists —
it now 404s static requests instead. Verified end to end: claim,
conflict refusal, second pick, start, and in-game colorIndex 5/2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:19:42 -04:00
Eric WagonerandClaude Fable 5 77ae65b263 Pick your standee: hotseat wizards choose their color while naming
The naming round now shows the six real wizard standees; each player
taps theirs before typing their name, claimed standees gray out, and
the next player defaults to the first free one. The choice travels as
an optional colors array in the engine config, the view exposes each
player's colorIndex, and the board, score sheet, treasure chests, and
wizard art all follow the chosen color instead of seat order —
persisting through hotseat saves. Verified: Alice claimed the yellow
standee and marched onto the board as the yellow wizard with yellow
treasure chests. (Online rooms still assign by seat order; a lobby
color picker can reuse the same config field later.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:15:07 -04:00
Eric WagonerandClaude Fable 5 98d7dee5c7 Hotseat setup round: pick a count, then each wizard names themselves
The comma-separated name field is gone. Hotseat now starts with a
2-6 count picker and "Gather N wizards", which opens a naming round
in the pass-the-device style: "Wizard 1 of 3 — what is your name?",
type it, pass the device on; the last wizard's button reads "Flip the
boards" and starts the game. Duplicate and empty names are refused
in place, the roster-so-far shows beneath, and "never mind" backs
out. Verified end to end: three names entered, game started, opening
handoff went to the die-roll winner.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:12:13 -04:00
Eric WagonerandClaude Fable 5 ac82c28750 Fix lobby overflow on phones: global box-sizing and wrapping rows
The box lid combined width:100% with 2.6rem side padding and no
box-sizing reset, so the card ran off the right edge of real phones
(Join, Play Here, and Claim all clipped). A global border-box reset
fixes the class of bug; the hotseat and claim rows now wrap, their
inputs go full-width on narrow screens, and the lid tightens its
padding and title below 900px.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:07:32 -04:00
Eric WagonerandClaude Fable 5 30aa1e778d Tap-to-inspect: selecting a card floats an enlarged, readable copy
Selecting any card in hand now also shows a 1.55x (1.3x on phones)
enlarged rendering floating at the corner — full card text readable
without pinch-zooming — with a dismiss button, auto-reset when the
selection changes, and the board left clear for choosing a target.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:06:18 -04:00
Eric WagonerandClaude Fable 5 0927836e7e Mobile pass: hand above the paperwork, dvh sizing, tight masthead
At phone width the layout now flows board -> actions -> your hand ->
turn slip -> score sheet -> chronicle, instead of burying the cards
beneath the paperwork below the fold. The board caps at 56dvh (dvh
throughout, for iOS's shifting address bar), the chronicle stops
hogging height, and the masthead holds one line. The hand is a
thumb-swipeable card row; pinch-zoom stays enabled for reading fine
card text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 01:00:23 -04:00
Eric WagonerandClaude Fable 5 9323399b60 Phase 3: hotseat play — the whole game in one browser
Hotseat runs the engine entirely client-side; no server is involved.
The lobby takes comma-separated names (2-6 wizards), and the game
plays through the same table UI, with one addition: a full-screen
hand-off card between actors — "pass the device to Morgana, tap when
only they can see the screen" — shown whenever the needed input moves
to another wizard (turns, counteractions, forced discards,
interrupts). Each player sees only their own hand while seated. The
game saves itself to localStorage after every command (config +
command log, replayed on resume — the server's own determinism
trick), so "set the game aside" keeps it and "abandon game" forgets
it; a Resume Hotseat button appears whenever a save exists. Fixed en
route: the engine's structuredClone cannot digest Svelte's reactive
proxies, so hotseat snapshots state before every engine call.
Verified live: 3-player game started, reloaded, resumed, turn ended,
device handed to the next wizard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 00:13:59 -04:00
Eric WagonerandClaude Fable 5 85b40be113 Support 2-6 players: the L, the cross, and the 3x2 from the rulebook
Three player counts join the square and the column: 3 players get the
stair/L with the AUTO WARP joining the two openings that face the
concave notch (convex openings pair geometrically — flagged as
interpretation of the diagram's letters); 5 players get the plus/cross
with tip-to-tip wraps and aisle-warp corners; 6 players get the 3x2
rectangle with straight-across wraps. All six verified layouts are
drawn on, so no count repeats a sector. Rooms accept up to six seats;
the lobby copy follows. 122 tests passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 00:08:15 -04:00
Eric WagonerandClaude Fable 5 8d10dfee67 In-game help: how-to-play guide, the 6e rules, and a card library
"Help & rules" in the masthead (lobby and table alike) opens a paper
booklet with three tabs: How To Play (the client's own controls —
moving, casting, attaching numbers and modifiers, fighting, turns,
transfers), The Rules (the 6th edition rules condensed faithfully
from the verified rulebook transcription, exact numbers throughout,
with a Jolly Games colophon), and a Card Library — all 135 playable
cards rendered as the real card faces, searchable by name or text,
each labeled with its quantity and set. Escape closes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 00:06:51 -04:00
Eric WagonerandClaude Fable 5 2184721ab4 Seat transfer phrases: carry your games to another device
While seated, "transfer seat" in the masthead mints a spoken-word
one-time phrase from the game's own vocabulary (ember-troll-dagger),
good for ten minutes. Typing it into the lobby's "claim a transferred
seat" box on any other device hands over the seat's token, adds the
game to that browser's ledger, and sits you straight down at the
table. Phrases are single-use, expire, are voided by a server restart
(seats never are), and both devices keep the seat afterward — the
phone on the couch and the desktop upstairs can play the same wizard.
Verified end to end: mint, claim, rejoin, and a second claim refused.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:58:52 -04:00
Eric WagonerandClaude Fable 5 bc8013863b Phase 2: async play-by-turn — the games ledger and turn signals
The lobby is now the front door to all your games. A "your games"
ledger lists every seat this browser holds — room code, whose turn it
is (including counteraction/discard/interrupt waits, which count as
your turn), the round, and how long since the last move — with
one-click resume, a forget control, and a green highlight when a game
waits on you. The server answers a token-validated myGames query with
per-seat summaries; the client polls every 45 seconds, so turns in
other rooms reach you wherever you are. Turn signals travel three
ways: the tab title flips to "● Your turn", the favicon grows a green
dot, and — opt-in via "notify me on my turn" — a browser notification
fires when a turn becomes yours anywhere. Landing in the app now
shows the ledger rather than teleporting into the last game;
mid-game socket drops still walk straight back to the table.
Verified live: two seeded games, ledger showing "gandalf's turn" and
YOUR TURN, one-click resume into the correct game with history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:53:13 -04:00
Eric WagonerandClaude Fable 5 7cfffb8ab5 Persistent games: rooms survive server restarts by log replay
Every room is now an append-only JSONL file (data/rooms/CODE.jsonl):
a birth-certificate meta line, then every join, start, and command.
Because the engine is deterministic, seed + log IS the game — on boot
the server replays each file and reconstructs the exact state, hands,
deck order, and chronicle. Verified live: create, join, play, KILL
the server, restart ("restored 1 room(s) from disk"), rejoin with the
seat token — identical positions, deck count, and hand, with the
chronicle history redelivered redacted per player. The client
remembers its seat (name, room, token) in localStorage and walks back
to the table automatically on connect, clearing the memory if the
seat is stale; a quiet "leave table" control forgets it on purpose.
This is the foundation phase 2 (play-by-turn) sits on: games now wait
indefinitely for their players — and debugging restarts cost nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:46:23 -04:00
Eric WagonerandClaude Fable 5 89961bbb59 ADD joins two number cards on movement, as printed
"You may add two NUMBER cards together for any single action" —
movement included. The engine accepts a second movement number when an
ADD accompanies it (once per turn); the client spends your Add
automatically and says so on the button. 120 tests passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:41:37 -04:00
Eric WagonerandClaude Fable 5 42ea9b8f2c Voluntary discards, and no card ever casts on the first click
The rulebook allows discarding cards purely to churn for new ones, but
the client had no way to start it: a "Discard cards..." action now
switches the hand into marking mode (with a "never mind" exit), and
confirming sends the discard. Alongside it, the hair-trigger casts are
gone: selecting a card never plays it anymore — untargeted spells and
stone displays get a Cast button in the hint bar, and a bare number
card gets an explicit "Play for +N movement" button. Every play is
now a two-step, which is the correct cost in a game with no undo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:35:02 -04:00
Eric WagonerandClaude Fable 5 87a5067c77 Make wraparound warps usable: click the arrow to step through
The engine's warps worked all along (verified in all six directions),
but the client offered no way to trigger one short of clicking the
destination cell across the map. The warp arrows are now buttons: when
you stand on an opening its arrow lights green — click it to step
through. Clicking a far arrow behaves as a click on its cell.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:28:51 -04:00
Eric WagonerandClaude Fable 5 3e1fe059fa Disable treasure buttons when they cannot apply
Pick Up Treasure is disabled unless an uncarried treasure sits on your
square (and your actions haven't ended); Drop Treasure unless you are
actually carrying one. The warp-step button reuses the same derived
state. Buttons stay visible for stable layout — they just stop
inviting impossible clicks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:26:20 -04:00
Eric WagonerandClaude Fable 5 8c85c13e15 Widen the room-code input so CODE fits with its letter-spacing
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:25:06 -04:00
Eric WagonerandClaude Fable 5 9501a85be4 Design pass: the client becomes Eric's table
The design system is the physical set itself, taken from the scans:
card-stock cream, warm ink, and the six wizard marker colors, laid on
a dark leather tabletop. Cards in your hand are now the actual cards —
vertical corner rails (ATTACK / L.O.S. / ADJACENT), condensed gothic
titles, brown body text on cream, number cards as big bordered
numerals with per-value border styles; selecting lifts a card from
the fan. The board matches the physical tiles: ink-outlined white
brick walls on cracked flagstone, plank doors, eight-point home stars
in each wizard's color, swirled treasure discs, warp arrows. The
sidebar is paper: a turn slip, the rulebook's own hand-scrawled
life-points score sheet (Caveat over ruled lines), and a typewritten
chronicle that scrolls itself. The lobby is the box lid. Type:
Oswald condensed display / Archivo Narrow body / Courier Prime log.
Buttons are ink stamps. Board height-caps so board, actions, and the
full hand share one screen; responsive stack below 900px; reduced
motion respected. Verified with live screenshots at 1440x900.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:21:03 -04:00
Eric WagonerandClaude Fable 5 c9524a37db Thumb Of God lands: the 6th edition is 100% implemented
Eric chose the "divine meteor" redesign for the one card that cannot
be digitized faithfully (the physical version has you flick the die at
the board from six inches). Digital form: aim at a square in sight;
the die drifts 0-2 squares in a random direction, then every token in
and around the landing square — ground objects, treasures, creatures,
and wizards alike — is flung to a random nearby square. Walls mean
nothing to falling cardboard; tokens knocked off the board settle at
the nearest edge, per the original card; there is no counteraction.

With this, all 128 unique cards of the 6th edition game (69 basic +
59 Expansion Set #1) are implemented, tested, and playable online.
The "unimplemented card" guard test now points at an Expansion #2
shelf card, which is the only kind left. 119 tests passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:05:47 -04:00
Eric WagonerandClaude Fable 5 06eea72ded Expansion wave 4: 27 combat, curse, and utility cards
Fortune: GIFT FROM ABOVE (+3, no ceiling), GIFT FROM BELOW (a trap in
the deck — 3 damage on the draw, harmless in the opening deal).
Modifiers: POWER ATTACK burns life into any damage spell. Curses:
WEAKNESS (drop your treasure, take double, carry nothing), STRENGTH
(double physical dealt; the two cancel), WALKING DEAD (half a point
per space walked, forever), DISEASE (the victim becomes a carrier who
infects everyone in squares they enter), IDIOT (the victim shambles
toward their nearest own treasure, able only to counteract, until
they stand on it and ask "What am I doing here?"). Defense: EMPATHY
(attacks bite their caster too), FORCE FIELD (spell-stopping
counteraction). Mischief: MENTAL SWAP (trade whole hands), MENTAL
FORCE (march someone three spaces), BUTT-HEAD (become a goat, ram for
the distance charged), HEAVE-HO (throw your carried treasure as a
weapon), THIEF and SWAP-MEET (item larceny), CHAOS (all hands in a
pile, shuffled, redealt), ILLUSIONARY ATTACK (a fake spell that hurts
if believed), WARD (a trapped treasure bites its thief), REMOVE CURSE
(strip any duration spell, rolling to hit the shrunken or invisible),
SWARTHMORE'S ENCHANTMENT (+1 on an enchanted object). Space-time:
DIMENSIONAL WARP (paired step-through tokens), REDIRECTION (swap two
outer exits' wraparounds), BIG MAN (fills the corridor: no entry, no
punches, no casting past him), FEAR (nobody approaches within 3),
and the out-of-turn pair — INTERRUPT and OPPORTUNITY FIRE — which
open a one-action window in another player's turn. Only THUMB OF GOD
remains, awaiting its digital redesign. 117 tests passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 23:01:42 -04:00
Eric WagonerandClaude Fable 5 3a35922a40 Expansion wave 3: twelve terrain cards
KILLER OOZE (1 damage on entry, slip on 1-2: drop treasure, 2 more,
flat on your face until you roll to stand), CREATE PIT (jumps roll a
D4 — clear it on 2-4, fall in on a 1 and climb out on later rolls),
ROSEBUSH (3-point passage, blocks sight), DUST CLOUD (a permanent
zone of blindness: direction rolls inside, no LOS through, defeats
Visionstone), FILL SQUARE WITH SLIME (entering ends your actions,
blocks sight), HANDFUL OF TACKS (adjacent-only scatter, 3 points to
cross), CREATE DOOR (a new locked door in any wall or corridor),
BOOBYTRAP (four face-down tokens, one secretly real — 4 points under
anyone but the caster; the caster's view alone marks the true token),
GLUE (objects pinned for twice the number card), SAFE (items locked
away from everyone but the creator), TRADER (swap two floor items in
sight; glue and safes hold fast), and STONE TO WATER (walls melt into
range-2 waves, stone blocks into range-4 bursts). LOS blocking is now
per-terrain-kind. The board renders all of it, boobytrap placement is
a four-click ritual, and Trader is two. 108 tests passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 22:52:34 -04:00