Sea Select: post-completion cascade — felt eases out → DRAW SEA → SEED MAP — TDD

Mirror CAST SKY's post-save cascade for the sea phase. When the 6-card spread
completes (live FLIP of the 6th card / AUTO DRAW finishing): linger ~3s on the
felt → the felt eases OUT (`.sea-page--cascade-out`, revealing the table-hex) →
DRAW SEA gives way to SEED MAP + the sea glow fires on the burger (handoff →
sea_btn) → +3s → SEED MAP eases IN. Same shape as CAST SKY → sky-btn glow →
DRAW SEA.

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-06-07 23:03:17 -04:00
parent 0f57cae50d
commit cf84fdc992
4 changed files with 97 additions and 34 deletions

View File

@@ -222,7 +222,7 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
function _setHasDrawn(on) {
if (delBtn) { delBtn.classList.toggle('btn-disabled', !on); delBtn.innerHTML = on ? 'DEL' : '×'; }
}
function _setComplete(on) {
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) {
@@ -234,6 +234,45 @@ 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).
var _SEA_CASCADE_LINGER = 3000, _SEA_FELT_FADE = 500, _SEED_DELAY = 3000;
var _cascadeRun = false;
function _startSeaCascade() {
if (_cascadeRun) return;
_cascadeRun = true;
setTimeout(_cascadeFeltOut, _SEA_CASCADE_LINGER);
}
function _cascadeFeltOut() {
page.classList.add('sea-page--cascade-out'); // CSS fades the felt 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');
var seaBtn = document.getElementById('id_sea_btn');
if (seaBtn) seaBtn.classList.add('active');
var burgerBtn = document.getElementById('id_burger_btn');
if (burgerBtn) burgerBtn.classList.add('glow-handoff');
setTimeout(_cascadeSeedMapIn, _SEED_DELAY);
}, _SEA_FELT_FADE);
}
function _cascadeSeedMapIn() {
var seedBtn = document.getElementById('id_seed_map_btn');
if (seedBtn) seedBtn.classList.remove('hex-phase-btn--out'); // eases in
}
function _collectHandFromDom() {
@@ -288,7 +327,7 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
_filled++;
if (_filled === 1) { _lockSpread(); _setHasDrawn(true); }
_postLock(_collectHandFromDom());
if (_filled >= order.length) _setComplete(true);
if (_filled >= order.length) _setComplete(true, true); // live → cascade
}
_hideOk();
});
@@ -320,7 +359,7 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
_setHasDrawn(true);
var idx = 0;
function placeNext() {
if (idx >= autoEntries.length) { _setComplete(true); return; }
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).
@@ -493,12 +532,12 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
}());
</script>
{# ── Completion glow cue (mirrors Sky Select) — the SEA glow-machine does NOT #}
{# fire while drawing. It starts only once ALL slots are drawn (the action btn #}
{# flips to data-state="complete", DRAW SEA gives way to SEED MAP), then hands #}
{# off burger → sea_btn → .sea-select so the user can review their sea. The #}
{# cue starts on the live completion TRANSITION (manual FLIP or AUTO DRAW), not #}
{# on a reload of an already-complete sea. User-spec 2026-06-07. #}
{# ── 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. #}
<script>
(function () {
var burgerBtn = document.getElementById('id_burger_btn');
@@ -507,16 +546,9 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
if (!burgerBtn || !seaBtn || !modal) return;
var seaSelect = modal.querySelector('.sea-select');
var actionBtn = document.getElementById('id_sea_action_btn');
if (!seaSelect || !actionBtn) return;
if (!seaSelect) return;
var started = false, glowDone = false;
function startGlow() {
if (started || glowDone) return;
started = true;
burgerBtn.classList.add('glow-handoff');
}
var glowDone = false;
function endGlow() {
glowDone = true;
burgerBtn.classList.remove('glow-handoff');
@@ -524,7 +556,8 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
seaSelect.classList.remove('glow-handoff');
}
// Handoff on clicks: burger → sea_btn → .sea-select → end.
// Handoff on clicks: burger → sea_btn → .sea-select → end. (The cascade adds
// `glow-handoff` to the burger to begin the sequence.)
burgerBtn.addEventListener('click', function () {
if (glowDone || !burgerBtn.classList.contains('glow-handoff')) return;
burgerBtn.classList.remove('glow-handoff');
@@ -539,14 +572,5 @@ Each draw persists onto the seat's Character.celtic_cross via epic:sea_save.
if (glowDone) return;
endGlow();
});
// Start the cue on the live completion transition (data-state → "complete").
var obs = new MutationObserver(function (mutations) {
for (var i = 0; i < mutations.length; i++) {
if (mutations[i].attributeName !== 'data-state') continue;
if (actionBtn.dataset.state === 'complete') startGlow();
}
});
obs.observe(actionBtn, { attributes: true, attributeFilter: ['data-state'] });
}());
</script>