PICK SEA styling: deck backs, card rank+icon display, fa-hand-dots Major Arcana — TDD

- migration 0010: icon='fa-hand-dots' for all Earthman Major Arcana number >= 2
  (Nomad/Schizo kept empty for distinct icons later)
- sea_deck view: switch from .values() to model instances; serializes corner_rank +
  suit_icon computed properties alongside DB fields
- sea overlay JS: _fillPos() renders <span class=fan-corner-rank> + <i fa-solid> HTML;
  tracks levity/gravity source via sea-card-slot--levity/gravity class; _reset() strips
  polarity classes; _showOk/_hideOk toggle sea-deck-stack--active
- template: gravity deck before levity; OK btn inside .sea-stack-face (absolute center);
  DECKS label (vertical-rl CCW) on stacks left; Gravity/Levity names under each pile
- _card-deck.scss: .sea-stacks-label (vertical-rl); .sea-stack-ok (absolute center on face);
  .sea-stack-name w. --quaUser/--terUser; glow on hover+:active+--active class —
  --ninUser for levity, --quaUser for gravity; sea-sig-card compact rank+icon display
- sea_partial view: ctx['room'] fix carried in from Sprint B

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-04-28 23:30:07 -04:00
parent 132e60864e
commit 6d75b9541f
4 changed files with 190 additions and 47 deletions

View File

@@ -30,17 +30,10 @@
</div>
{# Center — Significator (always placed) + Cover + Cross overlaid #}
<div class="sea-cross-cell sea-pos-center">
<div class="sig-stage-card" style="--sig-card-w: 4rem">
<div class="sig-stage-card sea-sig-card" style="--sig-card-w: 4rem">
{% if my_tray_sig %}
<div class="fan-card-face">
{% if my_tray_sig.arcana == "MAJOR" %}
<p class="fan-card-name">{{ my_tray_sig.name_title }}</p>
<p class="sig-qualifier-below">{% if user_polarity == "levity" %}{{ my_tray_sig.levity_qualifier }}{% else %}{{ my_tray_sig.gravity_qualifier }}{% endif %}</p>
{% else %}
<p class="sig-qualifier-above">{% if user_polarity == "levity" %}{{ my_tray_sig.levity_qualifier }}{% else %}{{ my_tray_sig.gravity_qualifier }}{% endif %}</p>
<p class="fan-card-name">{{ my_tray_sig.name_title }}</p>
{% endif %}
</div>
<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 %}
{% endif %}
</div>
{# Cover — CC/EV pos 1, stacked face-up on Sig #}
@@ -82,13 +75,18 @@
{# Two face-down deck piles — tap to proffer OK #}
<div class="sea-stacks">
<div class="sea-deck-stack sea-deck-stack--levity">
<div class="sea-stack-face"></div>
<button class="btn btn-confirm sea-stack-ok" type="button">OK</button>
</div>
<span class="sea-stacks-label">DECKS</span>
<div class="sea-deck-stack sea-deck-stack--gravity">
<div class="sea-stack-face"></div>
<button class="btn btn-confirm sea-stack-ok" type="button">OK</button>
<div class="sea-stack-face">
<button class="btn btn-confirm sea-stack-ok" type="button">OK</button>
</div>
<span class="sea-stack-name">Gravity</span>
</div>
<div class="sea-deck-stack sea-deck-stack--levity">
<div class="sea-stack-face">
<button class="btn btn-confirm sea-stack-ok" type="button">OK</button>
</div>
<span class="sea-stack-name">Levity</span>
</div>
</div>
</div>
@@ -163,6 +161,7 @@
if (_activeStack) {
const ok = _activeStack.querySelector('.sea-stack-ok');
if (ok) ok.style.display = 'none';
_activeStack.classList.remove('sea-deck-stack--active');
_activeStack = null;
}
}
@@ -170,19 +169,23 @@
function _showOk(stack) {
_hideOk();
_activeStack = stack;
stack.classList.add('sea-deck-stack--active');
const ok = stack.querySelector('.sea-stack-ok');
if (ok) ok.style.display = '';
}
function _fillPos(sel, card) {
function _fillPos(sel, card, isLevity) {
const cell = overlay.querySelector(sel);
if (!cell) return;
const slot = cell.querySelector('.sea-card-slot');
if (!slot) return;
slot.classList.remove('sea-card-slot--empty');
slot.classList.add('sea-card-slot--filled');
slot.classList.add(isLevity ? 'sea-card-slot--levity' : 'sea-card-slot--gravity');
slot.dataset.cardId = String(card.id);
slot.textContent = card.name;
slot.innerHTML =
`<span class="fan-corner-rank">${card.corner_rank}</span>` +
(card.suit_icon ? `<i class="fa-solid ${card.suit_icon}"></i>` : '');
_filled++;
if (lockBtn) lockBtn.disabled = (_filled < 6);
}
@@ -191,9 +194,9 @@
_filled = 0;
_hideOk();
overlay.querySelectorAll('.sea-card-slot').forEach(s => {
s.classList.remove('sea-card-slot--filled');
s.classList.remove('sea-card-slot--filled', 'sea-card-slot--levity', 'sea-card-slot--gravity');
s.classList.add('sea-card-slot--empty');
s.textContent = '';
s.innerHTML = '';
delete s.dataset.cardId;
});
if (lockBtn) lockBtn.disabled = true;
@@ -221,7 +224,7 @@
const pile = isLevity ? levityPile : gravityPile;
const card = pile.length ? pile.shift() : null;
const pos = _nextPosSelector();
if (card && pos) _fillPos(pos, card);
if (card && pos) _fillPos(pos, card, isLevity);
_hideOk();
});
}