fix(call-to-play): expire elapsed calls clearly

Deadline completion previously shared the green Ready presentation with a full
roster and remained visible forever. An abandoned call therefore looked ready
to launch and required its creator to return and cancel it.

Give elapsed calls a distinct Time's up state and a five-minute grace period in
which the creator can start or extend them. After that, both the reducer and
peer store remove the call as a unit. Filled calls remain ready until their
deadline, and active calls continue to retain complete history for late joiners.

Test Plan:
- `just fmt` -- passed
- `just clippy` -- passed
- `just test` -- passed, 147 peer tests
- `just frontend-test` -- passed, 22 tests
- `just build` -- passed
- `git diff --cached --check` -- passed
This commit is contained in:
ddidderr committed 2026-07-21 22:56:02 +02:00
1 parent 640d81d919
commit cc7dacf6c3
9 files changed
+171 -40

No files matched your search

@@ -1919,6 +1919,7 @@
border-radius: 12px;
}
.ctp-card.is-done { border-color: color-mix(in srgb, var(--ok) 45%, var(--bd-2)); }
.ctp-card.is-expired { border-color: color-mix(in srgb, var(--danger) 45%, var(--bd-2)); }
.ctp-card.is-started { opacity: 0.6; }
.ctp-card.is-focused { border-color: var(--accent); animation: ctp-cardflash 1.4s ease-out 1; }
@keyframes ctp-cardflash {
@@ -1956,6 +1957,7 @@
.ctp-card-timer[data-urgency="mid"] { color: var(--warn); }
.ctp-card-timer[data-urgency="high"] { color: var(--danger); }
.ctp-card.is-done .ctp-card-timer { color: var(--ok); font-size: 14px; text-transform: uppercase; letter-spacing: 0.04em; }
.ctp-card.is-expired .ctp-card-timer { color: var(--danger); }
.ctp-cancel-link {
display: inline-flex; align-items: center; gap: 6px;
align-self: flex-start;
@@ -2210,7 +2212,8 @@
/* ─── Quick-bar status variants — LED + label + row tint per status:
SCHEDULED (neutral) · CALL TO PLAY (accent, pulsing) ·
STARTING SOON (amber, glowing) · READY (green, steady) ─── */
STARTING SOON (amber, glowing) · READY (green, steady) ·
TIME'S UP (red, steady) ─── */
.ctp-ticker[data-status="scheduled"] {
background: var(--bg-2);
border-color: var(--bd-2);
@@ -2232,9 +2235,15 @@
box-shadow: 0 0 14px -2px color-mix(in srgb, var(--ok) 35%, transparent);
}
.ctp-ticker[data-status="ready"]:hover { background: color-mix(in srgb, var(--ok) 17%, var(--bg-2)); }
.ctp-ticker[data-status="expired"] {
background: color-mix(in srgb, var(--danger) 8%, var(--bg-2));
border-color: color-mix(in srgb, var(--danger) 45%, var(--bd-2));
}
.ctp-ticker[data-status="expired"]:hover { background: color-mix(in srgb, var(--danger) 13%, var(--bg-2)); }
.ctp-ticker-dot[data-status="scheduled"] { background: var(--t-3); animation: none; box-shadow: none; }
.ctp-ticker-dot[data-status="soon"] { background: var(--warn); animation: ctp-tickerpulse-warn 1.6s ease-out infinite; }
.ctp-ticker-dot[data-status="ready"] { background: var(--ok); animation: none; box-shadow: 0 0 6px var(--ok); }
.ctp-ticker-dot[data-status="expired"] { background: var(--danger); animation: none; box-shadow: none; }
@keyframes ctp-tickerpulse-warn {
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--warn) 55%, transparent); }
70% { box-shadow: 0 0 0 6px transparent; }
@@ -2243,8 +2252,10 @@
.ctp-ticker-label[data-status="scheduled"] { color: var(--t-2); }
.ctp-ticker-label[data-status="soon"] { color: var(--warn); }
.ctp-ticker-label[data-status="ready"] { color: var(--ok); }
.ctp-ticker-label[data-status="expired"] { color: var(--danger); }
.ctp-ticker[data-status="soon"] .ctp-ticker-cta { color: var(--warn); }
.ctp-ticker[data-status="ready"] .ctp-ticker-cta { color: var(--ok); }
.ctp-ticker[data-status="expired"] .ctp-ticker-cta { color: var(--danger); }
/* ─── Quick-bar inline chat preview ─── */
.ctp-ticker-chat {