292 lines
14 KiB
HTML
292 lines
14 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Spotify Roulette – Spiel</title>
|
||
|
||
<!-- Font -->
|
||
<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=Inter:wght@400;600;700&display=swap" rel="stylesheet">
|
||
|
||
<style>
|
||
:root{
|
||
--bg:#121212; --elev:#181818; --border:#282828; --text:#fff; --muted:#b3b3b3;
|
||
--accent:#1db954; --accent-press:#169e47; --glow:rgba(29,185,84,.25);
|
||
--radius:16px; --shadow:0 10px 30px rgba(0,0,0,.35);
|
||
}
|
||
*{box-sizing:border-box}
|
||
html,body{height:100%}
|
||
body{
|
||
margin:0; font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;
|
||
color:var(--text);
|
||
background:
|
||
radial-gradient(1200px 600px at 20% -10%, rgba(29,185,84,.15), transparent 60%),
|
||
radial-gradient(1000px 500px at 100% -20%, rgba(29,185,84,.12), transparent 70%),
|
||
var(--bg);
|
||
}
|
||
|
||
.container{max-width:1100px; margin:0 auto; padding:24px}
|
||
|
||
/* Header */
|
||
header{
|
||
display:flex; align-items:center; justify-content:space-between; gap:16px;
|
||
padding-bottom:12px; border-bottom:1px solid var(--border);
|
||
}
|
||
.brand{display:flex; align-items:center; gap:12px}
|
||
.logo{width:36px; height:36px; border-radius:50%; background:var(--accent); color:#121212;
|
||
display:grid; place-content:center; box-shadow:0 0 0 6px var(--glow)}
|
||
h1{margin:0; font-size:20px; letter-spacing:.2px}
|
||
.pill{display:inline-flex; align-items:center; gap:8px; padding:6px 10px; border-radius:999px;
|
||
border:1px solid var(--border); background:#101010; color:var(--muted); font-weight:600}
|
||
.pill b{color:#fff; letter-spacing:.08em}
|
||
|
||
.row{display:flex; gap:10px; align-items:center; flex-wrap:wrap}
|
||
.btn{border:0; padding:10px 14px; border-radius:999px; font-weight:700; cursor:pointer;
|
||
transition:transform .06s, filter .15s, background .2s}
|
||
.btn:active{transform:translateY(1px)}
|
||
.btn-primary{background:var(--accent); color:#0a0a0a}
|
||
.btn-primary:hover{filter:brightness(1.05)}
|
||
.btn-primary:active{background:var(--accent-press)}
|
||
.btn-ghost{background:transparent; color:var(--text); border:1px solid var(--border)}
|
||
.btn-ghost:hover{border-color:var(--muted)}
|
||
.muted{color:var(--muted)}
|
||
|
||
/* Grid */
|
||
.grid{display:grid; grid-template-columns:1.1fr .9fr; gap:24px; margin-top:20px}
|
||
@media (max-width:980px){.grid{grid-template-columns:1fr}}
|
||
|
||
/* Cards */
|
||
.card{background:var(--elev); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow)}
|
||
.card-hd{padding:18px 18px 8px; display:flex; align-items:center; justify-content:space-between}
|
||
.card-bd{padding:16px 18px 20px}
|
||
|
||
/* Now Playing */
|
||
.now{display:grid; grid-template-columns:96px 1fr; gap:16px; align-items:center}
|
||
.cover{width:96px; height:96px; border-radius:10px; background:#0d0d0d; border:1px solid var(--border);
|
||
overflow:hidden; display:grid; place-content:center}
|
||
.cover img{width:100%; height:100%; object-fit:cover}
|
||
.track-title{font-size:18px; font-weight:700; margin:0 0 2px}
|
||
.artists{color:var(--muted); margin:0 0 6px}
|
||
#songEmbed iframe{border-radius:10px; width:100%; height:80px; border:0}
|
||
|
||
/* Round area (Kreis) */
|
||
#roundArea[hidden]{display:none}
|
||
#options{
|
||
position:relative; width:100%; max-width:520px; aspect-ratio:1; margin:14px 0 6px;
|
||
border-radius:50%;
|
||
}
|
||
.player-option{
|
||
position:absolute;
|
||
left:50%;
|
||
top:50%;
|
||
transform-origin:center;
|
||
background:#0f0f0f; color:var(--text);
|
||
border:1px solid var(--border);
|
||
border-radius:999px; padding:8px 12px;
|
||
font-weight:700;
|
||
white-space:nowrap;
|
||
transition:filter .15s, transform .06s, background .2s, box-shadow .2s;
|
||
box-shadow:0 0 0 0 var(--glow);
|
||
}
|
||
.player-option:hover{filter:brightness(1.1)}
|
||
.player-option:active{transform:scale(.98)}
|
||
.player-option:disabled{opacity:.7}
|
||
.player-option.correct{background:var(--accent); color:#0a0a0a; box-shadow:0 0 0 6px var(--glow)}
|
||
|
||
/* Players + Scoreboard */
|
||
#playersList{list-style:none; padding:0; margin:0; display:grid;
|
||
grid-template-columns:repeat(auto-fill, minmax(200px,1fr)); gap:12px}
|
||
#playersList > *{display:flex; align-items:center; gap:10px; padding:10px;
|
||
border:1px solid var(--border); border-radius:12px; background:#101010}
|
||
|
||
#scoreboard{list-style:none; padding:0; margin:0}
|
||
#scoreboard li{display:flex; justify-content:space-between; padding:10px 4px; border-bottom:1px solid var(--border)}
|
||
|
||
/* Aside / Device select */
|
||
aside{background:transparent}
|
||
.section-title{font-weight:700; margin:0 0 10px}
|
||
#songList{list-style:none; padding:0; margin:0 0 14px; display:flex; flex-direction:column; gap:8px}
|
||
#songList li{padding:10px 12px; border:1px solid var(--border); border-radius:10px; background:#101010}
|
||
|
||
#deviceSelectArea label{color:var(--muted); font-size:13px}
|
||
#deviceSelect{
|
||
width:100%; margin-top:8px;
|
||
background:#0f0f0f; color:#fff; border:1px solid var(--border); border-radius:12px; padding:10px 12px; outline:none;
|
||
}
|
||
#deviceSelect:focus{border-color:var(--accent); box-shadow:0 0 0 3px var(--glow)}
|
||
|
||
/* Toast (Clipboard-Notice) */
|
||
.toast{position:fixed; left:50%; bottom:22px; transform:translateX(-50%);
|
||
background:#101010; color:#fff; border:1px solid var(--border);
|
||
padding:10px 14px; border-radius:999px; box-shadow:var(--shadow); z-index:50; display:none}
|
||
.toast.show{display:block}
|
||
|
||
/* Slide-out Drawer */
|
||
.drawer{position:fixed; top:0; right:0; height:100%; width:340px; background:#0f0f0f;
|
||
border-left:1px solid var(--border); transform:translateX(100%); transition:transform .25s ease;
|
||
z-index:60; display:flex; flex-direction:column}
|
||
.drawer.open{transform:translateX(0)}
|
||
.drawer header{padding:16px; border-bottom:1px solid var(--border); display:flex; justify-content:space-between; align-items:center}
|
||
.drawer .list{padding:12px 16px; overflow:auto}
|
||
.drawer .track{display:flex; align-items:center; gap:10px; padding:8px 6px; border-radius:10px}
|
||
.drawer .track:hover{background:#141414}
|
||
.drawer .tcover{width:40px; height:40px; border-radius:6px; background:#1a1a1a; border:1px solid var(--border)}
|
||
.drawer .meta{display:flex; flex-direction:column}
|
||
.drawer .meta b{font-size:13px}
|
||
.drawer .meta span{font-size:12px; color:var(--muted)}
|
||
|
||
/* === Pie-Slices === */
|
||
#options svg.options-svg { width:100%; height:auto; display:block; border-radius:50%; }
|
||
.wedge { fill:#181818; stroke: #ffffff; stroke-width:4; cursor:pointer; transition:filter .15s, fill .2s; }
|
||
.wedge:hover { filter:brightness(1.08); }
|
||
.wedge.selected { fill: #0d351c; }
|
||
/* .wedge.correct { fill:#1db954; } */
|
||
.wedge.correct { fill: var(--accent) !important; } /* grün */
|
||
.wedge.wrong { fill: #e22134 !important; } /* rot */
|
||
.wedge.disabled { pointer-events:none; opacity:.85; }
|
||
.wedge-label { fill:#fff; font-weight:700; font-size:35px; pointer-events:none; }
|
||
|
||
/* === Winner Overlay === */
|
||
.win-overlay{position:fixed; inset:0; display:none; align-items:center; justify-content:center;
|
||
background: radial-gradient(1000px 600px at 50% -10%, rgba(29,185,84,.18), transparent 60%), rgba(0,0,0,.72);
|
||
z-index:999}
|
||
.win-modal{width:min(720px,92vw); background:#121212; border:1px solid var(--border);
|
||
border-radius:24px; padding:28px 26px; box-shadow:0 24px 80px rgba(0,0,0,.6); text-align:center}
|
||
.win-trophy{width:88px; height:88px; margin:0 auto 10px; color:var(--accent); filter:drop-shadow(0 0 16px rgba(29,185,84,.35))}
|
||
.win-title{font-size:clamp(22px,4.5vw,28px); color:var(--muted); margin:6px 0 4px}
|
||
.win-name{font-size:clamp(32px,6vw,48px); font-weight:900; letter-spacing:.2px; margin:0 0 10px}
|
||
.win-points{color:#cfcfcf; margin:0 0 16px}
|
||
.win-actions{display:flex; gap:10px; justify-content:center; flex-wrap:wrap; margin-top:10px}
|
||
.confetti{position:absolute; top:-10vh; width:10px; height:16px; opacity:.9; animation:fall 3.2s linear infinite}
|
||
@keyframes fall{0%{transform:translateY(-10vh) rotate(0deg)}100%{transform:translateY(110vh) rotate(360deg)}}
|
||
|
||
/* Overlay-Scoreboard */
|
||
.win-leaderboard{ margin-top:14px; text-align:left }
|
||
.win-lead-title{ font-weight:800; color:#cfcfcf; margin:6px 0 8px }
|
||
.win-board{
|
||
list-style:none; padding:0; margin:0;
|
||
display:flex; flex-direction:column; gap:8px;
|
||
max-height:40vh; overflow:auto;
|
||
}
|
||
.win-board li{
|
||
display:flex; align-items:center; justify-content:space-between;
|
||
background:#0f0f0f; border:1px solid var(--border);
|
||
border-radius:12px; padding:10px 12px;
|
||
}
|
||
.win-board .left{ display:flex; align-items:center; gap:10px }
|
||
.win-board .rank{ width:28px; text-align:center; font-weight:900; color:#9f9f9f }
|
||
.win-board .name{ font-weight:700 }
|
||
.win-board .pts{ color:#cfcfcf; font-weight:700 }
|
||
.win-board li.winner{
|
||
border-color:var(--accent);
|
||
box-shadow:0 0 0 4px var(--glow);
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<header>
|
||
<div class="brand">
|
||
<!--TO DO eigenes Logo-->
|
||
<div class="logo" aria-hidden="true">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||
<path d="M12 2 2 7l10 5 10-5-10-5Zm10 7-10 5v9l10-5V9ZM2 9v9l10 5v-9L2 9Z"/>
|
||
</svg>
|
||
</div>
|
||
<h1>Spotify Roulette</h1>
|
||
</div>
|
||
<div class="row">
|
||
<span class="pill">Spiel-Code: <b id="gameId">—</b></span>
|
||
<button id="toggleSongList" class="btn btn-ghost" title="Zuletzt gehörte Songs">Songliste</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="grid">
|
||
<!-- LEFT: Now Playing + Runde -->
|
||
<section class="card">
|
||
<div class="card-hd">
|
||
<h2 style="margin:0">Now Playing</h2>
|
||
<div class="row">
|
||
<button id="startRound" class="btn btn-ghost">Runde starten</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-bd">
|
||
<section id="roundArea" hidden>
|
||
<div id="songEmbed" style="margin-bottom:12px"></div>
|
||
<div id="options"></div>
|
||
<div id="result" class="muted" style="margin-top:10px"></div>
|
||
<button id="nextRound" class="btn btn-primary" hidden>Nächster Song</button>
|
||
</section>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- RIGHT: Teilnehmer + Scoreboard -->
|
||
<section class="card">
|
||
<div class="card-hd">
|
||
<h2 style="margin:0">Teilnehmer</h2>
|
||
</div>
|
||
<div class="card-bd">
|
||
<ul id="playersList"></ul>
|
||
</div>
|
||
|
||
<div class="card-hd"><h2 style="margin:0">Scoreboard</h2></div>
|
||
<div class="card-bd">
|
||
<ul id="scoreboard"></ul>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<!-- ASIDE: Geladene Songs + Wiedergabegerät -->
|
||
<div id="deviceSelectArea"></div>
|
||
</aside>
|
||
</div>
|
||
|
||
<!-- Drawer -->
|
||
<aside class="drawer" id="songDrawer" aria-label="Zuletzt gehörte Songs">
|
||
<header>
|
||
<h3 style="margin:0">Zuletzt gehört</h3>
|
||
<button class="btn btn-ghost" id="closeDrawer">Schließen</button>
|
||
</header>
|
||
<div class="list" id="songListArea"></div>
|
||
</aside>
|
||
|
||
<!-- Toast -->
|
||
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
||
|
||
<!-- Winner Overlay -->
|
||
<div id="winnerOverlay" class="win-overlay" role="dialog" aria-modal="true" aria-labelledby="winName">
|
||
<div class="win-modal">
|
||
<div class="win-trophy" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" width="88" height="88" fill="currentColor">
|
||
<path d="M19 3h-3V2a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v1H5a1 1 0 0 0-1 1v2a5 5 0 0 0 5 5h.1A5.98 5.98 0 0 0 11 14.9V17H8a1 1 0 1 0 0 2h8a1 1 0 1 0 0-2h-3v-2.1A5.98 5.98 0 0 0 15.9 11H16a5 5 0 0 0 5-5V4a1 1 0 0 0-1-1ZM6 6V5h2v2a3 3 0 0 1-3-1Zm14 0a3 3 0 0 1-3 1V5h3v1Z"/>
|
||
</svg>
|
||
</div>
|
||
<p class="win-title">Gewonnen!</p>
|
||
<h2 id="winName" class="win-name">—</h2>
|
||
<p id="winPoints" class="win-points"></p>
|
||
|
||
<!-- Leaderboard im Overlay -->
|
||
<div class="win-leaderboard">
|
||
<div class="win-lead-title">Leaderboard</div>
|
||
<ol id="winBoard" class="win-board"></ol>
|
||
</div>
|
||
|
||
<div class="win-actions">
|
||
<!-- <button id="winNext" class="btn btn-primary">Neue Runde</button> -->
|
||
<button id="winClose" class="btn btn-ghost">Schließen</button>
|
||
<button id="winShare" class="btn btn-ghost">Teilen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Module -->
|
||
<script type="module" src="/js/utils.js"></script>
|
||
<script type="module" src="/js/start-round.js"></script>
|
||
<script type="module" src="/js/device-select.js"></script> <!-- füllt #deviceSelectArea -->
|
||
<script type="module" src="/js/game.js"></script>
|
||
</body>
|
||
</html>
|