feat: My Sign saved-sig state — --duoUser bg, centred card+stat-block, stage card auto-rotates for reversed sigs on landing. Three follow-up polish items atop the f609313 read-only-saved-sig batch.
(1) **`--duoUser` bg on the saved-sig aperture.** Per user spec — once the table hex is server-side gone (f609313's `{% if not current_significator %}` wrap), the now-mostly-empty olive aperture reads as a distinct mode vs the default landing (--priUser bg w. hex). New `.my-sign-page[data-current-card-id] { background-color: rgba(var(--duoUser), 1); }` block in `_card-deck.scss:644-696`. Keyed on `data-current-card-id` (present only when `current_significator` is set per `my_sign.html:20`) rather than the absence of `[data-phase="landing"]` — picker also lacks the hex but should keep --priUser. Mirrors how `.my-sea-page[data-phase="picker"]` swaps bg in `_gameboard.scss`.
(2) **Stage card + stat block centre in the aperture.** Default landing left-anchored the stage natural-sized at the top of the column (above the hex which filled the rest); w. the hex gone there's a wide empty page bottom. `.my-sign-page[data-current-card-id] .my-sign-stage` overrides to `flex: 1; justify-content: center; align-items: center; padding-left: 0;` — stage grows to fill, card+stat-block centre as a unit. `.my-sign-landing` collapses to `flex: 0 0 auto` + `position: static`; DEL is `position: absolute` so it walks up to `.my-sign-page` (already `position: relative`) + pins to the page corner. **2 traps caught mid-build** in the centring pass: (a) `.sig-stat-block`'s default `align-self: flex-end` (`_card-deck.scss:599`) overrode the parent's `align-items: center` on the cross axis, so the stat block floated to the bottom of the stage while the card sat at vertical-centre — forced `align-self: center` on this state. (b) `.my-sign-flip-btn`'s `left: calc(1.5rem + 0.4rem)` (`_card-deck.scss:747`) assumed the card sat flush against `.sig-stage`'s padded-left edge — true on the picker but wrong w. `justify-content: center`, FLIP landed at the stage's left edge w. the card centred ~3rem to the right of it. Re-derived left/bottom from the centred geometry: card's left edge in stage = `(100% - 2 * sig-card-w - 0.75rem) / 2` (the centred card+gap+stat group's left), card's bottom edge = `50% - sig-card-w * 0.8` from stage bottom (cardHeight = sig-card-w × 8/5 = × 1.6, half = × 0.8). `+ 0.4rem` on each lands FLIP just inside the card's bottom-left corner, same offset as the picker-side intent.
(3) **Stage card auto-rotates 180° on landing for saved-reversed sigs.** Server-side `data-polarity` attribute on `.my-sign-page` already reflected `significator_reversed` correctly (drives the polarity-themed color rules at `_card-deck.scss:917-1042` for levity/gravity ink) but the visual 180° rotation lives in the `stage-card--reversed` class which was only JS-applied via `_toggleOrientation()` (SPIN btn handler). On init w. a saved sig, `_populateStage(savedCardEl)` filled the card's data but didn't touch rotation — so saved-reversed sigs rendered upright on landing while the My Sign applet (template-driven, reads `request.user.significator_reversed` directly + conditionally adds `stage-card--reversed` per `_applet-my-sign.html:9`) correctly rotated them. Two surfaces disagreed → user read the applet as inverted ("non-reversed sig displays upside-down in the applet"). Actually the my_sign.html stage was the liar; the applet was right. Fixed at `my_sign.html:404-406` — after `_populateStage(savedCardEl) + stage.classList.add('sig-stage--frozen')`, if `revInput.value === '1'` (= saved reversed=True) call `_toggleOrientation()` once. That helper covers all three coordinated state mutations: `stageCard.classList.toggle('stage-card--reversed', on)` (visual 180° rotation), `statBlock.classList.toggle('is-reversed', on)` (swaps to reversal face per `_card-deck.scss:62-65`), `spinBtn.classList.toggle('is-reversed', on)` (visual indicator). Both surfaces now agree. Per user direction, a follow-up will lock my_sign.html SAVE to always write `reversed=False` (Tarot-tradition convention) — but the underlying rotation pipeline still has to work for room-side sig-select where reversed sigs are needed.
**TDD coverage**: no new tests — `test_landing_previews_saved_sig_on_stage` (updated in f609313) still passes as written (its assertions are around the frozen-stage + stat-block-visible + hex-absent contract, all of which hold under the centring + rotation patches). The reversed-sig-auto-rotate case is light-weight enough (one branch w. a well-known helper) to not need a dedicated FT; if it regresses, the existing room-side `_toggleOrientation` coverage in the gameboard FTs catches the helper itself + manual verify caught it here. Manual verify done on /billboard/my-sign/ w. `disco`'s saved Jack of Brands (reversed=False, renders upright + centred w. --duoUser bg + FLIP on card's bottom-left + DEL on page's bottom-right). 1211 IT/UT still green; one minor visual to chase before locking my_sign.html to non-reversed-only — verifying that a reversed-saved sig renders rotated on return (DB has none currently, will test after the follow-up).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -636,6 +636,65 @@ html:has(.sig-backdrop) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// Saved-sig read-only state — page bg shifts to --duoUser so the now-
|
||||
// hexless aperture reads as a distinct mode (mirrors how `.my-sea-page
|
||||
// [data-phase="picker"]` swaps bg in `_gameboard.scss`). Keyed on the
|
||||
// presence of `data-current-card-id` since that attribute renders only
|
||||
// when the user has a saved significator. Stage card + stat block also
|
||||
// center in the now-empty page aperture (default landing keeps stage
|
||||
// natural-sized at the top above the hex; here there's no hex so the
|
||||
// stage gets to grow + middle itself).
|
||||
.my-sign-page[data-current-card-id] {
|
||||
background-color: rgba(var(--duoUser), 1);
|
||||
|
||||
// Stage grows to fill the available column space + centres its card
|
||||
// row both horizontally + vertically. Override `.sig-stage`'s default
|
||||
// `align-items: flex-end` + `padding-left: 1.5rem` so card + stat
|
||||
// block land truly centred.
|
||||
.my-sign-stage {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
// `.sig-stat-block`'s default `align-self: flex-end` (line 599)
|
||||
// overrides the parent's `align-items: center` on the cross axis,
|
||||
// so the stat block was floating to the bottom of the stage while
|
||||
// the card sat at vertical-centre. Force `center` here to keep the
|
||||
// pair aligned in the centred row.
|
||||
.sig-stat-block { align-self: center; }
|
||||
|
||||
// FLIP was positioned via `left: calc(1.5rem + 0.4rem)` (default
|
||||
// rule below) assuming the card sat flush against the stage's
|
||||
// padded-left edge — true on the picker's left-anchored layout but
|
||||
// wrong here w. `justify-content: center` (the card moves to
|
||||
// wherever the group's left edge lands).
|
||||
// Re-derive FLIP's offsets from the centred geometry:
|
||||
// group width = card + gap + stat = 2 * --sig-card-w + 0.75rem
|
||||
// card's left edge (in stage) = (100% - group width) / 2
|
||||
// card's bottom edge (in stage) = 50% - (cardHeight / 2)
|
||||
// = 50% - --sig-card-w * 0.8
|
||||
// (cardHeight = w × 8/5 = w × 1.6)
|
||||
// The +0.4rem on each lands FLIP just inside the card's bottom-left
|
||||
// corner, matching the picker-side positioning intent.
|
||||
.my-sign-flip-btn {
|
||||
left: calc((100% - 2 * var(--sig-card-w) - 0.75rem) / 2 + 0.4rem);
|
||||
bottom: calc(50% - var(--sig-card-w) * 0.8 + 0.4rem);
|
||||
}
|
||||
|
||||
// Landing collapses since the hex is server-side gone — just DEL is
|
||||
// left + that's `position: absolute`. `position: static` here drops
|
||||
// landing's positioning context so DEL walks up to `.my-sign-page`
|
||||
// (already `position: relative`) + pins to the page corner.
|
||||
.my-sign-landing {
|
||||
flex: 0 0 auto;
|
||||
min-height: 0;
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
// Stage frame — fixed slice in picker phase, natural-sized on landing.
|
||||
// The picker min-height reserves real estate so hover-preview cards don't
|
||||
// shift adjacent layout; on landing the stage shrinks to its actual content
|
||||
|
||||
@@ -384,6 +384,17 @@
|
||||
// hidden, DEL is the only action). The picker grid stays hidden
|
||||
// until SCAN SIGN — but SCAN SIGN itself is gone in this state, so
|
||||
// the user must DEL → reload to ever re-enter picker phase.
|
||||
//
|
||||
// If the saved sig is reversed, also call _toggleOrientation() once
|
||||
// so the stage card visually rotates 180° + the stat block swaps to
|
||||
// its reversal face. The server-side `data-polarity` attribute on
|
||||
// .my-sign-page already reflects the reversed flag (drives polarity-
|
||||
// themed colors via the [data-polarity=...] CSS rules) but the
|
||||
// visual rotation lives in the `stage-card--reversed` class which
|
||||
// is JS-applied. Without this call the stage card lied: saved-
|
||||
// reversed sigs rendered upright on landing while the My Sign
|
||||
// applet (template-driven, reads significator_reversed directly)
|
||||
// correctly rotated them — surfaces disagreed.
|
||||
var savedId = pageEl.dataset.currentCardId;
|
||||
if (savedId && grid) {
|
||||
var savedCardEl = grid.querySelector(
|
||||
@@ -391,6 +402,9 @@
|
||||
if (savedCardEl) {
|
||||
_populateStage(savedCardEl);
|
||||
stage.classList.add('sig-stage--frozen');
|
||||
if (revInput.value === '1') {
|
||||
_toggleOrientation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user