diff --git a/packs/armour/LOG b/packs/armour/LOG
index bad08a5..ea0335e 100644
--- a/packs/armour/LOG
+++ b/packs/armour/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.251914 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.252258 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.252372 1744c7000 Level-0 table #5: 2618 bytes OK
-2026/07/22-11:06:42.252427 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.252467 1744c7000 Manual compaction at level-0 from '!items!GOAakTqMDZSCDOdE' @ 72057594037927935 : 1 .. '!items!vHyUhdri4IKpq9CW' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.342167 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.342695 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.342842 1724f3000 Level-0 table #5: 2618 bytes OK
+2026/07/22-22:53:09.342915 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.342966 1724f3000 Manual compaction at level-0 from '!items!GOAakTqMDZSCDOdE' @ 72057594037927935 : 1 .. '!items!vHyUhdri4IKpq9CW' @ 0 : 0; will stop at (end)
diff --git a/packs/bestiary/LOG b/packs/bestiary/LOG
index 7e8cd12..11d29c3 100644
--- a/packs/bestiary/LOG
+++ b/packs/bestiary/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.318704 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.319032 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.319244 1744c7000 Level-0 table #5: 5226 bytes OK
-2026/07/22-11:06:42.319295 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.319326 1744c7000 Manual compaction at level-0 from '!actors!351sA95uALULY3Kv' @ 72057594037927935 : 1 .. '!actors!rPfEYHmSoprnQdrY' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.402765 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.403157 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.403344 1724f3000 Level-0 table #5: 5226 bytes OK
+2026/07/22-22:53:09.403402 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.403444 1724f3000 Manual compaction at level-0 from '!actors!351sA95uALULY3Kv' @ 72057594037927935 : 1 .. '!actors!rPfEYHmSoprnQdrY' @ 0 : 0; will stop at (end)
diff --git a/packs/companions/LOG b/packs/companions/LOG
index f4f1f9e..9ce1fef 100644
--- a/packs/companions/LOG
+++ b/packs/companions/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.322638 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.323336 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.323628 1744c7000 Level-0 table #5: 4974 bytes OK
-2026/07/22-11:06:42.323701 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.323743 1744c7000 Manual compaction at level-0 from '!actors!1FGl7v1mODKvN9fy' @ 72057594037927935 : 1 .. '!actors!yEfKBqSa4GmIgOpx' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.406577 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.407048 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.407182 1724f3000 Level-0 table #5: 4974 bytes OK
+2026/07/22-22:53:09.407246 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.407280 1724f3000 Manual compaction at level-0 from '!actors!1FGl7v1mODKvN9fy' @ 72057594037927935 : 1 .. '!actors!yEfKBqSa4GmIgOpx' @ 0 : 0; will stop at (end)
diff --git a/packs/edges/LOG b/packs/edges/LOG
index 4a7a673..06f266c 100644
--- a/packs/edges/LOG
+++ b/packs/edges/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.310645 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.310944 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.311148 1744c7000 Level-0 table #5: 5417 bytes OK
-2026/07/22-11:06:42.311209 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.311251 1744c7000 Manual compaction at level-0 from '!items!21hRQJtWGk5fDQlo' @ 72057594037927935 : 1 .. '!items!zMphj752klzDFU3Q' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.395152 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.395469 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.395594 1724f3000 Level-0 table #5: 5417 bytes OK
+2026/07/22-22:53:09.395643 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.395676 1724f3000 Manual compaction at level-0 from '!items!21hRQJtWGk5fDQlo' @ 72057594037927935 : 1 .. '!items!zMphj752klzDFU3Q' @ 0 : 0; will stop at (end)
diff --git a/packs/gear/LOG b/packs/gear/LOG
index d24c560..0b958bc 100644
--- a/packs/gear/LOG
+++ b/packs/gear/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.268229 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.268754 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.268950 1744c7000 Level-0 table #5: 9529 bytes OK
-2026/07/22-11:06:42.269017 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.269061 1744c7000 Manual compaction at level-0 from '!items!27LcrYsIEEF3KYHA' @ 72057594037927935 : 1 .. '!items!yo48EzCVEwNdpkXF' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.357214 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.357850 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.358066 1724f3000 Level-0 table #5: 9529 bytes OK
+2026/07/22-22:53:09.358124 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.358162 1724f3000 Manual compaction at level-0 from '!items!27LcrYsIEEF3KYHA' @ 72057594037927935 : 1 .. '!items!yo48EzCVEwNdpkXF' @ 0 : 0; will stop at (end)
diff --git a/packs/gm/LOG b/packs/gm/LOG
index 1940d0c..d4f62b9 100644
--- a/packs/gm/LOG
+++ b/packs/gm/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.344443 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.344750 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.345011 1744c7000 Level-0 table #5: 10149 bytes OK
-2026/07/22-11:06:42.345068 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.345104 1744c7000 Manual compaction at level-0 from '!journal!JJEgDJvtWBnFbyZA' @ 72057594037927935 : 1 .. '!journal.pages!kBclT2QiwEQmOsX1.HSVD8c7BenQjb45v' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.428790 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.429078 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.429289 1724f3000 Level-0 table #5: 10149 bytes OK
+2026/07/22-22:53:09.429337 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.429371 1724f3000 Manual compaction at level-0 from '!journal!JJEgDJvtWBnFbyZA' @ 72057594037927935 : 1 .. '!journal.pages!kBclT2QiwEQmOsX1.HSVD8c7BenQjb45v' @ 0 : 0; will stop at (end)
diff --git a/packs/pregens/000004.log b/packs/pregens/000004.log
new file mode 100644
index 0000000..e69de29
diff --git a/packs/pregens/CURRENT b/packs/pregens/CURRENT
index f7753e2..1a84852 100644
--- a/packs/pregens/CURRENT
+++ b/packs/pregens/CURRENT
@@ -1 +1 @@
-MANIFEST-000006
+MANIFEST-000002
diff --git a/packs/pregens/LOG b/packs/pregens/LOG
index f02eb1f..5c2f1be 100644
--- a/packs/pregens/LOG
+++ b/packs/pregens/LOG
@@ -1,3 +1,5 @@
-2026/07/22-11:06:52.981350 16fe8f000 Recovering log #4
-2026/07/22-11:06:52.982189 16fe8f000 Delete type=0 #4
-2026/07/22-11:06:52.982310 16fe8f000 Delete type=3 #2
+2026/07/22-22:53:09.418973 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.419962 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.420751 1724f3000 Level-0 table #5: 120609 bytes OK
+2026/07/22-22:53:09.420822 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.420867 1724f3000 Manual compaction at level-0 from '!actors!3Kjqu9ibyf8TjSrk' @ 72057594037927935 : 1 .. '!actors.items!zR0OZzFsVQLTSlBM.uc6AKpWyZeREAX2M' @ 0 : 0; will stop at (end)
diff --git a/packs/pregens/MANIFEST-000002 b/packs/pregens/MANIFEST-000002
new file mode 100644
index 0000000..91c8077
Binary files /dev/null and b/packs/pregens/MANIFEST-000002 differ
diff --git a/packs/rules/000005.ldb b/packs/rules/000005.ldb
index 7b793b3..769576b 100644
Binary files a/packs/rules/000005.ldb and b/packs/rules/000005.ldb differ
diff --git a/packs/rules/LOG b/packs/rules/LOG
index fe8343e..1fdc4b9 100644
--- a/packs/rules/LOG
+++ b/packs/rules/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.341352 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.341857 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.342338 1744c7000 Level-0 table #5: 63868 bytes OK
-2026/07/22-11:06:42.342395 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.342429 1744c7000 Manual compaction at level-0 from '!journal!9PRuE5Q81BnO22fH' @ 72057594037927935 : 1 .. '!journal.pages!9PRuE5Q81BnO22fH.qCt9jGDZePPN5S6G' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.424854 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.425505 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.426080 1724f3000 Level-0 table #5: 77503 bytes OK
+2026/07/22-22:53:09.426135 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.426181 1724f3000 Manual compaction at level-0 from '!journal!CNrYR2rXwHMBm0lH' @ 72057594037927935 : 1 .. '!journal.pages!CNrYR2rXwHMBm0lH.yPi5N6WdEe1jn19i' @ 0 : 0; will stop at (end)
diff --git a/packs/rules/MANIFEST-000002 b/packs/rules/MANIFEST-000002
index 74d9f7e..ce18d77 100644
Binary files a/packs/rules/MANIFEST-000002 and b/packs/rules/MANIFEST-000002 differ
diff --git a/packs/spells/LOG b/packs/spells/LOG
index 20c262a..59616b6 100644
--- a/packs/spells/LOG
+++ b/packs/spells/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.303697 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.304162 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.304433 1744c7000 Level-0 table #5: 11569 bytes OK
-2026/07/22-11:06:42.304491 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.304526 1744c7000 Manual compaction at level-0 from '!items!13KOIsQOsgYqdVEX' @ 72057594037927935 : 1 .. '!items!zywiqamhRlcCgLJY' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.388862 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.389229 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.389426 1724f3000 Level-0 table #5: 11569 bytes OK
+2026/07/22-22:53:09.389478 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.389511 1724f3000 Manual compaction at level-0 from '!items!13KOIsQOsgYqdVEX' @ 72057594037927935 : 1 .. '!items!zywiqamhRlcCgLJY' @ 0 : 0; will stop at (end)
diff --git a/packs/tables/LOG b/packs/tables/LOG
index 582a57d..44f390b 100644
--- a/packs/tables/LOG
+++ b/packs/tables/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.348478 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.348956 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.349173 1744c7000 Level-0 table #5: 15588 bytes OK
-2026/07/22-11:06:42.349228 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.349266 1744c7000 Manual compaction at level-0 from '!tables!JEOcxCxjK6S6pVEZ' @ 72057594037927935 : 1 .. '!tables.results!rurx5ZPcPObdbNDD.bWBU56ZArkeat9DO' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.432699 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.433167 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.433520 1724f3000 Level-0 table #5: 15588 bytes OK
+2026/07/22-22:53:09.433594 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.433642 1724f3000 Manual compaction at level-0 from '!tables!JEOcxCxjK6S6pVEZ' @ 72057594037927935 : 1 .. '!tables.results!rurx5ZPcPObdbNDD.bWBU56ZArkeat9DO' @ 0 : 0; will stop at (end)
diff --git a/packs/treasure/LOG b/packs/treasure/LOG
index 0c356cd..efdaf47 100644
--- a/packs/treasure/LOG
+++ b/packs/treasure/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.294803 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.295458 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.296003 1744c7000 Level-0 table #5: 27619 bytes OK
-2026/07/22-11:06:42.296069 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.296113 1744c7000 Manual compaction at level-0 from '!items!0NDwQbhQQNeCCEco' @ 72057594037927935 : 1 .. '!items!yxds1AZFNXBpERdk' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.382119 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.382653 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.383100 1724f3000 Level-0 table #5: 27619 bytes OK
+2026/07/22-22:53:09.383168 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.383221 1724f3000 Manual compaction at level-0 from '!items!0NDwQbhQQNeCCEco' @ 72057594037927935 : 1 .. '!items!yxds1AZFNXBpERdk' @ 0 : 0; will stop at (end)
diff --git a/packs/vices/LOG b/packs/vices/LOG
index d85c1e5..5a91a77 100644
--- a/packs/vices/LOG
+++ b/packs/vices/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.313391 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.313714 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.313812 1744c7000 Level-0 table #5: 1461 bytes OK
-2026/07/22-11:06:42.313868 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.313900 1744c7000 Manual compaction at level-0 from '!items!7kO3VzfFPt8QRVXT' @ 72057594037927935 : 1 .. '!items!s50CZgFUaS5puSOp' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.397830 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.398050 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.398158 1724f3000 Level-0 table #5: 1461 bytes OK
+2026/07/22-22:53:09.398227 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.398280 1724f3000 Manual compaction at level-0 from '!items!7kO3VzfFPt8QRVXT' @ 72057594037927935 : 1 .. '!items!s50CZgFUaS5puSOp' @ 0 : 0; will stop at (end)
diff --git a/packs/weapons/LOG b/packs/weapons/LOG
index 5a61026..47694df 100644
--- a/packs/weapons/LOG
+++ b/packs/weapons/LOG
@@ -1,5 +1,5 @@
-2026/07/22-11:06:42.244940 173c2f000 Delete type=3 #1
-2026/07/22-11:06:42.246581 1744c7000 Level-0 table #5: started
-2026/07/22-11:06:42.246870 1744c7000 Level-0 table #5: 7415 bytes OK
-2026/07/22-11:06:42.246943 1744c7000 Delete type=0 #3
-2026/07/22-11:06:42.246992 1744c7000 Manual compaction at level-0 from '!items!0d6FZCtl78jEPkof' @ 72057594037927935 : 1 .. '!items!v3IghflF7cI2FzbU' @ 0 : 0; will stop at (end)
+2026/07/22-22:53:09.334806 171c5b000 Delete type=3 #1
+2026/07/22-22:53:09.336531 1724f3000 Level-0 table #5: started
+2026/07/22-22:53:09.336701 1724f3000 Level-0 table #5: 7415 bytes OK
+2026/07/22-22:53:09.336755 1724f3000 Delete type=0 #3
+2026/07/22-22:53:09.336799 1724f3000 Manual compaction at level-0 from '!items!0d6FZCtl78jEPkof' @ 72057594037927935 : 1 .. '!items!v3IghflF7cI2FzbU' @ 0 : 0; will stop at (end)
diff --git a/styles/vanity.css b/styles/vanity.css
index 2262bc1..51b5e13 100644
--- a/styles/vanity.css
+++ b/styles/vanity.css
@@ -376,6 +376,25 @@
font-style: normal;
margin-left: 4px;
}
+/* Targeting — the defender's answer, on the attack card */
+.vanity-target {
+ margin-top: 8px;
+ padding: 7px 9px;
+ border: 1px solid var(--v-plum);
+ border-left-width: 3px;
+ border-radius: 3px;
+ background: rgba(0, 0, 0, 0.05);
+}
+.vanity-target .tgt-head { font-size: 13px; }
+.vanity-target .tgt-sub { color: var(--v-ink2); font-size: 11.5px; margin-left: 5px; }
+.vanity-target .pierced { color: var(--v-gold-d); font-style: italic; }
+.vanity-target .tgt-dice { margin: 3px 0; font-size: 17px; line-height: 1.2; }
+.vanity-target .tgt-dice .d { margin-right: 2px; opacity: 0.55; }
+.vanity-target .tgt-dice .d.hit { opacity: 1; color: var(--v-teal-d); }
+.vanity-target .tgt-dice .d.one { opacity: 1; color: var(--v-plum); }
+.vanity-target .tgt-res { font-size: 11.5px; color: var(--v-ink2); margin-left: 6px; }
+.vanity-target .tgt-net { font-size: 12.5px; }
+
/* Straining the Weave (§17) — repeats in a scene raise the Threshold */
.vanity-sheet .strain-badge {
font-size: 10px;
diff --git a/system.json b/system.json
index 6e5a23c..840783c 100644
--- a/system.json
+++ b/system.json
@@ -2,7 +2,7 @@
"id": "vanity",
"title": "VANITY — the Vanity Role-Playing Game",
"description": "A generic d6 RPG where ego is a resource and the universe keeps score. Pool = Attribute + Knack + situation; 5–6 = a Success; fail with two 1s = a Stumble that banks a Bane of Karma (Draft 13). No hit points — Grit. Vanity, Karma and Glory drive play. Includes the full Draft 13 rulebook, all spells and gear, the starter bestiary, a 100-item Treasures & Relics library, twenty ready-to-play characters (each with cameo art, token and backstory), ten animal companions with the d10 Bond table and the one-shot adventure The Vain Crown.",
- "version": "0.9.8",
+ "version": "0.10.0",
"compatibility": {
"minimum": 13,
"verified": "14.364"
diff --git a/templates/character-sheet.hbs b/templates/character-sheet.hbs
index 4e0a033..17f7177 100644
--- a/templates/character-sheet.hbs
+++ b/templates/character-sheet.hbs
@@ -274,6 +274,9 @@
{{!-- ============ TAB: STORY ============ --}}
+ {{!-- The Bond is a Ranger's Locked Knack (§7). Anyone else who has somehow
+ acquired a companion keeps their card; nobody else is offered one. --}}
+ {{#if hasCompanion}}
diff --git a/vanity.mjs b/vanity.mjs
index 96bf4d8..0b4d2e1 100644
--- a/vanity.mjs
+++ b/vanity.mjs
@@ -96,11 +96,43 @@ const MANEUVERS = [
desc: "Make one extra attack immediately. Remember the cap: at most one extra attack per round, however it's paid for (Press or Rapid Shot, not both — §11a)." }
];
+/**
+ * Spell Effects (§17) — the caster's maneuver menu. Successes past the
+ * Threshold buy these as readily as they buy the spell's own scaling line.
+ * The `fits` line is guidance for the table, not a hard gate: common sense
+ * (§6a) decides whether this spell could plausibly do this thing.
+ */
+const SPELL_EFFECTS = [
+ { key: "ignite", label: "Ignite", cost: 1, hint: "1 Grit at the start of each of their turns until put out.",
+ desc: "The target burns: 1 Grit at the start of each of their turns until they spend an Action smothering it. Fire, acid, lightning." },
+ { key: "blind", label: "Blind", cost: 1, hint: "−2 dice on anything needing sight, one round.",
+ desc: "Light, dust or a flood of dark: −2 dice on anything that needs eyes, until the end of their next turn. Light, illusion, weather." },
+ { key: "snare", label: "Snare", cost: 1, hint: "Held until they win a Brawn contest.",
+ desc: "Roots, ice, webbing or grasping shadow — the target is Held until they win a Brawn contest to tear free. Nature, ice, shadow." },
+ { key: "push", label: "Push", cost: 1, hint: "Thrown a pace and knocked prone.",
+ desc: "The target is thrown a pace and knocked prone — off the parapet, out of the circle, into the fire you just lit. Force, wind, water." },
+ { key: "silence", label: "Silence", cost: 1, hint: "They cannot cast until their next turn.",
+ desc: "No breath, no words, no incantation: the target cannot cast until their next turn. The cleanest answer to an enemy spellcaster. Divine, mind, sound." },
+ { key: "shield", label: "Shield the Ally", cost: 1, hint: "An ally gains +2 defence dice until their next turn.",
+ desc: "Spend the power outward instead: an ally gains +2 defence dice until their next turn. Any protective list." },
+ { key: "spread", label: "Spread", cost: 2, hint: "Also strikes one further target within reach.",
+ desc: "The magic carries: it also strikes one further target within reach of the first, for your remaining damage. Fire, sound, area." },
+ { key: "linger", label: "Linger", cost: 2, hint: "Lasts a second round — and doesn't strain the Weave for it.",
+ desc: "The effect lasts a second round with no second casting — and crucially does not strain the Weave (§17) for it. Wards, clouds, blessings." },
+ { key: "unmake", label: "Unmake", cost: 2, hint: "Strip a ward, blessing, resistance or enchantment.",
+ desc: "Strip a magical defence: a ward, a blessing, a resistance, or one enchantment on an object. Arcane, divine." },
+ { key: "mark", label: "Mark of the Divine", cost: 2, hint: "Faith only: allies get +1 die against them all scene.",
+ desc: "Faith only. The target is Marked: every ally's attack against them gains +1 die until the scene ends. Divine only." }
+];
+
+/** Which menu a card's spare Successes may be spent from. */
+function menuFor(context) { return context === "spell" ? SPELL_EFFECTS : MANEUVERS; }
+
/**
* The interactive spend panel for attack cards: click maneuvers to buy them
* with Successes, dial in the defender's Successes, read off the damage.
*/
-function renderSpendPanel({ successes, spend, sublabel }) {
+function renderSpendPanel({ successes, spend, sublabel, menu = MANEUVERS, context = "attack", threshold = 0 }) {
const s = { defence: 0, damage: 0, maneuvers: [], ...(spend ?? {}) };
const net = successes - s.defence;
@@ -117,35 +149,36 @@ function renderSpendPanel({ successes, spend, sublabel }) {
`;
- if (net <= 0) {
+ if (net <= 0 && context !== "spell") {
return `
`;
}
+/**
+ * The exchange, resolved (§10): who was struck at, what their guard rolled,
+ * and what is left to land. Damage is applied on a button so the GM stays in
+ * charge of the fiction — Subdue, mercy and "he was already down" all live in
+ * the beat between the roll and the click.
+ */
+function targetBlock(target, results, spend) {
+ if (!target) return "";
+ const successes = results.filter(r => r >= 5).length;
+ const net = Math.max(0, successes - (spend?.defence ?? target.successes ?? 0));
+ const damage = spend?.damage ?? 0;
+ const pips = (target.results ?? []).map(r =>
+ `${DIE_FACES[r] ?? r}`).join("");
+ const pierceNote = target.piercing
+ ? (target.pierced
+ ? ` · armour-piercing — ${target.full}d cut to ${target.pool}d`
+ : ` · armour-piercing — nothing left to strip`)
+ : "";
+ return `
+
`;
+}
+
+/**
+ * Take the damage off the target's Grit and say what it did.
+ *
+ * Applied once — the button disarms itself afterwards, so a card cannot be
+ * clicked twice in the noise of a fight. Emptying Grit announces Taken Out
+ * (§5a) but never decides the state: Down, Broken or Dying is the GM's call,
+ * and Subdue was declared before the roll.
+ */
+async function applyDamageToTarget(message, damage) {
+ const f = message.flags?.vanity;
+ if (!f?.target?.uuid || damage <= 0) return;
+ if (f.applied) return ui.notifications.warn("That blow has already been paid for.");
+
+ const actor = await fromUuid(f.target.uuid);
+ if (!actor) return ui.notifications.warn("That target is no longer on the board.");
+
+ const grit = actor.system?.grit ?? {};
+ const before = Number(grit.value) || 0;
+ const after = Math.max(0, before - damage);
+ await actor.update({ "system.grit.value": after });
+
+ const takenOut = after === 0 && before > 0;
+ await message.update({ "flags.vanity.applied": true });
+ await ChatMessage.create({
+ speaker: { alias: "The Exchange" },
+ content: `