@@ -737,23 +719,6 @@
.creature-ring.selected { stroke-width: 4; stroke-dasharray: 5 3; }
.ground-object { fill: #a6812e; stroke: #4a3a10; stroke-width: 1; }
.illusion { stroke: #7a6f9a; stroke-width: 4; stroke-dasharray: 6 5; opacity: 0.7; }
- .illusion-shimmer {
- stroke: #cfc3f0;
- stroke-width: 3;
- stroke-dasharray: 4 5;
- stroke-linecap: round;
- pointer-events: none;
- animation: shimmer-drift 1.4s linear infinite;
- }
- .illusion-hit { stroke: transparent; stroke-width: 14; cursor: pointer; }
- @keyframes shimmer-drift {
- 0% { stroke-dashoffset: 0; opacity: 0.9; }
- 50% { opacity: 0.4; }
- 100% { stroke-dashoffset: -18; opacity: 0.9; }
- }
- @media (prefers-reduced-motion: reduce) {
- .illusion-shimmer { animation: none; opacity: 0.7; }
- }
.creature { cursor: pointer; }
.creature-body { fill: #3b3428; stroke-width: 2.5; }
.creature-body.selected { fill: #6b5a34; }
@@ -823,24 +788,6 @@
.ghost-slot:hover { fill: rgba(122, 162, 122, 0.22); }
.fx-layer { pointer-events: none; }
- .sight-layer { pointer-events: none; }
- .sight-line {
- stroke: #c9a72a;
- stroke-width: 2.5;
- stroke-linecap: round;
- stroke-dasharray: 7 6;
- opacity: 0.85;
- animation: sight-march 0.8s linear infinite;
- }
- .sight-mouth {
- fill: none;
- stroke: #c9a72a;
- stroke-width: 2;
- animation: warp-pulse 1.6s ease-in-out infinite;
- }
- @keyframes sight-march {
- to { stroke-dashoffset: -13; }
- }
.mover { transition: transform 260ms cubic-bezier(0.25, 0.8, 0.35, 1); }
.mover.snap { transition: none; }
@@ -867,7 +814,6 @@
}
@media (prefers-reduced-motion: reduce) {
.marked-cell, .marked-sector, .ghost-slot, .warp-dest { animation: none; }
- .sight-line, .sight-mouth { animation: none; }
.fx-layer { display: none; }
:global(.token-art.ghosted) { animation: none; }
.mover { transition: none; }
diff --git a/packages/web/src/FxGallery.svelte b/packages/web/src/FxGallery.svelte
index 268d927..8c75ec4 100644
--- a/packages/web/src/FxGallery.svelte
+++ b/packages/web/src/FxGallery.svelte
@@ -5,6 +5,9 @@
import type { Component } from "svelte";
import { FX_SPRITES } from "./fx-sprites";
import type { BoardFx, FxShape } from "./fx";
+ import FirewallEdge from "./FirewallEdge.svelte";
+ import IllusionShimmer from "./IllusionShimmer.svelte";
+ import SightTraceOverlay from "./SightTraceOverlay.svelte";
let tick = $state(0);
$effect(() => {
@@ -73,6 +76,63 @@
{/each}
+
+ The standing fires
+
+ Board furniture that burns as long as its spell holds — these loop by
+ nature, no beat needed. Same components the live board mounts.
+
+
+
+
+ wall of fire (H)
+
+
+
+ wall of fire (V)
+
+
+
+ illusion shimmer
+
+
+
+ sight trace (direct)
+
+
+
+ sight trace (through a warp)
+
+
diff --git a/packages/web/src/SightTraceOverlay.svelte b/packages/web/src/SightTraceOverlay.svelte
new file mode 100644
index 0000000..fe3b18a
--- /dev/null
+++ b/packages/web/src/SightTraceOverlay.svelte
@@ -0,0 +1,54 @@
+
+
+
+ {#if trace.kind === "direct"}
+
+ {:else}
+ {@const entry = { x: trace.entry.x * CELL, y: trace.entry.y * CELL }}
+ {@const exit = { x: trace.exit.x * CELL, y: trace.exit.y * CELL }}
+
+
+
+
+ {/if}
+
+
+