aperture architecture: lift the page-locking foundation (html/body/.container overflow:hidden + flex-column + min-height:0; .row flex-shrink:0) from 5 per-page SCSS files into _base.scss — was opt-in per page via `body.page-billboard` / `page-dashboard` / `page-gameboard` / `page-sky` / `page-wallet` etc., with 5 near-identical `html:has(body.page-X) { overflow: hidden }` + `body.page-X { … }` blocks duplicating the same rules; any page that forgot to set `page_class` in its view context (e.g. `epic.tarot_deck` — never set) rendered without the aperture, letting applet borders + titles clip past the fixed navbar/footer sidebars at narrower viewports; foundation now universal, page-specific overrides stay scoped — gameboard keeps `.container { overflow: clip }` (Firefox seat-tooltip scroll-anchoring quirk) + billboard/dashboard/gameboard keep `.row { margin-bottom: -1rem }` (h2-row tightening); page_class context vars + body class hooks preserved (FTs at test_bud_btn.py:370 / :379 still assert on them); regression gate: 60 layout-sensitive FTs (billboard, my_buds, bud_btn, applet_my_posts, dashboard, wallet, gameboard, layout_and_styling, jasmine) + 43 room FTs (gatekeeper_bud_btn, room_gatekeeper, room_sky_select, sharing) 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-12 17:16:12 -04:00
|
|
|
|
// Aperture foundation (html/body/.container overflow + flex-column) lives
|
|
|
|
|
|
// universally in _base.scss. Gameboard's only divergence: `overflow: clip`
|
|
|
|
|
|
// on .container instead of `hidden` — `clip` prevents the seat tooltip
|
|
|
|
|
|
// scroll-anchoring quirk Firefox triggers under overflow:hidden. The
|
|
|
|
|
|
// `.row { margin-bottom: -1rem }` pull mirrors the billboard/dashboard
|
|
|
|
|
|
// h2-row tightening.
|
2026-03-09 21:52:54 -04:00
|
|
|
|
|
|
|
|
|
|
body.page-gameboard {
|
|
|
|
|
|
.container {
|
2026-03-16 00:07:52 -04:00
|
|
|
|
overflow: clip;
|
2026-03-09 21:52:54 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.row {
|
|
|
|
|
|
margin-bottom: -1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.gameboard-page {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 425px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 550px) {
|
|
|
|
|
|
.gameboard-page {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 738px) {
|
|
|
|
|
|
.gameboard-page {
|
|
|
|
|
|
min-width: 666px;
|
|
|
|
|
|
}
|
2026-03-11 00:58:24 -04:00
|
|
|
|
|
|
|
|
|
|
body.page-gameboard .container {
|
|
|
|
|
|
overflow: visible;
|
|
|
|
|
|
}
|
2026-03-09 21:52:54 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-06 01:30:31 -04:00
|
|
|
|
@media (orientation: landscape) {
|
2026-03-23 01:06:14 -04:00
|
|
|
|
// Restore clip in landscape — overrides the >738px overflow:visible above,
|
|
|
|
|
|
// preventing the gameboard applets from bleeding into the footer sidebar.
|
|
|
|
|
|
body.page-gameboard .container {
|
|
|
|
|
|
overflow: clip;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Reset the 666px min-width so gameboard-page shrinks to fit within the
|
|
|
|
|
|
// sidebar-bounded container rather than overflowing into the footer sidebar.
|
|
|
|
|
|
.gameboard-page {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 23:48:20 -04:00
|
|
|
|
#id_applet_game_kit {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
#id_game_kit {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
position: relative;
|
2026-03-09 21:52:54 -04:00
|
|
|
|
display: flex;
|
2026-03-09 23:48:20 -04:00
|
|
|
|
flex-direction: row;
|
2026-03-24 22:25:25 -04:00
|
|
|
|
flex-wrap: wrap;
|
2026-03-09 23:48:20 -04:00
|
|
|
|
align-items: center;
|
2026-03-24 22:25:25 -04:00
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 0.75rem;
|
2026-03-09 23:48:20 -04:00
|
|
|
|
overflow-x: visible;
|
|
|
|
|
|
scrollbar-width: none;
|
|
|
|
|
|
&::-webkit-scrollbar { display: none; }
|
2026-03-09 22:42:30 -04:00
|
|
|
|
|
2026-03-09 23:48:20 -04:00
|
|
|
|
.token { position: static; }
|
2026-03-09 22:42:30 -04:00
|
|
|
|
|
2026-04-15 22:39:01 -04:00
|
|
|
|
.token:hover .token-tooltip,
|
|
|
|
|
|
.token:hover .tt { display: none; } // JS portal handles show/hide
|
2026-03-09 23:48:20 -04:00
|
|
|
|
|
|
|
|
|
|
.token,
|
|
|
|
|
|
.kit-item { font-size: 1.5rem; }
|
|
|
|
|
|
|
|
|
|
|
|
.kit-item { opacity: 0.6; }
|
2026-03-09 22:42:30 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-12 22:48:32 -04:00
|
|
|
|
#id_applet_new_game {
|
2026-03-09 22:42:30 -04:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-03-09 21:52:54 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-14 00:34:07 -04:00
|
|
|
|
#id_applet_my_games {
|
2026-05-12 22:48:32 -04:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2026-03-14 00:34:07 -04:00
|
|
|
|
|
2026-05-12 22:48:32 -04:00
|
|
|
|
.applet-list {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
padding-top: 0.25rem;
|
2026-03-14 00:34:07 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 23:48:20 -04:00
|
|
|
|
#id_tooltip_portal {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
|
2026-04-16 00:14:47 -04:00
|
|
|
|
padding: 0.75rem 1.5rem;
|
|
|
|
|
|
|
2026-04-21 15:46:30 -04:00
|
|
|
|
@extend %tt-token-fields;
|
2026-04-16 00:14:47 -04:00
|
|
|
|
|
|
|
|
|
|
.tt-equip-btns {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: -1rem;
|
|
|
|
|
|
top: -1rem;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
|
|
|
|
|
|
.btn { margin: 0; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-03 21:18:09 -04:00
|
|
|
|
// Tray sig-card tooltip (Phase 2) — PRV / NXT btns pinned to the bottom
|
|
|
|
|
|
// corners of the portal, 1rem outside the panel so the btn centres land
|
|
|
|
|
|
// exactly on the corners. The shared @stat-block-shared mixin in
|
|
|
|
|
|
// _card-deck.scss already does this for fan / sig / sea contexts; the
|
|
|
|
|
|
// portal isn't covered by that mixin so we re-state the rules here.
|
|
|
|
|
|
.fyi-prev,
|
|
|
|
|
|
.fyi-next {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: -1rem;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
z-index: 70;
|
|
|
|
|
|
}
|
|
|
|
|
|
.fyi-prev { left: -1rem; }
|
|
|
|
|
|
.fyi-next { right: -1rem; }
|
|
|
|
|
|
|
2026-03-09 23:48:20 -04:00
|
|
|
|
&.active { display: block; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 00:07:52 -04:00
|
|
|
|
#id_mini_tooltip_portal {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
font-size: 0.8em;
|
|
|
|
|
|
font-style: italic;
|
|
|
|
|
|
width: fit-content;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
text-align: right;
|
|
|
|
|
|
|
|
|
|
|
|
&.active { display: block; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 21:52:54 -04:00
|
|
|
|
@media (max-height: 500px) {
|
|
|
|
|
|
body.page-gameboard {
|
|
|
|
|
|
.container {
|
|
|
|
|
|
.row {
|
|
|
|
|
|
padding: 0.25rem 0;
|
|
|
|
|
|
.col-lg-6 h2 {
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-19 01:38:55 -04:00
|
|
|
|
|
|
|
|
|
|
// ─── My Sea sign-gate ────────────────────────────────────────────────────────
|
|
|
|
|
|
// Sprint 4b of [[project-my-sea-roadmap]]. Renders when User.significator
|
|
|
|
|
|
// is None, on both the standalone /gameboard/my-sea/ page AND the
|
|
|
|
|
|
// /gameboard/ My Sea applet. Look!-formatted Brief-style line w. FYI
|
|
|
|
|
|
// (→ /billboard/my-sign/) + BACK (→ /gameboard/) action buttons. Inline
|
|
|
|
|
|
// content (not portaled like .note-banner) — it IS the page content
|
|
|
|
|
|
// until a sig is picked, not a transient nudge.
|
|
|
|
|
|
.my-sea-sign-gate {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
padding: 1.5rem;
|
|
|
|
|
|
color: rgba(var(--terUser), 1);
|
|
|
|
|
|
|
|
|
|
|
|
.my-sea-sign-gate__line {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-size: 1.1rem;
|
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
// --terUser ink mirrors the gate's accent + signals "do this
|
|
|
|
|
|
// first" visually distinct from the body's standard --secUser.
|
|
|
|
|
|
color: rgba(var(--terUser), 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.my-sea-sign-gate__actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-19 15:15:37 -04:00
|
|
|
|
// Applet variant — denser layout, omits NVM (the user is already on
|
2026-05-19 01:38:55 -04:00
|
|
|
|
// the gameboard). Smaller line + just the FYI action surviving.
|
|
|
|
|
|
&.my-sea-sign-gate--applet {
|
|
|
|
|
|
padding: 0.5rem;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
|
|
|
|
|
|
.my-sea-sign-gate__line {
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-19 15:15:37 -04:00
|
|
|
|
|
|
|
|
|
|
// ─── My Sea DRAW SEA landing ─────────────────────────────────────────────────
|
|
|
|
|
|
// Sprint 5 iter 1 of [[project-my-sea-roadmap]]. When a user has a saved
|
|
|
|
|
|
// significator (gate passed), /gameboard/my-sea/ renders this landing
|
|
|
|
|
|
// screen: DRY table hex w. 6 chair seats labeled 1C-6C + central DRAW
|
|
|
|
|
|
// SEA btn. Mirrors my-sign's `.my-sign-page` + `.my-sign-landing`
|
|
|
|
|
|
// structure — same room-shell chain so room.js's scaleTable() can size
|
|
|
|
|
|
// the hex; same flex setup so the container chain propagates real
|
|
|
|
|
|
// height down for the scale calc.
|
|
|
|
|
|
.my-sea-page {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.my-sea-landing {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
2026-05-19 15:48:07 -04:00
|
|
|
|
// FREE DRAW btn — centered in the hex, mirrors SCAN SIGN's 2-line
|
|
|
|
|
|
// font sizing so "FREE/DRAW" sits cleanly inside the 4rem circle.
|
2026-05-19 15:15:37 -04:00
|
|
|
|
#id_draw_sea_btn {
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
line-height: 1.1;
|
|
|
|
|
|
white-space: normal;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-19 15:48:07 -04:00
|
|
|
|
// Chair-position labels (1C-6C). Mirrors the room's `.seat-role-
|
|
|
|
|
|
// label` grid placement (col 2, row 1 by default; flips to col 1
|
|
|
|
|
|
// for left-side seats 3/4/5 so the label sits closest to the hex)
|
|
|
|
|
|
// but uses a role-free class name — my-sea is the solo draw flow,
|
|
|
|
|
|
// no role-pick phase, so the room's role-grammar doesn't apply.
|
|
|
|
|
|
.table-seat .seat-position-label {
|
|
|
|
|
|
grid-column: 2;
|
|
|
|
|
|
grid-row: 1;
|
2026-05-19 15:15:37 -04:00
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
font-weight: 600;
|
2026-05-19 15:48:07 -04:00
|
|
|
|
letter-spacing: 0.05em;
|
|
|
|
|
|
color: rgba(var(--secUser), 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
.table-seat[data-slot="3"] .seat-position-label,
|
|
|
|
|
|
.table-seat[data-slot="4"] .seat-position-label,
|
|
|
|
|
|
.table-seat[data-slot="5"] .seat-position-label {
|
|
|
|
|
|
grid-column: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Seated chair (post-FREE DRAW). Visual transition mirrors
|
|
|
|
|
|
// `.table-seat.active .fa-chair` from _room.scss line 626 —
|
|
|
|
|
|
// --terUser color + --ninUser drop-shadow glow — but uses a stable
|
|
|
|
|
|
// `.seated` class (semantically distinct from `.active`: active =
|
|
|
|
|
|
// current turn in a multi-user room; seated = draw-locked occupant
|
|
|
|
|
|
// in this solo-flow). _room.scss line 596 makes the colour change
|
|
|
|
|
|
// a 0.6s ease transition so the chair animates rather than snaps.
|
|
|
|
|
|
// Status icon (.position-status-icon) colour swap fa-ban red →
|
|
|
|
|
|
// fa-circle-check green is handled by _room.scss lines 615-616.
|
|
|
|
|
|
.table-seat.seated .fa-chair {
|
2026-05-19 15:15:37 -04:00
|
|
|
|
color: rgba(var(--terUser), 1);
|
2026-05-19 15:48:07 -04:00
|
|
|
|
filter: drop-shadow(0 0 4px rgba(var(--ninUser), 1));
|
2026-05-19 15:15:37 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-19 16:06:14 -04:00
|
|
|
|
// Picker phase bg — `--duoUser` matches the table hex's interior so
|
|
|
|
|
|
// the landing→picker swap reads as a continuous surface (parallels
|
|
|
|
|
|
// `.my-sign-page[data-phase="picker"]` in _card-deck.scss line 704).
|
|
|
|
|
|
.my-sea-page[data-phase="picker"] {
|
|
|
|
|
|
background: rgba(var(--duoUser), 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-19 15:15:37 -04:00
|
|
|
|
.my-sea-picker {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
My Sea form col + SPREAD dropdown w. 3-card/6-card section dividers — Sprint 5 iter 3 of My Sea roadmap — TDD
Picker phase form col: SPREAD combobox w. 6 spread options under 2 horizontal section dividers ("3-card spreads" / "6-card spreads"), reversal-% caption, GRAVITY + LEVITY deck swatches, LOCK HAND + DEL btns. Default = Situation, Action, Outcome (a 3-card spread). Selecting a 6-card spread (Celtic Cross Waite-Smith or Escape Velocity) swaps `.my-sea-cross[data-spread-shape]` from `three-card` to `six-card` — revealing the crown / lay / cross cells that the default 3-card variants hide.
Naming correction (user-locked): the spread itself is a "three-card spread" not a "three-card cross" — "cross" stays scoped to the Celtic Cross variants (6-card spreads). CSS class `.my-sea-cross` carries grid-container semantics regardless of which spread shape is active; the spread-vs-cross distinction lives at the spread-name layer only.
- **View** (gameboard/views.py): `my_sea` adds `default_spread = "situation-action-outcome"` + `reversals_pct = 25` context keys.
- **Template** (my_sea.html): renders all 7 cross cells (crown/leave/core+cover+cross/loom/lay) unconditionally + adds `data-spread-shape="three-card"` to `.my-sea-cross`. Form col DRY-reuses gameroom `_sea_overlay.html`'s `.sea-form-col` shape — `.sea-form-main` w. `.sea-field` (SPREAD label + reversal hint + custom combobox) + `.sea-stacks` (GRAVITY + LEVITY swatches) + `.sea-form-actions` (LOCK HAND + DEL). 6 options + 2 dividers in the combobox `<ul>`; dividers are `role="presentation"` so `combobox.js` skips them naturally. Inline IIFE listens for the hidden `<input id="id_sea_spread">`'s `change` event + sets `.my-sea-cross`'s `data-spread-shape` based on whether the value is in `['waite-smith', 'escape-velocity']`. No new combobox.js wiring — the existing module's `change`-bubbling contract feeds straight in.
- **SCSS** (_gameboard.scss):
- `.my-sea-cross[data-spread-shape="three-card"]` — single-row `"leave core loom"` grid + `display: none` on crown/lay/cross.
- `.my-sea-cross[data-spread-shape="six-card"]` — inherits the gameroom `.sea-cross`'s 3×3 grid + reveals all cells.
- `.sea-select-divider` — section header style mirrors `.kit-bag-label`'s small-uppercase-underlined-letter-spaced --quaUser/0.75 treatment but HORIZONTAL (kit-bag uses `writing-mode: vertical-rl`; dropdown menus are flat). `pointer-events: none` belt-and-braces against accidental click/hover.
- `.my-sea-form-col` — width-constrains the form col so the picker's cross + form sit side-by-side.
**Iter-2 contract updated** (cells in DOM, hidden via CSS for 3-card default):
- FT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_hides_six_card_only_positions_by_default` — asserts the 3 cells are in the DOM but `is_displayed() == False` so iter-3's spread switch can reveal them via CSS without re-rendering.
- IT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_renders_six_card_only_positions_for_spread_switch` — assertContains the classes (server now renders them unconditionally).
**Tests**:
- 4 FTs in new `MySeaSpreadFormTest`: combobox renders 6 options + 2 dividers w. correct labels, default is Situation/Action/Outcome (hidden input value + visible current-label span + cross's data-spread-shape), picking Celtic Cross flips data-spread-shape to six-card + reveals crown/lay/cross, form col carries DECKS swatches + LOCK HAND + DEL + reversal-% caption. Combobox `<li>` options are inside `aria-expanded='false'` listbox → use `get_attribute("textContent")` not `.text` (which returns "" for Selenium-hidden elements).
- 7 ITs in new `MySeaSpreadFormTemplateTest`: default_spread + reversals_pct context keys, all 6 options + both labels render, 2 dividers render w. expected text, default option carries aria-selected="true", cross's initial data-spread-shape="three-card", form col DECKS + buttons + reversal hint render.
Tests: 32/32 FT green across test_bill_my_sign + test_game_my_sea; 1048/1048 IT/UT green in 52s.
Card-draw mechanics (clicking a deck swatch deposits a card into the next empty slot; LOCK HAND commits the draw) defer to iter 4 — this iter ships the spread-selection + layout-shape switch UI; the buttons are stubs (LOCK HAND starts disabled, DEL is a placeholder).
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:23:25 -04:00
|
|
|
|
gap: 1rem;
|
2026-05-19 15:15:37 -04:00
|
|
|
|
}
|
2026-05-19 16:06:14 -04:00
|
|
|
|
|
2026-05-19 19:38:53 -04:00
|
|
|
|
// .my-sea-cross renders all 6 surrounding positions (crown/leave/lay/
|
|
|
|
|
|
// loom + cover/cross overlaid on core) unconditionally. The SPREAD
|
|
|
|
|
|
// dropdown sets `data-spread="<name>"` on this element; per-spread
|
|
|
|
|
|
// rules below hide the positions each spread doesn't use. Inherits
|
|
|
|
|
|
// the 3×3 `grid-template-areas` from _card-deck.scss line 1189-1200
|
|
|
|
|
|
// so visible cells land in their canonical positions; hidden cells
|
|
|
|
|
|
// just leave their grid slots empty.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Per-spread position subsets — user-locked 2026-05-19:
|
|
|
|
|
|
// PPF: leave (1) cover (2) loom (3) — horizontal middle row
|
|
|
|
|
|
// SAO: lay (1) cover (2) crown (3) — vertical center column
|
|
|
|
|
|
// MBS: crown (1) lay (2) loom (3) — T-shape (crown + lay vertical, loom right)
|
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
|
|
|
|
// DOS: loom (1) cross (2) crown (3) — loom right · cross overlay · crown above
|
2026-05-19 19:38:53 -04:00
|
|
|
|
// CC variants: all 6 positions (Waite-Smith / Escape Velocity differ in DRAW ORDER only,
|
|
|
|
|
|
// not in position visibility).
|
|
|
|
|
|
|
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
|
|
|
|
// Bump grid gap on my-sea (gameroom .sea-cross stays at 0.5rem since
|
|
|
|
|
|
// gameroom slots have no per-position labels). The vertical leave/loom
|
|
|
|
|
|
// labels need ~1.5rem of horizontal clearance from adjacent cells, and
|
|
|
|
|
|
// the horizontal crown/cover/lay/cross labels need ~1rem of vertical
|
|
|
|
|
|
// clearance so they don't overlap into the next row.
|
|
|
|
|
|
.my-sea-cross {
|
|
|
|
|
|
gap: 1rem !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-19 19:38:53 -04:00
|
|
|
|
.my-sea-cross[data-spread="past-present-future"] {
|
My Sea form col + SPREAD dropdown w. 3-card/6-card section dividers — Sprint 5 iter 3 of My Sea roadmap — TDD
Picker phase form col: SPREAD combobox w. 6 spread options under 2 horizontal section dividers ("3-card spreads" / "6-card spreads"), reversal-% caption, GRAVITY + LEVITY deck swatches, LOCK HAND + DEL btns. Default = Situation, Action, Outcome (a 3-card spread). Selecting a 6-card spread (Celtic Cross Waite-Smith or Escape Velocity) swaps `.my-sea-cross[data-spread-shape]` from `three-card` to `six-card` — revealing the crown / lay / cross cells that the default 3-card variants hide.
Naming correction (user-locked): the spread itself is a "three-card spread" not a "three-card cross" — "cross" stays scoped to the Celtic Cross variants (6-card spreads). CSS class `.my-sea-cross` carries grid-container semantics regardless of which spread shape is active; the spread-vs-cross distinction lives at the spread-name layer only.
- **View** (gameboard/views.py): `my_sea` adds `default_spread = "situation-action-outcome"` + `reversals_pct = 25` context keys.
- **Template** (my_sea.html): renders all 7 cross cells (crown/leave/core+cover+cross/loom/lay) unconditionally + adds `data-spread-shape="three-card"` to `.my-sea-cross`. Form col DRY-reuses gameroom `_sea_overlay.html`'s `.sea-form-col` shape — `.sea-form-main` w. `.sea-field` (SPREAD label + reversal hint + custom combobox) + `.sea-stacks` (GRAVITY + LEVITY swatches) + `.sea-form-actions` (LOCK HAND + DEL). 6 options + 2 dividers in the combobox `<ul>`; dividers are `role="presentation"` so `combobox.js` skips them naturally. Inline IIFE listens for the hidden `<input id="id_sea_spread">`'s `change` event + sets `.my-sea-cross`'s `data-spread-shape` based on whether the value is in `['waite-smith', 'escape-velocity']`. No new combobox.js wiring — the existing module's `change`-bubbling contract feeds straight in.
- **SCSS** (_gameboard.scss):
- `.my-sea-cross[data-spread-shape="three-card"]` — single-row `"leave core loom"` grid + `display: none` on crown/lay/cross.
- `.my-sea-cross[data-spread-shape="six-card"]` — inherits the gameroom `.sea-cross`'s 3×3 grid + reveals all cells.
- `.sea-select-divider` — section header style mirrors `.kit-bag-label`'s small-uppercase-underlined-letter-spaced --quaUser/0.75 treatment but HORIZONTAL (kit-bag uses `writing-mode: vertical-rl`; dropdown menus are flat). `pointer-events: none` belt-and-braces against accidental click/hover.
- `.my-sea-form-col` — width-constrains the form col so the picker's cross + form sit side-by-side.
**Iter-2 contract updated** (cells in DOM, hidden via CSS for 3-card default):
- FT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_hides_six_card_only_positions_by_default` — asserts the 3 cells are in the DOM but `is_displayed() == False` so iter-3's spread switch can reveal them via CSS without re-rendering.
- IT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_renders_six_card_only_positions_for_spread_switch` — assertContains the classes (server now renders them unconditionally).
**Tests**:
- 4 FTs in new `MySeaSpreadFormTest`: combobox renders 6 options + 2 dividers w. correct labels, default is Situation/Action/Outcome (hidden input value + visible current-label span + cross's data-spread-shape), picking Celtic Cross flips data-spread-shape to six-card + reveals crown/lay/cross, form col carries DECKS swatches + LOCK HAND + DEL + reversal-% caption. Combobox `<li>` options are inside `aria-expanded='false'` listbox → use `get_attribute("textContent")` not `.text` (which returns "" for Selenium-hidden elements).
- 7 ITs in new `MySeaSpreadFormTemplateTest`: default_spread + reversals_pct context keys, all 6 options + both labels render, 2 dividers render w. expected text, default option carries aria-selected="true", cross's initial data-spread-shape="three-card", form col DECKS + buttons + reversal hint render.
Tests: 32/32 FT green across test_bill_my_sign + test_game_my_sea; 1048/1048 IT/UT green in 52s.
Card-draw mechanics (clicking a deck swatch deposits a card into the next empty slot; LOCK HAND commits the draw) defer to iter 4 — this iter ships the spread-selection + layout-shape switch UI; the buttons are stubs (LOCK HAND starts disabled, DEL is a placeholder).
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:23:25 -04:00
|
|
|
|
.sea-pos-crown,
|
2026-05-19 19:38:53 -04:00
|
|
|
|
.sea-pos-cross,
|
|
|
|
|
|
.sea-pos-lay { display: none; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.my-sea-cross[data-spread="situation-action-outcome"] {
|
|
|
|
|
|
.sea-pos-leave,
|
|
|
|
|
|
.sea-pos-loom,
|
|
|
|
|
|
.sea-pos-cross { display: none; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.my-sea-cross[data-spread="mind-body-spirit"] {
|
|
|
|
|
|
.sea-pos-leave,
|
|
|
|
|
|
.sea-pos-cover,
|
My Sea form col + SPREAD dropdown w. 3-card/6-card section dividers — Sprint 5 iter 3 of My Sea roadmap — TDD
Picker phase form col: SPREAD combobox w. 6 spread options under 2 horizontal section dividers ("3-card spreads" / "6-card spreads"), reversal-% caption, GRAVITY + LEVITY deck swatches, LOCK HAND + DEL btns. Default = Situation, Action, Outcome (a 3-card spread). Selecting a 6-card spread (Celtic Cross Waite-Smith or Escape Velocity) swaps `.my-sea-cross[data-spread-shape]` from `three-card` to `six-card` — revealing the crown / lay / cross cells that the default 3-card variants hide.
Naming correction (user-locked): the spread itself is a "three-card spread" not a "three-card cross" — "cross" stays scoped to the Celtic Cross variants (6-card spreads). CSS class `.my-sea-cross` carries grid-container semantics regardless of which spread shape is active; the spread-vs-cross distinction lives at the spread-name layer only.
- **View** (gameboard/views.py): `my_sea` adds `default_spread = "situation-action-outcome"` + `reversals_pct = 25` context keys.
- **Template** (my_sea.html): renders all 7 cross cells (crown/leave/core+cover+cross/loom/lay) unconditionally + adds `data-spread-shape="three-card"` to `.my-sea-cross`. Form col DRY-reuses gameroom `_sea_overlay.html`'s `.sea-form-col` shape — `.sea-form-main` w. `.sea-field` (SPREAD label + reversal hint + custom combobox) + `.sea-stacks` (GRAVITY + LEVITY swatches) + `.sea-form-actions` (LOCK HAND + DEL). 6 options + 2 dividers in the combobox `<ul>`; dividers are `role="presentation"` so `combobox.js` skips them naturally. Inline IIFE listens for the hidden `<input id="id_sea_spread">`'s `change` event + sets `.my-sea-cross`'s `data-spread-shape` based on whether the value is in `['waite-smith', 'escape-velocity']`. No new combobox.js wiring — the existing module's `change`-bubbling contract feeds straight in.
- **SCSS** (_gameboard.scss):
- `.my-sea-cross[data-spread-shape="three-card"]` — single-row `"leave core loom"` grid + `display: none` on crown/lay/cross.
- `.my-sea-cross[data-spread-shape="six-card"]` — inherits the gameroom `.sea-cross`'s 3×3 grid + reveals all cells.
- `.sea-select-divider` — section header style mirrors `.kit-bag-label`'s small-uppercase-underlined-letter-spaced --quaUser/0.75 treatment but HORIZONTAL (kit-bag uses `writing-mode: vertical-rl`; dropdown menus are flat). `pointer-events: none` belt-and-braces against accidental click/hover.
- `.my-sea-form-col` — width-constrains the form col so the picker's cross + form sit side-by-side.
**Iter-2 contract updated** (cells in DOM, hidden via CSS for 3-card default):
- FT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_hides_six_card_only_positions_by_default` — asserts the 3 cells are in the DOM but `is_displayed() == False` so iter-3's spread switch can reveal them via CSS without re-rendering.
- IT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_renders_six_card_only_positions_for_spread_switch` — assertContains the classes (server now renders them unconditionally).
**Tests**:
- 4 FTs in new `MySeaSpreadFormTest`: combobox renders 6 options + 2 dividers w. correct labels, default is Situation/Action/Outcome (hidden input value + visible current-label span + cross's data-spread-shape), picking Celtic Cross flips data-spread-shape to six-card + reveals crown/lay/cross, form col carries DECKS swatches + LOCK HAND + DEL + reversal-% caption. Combobox `<li>` options are inside `aria-expanded='false'` listbox → use `get_attribute("textContent")` not `.text` (which returns "" for Selenium-hidden elements).
- 7 ITs in new `MySeaSpreadFormTemplateTest`: default_spread + reversals_pct context keys, all 6 options + both labels render, 2 dividers render w. expected text, default option carries aria-selected="true", cross's initial data-spread-shape="three-card", form col DECKS + buttons + reversal hint render.
Tests: 32/32 FT green across test_bill_my_sign + test_game_my_sea; 1048/1048 IT/UT green in 52s.
Card-draw mechanics (clicking a deck swatch deposits a card into the next empty slot; LOCK HAND commits the draw) defer to iter 4 — this iter ships the spread-selection + layout-shape switch UI; the buttons are stubs (LOCK HAND starts disabled, DEL is a placeholder).
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:23:25 -04:00
|
|
|
|
.sea-pos-cross { display: none; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-19 19:38:53 -04:00
|
|
|
|
.my-sea-cross[data-spread="desire-obstacle-solution"] {
|
|
|
|
|
|
.sea-pos-leave,
|
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
|
|
|
|
.sea-pos-cover,
|
2026-05-19 19:38:53 -04:00
|
|
|
|
.sea-pos-lay { display: none; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Celtic Cross variants (waite-smith / escape-velocity) — all positions
|
|
|
|
|
|
// visible by default. No `display: none` overrides needed.
|
|
|
|
|
|
|
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
|
|
|
|
// Position-name caption — re-appropriates the GRAVITY/LEVITY
|
|
|
|
|
|
// `.sea-stack-name` typographic look (_card-deck.scss line 1557):
|
|
|
|
|
|
// small uppercase letter-spaced w. a subtle scaleY stretch,
|
|
|
|
|
|
// --terUser ink at 0.6 opacity. No polarity coloring — these are
|
|
|
|
|
|
// spread-position labels, not deck identifiers.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Labels live OUTSIDE the .sea-card-slot (sibling, inside the crucifix
|
|
|
|
|
|
// cell or the cover/cross wrapper) so they survive SeaDeal._fillSlot's
|
|
|
|
|
|
// `slot.innerHTML = …` clobber on draw. Each label is absolute-
|
|
|
|
|
|
// positioned to nearly touch the slot's nearest border per the user-
|
|
|
|
|
|
// locked spec:
|
|
|
|
|
|
// crown / cover — above top border
|
|
|
|
|
|
// lay / cross — below bottom border
|
|
|
|
|
|
// leave — left of left border, rotated 90° CCW
|
|
|
|
|
|
// loom — right of right border, rotated 90° CW
|
2026-05-19 19:38:53 -04:00
|
|
|
|
.sea-pos-label {
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
font-weight: 600;
|
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
|
|
|
|
opacity: 1;
|
|
|
|
|
|
color: rgba(var(--seciUser), 1);
|
2026-05-19 19:38:53 -04:00
|
|
|
|
text-align: center;
|
|
|
|
|
|
pointer-events: none;
|
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
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Cells need `position: relative` so absolute label children anchor
|
|
|
|
|
|
// to them. `.sea-pos-core` already has `position: relative` per the
|
|
|
|
|
|
// existing rule in _card-deck.scss line 1311; the other crucifix
|
|
|
|
|
|
// cells need it added.
|
|
|
|
|
|
.my-sea-cross .sea-crucifix-cell { position: relative; }
|
|
|
|
|
|
|
|
|
|
|
|
// Above top border — overlaps slot's top edge by 0.1rem (per the
|
|
|
|
|
|
// `.sea-stack-name` "tuck under" treatment in _card-deck.scss:1564).
|
|
|
|
|
|
.sea-pos-crown > .sea-pos-label,
|
|
|
|
|
|
.sea-pos-cover > .sea-pos-label {
|
|
|
|
|
|
bottom: 100%;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translate(-50%, 0.1rem) scaleY(1.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Cover + cross labels dim w. their slots — they sit on top of the
|
|
|
|
|
|
// sig card so a vivid label would compete w. the sig at idle. Default
|
|
|
|
|
|
// 0.25 opacity matches the slot's faint dotted-outline at idle; the
|
|
|
|
|
|
// parent's :hover state (propagated up when the inside `.sea-card-
|
|
|
|
|
|
// slot:hover` fires per CSS hover-ancestor rules) boosts to the
|
|
|
|
|
|
// `.sea-pos-label` baseline 0.6, matching the slot's `--duoUser` mask
|
|
|
|
|
|
// reveal.
|
|
|
|
|
|
.sea-pos-cover > .sea-pos-label,
|
|
|
|
|
|
.sea-pos-cross > .sea-pos-label {
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
transition: opacity 0.15s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sea-pos-cover:hover > .sea-pos-label,
|
|
|
|
|
|
.sea-pos-cross:hover > .sea-pos-label {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Below bottom border — same `0.1rem` overlap but downward.
|
|
|
|
|
|
.sea-pos-lay > .sea-pos-label,
|
|
|
|
|
|
.sea-pos-cross > .sea-pos-label {
|
|
|
|
|
|
top: 100%;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translate(-50%, -0.1rem) scaleY(1.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Left of left border, rotated 90° CCW — text reads bottom-to-top.
|
|
|
|
|
|
// `writing-mode: vertical-rl` puts text top-to-bottom (CW); a 180°
|
|
|
|
|
|
// rotation flips it to read bottom-to-top (CCW), satisfying the user-
|
|
|
|
|
|
// locked "Leave: counterclockwise" spec.
|
|
|
|
|
|
//
|
|
|
|
|
|
// `scaleX(1.2)` (instead of the horizontal labels' scaleY) widens the
|
|
|
|
|
|
// character column (perpendicular to text-flow) — for vertical-rl
|
|
|
|
|
|
// labels, that's the visible "width" the user noticed had been lost
|
|
|
|
|
|
// at this angle. Without it, the rotated labels look squat.
|
|
|
|
|
|
.sea-pos-leave > .sea-pos-label {
|
|
|
|
|
|
right: 100%;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
writing-mode: vertical-rl;
|
|
|
|
|
|
transform: translate(0.1rem, -50%) rotate(180deg) scaleX(1.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Right of right border, rotated 90° CW — text reads top-to-bottom.
|
|
|
|
|
|
// Native `writing-mode: vertical-rl` direction; no extra rotation.
|
|
|
|
|
|
.sea-pos-loom > .sea-pos-label {
|
|
|
|
|
|
left: 100%;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
writing-mode: vertical-rl;
|
|
|
|
|
|
transform: translate(-0.1rem, -50%) scaleX(1.2);
|
My Sea form col + SPREAD dropdown w. 3-card/6-card section dividers — Sprint 5 iter 3 of My Sea roadmap — TDD
Picker phase form col: SPREAD combobox w. 6 spread options under 2 horizontal section dividers ("3-card spreads" / "6-card spreads"), reversal-% caption, GRAVITY + LEVITY deck swatches, LOCK HAND + DEL btns. Default = Situation, Action, Outcome (a 3-card spread). Selecting a 6-card spread (Celtic Cross Waite-Smith or Escape Velocity) swaps `.my-sea-cross[data-spread-shape]` from `three-card` to `six-card` — revealing the crown / lay / cross cells that the default 3-card variants hide.
Naming correction (user-locked): the spread itself is a "three-card spread" not a "three-card cross" — "cross" stays scoped to the Celtic Cross variants (6-card spreads). CSS class `.my-sea-cross` carries grid-container semantics regardless of which spread shape is active; the spread-vs-cross distinction lives at the spread-name layer only.
- **View** (gameboard/views.py): `my_sea` adds `default_spread = "situation-action-outcome"` + `reversals_pct = 25` context keys.
- **Template** (my_sea.html): renders all 7 cross cells (crown/leave/core+cover+cross/loom/lay) unconditionally + adds `data-spread-shape="three-card"` to `.my-sea-cross`. Form col DRY-reuses gameroom `_sea_overlay.html`'s `.sea-form-col` shape — `.sea-form-main` w. `.sea-field` (SPREAD label + reversal hint + custom combobox) + `.sea-stacks` (GRAVITY + LEVITY swatches) + `.sea-form-actions` (LOCK HAND + DEL). 6 options + 2 dividers in the combobox `<ul>`; dividers are `role="presentation"` so `combobox.js` skips them naturally. Inline IIFE listens for the hidden `<input id="id_sea_spread">`'s `change` event + sets `.my-sea-cross`'s `data-spread-shape` based on whether the value is in `['waite-smith', 'escape-velocity']`. No new combobox.js wiring — the existing module's `change`-bubbling contract feeds straight in.
- **SCSS** (_gameboard.scss):
- `.my-sea-cross[data-spread-shape="three-card"]` — single-row `"leave core loom"` grid + `display: none` on crown/lay/cross.
- `.my-sea-cross[data-spread-shape="six-card"]` — inherits the gameroom `.sea-cross`'s 3×3 grid + reveals all cells.
- `.sea-select-divider` — section header style mirrors `.kit-bag-label`'s small-uppercase-underlined-letter-spaced --quaUser/0.75 treatment but HORIZONTAL (kit-bag uses `writing-mode: vertical-rl`; dropdown menus are flat). `pointer-events: none` belt-and-braces against accidental click/hover.
- `.my-sea-form-col` — width-constrains the form col so the picker's cross + form sit side-by-side.
**Iter-2 contract updated** (cells in DOM, hidden via CSS for 3-card default):
- FT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_hides_six_card_only_positions_by_default` — asserts the 3 cells are in the DOM but `is_displayed() == False` so iter-3's spread switch can reveal them via CSS without re-rendering.
- IT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_renders_six_card_only_positions_for_spread_switch` — assertContains the classes (server now renders them unconditionally).
**Tests**:
- 4 FTs in new `MySeaSpreadFormTest`: combobox renders 6 options + 2 dividers w. correct labels, default is Situation/Action/Outcome (hidden input value + visible current-label span + cross's data-spread-shape), picking Celtic Cross flips data-spread-shape to six-card + reveals crown/lay/cross, form col carries DECKS swatches + LOCK HAND + DEL + reversal-% caption. Combobox `<li>` options are inside `aria-expanded='false'` listbox → use `get_attribute("textContent")` not `.text` (which returns "" for Selenium-hidden elements).
- 7 ITs in new `MySeaSpreadFormTemplateTest`: default_spread + reversals_pct context keys, all 6 options + both labels render, 2 dividers render w. expected text, default option carries aria-selected="true", cross's initial data-spread-shape="three-card", form col DECKS + buttons + reversal hint render.
Tests: 32/32 FT green across test_bill_my_sign + test_game_my_sea; 1048/1048 IT/UT green in 52s.
Card-draw mechanics (clicking a deck swatch deposits a card into the next empty slot; LOCK HAND commits the draw) defer to iter 4 — this iter ships the spread-selection + layout-shape switch UI; the buttons are stubs (LOCK HAND starts disabled, DEL is a placeholder).
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:23:25 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Section dividers inside the SPREAD combobox — labels "3-card spreads"
|
|
|
|
|
|
// / "6-card spreads" separating the option groups. Styled to echo the
|
|
|
|
|
|
// `.kit-bag-label` treatment (small uppercase underlined letter-spaced
|
|
|
|
|
|
// --quaUser) but horizontal rather than vertical (kit-bag uses writing-
|
|
|
|
|
|
// mode: vertical-rl; this is a flat dropdown).
|
|
|
|
|
|
.sea-select-list .sea-select-divider {
|
|
|
|
|
|
font-size: 0.55rem;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
|
letter-spacing: 0.12em;
|
|
|
|
|
|
color: rgba(var(--quaUser), 0.75);
|
|
|
|
|
|
padding: 0.4rem 0.6rem 0.2rem;
|
|
|
|
|
|
pointer-events: none; // not selectable; combobox.js skips it
|
|
|
|
|
|
// (no role=option), but belt-and-braces
|
|
|
|
|
|
// against accidental hover/click styles.
|
|
|
|
|
|
list-style: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Form col on my-sea — same DRY treatment as the gameroom sea-overlay
|
|
|
|
|
|
// `.sea-form-col` (handled in _card-deck.scss) but sits next to the
|
|
|
|
|
|
// picker's cross on a `--duoUser` page. Just constrain the width so it
|
|
|
|
|
|
// doesn't fight the cross for horizontal space.
|
|
|
|
|
|
.my-sea-form-col {
|
|
|
|
|
|
flex: 0 0 16rem;
|
|
|
|
|
|
max-width: 16rem;
|
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
|
|
|
|
|
|
|
|
|
|
// Portal the SPREAD dropdown out of `.sea-form-main`'s overflow
|
|
|
|
|
|
// clip — by default the gameroom's `.sea-form-main { overflow-y:
|
|
|
|
|
|
// auto }` (from _card-deck.scss:1424) keeps the modal contents
|
|
|
|
|
|
// scrollable, but for my-sea's much shorter form the dropdown gets
|
|
|
|
|
|
// clipped instead of overlaying the LOCK HAND / DEL btns below.
|
|
|
|
|
|
// Setting overflow visible here lets the absolute-positioned
|
|
|
|
|
|
// `.sea-select-list` extend past the form area + sit "above
|
|
|
|
|
|
// everything else" via its existing z-index: 100.
|
|
|
|
|
|
.sea-form-main {
|
|
|
|
|
|
overflow: visible;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Bump the dropdown z-index well above the picker's stacking ints
|
|
|
|
|
|
// (cover z:3, cross z:4, modal stage z:9999 only opens on draw
|
|
|
|
|
|
// anyway). 1000 sits above any in-page layer the user might be
|
|
|
|
|
|
// interacting w. when they open the SPREAD picker.
|
|
|
|
|
|
.sea-select-list {
|
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
|
}
|
2026-05-19 16:06:14 -04:00
|
|
|
|
}
|
My Sea client-side card draw + DEL + LOCK HAND visual lock — Sprint 5 iter 4a of My Sea roadmap — TDD
Two-step deposit flow lifted from gameroom sea.js's `_fillSlot`: click a polarity stack → FLIP btn appears on the active stack → click FLIP → top card pops from that polarity's pile and deposits into `DRAW_ORDER[currentSpread][_filled]`. Per-spread hand-size completion (3 for any three-card spread, 6 for Celtic Cross variants) flips LOCK HAND from disabled to enabled. DEL fully resets — every filled slot reverts to `.sea-card-slot--empty` w. its `.sea-pos-label` re-rendered, piles re-clone from the immutable server payload, LOCK HAND re-disables. Switching spreads mid-draw triggers the same reset (position-subset + draw-order both change). LOCK HAND click visually locks the picker (`.my-sea-picker--locked` + `.btn-disabled` on stacks/DEL/itself) — server persistence defers to iter 4b.
**Card source**: new `_my_sea_deck_data(user)` helper in `apps/gameboard/views.py` mirrors the gameroom `epic.views.sea_deck` JSON contract — same `_card_dict` shape (id/name/arcana/suit/number/corner_rank/suit_icon/name_group/name_title/qualifiers/reversed). Differences from the room version per spec lock:
- No `room` context; excludes only the **current user's significator** (no other seated gamers).
- Backup-deck fallthrough: `user.equipped_deck or DeckVariant.filter(slug='earthman').first()` — mirrors `personal_sig_cards`, keeps the no-deck-equipped path working.
- Reversal probability hardcoded at 0.25 per the iter 3 spec lock. (Future per-user config will share a helper w. the gameroom's `stack_reversal_probability`.)
Deck data flows in via `{{ sea_deck_data|json_script:"id_my_sea_deck" }}` — Django's built-in script-tag JSON embedder. JS reads `id_my_sea_deck`'s textContent on init + maintains `_levityPile` / `_gravityPile` working copies that shift one card per deposit. DEL re-clones from the immutable initial payload rather than re-fetching (server is stateless wrt this client-side dealing — same shuffle survives DEL).
`.my-sea-picker--locked` SCSS: `.sea-deck-stack.btn-disabled` gets `pointer-events: none; opacity: 0.5` per [[feedback_btn_disabled_pointer_events]] convention. Hand state freezes; only iter 4b's LOCK HAND POST can mutate the persisted state from there.
**FTs** (9 in new `MySeaCardDrawTest`, using a `_draw_one(picker, polarity)` helper that clicks the stack + waits for the FLIP btn to surface + clicks FLIP):
- deck JSON embedded w. two polarity halves, disjoint card ids;
- user significator excluded from both halves;
- first LEVITY draw lands in SAO's first slot (`.sea-pos-lay`) w. `.sea-card-slot--filled.sea-card-slot--levity` + corner_rank inside;
- second draw (GRAVITY) lands in SAO's second slot (`.sea-pos-cover`) w. polarity reflected;
- 3 draws complete the SAO hand → LOCK HAND `disabled` attribute drops;
- DEL resets every filled slot, LOCK HAND re-disables;
- LOCK HAND click adds `.my-sea-picker--locked` + `.btn-disabled` on the stacks;
- switching to MBS mid-draw wipes the in-progress hand.
**ITs** (6 in new `MySeaDeckDataViewTest`):
- context `sea_deck_data` has `levity` + `gravity` keys, both lists;
- user significator absent from both halves;
- halves are disjoint sets of card ids;
- card dicts carry `id` / `corner_rank` / `suit_icon` / `reversed` (bool); shape matches gameroom contract;
- template embeds via `<script id="id_my_sea_deck" type="application/json">`;
- no-equipped-deck users get the Earthman backup pile (not empty).
Tests: 41/41 FT green across test_bill_my_sign + test_game_my_sea; 1055/1055 IT/UT green in 53s.
**Deferred to iter 4b** (server persistence):
- `MySeaDraw` model (FK to user, spread name, JSON field for hand layout, created_at);
- LOCK HAND POST endpoint → commits the hand to the DB;
- 1/24h FREE DRAW quota check + the eventual FREE DRAW → DRAW SEA btn-label swap;
- Sig stage card full populate (name/qualifier/keywords/FYI/SPIN/FLIP) — currently corner rank + suit icon only.
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:02:20 -04:00
|
|
|
|
|
|
|
|
|
|
// LOCK HAND post-commit visual-lock: dim everything that mutates the
|
|
|
|
|
|
// hand. `.btn-disabled` is the project's existing soft-disabled
|
|
|
|
|
|
// treatment per [[feedback_btn_disabled_pointer_events]] — pointer-
|
|
|
|
|
|
// events:none + opacity reduction. The deck stacks aren't buttons
|
|
|
|
|
|
// themselves so we apply the class manually + the rule below ensures
|
|
|
|
|
|
// they stop responding to clicks.
|
|
|
|
|
|
.my-sea-picker--locked {
|
|
|
|
|
|
.sea-deck-stack.btn-disabled {
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
|
|
// SPREAD combobox lock — applied after the first deposit so the user
|
|
|
|
|
|
// can't switch spread mid-draw + scramble the in-progress hand's
|
|
|
|
|
|
// position-to-card mapping. DEL releases the lock by removing this
|
|
|
|
|
|
// class. Same `pointer-events: none` treatment as `.btn-disabled` per
|
|
|
|
|
|
// [[feedback_btn_disabled_pointer_events]].
|
|
|
|
|
|
.sea-select.sea-select--locked {
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
}
|