add payday anim

This commit is contained in:
JCEEE
2026-09-20 17:58:38 +01:00
parent 1609e11c48
commit 8e4eef06f5
@@ -248,6 +248,15 @@
// ─── Payday countdown (child view) ───
let nowMs = $state(Date.now());
let eowFired = $state(false);
// Nyan-style payday party: rainbow banner + confetti when payday unlocks.
let paydayParty = $state(false);
let partyTimer: ReturnType<typeof setTimeout> | undefined;
function startPaydayParty() {
paydayParty = true;
fire();
if (partyTimer) clearTimeout(partyTimer);
partyTimer = setTimeout(() => (paydayParty = false), 15000);
}
$effect(() => {
const id = setInterval(() => (nowMs = Date.now()), 1000);
return () => clearInterval(id);
@@ -269,6 +278,7 @@
if (!pbToken || !famId) return;
eowFired = true;
memberApi.payday(famId).catch(() => {});
startPaydayParty();
});
function paydayWeekStart(): string {
@@ -1273,6 +1283,16 @@
</div>
</div>
{/if}
{#if paydayParty}
<div class="payday-party" role="status" onclick={() => (paydayParty = false)}>
<span class="party-cat">🐖</span>
<div class="party-body">
<p class="party-title">PAYDAY!!</p>
<p class="party-sub">Your cash is ready — go collect it! 🎉</p>
</div>
<span class="party-stars">🌈⭐🌈</span>
</div>
{/if}
<!-- HERO -->
<header class="hero">
<svg
@@ -3107,6 +3127,91 @@
font-size: 0.8rem;
opacity: 0.85;
}
/* ── Payday party: nyan-style colour-cycling celebration ── */
.payday-party {
display: flex;
align-items: center;
gap: 0.8rem;
margin-bottom: 1rem;
padding: 1rem 1.2rem;
border-radius: 14px;
color: #fff;
cursor: pointer;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
background: linear-gradient(
90deg,
#ef4444,
#f97316,
#eab308,
#22c55e,
#3b82f6,
#a855f7,
#ec4899,
#ef4444
);
background-size: 300% 100%;
animation:
nyan-slide 3s linear infinite,
party-pop 0.45s ease-out;
box-shadow: 0 8px 24px rgba(168, 85, 247, 0.45);
}
@keyframes nyan-slide {
to {
background-position: 300% 0;
}
}
@keyframes party-pop {
from {
transform: scale(0.85);
}
to {
transform: scale(1);
}
}
.party-cat {
font-size: 2.6rem;
animation: party-bounce 0.55s ease-in-out infinite alternate;
}
@keyframes party-bounce {
from {
transform: translateY(-4px) rotate(-10deg);
}
to {
transform: translateY(4px) rotate(10deg);
}
}
.party-body {
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.party-title {
margin: 0;
font-size: 1.5rem;
font-weight: 900;
letter-spacing: 0.1em;
animation: party-wiggle 1s ease-in-out infinite;
}
@keyframes party-wiggle {
0%,
100% {
transform: rotate(-2deg);
}
50% {
transform: rotate(2deg);
}
}
.party-sub {
margin: 0;
font-size: 0.85rem;
opacity: 0.95;
}
.party-stars {
margin-left: auto;
font-size: 1.4rem;
animation: party-bounce 0.55s ease-in-out infinite alternate-reverse;
}
/* Mobile: declared last so it wins over the base .goal-mini (.kanban)
rules (same specificity, source order decides). */
@media (max-width: 639px) {