diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte
index 36dfcc2..93e5f30 100644
--- a/packages/web/src/App.svelte
+++ b/packages/web/src/App.svelte
@@ -1756,6 +1756,20 @@
view && me ? view.treasures.filter((t) => t.owner !== me!.id && t.position &&
t.position.x === me!.home.x && t.position.y === me!.home.y).length : 0,
);
+ /** Chests you carried home THIS turn: the drop is the game's biggest
+ * moment, and the turn bar's quiet tally alone let it pass unremarked. */
+ let scoredThisTurn = $state(0);
+ let scoreTurnKey = "";
+ let homeAtTurnStart = 0;
+ $effect(() => {
+ const key = view && view.activePlayerId === view.you ? `${view.turn.round}:${view.activePlayerId}` : "";
+ if (key !== scoreTurnKey) { scoreTurnKey = key; homeAtTurnStart = treasuresHome; }
+ scoredThisTurn = key ? Math.max(0, treasuresHome - homeAtTurnStart) : 0;
+ });
+ /** Steps spent, actions not: the turn waits on a spell or End turn,
+ * and a still board with no word reads as a game that froze. */
+ const movesSpent = $derived(!!view && view.activePlayerId === view.you && !view.turn.actionsEnded &&
+ view.turn.movementAllowance - view.turn.movementUsed <= 0);
const paBurnable = $derived(me ? Array.from({ length: Math.max(0, me.life - 1) }, (_, i) => i + 1) : []);
/** Two treasures can share a square: one dispatches, more ask whose. */
const treasuresHere = $derived(
@@ -3291,8 +3305,16 @@
{/if}
{:else}
{view.turn.movementAllowance - view.turn.movementUsed} moves left{view.turn.attackUsed ? " ยท attack spent" : ""} ยท treasures home {treasuresHome} of 2
+ {#if movesSpent}
+
Out of steps โ cast a spell, or End turn.
+ {/if}
{/if}
+ {#if scoredThisTurn > 0 && treasuresHome < 2}
+
+ ๐ Scored โ {treasuresHome} of 2. One more stolen treasure carried home wins the game.
+
+ {/if}
{#if !prefs.coached && !view.turn.actionsEnded}
@@ -3658,7 +3680,7 @@
{:else if net.confirmed}
โ {net.confirmed} confirmed
{/if}
-
+
{/if}
@@ -4401,6 +4423,14 @@
.stamp:disabled { opacity: 0.45; cursor: default; }
.stamp.primary { background: #43331f; color: #e9e1cb; }
.stamp.primary:hover:not(:disabled) { background: #5a4429; }
+ .stamp.beckon:not(:disabled) { animation: beckon 1.6s ease-in-out infinite; }
+ @keyframes beckon {
+ 0%, 100% { box-shadow: 2px 2px 0 rgba(0,0,0,0.35), 0 0 0 0 rgba(201, 167, 42, 0); }
+ 50% { box-shadow: 2px 2px 0 rgba(0,0,0,0.35), 0 0 0 4px rgba(201, 167, 42, 0.55); }
+ }
+ @media (prefers-reduced-motion: reduce) {
+ .stamp.beckon:not(:disabled) { animation: none; box-shadow: 0 0 0 3px rgba(201, 167, 42, 0.7); }
+ }
.stamp.big { font-size: 0.95rem; padding: 0.6rem 1.3rem; }
.stamp.tiny { font-size: 0.62rem; padding: 0.22rem 0.5rem; box-shadow: 1px 1px 0 rgba(0,0,0,0.3); }
.prefs-slip { text-align: left; }
@@ -4598,6 +4628,7 @@
.slip.urgent { border-left: 4px solid #b3372b; transform: rotate(0.4deg); }
.slip.ambush-note { border-left: 4px solid #43331f; font-size: 0.85rem; }
.slip.catchup { border-left: 4px solid #5b3f9e; }
+ .slip.scored { border-left: 4px solid #c9a72a; background: #f3e7b8; }
.catchup-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.catchup-facts { list-style: none; margin: 0.45rem 0 0; padding: 0; font-size: 0.9rem; }
.catchup-facts li { margin: 0.15rem 0; }