diff --git a/src/apps/epic/static/apps/epic/sea.js b/src/apps/epic/static/apps/epic/sea.js index 75e59b4..c38b72c 100644 --- a/src/apps/epic/static/apps/epic/sea.js +++ b/src/apps/epic/static/apps/epic/sea.js @@ -227,6 +227,31 @@ var SeaDeal = (function () { }); } + // Polish-6 — FLIP btn (rotateY 0→90→0 over 500ms, toggle + // `.is-flipped-to-back` at midpoint). Renders unconditionally per + // user-spec "allow the FLIP btn everywhere"; no-ops when the card + // has no `.sig-stage-card-back-img` sibling (back-img only renders + // server-side for non-polarized image-equipped decks; text-mode + + // polarized image decks render no back-img + the FLIP is inert). + // Mirrors `_flipToBackAnimated` shape from my_sign.html / applet. + var flipBtn = stage.querySelector('.sea-stage-flip-btn'); + if (flipBtn) { + flipBtn.addEventListener('click', function () { + if (stageCard.dataset.flipping) return; + if (!stageCard.querySelector('.sig-stage-card-back-img')) return; + stageCard.dataset.flipping = '1'; + stageCard.animate([ + { transform: 'rotateY(0deg)' }, + { transform: 'rotateY(90deg)', offset: 0.5 }, + { transform: 'rotateY(0deg)' }, + ], { duration: 500, easing: 'ease' }); + setTimeout(function () { + stageCard.classList.toggle('is-flipped-to-back'); + }, 250); + setTimeout(function () { delete stageCard.dataset.flipping; }, 500); + }); + } + // Clicking the FYI panel itself dismisses it (same as sig-select caution) if (fyiPanel) { fyiPanel.addEventListener('click', function (e) { diff --git a/src/static_src/scss/_card-deck.scss b/src/static_src/scss/_card-deck.scss index 117b219..3ee411d 100644 --- a/src/static_src/scss/_card-deck.scss +++ b/src/static_src/scss/_card-deck.scss @@ -989,12 +989,15 @@ html:has(.sig-backdrop) { background: rgba(var(--duoUser), 1); } -// Polish-5: my_sign main + applet FLIP btns share one positioning rule. -// Both live INSIDE their card (`.sig-stage-card` / `.my-sign-applet-card`, -// both `position: relative`) so `bottom: 0.6rem; left: 0.6rem` anchors -// universally to card-bottom-left w/o needing surface-specific calc(). +// Polish-5: my_sign main + applet + sea_stage FLIP btns share one positioning +// rule. All live INSIDE their card (`.sig-stage-card` / `.my-sign-applet-card` +// / `.sig-stage-card.sea-stage-card`, all `position: relative`) so `bottom: +// 0.6rem; left: 0.6rem` anchors universally to card-bottom-left w/o needing +// surface-specific calc(). Polish-6 added sea-stage-flip-btn for the sea_stage +// modal per user-spec "allow the FLIP btn everywhere". .my-sign-flip-btn, -.my-sign-applet-flip-btn { +.my-sign-applet-flip-btn, +.sea-stage-flip-btn { @include flip-btn-base; z-index: 25; bottom: 0.6rem; @@ -1012,19 +1015,22 @@ html:has(.sig-backdrop) { .my-sign-stage.sig-stage--frozen .sig-stage-card:hover .my-sign-flip-btn, .my-sign-stage.sig-stage--frozen .sig-stage-card:has(.my-sign-flip-btn:hover) .my-sign-flip-btn, .my-sign-applet-card:hover .my-sign-applet-flip-btn, -.my-sign-applet-card:has(.my-sign-applet-flip-btn:hover) .my-sign-applet-flip-btn { +.my-sign-applet-card:has(.my-sign-applet-flip-btn:hover) .my-sign-applet-flip-btn, +.sea-stage-card:hover .sea-stage-flip-btn, +.sea-stage-card:has(.sea-stage-flip-btn:hover) .sea-stage-flip-btn { @extend %flip-btn-revealed; } -// Unified mid-flip-hide across all 3 surfaces. `[data-flipping]="1"` is set on +// Unified mid-flip-hide across all 4 surfaces. `[data-flipping]="1"` is set on // the card by each surface's FLIP handler for the 500ms rotation duration; // `%flip-btn-mid-flip`'s `display: none` makes the btn vanish INSTANTLY (per // user spec — no ease-out logic competing w. the click). Selector chains // differ per surface because the btn-to-card DOM relationship varies (btn is -// INSIDE the card on my_sign + applet post-polish-5; sibling under .tarot- -// fan-wrap for the fan). +// INSIDE the card on my_sign + applet + sea_stage; sibling under .tarot-fan- +// wrap for the fan carousel). .sig-stage-card[data-flipping] .my-sign-flip-btn, .my-sign-applet-card[data-flipping] .my-sign-applet-flip-btn, +.sea-stage-card[data-flipping] .sea-stage-flip-btn, .tarot-fan-wrap:has(.fan-card[data-flipping]) .fan-flip-btn { @extend %flip-btn-mid-flip; } diff --git a/src/templates/apps/billboard/_partials/_applet-my-sign.html b/src/templates/apps/billboard/_partials/_applet-my-sign.html index d7f98ad..b978104 100644 --- a/src/templates/apps/billboard/_partials/_applet-my-sign.html +++ b/src/templates/apps/billboard/_partials/_applet-my-sign.html @@ -18,23 +18,26 @@ data-card-id="{{ card.id }}" data-arcana-key="{{ card.arcana }}"> {% if card.deck_variant.has_card_images %} - {# Sprint A.6 — image-mode render mirrors my_sign.html's #} - {# .sig-stage-card--image treatment. Shares the SCSS rule #} - {# (comma-list selector) so the contour stroke + tray-card #} - {# silhouette black depth shadow + arcana stroke-color #} - {# come for free. #} + {% comment %} + Sprint A.6 — image-mode render mirrors my_sign.html's + .sig-stage-card--image treatment. Shares the SCSS rule + (comma-list selector) so the contour stroke + tray-card + silhouette black depth shadow + arcana stroke-color + come for free. + {% endcomment %} {{ card.name }} {% if not card.deck_variant.is_polarized %} - {# Non-polarized image deck: FLIP btn shows the deck back #} - {# image (same behavior as my_sign.html main page). Both #} - {# the back-img + flip-btn nest INSIDE the card so the #} - {# absolute-positioned FLIP btn anchors to the card's #} - {# bounds (card is position: relative in --image mode). #} + {% comment %} + Non-polarized image deck: back-img element renders the + deck-back PNG that FLIP toggles to. Back-img stays gated + (back-img is meaningless for polarized decks or text-mode); + the FLIP btn itself moved OUT of this gate in polish-6 so + it renders for every card (per user-spec "allow the FLIP + btn everywhere"). The JS click handler is a no-op when no + back-img sibling exists. + {% endcomment %} - {% endif %} {% else %}
@@ -42,15 +45,17 @@ {% if card.suit_icon %}{% endif %}
- {# `request.user.sig_face` is the rendering payload from #} - {# `TarotCard.applet_face()` — mirrors `populateCard` in #} - {# `stage-card.js:135-144`: #} - {# • Polarity-split (cards 48-49, trumps 19-21): #} - {# single-line title, qualifier blank. #} - {# • Major + qualifier: title carries a trailing #} - {# comma + qualifier renders BELOW. #} - {# • Non-Major (middle court, Schizo / Nomad w. no #} - {# qualifier): qualifier renders ABOVE the title. #} + {% comment %} + `request.user.sig_face` is the rendering payload from + `TarotCard.applet_face()` — mirrors `populateCard` in + `stage-card.js:135-144`: + • Polarity-split (cards 48-49, trumps 19-21): + single-line title, qualifier blank. + • Major + qualifier: title carries a trailing + comma + qualifier renders BELOW. + • Non-Major (middle court, Schizo / Nomad w. no + qualifier): qualifier renders ABOVE the title. + {% endcomment %} {% with face=request.user.sig_face %} {% if face.qualifier_first %}

{{ face.qualifier }}

@@ -67,6 +72,16 @@ {% if card.suit_icon %}{% endif %}
{% endif %} + {% comment %} + Polish-6 — FLIP btn rendered UNCONDITIONALLY (was gated on + `has_card_images and not is_polarized`). Per user-spec + 2026-05-25 PM "just allow the FLIP btn everywhere". JS + handler below picks behavior by sibling existence: back-img + present → flip-to-back animation; absent → no-op. + {% endcomment %} + {# Stat block — same shape as my_sign.html's `.sig-stat-block` #} {# (Emanation face label + keyword list) but no SPIN/FYI btns #} @@ -82,25 +97,28 @@ {% endcomment %} {% include "core/_partials/_stat_face.html" with face_modifier="upright" label_text="Emanation" card=card %} - {# Sprint A.6 — applet FLIP btn handler. Mirrors my_sign.html's #} - {# `_flipToBackAnimated()` shape (rotateY 0→90→0 over 500ms, class #} - {# toggle at halfway, `data-flipping` attr for SCSS to hide the #} - {# btn). Self-contained inline script — no shared module needed #} - {# since the applet is the only consumer outside the main page #} - {# (which has its own copy). Script wrapped inside the sig-present #} - {# branch AND inside `{% with card %}` scope so `card` references #} - {# resolve + the JS selector strings don't leak into the no-sig #} - {# DOM (which would trip substring-matching tests). #} - {% if card.deck_variant.has_card_images and not card.deck_variant.is_polarized %} + {% comment %} + Polish-6 — applet FLIP btn handler. Mirrors my_sign.html's + `_flipToBackAnimated()` (rotateY 0→90→0 over 500ms, class + toggle at halfway, `data-flipping` attr for SCSS to hide the + btn during animation). Script is now UNGATED (was conditional + on `has_card_images and not is_polarized` like the FLIP btn + itself); per user-spec "allow the FLIP btn everywhere", the + handler always wires + gracefully no-ops when there's no + `.sig-stage-card-back-img` sibling to toggle. + {% endcomment %} - {% endif %} {% endwith %} {% else %}

No sign chosen yet.

diff --git a/src/templates/apps/gameboard/_partials/_sea_stage.html b/src/templates/apps/gameboard/_partials/_sea_stage.html index 56b3093..001ac69 100644 --- a/src/templates/apps/gameboard/_partials/_sea_stage.html +++ b/src/templates/apps/gameboard/_partials/_sea_stage.html @@ -41,6 +41,24 @@ + {% comment %} + Polish-6 — back-img + FLIP btn. The back-img mirrors the + my_sign.html / _applet-my-sign.html pattern: only renders + for non-polarized image-equipped decks (since back-img is + meaningless otherwise; src derives from user's equipped + deck). FLIP btn renders UNCONDITIONALLY per user-spec "allow + the FLIP btn everywhere"; sea.js's click handler no-ops + when no back-img sibling exists. Multi-user gameroom is a + known limitation here — the back-img src is the room + viewer's deck-back, not the drawing gamer's, which is wrong + when different gamers' decks have different backs. Parked + for a future multi-user polish pass. + {% endcomment %} + {% if request.user.is_authenticated and request.user.equipped_deck.has_card_images and not request.user.equipped_deck.is_polarized %} + + {% endif %} +