Commit Graph

32 Commits

Author SHA1 Message Date
Disco DeDisco
cde556b178 SEED MAP felt: 2D d3-delaunay Voronoi/Delaunay dual graph (roadmap step 21, Step 1) — TDD
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful
New inline --duoUser felt (sibling of CAST SKY / DRAW SEA) that opens on
#id_seed_map_btn once the 6-card sea hand completes (hand_complete); paints a
Voronoi cell layer (territory) + a Delaunay edge layer (adjacency) from
PLACEHOLDER seeds. Card-driven seeding (the 6 Celtic-Cross cards) is Step 2.

- voronoi-map.js: window.SeedMap.draw/drawPlaceholder/clear over the bundled
  d3.min.js (d3-delaunay ships in the v7.9.0 UMD bundle — no new dep); a
  ResizeObserver re-tessellates to fill the felt on resize; data-seed reads d3's
  ring.index (survives skipped/degenerate cells in Step 2)
- _seed_map_overlay.html felt + room.html include + has-seed-stage (gated on hand_complete)
- three-way felt close (T3): openSeed closes sky+sea; openSky/openSea reciprocally close seed
- .room-menu-seed gear NVM pane + room-views.js seed-open branch
- _sky.scss felt block (T1: no aperture-fill light; T2: chained selector; fills the pane) + _room.scss aperture pin
- VoronoiMapSpec Jasmine + game_room_seed_map FT + 5 felt ITs; CarteTray NVM count 2->3

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:02:21 -04:00
Disco DeDisco
011e4b2d5a sea affinity prose reversals + reelhouse sea/sky reel-up swipe machine + SCROLL gear OK close — TDD
Three threads:

1. Reversal-aware SEA_DRAWN prose. The affinity clause now reads "the {orientation}
   of the {card}" — "emanation" upright, "reversal" when the drawn card is reversed
   — and a reversed card's abbrev gains a ℞ (U+211E, the sky wheel's retrograde
   glyph) <sub> subscript inside the existing no-wrap span, e.g. "(Q ♥℞)".
   _sea_affinity_for now carries the per-card `reversed` flag (it was in the hand
   entry but discarded) → event.data → to_prose via d.get("reversed", False).
   _card_abbrev gains reversed_flag=False so the shared SIG_READY caller is
   untouched (significators are picked, never drawn reversed). TDD: drama ITs +2
   (reversal orientation + ℞; upright emanation w.o ℞), epic IT +1 (reversed flows
   end-to-end), existing SEA_DRAWN prose tests updated to "emanation of the".

2. Reelhouse sea/sky reel-up swipe machine. The burger Sea/Sky reopen btns are
   .active from anywhere in the reelhouse, but opening a felt while parked on the
   reelhouse pane pinned the aperture there (html.*-open freezes the scroll) with
   the felt stranded off-screen in the hex pane (scroll-locked; NVM was the only
   escape). New RoomViews.scrollToHex(cb): from the reelhouse it reels the aperture
   UP to the ROOM hex, then fires the open cb on settle (the banner h2 reel follows
   via the existing IO); already at the hex → synchronous (in-hex CAST SKY / DRAW
   SEA unchanged). afterDescent (scrollend + 700ms ceiling) hoisted out of the Text
   sub-btn machine + shared. Both sub-btn handlers (burger-btn.js sky, _sea_overlay
   sea) route through it. Pinned in Jasmine (RoomViewsSpec +2) per the headless-
   drops-delayed-scroll trap — the delayed-vs-synchronous DECISION, not real scroll.

3. SCROLL gear OK now closes the menu. room-scroll.js's filter-submit handler
   referenced an UNDECLARED `roomMenu` → ReferenceError thrown AFTER applyFilter
   ran, so the filter applied but the menu never closed. Look it up via
   getElementById (mirrors the ATLAS OK / applets close). FT +1.

506 Jasmine specs + 8 room-scroll FTs + 61 drama + 649 epic/billboard ITs green.

[[project-sea-select-scroll-provenance]] [[feedback-headless-delayed-scroll-dropped]]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 01:27:03 -04:00
Disco DeDisco
d50645b216 sea deck stack: rename the stale .sea-stack-ok class to .sea-stack-flip (the btn renders FLIP now) — TDD
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful
Pure rename, no behavior change — the deck-stack reveal btn kept the name `.sea-stack-ok` from when it said OK; it has rendered FLIP for a while. Renamed across all 8 references: _card-deck.scss (reveal + pointer-events rules) + a _gameboard.scss comment; the 3 templates that emit or query it (_sea_overlay.html, _my_sea_deck_stack.html, my_sea.html inline JS); and the 3 tests that select it (test_game_room_select_sea FT, test_game_my_sea FT, test_sea_visit IT rendered-HTML asserts). Verified 47 green across all three surfaces (visit IT + gameroom PickSeaDeal stack FT + my_sea CardDraw FT).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 13:04:51 -04:00
Disco DeDisco
1a83c5f01c sea stage: gate the FLIP-back 0.3 polarity tint to the cloned dubbodeck (Sea Select); my_sea/visit monodeck backs render un-tinted
- the `.sea-stage--gravity/levity .sea-stage-card.is-flipped-to-back::after` 0.3 fill differentiates the two halves of a monodeck CLONED into a two-toned dubbodeck — only meaningful in Sea Select. my_sea / my_sea_visit draw a single monodeck that is never cloned, so the tint was just noise on their FLIP'd card-back
- the shared `_sea_stage.html` now takes a `sea_stage_dubbo` flag; only `_sea_overlay.html` (the gameroom Sea Select felt) passes it → `.sea-stage--dubbo` on #id_sea_stage. The two tint rules gate on `.sea-stage--dubbo.sea-stage--gravity/levity`; non-dubbo stages fall through to the empty, fill-less base `::after` (no visible overlay)

[[project-deck-segment-model]]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 11:29:04 -04:00
Disco DeDisco
945d110171 Sea Select: keep the sky btn lit on a revisited felt (+ clean felt-swap); slow the glow-handoff ease-out — TDD
Symmetry (user-spec): Sky Select leaves the burger sea btn lit while the sky
felt is up; Sea Select now mirrors it — openSea disables ONLY id_text_btn, no
longer id_sky_btn, so a revisited Sea Select keeps the completed sky one click
away. An adversarial pass flagged that the two felts are equal-z (z-index:5)
siblings and neither open path drops the other's open-class, so leaving the sky
btn lit and clicking it from inside the sea felt would DOUBLE-OPEN (sea paints
over sky → confusing no-op) — the same latent stack already reachable in the
sky->sea direction on a both-complete reload. Fixed in BOTH directions: openSea
now closes the sky felt first + openSky closes the sea felt first (each exposes
its close on window: closeSeaFelt / closeSkyFelt), so clicking the other phase's
lit btn performs a clean SWAP. The text-btn disable/restore chain stays correct
across the swap (closeSky restores text, openSea re-disables + recaches it).

Glow: the glow-handoff pulse ease-OUT now runs ~2.8s (was ~1.4s) — moved the
cycle to 3.2s with the bright peak at 12.5%, keeping the ease-IN swell at ~0.4s
(user asked for a longer linger).

[[feedback-felt-aperture-fill-covers-felt]] [[feedback-inline-partial-script-defer-for-later-partial]]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:57:24 -04:00
Disco DeDisco
82f4af9bcc Sea Select glow: defer sea-btn reopen + glow handoff past parse time; pulse the glow-handoff — TDD
room.html includes _sea_overlay.html (~L77) BEFORE _burger.html (~L167, which
holds #id_burger_btn + #id_sea_btn), so the overlay's two inline scripts
captured those btns at parse time → null → both bindings silently no-op'd:
the sea btn (active post-completion) did nothing on click + the burger stayed
stuck --priId because its glow-handoff transfer listener never bound. Defer both
the sea-btn REOPEN binding (_bindSeaReopen) & the burger→sea_btn→.sea-select
GLOW chain (_bindGlowHandoff) to DOMContentLoaded so the burger fan exists first.

Also make the glow-handoff halo PULSE (quick ease-in swell, slow ease-out decay
via per-segment timing fns + a lopsided 22/78 keystop split) instead of a flat
glow — the burger, then the sea btn after the handoff click, keep cueing
"click me to reopen your sea".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:39:38 -04:00
Disco DeDisco
0a6bfcf6cc Sea Select options: fixed-width chunks + 2-line spread names (add Rider-) — TDD
- The option chunks' width tracked the select label length (jumped between the
  two spreads). Fix the `.sea-form-col` to 19rem (a touch wider) so all three
  chunks share a standard width.
- Split the spread label onto two lines around the comma + add the "Rider-"
  prefix now there's room: "Celtic Cross," / "Rider-Waite-Smith" and "Celtic
  Cross," / "Escape Velocity". combobox.js now writes the current label via
  `innerHTML` (not textContent) so the `<br>` survives a selection — plain-text
  options (sky / my_sea) are unaffected (their innerHTML == textContent).
- ITs updated for the new gameroom labels (my_sea's single-line names untouched).
  953 epic+gameboard ITs + Jasmine green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:25:11 -04:00
Disco DeDisco
2bf439eab5 Sea Select options: disabled-btn contrast + AUTO DRAW scrolls back to the cross
- A disabled OK/DEL × inside a --priUser option chunk blended into it (the global
  `.btn-disabled` bg is also --priUser → no visible circle). Drop the disabled
  btns in `.sea-options-col` to the felt --duoUser so they read as a distinct
  disabled circle, like the deck-stack FLIP ×.
- AUTO DRAW now eases the felt back UP to the cross even when the user already
  OK'd the spread + scrolled DOWN to the options page — so he watches the cards
  land one-by-one. `_chooseSpread(slideIn)`: the OK reveal pins to the options
  (slide-in from above); AUTO DRAW (already chosen) skips the pin + just eases up
  to the cross. `_scrollToCross` now eases from the current scroll position.
- 12 PickSeaUnifiedFeltTest render ITs green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:14:25 -04:00
Disco DeDisco
1fe257a7a9 Sea Select options: OK beside the select + --priUser chunk rects — TDD
Restyle the spread-options page (post the scroll-snap refactor):
- OK `.btn-confirm` moves UP beside the `.sea-select` combobox (a new
  `.sea-select-row`), off the AUTO DRAW / DEL action row.
- OK gains `.btn-disabled` + × the moment the first card is drawn — inverse to
  DEL (which loses them then), simultaneous with the combobox locking. So
  `_chooseSpread` (OK) no longer locks; the lock + both btn states flip together
  at the first draw via `_setHasDrawn` + `_lockSpread`. Server-renders OK
  disabled/× when `saved_by_position`.
- The three chunks (spread/select/OK, the mini preview, AUTO DRAW/DEL) each get
  the same --priUser rounded rectangle as the GAME POST lines / composer
  (`_base.scss` `.form-control`): --priUser fill + half-alpha --secUser border +
  rounded + padding. The `.sea-form-col`/`-main` go transparent flex columns so
  the felt shows between the chunks.
- IT: OK enabled / DEL disabled when fresh; flips once a card is drawn.
  953 epic+gameboard ITs green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:02:14 -04:00
Disco DeDisco
edc9a49f06 Sea Select: refactor to scroll-snap options→cross (mirror Sky Select), drop the modal — TDD
The Gaussian spread modal couldn't hang off the burger #id_sea_btn anymore (that
button now also opens the felt). Mirror Sky Select's form→wheel scroll-snap
instead: the felt starts with the spread OPTIONS on the --duoUser felt; clicking
OK confirms the spread → the options shunt DOWN and the spread CROSS takes page 1
(scroll down to find the options again). No modal, no corner NVM.

- `_sea_overlay.html` restructured into `.sea-options-col` (the .sea-select
  combobox + mini preview + OK .btn-confirm + AUTO DRAW + DEL — NO deck stacks)
  and `.sea-cross-col` (the real .my-sea-cross + the Gravity/Levity deck stacks +
  the portaled stage). `#id_sea_overlay` is a `display:contents` passthrough so
  the two cols are the scroll-snap sections.
- OK (`#id_sea_confirm_spread`) → `_chooseSpread()`: adds `sea-spread-chosen` to
  the felt → SCSS engages `scroll-snap-type:y mandatory`, the cross-col gets
  `order:-1` (page 1), options shunt to page 2; locks the combobox; eases the
  scroller to the cross. AUTO DRAW also confirms first. A reload of an in-progress
  sea renders `sea-spread-chosen` (cross revealed) server-side.
- SCSS (`_sky.scss`): the sea felt is now a column scroller; `.sea-cross-col`
  `display:none` pre-confirm; the `sea-spread-chosen` scroll-snap block mirrors
  `body.sky-saved`. The options `.sea-form-col` goes transparent/content-sized
  (blends onto the felt, not the modal's --priUser card).
- Sea sub-btn: no longer activated by openSea; it's the POST-COMPLETION reopen
  affordance (cascade activates it + `sea_btn_active = hand_complete` ctx flag),
  an active click → `window.openSeaFelt()` (review the saved spread), like the
  sky btn. Removed the sea_btn open-modal IIFE + the corner NVM.
- IT: options-on-felt (combobox + OK + AUTO DRAW + DEL + preview) w. NO modal /
  NVM. 952 epic+gameboard ITs + Jasmine + PickSeaAsyncTransitionTest(3) green.

my_sea.html keeps its modal (untouched) — the gameroom intentionally diverges.

[[project-character-creation-spec]]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:53:43 -04:00
Disco DeDisco
cf84fdc992 Sea Select: post-completion cascade — felt eases out → DRAW SEA → SEED MAP — TDD
Mirror CAST SKY's post-save cascade for the sea phase. When the 6-card spread
completes (live FLIP of the 6th card / AUTO DRAW finishing): linger ~3s on the
felt → the felt eases OUT (`.sea-page--cascade-out`, revealing the table-hex) →
DRAW SEA gives way to SEED MAP + the sea glow fires on the burger (handoff →
sea_btn) → +3s → SEED MAP eases IN. Same shape as CAST SKY → sky-btn glow →
DRAW SEA.

- `_room_hex_center.html`: SEED MAP joins the hex-phase-stack; DRAW SEA goes
  --out once `hand_complete`, SEED MAP --out until then (a reload of a complete
  sea lands on SEED MAP server-side = the cascade's end-state). SEED MAP → the
  Voronoi map (roadmap step 21) is a stub — it only needs to APPEAR here
- `_sea_overlay.html`: `_setComplete(on, live)` runs `_startSeaCascade()` on the
  LIVE completion (FLIP / AUTO DRAW pass `live=true`; init does not, so a reload
  doesn't re-animate). The completion-glow IIFE no longer self-starts on the
  data-state transition — the cascade adds `glow-handoff` to the burger; the IIFE
  keeps only the burger → sea_btn → .sea-select handoff
- `.sea-page--cascade-out` SCSS (mirrors `.sky-page--cascade-out`)
- ITs: SEED MAP --out pre-completion (DRAW SEA in); SEED MAP in + DRAW SEA --out
  when hand_complete. 952 epic+gameboard ITs + PickSeaAsyncTransitionTest(3) green

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:03:17 -04:00
Disco DeDisco
0f57cae50d Sea Select: spread center significator supplies the card-face image — TDD
The felt cross's CENTER significator was hardcoded to the corner-rank + suit-icon
text thumbnail, so an image deck (RWS / Minchiate) showed a bare "0"+icon card
instead of its face. Mirror my_sea: render the `.sig-stage-card-img` (image mode)
when the sig's deck has card images, else fall through to the text thumbnail. The
tray sig stays the simple thumbnail (user-spec). The sig's `deck_variant` is the
card's OWN deck — the Sig Select pick is drawn from the Role Select contributed
deck (`_room_deck_variant`), so this is the correct image source (no equipped_deck
bug, unlike the earlier FLIP back-img).

- IT: an image-deck significator renders the center `sea-sig-card sig-stage-card--image`
- 949+ epic ITs green

; FLIP tint tweak (parallel edit): flipped-back overlay alpha 0.6 → 0.3

[[project-deck-segment-model]]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:47:54 -04:00
Disco DeDisco
d09dca56c0 Sky/Sea Select: ?seat-aware so a CARTE owner drives all 6 seats — TDD
A Carte Blanche gamer owns all 6 seats but could only cast sky / draw sea for ONE:
the sky/sea state IS per-seat (Character.seat), but the code resolved to the fixed
canonical (PC-first) seat, ignoring the ?seat switched to via the GATE VIEW pos-
circles — so the tray switched but the sky wheel / sea spread stuck on the
canonical seat + saves wrote back to it. Mirrors Sig Select's existing ?seat path.

- generalize `_acting_sig_seat` → `_acting_seat` (logic is sig-agnostic; 3 callers)
- `_role_select_context` SKY_SELECT branch keys off `selected_seat` (the ?seat-aware
  seat, already computed) instead of `_canonical_seat`: user_polarity,
  confirmed_char, user_seat_role, my_tray_sig, saved_by_position, saved_sea_spread,
  sea_default_spread, hand_complete, sea_back_image_url
- sky_save / sky_delete / sea_save / sea_delete / sea_deck resolve the acting seat
  via `_acting_seat(…, request.GET.get("seat"))`; sea_partial threads seat_param
- the sky + sea felts carry `?seat={{ current_slot }}` on their save/delete/deck/
  sea_partial action URLs so the POSTs target the switched-to seat
- single-seat flow unchanged (no ?seat → canonical fallback)
- ITs: CARTE owner — ?seat switches the displayed spread; sea_save/sky_save target
  the switched seat leaving the canonical seat's Character intact; felt URLs carry
  ?seat. 949 epic+gameboard ITs green.

; FLIP tint tweak (parallel edit): drop the polarity border, bump the flipped-back
overlay --quiUser/--terUser to 0.6 alpha

[[project-sig-select-seat-switch-open-problems]] [[project-deck-segment-model]]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:44:06 -04:00
Disco DeDisco
ab00774a49 Sea Select: drawn-slot reopen after refresh + FLIP polarity-tinted back — TDD
- fix: drawn spread slots silently no-op'd on click AFTER a refresh — SeaDeal's
  in-memory `_seaHand` is only populated by openStage/register during the live
  session, so a reload left it empty + the overlay click handler short-circuited
  (`if (!_seaHand[pos]) return`). `_sea_overlay.html` now re-seeds `_seaHand`
  from the server-rendered saved slots once the deck fetch resolves (cards
  looked up by `data-card-id`; reversed/polarity DOM-sourced) — the same fix
  my_sea already carries
- FLIP card-back: the sea stage now renders the deck back-img for ANY image-
  equipped deck w. a back image (dropped the `not is_polarized` gate — it
  omitted the back for the room's polarized Gravity/Levity draw, so FLIP
  no-op'd). The back-art is identical across polarities, so `_card-deck.scss`
  tints the FLIPped card by the drawn polarity: gravity --quiUser fill @ 0.3 +
  --quaUser border; levity --terUser fill @ 0.3 + --ninUser border (scoped to
  `.sea-stage--*`, so my_sign / applet stages are untouched)
- IT: an image-deck sea stage renders `.sig-stage-card-back-img`

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 21:53:35 -04:00
Disco DeDisco
c037e876e2 Sea Select: rebuild as a felt + Gaussian spread modal, unify w. my_sea — TDD
Hollow out the gameroom DRAW SEA dark modal into the two surfaces my_sea
uses: a --duoUser felt (.sea-page--room) filling the hex pane where the
Celtic Cross deals, + a Gaussian spread modal (#id_sea_spread_modal: the
.sea-select combobox w. the two Celtic Cross 6-card opts ONLY, AUTO DRAW/DEL,
a mini preview, + a corner #id_sea_cancel NVM). Opened by DRAW SEA
(html.sea-open); the room gear's NVM (room-menu-sea) returns to the hex;
#id_text_btn + #id_sky_btn go inert while the felt is open.

- persist: epic:sea_save / sea_delete upsert the seat's Character.celtic_cross
  (none of my_sea's MySeaDraw quota/Brief machinery); room ctx adds
  saved_by_position + saved_sea_spread + sea_default_spread + hand_complete so
  a reload re-renders the filled cross. celtic_cross field already existed (no
  migration)
- mini spread preview (_sea_spread_preview.html) in BOTH the gameroom + my_sea
  modals — shape only, NEVER dealt to: SeaDeal scopes its slot queries to
  .sea-cross:not(.sea-cross--preview)
- always TWO deck stacks (Gravity + Levity) in the room Sea Select — the gamer
  draws from either populated half (sea_deck split), even a CARTE monodeck;
  unlike my_sea / Sig Select's polarization collapse
- glow coordination: the sky-saved glow is muted in the sky/sea phases
  (html.sky-open / sea-open / sea-entered); sea glow color --priYl -> --priId
  (distinct from sky's --priTk); the sea glow-machine fires at hand-COMPLETION
  (mirrors Sky Select), not during drawing
- guard copy "Auto deal cards?" -> "Auto Draw cards?" (match the AUTO DRAW btn)
- fix: drop the stale `html.sea-open #id_aperture_fill { opacity:1 }` — it
  painted the opaque z-90 fill over the z-5 felt so the spread flashed then
  vanished (same trap as the CAST SKY felt); removed the dead .sea-backdrop /
  .sea-overlay / .sea-modal-* SCSS
- tests: epic PickSeaPersistTest (7) + PickSeaUnifiedFeltTest (6) ITs; SeaDeal
  preview-scoping + BurgerSpec sky-glow-mute Jasmine specs; my_sea sig-card
  ITs scoped to .my-sea-cross (the preview adds a 2nd .sea-sig-card)

[[feedback-felt-aperture-fill-covers-felt]]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 21:42:24 -04:00
Disco DeDisco
53cd7afeb4 feat: My Sea applet dynamic population + lay/leave POSITION_LABELS swap fix + My Sign applet stat-block + Brief-fied sign-gate + --duoUser olive on all four personal-data surfaces. Six visual+structural items batched across the dashboard/billboard/gameboard.
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful
(1) **My Sea applet dynamic population.** Applet at `_applet-my-sea.html` was referencing an undefined `latest_draw_cards` template var — fell through to "No draws yet" even when the user had an active draw. New helpers in `apps/gameboard/models.py`: `DRAW_ORDER` + `POSITION_LABELS` constants (Python mirrors of the JS dicts in `my_sea.html:274-293`) + `latest_draw_slots(user)` builder that pairs each spread position w. its drawn card + display label + polarity. Wired through `gameboard()` + `toggle_game_applets()` views as `my_sea_slots`. Applet now renders all spread slots in DRAW_ORDER: filled = `.my-sea-slot--filled.my-sea-slot--{gravity,levity}` w. corner-tl + face (name + arcana) + corner-br (mirror) markup (same shape language as my_sign.html `.sig-stage-card`), empty = `.my-sea-slot--empty` w. `0.15rem dashed rgba(var(--terUser), 1)` border (matches the picker's `.sea-card-slot` style exactly so the applet reads as a true scaled-down twin). Container queries (`container-type: size` on `.my-sea-scroll`) lift `--slot-w` to fill the applet's vertical aperture (`min(100cqi, calc((100cqh - 1rem) * 5 / 8))` carves the label row). Position labels pulled tight against the slot's bottom border (`margin-top: -0.15rem` crosses the border line) + vertically stretched (`transform: scaleY(1.4)` mirroring `.sea-pos-label` in `_card-deck.scss:1671-1684`) — empty-slot labels keep the same `--secUser` ink as filled-slot labels for title cohesion across the row. Horizontal-scroll on multi-card spreads via mousewheel — `bindMySeaWheel()` in `gameboard.js` translates vertical wheel events to `scrollLeft += deltaY` (lifted verbatim from `bindPaletteWheel` in `dashboard.js:7-14`).

(2) **lay/leave POSITION_LABELS swap fix.** User caught in the Escape Velocity picker that LEFT slot read "Lay" + BOTTOM slot read "Leave" — opposite of traditional Celtic Cross semantics (LEFT = Behind/past, BOTTOM = Beneath/root). Root cause: POSITION_LABELS for both Waite-Smith + Escape Velocity had `lay`/`leave` slug→label assignments inverted vs the CSS grid's spatial mapping (`_card-deck.scss:1276-1279` puts slug `lay` at BOTTOM, slug `leave` at LEFT). Fix in 5 places: `my_sea.html:287,292` JS POSITION_LABELS (WS: lay→"Beneath", leave→"Behind"; EV: lay→"Lay", leave→"Leave"), `gameboard/models.py:44-47` Python mirror, `test_game_my_sea.py:618-619` FT label-assertion table, `_sea_overlay.html:28,53` annotated comments (`sea-pos-leave` → "Behind (past) — CC pos 6 / EV pos 4"; `sea-pos-lay` → "Beneath (root) — CC pos 4 / EV pos 3"). Slug-to-CSS mapping, DRAW_ORDER, + DB persistence unchanged → no migrations, no data invalidation. **Crucial for Voronoi mapping correctness** per user spec.

(3) **My Sign applet — stage-card layout + stat-block beside.** Applet card markup upgraded to mirror my_sign.html `.sig-stage-card`: corner-tl + face (name + arcana centred) + corner-br (mirror, rotated 180°). Sized to fill applet height via container queries (`--applet-card-w: min(48cqi, 62.5cqh)` — 48cqi caps the card at half the row to leave room for the stat-block). Sibling `.my-sign-applet-stat-block` partial added — emanation/reversal face label + keyword list (from `card.keywords_upright` / `keywords_reversed` keyed off `significator_reversed`), no SPIN/FYI buttons (applet is read-only). Styling cribbed from `.sig-stat-block` in `_card-deck.scss:595-607` — priUser-translucent bg + terUser border + matching `--applet-card-w` sizing.

(4) **My Sea sign-gate refactored to Brief banner.** Was an inline `.my-sea-sign-gate` div w. its own SCSS — broke from the project's `Brief.showBanner` portal pattern. Refactored to a shared `_my_sea_sign_gate_brief.html` partial that fires `Brief.showBanner` w. title="Sign required" + line_text="Look!—pick your sign before drawing the Sea." + post_url=`/billboard/my-sign/`. Brief portals to the page-level h2 anchor via `note.js`'s `_alignToH2` (gaussian-glass `.note-banner` shell, FYI button → my-sign picker, NVM dismisses). Modifier class `.my-sea-sign-gate-brief` added post-render for FT selector disambiguation. note.js load hoisted to gameboard.html `{% block scripts %}` + the top of `my_sea.html {% block content %}` (single load per page — note.js declares `const Brief = ...` at global scope, second load = SyntaxError). All `.my-sea-sign-gate{,--applet,__line,__actions,__back,__fyi}` SCSS deleted. FTs (`test_no_sig_renders_lookline_gate_on_standalone_page` + 5 siblings) + ITs (`test_my_sea_applet_fires_sign_gate_brief_for_user_without_sig` etc.) updated to assert `.note-banner.my-sea-sign-gate-brief` + the JS-rendered FYI/NVM buttons inside the Brief shell.

(5) **Levity card text invisibility fix.** My-sea applet levity slots (--secUser bg) rendered their corner-rank + suit-icon invisible because `.fan-card-corner` carries a global `color: rgba(var(--secUser), 0.75)` rule at `_card-deck.scss:312-319` (specificity 0,1,0) that out-specifics the slot's inherited `color: --priUser`. Same trap as the `.fan-card-name { color: --quiUser }` global. Fix at `_gameboard.scss` inside the levity rule: explicit `.fan-card-corner { color: rgba(var(--priUser), 1) }` + `.fan-card-name { color: rgba(var(--priUser), 1) }` + `.fan-card-arcana { color: rgba(var(--priUser), 0.7) }` overrides at (1,3,1) specificity — beats the globals without `!important`. **Trap captured in memory** — pattern repeats across game-kit, my-sign, my-sea so worth pinning.

(6) **--duoUser olive on all five personal-data surfaces.** Per user spec, the four "personal" applets (My Sign on billboard, My Sea on gameboard, My Sky on dashboard) + the standalone Dashsky page + the standalone My Sign page got `background-color: rgba(var(--duoUser), 1)` so they read as a unified olive-bg group across navigation surfaces. For Dashsky specifically, the form column also got the override (`.sky-page .sky-form-col { background: --duoUser }`) — the base `.sky-form-col { background: --priUser }` (`_sky.scss:137`, shared w. the in-room CAST SKY modal) was leaving the dashsky form column purple inside the otherwise-olive page. Scoped to `.sky-page` so the in-room modal's purple form-col stays intact (sits over --secUser room bg, needs that contrast). One detour caught: tried `body.page-sky { background-color: --duoUser }` to fill the gap below .sky-page's content-sized aperture but it bled to navbar + footer (which sit outside .container) — reverted.

**TDD coverage**: 3 new ITs in `apps/gameboard/tests/integrated/test_views.py` — `test_my_sea_applet_renders_drawn_cards_in_draw_order` (SAO 1-of-3 fills `lay` slot, cover/crown render as empty placeholders), `test_my_sea_applet_labels_match_locked_spread` (SAO labels exactly Situation/Action/Outcome), `test_my_sea_applet_waite_smith_labels_post_fix` (regression pin for the WS Cover/Cross/Crown/**Beneath**/Before/**Behind** sequence post-swap-fix). Existing my-sea applet ITs updated to match the new selector vocabulary (`.my-sea-slot--filled` instead of `.my-sea-card`, Brief script substring instead of `.my-sea-sign-gate--applet`). 6 my-sea FTs updated to the Brief-banner contract. 1214/1214 IT/UT green.

**.gitignore**: temporary entry for `src/apps/epic/static/apps/epic/images/cards-faces/minchiate-fiorentine/` until images get renamed — flagged for removal once the rename lands. (Per user's wget download of the Minchiate faces into the gameboard cards/ tree this session.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 15:19:34 -04:00
Disco DeDisco
b6e93b9d64 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
Disco DeDisco
3242873625 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
Disco DeDisco
a319318740 sky/sea modal titles: PICK SKY/SEA → SKY/SEA SELECT (titles only — table-hex .btn-primary instances stay PICK SKY/SEA where SELECT wouldn't fit)
The in-room PICK SKY / PICK SEA overlay headers now read "SKY SELECT" / "SEA SELECT" — matches the SIG SELECT phase naming. The .btn-primary triggers in the table-hex (PICK<br>SIGS, PICK<br>SKY, PICK<br>SEA) keep their existing labels because the 4rem circular btn cap can't fit "SELECT" on a single line. No code-side renames (id_pick_sky_btn, etc. stay) — only the human-facing modal title text. 21-test sky/sea regression green.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 15:49:58 -04:00
Disco DeDisco
b29bcf5c38 tray sig-card tooltip: portal w. PRV|NXT pager — TDD
Phase 2 of the apps.tooltips integration on the tray. Hovering
.tray-sig-card > .sig-stage-card opens #id_tooltip_portal w. an FYI panel
that mirrors #id_fan_fyi_panel (Energy / Operation entries cycled via
PRV|NXT), but w.o. the stage block, w.o. Reversal entries, & w.o. the fan
stage's click-to-dismiss handler — the panel-body click is reserved for
future drag-and-drop on .tray-sig-card:active.

- _partials/_sig_fyi_panel.html — new partial, the .sig-info + PRV|NXT
  block extracted out of game_kit.html, _sig_select_overlay.html, &
  _sea_overlay.html. {% include %}d back from those 3 callers; pure
  copy-paste extraction (no behavioural change to fan stage, sig select,
  or sea select).
- room.html: .tray-sig-card > .sig-stage-card gains data-energies +
  data-operations (the only attrs StageCard.buildInfoData reads), keyed
  off my_tray_sig.energies_json / .operations_json (existing TarotCard
  properties).
- tray-tooltip.js: new sig branch — _showSig() builds the panel inline,
  paints via StageCard.renderFyi, & wires PRV|NXT cycle handlers; the
  mousemove union now covers the .fyi-prev / .fyi-next btn rects (the
  btns hang past the portal's left & right edges) so mouse-over them
  keeps the panel alive. Click stopPropagation on the btns prevents the
  panel-body click from reaching anything else.
- TrayTooltipSpec: 6 new sig-branch specs (panel structure; first energy
  entry rendered; PRV|NXT cycling; body click no-dismiss; pointer over
  btn rects keeps panel alive; pointer outside full union clears).
- test_component_tray_tooltip.py: 4 sig FTs (hover populates portal w.
  Energy/TESTLIBIDO/effect/1-of-2; PRV|NXT cycle; body click does NOT
  dismiss; mouseleave clears).

FT helper note — the sig FT's _hover dispatches a synthetic mouseenter
via JS rather than ActionChains.move_to_element, because the role-card
& sig-card cells sit side-by-side in the tray grid: the pointer's
animated path crosses the role-card on its way to the sig-card &
opens the role tooltip mid-flight, which then occludes the sig stage
by the time the move lands. Direct dispatch lands the event on the
intended trigger w.o. the cross-cell drag-by.

313 epic ITs + 335 Jasmine specs (incl. 6 new) + 6 tray-tooltip FTs all
green.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:07:33 -04:00
Disco DeDisco
3410f073f0 fan-card title symmetry; pips → Minor; tray Sig card
- title slot: <h3> → <p>; font-size 0.1 → 0.087 (deck) / 0.093 → 0.08 (sig/sea); text-wrap: balance — kills upright/reversal asymmetry & all per-card squeeze hacks
- trump 8 hyphen → U+2011, trump 9 space → U+00A0 (mig 0021) so titles wrap as intended
- pips (Earthman 1–10) → MINOR arcana (mig 0022); StageCard._arcanaDisplay() picks the right label
- PICK SEA: re-clicking a deposited slot now restores the server-rolled reversed state (sea.js _populate toggle)
- tray Sig card: render same .sig-stage-card.sea-sig-card (rank + icon, -5deg) as Sea center; --sig-card-w sized off --tray-cell-size
- title_squeeze_class kept as no-op for template compat
- 0020 (Self-Unimportance rename) included from prior turn

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 02:06:55 -04:00
Disco DeDisco
c264b6e3ee PICK SEA reversal axis: server-side roll + preview + deposited slot — TDD
- new apps/epic/utils.STACK_REVERSAL_PROBABILITY (=0.25) + stack_reversal_probability(user, room) helper; single source of truth across game phases & one-line swap point for forthcoming per-user-profile config
- sea_deck view rolls each card's `reversed` axis at fetch time using the helper, attaches to card JSON; matches the eager shuffle pattern (whole deal determined at phase start)
- room_view + sea_partial pass `stack_reversal_pct` into context for the new <p class="sea-reversal-hint">25% reversals</p> hint above the SPREAD combobox (italic, 0.7rem, 0.55 opacity)
- SeaDeal.openStage applies .stage-card--reversed + .is-reversed to stat block when card.reversed → preview lands face-reversed w. REVERSAL keywords
- _fillSlot adds .sea-card-slot--reversed → slot itself rotates 180° (bg + border + content stack flips, not just inner chars upside-down in place); .sea-pos-cross overrides to 270° to compose w. its existing 90°
- _fillSlot adds .sea-card-slot--rank-long when corner_rank.length ≥ 5 (XVIII / XXIII / XXVIII / XXXIII / XXXVIII / XLIII / XLVIII) → SCSS scaleX(0.7) + letter-spacing -0.05em squeezes horizontally w.o changing font-size

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 00:11:40 -04:00
Disco DeDisco
270e48ab2c cards 48–49 polarity-split titles; sea-stage mobile breakpoints; @comment fix — TDD
- migration 0015 fills card 49 levity_reversal=The Vibrational Mould of Man, gravity_reversal=The All-Bestowing Eagle (card 48 already seeded in 0004)
- _tarot_fan.html: 4 new data-* attrs (data-levity-emanation / data-gravity-emanation / data-levity-reversal / data-gravity-reversal); upright + reversal slots render full polarity-split title in name slot when set, qualifier slots blank
- StageCard.fromDataset: parse the 4 new attrs; populateCard: emanationOverride / reversalOverride per polarity bypasses the standard name+qualifier rendering
- model: emanation_for / reversal_for fall back to name_title (group prefix stripped) instead of full self.name; reversal_for uses self.reversal_qualifier (was leftover self.reversal post-rename)
- sea-stage-content: --sig-card-w lifted from inline style to SCSS w. portrait ≤480px / landscape ≤500h breakpoints both stepping to 130px (mirrors fan modal triggers); default 180px
- _tarot_fan.html: rewrite multi-line {# #} that rendered as page text into {% comment %}{% endcomment %}

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 21:51:23 -04:00
Disco DeDisco
2f039559e6 Game Kit fan stage + FLIP/SPIN; sig/sea/fan refactor — TDD
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful
- fan modal: stage block w. idle-reveal/careen-out; carousel shifts left so focused card sits left-of-center; SPIN rotates whole card via Element.animate(); FLIP toggles polarity (Levity ↔ Gravity) via perspective rotateY w. mid-flip repaint; SPIN state retained across FLIP; FLIP btn hover-revealed only when focused card or btn is hovered (:has)
- mobile breakpoints: --fan-card-w / --fan-card-h / --fan-stage-shift / --fan-carousel-step lifted to CSS vars on .tarot-fan-wrap; portrait ≤ 480px @ 150×230, landscape ≤ 500h @ 150×235; corners + face text/padding scale w. card width
- shared StageCard JS module (apps/epic/stage-card.js): fromDataset, populateCard, populateKeywords, buildInfoData, renderFyi — sig/sea/fan all delegate; ~150 lines de-duplicated
- shared @mixin stat-block-shared (SCSS) lifts duplicated stat-face / stat-keywords / sig-info rules; @mixin stage-card-polarity unifies sea-stage--levity/--gravity + fan[data-polarity] coloring
- model rename: TarotCard.reversal → reversal_qualifier (migration 0014); render-time fallback to current polarity's qualifier when blank
- class unification: .sig-info-open / .sea-info-open / .fyi-open → .fyi-open (on stat block); .sig-flip-btn / .sea-spin-btn / .fan-spin-btn → .spin-btn; same for .fyi-btn / .fyi-prev / .fyi-next
- custom combobox (apps/epic/combobox.js) replaces native <select> for PICK SEA spread picker — keyboard nav, click-outside-close, aria roles; Firefox/Chrome OS-rendered <option> ignored CSS
- Jasmine: FanStageSpec.js w. idle-reveal / population / SPIN / FYI / FLIP specs; sig + sea fixtures + IT view assertions updated for renamed classes
- 748 ITs + Jasmine green

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 21:01:52 -04:00
Disco DeDisco
26a3af21fa PICK SEA crucifix grid: rename CSS position classes + remove dead code
Stage 1 — free 'cross':
- sea-cross-cell → sea-crucifix-cell (template, Jasmine fixture, SCSS)

Stage 2 — semantic position names:
- sea-pos-past → sea-pos-leave; grid-area: past → leave
- sea-pos-center → sea-pos-core; grid-area: center → core
- sea-pos-future → sea-pos-loom; grid-area: future → loom
- sea-pos-root → sea-pos-lay; grid-area: root → lay
- grid-template-areas updated to match
- sea-pos-crossing removed (dead code — no element ever carried it)

Jasmine + 35 ITs green.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 14:19:30 -04:00
Disco DeDisco
e084bcc2d5 PICK SEA slot interaction: polarity card bg/border, cross-slot opacity fix, two-step tap; _hideOk ReferenceError removed from sea.js; Jasmine spec updated for two-step; migration 0012 PENTACLES cleanup — TDD
Some checks failed
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline failed
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 02:30:59 -04:00
Disco DeDisco
08aa4dc819 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
Disco DeDisco
6d75b9541f 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>
2026-04-28 23:30:07 -04:00
Disco DeDisco
132e60864e PICK SEA Sprint B: deck stacks, OK btn, card draw, LOCK HAND/DEL — TDD
- _sea_overlay.html: DEAL btn replaced by two .sea-deck-stack--levity/gravity
  piles; .sea-pos-cover + .sea-pos-cross overlaid on center sig slot; LOCK HAND
  (disabled) + DEL (.btn-danger) in .sea-form-actions; data-sea-deck-url attr
- sea overlay inline JS: _fetchDeck() loads shuffled piles from sea_deck endpoint;
  stack click → _showOk(); click elsewhere → _hideOk(); OK click → _fillPos()
  in next spread-order position; DEL → _reset(); LOCK HAND enables at 6 fills
- SPREAD_ORDER constants for waite-smith + escape-velocity spread types
- sea_deck view: shuffles full equipped deck minus all seated Significators,
  splits into levity (first half) + gravity (second half) JSON arrays
- epic:sea_deck URL registered
- sea_partial view: ctx['room'] = room added (fixes NoReverseMatch for sea_deck URL)
- _card-deck.scss: .sea-card-slot--filled; .sea-pos-cover/cross absolute overlay;
  .sea-deck-stack + .sea-stack-face; .sea-form-actions layout; removed old DEAL rule
- 9 Sprint B FTs green; 3 Sprint A FTs green; 730 ITs green

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:02:49 -04:00
Disco DeDisco
42be0c63dc SIG SELECT: read qualifiers from model fields; drop hardcoded Leavened/Graven
- _sig_select_overlay.html: add data-levity-qualifier + data-gravity-qualifier
  to sig card elements so JS can read per-card values
- sig-select.js: derive qualifier from cardEl.dataset instead of hardcoded string
- _sea_overlay.html: use my_tray_sig.levity_qualifier / gravity_qualifier;
  collapse MIDDLE/MAJOR/else branches → MAJOR vs rest (all non-major show
  qualifier above name; empty qualifier renders empty <p>)
- views.py: SIG READY event display uses card qualifier fields directly;
  removes separate MIDDLE / MAJOR / else branches

Earthman courts now show Elevated/Graven; pips show Relieving/Grieving;
Nomad and Popes show Enlightened/Engraven.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 22:33:42 -04:00
Disco DeDisco
ad7a354f8c PICK SEA overlay: sea SCSS → _card-deck.scss; Sig card + qualifier display; crossing slot deferred
- sea overlay SCSS moved from _natus.scss to end of _card-deck.scss (correct file for card/overlay primitives)
- Significator center slot: sig-stage-card w. .sea-cross context rule (background, border, aspect-ratio, overflow); fan-card-face name + sig-qualifier-above/below at 0.5rem w. word-wrap
- _sea_overlay.html: qualifier rendered from user_polarity + arcana (MIDDLE → Leavened/Graven above; MAJOR → below); crossing slot removed from HTML + grid-template-areas (deferred re-add later)
- SCSS grid trimmed to 3 rows (crown/past-center-future/root); .sea-pos-crossing class kept for later reuse

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 01:02:01 -04:00
Disco DeDisco
7fcb6f307c PICK SEA: modal w. Celtic Cross layout + spread select; PICK SKY swaps to PICK SEA after sky save — TDD
- _role_select_context: at SKY_SELECT, compute sky_confirmed (confirmed Character exists for seat) + user_polarity
- room.html: PICK SEA btn + _sea_overlay.html when sky_confirmed; PICK SKY + natus overlay otherwise
- _sea_overlay.html: transparent cards col (6-position cross, Sig at center) left; priUser form col (spread select) right; NVM cancel; JS open/close via html.sea-open
- _natus.scss: .sea-* rules mirror natus layout w. reversed columns; crossing slot rotated; dotted empty slots; sig slot solid; width/max-width replaces min() to avoid rem+vw unit mix
- select defaults: "Celtic Cross, Waite-Smith" for levity (PC/NC/SC); "Celtic Cross, Escape Velocity" for gravity (EC/AC/BC)

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 21:30:27 -04:00