game/2d/ui/gridpanel.razor.scss
root {
width: 100%;
height: 100%;
position: absolute;
}
.outer {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-color: #0d0820;
display: flex;
align-items: center;
justify-content: center;
padding-right: 0px;
pointer-events: none;
transition: background-color 2s ease;
overflow: hidden;
}
.grid-wrap {
display: flex;
flex-direction: column;
gap: 6px;
pointer-events: none;
position: relative; /* ← sits above particles */
z-index: 2;
}
.grid-row {
display: flex;
flex-direction: row;
gap: 6px;
pointer-events: none;
}
.cell {
width: 100px; height: 100px;
border-radius: 14px;
border: 2px solid rgba(255,255,255,0.07);
background-color: rgba(255,255,255,0.03);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
pointer-events: all;
transition: transform 0.1s, background-color 0.15s, border-color 0.15s;
}
.cell:hover {
background-color: rgba(167,139,250,0.08);
transform: scale(1.05);
}
/* Bomb chest */
.cell.chest {
background-color: rgba(114,126,143,0.10);
border-color: rgba(148,163,184,0.45);
}
.cell.bomb {
background-color: rgba(109,40,217,0.32);
border-color: rgba(167,139,250,0.75);
}
.cell.bomb:hover {
background-color: rgba(109,40,217,0.48);
}
/* Chest cell */
.cell.chest-spawn {
background-color: rgba(114,126,143,0.10);
border-color: rgba(148,163,184,0.45);
animation: chest-spawn 0.45s cubic-bezier(0.22,1,0.36,1) both;
}
/* Danger column — chest one step from game over */
.cell.danger-col {
border-color: rgba(251,191,36,0.35);
animation: danger-col-pulse 0.8s ease-in-out infinite alternate;
}
.cell.chest.danger {
border-color: rgba(251,191,36,0.80);
background-color: rgba(251,191,36,0.12);
animation: danger-col-pulse 0.8s ease-in-out infinite alternate;
}
@keyframes danger-col-pulse {
0% { border-color: rgba(251,191,36,0.30); box-shadow: none; }
100% { border-color: rgba(251,191,36,0.90); box-shadow: 0 0 8px rgba(251,191,36,0.25); }
}
/* Chest spawn animations */
@keyframes chest-spawn {
0% { opacity: 0; transform: scale(0.5) translateY(-8px); }
60% { opacity: 1; transform: scale(1.08) translateY(0px); }
80% { transform: scale(0.96); }
100% { opacity: 1; transform: scale(1.0); }
}
@keyframes chest-spawn-gem {
0% { opacity: 0; transform: scale(0.4) translateY(-10px); }
50% { opacity: 1; transform: scale(1.15) translateY(0px); }
75% { transform: scale(0.94); }
100% { opacity: 1; transform: scale(1.0); }
}
@keyframes chest-spawn-boss {
0% { opacity: 0; transform: scale(0.3) rotate(-5deg); }
50% { opacity: 1; transform: scale(1.20) rotate(2deg); }
75% { transform: scale(0.95) rotate(-1deg); }
100% { opacity: 1; transform: scale(1.0) rotate(0deg); }
}
/* Destroyed chest — scorched dark remains */
.cell.cell-scorched {
background-color: rgba(100, 11, 11, 0.589);
border-color: rgba(163, 163, 163, 0.801);
opacity: 0.55;
transition: background-color 0.3s ease, opacity 0.3s ease;
}
/* Fired bomb cell — slightly darkened blast mark */
.cell.cell-fired {
background-color: rgba(100, 11, 11, 0.589);
border-color: rgba(255, 255, 255, 0.616);
opacity: 0.45;
transition: background-color 0.3s ease, opacity 0.3s ease;
}
/* Secret Chest cell */
.cell.secret {
background-color: rgba(244,114,182,0.12);
border-color: rgba(244,114,182,0.55);
animation: secret-pulse 2s ease-in-out infinite;
}
@keyframes secret-pulse {
0% { border-color: rgba(244,114,182,0.40); }
50% { border-color: rgba(244,114,182,0.85); }
100% { border-color: rgba(244,114,182,0.40); }
}
.cell.empty:hover {
background-color: rgba(255,255,255,0.07);
}
.cell.pblast.p0 { border-color: rgba(96,165,250,0.65); background-color: rgba(59,130,246,0.12); }
.cell.pblast.p1 { border-color: rgba(52,211,153,0.65); background-color: rgba(16,185,129,0.12); }
.cell.pblast.p2 { border-color: rgba(251,146,60,0.65); background-color: rgba(234,88,12,0.12); }
.cell.phit.p0 { border-color: rgba(96,165,250,0.95); background-color: rgba(59,130,246,0.25); }
.cell.phit.p1 { border-color: rgba(52,211,153,0.95); background-color: rgba(16,185,129,0.25); }
.cell.phit.p2 { border-color: rgba(251,146,60,0.95); background-color: rgba(234,88,12,0.25); }
.cell.phit-survive {
border-color: rgba(251,191,36,0.90);
background-color: rgba(251,191,36,0.22);
}
.cell.pdestroy {
border-color: rgba(248,113,113,0.95);
background-color: rgba(239,68,68,0.28);
}
/* ── Preview hit — will destroy ── */
.cell.chest.pdestroy,
.cell.locked.pdestroy,
.cell.cursed.pdestroy,
.cell.void.pdestroy,
.cell.secret.pdestroy,
.cell.boss.pdestroy {
border-color: rgba(248,113,113,0.95);
background-color: rgba(239,68,68,0.28);
}
/* ── Preview hit — will survive (not enough hits) ── */
.cell.chest.phit-survive,
.cell.locked.phit-survive,
.cell.cursed.phit-survive,
.cell.void.phit-survive,
.cell.secret.phit-survive,
.cell.boss.phit-survive {
border-color: rgba(251,191,36,0.90);
background-color: rgba(251,191,36,0.22);
}
.cell.pchain {
border-color: rgba(196,181,253,0.65);
background-color: rgba(139,92,246,0.15);
}
/* Hover preview highlight on chests */
@keyframes phit-hover-pulse {
0% { border-color: rgba(248,113,113,0.40); background-color: rgba(239,68,68,0.08); }
100% { border-color: rgba(248,113,113,0.80); background-color: rgba(239,68,68,0.20); }
}
/* ── Hover preview highlight — all chest types ── */
.cell.chest.phit-hover,
.cell.locked.phit-hover,
.cell.cursed.phit-hover,
.cell.void.phit-hover,
.cell.secret.phit-hover,
.cell.boss.phit-hover {
border-color: rgba(248,113,113,0.70);
background-color: rgba(239,68,68,0.14);
animation: phit-hover-pulse 0.3s ease-in-out infinite alternate;
}
/* Hover origin cell */
/* ── Hover preview highlight — all chest types ── */
.cell.cell-hover-origin.p0 { background-color: rgba(59,130,246,0.25); border-color: rgba(96,165,250,0.80); border-width: 2px; }
.cell.cell-hover-origin.p1 { background-color: rgba(16,185,129,0.25); border-color: rgba(52,211,153,0.80); border-width: 2px; }
.cell.cell-hover-origin.p2 { background-color: rgba(234,88,12,0.25); border-color: rgba(251,146,60,0.80); border-width: 2px; }
/* pointer-events none on cell children to prevent hover flicker */
.icon {
font-size: 48px;
pointer-events: none;
background-color: transparent;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.badge {
position: absolute; top: 4px; right: 6px;
font-size: 14px; font-weight: 700;
color: rgba(80,220,140,0.95);
background-color: rgba(29,158,117,0.35);
border-radius: 4px; padding: 0 4px;
pointer-events: none;
}
.hits {
position: absolute; bottom: 10px; right: 6px;
font-size: 15px; font-weight: 700;
color: rgba(251,191,36,0.9);
pointer-events: none;
}
.cell-flash {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
border-radius: 12px;
pointer-events: none;
z-index: 3;
}
.cell-flash.flash-direct { animation: flash-origin-direct 0.6s ease-out forwards; }
.cell-flash.flash-chain { animation: flash-origin-chain 0.6s ease-out forwards; }
.cell-flash.flash-blast-direct { animation: flash-blast-direct 0.5s ease-out forwards; }
.cell-flash.flash-blast-chain { animation: flash-blast-chain 0.5s ease-out forwards; }
@keyframes flash-origin-direct {
0% { background-color: rgba(251,191,36,0.9); border: 3px solid #fbbf24; transform: scale(1.12); }
40% { background-color: rgba(251,191,36,0.5); transform: scale(1.04); }
100% { background-color: transparent; border-color: transparent; transform: scale(1.0); }
}
@keyframes flash-origin-chain {
0% { background-color: rgba(196,181,253,0.9); border: 3px solid #c4b5fd; transform: scale(1.12); }
40% { background-color: rgba(196,181,253,0.5); transform: scale(1.04); }
100% { background-color: transparent; border-color: transparent; transform: scale(1.0); }
}
@keyframes flash-blast-direct {
0% { background-color: rgba(251,191,36,0.3); border: 1px solid rgba(251,191,36,0.5); }
100% { background-color: transparent; border-color: transparent; }
}
@keyframes flash-blast-chain {
0% { background-color: rgba(196,181,253,0.3); border: 1px solid rgba(196,181,253,0.5); }
100% { background-color: transparent; border-color: transparent; }
}
@keyframes cell-appear {
0% { opacity: 0; transform: scale(0.85); }
100% { opacity: 1; transform: scale(1.0); }
}
/* CHEST ICONS */
/* normal chest */
.cell-chest-sprite {
position: relative;
width: 128px;
height: 128px;
image-rendering: pixelated;
pointer-events: none;
margin-left: -16px;
}
.chest-boss {
width: 140px;
height: 140px;
margin-left: -21.5px;
margin-bottom: 15px;
}
/* when mystery wave */
.cell-mystery-icon {
font-size: 32px;
pointer-events: none;
}
.cell.mystery {
background-color: rgba(20, 10, 40, 0.95);
border-color: rgba(167, 139, 250, 0.3);
}
/* ------------------ */
.chest-secret {
width: 140px;
height: 140px;
margin-left: -21.5px;
margin-bottom: 2px;
}
.chest-gem {
width: 140px;
height: 140px;
}
/* Background Particles in game*/
.grid-particles {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
overflow: hidden;
z-index: 0; /* ← behind grid */
}
.grid-particle {
position: absolute;
bottom: -10px;
opacity: 0;
animation-name: grid-particle-rise;
animation-timing-function: ease-in;
animation-iteration-count: infinite;
}
@keyframes grid-particle-rise {
0% { opacity: 0; transform: translateY(0px); }
15% { opacity: 1; }
80% { opacity: 0.8; transform: translateY(-60vh); }
100% { opacity: 0; transform: translateY(-100vh); }
}
.cell.boss {
position: relative;
overflow: visible;
background-color: rgba(251,191,36,0.25);
border-color: rgba(251,191,36,0.80);
border-width: 3px;
//animation: boss-pulse 1.2s ease-in-out infinite alternate; /* ← add this back */
}
/* Boss chest cell */
.cell.boss.chest-spawn {
position: relative;
overflow: visible;
background-color: rgba(251,191,36,0.25);
border-color: rgba(251,191,36,0.80);
border-width: 3px;
animation: boss-spawn-enter 0.5s cubic-bezier(0.22,1,0.36,1) both,
boss-pulse 1.2s ease-in-out 0.5s infinite alternate;
}
@keyframes boss-spawn-enter {
0% { opacity: 0; transform: scale(0.3) rotate(-5deg); background-color: rgba(251,191,36,0.05); }
60% { opacity: 1; transform: scale(1.15) rotate(2deg); }
100% { opacity: 1; transform: scale(1.0) rotate(0deg); }
}
/* HP bar sits at bottom of cell */
.boss-hp-bar {
position: absolute;
bottom: 8px;
left: 8px;
right: 8px;
height: 5px;
background-color: rgba(255,255,255,0.10);
border-radius: 3px;
overflow: hidden;
}
.boss-hp-fill {
height: 100%;
border-radius: 3px;
transition: width 0.3s ease, background-color 0.4s ease;
background-color: rgba(52,211,153,0.90); /* green at full */
}
.boss-suppress-icon {
position: absolute;
top: 4px;
left: 16px;
font-size: 24px;
pointer-events: none;
opacity: 0.80;
line-height: 10;
background-color: transparent;
}
/* These get applied via inline style instead — easier to drive from C# */
/* ---------------------- SCREEN SHAKE ----------------------- */
/* Add these: */
.grid-shake-wrap {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
pointer-events: none;
background-color: transparent;
}
.grid-shake-wrap.shake-small { animation: shake-small 0.25s ease-out; }
.grid-shake-wrap.shake-medium { animation: shake-medium 0.35s ease-out; }
.grid-shake-wrap.shake-strong { animation: shake-strong 0.45s ease-out; }
.grid-shake-wrap.shake-insane { animation: shake-insane 0.55s ease-out; }
@keyframes shake-small {
0%,100% { transform: translate(0,0); }
25% { transform: translate(-2px, 1px); }
50% { transform: translate(2px, -1px); }
75% { transform: translate(-1px, 2px); }
}
@keyframes shake-medium {
0%,100% { transform: translate(0,0); }
20% { transform: translate(-4px, 2px) rotate(-0.3deg); }
40% { transform: translate(4px,-3px) rotate(0.3deg); }
60% { transform: translate(-3px, 3px) rotate(-0.2deg); }
80% { transform: translate(3px,-2px) rotate(0.2deg); }
}
@keyframes shake-strong {
0%,100% { transform: translate(0,0); }
15% { transform: translate(-6px, 3px) rotate(-0.5deg); }
30% { transform: translate(6px,-4px) rotate(0.5deg); }
45% { transform: translate(-5px, 5px) rotate(-0.4deg); }
60% { transform: translate(5px,-4px) rotate(0.4deg); }
75% { transform: translate(-3px, 2px); }
90% { transform: translate(2px,-2px); }
}
@keyframes shake-insane {
0%,100% { transform: translate(0,0); }
10% { transform: translate(-9px, 5px) rotate(-0.7deg); }
20% { transform: translate(9px,-6px) rotate(0.7deg); }
30% { transform: translate(-8px, 7px) rotate(-0.6deg); }
40% { transform: translate(8px,-6px) rotate(0.6deg); }
50% { transform: translate(-6px, 5px) rotate(-0.4deg); }
60% { transform: translate(6px,-5px) rotate(0.4deg); }
70% { transform: translate(-4px, 3px); }
80% { transform: translate(3px,-3px); }
90% { transform: translate(-1px, 1px); }
}
/* ---------------- SCREEN SHAKE ------------------- */
.mini-hand.shake-small { animation: shake-small 0.25s ease-out; }
.mini-hand.shake-medium { animation: shake-medium 0.35s ease-out; }
.mini-hand.shake-strong { animation: shake-strong 0.45s ease-out; }
.mini-hand.shake-insane { animation: shake-insane 0.55s ease-out; }
/* ---------------- TUTORIAL FOR BOMB PLACEMENT STEP 3 (turn cell targeted red if it doesn't blast a bomb ------------------- */
.cell.tut-invalid {
opacity: 0.35;
cursor: not-allowed;
}
.cell.tut-invalid:hover {
background-color: rgba(255,0,0,0.08);
border-color: rgba(255,0,0,0.20);
transform: none; /* no scale on invalid */
}
/* ---------------------------------------------------------------------------------- */
/* -------------------------------- REVEAL CONSUMABLE ------------------------------- */
/* ---------------------------------------------------------------------------------- */
/* Reveal ghost cells — shows next wave chest positions */
.cell.cell-reveal {
background-color: rgba(251,191,36,0.15);
border-color: rgba(251,191,36,0.50);
animation: reveal-pulse 0.8s ease-in-out infinite alternate;
}
.cell.cell-reveal-gem {
background-color: rgba(52,211,153,0.15);
border-color: rgba(52,211,153,0.50);
animation: reveal-pulse 0.8s ease-in-out infinite alternate;
}
.cell.cell-reveal-bomb {
background-color: rgba(167,139,250,0.15);
border-color: rgba(167,139,250,0.50);
animation: reveal-pulse 0.8s ease-in-out infinite alternate;
}
@keyframes reveal-pulse {
0% { opacity: 0.4; }
100% { opacity: 1.0; }
}
/* ---------------------------------------------------------------------------------- */
/* ------------------------------------- OTHER CHESTS ------------------------------- */
/* ---------------------------------------------------------------------------------- */
/* ── Locked chest — steel grey, heavy feel ── */
.cell.locked {
background-color: rgba(148,163,184,0.25);
border-color: rgba(148,163,184,0.60);
}
/* ── Cursed chest — blood red ── */
.cell.cursed {
background-color: rgba(185,28,28,0.25);
border-color: rgba(239,68,68,0.60);
animation: cursed-pulse 1.5s ease-in-out infinite alternate;
}
@keyframes cursed-pulse {
0% { border-color: rgba(239,68,68,0.40); }
100% { border-color: rgba(239,68,68,0.90); }
}
/* ── Void chest — deep dark purple ── */
.cell.void {
background-color: rgba(88,28,135,0.35);
border-color: rgba(139,92,246,0.70);
animation: void-pulse 2s ease-in-out infinite alternate;
}
@keyframes void-pulse {
0% { background-color: rgba(88,28,135,0.20); }
100% { background-color: rgba(88,28,135,0.50); }
}
/* Reveal is for when we use a reveal consumable to reveal futur chests positions on next wave */
.cell.cell-reveal-locked {
background-color: rgba(148,163,184,0.12);
border-color: rgba(148,163,184,0.40);
animation: reveal-pulse 0.8s ease-in-out infinite alternate;
}
.cell.cell-reveal-cursed {
background-color: rgba(185,28,28,0.12);
border-color: rgba(239,68,68,0.40);
animation: reveal-pulse 0.8s ease-in-out infinite alternate;
}
.cell.cell-reveal-void {
background-color: rgba(88,28,135,0.15);
border-color: rgba(139,92,246,0.40);
animation: reveal-pulse 0.8s ease-in-out infinite alternate;
}