Add seeded deals and card movement animations
Every game is now dealt from a seed carried in the URL as `?seed=`, shown in the menu, and re-dealable by typing it in, so a deal can be shared or replayed. Seeds are hashed into a mulberry32 stream, independent of the Python shuffle bank. Cards previously teleported between zones: the only motion in the client was the hover lift and the legal-target pulse. Cards are keyed by card id, so a card that just moved into a zone mounts there and now animates in, with the motion disabled under prefers-reduced-motion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LmyprzuzanXRhpomc3Ga1i
This commit is contained in:
@@ -149,6 +149,19 @@ button:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }
|
||||
.menu-popover button:hover:not(:disabled) { border-color: var(--gold); }
|
||||
.menu-popover button:disabled { opacity: 0.3; cursor: default; }
|
||||
.menu-popover span { padding: 7px 4px 2px; color: #757a76; font: 8px Arial, sans-serif; letter-spacing: 0.08em; }
|
||||
.menu-seed { display: grid; gap: 4px; }
|
||||
.menu-seed span { padding: 4px 4px 0; color: var(--gold); }
|
||||
.menu-seed input {
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
border-radius: 4px;
|
||||
background: #0a1210;
|
||||
color: #ddd9ce;
|
||||
font: 11px/1 Arial, sans-serif;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.menu-seed input:focus-visible { border-color: var(--gold); outline: none; }
|
||||
|
||||
.table-center {
|
||||
position: absolute;
|
||||
@@ -276,6 +289,34 @@ button.lane__discard:disabled { opacity: 1; }
|
||||
.lane__discard.is-chosen .card { box-shadow: 0 0 0 2px var(--gold), 0 0 28px rgba(201, 163, 75, 0.25); }
|
||||
@keyframes target-pulse { 50% { filter: brightness(1.22); } }
|
||||
|
||||
/* Cards are keyed by card id, so only a card that just moved into a zone
|
||||
mounts there — these entry animations are its motion between zones. */
|
||||
@keyframes card-place {
|
||||
from { transform: translateY(-30px) scale(0.94) rotate(-1.5deg); opacity: 0; }
|
||||
to { transform: none; opacity: 1; }
|
||||
}
|
||||
@keyframes card-draw {
|
||||
from { transform: translateY(34px) scale(0.9); opacity: 0; }
|
||||
to { transform: none; opacity: 1; }
|
||||
}
|
||||
/* `backwards`, not `both`: a lingering final transform would outrank the
|
||||
hover-lift and .card--selected transforms once the animation ends. */
|
||||
.card-stack .card,
|
||||
.lane__discard .card { animation: card-place 0.26s ease-out backwards; }
|
||||
.human-hand .card,
|
||||
.opponent-hand .card { animation: card-draw 0.3s ease-out backwards; }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.card-stack .card,
|
||||
.lane__discard .card,
|
||||
.human-hand .card,
|
||||
.opponent-hand .card { animation: none; }
|
||||
.lane__zone.is-target,
|
||||
.lane__discard.is-target,
|
||||
.lane__discard.is-draw-target,
|
||||
.deck-stack.is-draw-target { animation: none; }
|
||||
}
|
||||
|
||||
.card {
|
||||
--card-color: #888;
|
||||
position: relative;
|
||||
|
||||
Reference in New Issue
Block a user