vod jam and earl vod.atverkackt.de
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

Clean up styling and other things

+270 -249
+1 -1
README.md
··· 1 - # vod jam with Earl 1 + # vod Jam and Earl 2 2 3 3 You've been tasked with transporting the VODs back from AtmosphereConf when something goes wrong with your ship. Your new task is to collect all the VODs as quickly as possible so they can be streamed! 4 4
+11 -3
scripts/generate-speaker-sprites.mjs
··· 8 8 */ 9 9 10 10 import sharp from 'sharp'; 11 - import { mkdir, writeFile } from 'node:fs/promises'; 11 + import { mkdir, writeFile, access } from 'node:fs/promises'; 12 12 import { join } from 'node:path'; 13 13 14 14 const BSKY_PUBLIC_API = 'https://public.api.bsky.app'; ··· 91 91 ); 92 92 93 93 const pixelated = await sharp(buffer) 94 + .resize(8, 8, { kernel: sharp.kernel.lanczos3 }) 94 95 .resize(24, 24, { kernel: sharp.kernel.nearest }) 95 96 .composite([{ input: mask, blend: 'dest-in' }]) 96 - .png() 97 + .webp({ lossless: true }) 97 98 .toBuffer(); 98 99 99 100 return pixelated; ··· 119 120 120 121 for (const handle of handles) { 121 122 process.stdout.write(` ${handle}... `); 123 + const outPath = join(OUTPUT_DIR, `${handle}.webp`); 124 + try { 125 + await access(outPath); 126 + console.log('already exists, skipped'); 127 + skipped++; 128 + continue; 129 + } catch { /* file does not exist, proceed */ } 122 130 try { 123 131 const avatarUrl = await fetchAvatar(handle); 124 132 if (!avatarUrl) { ··· 134 142 continue; 135 143 } 136 144 137 - await writeFile(join(OUTPUT_DIR, `${handle}.png`), png); 145 + await writeFile(join(OUTPUT_DIR, `${handle}.webp`), png); 138 146 console.log('ok'); 139 147 success++; 140 148 } catch (e) {
+4 -4
src/lib/Footer.svelte
··· 15 15 margin: 4px 0; 16 16 animation: font-switch 4s steps(1) infinite; 17 17 font-size: 0.75rem; 18 - color: #E8E0FF; 18 + color: var(--color-lavender); 19 19 opacity: 0.7; 20 20 text-shadow: 21 21 -1px -1px 0 rgba(11, 14, 23, 0.8), ··· 31 31 } 32 32 33 33 .credit-link { 34 - color: #E8E0FF; 34 + color: var(--color-lavender); 35 35 text-decoration: underline; 36 - text-decoration-color: #FF6B2C; 36 + text-decoration-color: var(--color-orange); 37 37 transition: color 0.15s; 38 38 } 39 39 40 40 .credit-link:hover { 41 - color: #FF6B2C; 41 + color: var(--color-orange); 42 42 } 43 43 </style>
+1 -1
src/lib/MeshBackground.svelte
··· 4 4 .tiled-bg { 5 5 position: fixed; 6 6 inset: 0; 7 - z-index: -2; 7 + z-index: var(--z-background); 8 8 background: url('/background.webp') repeat; 9 9 background-size: 256px 256px; 10 10 image-rendering: pixelated;
+8 -8
src/lib/VideoCard.svelte
··· 233 233 display: block; 234 234 animation: font-switch 4s steps(1) infinite; 235 235 font-size: 1.4rem; 236 - color: #FF6B2C; 236 + color: var(--color-orange); 237 237 font-weight: bold; 238 238 letter-spacing: 0.5px; 239 239 margin-bottom: 8px; ··· 248 248 position: relative; 249 249 width: 100%; 250 250 aspect-ratio: 16 / 9; 251 - background: #0B0E17; 251 + background: var(--color-space-black); 252 252 display: flex; 253 253 align-items: center; 254 254 justify-content: center; ··· 330 330 } 331 331 .scrub-bar-fill { 332 332 height: 100%; 333 - background: #FF6B2C; 333 + background: var(--color-orange); 334 334 transition: width 0.05s linear; 335 335 } 336 336 ··· 343 343 font-size: 0.9rem; 344 344 animation: font-switch 4s steps(1) infinite; 345 345 font-weight: 700; 346 - color: #E8E0FF; 346 + color: var(--color-lavender); 347 347 line-height: 1.3; 348 348 display: -webkit-box; 349 349 -webkit-line-clamp: 2; ··· 362 362 margin: 4px 0 0; 363 363 animation: font-switch 4s steps(1) infinite; 364 364 font-size: 0.8rem; 365 - color: #E8E0FF; 365 + color: var(--color-lavender); 366 366 opacity: 0.8; 367 367 text-decoration: underline; 368 - text-decoration-color: #FF6B2C; 368 + text-decoration-color: var(--color-orange); 369 369 transition: color 0.15s; 370 370 text-shadow: 371 371 -1px -1px 0 rgba(11, 14, 23, 0.8), ··· 375 375 } 376 376 377 377 .creator:hover { 378 - color: #FF6B2C; 378 + color: var(--color-orange); 379 379 } 380 380 381 381 .date { 382 382 margin: 2px 0 0; 383 383 animation: font-switch 4s steps(1) infinite; 384 384 font-size: 0.75rem; 385 - color: #FF6B2C; 385 + color: var(--color-orange); 386 386 font-style: italic; 387 387 text-shadow: 388 388 -1px -1px 0 rgba(11, 14, 23, 0.8),
+4 -4
src/lib/VideoPlayer.svelte
··· 334 334 .player-wrapper { 335 335 position: relative; 336 336 width: 100%; 337 - background: #0B0E17; 337 + background: var(--color-space-black); 338 338 overflow: hidden; 339 339 } 340 340 ··· 383 383 } 384 384 385 385 .time-sep { 386 - color: #FFD93D; 386 + color: var(--color-yellow); 387 387 font-family: 'Comic Sans MS', 'Comic Sans', cursive, system-ui; 388 388 font-size: 0.85rem; 389 389 } ··· 431 431 transition: 432 432 opacity 0.25s ease, 433 433 transform 0.2s ease; 434 - z-index: 5; 434 + z-index: var(--z-card); 435 435 } 436 436 437 437 .fullscreen-btn.visible { ··· 456 456 left: 10%; 457 457 right: 10%; 458 458 background: rgba(255, 107, 44, 0.85); 459 - color: #FFD93D; 459 + color: var(--color-yellow); 460 460 padding: 8px 12px; 461 461 border-radius: 6px; 462 462 font-family: 'Comic Sans MS', 'Comic Sans', cursive, system-ui;
+4 -4
src/lib/VodjamHeader.svelte
··· 67 67 } 68 68 69 69 .subtitle-link { 70 - color: #E8E0FF; 70 + color: var(--color-lavender); 71 71 text-decoration: underline; 72 - text-decoration-color: #FF6B2C; 72 + text-decoration-color: var(--color-orange); 73 73 transition: color 0.15s; 74 74 text-shadow: 75 75 -1px -1px 0 rgba(11, 14, 23, 0.8), ··· 79 79 } 80 80 81 81 .subtitle-link:hover { 82 - color: #FF6B2C; 82 + color: var(--color-orange); 83 83 } 84 84 85 85 .subtitle { 86 86 animation: font-switch 4s steps(1) infinite; 87 87 font-size: clamp(0.65rem, 1.4vw, 0.85rem); 88 - color: #E8E0FF; 88 + color: var(--color-lavender); 89 89 margin: 0; 90 90 line-height: 1.4; 91 91 opacity: 0.85;
+50 -50
src/lib/game/CollectionOverlay.svelte
··· 205 205 .collection-overlay { 206 206 position: fixed; 207 207 inset: 0; 208 - z-index: 500; 208 + z-index: var(--z-modal); 209 209 display: flex; 210 210 align-items: center; 211 211 justify-content: center; 212 - background: rgba(0, 0, 0, 0.85); 212 + background: var(--color-overlay-bg); 213 213 animation: fade-in 0.2s ease; 214 214 } 215 215 216 216 .collection-panel { 217 - background: #141833; 218 - border: 4px solid #FF6B2C; 217 + background: var(--color-dark-navy); 218 + border: 4px solid var(--color-orange); 219 219 border-radius: 2px; 220 220 padding: 1.25rem; 221 221 max-width: 560px; 222 222 width: 92vw; 223 223 max-height: 80vh; 224 - color: #E8E0FF; 224 + color: var(--color-lavender); 225 225 position: relative; 226 226 font-family: 'Courier New', Courier, monospace; 227 227 box-shadow: 228 - 0 0 0 2px #0B0E17, 228 + 0 0 0 2px var(--color-space-black), 229 229 0 0 40px rgba(255, 107, 44, 0.4), 230 230 inset 0 0 30px rgba(11, 14, 23, 0.5); 231 231 display: flex; ··· 237 237 top: 0.4rem; 238 238 right: 0.4rem; 239 239 background: none; 240 - border: 2px solid #FF6B2C; 241 - color: #FFD93D; 240 + border: 2px solid var(--color-orange); 241 + color: var(--color-yellow); 242 242 font-size: 1rem; 243 243 cursor: pointer; 244 244 padding: 0.15rem 0.4rem; ··· 248 248 } 249 249 250 250 .close-btn:hover { 251 - background: #FF6B2C; 252 - color: #0B0E17; 251 + background: var(--color-orange); 252 + color: var(--color-space-black); 253 253 } 254 254 255 255 .collection-title { 256 256 text-align: center; 257 257 margin: 0; 258 258 font-size: 1.3rem; 259 - color: #FFD93D; 259 + color: var(--color-yellow); 260 260 } 261 261 262 262 .panel-header { ··· 273 273 274 274 .auth-loading { 275 275 font-family: 'Courier New', Courier, monospace; 276 - color: #9990bb; 276 + color: var(--color-muted); 277 277 font-size: 0.65rem; 278 278 } 279 279 280 280 .auth-handle { 281 281 font-family: 'Courier New', Courier, monospace; 282 - color: #FF6B2C; 282 + color: var(--color-orange); 283 283 font-size: 0.85rem; 284 284 overflow: hidden; 285 285 text-overflow: ellipsis; ··· 297 297 } 298 298 299 299 .login-btn { 300 - background: #FF6B2C; 301 - border: 2px solid #ff8f5c; 302 - color: #0B0E17; 300 + background: var(--color-orange); 301 + border: 2px solid var(--color-orange-light); 302 + color: var(--color-space-black); 303 303 } 304 304 305 305 .login-btn:hover { 306 - background: #ff8f5c; 306 + background: var(--color-orange-light); 307 307 } 308 308 309 309 .logout-btn { 310 310 background: none; 311 - border: 2px solid #FF6B2C; 312 - color: #FF6B2C; 311 + border: 2px solid var(--color-orange); 312 + color: var(--color-orange); 313 313 } 314 314 315 315 .logout-btn:hover { 316 - background: #FF6B2C; 317 - color: #0B0E17; 316 + background: var(--color-orange); 317 + color: var(--color-space-black); 318 318 } 319 319 320 320 .login-form { ··· 326 326 .login-input { 327 327 font-family: 'Courier New', Courier, monospace; 328 328 font-size: 0.85rem; 329 - background: #0B0E17; 330 - border: 2px solid #1e2450; 329 + background: var(--color-space-black); 330 + border: 2px solid var(--color-border); 331 331 border-radius: 2px; 332 - color: #E8E0FF; 332 + color: var(--color-lavender); 333 333 padding: 6px 10px; 334 334 width: 200px; 335 335 } 336 336 337 337 .login-input:focus { 338 - border-color: #FF6B2C; 338 + border-color: var(--color-orange); 339 339 outline: none; 340 340 } 341 341 342 342 .login-go-btn { 343 - background: #FF6B2C; 344 - border: 2px solid #ff8f5c; 345 - color: #0B0E17; 343 + background: var(--color-orange); 344 + border: 2px solid var(--color-orange-light); 345 + color: var(--color-space-black); 346 346 } 347 347 348 348 .login-cancel-btn { 349 349 background: none; 350 - border: 2px solid #665f88; 351 - color: #9990bb; 350 + border: 2px solid var(--color-muted-dark); 351 + color: var(--color-muted); 352 352 } 353 353 354 354 .login-error { ··· 364 364 .empty-state { 365 365 text-align: center; 366 366 padding: 2rem 1rem; 367 - color: #9990bb; 367 + color: var(--color-muted); 368 368 font-size: 0.85rem; 369 369 } 370 370 371 371 .empty-hint { 372 372 font-size: 0.7rem; 373 373 margin-top: 0.5rem; 374 - color: #665f88; 374 + color: var(--color-muted-dark); 375 375 } 376 376 377 377 .collection-body { ··· 392 392 } 393 393 394 394 .day-tab { 395 - background: #0B0E17; 396 - border: 2px solid #1e2450; 395 + background: var(--color-space-black); 396 + border: 2px solid var(--color-border); 397 397 border-radius: 2px; 398 - color: #9990bb; 398 + color: var(--color-muted); 399 399 padding: 6px 8px; 400 400 font-family: 'Courier New', Courier, monospace; 401 401 font-size: 0.65rem; ··· 406 406 } 407 407 408 408 .day-tab:hover { 409 - border-color: #FF6B2C; 410 - color: #E8E0FF; 409 + border-color: var(--color-orange); 410 + color: var(--color-lavender); 411 411 } 412 412 413 413 .day-tab.active { 414 - border-color: #FF6B2C; 415 - background: #1e2450; 416 - color: #FFD93D; 414 + border-color: var(--color-orange); 415 + background: var(--color-border); 416 + color: var(--color-yellow); 417 417 } 418 418 419 419 .tab-text { ··· 434 434 } 435 435 436 436 .vod-list::-webkit-scrollbar-track { 437 - background: #0B0E17; 437 + background: var(--color-space-black); 438 438 } 439 439 440 440 .vod-list::-webkit-scrollbar-thumb { 441 - background: #FF6B2C; 441 + background: var(--color-orange); 442 442 border-radius: 2px; 443 443 } 444 444 ··· 447 447 align-items: center; 448 448 justify-content: space-between; 449 449 gap: 0.5rem; 450 - background: #0B0E17; 451 - border: 2px solid #1e2450; 450 + background: var(--color-space-black); 451 + border: 2px solid var(--color-border); 452 452 border-radius: 2px; 453 453 padding: 8px 10px; 454 454 cursor: pointer; ··· 456 456 text-align: left; 457 457 width: 100%; 458 458 font-family: 'Courier New', Courier, monospace; 459 - color: #E8E0FF; 459 + color: var(--color-lavender); 460 460 } 461 461 462 462 .vod-item:hover { 463 - border-color: #FFD93D; 464 - background: #1e2450; 463 + border-color: var(--color-yellow); 464 + background: var(--color-border); 465 465 } 466 466 467 467 .vod-item-info { ··· 475 475 .vod-title { 476 476 font-size: 0.7rem; 477 477 font-weight: bold; 478 - color: #E8E0FF; 478 + color: var(--color-lavender); 479 479 overflow: hidden; 480 480 text-overflow: ellipsis; 481 481 white-space: nowrap; ··· 483 483 484 484 .vod-speaker { 485 485 font-size: 0.6rem; 486 - color: #FF6B2C; 486 + color: var(--color-orange); 487 487 } 488 488 489 489 .vod-duration { 490 490 font-size: 0.6rem; 491 - color: #9990bb; 491 + color: var(--color-muted); 492 492 flex-shrink: 0; 493 493 } 494 494
+65 -85
src/lib/game/CompletionScreen.svelte
··· 18 18 let { 19 19 elapsedMs, 20 20 totalVods, 21 - onviewcollection, 22 - onexplore 21 + onviewcollection 23 22 }: { 24 23 elapsedMs: number; 25 24 totalVods: number; 26 25 onviewcollection?: () => void; 27 - onexplore?: () => void; 28 26 } = $props(); 29 27 30 28 let authReady = $state(false); ··· 103 101 return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`; 104 102 }); 105 103 106 - $effect(() => { 107 - function onKeydown(e: KeyboardEvent) { 108 - if (e.key === 'Escape') onexplore?.(); 109 - } 110 - window.addEventListener('keydown', onKeydown); 111 - return () => window.removeEventListener('keydown', onKeydown); 112 - }); 104 + 113 105 </script> 114 106 115 107 <div class="completion-screen"> ··· 129 121 </div> 130 122 </div> 131 123 132 - <div class="submit-area"> 133 - {#if !authReady} 134 - <span class="submit-loading">...</span> 135 - {:else if submitted} 136 - <p class="submit-success">Score submitted! ✓</p> 137 - {:else if loggedIn} 138 - <p class="submit-handle">@{handle ?? '...'}</p> 139 - <button 140 - class="action-btn submit-btn" 141 - onclick={submitScore} 142 - disabled={submitting} 143 - > 144 - <span class="btn-text">{submitting ? 'Submitting...' : 'Submit Score'}</span> 145 - </button> 146 - {:else if showLoginInput} 124 + {#if showLoginInput} 125 + <div class="login-area"> 147 126 <p class="submit-hint">Login with Bluesky to save your time to the leaderboard</p> 148 127 <form class="login-form" onsubmit={(e) => { e.preventDefault(); handleLogin(); }}> 149 128 <input ··· 153 132 bind:value={loginHandle} 154 133 disabled={loginLoading} 155 134 /> 156 - <button class="action-btn submit-btn" type="submit" disabled={loginLoading}> 135 + <button class="action-btn collection-btn" type="submit" disabled={loginLoading}> 157 136 <span class="btn-text">{loginLoading ? '...' : 'Go'}</span> 158 137 </button> 159 - <button class="action-btn explore-btn cancel-btn" type="button" onclick={() => { showLoginInput = false; loginError = ''; }}> 138 + <button class="action-btn cancel-btn" type="button" onclick={() => { showLoginInput = false; loginError = ''; }}> 160 139 <span class="btn-text">✕</span> 161 140 </button> 162 141 </form> 163 142 {#if loginError} 164 143 <p class="submit-error">{loginError}</p> 165 144 {/if} 166 - {:else} 167 - <button class="action-btn submit-btn" onclick={() => (showLoginInput = true)}> 145 + </div> 146 + {/if} 147 + 148 + {#if submitted} 149 + <p class="submit-success">Score submitted! ✓</p> 150 + {/if} 151 + {#if submitError} 152 + <p class="submit-error">{submitError}</p> 153 + {/if} 154 + {#if loggedIn && handle} 155 + <p class="submit-handle">@{handle}</p> 156 + {/if} 157 + 158 + <div class="completion-buttons"> 159 + {#if !authReady} 160 + <button class="action-btn collection-btn" disabled> 161 + <span class="btn-text">...</span> 162 + </button> 163 + {:else if submitted} 164 + <!-- Score already submitted, no submit button --> 165 + {:else if loggedIn} 166 + <button 167 + class="action-btn collection-btn" 168 + onclick={submitScore} 169 + disabled={submitting} 170 + > 171 + <span class="btn-text">{submitting ? 'Submitting...' : 'Submit Score'}</span> 172 + </button> 173 + {:else if !showLoginInput} 174 + <button class="action-btn collection-btn" onclick={() => (showLoginInput = true)}> 168 175 <span class="btn-text">Submit Score</span> 169 176 </button> 170 177 {/if} 171 - {#if submitError} 172 - <p class="submit-error">{submitError}</p> 173 - {/if} 174 - </div> 175 - 176 - <div class="completion-buttons"> 177 178 <button class="action-btn collection-btn" onclick={() => onviewcollection?.()}> 178 179 <span class="btn-text">View Collection</span> 179 180 </button> 180 - <button class="action-btn explore-btn" onclick={() => onexplore?.()}> 181 - <span class="btn-text">Keep Exploring</span> 182 - </button> 183 181 </div> 184 182 </div> 185 183 </div> ··· 188 186 .completion-screen { 189 187 position: fixed; 190 188 inset: 0; 191 - z-index: 700; 189 + z-index: var(--z-completion); 192 190 display: flex; 193 191 align-items: center; 194 192 justify-content: center; 195 - background: radial-gradient(ellipse at center, #1e2450 0%, #0B0E17 70%); 193 + background: radial-gradient(ellipse at center, var(--color-border) 0%, var(--color-space-black) 70%); 196 194 animation: fade-in 0.5s ease; 197 195 } 198 196 ··· 221 219 222 220 .congrats-title { 223 221 font-size: 2rem; 224 - color: #FFD93D; 222 + color: var(--color-yellow); 225 223 margin: 0 0 0.5rem; 226 224 line-height: 1.3; 227 225 } ··· 232 230 233 231 .congrats-sub { 234 232 font-family: 'Courier New', Courier, monospace; 235 - color: #E8E0FF; 233 + color: var(--color-lavender); 236 234 font-size: 1rem; 237 235 margin: 0 0 1.5rem; 238 236 } 239 237 240 238 .congrats-sub strong { 241 - color: #FF6B2C; 239 + color: var(--color-orange); 242 240 } 243 241 244 242 .time-display { 245 - background: #141833; 246 - border: 3px solid #FF6B2C; 243 + background: var(--color-dark-navy); 244 + border: 3px solid var(--color-orange); 247 245 border-radius: 2px; 248 246 padding: 1rem 1.5rem; 249 247 margin-bottom: 2rem; ··· 253 251 .time-label { 254 252 display: block; 255 253 font-family: 'Courier New', Courier, monospace; 256 - color: #9990bb; 254 + color: var(--color-muted); 257 255 font-size: 0.75rem; 258 256 margin-bottom: 0.5rem; 259 257 text-transform: uppercase; ··· 272 270 max-width: 360px; 273 271 } 274 272 275 - .submit-area { 273 + .login-area { 276 274 display: flex; 277 275 flex-direction: column; 278 276 align-items: center; 279 277 gap: 0.5rem; 280 - margin-bottom: 1.5rem; 278 + margin-bottom: 1rem; 281 279 width: 100%; 282 280 max-width: 360px; 283 281 } 284 282 285 283 .submit-hint { 286 284 font-family: 'Courier New', Courier, monospace; 287 - color: #9990bb; 285 + color: var(--color-muted); 288 286 font-size: 0.75rem; 289 287 margin: 0; 290 288 text-align: center; ··· 292 290 293 291 .submit-handle { 294 292 font-family: 'Courier New', Courier, monospace; 295 - color: #FF6B2C; 293 + color: var(--color-orange); 296 294 font-size: 0.85rem; 297 295 margin: 0; 298 296 } 299 297 300 298 .submit-success { 301 299 font-family: 'Courier New', Courier, monospace; 302 - color: #FFD93D; 300 + color: var(--color-yellow); 303 301 font-size: 0.95rem; 304 302 margin: 0; 305 303 } ··· 311 309 margin: 0; 312 310 } 313 311 314 - .submit-loading { 315 - font-family: 'Courier New', Courier, monospace; 316 - color: #9990bb; 317 - font-size: 0.75rem; 318 - } 319 - 320 - .submit-btn { 321 - background: #FFD93D; 322 - border-color: #ffe46a; 323 - color: #0B0E17; 324 - } 325 - 326 - .submit-btn:hover { 327 - background: #ffe46a; 328 - } 329 - 330 312 .login-form { 331 313 display: flex; 332 314 align-items: center; ··· 338 320 flex: 1; 339 321 font-family: 'Courier New', Courier, monospace; 340 322 font-size: 0.85rem; 341 - background: #0B0E17; 342 - border: 2px solid #1e2450; 323 + background: var(--color-space-black); 324 + border: 2px solid var(--color-border); 343 325 border-radius: 2px; 344 - color: #E8E0FF; 326 + color: var(--color-lavender); 345 327 padding: 0.6rem 0.75rem; 346 328 } 347 329 348 330 .login-input:focus { 349 - border-color: #FF6B2C; 331 + border-color: var(--color-orange); 350 332 outline: none; 351 333 } 352 334 353 335 .cancel-btn { 354 336 flex: 0; 355 337 padding: 0.75rem; 338 + background: var(--color-dark-navy); 339 + border-color: var(--color-muted-dark); 340 + color: var(--color-muted); 341 + } 342 + 343 + .cancel-btn:hover { 344 + background: var(--color-border); 345 + border-color: var(--color-muted); 356 346 } 357 347 358 348 .action-btn { ··· 381 371 } 382 372 383 373 .collection-btn { 384 - background: #FF6B2C; 385 - border-color: #ff8f5c; 386 - color: #0B0E17; 374 + background: var(--color-orange); 375 + border-color: var(--color-orange-light); 376 + color: var(--color-space-black); 387 377 } 388 378 389 379 .collection-btn:hover { 390 - background: #ff8f5c; 391 - } 392 - 393 - .explore-btn { 394 - background: #141833; 395 - border-color: #FFD93D; 396 - color: #FFD93D; 397 - } 398 - 399 - .explore-btn:hover { 400 - background: #1e2450; 380 + background: var(--color-orange-light); 401 381 } 402 382 403 383 @keyframes fade-in {
+12 -12
src/lib/game/GameHUD.svelte
··· 96 96 position: fixed; 97 97 inset: 0; 98 98 pointer-events: none; 99 - z-index: 100; 99 + z-index: var(--z-overlay); 100 100 font-family: 'Courier New', Courier, monospace; 101 101 } 102 102 ··· 114 114 } 115 115 116 116 .timer-display { 117 - background: #141833; 118 - border: 3px solid #FF6B2C; 119 - color: #FFD93D; 117 + background: var(--color-dark-navy); 118 + border: 3px solid var(--color-orange); 119 + color: var(--color-yellow); 120 120 padding: 8px 14px; 121 121 border-radius: 2px; 122 122 box-shadow: 0 0 20px rgba(255, 107, 44, 0.3); 123 123 } 124 124 125 125 .vod-counter { 126 - background: #141833; 127 - border: 3px solid #FF6B2C; 128 - color: #FFD93D; 126 + background: var(--color-dark-navy); 127 + border: 3px solid var(--color-orange); 128 + color: var(--color-yellow); 129 129 padding: 8px 16px; 130 130 border-radius: 2px; 131 131 font-size: 0.9rem; ··· 159 159 } 160 160 161 161 .counter-value { 162 - background: #0B0E17; 163 - border: 2px solid #FFD93D; 162 + background: var(--color-space-black); 163 + border: 2px solid var(--color-yellow); 164 164 border-radius: 2px; 165 165 padding: 2px 8px; 166 166 font-size: 1.1rem; ··· 174 174 left: 50%; 175 175 transform: translateX(-50%); 176 176 background: rgba(0, 0, 0, 0.7); 177 - color: #ffd93d; 177 + color: var(--color-yellow); 178 178 padding: 10px 24px; 179 179 border-radius: 8px; 180 180 font-size: 1rem; ··· 203 203 } 204 204 205 205 .interact-action { 206 - color: #ffd93d; 206 + color: var(--color-yellow); 207 207 font-size: 0.85rem; 208 208 margin-top: 2px; 209 209 } ··· 225 225 display: flex; 226 226 gap: 8px; 227 227 background: rgba(0, 0, 0, 0.5); 228 - color: #e8e0ff; 228 + color: var(--color-lavender); 229 229 padding: 6px 16px; 230 230 border-radius: 6px; 231 231 font-size: 0.8rem;
+14 -14
src/lib/game/IntroSequence.svelte
··· 430 430 .intro-container { 431 431 position: fixed; 432 432 inset: 0; 433 - z-index: 1000; 433 + z-index: var(--z-intro); 434 434 overflow: hidden; 435 435 background: #000; 436 436 outline: none; ··· 445 445 flex-direction: column; 446 446 align-items: center; 447 447 justify-content: center; 448 - background: #0B0E17; 448 + background: var(--color-space-black); 449 449 } 450 450 451 451 .dialog-frame { ··· 510 510 .dialog-text { 511 511 animation: font-switch 4s steps(1) infinite; 512 512 font-size: clamp(1rem, 3vw, 1.6rem); 513 - color: #FFD93D; 513 + color: var(--color-yellow); 514 514 margin: 16px 0 0; 515 515 text-align: center; 516 - text-shadow: 2px 2px 0 #0B0E17; 516 + text-shadow: 2px 2px 0 var(--color-space-black); 517 517 z-index: 1; 518 518 } 519 519 ··· 633 633 width: 4px; 634 634 height: 4px; 635 635 border-radius: 50%; 636 - background: #ff6b2c; 636 + background: var(--color-orange); 637 637 animation: particle-fly 0.8s ease-out forwards; 638 638 } 639 639 ··· 684 684 z-index: 2; 685 685 margin-top: 24px; 686 686 padding: 14px 36px; 687 - border: 4px solid #FF6B2C; 688 - background: linear-gradient(180deg, #FFD93D 0%, #FF6B2C 100%); 689 - color: #0B0E17; 687 + border: 4px solid var(--color-orange); 688 + background: linear-gradient(180deg, var(--color-yellow) 0%, var(--color-orange) 100%); 689 + color: var(--color-space-black); 690 690 font-size: clamp(1.2rem, 4vw, 2rem); 691 691 font-weight: bold; 692 692 animation: font-switch 4s steps(1) infinite, btn-bounce 1.2s ease-in-out infinite; 693 693 cursor: pointer; 694 694 text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.3); 695 695 box-shadow: 696 - 4px 4px 0 #0B0E17, 697 - 8px 8px 0 #1E2952; 696 + 4px 4px 0 var(--color-space-black), 697 + 8px 8px 0 var(--color-medium-blue); 698 698 transition: transform 0.15s ease; 699 699 image-rendering: pixelated; 700 700 } ··· 702 702 .start-btn:hover { 703 703 transform: scale(1.08); 704 704 box-shadow: 705 - 4px 4px 0 #0B0E17, 706 - 8px 8px 0 #1E2952, 705 + 4px 4px 0 var(--color-space-black), 706 + 8px 8px 0 var(--color-medium-blue), 707 707 0 0 20px rgba(255, 107, 44, 0.5); 708 708 } 709 709 ··· 756 756 757 757 .title-heading { 758 758 font-size: clamp(2rem, 6vw, 3.5rem); 759 - color: #FFD93D; 759 + color: var(--color-yellow); 760 760 margin: 0; 761 - text-shadow: 3px 3px 0 #0B0E17; 761 + text-shadow: 3px 3px 0 var(--color-space-black); 762 762 position: relative; 763 763 z-index: 1; 764 764 }
+15 -15
src/lib/game/Leaderboard.svelte
··· 61 61 .lb-title { 62 62 text-align: center; 63 63 font-size: 1.1rem; 64 - color: #FFD93D; 64 + color: var(--color-yellow); 65 65 margin: 0 0 0.75rem; 66 66 } 67 67 ··· 72 72 .lb-loading, .lb-error, .lb-empty { 73 73 text-align: center; 74 74 font-family: 'Courier New', Courier, monospace; 75 - color: #9990bb; 75 + color: var(--color-muted); 76 76 font-size: 0.75rem; 77 77 padding: 1rem; 78 78 } ··· 91 91 } 92 92 93 93 .lb-list::-webkit-scrollbar-track { 94 - background: #0B0E17; 94 + background: var(--color-space-black); 95 95 } 96 96 97 97 .lb-list::-webkit-scrollbar-thumb { 98 - background: #FF6B2C; 98 + background: var(--color-orange); 99 99 border-radius: 2px; 100 100 } 101 101 ··· 103 103 display: flex; 104 104 align-items: center; 105 105 gap: 0.5rem; 106 - background: #0B0E17; 107 - border: 2px solid #1e2450; 106 + background: var(--color-space-black); 107 + border: 2px solid var(--color-border); 108 108 border-radius: 2px; 109 109 padding: 6px 10px; 110 110 font-family: 'Courier New', Courier, monospace; 111 - color: #E8E0FF; 111 + color: var(--color-lavender); 112 112 } 113 113 114 114 .lb-gold { 115 - border-color: #FFD93D; 115 + border-color: var(--color-yellow); 116 116 background: #1a1c30; 117 117 } 118 118 ··· 128 128 129 129 .lb-rank { 130 130 font-size: 0.7rem; 131 - color: #9990bb; 131 + color: var(--color-muted); 132 132 min-width: 28px; 133 133 font-weight: bold; 134 134 } 135 135 136 - .lb-gold .lb-rank { color: #FFD93D; } 136 + .lb-gold .lb-rank { color: var(--color-yellow); } 137 137 .lb-silver .lb-rank { color: #C0C0C0; } 138 138 .lb-bronze .lb-rank { color: #CD7F32; } 139 139 ··· 155 155 width: 28px; 156 156 height: 28px; 157 157 border-radius: 2px; 158 - background: #1e2450; 159 - border: 1px solid #FF6B2C; 158 + background: var(--color-border); 159 + border: 1px solid var(--color-orange); 160 160 display: flex; 161 161 align-items: center; 162 162 justify-content: center; 163 163 font-size: 0.65rem; 164 - color: #9990bb; 164 + color: var(--color-muted); 165 165 } 166 166 167 167 .lb-info { ··· 183 183 184 184 .lb-handle { 185 185 font-size: 0.55rem; 186 - color: #FF6B2C; 186 + color: var(--color-orange); 187 187 overflow: hidden; 188 188 text-overflow: ellipsis; 189 189 white-space: nowrap; ··· 192 192 193 193 .lb-time { 194 194 font-size: 0.7rem; 195 - color: #FFD93D; 195 + color: var(--color-yellow); 196 196 font-weight: bold; 197 197 flex-shrink: 0; 198 198 font-variant-numeric: tabular-nums;
+26 -26
src/lib/game/PresentDetail.svelte
··· 142 142 .present-detail-overlay { 143 143 position: fixed; 144 144 inset: 0; 145 - z-index: 500; 145 + z-index: var(--z-modal); 146 146 display: flex; 147 147 align-items: center; 148 148 justify-content: center; ··· 151 151 } 152 152 153 153 .present-detail { 154 - background: #141833; 155 - border: 4px solid #FF6B2C; 154 + background: var(--color-dark-navy); 155 + border: 4px solid var(--color-orange); 156 156 border-radius: 2px; 157 157 padding: 1.25rem; 158 158 max-width: 420px; 159 159 width: 90vw; 160 - color: #E8E0FF; 160 + color: var(--color-lavender); 161 161 position: relative; 162 162 font-family: 'Courier New', Courier, monospace; 163 163 box-shadow: 164 - 0 0 0 2px #0B0E17, 164 + 0 0 0 2px var(--color-space-black), 165 165 0 0 40px rgba(255, 107, 44, 0.4), 166 166 inset 0 0 30px rgba(11, 14, 23, 0.5); 167 167 image-rendering: pixelated; ··· 172 172 top: 0.4rem; 173 173 right: 0.4rem; 174 174 background: none; 175 - border: 2px solid #FF6B2C; 176 - color: #FFD93D; 175 + border: 2px solid var(--color-orange); 176 + color: var(--color-yellow); 177 177 font-size: 1rem; 178 178 cursor: pointer; 179 179 padding: 0.15rem 0.4rem; ··· 183 183 } 184 184 185 185 .close-btn:hover { 186 - background: #FF6B2C; 187 - color: #0B0E17; 186 + background: var(--color-orange); 187 + color: var(--color-space-black); 188 188 } 189 189 190 190 .detail-header { ··· 200 200 display: flex; 201 201 align-items: center; 202 202 justify-content: center; 203 - background: #0B0E17; 204 - border: 2px solid #FFD93D; 203 + background: var(--color-space-black); 204 + border: 2px solid var(--color-yellow); 205 205 border-radius: 2px; 206 206 } 207 207 ··· 221 221 font-size: 0.85rem; 222 222 margin: 0 0 0.4rem; 223 223 line-height: 1.4; 224 - color: #FFD93D; 224 + color: var(--color-yellow); 225 225 font-family: 'Courier New', Courier, monospace; 226 226 font-weight: bold; 227 227 } 228 228 229 229 .creator a { 230 - color: #FF6B2C; 230 + color: var(--color-orange); 231 231 text-decoration: none; 232 232 font-size: 0.7rem; 233 233 } ··· 238 238 239 239 .speaker-name { 240 240 display: block; 241 - color: #E8E0FF; 241 + color: var(--color-lavender); 242 242 font-size: 0.75rem; 243 243 margin-bottom: 0.2rem; 244 244 } 245 245 246 246 .description { 247 - color: #9990bb; 247 + color: var(--color-muted); 248 248 font-size: 0.6rem; 249 249 line-height: 1.6; 250 250 margin: 0.5rem 0; ··· 262 262 } 263 263 264 264 .meta { 265 - color: #9990bb; 265 + color: var(--color-muted); 266 266 font-size: 0.65rem; 267 267 margin: 0.4rem 0; 268 268 display: flex; ··· 276 276 overflow: hidden; 277 277 border-radius: 2px; 278 278 margin: 0.5rem 0 0.75rem; 279 - background: linear-gradient(135deg, #0B0E17, #141833); 280 - border: 2px solid #1e2450; 279 + background: linear-gradient(135deg, var(--color-space-black), var(--color-dark-navy)); 280 + border: 2px solid var(--color-border); 281 281 } 282 282 283 283 .thumbnail img { ··· 316 316 } 317 317 318 318 .collect-btn { 319 - background: #FF6B2C; 320 - border-color: #ff8f5c; 321 - color: #0B0E17; 319 + background: var(--color-orange); 320 + border-color: var(--color-orange-light); 321 + color: var(--color-space-black); 322 322 } 323 323 324 324 .collect-btn:hover { 325 - background: #ff8f5c; 325 + background: var(--color-orange-light); 326 326 } 327 327 328 328 .watch-btn { 329 - background: #141833; 330 - border-color: #FFD93D; 331 - color: #FFD93D; 329 + background: var(--color-dark-navy); 330 + border-color: var(--color-yellow); 331 + color: var(--color-yellow); 332 332 } 333 333 334 334 .watch-btn:hover { 335 - background: #1e2450; 335 + background: var(--color-border); 336 336 } 337 337 338 338 @keyframes fade-in {
+5
src/lib/theme.ts
··· 1 1 /** 2 2 * VoD Jam theme: deep space color scheme, seeded randomness, and card layout utilities. 3 + * 4 + * NOTE: These colors are also defined as CSS custom properties in 5 + * src/routes/+layout.svelte (:root). Keep both in sync when changing values. 6 + * Use CSS variables (var(--color-*)) in <style> blocks and these TS constants 7 + * only in JS contexts (canvas, SVG, inline style computations). 3 8 */ 4 9 5 10 /** Brand colors — ToeJam & Earl deep space palette */
+36 -7
src/routes/+layout.svelte
··· 33 33 <AnimatedCursor /> 34 34 35 35 <style> 36 + :global(:root) { 37 + /* Brand colors — ToeJam & Earl deep space palette (synced with theme.ts) */ 38 + --color-space-black: #0B0E17; 39 + --color-dark-navy: #141833; 40 + --color-medium-blue: #1E2952; 41 + --color-orange: #FF6B2C; 42 + --color-orange-light: #ff8f5c; 43 + --color-yellow: #FFD93D; 44 + --color-lavender: #E8E0FF; 45 + --color-deep-blue: #0A182B; 46 + --color-muted: #9990bb; 47 + --color-muted-dark: #665f88; 48 + --color-border: #1e2450; 49 + --color-overlay-bg: rgba(0, 0, 0, 0.85); 50 + 51 + /* Breakpoints (reference — used in component @media queries): 52 + sm: 480px, md: 600px */ 53 + 54 + /* Z-index scale */ 55 + --z-background: -2; 56 + --z-base: 1; 57 + --z-card: 5; 58 + --z-overlay: 100; 59 + --z-modal: 500; 60 + --z-toast: 600; 61 + --z-completion: 700; 62 + --z-intro: 1000; 63 + } 64 + 36 65 :global(html, body) { 37 66 margin: 0; 38 67 padding: 0; 39 68 min-height: 100vh; 40 69 overflow: hidden; 41 - background: #0b0e17; 70 + background: var(--color-space-black); 42 71 } 43 72 44 73 :global(body) { 45 74 font-family: 'Comic Sans MS', 'Comic Sans', cursive, system-ui; 46 - color: #e8e0ff; 75 + color: var(--color-lavender); 47 76 } 48 77 49 78 :global(*) { ··· 52 81 53 82 /* Selection color to match theme */ 54 83 :global(::selection) { 55 - background: #FF6B2C80; 56 - color: #E8E0FF; 84 + background: color-mix(in srgb, var(--color-orange) 50%, transparent); 85 + color: var(--color-lavender); 57 86 } 58 87 59 88 /* Scrollbar theming */ ··· 62 91 } 63 92 64 93 :global(::-webkit-scrollbar-track) { 65 - background: #0B0E17; 94 + background: var(--color-space-black); 66 95 } 67 96 68 97 :global(::-webkit-scrollbar-thumb) { 69 - background: #1E2952; 98 + background: var(--color-medium-blue); 70 99 border-radius: 5px; 71 100 } 72 101 73 102 :global(::-webkit-scrollbar-thumb:hover) { 74 - background: #FF6B2C; 103 + background: var(--color-orange); 75 104 } 76 105 </style>
+10 -11
src/routes/+page.svelte
··· 406 406 {elapsedMs} 407 407 totalVods={videos.length} 408 408 onviewcollection={handleCompletionViewCollection} 409 - onexplore={handleCompletionExplore} 410 409 /> 411 410 {/if} 412 411 ··· 446 445 align-items: center; 447 446 justify-content: center; 448 447 height: 100vh; 449 - background: #0b0e17; 450 - color: #e8e0ff; 448 + background: var(--color-space-black); 449 + color: var(--color-lavender); 451 450 } 452 451 453 452 .loading-ship { ··· 476 475 align-items: center; 477 476 justify-content: center; 478 477 height: 100vh; 479 - background: #0b0e17; 480 - color: #ff6b2c; 478 + background: var(--color-space-black); 479 + color: var(--color-orange); 481 480 gap: 1rem; 482 481 } 483 482 ··· 487 486 } 488 487 489 488 .retry-btn { 490 - background: #ff6b2c; 489 + background: var(--color-orange); 491 490 color: #fff; 492 491 border: none; 493 492 padding: 0.75rem 2rem; ··· 500 499 .video-overlay { 501 500 position: fixed; 502 501 inset: 0; 503 - z-index: 600; 504 - background: #0b0e17; 502 + z-index: var(--z-toast); 503 + background: var(--color-space-black); 505 504 overflow-y: auto; 506 505 display: flex; 507 506 flex-direction: column; ··· 529 528 530 529 .video-close-btn:hover { 531 530 background: #e94560; 532 - color: #fff; 531 + color: var(--color-lavender); 533 532 } 534 533 535 534 .video-container { ··· 546 545 547 546 .video-title { 548 547 margin: 0; 549 - color: #e8e0ff; 548 + color: var(--color-lavender); 550 549 font-size: 1.4rem; 551 550 animation: font-switch 4s steps(1) infinite; 552 551 } ··· 559 558 } 560 559 561 560 .creator-link { 562 - color: #ffd93d; 561 + color: var(--color-yellow); 563 562 text-decoration: none; 564 563 } 565 564
+4 -4
src/routes/leaderboard/+page.svelte
··· 17 17 <style> 18 18 .leaderboard-page { 19 19 min-height: 100vh; 20 - background: radial-gradient(ellipse at center, #1e2450 0%, #0B0E17 70%); 20 + background: radial-gradient(ellipse at center, var(--color-border) 0%, var(--color-space-black) 70%); 21 21 display: flex; 22 22 flex-direction: column; 23 23 align-items: center; ··· 34 34 .back-link { 35 35 align-self: flex-start; 36 36 font-family: 'Courier New', Courier, monospace; 37 - color: #9990bb; 37 + color: var(--color-muted); 38 38 text-decoration: none; 39 39 font-size: 0.875rem; 40 40 margin-bottom: 1rem; ··· 42 42 } 43 43 44 44 .back-link:hover { 45 - color: #FFD93D; 45 + color: var(--color-yellow); 46 46 } 47 47 48 48 .page-title { 49 49 font-size: 2rem; 50 - color: #FFD93D; 50 + color: var(--color-yellow); 51 51 margin: 0 0 1.5rem; 52 52 } 53 53
static/sprites/speakers/byarielm.fyi.webp

This is a binary file and will not be displayed.

static/sprites/speakers/cameron.stream.webp

This is a binary file and will not be displayed.