my-sea deck-stack + spread-card glow: unify hover-reveal / click-persist + --ninUser halo — TDD

Unify the glow/FLIP interaction across the owner picker (my_sea) + the read-only
spectator (my_sea_visit), then carry the same selection halo onto the spread
cards + deck-stack faces.

DECK STACK (user-spec 2026-05-30) — the owner revealed the FLIP only on click
(persisted) but never on hover; the spectator revealed it on hover but never
persisted. Now BOTH do both:
- `.sea-stack-ok` reveal is a single shared rule in _card-deck.scss — opacity
  fades in on hover/focus (ephemeral) OR via the JS-set `.sea-deck-stack--active`
  class (click-persist, same class the face-glow rides). The owner's inline
  `display` toggling is gone (`_showOk`/`_hideOk` just flip `--active`); the
  spectator's hover-only override in _gameboard.scss is removed.
- Interactivity stays gated on `--active`, NOT hover: hover is a purely VISUAL
  preview (matching the spectator's disabled FLIP). This preserves the owner's
  two-step deal — were the FLIP click-through on hover, a single stack-click
  would land on the centred FLIP + deal early (caught by the draw FT).
- Spectator persist wired in my_sea_visit.html (click a stack → `--active`,
  click elsewhere clears); its FLIP stays `.btn-disabled` (read-only).

SPREAD CARDS — the same hover-glow + active-persist now on EVERY spread card,
building on the cover/cross rules. The prior `.sea-card-slot--focused` glow
(0-1-0) was silently overridden by the filled-card drop-shadow ladder (up to
0-4-0) and never rendered (verified live); `!important` (consistent w. the
existing `opacity:1 !important` there) makes the halo win on hover + focus. The
halo is symmetric (rotation-invariant). No colour change — box-shadow only.

DECK FACE HALO — the levity + gravity stack glows now mirror the card halo's
tuned geometry (0.5rem blur / 0.5rem spread / 0.3 alpha), each in its own
polarity colour (--ninUser / --quaUser); single keeps its own tone.

Verified live in Firefox: deck FLIP persists on click + fades on hover; the card
halo wins over the drop-shadow on hover/focus across crown/cover/(reversed-)cross;
levity/gravity deck glows match the card halo. Draw FTs green (single-draw, hand-
completion, AUTO DRAW, auto-drawn-slot reopen) — the two-step deal + card focus
survive the display→opacity switch.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-30 01:11:39 -04:00
parent 7e876557aa
commit b7d871388e
4 changed files with 89 additions and 26 deletions

View File

@@ -1696,15 +1696,35 @@ $sea-card-h: 6.5rem;
.sea-pos-cover .sea-card-slot--visible { opacity: 0.3; animation: sea-cover-appear 2s ease; }
.sea-pos-cross .sea-card-slot--visible { opacity: 0.15; animation: sea-cross-appear 2s ease; }
// Hover: reveal fully (snappy)
.sea-pos-cover .sea-card-slot--visible:hover,
.sea-pos-cross .sea-card-slot--visible:hover { opacity: 1; transition: opacity 0.15s ease; }
// ── Spread-card hover-glow + active-persist (user-spec 2026-05-30) ──────────
// Mirror the deck-stack's hover / `--active` glow onto EACH spread card: the
// customary --ninUser selection halo appears on HOVER + PERSISTS on the active
// (`.sea-card-slot--focused`) two-tap selection sea.js sets. Builds on the
// cover/cross opacity-reveal rules above.
//
// `!important`: the filled-card drop-shadow ladder (lines ~498-542) climbs to
// 0-4-0, so on a hovered/focused rotated card it out-specifies a plain
// `.sea-card-slot--focused` box-shadow (0-1-0) — exactly why the PRIOR focused
// glow silently never rendered (verified 2026-05-30). The halo is symmetric so
// it's rotation-invariant; it replaces the tiny directional drop (the
// established focused intent). No colour change — box-shadow only.
$_sea-card-glow: 0 0 0.5rem 0.5rem rgba(var(--ninUser), 0.3), 0 0 0.4rem rgba(0, 0, 0, 0.85);
// Focused (first tap): persist at opacity 1 + selection glow until modal opens
// Hover: Cover/Cross bump opacity to full (they overlay the semi-transparent
// sig); every position then gains the halo.
.sea-pos-cover .sea-card-slot--visible:hover,
.sea-pos-cross .sea-card-slot--visible:hover { opacity: 1; }
.my-sea-cross .sea-card-slot--visible:hover {
box-shadow: $_sea-card-glow !important;
transition: opacity 0.15s ease, box-shadow 0.15s ease;
}
// Active/persist (first tap): hold full opacity + the halo until the second tap
// opens the stage (sea.js two-tap pattern, all positions).
.sea-card-slot--focused {
opacity: 1 !important;
transition: opacity 0.15s ease, box-shadow 0.15s ease;
box-shadow: 0 0 0.5rem 0.25rem rgba(var(--ninUser), 0.35), 0 0 0.4rem rgba(0, 0, 0, 0.85);
box-shadow: $_sea-card-glow !important;
}
// Cover + Cross — absolutely overlaid on the Sig card in .sea-pos-core
@@ -2000,7 +2020,27 @@ $sea-card-h: 6.5rem;
margin: 0 auto;
transform: translateY(-50%);
z-index: 5;
// FLIP reveal — UNIFIED across the owner picker (my_sea) + the read-only
// spectator (my_sea_visit), user-spec 2026-05-30. Hidden + non-interactive
// by default; HOVER / focus fades it in ephemerally; a CLICK PERSISTS it via
// the JS-set `.sea-deck-stack--active` class (the same class the face-glow
// rules below persist on). Was split: the owner toggled `display` on click
// only (no hover); the spectator faded on hover only (no persist).
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.sea-deck-stack:hover .sea-stack-ok,
.sea-deck-stack:focus-within .sea-stack-ok,
.sea-deck-stack--active .sea-stack-ok { opacity: 1; }
// Interactivity is gated on the PERSIST state (`--active`), NOT hover. Hover is
// a purely VISUAL preview — same as the spectator's disabled FLIP, whose "hover
// effect" is just the reveal. Keeping the FLIP click-through on hover preserves
// the owner's two-step deal: click the stack (→ `--active`) THEN click the FLIP.
// Were it clickable on hover, a single stack-click would land on the centred
// FLIP itself + deal early. The spectator's FLIP stays `.btn-disabled` (read-
// only) so it never becomes interactive even while persisted.
.sea-deck-stack--active .sea-stack-ok:not(.btn-disabled) { pointer-events: auto; }
.sea-deck-stack { gap: 0; } // remove gap so name slides under the face
@@ -2021,8 +2061,13 @@ $sea-card-h: 6.5rem;
// Deck backs — face-down pile colour identifies polarity
$_sea-shadow: 1px 2px 0 rgba(0,0,0,0.7), 0 4px 0 rgba(0,0,0,0.18), 2px 5px 5px rgba(0,0,0,0.5);
$_glow-levity: 0 0 0.8rem 0.15rem rgba(var(--ninUser), 0.6);
$_glow-gravity: 0 0 0.8rem 0.15rem rgba(var(--quaUser), 0.6);
// Levity + gravity stack glows mirror the spread-card halo's tuned geometry
// ($_sea-card-glow, user-tuned 2026-05-30: 0.5rem blur / 0.5rem spread / 0.3
// alpha) so the deck glows read identically to the card halo — each keeps its
// own polarity colour (--ninUser / --quaUser). Duplicated literally (not
// shared) so each stays independently tunable. (single keeps its own tone.)
$_glow-levity: 0 0 0.5rem 0.5rem rgba(var(--ninUser), 0.3);
$_glow-gravity: 0 0 0.5rem 0.5rem rgba(var(--ninUser), 0.3);
.sea-deck-stack--levity .sea-stack-face {
background: rgba(var(--terUser), 0.88);

View File

@@ -808,20 +808,11 @@ body.page-gameboard {
transform-origin: center;
}
// Read-only FLIP (disabled ×) — hidden until its stack is hovered/focused,
// then eases in: same reveal shape as the shared `flip-btn-base` mixin
// (opacity 0→1 over 0.3s ease), inlined here because _gameboard.scss is
// imported before _card-deck.scss (where the mixin lives). Stays non-
// interactive — `.btn-disabled` keeps pointer-events:none. The base
// `.sea-stack-ok` positioning is untouched (we only add the fade).
.sea-stack-ok {
opacity: 0;
transition: opacity 0.3s ease;
}
.sea-deck-stack:hover .sea-stack-ok,
.sea-deck-stack:focus-within .sea-stack-ok {
opacity: 1;
}
// FLIP reveal (hover-fade + click-persist) is now the SHARED `.sea-stack-ok`
// behaviour in _card-deck.scss — unified with the owner picker per user-spec
// 2026-05-30, so the visitor's prior hover-only override is gone. The
// spectator's click-persist (`.sea-deck-stack--active`) is wired in
// my_sea_visit.html (the read-only FLIP stays disabled throughout).
}
// ── Iter 4b: Brief banner + DEL guard portal ─────────────────────────────────

View File

@@ -357,10 +357,12 @@
return DRAW_ORDER[hidden.value] || [];
}
// FLIP visibility is now CSS-driven (opacity on hover/focus +
// the `.sea-deck-stack--active` persist class — unified w. the
// spectator page, user-spec 2026-05-30). `_showOk`/`_hideOk` just
// toggle `--active`; no more inline `display` juggling.
function _hideOk() {
if (_activeStack) {
var ok = _activeStack.querySelector('.sea-stack-ok');
if (ok) ok.style.display = 'none';
_activeStack.classList.remove('sea-deck-stack--active');
_activeStack = null;
}
@@ -369,8 +371,6 @@
_hideOk();
_activeStack = stack;
stack.classList.add('sea-deck-stack--active');
var ok = stack.querySelector('.sea-stack-ok');
if (ok) ok.style.display = '';
}
function _fillSlot(positionName, card, isLevity) {
@@ -529,7 +529,6 @@
});
var ok = stack.querySelector('.sea-stack-ok');
if (ok) {
ok.style.display = 'none';
ok.addEventListener('click', function (e) {
e.stopPropagation();
// Hand complete → FLIP is disabled. No draw.

View File

@@ -137,6 +137,34 @@
}
}());
</script>
{# Deck-stack persist — the spectator's FLIP is disabled (read-only), but #}
{# clicking a stack PERSISTS its glow + the revealed × via the shared #}
{# `.sea-deck-stack--active` class (same persist the owner picker uses, #}
{# user-spec 2026-05-30). Hover-reveal + the fade are the shared CSS; this #}
{# only adds the click-to-lock. Clicking another stack / elsewhere clears. #}
<script>
(function () {
var overlay = document.getElementById('id_sea_overlay');
if (!overlay) return;
var stacks = overlay.querySelectorAll('.sea-deck-stack');
if (!stacks.length) return;
function clearAll(except) {
stacks.forEach(function (s) {
if (s !== except) s.classList.remove('sea-deck-stack--active');
});
}
stacks.forEach(function (stack) {
stack.addEventListener('click', function (e) {
e.stopPropagation(); // don't let the overlay handler clear it
var on = stack.classList.contains('sea-deck-stack--active');
clearAll(stack);
stack.classList.toggle('sea-deck-stack--active', !on);
});
});
// A click anywhere else on the cross drops the persisted reveal.
overlay.addEventListener('click', function () { clearAll(null); });
}());
</script>
{# Async witness — a WS to `mysea_<owner>` pushes the owner's hand as each #}
{# card lands; SeaDeal.register fills the cross slot (+ seeds it clickable) #}
{# live, no refresh. A DEL (empty hand) re-empties the cleared slots. #}