diff --git a/src/apps/epic/models.py b/src/apps/epic/models.py
index 84794cb..c14a269 100644
--- a/src/apps/epic/models.py
+++ b/src/apps/epic/models.py
@@ -341,6 +341,16 @@ class TarotCard(models.Model):
import json
return json.dumps(self.cautions)
+ @property
+ def mechanisms_json(self):
+ import json
+ return json.dumps(self.mechanisms)
+
+ @property
+ def articulations_json(self):
+ import json
+ return json.dumps(self.articulations)
+
def __str__(self):
return self.name
diff --git a/src/apps/epic/static/apps/epic/sig-select.js b/src/apps/epic/static/apps/epic/sig-select.js
index 6fc49be..232b05c 100644
--- a/src/apps/epic/static/apps/epic/sig-select.js
+++ b/src/apps/epic/static/apps/epic/sig-select.js
@@ -6,7 +6,7 @@ var SigSelect = (function () {
};
var overlay, deckGrid, stage, stageCard, statBlock;
- var cautionEl, cautionEffect, cautionPrev, cautionNext, cautionIndexEl;
+ var cautionEl, cautionEffect, cautionTitle, cautionPrev, cautionNext, cautionIndexEl;
var _flipBtn, _cautionBtn, _flipOrigLabel, _cautionOrigLabel;
var reserveUrl, readyUrl, userRole, userPolarity;
@@ -47,13 +47,16 @@ var SigSelect = (function () {
function _renderCaution() {
if (_cautionData.length === 0) {
- cautionEffect.innerHTML = 'Rival interactions pending.';
+ if (cautionTitle) cautionTitle.textContent = 'Ally Interaction';
+ cautionEffect.innerHTML = 'No ally interactions defined.';
cautionPrev.disabled = true;
cautionNext.disabled = true;
cautionIndexEl.textContent = '';
return;
}
- cautionEffect.innerHTML = _cautionData[_cautionIdx];
+ var entry = _cautionData[_cautionIdx];
+ if (cautionTitle) cautionTitle.textContent = entry.category || '';
+ cautionEffect.innerHTML = entry.effect || '';
cautionPrev.disabled = (_cautionData.length <= 1);
cautionNext.disabled = (_cautionData.length <= 1);
cautionIndexEl.textContent = _cautionData.length > 1
@@ -64,7 +67,9 @@ var SigSelect = (function () {
function _openCaution() {
if (!_focusedCardEl) return;
try {
- _cautionData = JSON.parse(_focusedCardEl.dataset.cautions || '[]');
+ var mechanisms = JSON.parse(_focusedCardEl.dataset.mechanisms || '[]');
+ var articulations = JSON.parse(_focusedCardEl.dataset.articulations || '[]');
+ _cautionData = mechanisms.concat(articulations);
} catch (e) {
_cautionData = [];
}
@@ -623,6 +628,7 @@ var SigSelect = (function () {
cautionEl = stage.querySelector('.sig-caution-tooltip');
cautionEffect = cautionEl.querySelector('.sig-caution-effect');
+ cautionTitle = cautionEl.querySelector('.sig-caution-title');
cautionPrev = statBlock.querySelector('.sig-caution-prev');
cautionNext = statBlock.querySelector('.sig-caution-next');
cautionIndexEl = cautionEl.querySelector('.sig-caution-index');
diff --git a/src/static/tests/SigSelectSpec.js b/src/static/tests/SigSelectSpec.js
index a93d148..15f1aef 100644
--- a/src/static/tests/SigSelectSpec.js
+++ b/src/static/tests/SigSelectSpec.js
@@ -29,21 +29,20 @@ describe("SigSelect", () => {
@@ -61,6 +60,8 @@ describe("SigSelect", () => {
data-keywords-upright="action,impulsiveness,ambition"
data-keywords-reversed="no direction,disregard for consequences"
data-cautions="${cardCautions.replace(/"/g, '"')}"
+ data-mechanisms="[]"
+ data-articulations="[]"
data-levity-qualifier="Elevated"
data-gravity-qualifier="Graven"
data-reversal="Territoriality">
@@ -238,71 +239,96 @@ describe("SigSelect", () => {
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(true);
});
- it("shows placeholder text when cautions list is empty", () => {
- card.dataset.cautions = "[]";
+ it("shows placeholder when both mechanisms and articulations are empty", () => {
+ card.dataset.mechanisms = "[]";
+ card.dataset.articulations = "[]";
openCaution();
- expect(cautionEffect.innerHTML).toContain("pending");
+ expect(cautionEffect.innerHTML).toContain("No ally interactions defined");
});
- it("renders first caution effect HTML including .card-ref spans", () => {
- card.dataset.cautions = JSON.stringify(['First Card effect.']);
+ it("renders first mechanism effect HTML including .card-ref spans", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: 'First Card effect.' }
+ ]);
openCaution();
expect(cautionEffect.querySelector(".card-ref")).not.toBeNull();
expect(cautionEffect.querySelector(".card-ref").textContent).toBe("Card");
});
- it("with 1 caution both nav arrows are disabled", () => {
- card.dataset.cautions = JSON.stringify(["Single caution."]);
+ it("with 1 entry both nav arrows are disabled", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "Single." }]);
openCaution();
expect(cautionPrev.disabled).toBe(true);
expect(cautionNext.disabled).toBe(true);
});
- it("with multiple cautions both nav arrows are always enabled", () => {
- card.dataset.cautions = JSON.stringify(["C1", "C2", "C3", "C4"]);
+ it("with multiple entries both nav arrows are always enabled", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "C1" },
+ { category: "Mechanism", effect: "C2" },
+ { category: "Mechanism", effect: "C3" },
+ { category: "Mechanism", effect: "C4" },
+ ]);
openCaution();
expect(cautionPrev.disabled).toBe(false);
expect(cautionNext.disabled).toBe(false);
});
- it("next click advances to second caution", () => {
- card.dataset.cautions = JSON.stringify(["First", "Second"]);
+ it("next click advances to second entry", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Second" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("Second");
});
- it("next wraps from last caution back to first", () => {
- card.dataset.cautions = JSON.stringify(["First", "Last"]);
+ it("next wraps from last entry back to first", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Last" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("First");
});
- it("prev click goes back to first caution", () => {
- card.dataset.cautions = JSON.stringify(["First", "Second"]);
+ it("prev click goes back to first entry", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Second" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
cautionPrev.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("First");
});
- it("prev wraps from first caution to last", () => {
- card.dataset.cautions = JSON.stringify(["First", "Middle", "Last"]);
+ it("prev wraps from first entry to last", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Middle" },
+ { category: "Mechanism", effect: "Last" },
+ ]);
openCaution();
cautionPrev.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("Last");
});
- it("index label shows n / total when multiple cautions", () => {
- card.dataset.cautions = JSON.stringify(["C1", "C2", "C3"]);
+ it("index label shows n / total when multiple entries", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "C1" },
+ { category: "Mechanism", effect: "C2" },
+ { category: "Mechanism", effect: "C3" },
+ ]);
openCaution();
expect(testDiv.querySelector(".sig-caution-index").textContent).toBe("1 / 3");
});
- it("index label is empty when only 1 caution", () => {
- card.dataset.cautions = JSON.stringify(["Only one."]);
+ it("index label is empty when only 1 entry", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "Only one." }]);
openCaution();
expect(testDiv.querySelector(".sig-caution-index").textContent).toBe("");
});
@@ -314,8 +340,11 @@ describe("SigSelect", () => {
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(false);
});
- it("opening again resets to first caution", () => {
- card.dataset.cautions = JSON.stringify(["First", "Second"]);
+ it("opening again resets to first entry", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Second" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
// Close and reopen
@@ -757,4 +786,87 @@ describe("SigSelect", () => {
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
});
+
+ // ── FYI tooltip — mechanisms + articulations data source ──────────────── //
+ //
+ // Sprint 2: the caution tooltip is reworked to draw from data-mechanisms and
+ // data-articulations instead of data-cautions. Entries are {category, effect}
+ // dicts; the category label replaces the old "Caution!" title; the caution-type
+ // reads "Ally Interaction". Shoptalk is absent.
+
+ describe("FYI from mechanisms + articulations", () => {
+ var cautionEffect, cautionTitle, cautionType, cautionPrev, cautionNext, cautionBtn;
+
+ beforeEach(() => {
+ makeFixture();
+ cautionEffect = testDiv.querySelector(".sig-caution-effect");
+ cautionTitle = testDiv.querySelector(".sig-caution-title");
+ cautionType = testDiv.querySelector(".sig-caution-type");
+ cautionPrev = testDiv.querySelector(".sig-caution-prev");
+ cautionNext = testDiv.querySelector(".sig-caution-next");
+ cautionBtn = testDiv.querySelector(".sig-caution-btn");
+ });
+
+ function hover() {
+ card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
+ }
+ function openFYI() {
+ hover();
+ cautionBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ }
+
+ it("caution-type label reads 'Ally Interaction'", () => {
+ openFYI();
+ expect(cautionType.textContent).toBe("Ally Interaction");
+ });
+
+ it("shows 'No ally interactions defined.' when both lists are empty", () => {
+ card.dataset.mechanisms = "[]";
+ card.dataset.articulations = "[]";
+ openFYI();
+ expect(cautionEffect.textContent).toContain("No ally interactions defined");
+ });
+
+ it("renders first mechanism effect and sets title to its category", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "The card amplifies adjacent power." }
+ ]);
+ openFYI();
+ expect(cautionTitle.textContent).toBe("Mechanism");
+ expect(cautionEffect.textContent).toContain("amplifies adjacent power");
+ });
+
+ it("mechanisms come before articulations in the combined list", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "First" }]);
+ card.dataset.articulations = JSON.stringify([{ category: "Articulation", effect: "Second" }]);
+ openFYI();
+ expect(cautionEffect.textContent).toContain("First");
+ cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ expect(cautionEffect.textContent).toContain("Second");
+ });
+
+ it("articulation title is set from its category field", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "M1" }]);
+ card.dataset.articulations = JSON.stringify([{ category: "Articulation", effect: "A1" }]);
+ openFYI();
+ cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ expect(cautionTitle.textContent).toBe("Articulation");
+ });
+
+ it("effect HTML is injected (supports .card-ref spans)", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: 'Draw The Occultist.' }
+ ]);
+ openFYI();
+ expect(cautionEffect.querySelector(".card-ref")).not.toBeNull();
+ expect(cautionEffect.querySelector(".card-ref").textContent).toBe("The Occultist");
+ });
+
+ it("shoptalk element is absent or empty", () => {
+ openFYI();
+ var shoptalk = testDiv.querySelector(".sig-caution-shoptalk");
+ // Either removed from DOM or has no visible content
+ expect(!shoptalk || shoptalk.textContent.trim() === "").toBe(true);
+ });
+ });
});
diff --git a/src/static_src/scss/_card-deck.scss b/src/static_src/scss/_card-deck.scss
index 06465fa..09fcfb3 100644
--- a/src/static_src/scss/_card-deck.scss
+++ b/src/static_src/scss/_card-deck.scss
@@ -173,7 +173,6 @@ html:has(.sig-backdrop) {
align-items: flex-end;
padding-left: 1.5rem;
gap: 0.75rem;
-
// Preview card — width driven by JS via --sig-card-w; aspect-ratio derives height.
.sig-stage-card {
flex-shrink: 0;
@@ -188,6 +187,7 @@ html:has(.sig-backdrop) {
position: relative;
padding: 0.25rem;
overflow: hidden;
+ transition: transform 0.4s ease;
// game-kit sets .fan-card-corner { position: absolute; top/left offsets }
// so these just need display/font overrides; the corners land at the card edges.
@@ -230,6 +230,26 @@ html:has(.sig-backdrop) {
.fan-card-name { font-size: calc(var(--sig-card-w, 120px) * 0.093); font-weight: 600; }
.fan-card-arcana { font-size: calc(var(--sig-card-w, 120px) * 0.067); text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.5; }
.fan-card-correspondence{ display: none; } // Minchiate equivalence shown in game-kit only
+ // Reversed face — counter-rotated so they read forward when card is spun
+ .fan-card-reversal-qualifier,
+ .fan-card-reversal-name {
+ transform: rotate(180deg);
+ opacity: 0.25;
+ transition: opacity 0.2s;
+ font-weight: 600;
+ }
+ .fan-card-reversal-qualifier { font-size: calc(var(--sig-card-w, 120px) * 0.093); }
+ .fan-card-reversal-name { font-size: calc(var(--sig-card-w, 120px) * 0.073); opacity: 0.2; }
+ }
+
+ &.stage-card--reversed {
+ transform: rotate(180deg);
+
+ .fan-card-reversal-qualifier,
+ .fan-card-reversal-name { opacity: 1; }
+ .fan-card-name,
+ .sig-qualifier-above,
+ .sig-qualifier-below { opacity: 0.25; }
}
}
diff --git a/src/static_src/tests/SigSelectSpec.js b/src/static_src/tests/SigSelectSpec.js
index a93d148..15f1aef 100644
--- a/src/static_src/tests/SigSelectSpec.js
+++ b/src/static_src/tests/SigSelectSpec.js
@@ -29,21 +29,20 @@ describe("SigSelect", () => {
@@ -61,6 +60,8 @@ describe("SigSelect", () => {
data-keywords-upright="action,impulsiveness,ambition"
data-keywords-reversed="no direction,disregard for consequences"
data-cautions="${cardCautions.replace(/"/g, '"')}"
+ data-mechanisms="[]"
+ data-articulations="[]"
data-levity-qualifier="Elevated"
data-gravity-qualifier="Graven"
data-reversal="Territoriality">
@@ -238,71 +239,96 @@ describe("SigSelect", () => {
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(true);
});
- it("shows placeholder text when cautions list is empty", () => {
- card.dataset.cautions = "[]";
+ it("shows placeholder when both mechanisms and articulations are empty", () => {
+ card.dataset.mechanisms = "[]";
+ card.dataset.articulations = "[]";
openCaution();
- expect(cautionEffect.innerHTML).toContain("pending");
+ expect(cautionEffect.innerHTML).toContain("No ally interactions defined");
});
- it("renders first caution effect HTML including .card-ref spans", () => {
- card.dataset.cautions = JSON.stringify(['First Card effect.']);
+ it("renders first mechanism effect HTML including .card-ref spans", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: 'First Card effect.' }
+ ]);
openCaution();
expect(cautionEffect.querySelector(".card-ref")).not.toBeNull();
expect(cautionEffect.querySelector(".card-ref").textContent).toBe("Card");
});
- it("with 1 caution both nav arrows are disabled", () => {
- card.dataset.cautions = JSON.stringify(["Single caution."]);
+ it("with 1 entry both nav arrows are disabled", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "Single." }]);
openCaution();
expect(cautionPrev.disabled).toBe(true);
expect(cautionNext.disabled).toBe(true);
});
- it("with multiple cautions both nav arrows are always enabled", () => {
- card.dataset.cautions = JSON.stringify(["C1", "C2", "C3", "C4"]);
+ it("with multiple entries both nav arrows are always enabled", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "C1" },
+ { category: "Mechanism", effect: "C2" },
+ { category: "Mechanism", effect: "C3" },
+ { category: "Mechanism", effect: "C4" },
+ ]);
openCaution();
expect(cautionPrev.disabled).toBe(false);
expect(cautionNext.disabled).toBe(false);
});
- it("next click advances to second caution", () => {
- card.dataset.cautions = JSON.stringify(["First", "Second"]);
+ it("next click advances to second entry", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Second" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("Second");
});
- it("next wraps from last caution back to first", () => {
- card.dataset.cautions = JSON.stringify(["First", "Last"]);
+ it("next wraps from last entry back to first", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Last" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("First");
});
- it("prev click goes back to first caution", () => {
- card.dataset.cautions = JSON.stringify(["First", "Second"]);
+ it("prev click goes back to first entry", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Second" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
cautionPrev.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("First");
});
- it("prev wraps from first caution to last", () => {
- card.dataset.cautions = JSON.stringify(["First", "Middle", "Last"]);
+ it("prev wraps from first entry to last", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Middle" },
+ { category: "Mechanism", effect: "Last" },
+ ]);
openCaution();
cautionPrev.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(cautionEffect.innerHTML).toContain("Last");
});
- it("index label shows n / total when multiple cautions", () => {
- card.dataset.cautions = JSON.stringify(["C1", "C2", "C3"]);
+ it("index label shows n / total when multiple entries", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "C1" },
+ { category: "Mechanism", effect: "C2" },
+ { category: "Mechanism", effect: "C3" },
+ ]);
openCaution();
expect(testDiv.querySelector(".sig-caution-index").textContent).toBe("1 / 3");
});
- it("index label is empty when only 1 caution", () => {
- card.dataset.cautions = JSON.stringify(["Only one."]);
+ it("index label is empty when only 1 entry", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "Only one." }]);
openCaution();
expect(testDiv.querySelector(".sig-caution-index").textContent).toBe("");
});
@@ -314,8 +340,11 @@ describe("SigSelect", () => {
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(false);
});
- it("opening again resets to first caution", () => {
- card.dataset.cautions = JSON.stringify(["First", "Second"]);
+ it("opening again resets to first entry", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "First" },
+ { category: "Mechanism", effect: "Second" },
+ ]);
openCaution();
cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
// Close and reopen
@@ -757,4 +786,87 @@ describe("SigSelect", () => {
expect(takeSigBtn.classList.contains("btn-cancel")).toBe(false);
});
});
+
+ // ── FYI tooltip — mechanisms + articulations data source ──────────────── //
+ //
+ // Sprint 2: the caution tooltip is reworked to draw from data-mechanisms and
+ // data-articulations instead of data-cautions. Entries are {category, effect}
+ // dicts; the category label replaces the old "Caution!" title; the caution-type
+ // reads "Ally Interaction". Shoptalk is absent.
+
+ describe("FYI from mechanisms + articulations", () => {
+ var cautionEffect, cautionTitle, cautionType, cautionPrev, cautionNext, cautionBtn;
+
+ beforeEach(() => {
+ makeFixture();
+ cautionEffect = testDiv.querySelector(".sig-caution-effect");
+ cautionTitle = testDiv.querySelector(".sig-caution-title");
+ cautionType = testDiv.querySelector(".sig-caution-type");
+ cautionPrev = testDiv.querySelector(".sig-caution-prev");
+ cautionNext = testDiv.querySelector(".sig-caution-next");
+ cautionBtn = testDiv.querySelector(".sig-caution-btn");
+ });
+
+ function hover() {
+ card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
+ }
+ function openFYI() {
+ hover();
+ cautionBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ }
+
+ it("caution-type label reads 'Ally Interaction'", () => {
+ openFYI();
+ expect(cautionType.textContent).toBe("Ally Interaction");
+ });
+
+ it("shows 'No ally interactions defined.' when both lists are empty", () => {
+ card.dataset.mechanisms = "[]";
+ card.dataset.articulations = "[]";
+ openFYI();
+ expect(cautionEffect.textContent).toContain("No ally interactions defined");
+ });
+
+ it("renders first mechanism effect and sets title to its category", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: "The card amplifies adjacent power." }
+ ]);
+ openFYI();
+ expect(cautionTitle.textContent).toBe("Mechanism");
+ expect(cautionEffect.textContent).toContain("amplifies adjacent power");
+ });
+
+ it("mechanisms come before articulations in the combined list", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "First" }]);
+ card.dataset.articulations = JSON.stringify([{ category: "Articulation", effect: "Second" }]);
+ openFYI();
+ expect(cautionEffect.textContent).toContain("First");
+ cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ expect(cautionEffect.textContent).toContain("Second");
+ });
+
+ it("articulation title is set from its category field", () => {
+ card.dataset.mechanisms = JSON.stringify([{ category: "Mechanism", effect: "M1" }]);
+ card.dataset.articulations = JSON.stringify([{ category: "Articulation", effect: "A1" }]);
+ openFYI();
+ cautionNext.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ expect(cautionTitle.textContent).toBe("Articulation");
+ });
+
+ it("effect HTML is injected (supports .card-ref spans)", () => {
+ card.dataset.mechanisms = JSON.stringify([
+ { category: "Mechanism", effect: 'Draw The Occultist.' }
+ ]);
+ openFYI();
+ expect(cautionEffect.querySelector(".card-ref")).not.toBeNull();
+ expect(cautionEffect.querySelector(".card-ref").textContent).toBe("The Occultist");
+ });
+
+ it("shoptalk element is absent or empty", () => {
+ openFYI();
+ var shoptalk = testDiv.querySelector(".sig-caution-shoptalk");
+ // Either removed from DOM or has no visible content
+ expect(!shoptalk || shoptalk.textContent.trim() === "").toBe(true);
+ });
+ });
});
diff --git a/src/templates/apps/gameboard/_partials/_sig_select_overlay.html b/src/templates/apps/gameboard/_partials/_sig_select_overlay.html
index e20e40b..a632548 100644
--- a/src/templates/apps/gameboard/_partials/_sig_select_overlay.html
+++ b/src/templates/apps/gameboard/_partials/_sig_select_overlay.html
@@ -29,6 +29,8 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
{# not shown in sig-select — game-kit only #}
+
+
@@ -39,19 +41,18 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
@@ -72,9 +73,11 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
data-correspondence="{{ card.correspondence|default:'' }}"
data-keywords-upright="{{ card.keywords_upright|join:',' }}"
data-keywords-reversed="{{ card.keywords_reversed|join:',' }}"
- data-cautions="{{ card.cautions_json }}"
+ data-mechanisms="{{ card.mechanisms_json }}"
+ data-articulations="{{ card.articulations_json }}"
data-levity-qualifier="{{ card.levity_qualifier }}"
- data-gravity-qualifier="{{ card.gravity_qualifier }}">
+ data-gravity-qualifier="{{ card.gravity_qualifier }}"
+ data-reversal="{{ card.reversal }}">
{{ card.corner_rank }}
{% if card.suit_icon %}{% endif %}