734 lines
15 KiB
CSS
734 lines
15 KiB
CSS
/* Compiled CSS (from style.scss) - included directly so no build step is required */
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
html,
|
|
body {
|
|
height: 100%;
|
|
}
|
|
body {
|
|
font-family:
|
|
Inter,
|
|
system-ui,
|
|
-apple-system,
|
|
"Segoe UI",
|
|
Roboto,
|
|
"Helvetica Neue",
|
|
Arial;
|
|
margin: 0;
|
|
padding: 28px;
|
|
background: linear-gradient(180deg, #fff7f0 0%, #f3f6f9 60%);
|
|
color: #0b1220;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
}
|
|
|
|
.page {
|
|
max-width: 1100px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
header h2 {
|
|
margin: 0 0 12px 0;
|
|
color: #0b1220;
|
|
}
|
|
|
|
.card {
|
|
background: linear-gradient(180deg, #fffaf6, #ffffff);
|
|
border: 1px solid rgba(11, 18, 32, 0.06);
|
|
padding: 18px;
|
|
border-radius: 14px;
|
|
box-shadow: 0 10px 30px rgba(11, 18, 32, 0.06);
|
|
}
|
|
|
|
button {
|
|
background: linear-gradient(90deg, #ff7a59, #ffb199);
|
|
color: #1b1b1f;
|
|
border: none;
|
|
padding: 10px 14px;
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
font-weight: 700;
|
|
box-shadow: 0 8px 20px rgba(255, 122, 89, 0.12);
|
|
transition:
|
|
transform 160ms ease,
|
|
box-shadow 160ms ease;
|
|
}
|
|
button[disabled] {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
input[type="text"],
|
|
input[type="search"] {
|
|
padding: 8px;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(11, 18, 32, 0.06);
|
|
background: transparent;
|
|
color: inherit;
|
|
}
|
|
|
|
a {
|
|
color: #2563eb;
|
|
text-decoration: none;
|
|
}
|
|
a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
#log {
|
|
white-space: pre-wrap;
|
|
border-radius: 8px;
|
|
background: #f3f6f9;
|
|
padding: 8px;
|
|
height: 280px;
|
|
overflow: auto;
|
|
color: #0b1220;
|
|
}
|
|
|
|
#board {
|
|
width: 800px;
|
|
height: 800px;
|
|
display: grid;
|
|
grid-template-columns: repeat(8, 1fr);
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
background-size: cover;
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
#board {
|
|
width: min(92vw, 680px);
|
|
height: min(92vw, 680px);
|
|
}
|
|
}
|
|
|
|
.muted {
|
|
opacity: 0.9;
|
|
color: #475569;
|
|
}
|
|
|
|
.controls {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: center;
|
|
}
|
|
|
|
#players {
|
|
margin: 6px 0;
|
|
padding-left: 18px;
|
|
}
|
|
|
|
/* invite button - dark text for light theme */
|
|
#copyInviteBtn {
|
|
background: transparent;
|
|
border: 1px solid rgba(11, 18, 32, 0.06);
|
|
color: #0b1220;
|
|
padding: 6px 10px;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
#youAreHost {
|
|
background: rgba(255, 255, 255, 0.04);
|
|
padding: 6px 8px;
|
|
border-radius: 6px;
|
|
margin-left: 6px;
|
|
}
|
|
|
|
/* layout helpers used from game.html */
|
|
.layout-row {
|
|
display: flex;
|
|
gap: 20px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
#turnBanner {
|
|
color: #5a2b12;
|
|
background: linear-gradient(90deg, #fff1e6, #ffe6da);
|
|
padding: 10px 14px;
|
|
border-radius: 12px;
|
|
font-weight: 800;
|
|
display: none;
|
|
border: 1px solid rgba(90, 43, 18, 0.06);
|
|
box-shadow: 0 6px 18px rgba(90, 43, 18, 0.04);
|
|
}
|
|
#turnBanner.show {
|
|
display: inline-block;
|
|
}
|
|
|
|
/* board tweaks */
|
|
#board {
|
|
border: 1px solid rgba(11, 18, 32, 0.06);
|
|
position: relative;
|
|
background-image: url("/assets/chess-pieces/boards/8x8_wood.svg");
|
|
background-size: cover;
|
|
}
|
|
|
|
/* square cells and piece images (used by renderFen) */
|
|
.square {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
}
|
|
.square img,
|
|
.piece {
|
|
max-width: 78%;
|
|
max-height: 78%;
|
|
user-select: none;
|
|
position: relative;
|
|
z-index: 3;
|
|
}
|
|
|
|
/* marker shown on target squares for legal moves */
|
|
.square {
|
|
position: relative;
|
|
}
|
|
.square .move-marker {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 18%;
|
|
height: 18%;
|
|
border-radius: 50%;
|
|
pointer-events: auto;
|
|
cursor: pointer;
|
|
background: rgba(37, 99, 235, 0.95);
|
|
box-shadow: 0 2px 6px rgba(11, 18, 32, 0.06);
|
|
z-index: 3;
|
|
transition:
|
|
transform 120ms ease,
|
|
opacity 120ms ease;
|
|
}
|
|
.square .move-marker:hover {
|
|
transform: translate(-50%, -50%) scale(1.12);
|
|
opacity: 0.95;
|
|
}
|
|
|
|
/* square highlight when selecting a target for a card: a small hollow ring strictly inside the square */
|
|
.square.targetable {
|
|
outline: none;
|
|
box-shadow: none;
|
|
}
|
|
.square.targetable::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 36%;
|
|
height: 36%;
|
|
border-radius: 50%;
|
|
border: 2px solid rgba(255, 121, 63, 0.98);
|
|
box-shadow: 0 6px 18px rgba(255, 121, 63, 0.1) inset;
|
|
pointer-events: none;
|
|
z-index: 3;
|
|
}
|
|
|
|
/* small circular mine marker strictly inside the square */
|
|
.square .mine-dot {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 36%;
|
|
height: 36%;
|
|
border-radius: 50%;
|
|
background: radial-gradient(
|
|
circle at 30% 30%,
|
|
#fff2ea 0%,
|
|
#ff9a76 30%,
|
|
#c94b2b 100%
|
|
);
|
|
box-shadow:
|
|
inset 0 2px 8px rgba(255, 255, 255, 0.28),
|
|
0 4px 12px rgba(0, 0, 0, 0.2);
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|
|
|
|
/* image-based mine: prefer SVG with transparent background for crisp edges */
|
|
.square .mine-img {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 40%;
|
|
height: auto;
|
|
max-height: 42%;
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
image-rendering: -webkit-optimize-contrast;
|
|
}
|
|
|
|
/* When a square is veiled by brouillard, hide its piece images to ensure full concealment
|
|
This is a robust fallback in case stacking context / transform interactions make an overlay
|
|
not fully cover the image. Hiding the image guarantees the piece is not visible. */
|
|
.square.veiled img,
|
|
.square.veiled .piece {
|
|
visibility: hidden !important;
|
|
opacity: 0 !important;
|
|
}
|
|
|
|
/* highlighted/selected square when a player selects a piece */
|
|
.square.selected {
|
|
outline: 3px solid rgba(245, 158, 11, 0.95); /* amber */
|
|
box-shadow:
|
|
inset 0 0 0 3px rgba(245, 158, 11, 0.06),
|
|
0 8px 20px rgba(11, 18, 32, 0.04);
|
|
z-index: 4;
|
|
}
|
|
.square.selected .piece {
|
|
transform: scale(1.04);
|
|
transition: transform 140ms ease;
|
|
}
|
|
|
|
/* hide the visual selection outline for a square while keeping the piece visible */
|
|
.square.hide-selection.selected,
|
|
.square.hide-selection.selected-remote {
|
|
outline: none !important;
|
|
box-shadow: none !important;
|
|
}
|
|
.square.hide-selection {
|
|
transition: background 120ms ease;
|
|
}
|
|
|
|
/* remote selection (other player's selection) */
|
|
.square.selected-remote {
|
|
outline: 3px solid rgba(37, 99, 235, 0.95); /* blue */
|
|
box-shadow:
|
|
inset 0 0 0 3px rgba(37, 99, 235, 0.06),
|
|
0 6px 16px rgba(11, 18, 32, 0.04);
|
|
z-index: 3;
|
|
}
|
|
.square.selected-remote .piece {
|
|
transform: scale(1.02);
|
|
transition: transform 140ms ease;
|
|
}
|
|
|
|
/* basic checkerboard coloring */
|
|
.square.dark {
|
|
background: linear-gradient(180deg, #fff8f4, #fff1ec);
|
|
}
|
|
.square.light {
|
|
background: linear-gradient(180deg, #fdeee2, #f6d9c5);
|
|
}
|
|
.square {
|
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
/* flipped board for black perspective */
|
|
#board.flipped {
|
|
transform: rotate(180deg);
|
|
}
|
|
#board.flipped .square img {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
/* Lobby / hero styles - decorative and fun */
|
|
.hero {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 28px;
|
|
border-radius: 12px;
|
|
margin-bottom: 18px;
|
|
overflow: hidden;
|
|
position: relative;
|
|
border: 1px solid rgba(11, 18, 32, 0.04);
|
|
background: linear-gradient(
|
|
135deg,
|
|
rgba(99, 102, 241, 0.06),
|
|
rgba(99, 102, 241, 0.03)
|
|
);
|
|
}
|
|
.hero .title {
|
|
font-size: 28px;
|
|
font-weight: 800;
|
|
letter-spacing: -0.5px;
|
|
}
|
|
.hero .subtitle {
|
|
color: rgba(11, 18, 32, 0.65);
|
|
margin-top: 6px;
|
|
font-weight: 500;
|
|
}
|
|
.hero .decor {
|
|
position: absolute;
|
|
right: -80px;
|
|
top: -60px;
|
|
opacity: 0.06;
|
|
width: 420px;
|
|
height: 420px;
|
|
filter: blur(6px);
|
|
transform: rotate(20deg);
|
|
}
|
|
|
|
.card.lobby {
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 14px;
|
|
}
|
|
|
|
/* buttons variants */
|
|
.btn-ghost {
|
|
background: transparent;
|
|
border: 1px solid rgba(11, 18, 32, 0.06);
|
|
padding: 8px 10px;
|
|
border-radius: 8px;
|
|
color: inherit;
|
|
}
|
|
.btn-cta {
|
|
background: linear-gradient(90deg, #3b82f6, #60a5fa);
|
|
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.18);
|
|
}
|
|
|
|
/* draw button: compact rectangular CTA used in the cards area */
|
|
.btn-draw {
|
|
background: linear-gradient(90deg, #ff7a59, #ffb199); /* warm gradient */
|
|
color: #1b1b1f;
|
|
padding: 10px 14px;
|
|
border-radius: 12px;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-weight: 900;
|
|
font-size: 14px;
|
|
min-width: 140px;
|
|
height: 46px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 10px 22px rgba(255, 122, 89, 0.12);
|
|
}
|
|
.btn-draw[disabled] {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* cancel button shown during pending card selection: white background, red text for clear contrast */
|
|
.btn-cancel {
|
|
background: #ffffff;
|
|
color: #b91c1c; /* red */
|
|
border: 1px solid rgba(185, 28, 28, 0.14);
|
|
padding: 6px 8px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-weight: 700;
|
|
}
|
|
.btn-cancel:hover {
|
|
background: #fff5f5;
|
|
}
|
|
|
|
/* explosion animation used when a mine detonates */
|
|
.explosion {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%) scale(0.6);
|
|
width: 64%;
|
|
height: 64%;
|
|
border-radius: 50%;
|
|
pointer-events: none;
|
|
z-index: 6;
|
|
background: radial-gradient(
|
|
circle at 30% 30%,
|
|
rgba(255, 255, 200, 0.98) 0%,
|
|
rgba(255, 160, 60, 0.95) 40%,
|
|
rgba(220, 40, 40, 0.95) 60%,
|
|
rgba(120, 10, 10, 0.85) 100%
|
|
);
|
|
box-shadow:
|
|
0 6px 18px rgba(0, 0, 0, 0.35),
|
|
inset 0 6px 18px rgba(255, 255, 255, 0.08);
|
|
opacity: 1;
|
|
transform-origin: 50% 50%;
|
|
transform: translate(-50%, -50%) scale(0.2);
|
|
}
|
|
.explosion.play {
|
|
animation: explode 520ms ease-out forwards;
|
|
}
|
|
@keyframes explode {
|
|
0% {
|
|
transform: translate(-50%, -50%) scale(0.2);
|
|
opacity: 1;
|
|
filter: blur(0px);
|
|
}
|
|
60% {
|
|
transform: translate(-50%, -50%) scale(1.15);
|
|
opacity: 0.95;
|
|
filter: blur(1px);
|
|
}
|
|
100% {
|
|
transform: translate(-50%, -50%) scale(1.6);
|
|
opacity: 0;
|
|
filter: blur(6px);
|
|
}
|
|
}
|
|
|
|
/* small debris puffs for a subtle effect */
|
|
.explosion::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 120%;
|
|
height: 120%;
|
|
border-radius: 50%;
|
|
background: radial-gradient(
|
|
circle,
|
|
rgba(255, 200, 100, 0.6) 0%,
|
|
rgba(255, 200, 100, 0) 40%
|
|
);
|
|
opacity: 0;
|
|
animation: debris 520ms ease-out forwards;
|
|
}
|
|
@keyframes debris {
|
|
0% {
|
|
opacity: 0.9;
|
|
transform: translate(-50%, -50%) scale(0.8);
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
transform: translate(-50%, -50%) scale(1.8);
|
|
}
|
|
}
|
|
|
|
/* video-based explosion: used when a .mov/mp4 file is provided in /assets/media/ */
|
|
.explosion-video {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
z-index: 7;
|
|
pointer-events: none;
|
|
width: 160%;
|
|
height: auto;
|
|
max-width: 220px;
|
|
max-height: 220px;
|
|
display: block;
|
|
object-fit: contain;
|
|
}
|
|
|
|
/* floating small piece icons */
|
|
.floating-piece {
|
|
position: absolute;
|
|
opacity: 0.06;
|
|
width: 160px;
|
|
height: 160px;
|
|
pointer-events: none;
|
|
transform: translate(-10%, -10%);
|
|
}
|
|
|
|
/* subtle button hover */
|
|
button:hover {
|
|
transform: translateY(-3px);
|
|
transition: all 180ms ease;
|
|
}
|
|
|
|
/* players area */
|
|
.players-list {
|
|
list-style: none;
|
|
padding-left: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
.players-list li {
|
|
background: rgba(11, 18, 32, 0.03);
|
|
padding: 6px 10px;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
/* card hand UI */
|
|
.player-hand {
|
|
border: 1px solid rgba(11, 18, 32, 0.06);
|
|
padding: 8px;
|
|
border-radius: 8px;
|
|
background: #fff;
|
|
}
|
|
.card-item {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: flex-start;
|
|
background: #f8fafc;
|
|
padding: 8px;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(11, 18, 32, 0.04);
|
|
}
|
|
.card-art {
|
|
width: 64px;
|
|
height: 60px;
|
|
background: linear-gradient(180deg, #eef2ff, #ffffff);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #475569;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
}
|
|
.card-meta {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.card-title {
|
|
font-weight: 700;
|
|
color: #0b1220;
|
|
}
|
|
.card-desc {
|
|
color: #475569;
|
|
font-size: 13px;
|
|
}
|
|
|
|
/* Pokemon-style horizontal card hand */
|
|
.pokemon-hand {
|
|
padding: 10px;
|
|
border-radius: 10px;
|
|
background: linear-gradient(180deg, #ffffff, #fbfdff);
|
|
border: 1px solid rgba(11, 18, 32, 0.04);
|
|
}
|
|
.pokemon-hand-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 8px;
|
|
}
|
|
.pokemon-hand-title {
|
|
font-weight: 800;
|
|
}
|
|
.pokemon-hand-counts {
|
|
display: flex;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
.opponent-count {
|
|
background: rgba(11, 18, 32, 0.03);
|
|
padding: 4px 8px;
|
|
border-radius: 8px;
|
|
font-size: 12px;
|
|
}
|
|
.cards-row {
|
|
display: flex;
|
|
gap: 12px;
|
|
overflow-x: auto;
|
|
padding-bottom: 6px;
|
|
}
|
|
.pokemon-card {
|
|
flex: 0 0 180px;
|
|
height: 350px;
|
|
border-radius: 12px;
|
|
background: linear-gradient(180deg, #fff, #f6f8ff);
|
|
box-shadow: 0 10px 24px rgba(11, 18, 32, 0.06);
|
|
border: 1px solid rgba(11, 18, 32, 0.04);
|
|
display: flex;
|
|
flex-direction: column;
|
|
cursor: pointer;
|
|
}
|
|
.pokemon-card-top {
|
|
flex: 0 0 120px;
|
|
padding: 8px;
|
|
}
|
|
.pokemon-card-art {
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(135deg, #f0f9ff, #eef2ff);
|
|
border-radius: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #475569;
|
|
font-weight: 700;
|
|
}
|
|
.pokemon-card-mid {
|
|
padding: 8px;
|
|
flex: 1 1 auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
.pokemon-card-title {
|
|
font-weight: 800;
|
|
color: #0b1220;
|
|
}
|
|
.pokemon-card-desc {
|
|
color: #475569;
|
|
font-size: 13px;
|
|
}
|
|
.pokemon-card-footer {
|
|
padding: 8px;
|
|
border-top: 1px solid rgba(11, 18, 32, 0.02);
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
.card-play-btn {
|
|
background: linear-gradient(90deg, #3b82f6, #60a5fa);
|
|
color: white;
|
|
border: none;
|
|
padding: 6px 10px;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
}
|
|
.card-play-btn:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
/* small responsive tweak */
|
|
@media (max-width: 700px) {
|
|
.pokemon-card {
|
|
flex: 0 0 140px;
|
|
height: 220px;
|
|
}
|
|
}
|
|
|
|
/* shuffle animation: quick jitter + opacity pulse to emphasize randomization */
|
|
#board.shuffle-play {
|
|
transition:
|
|
transform 700ms cubic-bezier(0.2, 0.9, 0.2, 1),
|
|
opacity 700ms ease;
|
|
transform: translateY(-6px) rotate(-1deg) scale(0.995);
|
|
opacity: 0.9;
|
|
}
|
|
#board.shuffle-play .square img {
|
|
transition: transform 700ms cubic-bezier(0.2, 0.9, 0.2, 1);
|
|
transform: rotate(2deg) scale(0.995);
|
|
filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.06));
|
|
}
|
|
|
|
.square.targeted::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgb(255, 201, 201);
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|
|
|
|
.square.last-move-dest::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgb(212, 255, 241);
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|
|
|
|
.square.last-move-source::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgb(212, 255, 241);
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|