diff --git a/src/apps/epic/tests/integrated/test_views.py b/src/apps/epic/tests/integrated/test_views.py
index d3db315..ae76c71 100644
--- a/src/apps/epic/tests/integrated/test_views.py
+++ b/src/apps/epic/tests/integrated/test_views.py
@@ -4137,12 +4137,23 @@ class PickSeaUnifiedFeltTest(TestCase):
self.assertNotIn("sea-backdrop", content)
self.assertNotIn("sea-modal-wrap", content)
- def test_gaussian_spread_modal_with_preview_and_corner_nvm(self):
+ def test_options_on_felt_no_modal_no_nvm(self):
+ """The spread OPTIONS render directly on the felt (.sea-options-col) —
+ the combobox + OK + AUTO DRAW + DEL + the mini preview — mirroring Sky
+ Select's form-on-felt. The Gaussian modal + corner NVM are gone; OK
+ (.btn-confirm) shunts the options down to reveal the cross (user-spec
+ 2026-06-07)."""
content = self.client.get(self.url).content.decode()
- self.assertIn('id="id_sea_spread_modal"', content)
- self.assertIn("my-sea-spread-modal__backdrop", content)
- self.assertIn("sea-cross--preview", content) # mini preview
- self.assertIn('id="id_sea_cancel"', content) # corner NVM
+ self.assertIn("sea-options-col", content)
+ self.assertIn("sea-cross-col", content)
+ self.assertIn('id="id_sea_confirm_spread"', content) # OK .btn-confirm
+ self.assertIn("sea-cross--preview", content) # mini preview kept
+ self.assertIn('id="id_sea_action_btn"', content) # AUTO DRAW
+ self.assertIn('id="id_sea_del"', content) # DEL
+ # The Gaussian modal + corner NVM are gone.
+ self.assertNotIn('id="id_sea_spread_modal"', content)
+ self.assertNotIn("my-sea-spread-modal__backdrop", content)
+ self.assertNotIn('id="id_sea_cancel"', content)
def test_auto_draw_replaces_lock_hand(self):
content = self.client.get(self.url).content.decode()
diff --git a/src/apps/epic/views.py b/src/apps/epic/views.py
index ac64cfb..0f20604 100644
--- a/src/apps/epic/views.py
+++ b/src/apps/epic/views.py
@@ -687,6 +687,11 @@ def _role_select_context(room, user, seat_param=None):
)
# 6-card Celtic Cross is complete at 6 placed cards.
ctx["hand_complete"] = len(_sea_hand) >= 6
+ # Burger Sea sub-btn = the post-completion REOPEN affordance (active
+ # only once the spread is complete, like the Sky sub-btn after a save
+ # — NOT during drawing). So a reload of a complete sea renders it
+ # active; the live cascade activates it client-side otherwise.
+ ctx["sea_btn_active"] = ctx["hand_complete"]
# Sea-stage FLIP card-back — sourced from the SEAT's contributed deck,
# NOT request.user.equipped_deck (which `select_role` nulls out once
# the deck is contributed to the room → the back-img silently never
diff --git a/src/static_src/scss/_sky.scss b/src/static_src/scss/_sky.scss
index 833883d..ac25f15 100644
--- a/src/static_src/scss/_sky.scss
+++ b/src/static_src/scss/_sky.scss
@@ -85,6 +85,8 @@ html.sea-open {
inset: 0;
z-index: 5;
display: flex;
+ flex-direction: column;
+ overflow-y: auto; // the felt is the scroller (options ⇄ cross)
background: rgba(var(--duoUser), 1);
// Hidden until opened — pointer-events off so the hidden felt can't eat the
// DRAW SEA btn click beneath it.
@@ -97,6 +99,48 @@ html.sea-open .sea-page.sea-page--room {
pointer-events: auto;
}
+// ── Sea felt sections — mirror Sky Select's form→wheel scroll-snap ────────────
+// #id_sea_overlay is a layout passthrough so the two sections become direct flex
+// children of the scroller for scroll-snap (like `.sky-modal-body{display:contents}`).
+.sea-page--room .sea-overlay-content { display: contents; }
+
+.sea-page--room .sea-options-col,
+.sea-page--room .sea-cross-col {
+ flex: 1 0 auto;
+ min-height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative; // pins the deck stacks to the cross section
+}
+
+// The options form blends onto the --duoUser felt (transparent, content-sized) —
+// "the modal contents on green felt", not the modal's purple --priUser card.
+.sea-page--room .sea-options-col .sea-form-col {
+ background: transparent;
+ width: auto;
+ min-width: 14rem;
+}
+
+// Pre-confirm: only the options show (the cross is hidden), filling the aperture
+// (mirrors `body:not(.sky-saved) .sky-wheel-col{display:none}`).
+.sea-page--room:not(.sea-spread-chosen) .sea-cross-col { display: none; }
+
+// OK → the aperture flips into scroll-snap: the cross takes page 1 (order:-1) +
+// the options shunt to page 2. Mirrors the `body.sky-saved` block.
+.sea-page--room.sea-spread-chosen {
+ scroll-snap-type: y mandatory;
+
+ .sea-options-col,
+ .sea-cross-col {
+ scroll-snap-align: start;
+ scroll-snap-stop: always;
+ height: 100%;
+ flex: 0 0 auto;
+ }
+ .sea-cross-col { order: -1; }
+}
+
// Hide the position strip while the sea felt is up (same as the sky felt) so the
// felt reads as a clean homogeneous surface.
html.sea-open .position-strip {
diff --git a/src/templates/apps/gameboard/_partials/_sea_overlay.html b/src/templates/apps/gameboard/_partials/_sea_overlay.html
index be08783..f409619 100644
--- a/src/templates/apps/gameboard/_partials/_sea_overlay.html
+++ b/src/templates/apps/gameboard/_partials/_sea_overlay.html
@@ -1,158 +1,148 @@
{% load static %}
{% comment %}
-DRAW SEA — Sea Select felt + Gaussian spread modal, unified with my_sea.html
-(2026-06-07). Replaces the legacy dark single-modal. TWO surfaces, mirroring
-my_sea:
- • Sea Select FELT (.sea-page--room) — a --duoUser fill of the hex pane; the
- real Celtic-Cross spread deals here + the deck stacks. Opened by
- #id_pick_sea_btn (html.sea-open); the room gear's NVM returns to the hex.
- • Gaussian spread MODAL (#id_sea_spread_modal) — the .sea-select combobox
- (the two Celtic Cross 6-card options ONLY, per user-spec 2026-06-07) +
- AUTO DRAW + DEL + a miniaturized shape preview + a corner #id_sea_cancel
- NVM (closes back to the felt). Opened via the burger #id_sea_btn.
-Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
+DRAW SEA — Sea Select felt (2026-06-07), mirroring Sky Select's form→wheel
+scroll-snap. The felt starts with the spread OPTIONS (.sea-options-col, page 1):
+the .sea-select combobox (the two Celtic Cross spreads only) + a mini shape
+preview + OK + AUTO DRAW + DEL. Clicking OK confirms the spread → the options
+shunt DOWN and the spread CROSS (.sea-cross-col, w. the Gravity/Levity deck
+stacks) takes page 1 via scroll-snap (scroll down to find the options again).
+NO modal, NO corner NVM. The burger #id_sea_btn is the post-completion REOPEN
+affordance (active only once the spread is complete, like the sky btn) — NOT
+active during drawing. Each draw persists onto the seat's Character.celtic_cross
+via epic:sea_save. `?seat` threads the CARTE-selected seat onto the action URLs.
{% endcomment %}
-
-
+
- {# ── Felt cross — the REAL deal target (.my-sea-cross). Sig pins core; the #}
- {# six CC positions render w. labels + saved-hand pre-fill. The default #}
- {# spread is the polarity-matched Celtic Cross (or the saved one). #}
-
-
-
-
- {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="crown" saved=saved_by_position.crown crossing=False %}
-
-
-
- {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="leave" saved=saved_by_position.leave crossing=False %}
-
-
- {# Center significator — supply the card-FACE image when the sig's #}
- {# deck has one (mirrors my_sea); else fall through to the corner- #}
- {# rank + suit-icon text thumbnail (the tray sig stays text-only). #}
- {# my_tray_sig's deck_variant is the card's OWN deck (the Sig #}
- {# Select pick comes from the Role Select contributed deck via #}
- {# _room_deck_variant), so this is the right image source. #}
-
-
- {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="cover" saved=saved_by_position.cover crossing=False %}
-
-
-
- {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="cross" saved=saved_by_position.cross crossing=True %}
-
-
-
-
- {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="loom" saved=saved_by_position.loom crossing=False %}
-
-
-
- {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="lay" saved=saved_by_position.lay crossing=False %}
-
-
-
-
- {# ── Deck stacks — pinned bottom-right of the felt, so FLIP stays usable #}
- {# while the spread modal is closed. ALWAYS two stacks (Gravity + Levity), #}
- {# unlike my_sea / Sig Select: in the room's Sea Select phase the gamer may #}
- {# draw from EITHER populated half (sea_deck returns both), even a CARTE #}
- {# user whose monodeck is presented across the two polarity stacks #}
- {# (user-spec 2026-06-07). So the polarization-conditional shared partial #}
- {# is intentionally NOT used here. #}
-
-
- DECKS
-
-
-
-
- Gravity
-
-
-
-
-
- Levity
-
-
-
-
- {# ── Gaussian spread modal — opens on the burger #id_sea_btn. #}
-
-
-
- {# Corner NVM (top-right) — closes the modal back to the felt. #}
-
-
- {# Miniaturized spread preview — shape only, never dealt to. #}
- {% include "apps/gameboard/_partials/_sea_spread_preview.html" with preview_spread=sea_default_spread sig=my_tray_sig %}
-
-
-
-
-
-
{{ stack_reversal_pct|default:25 }}% reversals
- {# Two Celtic Cross 6-card spreads only (user-spec 2026-06-07). #}
-
-
+ {# ── OPTIONS section — page 1 initially; shunts to page 2 on OK. The select #}
+ {# form + preview + the OK / AUTO DRAW / DEL actions. NO deck stacks here. #}
+
+
+
+
+
+
{{ stack_reversal_pct|default:25 }}% reversals
+ {# Two Celtic Cross 6-card spreads only (user-spec 2026-06-07). #}
+
+
- {# AUTO DRAW (mid-draw) — commits the remaining hand in one POST + #}
- {# animates placement onto the felt. Disabled once complete (the #}
- {# room has no GATE VIEW yet — character creation ends at the #}
- {# Voronoi map, roadmap step 21). #}
-
-
+ {# Miniaturized spread preview — shape only, never dealt to. Shows #}
+ {# the chosen spread's shape before OK reveals the real cross. #}
+ {% include "apps/gameboard/_partials/_sea_spread_preview.html" with preview_spread=sea_default_spread sig=my_tray_sig %}
+
+
+
+ {# OK confirms the spread → shunts the options down + reveals the #}
+ {# cross (a regular .btn-confirm, NOT a big .btn-primary). #}
+
+ {# AUTO DRAW commits the remaining hand in one POST + animates onto #}
+ {# the cross (confirms the spread first if not yet). DEL clears. #}
+
+
+
+
+
+
+ {# ── CROSS section — hidden until OK; then order:-1 takes page 1. The REAL #}
+ {# deal target (.my-sea-cross) + the Gravity/Levity deck stacks (FLIP). #}
+
+
+
+
+
+ {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="crown" saved=saved_by_position.crown crossing=False %}
+
+
+
+ {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="leave" saved=saved_by_position.leave crossing=False %}
+
+
+ {# Center significator — supply the card-FACE image when the sig's #}
+ {# deck has one (mirrors my_sea); else the corner-rank + suit-icon #}
+ {# text thumbnail. my_tray_sig.deck_variant is the card's OWN deck #}
+ {# (Sig Select picks from the Role Select contributed deck). #}
+
+
+ {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="cover" saved=saved_by_position.cover crossing=False %}
+
+
+
+ {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="cross" saved=saved_by_position.cross crossing=True %}
+
+
+
+
+ {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="loom" saved=saved_by_position.loom crossing=False %}
+
+
+
+ {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="lay" saved=saved_by_position.lay crossing=False %}
+
+
+
+
+ {# ALWAYS two stacks (Gravity + Levity) — the gamer draws from EITHER #}
+ {# populated half (sea_deck returns both), even a CARTE monodeck. #}
+
+
+ DECKS
+
+
+
+
+ Gravity
+
+
+
+
+
+ Levity
- {# Sea stage — portaled big-card viewer (shared w. my_sea). #}
+ {# Sea stage — portaled big-card viewer (position:fixed, escapes the snap). #}
{% include "apps/gameboard/_partials/_sea_stage.html" %}
@@ -166,7 +156,6 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
if (!page || !overlay) return;
// ── Per-spread draw order + labels — the two Celtic Cross variants only.
- // WS / EV share the 6 positions but differ in draw order + diagonal labels.
var DRAW_ORDER = {
'waite-smith': ['cover', 'cross', 'crown', 'lay', 'loom', 'leave'],
'escape-velocity': ['cover', 'cross', 'lay', 'leave', 'crown', 'loom'],
@@ -181,6 +170,7 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
var preview = overlay.querySelector('.sea-cross--preview');
var actionBtn = overlay.querySelector('#id_sea_action_btn');
var delBtn = overlay.querySelector('#id_sea_del');
+ var okBtn = overlay.querySelector('#id_sea_confirm_spread');
var seaSelect = overlay.querySelector('[data-combobox][data-combobox-target="id_sea_spread"]');
if (!hidden || !cross) return;
@@ -206,7 +196,6 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
.catch(function () {});
}
- // ── FLIP affordance (CSS-driven via .sea-deck-stack--active).
function _hideOk() {
if (_activeStack) { _activeStack.classList.remove('sea-deck-stack--active'); _activeStack = null; }
}
@@ -215,16 +204,43 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
function _lockSpread() {
if (seaSelect) { seaSelect.classList.add('sea-select--locked'); seaSelect.setAttribute('aria-disabled', 'true'); }
}
- function _unlockSpread() {
- if (seaSelect) { seaSelect.classList.remove('sea-select--locked'); seaSelect.removeAttribute('aria-disabled'); }
+
+ // ── Confirm the chosen spread → shunt the options DOWN + reveal the cross.
+ // Mirrors Sky Select's form→wheel: the felt page gets `sea-spread-chosen` → the
+ // SCSS engages the scroll-snap (the cross takes page 1 via order:-1, the options
+ // shunt to page 2); lock the combobox; ease the scroller up to the cross.
+ // Idempotent. Triggered by OK and (defensively) by AUTO DRAW.
+ var _spreadChosen = page.classList.contains('sea-spread-chosen');
+ function _chooseSpread() {
+ if (_spreadChosen) return;
+ _spreadChosen = true;
+ page.classList.add('sea-spread-chosen');
+ _lockSpread();
+ _scrollToCross();
}
+ function _scrollToCross() {
+ // Pin to the options (now page 2) so the cross slides in from above, then ease
+ // up to the cross (page 1) — mirrors the sky felt's form-shunt reveal.
+ var opts = overlay.querySelector('.sea-options-col');
+ if (opts) page.scrollTop = opts.offsetTop;
+ if (page.scrollTop === 0) return;
+ var start = page.scrollTop, t0 = null, DUR = 320;
+ function step(now) {
+ if (t0 === null) t0 = now;
+ var t = Math.min(1, (now - t0) / DUR);
+ var u = 1 - t, e = 1 - u * u * u;
+ page.scrollTop = Math.max(0, start * (1 - e));
+ if (t < 1) requestAnimationFrame(step);
+ }
+ requestAnimationFrame(step);
+ }
+ if (okBtn) okBtn.addEventListener('click', _chooseSpread);
function _setHasDrawn(on) {
if (delBtn) { delBtn.classList.toggle('btn-disabled', !on); delBtn.innerHTML = on ? 'DEL' : '×'; }
}
function _setComplete(on, live) {
_locked = on;
- overlay.classList.toggle('my-sea-picker--locked', on);
overlay.querySelectorAll('.sea-deck-stack .sea-stack-ok').forEach(function (btn) {
btn.classList.toggle('btn-disabled', on);
btn.innerHTML = on ? '×' : 'FLIP';
@@ -234,17 +250,13 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
actionBtn.classList.toggle('btn-disabled', on);
}
_hideOk();
- // Live completion (manual FLIP of the 6th card / AUTO DRAW finishing) → run
- // the post-completion cascade. NOT on init (a reload of an already-complete
- // sea lands on the SEED MAP hex server-side, no animation).
if (on && live) _startSeaCascade();
}
// ── Post-completion cascade (mirrors CAST SKY's _startSaveCascade) ──────────
- // The 6-card spread completes → linger ~3s on the felt → the felt eases OUT
- // (revealing the table-hex) → DRAW SEA gives way to SEED MAP + the sea glow
- // fires (burger → sea_btn handoff) → +3s → SEED MAP eases IN. Same shape as
- // the CAST SKY → sky-btn glow → DRAW SEA sequence (user-spec 2026-06-07).
+ // 6 cards down → linger ~3s → the felt eases OUT (revealing the table-hex) →
+ // DRAW SEA gives way to SEED MAP + the sea glow fires (burger → sea_btn) → +3s
+ // → SEED MAP eases IN. Same shape as CAST SKY → sky-btn glow → DRAW SEA.
var _SEA_CASCADE_LINGER = 3000, _SEA_FELT_FADE = 500, _SEED_DELAY = 3000;
var _cascadeRun = false;
function _startSeaCascade() {
@@ -253,16 +265,16 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
setTimeout(_cascadeFeltOut, _SEA_CASCADE_LINGER);
}
function _cascadeFeltOut() {
- page.classList.add('sea-page--cascade-out'); // CSS fades the felt out
+ page.classList.add('sea-page--cascade-out');
setTimeout(function () {
document.documentElement.classList.remove('sea-open');
page.classList.remove('sea-page--cascade-out');
_restorePhaseBtns();
if (window.RoomViews && window.RoomViews.syncGear) window.RoomViews.syncGear();
- // DRAW SEA is stale → ease it out; keep the sea_btn active (reopen/review)
- // + start the sea glow handoff on the burger, concurrent w. the hex reveal.
var seaPhaseBtn = document.getElementById('id_pick_sea_btn');
if (seaPhaseBtn) seaPhaseBtn.classList.add('hex-phase-btn--out');
+ // The sea btn becomes the reopen/review affordance (active only NOW — the
+ // post-completion cue, like the sky btn) + the sea glow fires on the burger.
var seaBtn = document.getElementById('id_sea_btn');
if (seaBtn) seaBtn.classList.add('active');
var burgerBtn = document.getElementById('id_burger_btn');
@@ -272,7 +284,7 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
}
function _cascadeSeedMapIn() {
var seedBtn = document.getElementById('id_seed_map_btn');
- if (seedBtn) seedBtn.classList.remove('hex-phase-btn--out'); // eases in
+ if (seedBtn) seedBtn.classList.remove('hex-phase-btn--out');
}
function _collectHandFromDom() {
@@ -292,7 +304,6 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
return hand;
}
- // ── Persist — upsert the full current hand onto the seat's Character.
function _postLock(hand) {
if (!hand.length) return Promise.resolve(null);
return fetch(SEA_SAVE_URL, {
@@ -335,8 +346,9 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
});
overlay.addEventListener('click', _hideOk);
- // ── AUTO DRAW — commit remaining hand in ONE POST, then animate placement.
+ // ── AUTO DRAW — confirm the spread (reveal the cross) then commit + animate.
function _autoDraw() {
+ _chooseSpread(); // ensure the cross is revealed before dealing onto it
var order = _currentOrder();
var remaining = order.length - _filled;
if (remaining <= 0) return;
@@ -361,8 +373,6 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
function placeNext() {
if (idx >= autoEntries.length) { _setComplete(true, true); return; } // live → cascade
var e = autoEntries[idx++];
- // Gameroom Sea Select always has BOTH polarity stacks (no single-stack
- // fallback — that's a my_sea monodeck concern, not here).
var stack = overlay.querySelector('.sea-deck-stack--' + (e.isLevity ? 'levity' : 'gravity'));
if (stack) _showOk(stack);
setTimeout(function () {
@@ -404,7 +414,7 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
});
}
- // ── Spread switch → re-layout the felt cross + the preview + labels.
+ // ── Spread switch (pre-OK) → re-layout the felt cross + the preview + labels.
function syncLabels(spread) {
var labels = POSITION_LABELS[spread] || {};
cross.querySelectorAll('.sea-pos-label').forEach(function (el) {
@@ -419,10 +429,8 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
hidden.addEventListener('change', sync);
// ── Open / close the felt (DRAW SEA ⇄ gear NVM nav). On open: disable the
- // burger Text + Sky sub-btns (Text's swipe machine would drive into the
- // scroll-locked reelhouse; Sky would reopen the wheel mid-draw), light the
- // sea_btn, mark html.sea-entered (mutes the sky-saved glow henceforth), and
- // start the burger glow handoff. Server baseline restored on close.
+ // burger Text + Sky sub-btns. The sea btn is NOT activated here — it's the
+ // post-completion reopen affordance (see the cascade + the reopen binding).
var _disabledBtns = [];
function _disablePhaseBtns() {
_disabledBtns = [];
@@ -437,14 +445,8 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
}
function openSea() {
document.documentElement.classList.add('sea-open');
- // Once DRAW SEA is engaged the sky-saved glow stays muted (user-spec
- // 2026-06-07, conditions 2 & 3). The SEA glow-machine does NOT fire here —
- // mirroring Sky Select, it holds until the hand is complete (see the
- // completion-glow IIFE below), so drawing stays quiet.
document.documentElement.classList.add('sea-entered');
_disablePhaseBtns();
- var seaBtn = document.getElementById('id_sea_btn');
- if (seaBtn) seaBtn.classList.add('active');
if (window.RoomViews && window.RoomViews.syncGear) window.RoomViews.syncGear();
}
function closeSea() {
@@ -456,13 +458,20 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
var pickSeaBtn = document.getElementById('id_pick_sea_btn');
if (pickSeaBtn) pickSeaBtn.addEventListener('click', openSea);
+ // ── Sea sub-btn = post-completion REOPEN (active only once the spread is
+ // complete, like the sky btn). An ACTIVE click re-shows the felt to review the
+ // saved spread (window.openSeaFelt); inactive clicks fall to burger-btn.js's
+ // --priRd flash.
+ var seaSubBtn = document.getElementById('id_sea_btn');
+ if (seaSubBtn) {
+ seaSubBtn.addEventListener('click', function () {
+ if (!seaSubBtn.classList.contains('active')) return;
+ openSea();
+ });
+ }
+
// Re-seed SeaDeal's `_seaHand` from the server-rendered saved slots so they
- // stay clickable to RE-OPEN the stage after a refresh. Without this, SeaDeal's
- // in-memory `_seaHand` is only populated by openStage/register during the live
- // session → after a reload the overlay click handler short-circuits on
- // `if (!_seaHand[pos]) return` and the saved slots silently no-op (user-reported
- // 2026-06-07; same fix my_sea carries). The card payloads come from the deck
- // fetch (looked up by `data-card-id`); `reversed`/polarity are DOM-sourced.
+ // stay clickable to RE-OPEN the stage after a refresh.
function _seedSavedHand() {
if (!window.SeaDeal || !window.SeaDeal.seedHand) return;
var byId = {};
@@ -480,14 +489,10 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
SeaDeal.seedHand(seed);
}
- // ── Init — bind SeaDeal to the (possibly injected) overlay, seed the deck,
- // then re-seed the saved hand once the deck fetch resolves.
+ // ── Init — bind SeaDeal, seed the deck, then re-seed the saved hand.
if (window.SeaDeal && window.SeaDeal.reinit) SeaDeal.reinit();
_fetchDeck().then(_seedSavedHand);
_filled = cross.querySelectorAll('.sea-card-slot.sea-card-slot--filled').length;
- // Already-drawn sea hand → "in Sea Select or beyond" (user-spec 2026-06-07,
- // condition 3): mark sea-entered on load so the sky-saved glow stays muted
- // across reloads, not just after a live DRAW SEA click.
if (_filled > 0) document.documentElement.classList.add('sea-entered');
if (_filled >= _currentOrder().length) { _setComplete(true); _lockSpread(); _setHasDrawn(true); }
else if (_filled > 0) { _lockSpread(); _setHasDrawn(true); }
@@ -495,58 +500,17 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
}());
-{# ── Spread modal open/close — opens on the burger #id_sea_btn (active while #}
-{# the felt is up). Closes on the corner NVM (#id_sea_cancel), the Gaussian #}
-{# backdrop, Escape, or a guard-OK (so AUTO DRAW / DEL run against a closed #}
-{# modal). Mirrors my_sea's Phase-2 IIFE. #}
-
-
{# ── Sea glow handoff — the SEA glow-machine does NOT fire while drawing. The #}
{# post-completion CASCADE (in the picker IIFE above) STARTS it on the burger #}
{# as the felt eases out (DRAW SEA → SEED MAP), mirroring CAST SKY's burger → #}
{# sky-btn cue. This IIFE only carries the handoff: burger → sea_btn → #}
-{# .sea-select → end, so the user is led to review their sea. User-spec #}
-{# 2026-06-07. #}
+{# .sea-select → end, so the user is led to reopen + review their sea. #}