736 lines
21 KiB
CSS
736 lines
21 KiB
CSS
/* =========================================================================
|
|
Texas Hold X Replay — Pixel-art skin & responsive layout
|
|
-------------------------------------------------------------------------
|
|
Design goals:
|
|
1. Stage zone (table + seats + animations) is visually isolated from the
|
|
interaction zone (controls + event log). Each zone has independent
|
|
overflow rules so speech bubbles never get clipped.
|
|
2. Pixel-art aesthetic: hard edges, stepped shadows (no blur), 8-bit
|
|
palette, monospace typography.
|
|
3. Three responsive breakpoints (desktop 3-col → tablet 2-col → mobile
|
|
stacked) — see media queries at the bottom of this file.
|
|
========================================================================= */
|
|
|
|
:root {
|
|
color-scheme: dark;
|
|
|
|
/* Palette — keep limited and high-contrast for that 8-bit look. */
|
|
--ink: #f7efd2;
|
|
--ink-dim: #c8b98a;
|
|
--muted: #8e8466;
|
|
--panel: #221a17;
|
|
--panel-2: #2d231f;
|
|
--panel-3: #3a2c25;
|
|
--line: #5d4638;
|
|
--line-soft: #3b2c25;
|
|
|
|
/* Felt greens. */
|
|
--felt: #1d8a5f;
|
|
--felt-dark: #0c4a37;
|
|
--felt-light: #4ec089;
|
|
--felt-rail: #6c3a20;
|
|
--felt-rail-dark: #3a1d10;
|
|
|
|
/* Accents. */
|
|
--gold: #f0b64d;
|
|
--gold-dark: #b87a1f;
|
|
--red: #e24b4b;
|
|
--blue: #53a6de;
|
|
--green: #63cb73;
|
|
--purple: #b773d3;
|
|
|
|
--shadow: rgba(8, 6, 5, 0.55);
|
|
--pixel: 3px;
|
|
|
|
/* Seat sizing scales with the stage width via container query fallback
|
|
(clamp on viewport). */
|
|
--seat-size: clamp(96px, 13vw, 138px);
|
|
--avatar-size: 44px;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
body {
|
|
min-height: 100vh;
|
|
margin: 0;
|
|
color: var(--ink);
|
|
/* Pixel-art monospace stack — keeps numerals crisp & blocky. */
|
|
font-family: "Courier New", "Lucida Console", "Press Start 2P", monospace;
|
|
font-size: 13px;
|
|
letter-spacing: 0.2px;
|
|
background:
|
|
/* Tiny noise made from offset diagonals for that CRT pixel-grid feel. */
|
|
linear-gradient(45deg, rgba(255,255,255,0.025) 25%, transparent 25%) 0 0 / 8px 8px,
|
|
linear-gradient(135deg, rgba(255,255,255,0.025) 25%, transparent 25%) 0 0 / 8px 8px,
|
|
radial-gradient(ellipse at 50% 0%, #2a1f1a 0%, #14100e 60%, #0d0a08 100%);
|
|
image-rendering: pixelated;
|
|
-webkit-font-smoothing: none;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
}
|
|
|
|
button, input, select { font: inherit; }
|
|
|
|
/* ---------- Buttons (chunky pixel "press" feel) ---------- */
|
|
button, .file-btn {
|
|
min-height: 42px;
|
|
border: var(--pixel) solid #17100d;
|
|
color: var(--ink);
|
|
background: #3a2b24;
|
|
box-shadow:
|
|
inset -3px -3px 0 rgba(0,0,0,0.35),
|
|
inset 3px 3px 0 rgba(255,255,255,0.08),
|
|
0 4px 0 #120d0b;
|
|
cursor: pointer;
|
|
transition: transform 90ms steps(2), filter 90ms steps(2);
|
|
}
|
|
button:hover, .file-btn:hover { filter: brightness(1.12); }
|
|
button:active, .file-btn:active {
|
|
transform: translateY(3px);
|
|
box-shadow:
|
|
inset -3px -3px 0 rgba(0,0,0,0.35),
|
|
inset 3px 3px 0 rgba(255,255,255,0.08),
|
|
0 1px 0 #120d0b;
|
|
}
|
|
button:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
|
|
input, select {
|
|
width: 100%;
|
|
min-height: 40px;
|
|
border: 2px solid var(--line);
|
|
border-radius: 0;
|
|
color: var(--ink);
|
|
background: #15110f;
|
|
padding: 9px 10px;
|
|
outline: none;
|
|
}
|
|
input:focus, select:focus { border-color: var(--gold); }
|
|
|
|
/* ---------- Shell / Topbar ---------- */
|
|
.app-shell {
|
|
width: min(1640px, 100%);
|
|
margin: 0 auto;
|
|
padding: 18px;
|
|
}
|
|
|
|
.topbar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 16px;
|
|
padding: 14px 16px;
|
|
border: var(--pixel) solid #49382e;
|
|
background:
|
|
linear-gradient(180deg, #322620 0%, #1f1715 100%);
|
|
box-shadow: 0 8px 0 #0d0908, 0 18px 34px var(--shadow);
|
|
}
|
|
|
|
.brand-lockup { display: flex; gap: 14px; align-items: center; min-width: 0; }
|
|
.brand-meta { min-width: 0; }
|
|
|
|
.chip-mark {
|
|
display: grid;
|
|
place-items: center;
|
|
width: 58px;
|
|
height: 58px;
|
|
flex: 0 0 auto;
|
|
border: 4px dashed #fff4bc;
|
|
border-radius: 50%;
|
|
color: #20130b;
|
|
background: radial-gradient(circle, #ffe28a 0 42%, #c73f3d 43% 100%);
|
|
font-weight: 900;
|
|
text-shadow: 1px 1px 0 rgba(255,255,255,0.45);
|
|
/* Slow 8-bit chip rotation when idle for a touch of life. */
|
|
animation: chipSpin 6s linear infinite;
|
|
}
|
|
|
|
h1, h2, p { margin: 0; }
|
|
h1 { font-size: clamp(18px, 2.3vw, 28px); }
|
|
h2 { margin-bottom: 12px; color: var(--gold); font-size: 15px; }
|
|
|
|
#subtitle { margin-top: 5px; color: var(--ink-dim); font-size: 12px; }
|
|
|
|
.status-strip {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* Generic pixel "tag" badge — shared by status, hand, pot, etc. */
|
|
.badge {
|
|
display: inline-block;
|
|
border: 2px solid #16100d;
|
|
padding: 7px 10px;
|
|
color: #1b120c;
|
|
font-weight: 700;
|
|
white-space: nowrap;
|
|
box-shadow: inset -2px -2px 0 rgba(0,0,0,0.18);
|
|
}
|
|
.badge-gold { background: var(--gold); }
|
|
.badge-blue { background: var(--blue); color: #0f1c2e; }
|
|
|
|
/* ---------- Layout Grid ---------- */
|
|
/* Desktop: stage in middle, controls left, events right. The stage column
|
|
is the dominant area (1.4fr) so the table breathes. */
|
|
.layout-grid {
|
|
display: grid;
|
|
grid-template-columns: minmax(260px, 320px) minmax(0, 1.4fr) minmax(280px, 340px);
|
|
grid-template-areas: "controls stage events";
|
|
gap: 16px;
|
|
margin-top: 18px;
|
|
align-items: start;
|
|
}
|
|
.control-panel { grid-area: controls; display: grid; gap: 14px; }
|
|
.stage-zone { grid-area: stage; min-width: 0; }
|
|
.event-panel { grid-area: events; display: grid; gap: 14px; }
|
|
|
|
/* ---------- Panel sections (the chunky bordered cards) ---------- */
|
|
.panel-section {
|
|
border: var(--pixel) solid #49382e;
|
|
background: linear-gradient(180deg, var(--panel-2), var(--panel));
|
|
padding: 14px;
|
|
box-shadow: 0 7px 0 #0e0a08, 0 14px 26px var(--shadow);
|
|
}
|
|
.panel-section label {
|
|
display: grid;
|
|
gap: 6px;
|
|
margin-bottom: 10px;
|
|
color: var(--ink-dim);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.button-row, .transport-row, .auto-grid { display: grid; gap: 9px; }
|
|
.button-row { grid-template-columns: 1fr 1fr; }
|
|
.transport-row { grid-template-columns: repeat(4, minmax(42px, 1fr)); }
|
|
.auto-grid { grid-template-columns: 1fr 86px; align-items: end; }
|
|
|
|
.toggle-line { display: flex !important; flex-direction: row; align-items: center; min-height: 40px; }
|
|
.toggle-line input { width: auto; min-height: auto; accent-color: var(--gold); }
|
|
|
|
.primary-btn { color: #1b120c; background: var(--gold); }
|
|
|
|
.file-btn {
|
|
display: grid; place-items: center; text-align: center;
|
|
position: relative; overflow: hidden;
|
|
}
|
|
.file-btn input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
|
|
|
|
.progress-wrap {
|
|
height: 12px; margin-top: 14px;
|
|
border: 2px solid #120d0b; background: #14100e;
|
|
}
|
|
#progressBar {
|
|
width: 0; height: 100%;
|
|
background: linear-gradient(90deg, var(--red), var(--gold), var(--green));
|
|
transition: width 220ms linear;
|
|
}
|
|
|
|
.stat-list { display: grid; gap: 8px; margin: 0; }
|
|
.stat-list div {
|
|
display: flex; justify-content: space-between; gap: 12px;
|
|
border-bottom: 1px solid rgba(255,255,255,0.08);
|
|
padding-bottom: 7px;
|
|
}
|
|
.stat-list dt { color: var(--ink-dim); }
|
|
.stat-list dd { margin: 0; text-align: right; }
|
|
|
|
/* ---------- Stage zone ---------- */
|
|
.stage-head {
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
gap: 10px; margin-bottom: 12px;
|
|
}
|
|
.stage-head-left { display: flex; gap: 10px; align-items: center; min-width: 0; }
|
|
#streetLabel { font-size: clamp(16px, 2vw, 22px); color: var(--ink); }
|
|
|
|
/* The poker-table is the positioning context for seats. Its overflow MUST
|
|
stay visible so seats placed near the table edges (and their speech
|
|
bubbles) can extend slightly outside the green felt without being clipped.
|
|
The visual clipping is handled by .felt-shell instead. */
|
|
.poker-table {
|
|
position: relative;
|
|
/* Reserve vertical breathing room above/below the felt for seats that
|
|
visually sit on the rail and for speech bubbles. */
|
|
padding: 70px 12px 80px;
|
|
/* Explicit responsive height — required because seats and felt are
|
|
absolutely positioned and the inner content (community area) is
|
|
center-aligned. clamp() keeps it usable from 640px to ~820px. */
|
|
height: clamp(620px, 64vw, 820px);
|
|
overflow: visible;
|
|
}
|
|
|
|
/* Felt shell — actual visible green table. Absolutely positioned and
|
|
inset within poker-table so seats/bubbles can spill outside. */
|
|
.felt-shell {
|
|
position: absolute;
|
|
inset: 60px 0 70px;
|
|
border: 6px solid var(--felt-rail);
|
|
border-radius: 46% / 36%;
|
|
background: var(--felt-rail-dark);
|
|
box-shadow:
|
|
inset 0 0 0 10px var(--felt-rail),
|
|
inset 0 0 0 16px #2c1b12,
|
|
0 10px 0 #130c09,
|
|
0 24px 44px var(--shadow);
|
|
overflow: hidden;
|
|
pointer-events: none;
|
|
}
|
|
.felt-rail {
|
|
/* Decorative pixel "studs" running along the rail. */
|
|
position: absolute; inset: -2px;
|
|
background:
|
|
repeating-linear-gradient(90deg,
|
|
transparent 0 22px, rgba(255,220,160,0.18) 22px 24px),
|
|
repeating-linear-gradient(0deg,
|
|
transparent 0 22px, rgba(255,220,160,0.18) 22px 24px);
|
|
mix-blend-mode: screen;
|
|
opacity: 0.4;
|
|
}
|
|
.felt-surface {
|
|
position: absolute;
|
|
inset: 12px;
|
|
border-radius: 44% / 32%;
|
|
background:
|
|
radial-gradient(ellipse at 50% 50%, var(--felt-light) 0%, var(--felt) 38%, var(--felt-dark) 100%);
|
|
overflow: hidden;
|
|
}
|
|
.felt-grid {
|
|
position: absolute; inset: 0;
|
|
opacity: 0.18;
|
|
/* 1px-wide pixel grid for the felt — gives a chess-board-like 8-bit feel. */
|
|
background:
|
|
linear-gradient(90deg, transparent calc(100% - 2px), rgba(255,255,255,0.4) 0) 0 0 / 28px 28px,
|
|
linear-gradient(180deg, transparent calc(100% - 2px), rgba(255,255,255,0.28) 0) 0 0 / 28px 28px;
|
|
image-rendering: pixelated;
|
|
}
|
|
.felt-glow {
|
|
position: absolute; inset: 0;
|
|
background: radial-gradient(ellipse at 50% 45%, rgba(255,255,255,0.18), transparent 55%);
|
|
pointer-events: none;
|
|
}
|
|
.felt-mark {
|
|
position: absolute;
|
|
left: 50%; top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
font-size: clamp(48px, 7vw, 96px);
|
|
font-weight: 900;
|
|
color: rgba(0,0,0,0.18);
|
|
letter-spacing: 6px;
|
|
text-shadow: 2px 2px 0 rgba(255,255,255,0.06);
|
|
user-select: none;
|
|
}
|
|
|
|
/* ---------- Community area (board + message) ---------- */
|
|
.community-area {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: min(60%, 560px);
|
|
display: grid;
|
|
justify-items: center;
|
|
gap: 14px;
|
|
z-index: 3;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card-row {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: clamp(6px, 1vw, 12px);
|
|
min-height: 76px;
|
|
}
|
|
.card-row.board { min-height: 92px; }
|
|
|
|
/* ---------- Cards ---------- */
|
|
.card {
|
|
display: grid;
|
|
grid-template-rows: auto 1fr auto;
|
|
width: clamp(48px, 5.4vw, 70px);
|
|
height: clamp(66px, 7.4vw, 96px);
|
|
border: 3px solid #1c1411;
|
|
background: #fff7df;
|
|
color: #17100d;
|
|
padding: 4px 6px;
|
|
box-shadow:
|
|
inset -2px -2px 0 rgba(0,0,0,0.12),
|
|
inset 2px 2px 0 rgba(255,255,255,0.6),
|
|
0 5px 0 rgba(0,0,0,0.42);
|
|
transform-origin: center;
|
|
animation: cardDeal 520ms cubic-bezier(.2,.9,.2,1);
|
|
font-family: "Courier New", monospace;
|
|
}
|
|
.card.red { color: #b92732; }
|
|
.card.back {
|
|
background:
|
|
/* 8-bit checker pattern for card back. */
|
|
linear-gradient(45deg, #2d579a 25%, transparent 25%) 0 0 / 8px 8px,
|
|
linear-gradient(-45deg, #2d579a 25%, transparent 25%) 0 0 / 8px 8px,
|
|
linear-gradient(45deg, transparent 75%, #2d579a 75%) 0 0 / 8px 8px,
|
|
linear-gradient(-45deg, transparent 75%, #2d579a 75%) 0 0 / 8px 8px,
|
|
#173a72;
|
|
}
|
|
.card .rank { font-size: clamp(12px, 1.2vw, 16px); font-weight: 900; line-height: 1; }
|
|
.card .suit { display: grid; place-items: center; font-size: clamp(18px, 2vw, 28px); }
|
|
.card .rank.bottom { transform: rotate(180deg); justify-self: end; }
|
|
|
|
.table-message {
|
|
min-height: 32px;
|
|
max-width: min(540px, 82%);
|
|
border: 3px solid #14100d;
|
|
padding: 8px 12px;
|
|
color: #1b120c;
|
|
background: #ffe28a;
|
|
text-align: center;
|
|
box-shadow: 0 4px 0 rgba(0,0,0,0.38);
|
|
}
|
|
|
|
/* ---------- Seats ---------- */
|
|
.seat-layer {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 4;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.seat {
|
|
--x: 50%;
|
|
--y: 50%;
|
|
position: absolute;
|
|
left: var(--x);
|
|
top: var(--y);
|
|
width: var(--seat-size);
|
|
transform: translate(-50%, -50%);
|
|
transition: transform 220ms steps(4), filter 220ms ease;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
/* Active seat — slight lift + glow + sprite "hop" animation. */
|
|
.seat.active {
|
|
filter: drop-shadow(0 0 14px rgba(240,182,77,0.85));
|
|
z-index: 6;
|
|
}
|
|
.seat.active .avatar { animation: avatarHop 520ms ease; }
|
|
|
|
.seat.folded { opacity: 0.55; filter: grayscale(0.6); }
|
|
.seat.folded .avatar { transform: rotate(-8deg); }
|
|
|
|
/* Action-driven sprite reactions (added by JS as transient classes). */
|
|
.seat.act-fold .avatar { animation: avatarFold 600ms ease forwards; }
|
|
.seat.act-bet .avatar,
|
|
.seat.act-raise .avatar,
|
|
.seat.act-all_in .avatar { animation: avatarShake 480ms ease; }
|
|
.seat.act-call .avatar,
|
|
.seat.act-check .avatar { animation: avatarNod 480ms ease; }
|
|
.seat.act-award .avatar { animation: avatarCheer 900ms ease; }
|
|
|
|
/* Speech bubble — placed above the seat by default; below for top seats so
|
|
it does not punch out of the table viewport. */
|
|
.speech {
|
|
position: absolute;
|
|
left: 50%;
|
|
bottom: calc(100% + 8px);
|
|
min-width: 72px;
|
|
max-width: 160px;
|
|
transform: translateX(-50%);
|
|
border: 3px solid #160f0c;
|
|
padding: 6px 10px;
|
|
color: #1a110b;
|
|
background: #fff2b7;
|
|
text-align: center;
|
|
font-weight: 700;
|
|
font-size: 12px;
|
|
box-shadow: 0 4px 0 rgba(0,0,0,0.38);
|
|
animation: bubblePop 380ms ease;
|
|
z-index: 8;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
/* Bubble tail — pixel-art triangle made from a rotated solid square. */
|
|
.speech::after {
|
|
content: "";
|
|
position: absolute;
|
|
left: 50%; top: 100%;
|
|
transform: translate(-50%, -3px) rotate(45deg);
|
|
width: 12px; height: 12px;
|
|
background: #fff2b7;
|
|
border-right: 3px solid #160f0c;
|
|
border-bottom: 3px solid #160f0c;
|
|
}
|
|
/* For top-row seats, flip the bubble below so it does not get clipped. */
|
|
.seat.seat-top .speech {
|
|
bottom: auto;
|
|
top: calc(100% + 8px);
|
|
}
|
|
.seat.seat-top .speech::after {
|
|
top: auto; bottom: 100%;
|
|
transform: translate(-50%, 3px) rotate(225deg);
|
|
}
|
|
/* Color the bubble by action category. */
|
|
.speech.kind-fold { background: #d8d2bc; }
|
|
.speech.kind-call,
|
|
.speech.kind-check { background: #c2e6c8; }
|
|
.speech.kind-bet,
|
|
.speech.kind-raise,
|
|
.speech.kind-all_in { background: #ffc7a8; color: #5a1f0a; }
|
|
.speech.kind-award { background: #ffe28a; }
|
|
|
|
/* Player name+avatar+stack box. */
|
|
.player-box {
|
|
border: 3px solid #15100d;
|
|
background: linear-gradient(180deg, #423128, #261c18);
|
|
padding: 8px;
|
|
box-shadow:
|
|
inset -3px -3px 0 rgba(0,0,0,0.35),
|
|
inset 3px 3px 0 rgba(255,255,255,0.08),
|
|
0 6px 0 #120d0b;
|
|
position: relative;
|
|
}
|
|
|
|
.player-head {
|
|
display: grid;
|
|
grid-template-columns: var(--avatar-size) 1fr auto;
|
|
gap: 8px;
|
|
align-items: center;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* Avatar — wraps an inline SVG pixel-art portrait generated by JS. The SVG
|
|
is an 8x8 grid of <rect> elements; the host element only provides the
|
|
square frame, border, and animation hook. */
|
|
.avatar {
|
|
width: var(--avatar-size);
|
|
height: var(--avatar-size);
|
|
flex: 0 0 auto;
|
|
position: relative;
|
|
border: 2px solid #15100d;
|
|
background: #211814;
|
|
image-rendering: pixelated;
|
|
display: grid;
|
|
place-items: stretch;
|
|
overflow: hidden;
|
|
box-shadow: inset -2px -2px 0 rgba(0,0,0,0.35);
|
|
}
|
|
.avatar svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
shape-rendering: crispEdges;
|
|
}
|
|
|
|
.player-name {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-weight: 900;
|
|
font-size: 12px;
|
|
color: var(--ink);
|
|
}
|
|
|
|
.dealer {
|
|
display: none;
|
|
flex: 0 0 auto;
|
|
border: 2px solid #130e0b;
|
|
padding: 2px 6px;
|
|
color: #17100d;
|
|
background: var(--gold);
|
|
font-weight: 900;
|
|
font-size: 11px;
|
|
}
|
|
.seat.dealer-seat .dealer { display: inline-block; }
|
|
|
|
.player-meta {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
margin-top: 6px;
|
|
font-size: 11px;
|
|
}
|
|
.stack { color: var(--green); }
|
|
.bet { min-height: 14px; color: var(--gold); }
|
|
|
|
.hole-cards {
|
|
justify-content: flex-start;
|
|
min-height: 38px;
|
|
margin-top: 6px;
|
|
gap: 4px;
|
|
}
|
|
.hole-cards .card {
|
|
width: clamp(26px, 2.8vw, 36px);
|
|
height: clamp(36px, 4vw, 50px);
|
|
padding: 2px 3px;
|
|
border-width: 2px;
|
|
box-shadow:
|
|
inset -1px -1px 0 rgba(0,0,0,0.12),
|
|
inset 1px 1px 0 rgba(255,255,255,0.6),
|
|
0 3px 0 rgba(0,0,0,0.42);
|
|
}
|
|
.hole-cards .card .rank { font-size: 10px; }
|
|
.hole-cards .card .suit { font-size: 14px; }
|
|
|
|
/* Chip stack indicator drawn near the player's bet (gives action visual
|
|
weight even before the bubble). */
|
|
.chip-pile {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: -14px;
|
|
transform: translateX(-50%);
|
|
display: flex;
|
|
gap: 2px;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transition: opacity 200ms steps(2);
|
|
}
|
|
.chip-pile.visible { opacity: 1; }
|
|
.chip-pile .chip {
|
|
width: 12px; height: 12px;
|
|
border-radius: 50%;
|
|
border: 2px solid #160f0c;
|
|
background: var(--gold);
|
|
box-shadow: 0 2px 0 rgba(0,0,0,0.5);
|
|
}
|
|
.chip-pile .chip:nth-child(2) { background: var(--red); }
|
|
.chip-pile .chip:nth-child(3) { background: var(--blue); }
|
|
|
|
/* ---------- Event log ---------- */
|
|
.event-panel .panel-section { display: flex; flex-direction: column; }
|
|
.event-log {
|
|
display: grid;
|
|
gap: 8px;
|
|
/* Use viewport-relative max-height for desktop, with a fallback minimum
|
|
so the log never collapses to nothing. The actual scrollable height is
|
|
plenty for 20+ events while still aligning with the table's height. */
|
|
max-height: clamp(420px, 70vh, 760px);
|
|
overflow: auto;
|
|
margin: 0;
|
|
padding: 0 4px 0 26px;
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--gold-dark) #1a1310;
|
|
}
|
|
.event-log::-webkit-scrollbar { width: 10px; }
|
|
.event-log::-webkit-scrollbar-track { background: #1a1310; }
|
|
.event-log::-webkit-scrollbar-thumb {
|
|
background: var(--gold-dark);
|
|
border: 2px solid #1a1310;
|
|
}
|
|
.event-log li {
|
|
border-left: 4px solid var(--line);
|
|
padding: 7px 8px;
|
|
color: var(--ink-dim);
|
|
background: rgba(0,0,0,0.18);
|
|
word-break: break-word;
|
|
white-space: normal;
|
|
line-height: 1.4;
|
|
}
|
|
.event-log li.current {
|
|
border-color: var(--gold);
|
|
color: var(--ink);
|
|
background: rgba(240,182,77,0.14);
|
|
box-shadow: inset 2px 0 0 var(--gold);
|
|
}
|
|
.event-log li.past { opacity: 0.85; }
|
|
|
|
/* ---------- Animations ---------- */
|
|
@keyframes cardDeal {
|
|
from { opacity: 0; transform: translateY(-18px) rotate(-6deg) scale(0.86); }
|
|
to { opacity: 1; transform: translateY(0) rotate(0) scale(1); }
|
|
}
|
|
@keyframes bubblePop {
|
|
from { opacity: 0; transform: translate(-50%, 8px) scale(0.8); }
|
|
to { opacity: 1; transform: translate(-50%, 0) scale(1); }
|
|
}
|
|
@keyframes chipSpin {
|
|
0%, 90%, 100% { transform: rotate(0); }
|
|
95% { transform: rotate(8deg); }
|
|
}
|
|
@keyframes avatarHop {
|
|
0%, 100% { transform: translateY(0); }
|
|
40% { transform: translateY(-6px); }
|
|
70% { transform: translateY(-2px); }
|
|
}
|
|
@keyframes avatarShake {
|
|
0%, 100% { transform: translateX(0); }
|
|
20% { transform: translateX(-3px) rotate(-3deg); }
|
|
40% { transform: translateX(3px) rotate(3deg); }
|
|
60% { transform: translateX(-2px) rotate(-2deg); }
|
|
80% { transform: translateX(2px) rotate(2deg); }
|
|
}
|
|
@keyframes avatarNod {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(3px); }
|
|
}
|
|
@keyframes avatarFold {
|
|
0% { transform: rotate(0) translateY(0); }
|
|
100% { transform: rotate(-12deg) translateY(2px); }
|
|
}
|
|
@keyframes avatarCheer {
|
|
0%, 100% { transform: translateY(0) rotate(0); }
|
|
20% { transform: translateY(-8px) rotate(-6deg); }
|
|
50% { transform: translateY(-4px) rotate(6deg); }
|
|
80% { transform: translateY(-6px) rotate(-3deg); }
|
|
}
|
|
|
|
/* =========================================================================
|
|
Responsive breakpoints
|
|
- Tablet (<=1180px): drop to 2 columns. Stage on top spans full width;
|
|
controls + events sit side-by-side underneath.
|
|
- Mobile (<=760px): single column; stage first, then controls, then log.
|
|
========================================================================= */
|
|
|
|
@media (max-width: 1180px) {
|
|
.layout-grid {
|
|
grid-template-columns: minmax(260px, 1fr) minmax(260px, 1fr);
|
|
grid-template-areas:
|
|
"stage stage"
|
|
"controls events";
|
|
}
|
|
.event-log { max-height: 360px; }
|
|
:root { --seat-size: clamp(96px, 16vw, 130px); }
|
|
}
|
|
|
|
@media (max-width: 760px) {
|
|
.app-shell { padding: 10px; }
|
|
.topbar {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.status-strip { justify-content: flex-start; }
|
|
|
|
.layout-grid {
|
|
grid-template-columns: 1fr;
|
|
grid-template-areas:
|
|
"stage"
|
|
"controls"
|
|
"events";
|
|
}
|
|
|
|
.stage-head {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.poker-table {
|
|
padding: 56px 4px 64px;
|
|
height: 600px;
|
|
}
|
|
.felt-shell {
|
|
inset: 48px 0 56px;
|
|
border-radius: 28px;
|
|
}
|
|
.felt-surface { border-radius: 22px; }
|
|
|
|
.community-area { width: min(76%, 420px); }
|
|
|
|
.event-log { max-height: 240px; }
|
|
|
|
:root {
|
|
--seat-size: clamp(86px, 30vw, 120px);
|
|
--avatar-size: 36px;
|
|
}
|
|
}
|
|
|
|
/* Reduce-motion users get static sprites (no shake/hop). */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after { animation: none !important; transition: none !important; }
|
|
}
|