Adding arcade

This commit is contained in:
Dan 2026-02-01 14:10:55 +00:00
parent d08b7a4c8f
commit 898528d390
18 changed files with 653 additions and 198 deletions

View file

@ -16,7 +16,7 @@ class ArcadeGamesController {
// State // State
this.currentGameIndex = 0; this.currentGameIndex = 0;
this.gameCards = []; this.gameCards = [];
this.soundEnabled = true; this.soundEnabled = false; // Start muted
this.sounds = {}; this.sounds = {};
// Initialize // Initialize
@ -31,9 +31,14 @@ class ArcadeGamesController {
init() { init() {
this.gameCards = Array.from(this.gamesGrid.querySelectorAll('.game-card')); this.gameCards = Array.from(this.gamesGrid.querySelectorAll('.game-card'));
// Select first game by default // Select first released game by default
if (this.gameCards.length > 0) { if (this.gameCards.length > 0) {
this.selectGame(0); const firstReleasedIndex = this.gameCards.findIndex(card => this.isGameReleased(card));
if (firstReleasedIndex >= 0) {
this.selectGame(firstReleasedIndex);
} else {
this.selectGame(0);
}
} else { } else {
this.showInsertCoin(); this.showInsertCoin();
} }
@ -142,8 +147,10 @@ class ArcadeGamesController {
// Game cards click // Game cards click
this.gameCards.forEach((card, index) => { this.gameCards.forEach((card, index) => {
card.addEventListener('click', () => { card.addEventListener('click', () => {
this.selectGame(index); if (this.isGameReleased(card)) {
this.pressSelect(); this.selectGame(index);
this.pressSelect();
}
}); });
card.addEventListener('mouseenter', () => { card.addEventListener('mouseenter', () => {
@ -165,12 +172,16 @@ class ArcadeGamesController {
* Set up sound toggle button * Set up sound toggle button
*/ */
setupSoundToggle() { setupSoundToggle() {
const soundOn = this.soundToggle.querySelector('.sound-on');
const soundOff = this.soundToggle.querySelector('.sound-off');
// Set initial state (muted)
soundOn.style.display = 'none';
soundOff.style.display = 'inline-block';
this.soundToggle.addEventListener('click', () => { this.soundToggle.addEventListener('click', () => {
this.soundEnabled = !this.soundEnabled; this.soundEnabled = !this.soundEnabled;
const soundOn = this.soundToggle.querySelector('.sound-on');
const soundOff = this.soundToggle.querySelector('.sound-off');
if (this.soundEnabled) { if (this.soundEnabled) {
soundOn.style.display = 'inline-block'; soundOn.style.display = 'inline-block';
soundOff.style.display = 'none'; soundOff.style.display = 'none';
@ -213,8 +224,9 @@ class ArcadeGamesController {
* Navigate left in grid * Navigate left in grid
*/ */
moveLeft() { moveLeft() {
if (this.currentGameIndex > 0) { const newIndex = this.findPreviousSelectableGame(this.currentGameIndex);
this.selectGame(this.currentGameIndex - 1); if (newIndex !== -1) {
this.selectGame(newIndex);
this.playSound('move'); this.playSound('move');
} }
} }
@ -223,8 +235,9 @@ class ArcadeGamesController {
* Navigate right in grid * Navigate right in grid
*/ */
moveRight() { moveRight() {
if (this.currentGameIndex < this.gameCards.length - 1) { const newIndex = this.findNextSelectableGame(this.currentGameIndex);
this.selectGame(this.currentGameIndex + 1); if (newIndex !== -1) {
this.selectGame(newIndex);
this.playSound('move'); this.playSound('move');
} }
} }
@ -243,6 +256,37 @@ class ArcadeGamesController {
return Math.floor((gridWidth + gap) / (cardWidth + gap)) || 1; return Math.floor((gridWidth + gap) / (cardWidth + gap)) || 1;
} }
/**
* Check if a game is released
*/
isGameReleased(card) {
return card.dataset.released === 'true';
}
/**
* Find next selectable game (skip unreleased)
*/
findNextSelectableGame(currentIndex) {
for (let i = currentIndex + 1; i < this.gameCards.length; i++) {
if (this.isGameReleased(this.gameCards[i])) {
return i;
}
}
return -1;
}
/**
* Find previous selectable game (skip unreleased)
*/
findPreviousSelectableGame(currentIndex) {
for (let i = currentIndex - 1; i >= 0; i--) {
if (this.isGameReleased(this.gameCards[i])) {
return i;
}
}
return -1;
}
/** /**
* Select a game card * Select a game card
*/ */
@ -265,9 +309,15 @@ class ArcadeGamesController {
* Press select button (navigate to game) * Press select button (navigate to game)
*/ */
pressSelect() { pressSelect() {
const selectedCard = this.gameCards[this.currentGameIndex];
// Don't allow selecting unreleased games
if (!this.isGameReleased(selectedCard)) {
return;
}
this.playSound('select'); this.playSound('select');
const selectedCard = this.gameCards[this.currentGameIndex];
const link = selectedCard.querySelector('a'); const link = selectedCard.querySelector('a');
if (link) { if (link) {

View file

@ -67,13 +67,23 @@
.arcade-cabinet { .arcade-cabinet {
background: background:
repeating-linear-gradient(90deg, #3d2817 0px, #4a3420 2px, #3d2817 4px), linear-gradient(
linear-gradient(180deg, #4a3420 0%, #2d1f12 100%); 135deg,
border: 4px solid #2a2a2a; rgba(255, 0, 255, 0.1) 0%,
rgba(0, 255, 255, 0.1) 50%,
rgba(255, 255, 0, 0.1) 100%
),
repeating-linear-gradient(90deg, #1a1a2e 0px, #2a2a4e 2px, #1a1a2e 4px),
linear-gradient(180deg, #2a2a4e 0%, #1a1a2e 100%);
border: 6px solid #5a5a8e;
border-top: none; border-top: none;
border-bottom: none; border-bottom: none;
position: relative; position: relative;
padding-top: 30px; padding-top: 30px;
box-shadow:
0 0 30px rgba(138, 43, 226, 0.3),
0 0 60px rgba(0, 255, 255, 0.2),
inset 0 0 100px rgba(138, 43, 226, 0.1);
@include media-down(lg) { @include media-down(lg) {
border-left: none; border-left: none;
@ -87,28 +97,56 @@
.arcade-marquee { .arcade-marquee {
position: relative; position: relative;
background: linear-gradient(180deg, #1a1a1a 0%, #0a0a0a 100%); background:
border: 4px solid #2a2a2a; linear-gradient(
border-bottom: none; 135deg,
border-radius: 20px 20px 0 0; #ff00ff 0%,
padding: 20px; #00ffff 25%,
#ffff00 50%,
#ff00ff 75%,
#00ffff 100%
),
linear-gradient(180deg, #1a1a2e 0%, #0f0f1e 100%);
background-size:
400% 100%,
100% 100%;
background-blend-mode: overlay, normal;
border: 6px solid #bcffff;
border-radius: 20px;
margin-bottom: -20px;
z-index: 9;
padding: 30px 20px;
text-align: center; text-align: center;
box-shadow: box-shadow:
inset 0 2px 4px rgba(255, 255, 255, 0.1), 0 0 20px rgba(0, 255, 255, 0.9),
0 0 40px rgba(0, 255, 255, 0.7),
0 0 60px rgba(255, 0, 255, 0.6),
0 0 80px rgba(255, 255, 0, 0.5),
0 0 100px rgba(255, 0, 255, 0.4),
inset 0 2px 8px rgba(255, 255, 255, 0.3),
inset 0 -2px 4px rgba(0, 0, 0, 0.5); inset 0 -2px 4px rgba(0, 0, 0, 0.5);
animation: marqueeGradient 8s linear infinite;
@include media-down(lg) { @include media-down(lg) {
border-radius: 0; border-radius: 0;
padding: 15px 10px; padding: 20px 10px;
border-left: none; }
border-right: none;
// Sound toggle in marquee
.marquee-sound-toggle {
position: absolute;
top: 50%;
right: 80px;
transform: translateY(-50%);
text-align: center;
z-index: 10;
@include media-down(lg) {
right: 50px;
}
} }
.marquee-content { .marquee-content {
color: #0f0;
text-shadow:
0 0 10px rgba(0, 255, 0, 0.8),
0 0 20px rgba(0, 255, 0, 0.5);
font-family: "Pixelify Sans", monospace; font-family: "Pixelify Sans", monospace;
@include media-down(lg) { @include media-down(lg) {
@ -116,23 +154,51 @@
} }
} }
.arcade-title-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.arcade-title { .arcade-title {
margin: 0; margin: 0;
animation: textGlow 2s ease-in-out infinite alternate; color: #fff;
font-weight: bold;
animation: arcadeTitleGlow 2s ease-in-out infinite alternate;
}
.arcade-subtitle {
font-size: 1rem;
font-weight: bold;
letter-spacing: 3px;
color: #ffff00;
text-shadow:
0 0 10px rgba(255, 255, 0, 1),
0 0 20px rgba(255, 255, 0, 0.8),
0 0 30px rgba(255, 165, 0, 0.6);
animation: subtitlePulse 1.5s ease-in-out infinite;
@include media-down(lg) {
font-size: 0.7rem;
letter-spacing: 2px;
}
} }
} }
.marquee-light { .marquee-light {
position: absolute; position: absolute;
top: 50%; top: 50%;
width: 20px; width: 24px;
height: 20px; height: 24px;
background: #ff0; background: radial-gradient(circle at 30% 30%, #fff, #ff00ff);
border-radius: 50%; border-radius: 50%;
box-shadow: box-shadow:
0 0 15px #ff0, 0 0 20px rgba(255, 0, 255, 1),
0 0 30px #ff0; 0 0 40px rgba(255, 0, 255, 0.8),
animation: marquee-blink 1.5s ease-in-out infinite alternate; 0 0 60px rgba(0, 255, 255, 0.6),
inset 0 0 10px rgba(255, 255, 255, 0.5);
animation: marqueeLightCycle 2s ease-in-out infinite;
&.marquee-light-left { &.marquee-light-left {
left: 20px; left: 20px;
@ -140,12 +206,12 @@
&.marquee-light-right { &.marquee-light-right {
right: 20px; right: 20px;
animation-delay: 0.75s; animation-delay: 1s;
} }
@include media-down(lg) { @include media-down(lg) {
width: 12px; width: 16px;
height: 12px; height: 16px;
&.marquee-light-left { &.marquee-light-left {
left: 10px; left: 10px;
@ -156,13 +222,76 @@
} }
} }
@keyframes marquee-blink { @keyframes marqueeGradient {
0% {
background-position:
0% 50%,
0% 0%;
}
100% {
background-position:
400% 50%,
0% 0%;
}
}
@keyframes marqueeLightCycle {
0%,
100% {
background: radial-gradient(circle at 30% 30%, #fff, #ff00ff);
box-shadow:
0 0 20px rgba(255, 0, 255, 1),
0 0 40px rgba(255, 0, 255, 0.8),
0 0 60px rgba(0, 255, 255, 0.6);
}
33% {
background: radial-gradient(circle at 30% 30%, #fff, #00ffff);
box-shadow:
0 0 20px rgba(0, 255, 255, 1),
0 0 40px rgba(0, 255, 255, 0.8),
0 0 60px rgba(255, 255, 0, 0.6);
}
66% {
background: radial-gradient(circle at 30% 30%, #fff, #ffff00);
box-shadow:
0 0 20px rgba(255, 255, 0, 1),
0 0 40px rgba(255, 255, 0, 0.8),
0 0 60px rgba(255, 0, 255, 0.6);
}
}
@keyframes subtitlePulse {
0%, 0%,
100% { 100% {
opacity: 1; opacity: 1;
transform: scale(1);
} }
50% { 50% {
opacity: 0.3; opacity: 0.7;
transform: scale(1.05);
}
}
@keyframes arcadeTitleGlow {
0% {
text-shadow:
0 0 10px rgba(0, 255, 255, 1),
0 0 20px rgba(0, 255, 255, 0.8),
0 0 30px rgba(255, 0, 255, 0.6),
0 0 40px rgba(255, 0, 255, 0.4),
2px 2px 0px rgba(255, 0, 255, 0.8),
-2px -2px 0px rgba(0, 255, 255, 0.8);
filter: drop-shadow(0 0 20px rgba(255, 255, 0, 0.5));
}
100% {
text-shadow:
0 0 15px rgba(0, 255, 255, 1),
0 0 25px rgba(0, 255, 255, 0.9),
0 0 35px rgba(255, 0, 255, 0.7),
0 0 50px rgba(255, 0, 255, 0.5),
2px 2px 0px rgba(255, 0, 255, 0.9),
-2px -2px 0px rgba(0, 255, 255, 0.9);
filter: drop-shadow(0 0 30px rgba(255, 255, 0, 0.7));
} }
} }
@ -171,19 +300,31 @@
// ============================================ // ============================================
.arcade-screen-bezel { .arcade-screen-bezel {
background: linear-gradient(145deg, #e8e0c8, #c4b89a); background:
linear-gradient(
135deg,
rgba(255, 0, 255, 0.2) 0%,
rgba(0, 255, 255, 0.2) 100%
),
linear-gradient(145deg, #3a3a5e, #2a2a4e);
padding: 30px; padding: 30px;
margin: 30px; margin: 30px;
margin-top: 0px; margin-top: 0px;
border-radius: 12px; border-radius: 12px;
border: 3px solid #5a5a8e;
box-shadow: box-shadow:
inset 0 4px 8px rgba(0, 0, 0, 0.4), 0 0 20px rgba(138, 43, 226, 0.3),
inset 0 -2px 4px rgba(255, 255, 255, 0.3); 0 0 40px rgba(0, 255, 255, 0.2),
inset 0 4px 8px rgba(0, 0, 0, 0.6),
inset 0 -2px 4px rgba(138, 43, 226, 0.3);
position: relative; position: relative;
@include media-down(lg) { @include media-down(lg) {
margin: 15px; margin: 15px;
padding: 20px; padding: 20px;
border: 0;
padding: 0;
margin: 0;
} }
&::before { &::before {
@ -192,25 +333,38 @@
bottom: 10px; bottom: 10px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
color: #666; color: #7dffff;
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
letter-spacing: 2px; letter-spacing: 3px;
text-shadow:
0 0 5px rgba(0, 255, 255, 0.8),
0 0 10px rgba(0, 255, 255, 0.5);
@include media-down(lg) {
display: none;
}
} }
} }
.arcade-screen { .arcade-screen {
width: 100%; width: 100%;
min-height: 500px; min-height: 500px;
background: #000; background:
radial-gradient(ellipse at center, rgba(138, 43, 226, 0.05) 0%, #000 60%),
#000;
border-radius: 8px; border-radius: 8px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border: 2px solid #1a1a3e;
box-shadow: box-shadow:
inset 0 0 80px rgba(0, 255, 100, 0.1), 0 0 30px rgba(138, 43, 226, 0.4),
inset 0 0 40px rgba(0, 255, 100, 0.05), 0 0 60px rgba(0, 255, 255, 0.2),
inset 3px 3px 8px rgba(255, 255, 255, 0.1), inset 0 0 100px rgba(138, 43, 226, 0.15),
inset -3px -3px 8px rgba(0, 0, 0, 0.5); inset 0 0 80px rgba(0, 255, 255, 0.1),
inset 0 0 40px rgba(255, 0, 255, 0.08),
inset 3px 3px 8px rgba(138, 43, 226, 0.2),
inset -3px -3px 8px rgba(0, 0, 0, 0.8);
@include media-down(lg) { @include media-down(lg) {
min-height: 400px; min-height: 400px;
@ -268,10 +422,12 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 30px; padding: 30px;
color: #0f0; color: #7dffff;
z-index: 1; z-index: 1;
font-family: monospace; font-family: monospace;
text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); text-shadow:
0 0 10px rgba(0, 255, 255, 0.8),
0 0 20px rgba(138, 43, 226, 0.4);
@include media-down(lg) { @include media-down(lg) {
padding: 20px; padding: 20px;
@ -288,37 +444,90 @@
margin-bottom: 30px; margin-bottom: 30px;
@include media-down(lg) { @include media-down(lg) {
padding-top: 10px;
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: 15px; gap: 15px;
max-height: 400px; max-height: auto;
overflow-y: auto; overflow-y: auto;
@include scrollbar-custom(#0f0, 6px); @include scrollbar-custom(#7dffff, 6px);
} }
} }
.game-card { .game-card {
background: rgba(0, 255, 0, 0.05); background: linear-gradient(
border: 2px solid rgba(0, 255, 0, 0.3); 135deg,
rgba(138, 43, 226, 0.08) 0%,
rgba(0, 255, 255, 0.08) 100%
);
border: 2px solid rgba(0, 255, 255, 0.3);
border-radius: 8px; border-radius: 8px;
padding: 15px; padding: 15px;
transition: all 0.3s ease; transition: all 0.3s ease;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
box-shadow: 0 0 10px rgba(138, 43, 226, 0.2);
@include enhance-3d-transform; @include enhance-3d-transform;
&:hover, &:hover,
&.selected { &.selected {
background: rgba(0, 255, 0, 0.1); background: linear-gradient(
border-color: rgba(0, 255, 0, 0.8); 135deg,
rgba(138, 43, 226, 0.15) 0%,
rgba(0, 255, 255, 0.15) 100%
);
border-color: rgba(0, 255, 255, 0.8);
box-shadow: box-shadow:
0 0 20px rgba(0, 255, 0, 0.4), 0 0 20px rgba(0, 255, 255, 0.5),
inset 0 0 10px rgba(0, 255, 0, 0.1); 0 0 40px rgba(138, 43, 226, 0.3),
inset 0 0 20px rgba(0, 255, 255, 0.1);
transform: translateY(-2px); transform: translateY(-2px);
@include media-down(lg) {
box-shadow: 0 0;
animation: none;
}
} }
&.selected { &.selected {
animation: card-pulse 1s ease-in-out infinite alternate; animation: card-pulse 1s ease-in-out infinite alternate;
.game-logo {
filter: drop-shadow(0 0 10px rgba(0, 255, 255, 0.8))
drop-shadow(0 0 20px rgba(138, 43, 226, 0.6));
}
.game-title {
color: #ffff00;
text-shadow:
0 0 10px rgba(255, 255, 0, 1),
0 0 20px rgba(255, 0, 255, 0.6);
}
}
// Unreleased games styling
&.unreleased {
opacity: 0.5;
cursor: not-allowed;
filter: grayscale(0.7);
&:hover,
&.selected {
background: linear-gradient(
135deg,
rgba(138, 43, 226, 0.08) 0%,
rgba(0, 255, 255, 0.08) 100%
);
border-color: rgba(0, 255, 255, 0.3);
box-shadow: 0 0 10px rgba(138, 43, 226, 0.2);
transform: none;
animation: none;
}
.game-logo {
opacity: 0.7;
filter: grayscale(0.7);
}
} }
// ============================================ // ============================================
@ -327,7 +536,36 @@
.game-summary { .game-summary {
display: flex; display: flex;
flex-direction: column;
gap: 15px; gap: 15px;
position: relative;
.game-link {
display: contents;
text-decoration: none;
}
.coming-soon-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-family: "Pixelify Sans", monospace;
font-size: 1.2rem;
font-weight: bold;
color: #ff0;
text-shadow:
0 0 10px rgba(255, 255, 0, 0.8),
0 0 20px rgba(255, 255, 0, 0.5),
2px 2px 0px rgba(0, 0, 0, 0.8);
letter-spacing: 2px;
z-index: 10;
background: rgba(0, 0, 0, 0.7);
padding: 8px 16px;
border-radius: 4px;
border: 2px solid rgba(255, 255, 0, 0.6);
animation: coming-soon-pulse 2s ease-in-out infinite;
}
} }
.game-thumbnail { .game-thumbnail {
@ -337,13 +575,16 @@
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(0, 255, 0, 0.3); border: 1px solid rgba(0, 255, 255, 0.4);
box-shadow:
0 0 10px rgba(138, 43, 226, 0.3),
inset 0 0 10px rgba(0, 255, 255, 0.1);
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
filter: contrast(1.1) brightness(0.9); filter: contrast(1.2) brightness(0.95) saturate(1.2);
} }
} }
@ -358,6 +599,42 @@
} }
} }
.game-logo {
flex-shrink: 0;
width: 100%;
border-radius: 6px;
overflow: visible;
background: #7dffff;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
-webkit-mask-size: contain;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
img {
width: 100%;
height: 100%;
object-fit: contain;
opacity: 0;
}
}
.game-logo-placeholder {
display: flex;
align-items: center;
justify-content: center;
.placeholder-icon {
font-size: 2rem;
opacity: 0.5;
}
}
.game-info { .game-info {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
@ -365,15 +642,26 @@
.game-title { .game-title {
font-size: 1.2rem; font-size: 1.2rem;
margin: 0 0 8px 0; margin: 0;
color: #7dffff;
font-weight: bold;
text-shadow:
0 0 5px rgba(0, 255, 255, 0.8),
0 0 10px rgba(138, 43, 226, 0.4);
a { a {
color: greenyellow; color: #7dffff;
text-decoration: none; text-decoration: none;
text-shadow: 0 0 5px rgba(173, 255, 47, 0.5); font-weight: bold;
text-shadow:
0 0 5px rgba(0, 255, 255, 0.8),
0 0 10px rgba(138, 43, 226, 0.4);
&:hover { &:hover {
text-shadow: 0 0 10px rgba(173, 255, 47, 0.8); color: #ffff00;
text-shadow:
0 0 10px rgba(255, 255, 0, 1),
0 0 20px rgba(255, 0, 255, 0.6);
} }
} }
} }
@ -391,19 +679,41 @@
.game-genre { .game-genre {
display: inline-block; display: inline-block;
padding: 3px 8px; padding: 3px 8px;
background: rgba(0, 255, 0, 0.2); background: linear-gradient(
border: 1px solid rgba(0, 255, 0, 0.5); 135deg,
rgba(138, 43, 226, 0.3) 0%,
rgba(0, 255, 255, 0.3) 100%
);
border: 1px solid rgba(0, 255, 255, 0.6);
border-radius: 3px; border-radius: 3px;
box-shadow: 0 0 5px rgba(138, 43, 226, 0.4);
} }
} }
} }
@keyframes card-pulse { @keyframes card-pulse {
0% { 0% {
box-shadow: 0 0 20px rgba(0, 255, 0, 0.4); box-shadow:
0 0 20px rgba(0, 255, 255, 0.5),
0 0 40px rgba(138, 43, 226, 0.3);
} }
100% { 100% {
box-shadow: 0 0 30px rgba(0, 255, 0, 0.6); box-shadow:
0 0 30px rgba(0, 255, 255, 0.7),
0 0 60px rgba(138, 43, 226, 0.5),
0 0 80px rgba(255, 0, 255, 0.3);
}
}
@keyframes coming-soon-pulse {
0%,
100% {
opacity: 0.9;
transform: translate(-50%, -50%) scale(1);
}
50% {
opacity: 1;
transform: translate(-50%, -50%) scale(1.05);
} }
} }
} }
@ -473,16 +783,20 @@
.control-panel-surface { .control-panel-surface {
background: background:
linear-gradient(135deg, #4a4a4a 25%, transparent 25%), linear-gradient(
linear-gradient(225deg, #4a4a4a 25%, transparent 25%), 135deg,
linear-gradient(45deg, #3a3a3a 25%, transparent 25%), rgba(138, 43, 226, 0.15) 0%,
linear-gradient(315deg, #3a3a3a 25%, #2a2a2a 25%); rgba(0, 255, 255, 0.15) 50%,
background-size: 20px 20px; rgba(255, 0, 255, 0.15) 100%
background-position: ),
0 0, repeating-linear-gradient(
0 10px, 45deg,
10px -10px, #2a2a4e 0px,
-10px 0px; #2a2a4e 10px,
#1a1a3e 10px,
#1a1a3e 20px
),
linear-gradient(180deg, #2a2a4e 0%, #1a1a3e 100%);
height: 225px; height: 225px;
padding: 40px; padding: 40px;
position: relative; position: relative;
@ -493,20 +807,23 @@
transform-origin: bottom center; transform-origin: bottom center;
transform-style: preserve-3d; transform-style: preserve-3d;
// Enhanced 3D beveled edges // Enhanced 3D beveled edges with neon glow
box-shadow: box-shadow:
// Neon glow
0 0 20px rgba(138, 43, 226, 0.3),
0 0 40px rgba(0, 255, 255, 0.2),
// Stronger outer shadow for lifted appearance // Stronger outer shadow for lifted appearance
0 12px 24px rgba(0, 0, 0, 0.6), 0 12px 24px rgba(0, 0, 0, 0.6),
0 8px 16px rgba(0, 0, 0, 0.4), 0 8px 16px rgba(0, 0, 0, 0.4),
0 4px 8px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.3),
// Top highlight (raised effect) // Top highlight (raised effect)
inset 0 3px 0 rgba(0, 0, 0, 0.2), inset 0 3px 0 rgba(0, 255, 255, 0.2),
inset 0 6px 12px rgba(0, 0, 0, 0.08), inset 0 6px 12px rgba(138, 43, 226, 0.08),
// Bottom shadow (depth) // Bottom shadow (depth)
inset 0 -3px 0 rgba(0, 0, 0, 0.7), inset 0 -3px 0 rgba(0, 0, 0, 0.7),
inset 0 -6px 12px rgba(0, 0, 0, 0.5), inset 0 -6px 12px rgba(0, 0, 0, 0.5),
// Left highlight // Left highlight
inset 3px 0 0 rgba(255, 255, 255, 0.12), inset 3px 0 0 rgba(138, 43, 226, 0.15),
// Right shadow // Right shadow
inset -3px 0 0 rgba(0, 0, 0, 0.6); inset -3px 0 0 rgba(0, 0, 0, 0.6);
@ -522,12 +839,12 @@
background: background:
radial-gradient( radial-gradient(
ellipse at top left, ellipse at top left,
rgba(255, 255, 255, 0.1) 0%, rgba(0, 255, 255, 0.15) 0%,
transparent 50% transparent 50%
), ),
radial-gradient( radial-gradient(
ellipse at bottom right, ellipse at bottom right,
rgba(0, 0, 0, 0.4) 0%, rgba(138, 43, 226, 0.2) 0%,
transparent 50% transparent 50%
); );
pointer-events: none; pointer-events: none;
@ -558,8 +875,11 @@
margin-top: 10px; margin-top: 10px;
font-family: "Pixelify Sans", monospace; font-family: "Pixelify Sans", monospace;
font-size: 0.75rem; font-size: 0.75rem;
color: #666; color: #7dffff;
letter-spacing: 1px; letter-spacing: 1px;
text-shadow:
0 0 5px rgba(0, 255, 255, 0.6),
0 0 10px rgba(138, 43, 226, 0.3);
} }
// Buttons overlay - positioned absolutely to avoid perspective transform // Buttons overlay - positioned absolutely to avoid perspective transform
@ -578,23 +898,6 @@
} }
} }
// Sound toggle overlay - positioned absolutely to avoid perspective transform
.sound-toggle-overlay {
position: absolute;
right: 15%;
top: 50%;
transform: translate(0, -50%) scaleY(0.85);
z-index: 10;
pointer-events: none;
text-align: center;
@include media-down(lg) {
position: static;
transform: none;
pointer-events: auto;
}
}
// ============================================ // ============================================
// JOYSTICK // JOYSTICK
// ============================================ // ============================================
@ -605,7 +908,7 @@
// Position absolutely to place on control panel without perspective transform // Position absolutely to place on control panel without perspective transform
position: absolute; position: absolute;
left: 12.5%; left: 12.5%;
top: 35%; top: 40%;
transform: translateY(-50%); transform: translateY(-50%);
z-index: 0; // Behind the control panel surface z-index: 0; // Behind the control panel surface
pointer-events: none; // Allow clicks to pass through to buttons below pointer-events: none; // Allow clicks to pass through to buttons below
@ -685,12 +988,14 @@
transform: translateX(-50%); transform: translateX(-50%);
width: 40px; width: 40px;
height: 40px; height: 40px;
background: radial-gradient(circle at 30% 30%, #ff3333 0%, #cc0000 100%); background: radial-gradient(circle at 30% 30%, #00ffff 0%, #00aaaa 100%);
border-radius: 50%; border-radius: 50%;
box-shadow: box-shadow:
0 0 15px rgba(0, 255, 255, 0.6),
0 0 30px rgba(0, 255, 255, 0.4),
0 4px 8px rgba(0, 0, 0, 0.6), 0 4px 8px rgba(0, 0, 0, 0.6),
inset -2px -2px 4px rgba(0, 0, 0, 0.4), inset -2px -2px 4px rgba(0, 0, 0, 0.4),
inset 2px 2px 4px rgba(255, 100, 100, 0.4); inset 2px 2px 4px rgba(0, 255, 255, 0.6);
} }
.joystick.up .joystick-stick { .joystick.up .joystick-stick {
@ -771,6 +1076,8 @@
border-radius: 50%; border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ffff00 0%, #cccc00 100%); background: radial-gradient(circle at 30% 30%, #ffff00 0%, #cccc00 100%);
box-shadow: box-shadow:
0 0 15px rgba(255, 255, 0, 0.6),
0 0 30px rgba(255, 255, 0, 0.4),
0 6px 0 #999900, 0 6px 0 #999900,
0 8px 12px rgba(0, 0, 0, 0.6), 0 8px 12px rgba(0, 0, 0, 0.6),
inset -2px -2px 4px rgba(0, 0, 0, 0.3), inset -2px -2px 4px rgba(0, 0, 0, 0.3),
@ -781,27 +1088,33 @@
} }
.arcade-button-select .button-top { .arcade-button-select .button-top {
background: radial-gradient(circle at 30% 30%, #ff3333 0%, #cc0000 100%); background: radial-gradient(circle at 30% 30%, #ff00ff 0%, #cc00cc 100%);
box-shadow: box-shadow:
0 6px 0 #990000, 0 0 15px rgba(255, 0, 255, 0.6),
0 0 30px rgba(255, 0, 255, 0.4),
0 6px 0 #990099,
0 8px 12px rgba(0, 0, 0, 0.6), 0 8px 12px rgba(0, 0, 0, 0.6),
inset -2px -2px 4px rgba(0, 0, 0, 0.3), inset -2px -2px 4px rgba(0, 0, 0, 0.3),
inset 2px 2px 4px rgba(255, 100, 100, 0.5); inset 2px 2px 4px rgba(255, 100, 255, 0.5);
} }
.arcade-button-back .button-top { .arcade-button-back .button-top {
background: radial-gradient(circle at 30% 30%, #3333ff 0%, #0000cc 100%); background: radial-gradient(circle at 30% 30%, #00ffff 0%, #00cccc 100%);
box-shadow: box-shadow:
0 6px 0 #000099, 0 0 15px rgba(0, 255, 255, 0.6),
0 0 30px rgba(0, 255, 255, 0.4),
0 6px 0 #009999,
0 8px 12px rgba(0, 0, 0, 0.6), 0 8px 12px rgba(0, 0, 0, 0.6),
inset -2px -2px 4px rgba(0, 0, 0, 0.3), inset -2px -2px 4px rgba(0, 0, 0, 0.3),
inset 2px 2px 4px rgba(100, 100, 255, 0.5); inset 2px 2px 4px rgba(100, 255, 255, 0.5);
} }
.arcade-button:active .button-top, .arcade-button:active .button-top,
.arcade-button.pressed .button-top { .arcade-button.pressed .button-top {
top: 4px; top: 4px;
box-shadow: box-shadow:
0 0 10px rgba(255, 255, 0, 0.5),
0 0 20px rgba(255, 255, 0, 0.3),
0 2px 0 #999900, 0 2px 0 #999900,
0 4px 8px rgba(0, 0, 0, 0.6), 0 4px 8px rgba(0, 0, 0, 0.6),
inset -2px -2px 4px rgba(0, 0, 0, 0.3), inset -2px -2px 4px rgba(0, 0, 0, 0.3),
@ -811,19 +1124,23 @@
.arcade-button-select:active .button-top, .arcade-button-select:active .button-top,
.arcade-button-select.pressed .button-top { .arcade-button-select.pressed .button-top {
box-shadow: box-shadow:
0 2px 0 #990000, 0 0 10px rgba(255, 0, 255, 0.5),
0 0 20px rgba(255, 0, 255, 0.3),
0 2px 0 #990099,
0 4px 8px rgba(0, 0, 0, 0.6), 0 4px 8px rgba(0, 0, 0, 0.6),
inset -2px -2px 4px rgba(0, 0, 0, 0.3), inset -2px -2px 4px rgba(0, 0, 0, 0.3),
inset 2px 2px 4px rgba(255, 100, 100, 0.5); inset 2px 2px 4px rgba(255, 100, 255, 0.5);
} }
.arcade-button-back:active .button-top, .arcade-button-back:active .button-top,
.arcade-button-back.pressed .button-top { .arcade-button-back.pressed .button-top {
box-shadow: box-shadow:
0 2px 0 #000099, 0 0 10px rgba(0, 255, 255, 0.5),
0 0 20px rgba(0, 255, 255, 0.3),
0 2px 0 #009999,
0 4px 8px rgba(0, 0, 0, 0.6), 0 4px 8px rgba(0, 0, 0, 0.6),
inset -2px -2px 4px rgba(0, 0, 0, 0.3), inset -2px -2px 4px rgba(0, 0, 0, 0.3),
inset 2px 2px 4px rgba(100, 100, 255, 0.5); inset 2px 2px 4px rgba(100, 255, 255, 0.5);
} }
// ============================================ // ============================================
@ -837,10 +1154,10 @@
.sound-toggle { .sound-toggle {
width: 50px; width: 50px;
height: 50px; height: 50px;
border: 2px solid #3a3a3a; border: 2px solid #5a5a8e;
border-radius: 8px; border-radius: 8px;
background: #1a1a1a; background: linear-gradient(145deg, #2a2a4e, #1a1a3e);
color: #0f0; color: #ffff00;
font-size: 1.5rem; font-size: 1.5rem;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
@ -848,15 +1165,19 @@
z-index: 10; z-index: 10;
position: relative; position: relative;
box-shadow: box-shadow:
0 0 15px rgba(255, 255, 0, 0.4),
0 0 30px rgba(255, 255, 0, 0.2),
inset 0 2px 4px rgba(0, 0, 0, 0.6), inset 0 2px 4px rgba(0, 0, 0, 0.6),
0 2px 4px rgba(0, 0, 0, 0.3); 0 2px 4px rgba(0, 0, 0, 0.3);
&:hover { &:hover {
background: #2a2a2a; background: linear-gradient(145deg, #3a3a5e, #2a2a4e);
border-color: #7a7aae;
box-shadow: box-shadow:
0 0 20px rgba(255, 255, 0, 0.6),
0 0 40px rgba(255, 255, 0, 0.4),
inset 0 2px 4px rgba(0, 0, 0, 0.6), inset 0 2px 4px rgba(0, 0, 0, 0.6),
0 2px 4px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.3);
0 0 10px rgba(0, 255, 0, 0.3);
} }
&:active { &:active {
@ -866,8 +1187,10 @@
&:focus { &:focus {
outline: none; outline: none;
box-shadow: box-shadow:
0 0 20px rgba(255, 255, 0, 0.8),
0 0 40px rgba(255, 255, 0, 0.6),
inset 0 2px 4px rgba(0, 0, 0, 0.6), inset 0 2px 4px rgba(0, 0, 0, 0.6),
0 0 0 2px rgba(0, 255, 0, 0.5); 0 0 0 2px rgba(255, 255, 0, 0.5);
} }
} }
@ -875,23 +1198,44 @@
display: inline-block; display: inline-block;
} }
.sound-label {
text-align: center;
margin-top: 8px;
font-family: "Pixelify Sans", monospace;
font-size: 0.65rem;
color: #7dffff;
letter-spacing: 1px;
text-shadow:
0 0 5px rgba(0, 255, 255, 0.6),
0 0 10px rgba(138, 43, 226, 0.3);
}
// ============================================ // ============================================
// CABINET BASE // CABINET BASE
// ============================================ // ============================================
.cabinet-base { .cabinet-base {
width: 112%; width: 112.5%;
margin-left: -6%; margin-left: -6%;
height: 80px; height: 80px;
background: linear-gradient(180deg, #2d1f12 0%, #1d1208 100%); background:
border: 4px solid #2a2a2a; linear-gradient(
135deg,
rgba(255, 0, 255, 0.15) 0%,
rgba(0, 255, 255, 0.15) 50%,
rgba(255, 255, 0, 0.15) 100%
),
linear-gradient(180deg, #2a2a4e 0%, #1a1a2e 100%);
border: 6px solid #5a5a8e;
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: box-shadow:
0 0 40px rgba(138, 43, 226, 0.4),
0 0 80px rgba(0, 255, 255, 0.3),
0 30px 60px rgba(0, 0, 0, 0.9), 0 30px 60px rgba(0, 0, 0, 0.9),
inset 0 2px 4px rgba(255, 255, 255, 0.1), inset 0 2px 4px rgba(255, 255, 255, 0.2),
inset 0 -2px 4px rgba(0, 0, 0, 0.5); inset 0 -2px 4px rgba(0, 0, 0, 0.5);
@include media-down(lg) { @include media-down(lg) {
@ -916,10 +1260,13 @@
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
font-size: 0.65rem; font-size: 0.65rem;
color: #666; color: #7dffff;
font-family: "Pixelify Sans", monospace; font-family: "Pixelify Sans", monospace;
letter-spacing: 1px; letter-spacing: 1px;
white-space: nowrap; white-space: nowrap;
text-shadow:
0 0 5px rgba(0, 255, 255, 0.8),
0 0 10px rgba(138, 43, 226, 0.4);
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View file

@ -0,0 +1,47 @@
---
title: "Badgeware 2350 Are Pretty Cool"
date: 2026-02-02
tags:
- gadgets
draft: false
---
My set of [Badgeware](https://badgewa.re/) badges arrived this week and I wanted to take a minute just go over unboxing and my initial thoughts. Youve already had the spoiler; I think theyre pretty cool. 
{{< img src="images/DSCF3545.jpg" alt="Set of three Badgeware badge boxes" width="900x" >}}
# What are they?
Made by [Pimoroni](https://shop.pimoroni.com/) here in the UK, they are smart badges that run MicroPython and have a little battery onboard, with WiFi and Bluetooth too. They come in three flavours: eInk, IPS display and LED array. I got one of each. The underlying specs are the same regardless of which version youre looking at, an RP2350, 16mb flash storage, and 8mb PSRAM. You can check out the full specs on the [website](https://badgewa.re/) if youre into that sort of thing.
{{< img src="images/DSCF3552.jpg" alt="Box contents of the Tufty 2350" width="900x" >}}
# Unboxing
Popping them open is quick enough, they come in recycled feeling cardboard (it doesn't state it is, but it isn't the horribly glossy style cardboard) and inside the box is the device, a different lanyard in for each type, a brief getting started leaflet and most importantly a STICKER. 
{{< img src="images/DSCF3553.jpg" alt="Stickers waiting to find a home on one of my laptops" width="900x" >}}
The only downside is theres no protection on the screen casing itself, and one of mine arrived with a scratch - I presume from the lanyard clip - but a brief email to Pimoroni support and they had dispatched a replacement cover the same day so I cant complain about that. I do feel this could have been avoided, but at the cost of adding disposable packaging to the box. I guess for the small amount of replacement covers they might have to issue the planet is overall thankful for the lack of disposable packaging included.
# Initial Impressions
The devices themselves feel sturdy, though for longevity if I were going to be using these anywhere other than around my office I would likely try and find a screen protector to fit. 
The boot up process is essentially instant across devices, the eink display takes a little while to refresh but this seems to be tinker-able according to the API documentation so I should be able to speed this up for my own purposes once an application is loaded - at the cost of ghosting. Thats the price you pay for eink.
The LEDs on the LED version are super bright which is awesome, and the brightness can be adjusted programmatically if you wanted something a bit dimmer.
The IPS screen is vivid and bright, and has an awesome menu screen with a background animation. 
Navigating through the menus and demo projects is largely a good experience, eink being the exception of course. Once you load up an actual application regardless of device though none of the demos seemed to indicate that the device wont be perfectly capable for a variety of uses included some small games. 
# Planned Projects
I dont think Ill be making any games on these devices any time soon - I have enough other projects on the go. 
The first projects I want to tackle are displaying my site visitor stats on the screen, the latest guestbook post, and the top scores of Whittler. The nice thing is that for the most part these should easily work on any of the three devices, only needing adjustments for the screen dimensions. 
The [documentation is available online](https://badgewa.re/docs), though doesnt seem fully complete as of time of posting - its missing documentation on the scroll action thats present in a few of the LED demos for example. Pimoroni have a history about being very good about this kind of thing though, so expect it will be updated in due course.
Time to get tinkering! 

View file

@ -1,10 +0,0 @@
---
title: "Test"
type: games
logo: logo.avif
thumbnail: "logo.avif"
description: "A clicker game where you whittle wood to earn ¥1,000,000"
genre: "Clicker"
---
Whittler the game :)

View file

@ -0,0 +1,11 @@
---
title: "Tiddler"
type: games
layout: tiddler
logo: logo.avif
thumbnail: "logo.avif"
description: "Coming Soon"
released: false
---
Whittler the game :)

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -2,10 +2,10 @@
title: "Whittler" title: "Whittler"
type: games type: games
layout: whittler layout: whittler
logo: logo.avif logo: whittler.webp
thumbnail: "logo.avif" thumbnail: "logo.avif"
description: "A clicker game where you whittle wood to earn ¥1,000,000" description: "A clicker game where you whittle wood to earn ¥1,000,000"
genre: "Clicker" released: true
--- ---
Whittler the game :) Whittler the game :)

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 KiB

View file

@ -1,7 +1,7 @@
{{ define "main" }} {{ define "main" }}
<div class="games-arcade-page"> <div class="games-arcade-page">
<div class="desk"></div> <div class="desk hidden-lg-down"></div>
<div class="xfiles-poster hidden-lg-down"> <div class="xfiles-poster hidden-lg-down">
<div class="xfiles-content"> <div class="xfiles-content">
@ -20,22 +20,44 @@
<div class="believe-text">I WANT TO<br />BELIEVE</div> <div class="believe-text">I WANT TO<br />BELIEVE</div>
</div> </div>
</div> </div>
<div class="arcade-neon">{{ partial "elements/neon-sign.html" . }}</div> <div class="arcade-neon hidden-lg-down">
<div class="arcade-lamp">{{ partial "elements/lavalamp.html" . }}</div> {{ partial "elements/neon-sign.html" . }}
</div>
<div class="arcade-lamp hidden-lg-down">
{{ partial "elements/lavalamp.html" . }}
</div>
<div class="arcade-container"> <div class="arcade-container">
<!-- Arcade Cabinet Top/Marquee --> <!-- Arcade Cabinet Top/Marquee -->
<div class="arcade-marquee"> <div class="arcade-marquee">
<div class="marquee-light marquee-light-left"></div> <div class="marquee-light marquee-light-left hidden-lg-down"></div>
<div class="marquee-content"> <div class="marquee-content">
<pre class="arcade-title"> <div class="arcade-title-wrapper">
██████ █████ ███ ███ ███████ ███████ <pre class="arcade-title">
██ ██ ██ ████ ████ ██ ██ ██████╗ █████╗ ███╗ ███╗███████╗███████╗
██ ███ ███████ ██ ████ ██ █████ ███████ ██╔════╝ ██╔══██╗████╗ ████║██╔════╝██╔════╝
██ ██ ██ ██ ██ ██ ██ ██ ██ ██║ ███╗███████║██╔████╔██║█████╗ ███████╗
██████ ██ ██ ██ ██ ███████ ███████ ██║ ██║██╔══██║██║╚██╔╝██║██╔══╝ ╚════██║
</pre> ╚██████╔╝██║ ██║██║ ╚═╝ ██║███████╗███████║
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝
</pre>
<div class="arcade-subtitle">INSERT COIN</div>
</div>
</div>
<div class="marquee-light marquee-light-right hidden-lg-down"></div>
<!-- Sound Toggle in Header -->
<div class="marquee-sound-toggle">
<button
class="sound-toggle"
id="sound-toggle"
aria-label="Toggle Sound"
>
<span class="sound-icon sound-on">🔊</span>
<span class="sound-icon sound-off" style="display: none">🔇</span>
</button>
<div class="sound-label">SOUND</div>
</div> </div>
<div class="marquee-light marquee-light-right"></div>
</div> </div>
<!-- Main Cabinet Body --> <!-- Main Cabinet Body -->
@ -46,8 +68,13 @@
<div class="screen-content"> <div class="screen-content">
<!-- Games Grid/List --> <!-- Games Grid/List -->
<div class="games-grid" id="games-grid"> <div class="games-grid" id="games-grid">
{{ range $index, $element := .Paginator.Pages }} {{ range $index, $element := (.Paginator.Pages.ByParam
<div class="game-card" data-game-index="{{ $index }}"> "released").Reverse }}
<div
class="game-card{{ if not .Params.released }} unreleased{{ end }}"
data-game-index="{{ $index }}"
data-released="{{ .Params.released }}"
>
{{ .Render "summary" }} {{ .Render "summary" }}
</div> </div>
{{ end }} {{ end }}
@ -68,7 +95,7 @@
</div> </div>
<!-- Control Panel --> <!-- Control Panel -->
<div class="control-panel"> <div class="control-panel hidden-lg-down">
<!-- Joystick (separate from surface to avoid perspective transform) --> <!-- Joystick (separate from surface to avoid perspective transform) -->
<div class="joystick-container"> <div class="joystick-container">
<div class="joystick" id="arcade-joystick"> <div class="joystick" id="arcade-joystick">
@ -77,11 +104,10 @@
<div class="joystick-ball"></div> <div class="joystick-ball"></div>
</div> </div>
</div> </div>
<div class="control-label">MOVE</div>
</div> </div>
<!-- Buttons (separate from surface to avoid perspective transform) --> <!-- Buttons (separate from surface to avoid perspective transform) -->
<div class="buttons-overlay"> <div class="buttons-overlay hidden-lg-down">
<div class="buttons-container"> <div class="buttons-container">
<div class="arcade-button-wrap"> <div class="arcade-button-wrap">
<button <button
@ -106,26 +132,10 @@
</div> </div>
</div> </div>
<!-- Mute Toggle (separate from surface to avoid perspective transform) --> <div class="control-panel-surface hidden-lg-down"></div>
<div class="sound-toggle-overlay">
<button
class="sound-toggle"
id="sound-toggle"
aria-label="Toggle Sound"
>
<span class="sound-icon sound-on">🔊</span>
<span class="sound-icon sound-off" style="display: none">🔇</span>
</button>
<div class="control-label">SOUND</div>
</div>
<div class="control-panel-surface">
<!-- Visual surface only, no interactive elements -->
</div>
</div> </div>
<!-- Cabinet Base/Kick Plate --> <div class="cabinet-base hidden-lg-down">
<div class="cabinet-base">
<div class="coin-slot"></div> <div class="coin-slot"></div>
</div> </div>
</div> </div>

View file

@ -1,27 +1,27 @@
<div class="game-summary"> <div class="game-summary">
{{ if .Params.thumbnail }} {{ if .Params.released }}
<div class="game-thumbnail"> <a href="{{ .RelPermalink }}" class="game-link">
<img src="{{ .Params.thumbnail }}" alt="{{ .Title }}" loading="lazy"> {{ end }}
{{ if .Params.logo }}
<div
class="game-logo"
style="-webkit-mask-image: url('{{ .RelPermalink }}{{ .Params.logo }}'); mask-image: url('{{ .RelPermalink }}{{ .Params.logo }}');"
>
<img
src="{{ .RelPermalink }}{{ .Params.logo }}"
alt="{{ .Title }}"
loading="lazy"
/>
</div> </div>
{{ else }} {{ else }}
<div class="game-thumbnail game-thumbnail-placeholder"> <div class="game-logo game-logo-placeholder">
<div class="placeholder-icon">🎮</div> <div class="placeholder-icon">🎮</div>
</div> </div>
{{ end }}
{{ if not .Params.released }}
<div class="coming-soon-label">COMING SOON</div>
{{ end }}
{{ if .Params.released }}
</a>
{{ end }} {{ end }}
<div class="game-info">
<h3 class="game-title">
<a href="{{ .Permalink }}">{{ .Title }}</a>
</h3>
{{ if .Params.description }}
<p class="game-description">{{ .Params.description }}</p>
{{ end }}
{{ if .Params.genre }}
<div class="game-meta">
<span class="game-genre">{{ .Params.genre }}</span>
</div>
{{ end }}
</div>
</div> </div>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.