legacy-arrflix/web-overrides/popup-designs/preview.html
s8n 508fc42a1e next-ep popup: design A (Cinematic Strip) shipped to dev + designs A/B/C archived
Adds web-overrides/popup-designs/ with the 4-up preview (preview.html) and
three standalone candidate designs (a-strip.html / b-terminal.html /
c-minimal.html) so we can revisit the alternates later without re-running
the design generator. Owner picked A.

Design A is wired into Jellyfin's stock .upNextDialog by overriding its
CSS to a full-bleed bottom 26 % strip with white 'Start Now' CTA and a
custom SVG countdown ring that mirrors .upNextDialog-countdownText. The
DOM stays intact so Jellyfin's own countdown timer and click handlers on
.btnStartNow / .btnHide keep working untouched.

Shim is bracketed by NEXT-EP-POPUP-BEGIN / NEXT-EP-POPUP-END markers
inside the existing ARRFLIX-SHIM block in web-overrides/index-dev.html.
Only deployed to dev (dev.arrflix.s8n.ru) for spot-check; promote to
prod once verified by editing prod's index.html the same way and
redeploying via the nsenter trick.

Adds bin/revert-next-ep-popup.sh — sed-deletes between markers, defaults
to dev with --prod flag for prod target. Saves a timestamped backup and
prints the redeploy command.
2026-05-10 02:44:40 +01:00

760 lines
22 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ARRFLIX — Next-Episode popup designs</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&family=Bebas+Neue&family=Anton&display=swap" rel="stylesheet">
<style>
:root {
--arrflix-red: #E50914;
--arrflix-red-dark: #B00710;
--arrflix-bg: #0a0a0a;
--ink: #fff;
--ink-dim: rgba(255,255,255,0.55);
--ink-faint: rgba(255,255,255,0.3);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
background: #050505;
color: var(--ink);
font-family: 'Geist', system-ui, sans-serif;
font-feature-settings: "ss01", "ss02";
overflow-x: hidden;
}
.header {
position: sticky; top: 0; z-index: 100;
background: rgba(5,5,5,0.85);
backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(255,255,255,0.08);
padding: 18px 28px;
display: flex; align-items: center; justify-content: space-between;
}
.header h1 {
font-family: 'Bebas Neue', sans-serif;
letter-spacing: 0.1em;
font-size: 20px;
color: var(--arrflix-red);
}
.header .meta {
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ink-dim);
}
.picker {
display: flex; gap: 10px;
}
.picker button {
background: transparent;
border: 1px solid rgba(255,255,255,0.15);
color: var(--ink-dim);
padding: 8px 14px;
font-family: inherit;
font-size: 11px;
letter-spacing: 0.15em;
text-transform: uppercase;
cursor: pointer;
transition: all 0.15s ease;
}
.picker button:hover {
border-color: var(--arrflix-red);
color: var(--ink);
}
.picker button.active {
background: var(--arrflix-red);
border-color: var(--arrflix-red);
color: white;
}
.stage {
position: relative;
width: 100%;
aspect-ratio: 16/9;
max-height: calc(100vh - 80px);
background: black;
overflow: hidden;
}
.stage::before {
/* Star Wars credits backdrop */
content: '';
position: absolute; inset: 0;
background:
radial-gradient(ellipse 60% 40% at 50% 50%, rgba(20,20,40,0.4) 0%, transparent 70%),
black;
}
.stars {
position: absolute; inset: 0;
background-image:
radial-gradient(1px 1px at 20% 30%, white, transparent),
radial-gradient(1px 1px at 65% 50%, white, transparent),
radial-gradient(1px 1px at 80% 20%, rgba(255,255,255,0.7), transparent),
radial-gradient(2px 2px at 10% 70%, white, transparent),
radial-gradient(1px 1px at 85% 80%, rgba(255,255,255,0.8), transparent),
radial-gradient(1px 1px at 45% 90%, white, transparent),
radial-gradient(1px 1px at 30% 15%, rgba(255,255,255,0.5), transparent),
radial-gradient(1.5px 1.5px at 70% 75%, white, transparent),
radial-gradient(1px 1px at 5% 45%, white, transparent),
radial-gradient(1px 1px at 95% 60%, rgba(255,255,255,0.6), transparent),
radial-gradient(1px 1px at 25% 85%, white, transparent),
radial-gradient(1px 1px at 55% 25%, white, transparent),
radial-gradient(2px 2px at 50% 50%, rgba(255,255,255,0.4), transparent),
radial-gradient(1px 1px at 15% 92%, white, transparent),
radial-gradient(1px 1px at 88% 35%, white, transparent);
background-size: 100% 100%;
}
.credits {
position: absolute;
top: 18%;
left: 50%;
transform: translateX(-50%);
color: #4488dd;
font-family: 'Geist', sans-serif;
font-weight: 500;
font-size: 14px;
letter-spacing: 0.04em;
text-align: center;
line-height: 1.7;
opacity: 0.55;
text-shadow: 0 0 4px rgba(68,136,221,0.2);
}
.credits .row {
display: grid; grid-template-columns: 1fr 1fr; gap: 32px;
text-align: left;
margin-bottom: 18px;
}
.credits .row .role { text-align: right; opacity: 0.85; }
.credits .row .name { text-transform: uppercase; }
.credits .title-block { text-align: center; margin-bottom: 18px; }
/* Stop the star backdrop from comUSER-Eing */
.stage .popup-host {
position: absolute; inset: 0; z-index: 50;
pointer-events: none;
}
.stage .popup-host > * { pointer-events: auto; }
/* === DESIGN A — CINEMATIC STRIP === */
.design-a {
position: absolute;
bottom: 0; left: 0; right: 0;
height: 26%;
background: linear-gradient(to top, rgba(0,0,0,0.95) 50%, rgba(0,0,0,0.7) 80%, transparent);
padding: 28px 56px 32px;
display: flex;
align-items: center;
gap: 36px;
transform: translateY(0);
animation: slideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slideUp {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.design-a .ring {
position: relative;
width: 88px; height: 88px;
flex-shrink: 0;
}
.design-a .ring svg { transform: rotate(-90deg); }
.design-a .ring circle {
fill: none;
stroke-width: 3;
}
.design-a .ring .track { stroke: rgba(255,255,255,0.1); }
.design-a .ring .progress {
stroke: var(--arrflix-red);
stroke-dasharray: 264;
stroke-dashoffset: 67;
stroke-linecap: round;
transition: stroke-dashoffset 1s linear;
filter: drop-shadow(0 0 8px rgba(229, 9, 20, 0.5));
}
.design-a .ring .num {
position: absolute; inset: 0;
display: flex; align-items: center; justify-content: center;
font-family: 'JetBrains Mono', monospace;
font-size: 28px;
font-weight: 500;
letter-spacing: -0.02em;
}
.design-a .info { flex: 1; min-width: 0; }
.design-a .label {
font-size: 10px;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--arrflix-red);
margin-bottom: 8px;
font-weight: 600;
}
.design-a .title {
font-size: 28px;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 6px;
line-height: 1.1;
}
.design-a .episode-title {
font-size: 16px;
color: var(--ink-dim);
margin-bottom: 4px;
}
.design-a .meta {
font-size: 12px;
color: var(--ink-faint);
letter-spacing: 0.04em;
}
.design-a .actions {
display: flex; gap: 10px;
flex-shrink: 0;
}
.design-a .btn {
border: none;
background: white;
color: black;
padding: 14px 28px;
font-family: inherit;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.02em;
cursor: pointer;
display: flex;
align-items: center;
gap: 10px;
transition: all 0.15s ease;
}
.design-a .btn:hover { background: rgba(255,255,255,0.85); }
.design-a .btn-secondary {
background: rgba(255,255,255,0.08);
color: white;
backdrop-filter: blur(10px);
}
.design-a .btn-secondary:hover { background: rgba(255,255,255,0.16); }
/* === DESIGN B — TERMINAL CARD === */
.design-b {
position: absolute;
bottom: 32px; right: 32px;
width: 380px;
background: rgba(5,5,5,0.92);
backdrop-filter: blur(14px);
border: 1px solid rgba(255,255,255,0.12);
border-left: 2px solid var(--arrflix-red);
padding: 20px 22px;
font-family: 'JetBrains Mono', monospace;
animation: slideRight 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slideRight {
from { transform: translateX(20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.design-b .top-row {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 14px;
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
}
.design-b .tag {
color: var(--arrflix-red);
font-weight: 700;
}
.design-b .countdown {
color: var(--ink-dim);
}
.design-b .countdown strong {
color: var(--ink);
font-weight: 700;
}
.design-b hr {
border: none;
border-top: 1px dashed rgba(255,255,255,0.1);
margin: 12px 0;
}
.design-b .ep {
font-family: 'Geist', sans-serif;
font-size: 15px;
font-weight: 500;
margin-bottom: 4px;
color: var(--ink);
letter-spacing: -0.01em;
}
.design-b .ep-meta {
font-size: 11px;
color: var(--ink-faint);
letter-spacing: 0.06em;
margin-bottom: 16px;
}
.design-b .progress-bar {
height: 2px;
background: rgba(255,255,255,0.08);
margin-bottom: 18px;
position: relative;
}
.design-b .progress-bar::after {
content: '';
position: absolute;
top: 0; left: 0;
height: 100%;
width: 75%;
background: var(--arrflix-red);
box-shadow: 0 0 8px rgba(229,9,20,0.6);
animation: fillBar 17s linear forwards;
}
@keyframes fillBar {
to { width: 100%; }
}
.design-b .actions {
display: flex; gap: 8px;
}
.design-b .btn {
flex: 1;
background: transparent;
border: 1px solid rgba(255,255,255,0.18);
color: white;
padding: 9px 14px;
font-family: inherit;
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
cursor: pointer;
transition: all 0.15s ease;
}
.design-b .btn-primary {
background: var(--arrflix-red);
border-color: var(--arrflix-red);
}
.design-b .btn-primary:hover { background: var(--arrflix-red-dark); }
.design-b .btn-secondary:hover {
background: rgba(255,255,255,0.06);
border-color: rgba(255,255,255,0.3);
}
/* === DESIGN C — MINIMAL BAR === */
.design-c {
position: absolute;
bottom: 0; left: 0; right: 0;
pointer-events: auto;
animation: fadeUp 0.4s ease;
}
@keyframes fadeUp {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.design-c .progress-line {
height: 3px;
background: rgba(255,255,255,0.1);
position: relative;
}
.design-c .progress-line::after {
content: '';
position: absolute;
top: 0; left: 0;
height: 100%;
width: 75%;
background: var(--arrflix-red);
animation: fillBar 17s linear forwards;
}
.design-c .row {
background: linear-gradient(to bottom, transparent, rgba(0,0,0,0.85) 30%);
padding: 24px 56px 22px;
display: flex;
align-items: center;
gap: 24px;
}
.design-c .label {
font-family: 'Bebas Neue', sans-serif;
font-size: 13px;
letter-spacing: 0.32em;
color: var(--arrflix-red);
flex-shrink: 0;
}
.design-c .text {
flex: 1;
font-size: 14px;
color: var(--ink-dim);
letter-spacing: 0.02em;
}
.design-c .text strong {
color: var(--ink);
font-weight: 500;
margin-right: 8px;
}
.design-c .text .countdown {
color: var(--arrflix-red);
font-family: 'JetBrains Mono', monospace;
font-weight: 500;
margin-left: 8px;
}
.design-c .actions {
display: flex; gap: 16px;
}
.design-c .btn {
background: transparent;
border: none;
color: white;
font-family: inherit;
font-size: 12px;
letter-spacing: 0.2em;
text-transform: uppercase;
font-weight: 600;
cursor: pointer;
padding: 8px 0;
position: relative;
transition: color 0.15s ease;
}
.design-c .btn::after {
content: '';
position: absolute;
left: 0; bottom: 0;
height: 1px; width: 100%;
background: currentColor;
opacity: 0.3;
transition: opacity 0.15s ease;
}
.design-c .btn:hover::after { opacity: 1; }
.design-c .btn-primary { color: var(--arrflix-red); }
/* === DESIGN D — POSTER CARD === */
.design-d {
position: absolute;
bottom: 28px; right: 28px;
width: 460px;
background: linear-gradient(135deg, rgba(15,15,15,0.95), rgba(5,5,5,0.95));
backdrop-filter: blur(20px);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 4px;
overflow: hidden;
display: flex;
box-shadow: 0 20px 60px rgba(0,0,0,0.6),
0 0 0 1px rgba(229,9,20,0.15);
animation: slideRight 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.design-d .poster {
width: 160px;
flex-shrink: 0;
background:
linear-gradient(135deg, rgba(229,9,20,0.3), transparent 60%),
radial-gradient(circle at 30% 30%, #2a1a1a, #050505);
position: relative;
display: flex; align-items: flex-end;
padding: 16px;
}
.design-d .poster::before {
content: '';
position: absolute; inset: 0;
background-image:
radial-gradient(1px 1px at 20% 30%, white, transparent),
radial-gradient(1px 1px at 70% 50%, white, transparent),
radial-gradient(1px 1px at 40% 70%, white, transparent),
radial-gradient(1px 1px at 80% 80%, white, transparent),
radial-gradient(1px 1px at 50% 20%, white, transparent),
radial-gradient(1.5px 1.5px at 60% 60%, white, transparent);
opacity: 0.4;
}
.design-d .poster .ep-num {
position: relative;
font-family: 'Anton', sans-serif;
font-size: 64px;
line-height: 0.9;
color: var(--arrflix-red);
letter-spacing: -0.04em;
text-shadow: 0 4px 20px rgba(229,9,20,0.5);
}
.design-d .body {
flex: 1;
padding: 18px 20px 16px;
display: flex;
flex-direction: column;
}
.design-d .top {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 10px;
}
.design-d .label {
font-size: 9.5px;
letter-spacing: 0.32em;
text-transform: uppercase;
color: var(--arrflix-red);
font-weight: 700;
}
.design-d .timer {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
letter-spacing: 0.04em;
color: var(--ink-dim);
}
.design-d .timer strong {
color: var(--ink);
font-weight: 700;
}
.design-d .show {
font-size: 13px;
color: var(--ink-faint);
margin-bottom: 4px;
letter-spacing: 0.02em;
}
.design-d .ep-title {
font-size: 18px;
font-weight: 600;
letter-spacing: -0.02em;
line-height: 1.2;
margin-bottom: 12px;
}
.design-d .meta {
font-size: 11px;
color: var(--ink-faint);
letter-spacing: 0.06em;
margin-bottom: 14px;
text-transform: uppercase;
}
.design-d .meta .dot { margin: 0 8px; opacity: 0.5; }
.design-d .actions {
display: flex; gap: 8px;
margin-top: auto;
}
.design-d .btn {
flex: 1;
border: none;
padding: 11px 12px;
font-family: inherit;
font-size: 11.5px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
transition: all 0.15s ease;
border-radius: 2px;
}
.design-d .btn-primary {
background: var(--arrflix-red);
color: white;
box-shadow: 0 4px 14px rgba(229,9,20,0.3);
}
.design-d .btn-primary:hover { background: var(--arrflix-red-dark); }
.design-d .btn-secondary {
background: transparent;
color: var(--ink-dim);
border: 1px solid rgba(255,255,255,0.15);
}
.design-d .btn-secondary:hover {
color: var(--ink);
border-color: rgba(255,255,255,0.4);
}
/* hidden helper */
.hidden { display: none !important; }
/* design label */
.design-label {
position: absolute;
top: 24px; left: 28px;
z-index: 60;
font-family: 'Bebas Neue', sans-serif;
letter-spacing: 0.18em;
font-size: 13px;
color: rgba(255,255,255,0.5);
border-left: 2px solid var(--arrflix-red);
padding-left: 12px;
line-height: 1.4;
}
.design-label strong {
display: block;
color: white;
font-size: 18px;
letter-spacing: 0.1em;
}
.design-label .desc {
font-family: 'Geist', sans-serif;
font-size: 11px;
letter-spacing: 0.04em;
text-transform: none;
color: var(--ink-dim);
margin-top: 4px;
max-width: 300px;
}
.footer {
padding: 28px 56px 36px;
font-size: 12px;
color: var(--ink-faint);
letter-spacing: 0.04em;
line-height: 1.7;
border-top: 1px solid rgba(255,255,255,0.06);
}
.footer strong { color: var(--ink-dim); font-weight: 500; }
.footer kbd {
background: rgba(255,255,255,0.08);
border: 1px solid rgba(255,255,255,0.12);
border-radius: 3px;
padding: 2px 6px;
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
color: var(--ink-dim);
}
</style>
</head>
<body>
<header class="header">
<h1>ARRFLIX · NEXT-EPISODE POPUP</h1>
<div class="picker">
<button data-d="a" class="active">A · STRIP</button>
<button data-d="b">B · TERMINAL</button>
<button data-d="c">C · MINIMAL</button>
<button data-d="d">D · POSTER</button>
</div>
<div class="meta">PICK ONE · 2026-05-10</div>
</header>
<div class="stage">
<div class="stars"></div>
<div class="credits">
<div class="title-block" style="color:#4488dd; font-weight:600;">
Production Services Provided by CGCG, Inc.
</div>
<div class="row">
<div class="role">Lighting Director<br>Lighting Lead<br>Lighting Artists</div>
<div class="name">Chung-Kai Hsueh<br>Yin-Jung Huang<br>Jung-Tzu Chang · Po-Jui Chiu<br>Char Ho · Luna Jiang<br>Chuan-Sheng Lan · Po-Yu Li</div>
</div>
<div class="row">
<div class="role">Special Effects Director<br>Special Effects Artists</div>
<div class="name">Chia-Hung Chu<br>Jia-You Cai · Lin-Chi Chen<br>Cai-Jhu Li · Zhi-Hao Liu</div>
</div>
<div class="row">
<div class="role">Production Technology</div>
<div class="name">Indigo Tang · Joe Chang<br>I Chiang · Chih-Chiang Tsai</div>
</div>
</div>
<div class="design-label" id="dlabel">
<strong>A · CINEMATIC STRIP</strong>
<div class="desc">Full-bleed bottom strip. Big countdown ring. White CTA = Netflix muscle memory. Grand.</div>
</div>
<div class="popup-host">
<!-- DESIGN A -->
<div class="design-a" data-design="a">
<div class="ring">
<svg width="88" height="88" viewBox="0 0 88 88">
<circle class="track" cx="44" cy="44" r="42"/>
<circle class="progress" cx="44" cy="44" r="42"/>
</svg>
<div class="num">17</div>
</div>
<div class="info">
<div class="label">Up Next</div>
<div class="title">Star Wars: Maul · Shadow Lord</div>
<div class="episode-title">S1·E3 — Chapter 3: The Crucible</div>
<div class="meta">22 min · Ends at 2:49 AM</div>
</div>
<div class="actions">
<button class="btn">▶ Start Now</button>
<button class="btn btn-secondary">Hide</button>
</div>
</div>
<!-- DESIGN B -->
<div class="design-b hidden" data-design="b">
<div class="top-row">
<span class="tag">▎ Up Next</span>
<span class="countdown"><strong>17</strong>s</span>
</div>
<hr>
<div style="font-family:'Geist',sans-serif; font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 4px;">Star Wars: Maul · Shadow Lord · S1E3</div>
<div class="ep">Chapter 3: The Crucible</div>
<div class="ep-meta">22m / ends 02:49</div>
<div class="progress-bar"></div>
<div class="actions">
<button class="btn btn-primary">▶ Start now</button>
<button class="btn btn-secondary">Hide</button>
</div>
</div>
<!-- DESIGN C -->
<div class="design-c hidden" data-design="c">
<div class="progress-line"></div>
<div class="row">
<div class="label">UP NEXT</div>
<div class="text">
<strong>Star Wars: Maul · Shadow Lord</strong>S1·E3 — Chapter 3: The Crucible
<span class="countdown">00:17</span>
</div>
<div class="actions">
<button class="btn btn-primary">Start now ▶</button>
<button class="btn">Hide</button>
</div>
</div>
</div>
<!-- DESIGN D -->
<div class="design-d hidden" data-design="d">
<div class="poster"><div class="ep-num">E3</div></div>
<div class="body">
<div class="top">
<div class="label">Up Next</div>
<div class="timer">in <strong>17</strong>s</div>
</div>
<div class="show">Star Wars: Maul · Shadow Lord</div>
<div class="ep-title">Chapter 3: The Crucible</div>
<div class="meta">Season 1 <span class="dot">·</span> 22 min <span class="dot">·</span> Ends 02:49</div>
<div class="actions">
<button class="btn btn-primary">▶ Start now</button>
<button class="btn btn-secondary">Hide</button>
</div>
</div>
</div>
</div>
</div>
<div class="footer">
Pick one (<kbd>1</kbd><kbd>4</kbd> or click). When approved, design ships as a JS shim into <strong>web-overrides/index.html</strong> bracketed by <strong>NEXT-EP-POPUP-BEGIN/END</strong> markers, with one-shot revert via <strong>bin/revert-next-ep-popup.sh</strong> (matching the sub-label-shim pattern). The shim hides Jellyfin's stock card and renders the chosen design in its place when Jellyfin signals an upcoming episode.
</div>
<script>
const labels = {
a: ['A · CINEMATIC STRIP', 'Full-bleed bottom strip. Big countdown ring. White CTA = Netflix muscle memory. Grand.'],
b: ['B · TERMINAL CARD', 'Bottom-right card with monospace, dashed dividers, red accent line. Edgy, ARRFLIX-distinct.'],
c: ['C · MINIMAL BAR', 'Thin progress line + small text strip across bottom. Disappears into UX. Power-user.'],
d: ['D · POSTER CARD', 'Bottom-right card with episode-number tile + show + ep title + dual buttons. Polished, pragmatic.'],
};
const buttons = document.querySelectorAll('.picker button');
const designs = document.querySelectorAll('[data-design]');
const lbl = document.getElementById('dlabel');
function show(d) {
buttons.forEach(b => b.classList.toggle('active', b.dataset.d === d));
designs.forEach(el => el.classList.toggle('hidden', el.dataset.design !== d));
lbl.querySelector('strong').textContent = labels[d][0];
lbl.querySelector('.desc').textContent = labels[d][1];
// restart fillBar animations on switch (recreate elements)
designs.forEach(el => {
if (el.dataset.design === d) {
el.style.animation = 'none';
void el.offsetHeight;
el.style.animation = '';
}
});
}
buttons.forEach(b => b.addEventListener('click', () => show(b.dataset.d)));
document.addEventListener('keydown', (e) => {
const map = { '1':'a', '2':'b', '3':'c', '4':'d' };
if (map[e.key]) show(map[e.key]);
});
// animated countdown
let t = 17;
setInterval(() => {
t--;
if (t < 0) t = 17;
document.querySelector('.design-a .num').textContent = t;
document.querySelector('.design-a .ring .progress').style.strokeDashoffset = 67 + (264 - 67) * (1 - t/17);
document.querySelector('.design-b .top-row strong').textContent = t;
document.querySelector('.design-c .countdown').textContent = '00:' + String(t).padStart(2,'0');
document.querySelector('.design-d .timer strong').textContent = t;
}, 1000);
</script>
</body>
</html>