- 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>
- sig-select.js: three-way reversal branch — major (qualifier + concept name),
non-major w. reversal (suit qualifier word on own line + card title),
non-major fallback (polarity qualifier only)
- template: .fan-card-face-upright + .fan-card-face-reversal wrapper divs for
compact centred text groups; arcana label sits between them
- _card-deck.scss: wrapper divs display:flex; padding-top on reversal group
equalises gap to MIDDLE ARCANA label on both sides; removes margin:auto overcorrect
- migration 0007: populates reversal qualifier word per suit on Earthman Middle
Arcana court cards (Seething/Gloomy/Nervous/Vacant); clears The Schizo's
incorrectly inherited Territoriality reversal
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
Sprint 1 (template + SCSS):
- Stage card gains .fan-card-reversal-name + .fan-card-reversal-qualifier elements
(pre-rotated 180° so they read forward after card spins); sig cards gain data-reversal attr
- _card-deck.scss: Z-axis rotate(180deg) spin on .stage-card--reversed; reversed elements
dim @ opacity 0.25 normally, flip to 1 when card is spun; upright content dims in return
- Stat face labels: Upright→Emanation, Reversed→Reversal
- Fixture updated: Emanation/Reversal labels; reversal elements + data-reversal attr
Sprint 2 (FYI from mechanisms + articulations):
- sig-select.js: _openCaution() now parses data-mechanisms + data-articulations (concat)
instead of data-cautions; _renderCaution() sets .sig-caution-title from entry.category,
.sig-caution-effect.innerHTML from entry.effect; empty fallback: "No ally interactions"
- TarotCard model: mechanisms_json + articulations_json @property (parallel to cautions_json)
- Template: data-cautions→data-mechanisms+data-articulations; "Caution!"→"" title (set by JS);
"Rival Interaction"→"Ally Interaction"; shoptalk <p> removed
- SigSelectSpec.js: all old caution tests migrated to {category,effect} dict format +
data-mechanisms; 7-spec "FYI from mechanisms + articulations" describe block; 242 specs green
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
Sprint 2 UI (game kit applet):
- _applet-game-kit.html: in-use deck → two disabled × buttons, .tt-deck-game-name;
in-use Carte Blanche → two disabled × buttons, data-current-room-name,
.tt-token-room-name; tooltip content mirrors kit bag panel (Default, card count,
description, Stock version)
- gameboard.js buildMiniContent: 'In-Use' for tokens w. data-current-room-name set
- _kit_bag_panel.html: Deck section always renders (placeholder when unequipped)
View safeguards:
- select_role: look up existing deck from prior seat in same room before
equipped_deck (Carte Blanche multi-seat); only unequip when using equipped_deck
- drop_token Carte: reject 409 if token.current_room is a different room;
unequip from equipped_trinket on drop
ITs: SelectRoleMultiSeatTest (2), DropTokenViewTest +3 (carte drop, unequip, lock)
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
- _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>
- 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>
- Move #id_natus_tooltip out of #id_applets_container (container-type:
inline-size breaks position:fixed) → add to home.html alongside
#id_tooltip_portal
- Move #id_natus_tooltip out of .natus-modal-wrap (transform breaks
position:fixed) → place as sibling of .natus-overlay in room.html
- Add _positionTooltip() helper in natus-wheel.js: flips tooltip to
left of cursor when it would overflow right edge; clamps both axes
- Replace hardcoded 280px in dashboard.js palette tooltip with measured
offsetWidth; add left-edge floor (Math.max margin)
- Planet tooltip format: @14.0° Capricorn (<svg-icon>) using preloaded
_signPaths; falls back to unicode symbol if not yet loaded
- Add .tt-sign-icon SCSS: fill:currentColor, vertical-align:middle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- natus-wheel.js: per-planet <g> group with data-planet/sign/degree/retrograde
attrs; mouseover/mouseout on group (pointer-events:none on child text/℞ so
the whole apparatus triggers hover); tooltip uses .tt-title/.tt-description;
in-sign degree via _inSignDeg() (ecliptic % 30); D3 switched from CDN to
local d3.min.js
- _natus.scss: .nw-planet--hover glow; #id_natus_tooltip position:fixed z-200
- _natus_overlay.html: tooltip div uses .tt; local d3.min.js script tag
- T3/T4/T5 converted from Selenium execute_script to Jasmine unit tests
(NatusWheelSpec.js) — NatusWheel was never defined in headless GeckoDriver;
SpecRunner.html updated to load D3 + natus-wheel.js
- test_pick_sky.py: NatusWheelTooltipTest removed (replaced by Jasmine)
- test_component_cards_tarot / test_trinket_carte_blanche: equip assertions
updated from legacy .equip-deck-btn/.equip-trinket-btn mini-tooltip pattern
to current DON|DOFF (.btn-equip in main portal); mini-portal text assertions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace Unicode glyph text elements with inline SVG <path> icons loaded from icons/zodiac-signs/*.svg files
- NatusWheel.preload() fetches + caches all 12 zodiac SVG paths before first draw; auto-detects static base URL from <script src>
- Per-element (fire/stone/air/water) colored circles behind zodiac icons; icon fill colors use element palette vars
- PLANET_ELEMENTS map (au/ag/hg/cu/fe/sn/pb/u/np/pu) drives per-planet circle + label CSS modifier classes
- Planet circles: ternary fill + senary stroke per alchemical metal palette; labels: senary fill + stroke halo
- Zodiac icon scale = 85% of circle diameter (15% inset so icons breathe inside circles)
- Zodiac + house segment bg opacity 0.5; all ring/segment borders --terUser
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix D3 arc coordinate offset (add π/2 to all arc angles — D3 subtracts it
internally, causing fills to render 90° CW from label midpoints)
- Fix house-12 wrap-around: normalise nextCusp += 360 when it crosses 0°,
eliminating the 330° ghost arc that buried house fill/number layers
- Draw all house fills before cusp lines + numbers (z-order fix)
- SCSS: sign/element fills corrected to rgba(var(--priXx, R, G, B), α) —
CSS vars are raw RGB tuples so bare var() in fill was invalid
- brighten Stone/Air/Water fallback colours; raise house fill opacities
- Button layout: SAVE SKY moves into form column (full-width, pinned bottom);
NVM becomes a btn-sm circle anchored on the modal's top-right corner via
.natus-modal-wrap (position:relative, outside overflow:hidden modal);
entrance animation moved to wrapper so NVM rides the fade+slide
- Form fields wrapped in .natus-form-main (scrollable); portrait layout
switches form-col to flex-row so form spans most width, SAVE SKY on right
- Modal max-height 92→96vh, max-width 840→920px, SVG cap 400→480px
- FT: PickSkyLocalStorageTest (2 tests) — form fields restored after NVM
and after page refresh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New _card-deck.scss: sig select styles moved out of _room.scss + _game-kit.scss
- sig-select.js: 3 WS bug fixes — thumbs-up deferred to window.load (layout settled
before getBoundingClientRect), hover cursor cleared for all cards on reservation
(not just the reserved card), applyHover guards against already-reserved roles
- Own-role indicators: gamer now sees their own role-coloured card outline + thumbs-up
- Reservation glow: replaced blurry role+ninUser double-shadow with crisp 2px outline
- Gravity qualifier: Graven text set to --terUser (matches Leavened/--quiUser pattern)
- Role card SVGs refreshed; starter-role-Blank removed
- FTs + Jasmine specs extended for sig select WS behaviour
- setup_sig_session management command for multi-browser manual testing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
roles_revealed WS event removed; select_role last pick now fires _notify_all_roles_filled() + stays in ROLE_SELECT; new pick_sigs view (POST /room/<uuid>/pick-sigs) transitions ROLE_SELECT→SIG_SELECT + broadcasts sig_select_started; room.html shows .pick-sigs-btn when all 6 roles filled; PICK SIGS btn absent during mid-selection; 11 new/modified ITs in SelectRoleViewTest + RoomViewAllRolesFilledTest + PickSigsViewTest
consumer: LEVITY_ROLES {PC/NC/SC} + GRAVITY_ROLES {BC/EC/AC}; connects to per-polarity cursor group (cursors_{id}_levity/gravity); receive_json routes cursor_move to cursor group; new handlers all_roles_filled, sig_select_started, cursor_move; CursorMoveConsumerTest (TransactionTestCase, @tag channels): levity cursor reaches fellow levity player, does not reach gravity player
game kit gear menu: #id_game_kit_menu registered in _applets.scss %applet-menu + fixed-position + landscape offset; id_gk_sections_container added to appletContainerIds in applets.js so OK submit dismisses menu; _game_kit_sections.html sections use entry.applet.grid_cols/grid_rows (was hardcoded 6); %applets-grid applied to #id_gk_sections_container (direct parent of sections, not outer wrapper); FT setUp seeds gk-* applets via get_or_create
drama test reorg: integrated/test_views.py deleted (no drama views); two test classes moved to epic/tests/integrated/test_views.py + GameEvent import added; drama/tests/unit/test_models.py → drama/tests/integrated/test_models.py; unit/ dir removed
login form: position:fixed + vertically centred in base styles across all breakpoints; 24rem width, text-align:center; landscape block reduced to left/right sidebar offsets; alert moved below h2; left-side position indicator slots 3/4/5 column order flipped via CSS data-slot selectors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
game kit: new Applet model rows (context=game-kit) for Trinkets, Tokens, Card Decks, Dice Sets via applets migration 0008; _game_kit_context() helper in gameboard.views; toggle_game_kit_sections view + URL; new _game_kit_sections.html (HTMX-swappable, visibility-conditional) + _game_kit_applet_menu.html partials; game_kit.html wired to gear btn + menu; Dice Sets now renders _forthcoming.html partial; 16 new green ITs in GameKitViewTest + ToggleGameKitSectionsViewTest
login form: .input-group now position:fixed + vertically centred (top:50%) across all breakpoints as default; landscape block reduced to left/right sidebar offsets only; form-control width 24rem, text-align:center; alert block moved below h2 in base.html; alert margin 0.75rem all sides; home.html header switches between Howdy Stranger (anon) and Dashboard (authed)
room.html position indicators: slots 3/4/5 (AC/SC/EC) column order flipped via SCSS data-slot selectors so .fa-chair sits table-side and label+status icon sit outward
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _animationPending set before fetch (not in .then()) — blocks WS turn advance during in-flight request
- _placeCardDelay (3s) + _postTrayDelay (3s) give gamer time to see each step; both zeroed by _testReset()
- .role-confirmed class: full-opacity chair after placeCard completes; server-rendered on reload
- Slot circles disappear in join order (slot 1 first) via count-based logic, not role-label matching
- data-active-slot on card-stack; handleTurnChanged writes it for selectRole() to read
- #id_tray_wrap not rendered during gate phase ({% if room.table_status %})
- Tray slide/arc-in slowed to 1s for diagnostics; wobble kept at 0.45s
- Obsolete test_roles_revealed_simultaneously FT removed; T8 tray FT uses ROLE_SELECT room
- Jasmine macrotask flush pattern: await new Promise(r => setTimeout(r, 0)) after fetch .then()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Split .gate-overlay into .gate-backdrop (z-100, blur) + .gate-overlay modal (z-120) so .table-position elements (z-110) render above backdrop but below modal
- New _table_positions.html partial: 6 .table-position divs with .fa-chair, role label, and .fa-ban/.fa-circle-check status icons; included unconditionally in room.html
- New epic:room view at /gameboard/room/<uuid>/; gatekeeper redirects there when table_status set; pick_roles redirects there
- role-select.js: adds .active glow to position on selectRole(); swaps .fa-ban→.fa-circle-check in placeCard onComplete; handleTurnChanged clears stale .active from all positions
- FTs: PositionIndicatorsTest (5 tests) + RoleSelectTest 8a/8b (glow + check state)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CARTE type, User.equipped_trinket FK, Token.slots_claimed field; post_save signal sets
equipped_trinket=COIN for new users, PASS for staff; kit bag now shows only the equipped
trinket in Trinkets section; Game Kit applet mini tooltip portal shows Equipped or Equip
Trinket per token; AJAX POST equip-trinket id updates equippedId in-place; equip btn
now works for COIN, PASS, and CARTE (data-token-id added to all three); Gatekeeper CARTE
flow: drop_token sets current_room (no slot reserved); each empty slot up to
slots_claimed+1 gets a drop-token-btn; slots_claimed high-water mark advances on fill,
never decrements; highest CARTE-filled slot gets NVM (release_slot); token_return_btn
resets current_room + slots_claimed + un-fills all CARTE slots; gate_status always returns
full template so launch-game-btn persists via HTMX when gate_status == OPEN; room.html
includes gatekeeper when GATHERING or OPEN; new FT test_trinket_carte_blanche.py (2
tests, both passing); 299 tests green