2026-04-26 21:30:27 -04:00
|
|
|
{% load static %}
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
{# DRAW SEA overlay — Celtic Cross spread entry #}
|
2026-04-26 21:30:27 -04:00
|
|
|
{# Included in room.html when table_status == "SKY_SELECT" and sky_confirmed #}
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
{# Layout is the reverse of CAST SKY: cards left (transparent), form right #}
|
2026-04-26 21:30:27 -04:00
|
|
|
|
|
|
|
|
<div class="sea-backdrop"></div>
|
2026-04-28 23:02:49 -04:00
|
|
|
<div class="sea-overlay" id="id_sea_overlay"
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
data-sea-deck-url="{% url 'epic:sea_deck' room.id %}"
|
|
|
|
|
data-sea-user-polarity="{{ user_polarity }}">
|
2026-04-26 21:30:27 -04:00
|
|
|
|
|
|
|
|
<div class="sea-modal-wrap">
|
|
|
|
|
<div class="sea-modal">
|
|
|
|
|
|
|
|
|
|
<header class="sea-modal-header">
|
2026-05-08 15:49:58 -04:00
|
|
|
<h2>SEA <span>SELECT</span></h2>
|
2026-05-01 02:06:55 -04:00
|
|
|
<p>Draw +6 cards to describe your character's influences and seed the map.</p>
|
2026-04-26 21:30:27 -04:00
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<div class="sea-modal-body">
|
|
|
|
|
|
|
|
|
|
{# ── Cards column (transparent) ───────────────────────────── #}
|
|
|
|
|
<div class="sea-cards-col">
|
|
|
|
|
<div class="sea-cross">
|
2026-04-28 23:02:49 -04:00
|
|
|
{# Crown — CC pos 3 / EV pos 5 #}
|
2026-04-29 14:19:30 -04:00
|
|
|
<div class="sea-crucifix-cell sea-pos-crown">
|
2026-04-26 21:30:27 -04:00
|
|
|
<div class="sea-card-slot sea-card-slot--empty"></div>
|
|
|
|
|
</div>
|
2026-04-28 23:02:49 -04:00
|
|
|
{# Beneath (past) — CC pos 4 / EV pos 3 #}
|
2026-04-29 14:19:30 -04:00
|
|
|
<div class="sea-crucifix-cell sea-pos-leave">
|
2026-04-26 21:30:27 -04:00
|
|
|
<div class="sea-card-slot sea-card-slot--empty"></div>
|
|
|
|
|
</div>
|
2026-04-28 23:02:49 -04:00
|
|
|
{# Center — Significator (always placed) + Cover + Cross overlaid #}
|
2026-04-29 14:19:30 -04:00
|
|
|
<div class="sea-crucifix-cell sea-pos-core">
|
2026-04-28 23:30:07 -04:00
|
|
|
<div class="sig-stage-card sea-sig-card" style="--sig-card-w: 4rem">
|
2026-04-26 21:30:27 -04:00
|
|
|
{% if my_tray_sig %}
|
2026-04-28 23:30:07 -04:00
|
|
|
<span class="fan-corner-rank">{{ my_tray_sig.corner_rank }}</span>
|
|
|
|
|
{% if my_tray_sig.suit_icon %}<i class="fa-solid {{ my_tray_sig.suit_icon }}"></i>{% endif %}
|
2026-04-26 21:30:27 -04:00
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
2026-04-28 23:02:49 -04:00
|
|
|
{# Cover — CC/EV pos 1, stacked face-up on Sig #}
|
|
|
|
|
<div class="sea-pos-cover">
|
|
|
|
|
<div class="sea-card-slot sea-card-slot--empty"></div>
|
|
|
|
|
</div>
|
|
|
|
|
{# Cross — CC/EV pos 2, rotated 90° on Cover #}
|
|
|
|
|
<div class="sea-pos-cross">
|
|
|
|
|
<div class="sea-card-slot sea-card-slot--empty sea-card-slot--crossing"></div>
|
|
|
|
|
</div>
|
2026-04-26 21:30:27 -04:00
|
|
|
</div>
|
2026-04-28 23:02:49 -04:00
|
|
|
{# Before (future) — CC pos 5 / EV pos 6 #}
|
2026-04-29 14:19:30 -04:00
|
|
|
<div class="sea-crucifix-cell sea-pos-loom">
|
2026-04-26 21:30:27 -04:00
|
|
|
<div class="sea-card-slot sea-card-slot--empty"></div>
|
|
|
|
|
</div>
|
2026-04-28 23:02:49 -04:00
|
|
|
{# Behind (root) — CC pos 6 / EV pos 4 #}
|
2026-04-29 14:19:30 -04:00
|
|
|
<div class="sea-crucifix-cell sea-pos-lay">
|
2026-04-26 21:30:27 -04:00
|
|
|
<div class="sea-card-slot sea-card-slot--empty"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{# ── Form column (priUser / opaque) ───────────────────────── #}
|
|
|
|
|
<div class="sea-form-col">
|
|
|
|
|
|
|
|
|
|
<div class="sea-form-main">
|
|
|
|
|
<div class="sea-field">
|
2026-04-30 21:01:52 -04:00
|
|
|
<label for="id_sea_spread" id="id_sea_spread_label">Spread</label>
|
2026-05-01 00:11:40 -04:00
|
|
|
{% comment %}
|
|
|
|
|
Reversal-rate hint — `stack_reversal_pct` flows from
|
|
|
|
|
apps.epic.utils.stack_reversal_probability via the
|
|
|
|
|
view. Currently a module default; placeholder UI for
|
|
|
|
|
a forthcoming per-user setting.
|
|
|
|
|
{% endcomment %}
|
|
|
|
|
<p class="sea-reversal-hint">{{ stack_reversal_pct|default:25 }}% reversals</p>
|
2026-04-30 21:01:52 -04:00
|
|
|
{% comment %}
|
|
|
|
|
Custom combobox — native <select> dropdowns ignore most CSS on
|
|
|
|
|
Firefox/Chrome (OS-rendered list); this gives full styling control.
|
|
|
|
|
combobox.js wires up the keyboard nav, click-outside-to-close, and
|
|
|
|
|
writes the chosen value to the hidden <input id="id_sea_spread"> so
|
|
|
|
|
sea.js's existing `spreadSel.value` read still works.
|
|
|
|
|
{% endcomment %}
|
|
|
|
|
<input type="hidden" id="id_sea_spread" name="spread"
|
|
|
|
|
value="{% if user_polarity == 'levity' %}waite-smith{% else %}escape-velocity{% endif %}">
|
|
|
|
|
<div class="sea-select"
|
|
|
|
|
data-combobox
|
|
|
|
|
data-combobox-target="id_sea_spread"
|
|
|
|
|
role="combobox"
|
|
|
|
|
aria-expanded="false"
|
|
|
|
|
aria-haspopup="listbox"
|
|
|
|
|
aria-labelledby="id_sea_spread_label"
|
|
|
|
|
tabindex="0">
|
|
|
|
|
<span class="sea-select-current">{% if user_polarity == "levity" %}Celtic Cross, Waite-Smith{% else %}Celtic Cross, Escape Velocity{% endif %}</span>
|
|
|
|
|
<span class="sea-select-arrow" aria-hidden="true">▾</span>
|
|
|
|
|
<ul class="sea-select-list" role="listbox">
|
|
|
|
|
{% if user_polarity == "levity" %}
|
|
|
|
|
<li role="option" data-value="waite-smith" aria-selected="true">Celtic Cross, Waite-Smith</li>
|
|
|
|
|
<li role="option" data-value="escape-velocity" aria-selected="false">Celtic Cross, Escape Velocity</li>
|
|
|
|
|
{% else %}
|
|
|
|
|
<li role="option" data-value="escape-velocity" aria-selected="true">Celtic Cross, Escape Velocity</li>
|
|
|
|
|
<li role="option" data-value="waite-smith" aria-selected="false">Celtic Cross, Waite-Smith</li>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
2026-04-26 21:30:27 -04:00
|
|
|
</div>
|
2026-04-28 23:02:49 -04:00
|
|
|
|
|
|
|
|
{# Two face-down deck piles — tap to proffer OK #}
|
|
|
|
|
<div class="sea-stacks">
|
2026-04-28 23:30:07 -04:00
|
|
|
<span class="sea-stacks-label">DECKS</span>
|
2026-04-28 23:02:49 -04:00
|
|
|
<div class="sea-deck-stack sea-deck-stack--gravity">
|
2026-04-28 23:30:07 -04:00
|
|
|
<div class="sea-stack-face">
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
<button class="btn btn-reveal sea-stack-ok" type="button">FLIP</button>
|
2026-04-28 23:30:07 -04:00
|
|
|
</div>
|
|
|
|
|
<span class="sea-stack-name">Gravity</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="sea-deck-stack sea-deck-stack--levity">
|
|
|
|
|
<div class="sea-stack-face">
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
<button class="btn btn-reveal sea-stack-ok" type="button">FLIP</button>
|
2026-04-28 23:30:07 -04:00
|
|
|
</div>
|
|
|
|
|
<span class="sea-stack-name">Levity</span>
|
2026-04-28 23:02:49 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-26 21:30:27 -04:00
|
|
|
</div>
|
|
|
|
|
|
2026-04-28 23:02:49 -04:00
|
|
|
<div class="sea-form-actions">
|
|
|
|
|
<button type="button" id="id_sea_lock_hand" class="btn btn-primary" disabled>
|
|
|
|
|
LOCK HAND
|
|
|
|
|
</button>
|
|
|
|
|
<button type="button" id="id_sea_del" class="btn btn-danger">
|
|
|
|
|
DEL
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-04-26 21:30:27 -04:00
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>{# /.sea-modal-body #}
|
|
|
|
|
|
|
|
|
|
</div>{# /.sea-modal #}
|
|
|
|
|
|
|
|
|
|
<button type="button" id="id_sea_cancel" class="btn btn-cancel btn-sm">NVM</button>
|
|
|
|
|
|
|
|
|
|
</div>{# /.sea-modal-wrap #}
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
|
|
|
|
|
{# ── Sea stage — big card viewer ─────────────────────────────────────────── #}
|
My Sea iter-4a follow-up batch: modal port + draw polish + label positioning + Major Arcana fix — TDD
User-driven bug-squash + UX-polish cycle on top of iter 4a (ca2a62f). All 14 fixes ship behind the same iter-4a banner since they close the substage's UX gaps without expanding scope to iter 4b's persistence layer.
SeaDeal modal port — extracted apps/gameboard/_partials/_sea_stage.html shared by gameroom + my-sea; aliased .my-sea-picker w. id=id_sea_overlay so SeaDeal.init() finds it; FLIP click → SeaDeal.openStage delegation instead of bare _fillSlot. Fixes the user-reported 'thumbnail disappears' bug — slot was landing at opacity 0 (.--filled w.o .--visible) because SeaDeal's _hideStage (which adds --visible on modal dismiss) was never running. 3 new FTs cover the modal flow.
Spread lock + DEL reshuffle — _lockSpread/_unlockSpread toggle .sea-select--locked class on the combobox; first deposit locks, _resetHand unlocks. _reshuffleDeck Fisher-Yates over combined piles + re-rolls 25% reversal axis on DEL so successive DELs don't re-deal the same hand. Verified Claudezilla: 3 DEL cycles produced distinct lay cards (150 → 114 → 155).
Cover/cross empty slots — subtle dotted outline (transparent bg + 0.25 alpha border) w. --duoUser mask reveal on hover/touch. Per the user spec; rule lives in _card-deck.scss (shared between gameroom + my-sea). Plus matching label-opacity (0.25 idle → 0.6 hover) via CSS :hover ancestor propagation.
DOS spec — Solution moved from cover → crown per user correction. DRAW_ORDER ['loom', 'cross', 'crown']; POSITION_LABELS {loom: Desire, cross: Obstacle, crown: Solution}; SCSS hide list flipped from [leave, crown, lay] → [leave, cover, lay]; FT/IT assertions updated.
SAO → DOS soft-reload bug — Firefox autofill on hidden input restored the previous-session DOS value, tripping combobox.js's change-event guard. Fix: autocomplete=off + force-sync hidden.value from server-rendered aria-selected option in init. Captured as feedback_firefox_autofill_hidden_inputs (generalizable trap).
.sea-pos-label outside .sea-card-slot — moved label to be a sibling of the slot in the cell, so SeaDeal innerHTML clobber on draw doesn't erase it. Per-position absolute positioning touching slot borders: crown/cover above (translate -50%, 0.1rem, scaleY 1.2); lay/cross below (translate -50%, -0.1rem, scaleY 1.2); leave left, CCW (writing-mode vertical-rl + rotate 180deg + scaleX 1.2); loom right, CW (writing-mode vertical-rl + scaleX 1.2). scaleX for rotated labels (not scaleY) — perpendicular to text-flow is the visible-width direction after rotation. .my-sea-cross gap bumped to 1.75rem for label clearance.
Escape Velocity label swaps — POSITION_LABELS for escape-velocity: {crown: Crown, leave: Lay, cover: Cover, cross: Cross, loom: Loom, lay: Leave}. Replaces the Waite-Smith Behind/Beneath/Before per user spec.
SPREAD dropdown portal — .my-sea-form-col .sea-form-main { overflow: visible } + .sea-select-list { z-index: 1000 } so the dropdown extends past the form-main scroll area + sits above the picker stacking ints. Gameroom .sea-form-main still scrolls (only my-sea opts out).
Major Arcana polarity-split rendering — added 9 missing _card_dict keys to my-sea's _my_sea_deck_data to match gameroom epic.views.sea_deck's contract: levity_emanation, gravity_emanation, levity_reversal, gravity_reversal, italic_word, keywords_upright, keywords_reversed, energies, operations. Without these StageCard.populateCard falls through to plain name_title for trumps 19-21 + cards 48-49. Iter 4b cleanup candidate: extract apps.epic.utils.card_dict() to DRY the now-identical helpers.
Tests deferred — user explicitly belayed FT runs during the bug-fix substage. Iter 4b will re-establish a green sweep before its commit lands.
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 22:02:27 -04:00
|
|
|
{# Extracted to a shared partial so the my-sea picker (Sprint 5 iter 4-bugs) #}
|
|
|
|
|
{# reuses the same DOM contract that SeaDeal binds to. #}
|
|
|
|
|
{% include "apps/gameboard/_partials/_sea_stage.html" %}
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
|
2026-04-26 21:30:27 -04:00
|
|
|
</div>{# /.sea-overlay #}
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
(function () {
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
const overlay = document.getElementById('id_sea_overlay');
|
|
|
|
|
const cancelBtn = document.getElementById('id_sea_cancel');
|
|
|
|
|
|
|
|
|
|
function openSea() {
|
|
|
|
|
document.documentElement.classList.add('sea-open');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeSea() {
|
|
|
|
|
document.documentElement.classList.remove('sea-open');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const pickSeaBtn = document.getElementById('id_pick_sea_btn');
|
|
|
|
|
if (pickSeaBtn) pickSeaBtn.addEventListener('click', openSea);
|
|
|
|
|
cancelBtn.addEventListener('click', closeSea);
|
2026-04-28 23:02:49 -04:00
|
|
|
|
|
|
|
|
// ── Deck draw ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
const SEA_DECK_URL = overlay.dataset.seaDeckUrl;
|
|
|
|
|
|
|
|
|
|
const SPREAD_ORDER = {
|
2026-04-29 14:19:30 -04:00
|
|
|
'waite-smith': ['.sea-pos-cover', '.sea-pos-cross', '.sea-pos-crown', '.sea-pos-lay', '.sea-pos-loom', '.sea-pos-leave'],
|
|
|
|
|
'escape-velocity': ['.sea-pos-cover', '.sea-pos-cross', '.sea-pos-lay', '.sea-pos-leave', '.sea-pos-crown', '.sea-pos-loom'],
|
2026-04-28 23:02:49 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let levityPile = [], gravityPile = [];
|
|
|
|
|
let _filled = 0;
|
|
|
|
|
let _activeStack = null;
|
|
|
|
|
|
|
|
|
|
const spreadSel = overlay.querySelector('#id_sea_spread');
|
|
|
|
|
const lockBtn = overlay.querySelector('#id_sea_lock_hand');
|
|
|
|
|
const delBtn = overlay.querySelector('#id_sea_del');
|
|
|
|
|
|
|
|
|
|
function _spreadKey() {
|
|
|
|
|
return spreadSel ? spreadSel.value : 'waite-smith';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _nextPosSelector() {
|
|
|
|
|
const order = SPREAD_ORDER[_spreadKey()] || SPREAD_ORDER['waite-smith'];
|
|
|
|
|
return order[_filled] || null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _hideOk() {
|
|
|
|
|
if (_activeStack) {
|
|
|
|
|
const ok = _activeStack.querySelector('.sea-stack-ok');
|
|
|
|
|
if (ok) ok.style.display = 'none';
|
2026-04-28 23:30:07 -04:00
|
|
|
_activeStack.classList.remove('sea-deck-stack--active');
|
2026-04-28 23:02:49 -04:00
|
|
|
_activeStack = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _showOk(stack) {
|
|
|
|
|
_hideOk();
|
|
|
|
|
_activeStack = stack;
|
2026-04-28 23:30:07 -04:00
|
|
|
stack.classList.add('sea-deck-stack--active');
|
2026-04-28 23:02:49 -04:00
|
|
|
const ok = stack.querySelector('.sea-stack-ok');
|
|
|
|
|
if (ok) ok.style.display = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _reset() {
|
|
|
|
|
_filled = 0;
|
|
|
|
|
_hideOk();
|
|
|
|
|
overlay.querySelectorAll('.sea-card-slot').forEach(s => {
|
2026-04-29 02:30:59 -04:00
|
|
|
s.classList.remove('sea-card-slot--filled', 'sea-card-slot--visible', 'sea-card-slot--focused', 'sea-card-slot--levity', 'sea-card-slot--gravity');
|
2026-04-28 23:02:49 -04:00
|
|
|
s.classList.add('sea-card-slot--empty');
|
2026-04-28 23:30:07 -04:00
|
|
|
s.innerHTML = '';
|
2026-04-28 23:02:49 -04:00
|
|
|
delete s.dataset.cardId;
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
delete s.dataset.posKey;
|
2026-04-28 23:02:49 -04:00
|
|
|
});
|
|
|
|
|
if (lockBtn) lockBtn.disabled = true;
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
if (window.SeaDeal) SeaDeal.resetHand();
|
2026-04-28 23:02:49 -04:00
|
|
|
_fetchDeck();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _fetchDeck() {
|
|
|
|
|
fetch(SEA_DECK_URL, { credentials: 'same-origin' })
|
|
|
|
|
.then(r => r.json())
|
|
|
|
|
.then(data => { levityPile = data.levity || []; gravityPile = data.gravity || []; })
|
|
|
|
|
.catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
overlay.querySelectorAll('.sea-deck-stack').forEach(stack => {
|
|
|
|
|
stack.addEventListener('click', e => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
_activeStack === stack ? _hideOk() : _showOk(stack);
|
|
|
|
|
});
|
|
|
|
|
const ok = stack.querySelector('.sea-stack-ok');
|
|
|
|
|
if (ok) {
|
|
|
|
|
ok.style.display = 'none';
|
|
|
|
|
ok.addEventListener('click', e => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
const isLevity = stack.classList.contains('sea-deck-stack--levity');
|
|
|
|
|
const pile = isLevity ? levityPile : gravityPile;
|
|
|
|
|
const card = pile.length ? pile.shift() : null;
|
|
|
|
|
const pos = _nextPosSelector();
|
PICK SEA Sprint C: sea stage card viewer — FLIP in, SPIN/FYI, deposit/re-expand — TDD
- sea.js: SeaDeal module — openStage() shows big card viewer w. flip-in animation;
SPIN toggles stage-card--reversed; FYI shows energies/operations (Energy/Operation
titles, PRV/NXT nav); backdrop click deposits card to slot; click deposited slot
re-opens stage; resetHand() clears hand on DEL
- sea_deck view: adds name_group/name_title/reversal/keywords_upright/keywords_reversed/
energies/operations to each card dict (full sig-select stage data set)
- _sea_overlay.html: data-sea-user-polarity attr; sea stage HTML (sig-stage-card shell
+ fan-card-face-upright/reversal structure + sea-stat-block w. SPIN/FYI/PRV/NXT);
FLIP click calls SeaDeal.openStage(); _fillPos removed (sea.js handles slot fill);
_reset calls SeaDeal.resetHand()
- room.html: sea.js included alongside sig-select.js
- _card-deck.scss: sea-stage layout (fixed overlay, backdrop, content row); sea-stage-card
w. @keyframes sea-flip-in (3D rotateY perspective); sea-stat-block scoped styles
incl. SPIN/FYI btns, stat faces, sig-info FYI panel
- SeaDealSpec.js: 20 Jasmine specs — openStage, SPIN, FYI, backdrop dismiss, slot re-expand
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 01:12:06 -04:00
|
|
|
if (card && pos) {
|
|
|
|
|
_filled++;
|
|
|
|
|
if (lockBtn) lockBtn.disabled = (_filled < 6);
|
|
|
|
|
if (window.SeaDeal) SeaDeal.openStage(card, pos, isLevity);
|
|
|
|
|
}
|
2026-04-28 23:02:49 -04:00
|
|
|
_hideOk();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
overlay.addEventListener('click', _hideOk);
|
|
|
|
|
if (delBtn) delBtn.addEventListener('click', _reset);
|
|
|
|
|
|
|
|
|
|
_fetchDeck();
|
2026-04-29 02:30:59 -04:00
|
|
|
if (window.SeaDeal) SeaDeal.reinit();
|
2026-04-26 21:30:27 -04:00
|
|
|
})();
|
|
|
|
|
</script>
|