golfgame/client/style.css
adlee-was-taken 7dc27fe882 Use window.innerHeight for mobile viewport height on Chrome Android
Set --app-height CSS custom property from window.innerHeight via JS,
which is the only reliable way to get the actual visible viewport on
Chrome Android. Falls back to 100vh if JS hasn't loaded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:28:58 -05:00

5431 lines
111 KiB
CSS

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
/* Dark emerald pool table felt */
background: linear-gradient(180deg, #0a4528 0%, #0d5030 50%, #0a4528 100%);
min-height: 100vh;
color: #fff;
}
#app {
max-width: 1100px;
margin: 0 auto;
padding: 15px;
min-height: 100vh;
}
/* Game screen uses full width */
#game-screen {
max-width: none;
width: 100vw;
margin-left: calc(-50vw + 50%);
padding: 0;
box-sizing: border-box;
}
.screen {
display: none;
}
.screen.active {
display: block;
}
/* Lobby Screen */
#lobby-screen {
max-width: 400px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
/* Golf title - golf ball with dimples and shine */
.golf-title {
font-size: 1.3em;
font-weight: 800;
letter-spacing: 0.02em;
/* Shiny gradient like a golf ball surface */
background:
/* Dimple pattern - diagonal grid */
radial-gradient(circle at 3px 3px, rgba(0,0,0,0.18) 2px, transparent 2px),
/* Shiny highlight gradient - whiter */
linear-gradient(
135deg,
#ffffff 0%,
#ffffff 25%,
#f5f5f2 50%,
#ffffff 75%,
#f0f0ed 100%
);
background-size: 10px 10px, 100% 100%;
background-position: 0 0, 0 0;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow:
2px 2px 4px rgba(0, 0, 0, 0.2),
-1px -1px 0 rgba(255, 255, 255, 0.4);
filter: drop-shadow(1px 1px 1px rgba(0,0,0,0.15));
}
/* Golf ball logo with card suits */
.golfball-logo {
width: 1.1em;
height: 1.1em;
vertical-align: middle;
margin-right: 18px;
filter: drop-shadow(1px 2px 2px rgba(0,0,0,0.25));
}
/* Golfer swing animation */
.golfer-swing {
display: inline-block;
transform: scaleX(-1);
animation: golf-swing 0.8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
animation-delay: 0.3s;
}
@keyframes golf-swing {
0% {
transform: scaleX(-1) translateX(0) rotate(0deg);
}
/* Wind up - pull back leg */
30% {
transform: scaleX(-1) translateX(-8px) rotate(-15deg);
}
/* Hold briefly */
40% {
transform: scaleX(-1) translateX(-8px) rotate(-15deg);
}
/* KICK! */
55% {
transform: scaleX(-1) translateX(8px) rotate(20deg);
}
/* Follow through */
80% {
transform: scaleX(-1) translateX(4px) rotate(12deg);
}
/* Final pose - freeze */
100% {
transform: scaleX(-1) translateX(3px) rotate(10deg);
}
}
/* Kicked golf ball - parabolic trajectory */
.kicked-ball {
display: inline-block;
font-size: 0.2em;
position: relative;
opacity: 0;
animation: ball-kicked 0.7s linear forwards;
animation-delay: 0.72s;
}
/* Trajectory: y = 0.0124x² - 1.42x (parabola with peak at x=57) */
@keyframes ball-kicked {
0% {
transform: translate(-12px, 8px) scale(1);
opacity: 1;
}
15% {
transform: translate(8px, -16px) scale(1);
opacity: 1;
}
30% {
transform: translate(28px, -31px) scale(1);
opacity: 1;
}
45% {
transform: translate(48px, -38px) scale(0.95);
opacity: 1;
}
55% {
transform: translate(63px, -38px) scale(0.9);
opacity: 1;
}
70% {
transform: translate(83px, -27px) scale(0.85);
opacity: 0.9;
}
85% {
transform: translate(103px, -6px) scale(0.75);
opacity: 0.6;
}
100% {
transform: translate(118px, 25px) scale(0.65);
opacity: 0;
}
}
#lobby-screen .form-group {
text-align: left;
}
#lobby-screen input {
text-align: center;
font-size: 1.1rem;
}
#lobby-screen #room-code {
text-transform: uppercase;
letter-spacing: 0.3em;
font-weight: 600;
}
/* Waiting Screen */
#waiting-screen {
max-width: 1000px;
margin: 0 auto;
padding: 20px 40px;
position: relative;
}
/* Desktop: side-by-side layout */
.waiting-layout {
display: grid;
grid-template-columns: 220px 1fr;
gap: 25px;
align-items: start;
padding-top: 70px;
}
.waiting-left-col {
display: flex;
flex-direction: column;
gap: 10px;
}
.waiting-left-col .players-list {
background: rgba(0,0,0,0.2);
border-radius: 10px;
padding: 15px;
}
.waiting-left-col .players-list h3 {
margin: 0 0 10px 0;
font-size: 1rem;
}
#waiting-screen .settings {
background: rgba(0,0,0,0.2);
border-radius: 10px;
padding: 20px;
}
#waiting-screen .settings h3 {
margin: 0 0 15px 0;
}
/* Basic settings in a row */
.basic-settings-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 15px;
align-items: end;
}
.basic-settings-row .form-group {
margin-bottom: 0;
}
.basic-settings-row .form-group label {
font-size: 0.8rem;
margin-bottom: 4px;
display: block;
}
.basic-settings-row select {
width: 100%;
padding: 8px 4px;
}
/* Stepper Control */
.stepper-control {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
background: rgba(0,0,0,0.3);
border-radius: 6px;
padding: 4px 8px;
}
.stepper-btn {
width: 28px;
height: 28px;
border: none;
border-radius: 4px;
background: #4a5568;
color: white;
font-size: 1.2rem;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.15s;
}
.stepper-btn:hover {
background: #5a6578;
}
.stepper-btn:active {
background: #3a4558;
}
.stepper-value {
min-width: 24px;
text-align: center;
font-weight: bold;
font-size: 1.1rem;
}
/* Deck Color Selector */
.deck-color-selector {
display: flex;
align-items: center;
gap: 10px;
}
.deck-color-selector select {
flex: 1;
}
.deck-color-preview {
display: flex;
gap: 3px;
padding: 4px;
background: rgba(0,0,0,0.3);
border-radius: 4px;
}
.preview-card {
width: 16px;
height: 22px;
border-radius: 2px;
border: 1px solid rgba(255,255,255,0.2);
}
/* Deck color classes for preview cards */
.deck-red { background: linear-gradient(135deg, #c41e3a 0%, #922b21 100%); }
.deck-blue { background: linear-gradient(135deg, #2e5cb8 0%, #1a3a7a 100%); }
.deck-green { background: linear-gradient(135deg, #228b22 0%, #145214 100%); }
.deck-gold { background: linear-gradient(135deg, #daa520 0%, #b8860b 100%); }
.deck-purple { background: linear-gradient(135deg, #6a0dad 0%, #4b0082 100%); }
.deck-teal { background: linear-gradient(135deg, #008b8b 0%, #005f5f 100%); }
.deck-pink { background: linear-gradient(135deg, #db7093 0%, #c04f77 100%); }
.deck-slate { background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); }
.deck-orange { background: linear-gradient(135deg, #e67e22 0%, #d35400 100%); }
.deck-cyan { background: linear-gradient(135deg, #00bcd4 0%, #0097a7 100%); }
.deck-brown { background: linear-gradient(135deg, #8b4513 0%, #5d2f0d 100%); }
/* CPU Controls Section - below players list */
.cpu-controls-section {
background: rgba(0,0,0,0.2);
border-radius: 8px;
padding: 10px 12px;
}
.cpu-controls-section h4 {
margin: 0 0 6px 0;
font-size: 0.8rem;
color: #f4a460;
}
.cpu-controls-section .cpu-controls {
display: flex;
gap: 6px;
}
.cpu-controls-section .cpu-controls .btn {
flex: 1;
padding: 6px 0;
font-size: 1rem;
font-weight: bold;
}
#waiting-message {
grid-column: 1 / -1;
text-align: center;
margin-top: 15px;
}
/* Mobile: stack vertically */
@media (max-width: 700px) {
.waiting-layout {
grid-template-columns: 1fr;
}
.basic-settings-row {
grid-template-columns: 1fr 1fr;
}
}
/* Room Code Banner - styled as a hanging ribbon/bookmark */
.room-code-banner {
position: fixed;
top: 0;
left: 20px;
z-index: 100;
background: linear-gradient(180deg, #d4845a 0%, #c4723f 50%, #b8663a 100%);
padding: 10px 14px 18px;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
box-shadow: 2px 4px 12px rgba(0, 0, 0, 0.3);
/* Ribbon forked end (snake tongue style) */
clip-path: polygon(0 0, 100% 0, 100% 100%, 50% calc(100% - 10px), 0 100%);
}
.room-code-banner::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(180deg, rgba(255,255,255,0.3) 0%, transparent 100%);
}
.room-code-value {
font-size: 1.5rem;
font-weight: 800;
font-family: 'Courier New', monospace;
letter-spacing: 0.15em;
color: #fff;
text-shadow: 0 2px 2px rgba(0, 0, 0, 0.3);
}
.room-code-buttons {
display: flex;
gap: 5px;
}
.room-code-copy {
background: rgba(255, 255, 255, 0.85);
border: none;
border-radius: 4px;
padding: 4px 6px;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.2s;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.room-code-copy:hover {
background: rgba(255, 255, 255, 0.35);
transform: scale(1.1);
}
.room-code-copy:active {
transform: scale(0.95);
}
h1 {
font-size: 3rem;
text-align: center;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.subtitle {
text-align: center;
opacity: 0.8;
margin-bottom: 40px;
}
h2 {
text-align: center;
margin-bottom: 20px;
}
h3 {
margin-bottom: 15px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
input, select {
width: 100%;
padding: 12px;
border: none;
border-radius: 8px;
font-size: 1rem;
background: rgba(255,255,255,0.9);
color: #333;
}
input::placeholder {
color: #999;
}
.button-group {
margin-bottom: 20px;
}
.btn {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
width: 100%;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.btn:active {
transform: translateY(0);
}
.btn-primary {
background: #f4a460;
color: #1a472a;
}
/* Pulsing glow for Next Hole button */
#next-round-btn:not(.hidden) {
animation: glow-pulse 1.5s ease-in-out infinite;
}
@keyframes glow-pulse {
0%, 100% {
box-shadow: 0 0 5px rgba(244, 164, 96, 0.4),
0 0 10px rgba(244, 164, 96, 0.2);
}
50% {
box-shadow: 0 0 15px rgba(244, 164, 96, 0.8),
0 0 30px rgba(244, 164, 96, 0.4),
0 0 45px rgba(244, 164, 96, 0.2);
}
}
.btn-secondary {
background: #fff;
color: #1a472a;
}
.btn-danger {
background: #c0392b;
color: #fff;
}
.btn-success {
background: #27ae60;
color: #fff;
}
.btn-small {
padding: 8px 16px;
font-size: 0.9rem;
width: auto;
}
.game-buttons .btn-next-round {
padding: 10px 20px;
font-size: 1rem;
font-weight: 600;
width: 100%;
background: #f4a460;
color: #1a472a;
}
.btn.disabled,
.btn:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* Waiting state for non-host next hole button */
#next-round-btn.waiting {
animation: none;
background: rgba(244, 164, 96, 0.4);
color: rgba(255, 255, 255, 0.8);
box-shadow: none;
}
.divider {
text-align: center;
margin: 30px 0;
opacity: 0.6;
}
.error {
color: #e74c3c;
text-align: center;
margin-top: 15px;
}
.info {
text-align: center;
opacity: 0.8;
margin: 20px 0;
}
.recommendation {
color: #f4a460;
font-size: 0.8rem;
margin-top: 6px;
padding: 6px 8px;
background: rgba(244, 164, 96, 0.15);
border-radius: 4px;
border-left: 3px solid #f4a460;
}
.hidden {
display: none !important;
}
/* Players List */
.players-list {
background: rgba(0,0,0,0.2);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
}
.players-list ul {
list-style: none;
}
.players-list li {
padding: 10px;
background: rgba(255,255,255,0.1);
border-radius: 6px;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.players-list .host-badge {
background: #f4a460;
color: #1a472a;
padding: 2px 8px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
}
.players-list .cpu-badge {
background: #3498db;
color: #fff;
padding: 2px 8px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
}
.cpu-style {
font-size: 0.8rem;
opacity: 0.7;
font-style: italic;
}
.cpu-controls {
display: flex;
gap: 10px;
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 0.9rem;
}
.checkbox-label input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.radio-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.radio-label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
font-size: 0.95rem;
font-weight: 500;
}
.radio-label input[type="radio"] {
width: 18px;
height: 18px;
cursor: pointer;
flex-shrink: 0;
}
.radio-label .rule-desc {
width: auto;
margin-left: 0;
opacity: 0.6;
font-weight: 400;
font-size: 0.8rem;
white-space: nowrap;
}
/* Settings */
.settings {
background: rgba(0,0,0,0.2);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
}
/* Game Screen */
.game-header {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
padding: 10px 20px;
background: rgba(0,0,0,0.35);
font-size: 0.9rem;
width: 100vw;
margin-left: calc(-50vw + 50%);
box-sizing: border-box;
}
.header-col {
display: flex;
align-items: center;
}
.header-col-left {
justify-content: flex-start;
gap: 12px;
}
.header-col-center {
justify-content: center;
display: flex;
align-items: center;
gap: 10px;
}
.header-col-right {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
gap: 6px;
flex-wrap: nowrap;
min-width: max-content;
}
#game-logout-btn {
padding: 4px 8px;
font-size: 0.75rem;
}
.game-header .round-info {
font-weight: 600;
white-space: nowrap;
}
#leave-game-btn {
padding: 6px 12px;
font-size: 0.8rem;
}
.mute-btn {
background: transparent;
border: none;
font-size: 1.1rem;
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
opacity: 0.8;
transition: opacity 0.2s;
}
.mute-btn:hover {
opacity: 1;
background: rgba(255,255,255,0.1);
}
/* Active Rules (in header) */
.active-rules-bar {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.85rem;
}
.active-rules-bar.hidden {
display: none;
}
.active-rules-bar .rules-label {
color: rgba(255, 255, 255, 0.5);
font-weight: 500;
}
.active-rules-bar .rules-list {
display: flex;
gap: 5px;
flex-wrap: nowrap;
}
.active-rules-bar .rule-tag {
background: rgba(244, 164, 96, 0.3);
color: #f4a460;
padding: 2px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
}
.active-rules-bar .rule-tag.standard {
background: rgba(255, 255, 255, 0.15);
color: rgba(255, 255, 255, 0.7);
}
.active-rules-bar .rule-tag.rule-more {
background: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.6);
cursor: help;
border: 1px dashed rgba(255, 255, 255, 0.3);
}
.active-rules-bar .rule-tag.rule-more:hover {
background: rgba(255, 255, 255, 0.2);
color: rgba(255, 255, 255, 0.9);
}
.active-rules-bar .rule-tag.unranked {
background: rgba(220, 80, 80, 0.3);
color: #f08080;
border: 1px solid rgba(220, 80, 80, 0.4);
}
/* Unranked notice in waiting room */
.unranked-notice {
background: rgba(220, 80, 80, 0.15);
border: 1px solid rgba(220, 80, 80, 0.3);
color: #f0a0a0;
font-size: 0.8rem;
padding: 6px 12px;
border-radius: 6px;
margin: 8px 0;
text-align: center;
}
.unranked-notice.hidden {
display: none;
}
/* Card Styles */
.card {
width: clamp(65px, 5.5vw, 100px);
height: clamp(91px, 7.7vw, 140px);
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: clamp(2rem, 2.5vw, 3.2rem);
font-weight: bold;
cursor: pointer;
/* No CSS transition - hover effects handled by anime.js */
position: relative;
user-select: none;
}
/* Hover state set by anime.js - do not add CSS hover transform here */
.card-back {
/* Bee-style diamond grid pattern - default red with white crosshatch */
background-color: #c41e3a;
background-image:
linear-gradient(45deg, rgba(255,255,255,0.25) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255,255,255,0.25) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.25) 75%),
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.25) 75%);
background-size: 8px 8px;
background-position: 0 0, 0 4px, 4px -4px, -4px 0;
border: 3px solid #8b1528;
color: #fff;
font-size: clamp(1.8rem, 2.5vw, 3rem);
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
/* Card back color variants */
.card-back.back-red { background-color: #c41e3a; border-color: #8b1528; }
.card-back.back-blue { background-color: #2e5cb8; border-color: #1a3a7a; }
.card-back.back-green { background-color: #228b22; border-color: #145214; }
.card-back.back-gold { background-color: #daa520; border-color: #b8860b; }
.card-back.back-purple { background-color: #6a0dad; border-color: #4b0082; }
.card-back.back-teal { background-color: #008b8b; border-color: #005f5f; }
.card-back.back-pink { background-color: #db7093; border-color: #c04f77; }
.card-back.back-slate { background-color: #4a5568; border-color: #2d3748; }
.card-back.back-orange { background-color: #e67e22; border-color: #d35400; }
.card-back.back-cyan { background-color: #00bcd4; border-color: #0097a7; }
.card-back.back-brown { background-color: #8b4513; border-color: #5d2f0d; }
.card-front {
background: #fff;
border: 2px solid #ddd;
color: #333;
text-align: center;
}
.card-front.red {
color: #c0392b;
}
.card-front.black {
color: #2c3e50;
}
.card-front.joker {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
}
.card-front.joker .joker-icon {
font-size: 1.6em;
line-height: 1;
}
.card-front.joker .joker-label {
font-size: 0.45em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #9b59b6;
}
/* Unknown card placeholder (locally flipped, server hasn't confirmed yet) */
.card-front .unknown-card {
font-size: 1.8em;
color: #7f8c8d;
opacity: 0.6;
}
.card.clickable {
cursor: pointer;
box-shadow: 0 0 0 2px rgba(244, 164, 96, 0.5);
}
.card.clickable:hover {
box-shadow: 0 0 0 3px #f4a460;
}
/* Disable hover effects when not player's turn */
#game-screen.not-my-turn .card {
cursor: default;
}
#game-screen.not-my-turn .card:hover {
transform: none;
box-shadow: none;
}
#game-screen.not-my-turn #deck,
#game-screen.not-my-turn #discard {
cursor: default;
}
#game-screen.not-my-turn #deck:hover,
#game-screen.not-my-turn #discard:hover {
transform: none;
box-shadow: none;
}
.card.selected,
.card.clickable.selected {
box-shadow: 0 0 0 4px #fff, 0 0 12px 4px #f4a460;
transform: scale(1.08);
}
/* Card Grid */
.card-grid {
display: grid;
grid-template-columns: repeat(3, clamp(65px, 5.5vw, 100px));
gap: clamp(8px, 0.8vw, 14px);
justify-content: center;
}
/* Game Table Layout */
.game-table {
display: flex;
flex-direction: column;
align-items: center;
gap: 25px;
width: 100%;
}
/* Player row - deck/discard and player cards side by side */
.player-row {
display: flex;
justify-content: center;
align-items: center;
gap: 25px;
width: 100%;
flex-wrap: wrap;
}
.opponents-row {
display: flex;
flex-wrap: nowrap;
justify-content: center;
align-items: flex-end;
gap: clamp(12px, 1.8vw, 35px);
min-height: clamp(120px, 14vw, 200px);
padding: 8px 20px 0;
width: 100%;
}
/* Arch layout - middle items higher, edges lower with rotation for "around the table" feel */
.opponents-row .opponent-area {
flex-shrink: 0;
transition: transform 0.3s ease;
}
/* 2 opponents: slight rotation toward center */
.opponents-row .opponent-area:first-child:nth-last-child(2) {
margin-bottom: 15px;
transform: rotate(-4deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(2) ~ .opponent-area {
margin-bottom: 15px;
transform: rotate(4deg);
}
/* 3 opponents: middle higher, edges rotated toward center */
.opponents-row .opponent-area:first-child:nth-last-child(3) {
margin-bottom: 0;
transform: rotate(-6deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(3) ~ .opponent-area:not(:last-child) {
margin-bottom: 35px;
transform: rotate(0deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(3) ~ .opponent-area:last-child {
margin-bottom: 0;
transform: rotate(6deg);
}
/* 4 opponents: arch shape with rotation toward center */
.opponents-row .opponent-area:first-child:nth-last-child(4) {
margin-bottom: 0;
transform: rotate(-8deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(4) ~ .opponent-area:nth-child(2) {
margin-bottom: 30px;
transform: rotate(-3deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(4) ~ .opponent-area:nth-child(3) {
margin-bottom: 30px;
transform: rotate(3deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(4) ~ .opponent-area:last-child {
margin-bottom: 0;
transform: rotate(8deg);
}
/* 5 opponents: deeper arch with graduated rotation toward center */
.opponents-row .opponent-area:first-child:nth-last-child(5) {
margin-bottom: 0;
transform: rotate(-10deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:nth-child(2) {
margin-bottom: 25px;
transform: rotate(-5deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:nth-child(3) {
margin-bottom: 40px;
transform: rotate(0deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:nth-child(4) {
margin-bottom: 25px;
transform: rotate(5deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:last-child {
margin-bottom: 0;
transform: rotate(10deg);
}
.table-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
background: rgba(0,0,0,0.15);
padding: 15px 20px;
border-radius: 12px;
}
.deck-area {
display: flex;
gap: 15px;
align-items: flex-start;
}
/* Gentle pulse when it's your turn to draw - handled by anime.js */
/* The .your-turn-to-draw class triggers anime.js startTurnPulse() */
/* Held card slot - hidden, using floating card over discard instead */
/* Draw animation card (Anime.js powered) */
.draw-anim-card {
position: fixed;
z-index: 200;
perspective: 800px;
pointer-events: none;
}
.draw-anim-inner {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
}
.draw-anim-front,
.draw-anim-back {
position: absolute;
width: 100% !important;
height: 100% !important;
backface-visibility: hidden;
border-radius: 8px;
}
.draw-anim-front {
transform: rotateY(0deg);
}
.draw-anim-back {
transform: rotateY(180deg);
}
.held-card-slot {
display: none !important;
}
/* Held card floating above and between deck and discard (larger, closer to viewer) */
.held-card-floating {
position: fixed;
top: 0;
left: 0;
z-index: 100;
border: 3px solid #f4a460 !important;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4), 0 0 20px rgba(244, 164, 96, 0.6) !important;
pointer-events: none;
/* No transition - anime.js handles animations */
}
.held-card-floating.hidden {
opacity: 0;
pointer-events: none;
}
/* Pop-in animation - now handled by anime.js popIn() */
/* Keeping class for backwards compatibility */
.held-card-floating.pop-in {
/* Animation handled by JS */
}
/* Animate floating card dropping to discard pile (when drawn from discard) */
.held-card-floating.dropping {
border-color: transparent !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
/* transition removed - anime.js handles animations */
}
/* Swoop animation for deck → immediate discard */
.held-card-floating.swooping {
/* transition removed - anime.js handles animations */
}
.held-card-floating.swooping.landed {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
}
.deck-area .card {
width: clamp(80px, 7vw, 120px);
height: clamp(112px, 9.8vw, 168px);
font-size: clamp(2.4rem, 3.2vw, 4rem);
}
#discard {
background: rgba(255,255,255,0.1);
border: 2px dashed rgba(255,255,255,0.3);
}
#discard.has-card {
background: #fff;
border: 2px solid #ddd;
}
/* Holding state - when player has drawn a card */
#discard.holding {
background: #fff;
border: 3px solid #f4a460;
box-shadow: 0 0 15px rgba(244, 164, 96, 0.6);
transform: scale(1.05);
}
/* Picked-up state - dimmed when someone is holding a card */
#discard.picked-up {
opacity: 0.5;
filter: grayscale(40%);
}
.discard-stack {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.discard-stack .btn {
white-space: nowrap;
}
/* Discard button as a tab attached to right side of held card */
#discard-btn {
position: fixed;
z-index: 101;
writing-mode: vertical-rl;
text-orientation: mixed;
padding: 16px 8px;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.05em;
border-radius: 0 8px 8px 0;
background: linear-gradient(90deg, #e8914d 0%, #f4a460 100%);
color: #1a472a;
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3);
border: none;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s, opacity 0.15s;
}
#discard-btn:hover {
transform: scale(1.05);
box-shadow: 3px 3px 12px rgba(0, 0, 0, 0.4);
}
#discard-btn:active {
transform: scale(0.98);
}
#deck.disabled,
#discard.disabled {
opacity: 0.5;
cursor: not-allowed;
filter: grayscale(50%);
}
#deck.disabled:hover,
#discard.disabled:hover {
transform: none;
box-shadow: none;
}
/* Highlight flash when drawing from a pile - uses ::after for guaranteed visibility */
#deck.draw-pulse,
#discard.draw-pulse {
position: relative;
z-index: 250;
}
#deck.draw-pulse::after,
#discard.draw-pulse::after {
content: '';
position: absolute;
top: -8px;
left: -8px;
right: -8px;
bottom: -8px;
border: 4px solid gold;
border-radius: 10px;
animation: draw-highlight-ring 0.4s ease-out forwards;
pointer-events: none;
z-index: 999;
}
@keyframes draw-highlight-ring {
0% {
opacity: 1;
transform: scale(0.9);
border-width: 4px;
}
30% {
opacity: 1;
transform: scale(1.1);
border-width: 6px;
}
100% {
opacity: 0;
transform: scale(1.2);
border-width: 2px;
}
}
/* Deck "dealing" effect when drawing from deck */
#deck.dealing {
animation: deck-deal 0.15s ease-out;
}
@keyframes deck-deal {
0% { transform: scale(1); }
30% { transform: scale(0.97) translateY(2px); }
100% { transform: scale(1); }
}
/* Card appearing on discard pile */
.card-flip-in {
animation: cardFlipIn 0.25s ease-out;
}
@keyframes cardFlipIn {
from { opacity: 0.5; }
to { opacity: 1; }
}
/* Discard pile pulse when card lands - handled by anime.js pulseDiscard() */
/* The .discard-land class is kept for backwards compatibility */
/* CPU considering discard pile - handled by anime.js startCpuThinking() */
/* The .cpu-considering class is still used as a flag, but animation is via JS */
/* Discard pickup animation - simple dim */
#discard.discard-pickup {
animation: discardPickup 0.25s ease-out;
}
@keyframes discardPickup {
0% {
opacity: 1;
}
50% {
opacity: 0.6;
}
100% {
opacity: 1;
}
}
/* Swap animation overlay */
.swap-animation {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1000;
}
.swap-animation.hidden {
display: none;
}
.swap-card {
position: absolute;
width: 70px;
height: 98px;
perspective: 800px;
}
.swap-card.hidden {
display: none;
}
.swap-card-inner {
position: relative;
width: 100%;
height: 100%;
border-radius: 8px;
transform-style: preserve-3d;
/* transition removed - anime.js handles all flip animations */
}
.swap-card.flipping .swap-card-inner {
transform: rotateY(180deg);
}
.swap-card-front,
.swap-card-back {
position: absolute;
width: 100%;
height: 100%;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
box-shadow: 0 4px 15px rgba(0,0,0,0.4);
backface-visibility: hidden;
}
.swap-card-back {
background: linear-gradient(135deg, #c0392b 0%, #922b21 100%);
color: rgba(255,255,255,0.4);
font-size: 2rem;
}
/* Swap card back color variants */
.swap-card-back.back-red { background: linear-gradient(135deg, #c41e3a 0%, #922b21 100%); }
.swap-card-back.back-blue { background: linear-gradient(135deg, #2e5cb8 0%, #1a3a7a 100%); }
.swap-card-back.back-green { background: linear-gradient(135deg, #228b22 0%, #145214 100%); }
.swap-card-back.back-gold { background: linear-gradient(135deg, #daa520 0%, #b8860b 100%); }
.swap-card-back.back-purple { background: linear-gradient(135deg, #6a0dad 0%, #4b0082 100%); }
.swap-card-back.back-teal { background: linear-gradient(135deg, #008b8b 0%, #005f5f 100%); }
.swap-card-back.back-pink { background: linear-gradient(135deg, #db7093 0%, #c04f77 100%); }
.swap-card-back.back-slate { background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); }
.swap-card-back.back-orange { background: linear-gradient(135deg, #e67e22 0%, #d35400 100%); }
.swap-card-back.back-cyan { background: linear-gradient(135deg, #00bcd4 0%, #0097a7 100%); }
.swap-card-back.back-brown { background: linear-gradient(135deg, #8b4513 0%, #5d2f0d 100%); }
.swap-card-front {
background: linear-gradient(145deg, #fff 0%, #f5f5f5 100%);
border: 2px solid #ddd;
font-size: clamp(1.8rem, 2.2vw, 2.8rem);
flex-direction: column;
color: #333;
line-height: 1.1;
font-weight: bold;
transform: rotateY(180deg);
}
.swap-card-front.red {
color: #c0392b;
}
.swap-card-front.black {
color: #2c3e50;
}
.swap-card-front.joker {
color: #9b59b6;
}
.swap-card-front .joker-icon {
font-size: 1.5em;
line-height: 1;
}
.swap-card-front .joker-label {
font-size: 0.4em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.swap-card-front.unknown {
color: #7f8c8d;
}
.swap-card-front .unknown-icon {
font-size: 2em;
opacity: 0.6;
}
.swap-card.moving {
/* transition removed - anime.js handles animations */
}
/* Card in hand fading during swap */
.card.swap-out {
opacity: 0;
/* transition removed - anime.js handles animations */
}
/* Discard fading during swap */
#discard.swap-to-hand {
opacity: 0;
/* transition removed - anime.js handles animations */
}
/* Subtle swap pulse for face-to-face swaps - handled by anime.js pulseSwap() */
/* Keeping the class for backwards compatibility */
/* Fade transitions for swap animation */
.card.fade-out,
.held-card-floating.fade-out,
.anim-card.fade-out {
opacity: 0;
/* transition removed - anime.js handles animations */
}
.card.fade-in,
.held-card-floating.fade-in,
.anim-card.fade-in {
opacity: 1;
/* transition removed - anime.js handles animations */
}
/* Pulse animation for clickable cards during initial flip phase */
/* Now handled by anime.js startInitialFlipPulse() for consistency */
/* Keeping the class as a hook but animation is via JS */
.card.clickable.initial-flip-pulse {
/* Fallback static glow if JS doesn't start animation */
box-shadow: 0 0 0 2px rgba(244, 164, 96, 0.5);
}
/* Held card pulse glow for local player's turn */
/* Keeping CSS animation for this as it's a simple looping effect */
.held-card-floating.your-turn-pulse {
animation: heldCardPulse 1.5s ease-in-out infinite;
}
@keyframes heldCardPulse {
0%, 100% {
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4), 0 0 20px rgba(244, 164, 96, 0.6);
}
50% {
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4), 0 0 30px rgba(244, 164, 96, 0.9),
0 0 45px rgba(244, 164, 96, 0.4);
}
}
/* Player Area */
.player-section {
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 0;
}
.player-area {
position: relative;
background: rgba(0,0,0,0.2);
border-radius: 8px;
padding: 10px 15px;
text-align: center;
}
/* Opponent Areas */
.opponent-area {
position: relative;
background: rgba(0,0,0,0.2);
border-radius: 8px;
padding: clamp(4px, 0.5vw, 10px) clamp(6px, 0.7vw, 14px) clamp(6px, 0.7vw, 14px);
text-align: center;
}
.opponent-area h4 {
font-size: clamp(0.7rem, 0.8vw, 1rem);
margin: 0 0 6px 0;
padding: clamp(3px, 0.35vw, 6px) clamp(8px, 0.9vw, 14px);
background: rgba(244, 164, 96, 0.5);
border-radius: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
justify-content: space-between;
align-items: center;
}
.opponent-showing,
.player-showing {
font-weight: 700;
color: rgba(255, 255, 255, 0.9);
background: rgba(0, 0, 0, 0.25);
padding: 1px 6px;
border-radius: 3px;
font-size: 0.9em;
margin-left: 8px;
}
/* Player area header - matches opponent style */
.player-area h4 {
font-size: clamp(0.8rem, 1vw, 1.1rem);
margin: 0 0 8px 0;
padding: clamp(4px, 0.4vw, 8px) clamp(10px, 1vw, 16px);
background: rgba(244, 164, 96, 0.6);
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
}
.opponent-area .card-grid {
display: grid;
grid-template-columns: repeat(3, clamp(45px, 4vw, 75px));
gap: clamp(4px, 0.4vw, 8px);
}
.opponent-area .card {
width: clamp(45px, 4vw, 75px);
height: clamp(63px, 5.6vw, 105px);
font-size: clamp(1.3rem, 1.5vw, 2.2rem);
border-radius: 5px;
}
.opponent-area.current-turn {
background: rgba(244, 164, 96, 0.3);
box-shadow: 0 0 0 2px #f4a460;
}
/* Local player turn highlight - green tint to match "your turn" status */
.player-area.current-turn {
background: rgba(181, 212, 132, 0.25);
box-shadow: 0 0 0 2px #9ab973;
}
/* Round winner highlight */
.opponent-area.round-winner h4,
.player-area.round-winner h4 {
background: rgba(180, 255, 80, 0.85);
color: #1a1a1a;
}
.winner-crown {
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
margin-right: 3px;
}
/* Status message in header */
.status-message {
padding: 6px 16px;
border-radius: 4px;
font-size: 0.9rem;
font-weight: 600;
text-align: center;
white-space: nowrap;
color: #fff;
}
.status-message.your-turn {
background: linear-gradient(135deg, #b5d484 0%, #9ab973 100%);
color: #2d3436;
}
/* Opponent turn status - subtle purple/slate for other player's turn */
.status-message.opponent-turn {
background: linear-gradient(135deg, #8b7eb8 0%, #6b5b95 100%);
color: #fff;
}
/* CPU action status - subtle blue to indicate CPU is doing something */
.status-message.cpu-action {
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
color: #fff;
}
/* Final turn badge - enhanced V3 with countdown */
.final-turn-badge {
display: flex;
align-items: center;
gap: 6px;
background: linear-gradient(135deg, #ff6b35 0%, #d63031 100%);
color: #fff;
padding: 6px 14px;
border-radius: 8px;
font-weight: 700;
letter-spacing: 0.05em;
white-space: nowrap;
box-shadow: 0 4px 20px rgba(214, 48, 49, 0.4);
animation: final-turn-pulse 1.5s ease-in-out infinite;
}
.final-turn-badge .final-turn-text {
font-size: 0.85rem;
}
.final-turn-badge.hidden {
display: none;
}
@keyframes final-turn-pulse {
0%, 100% {
box-shadow: 0 2px 12px rgba(214, 48, 49, 0.4);
}
50% {
box-shadow: 0 2px 20px rgba(214, 48, 49, 0.6);
}
}
@keyframes toastIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Flip prompt */
.flip-prompt {
background: linear-gradient(135deg, #f4a460 0%, #e8914d 100%);
color: #1a472a;
padding: 6px 16px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
text-align: center;
white-space: nowrap;
}
.flip-prompt.hidden {
display: none;
}
/* Game screen layout */
#game-screen.active {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
padding: 10px;
}
.game-layout {
display: flex;
justify-content: center;
width: 100%;
align-items: flex-start;
}
.game-main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
width: 100%;
}
/* Side Panels - positioned in bottom corners */
.side-panel {
position: fixed;
bottom: 15px;
background: linear-gradient(145deg, rgba(15, 50, 35, 0.92) 0%, rgba(8, 30, 20, 0.95) 100%);
border-radius: 10px;
padding: 10px 12px;
width: 200px;
z-index: 100;
backdrop-filter: blur(10px);
border: 1px solid rgba(244, 164, 96, 0.25);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}
.side-panel.left-panel {
left: 15px;
}
.side-panel.right-panel {
right: 15px;
}
.side-panel > h4 {
font-size: 0.7rem;
text-align: center;
margin-bottom: 8px;
color: #f4a460;
text-transform: uppercase;
letter-spacing: 0.15em;
font-weight: 700;
border-bottom: 1px solid rgba(244, 164, 96, 0.2);
padding-bottom: 6px;
}
/* Standings list - two sections, top 4 each */
.standings-section {
margin-bottom: 6px;
}
.standings-section:last-child {
margin-bottom: 0;
}
.standings-title {
font-size: 0.6rem;
color: rgba(255,255,255,0.5);
text-transform: uppercase;
letter-spacing: 0.1em;
padding-bottom: 2px;
margin-bottom: 2px;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.standings-list .rank-row {
display: grid;
grid-template-columns: 18px 1fr 30px;
gap: 3px;
font-size: 0.7rem;
padding: 1px 0;
align-items: center;
}
.standings-list .rank-pos {
text-align: center;
font-size: 0.65rem;
}
.standings-list .rank-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.standings-list .rank-val {
text-align: right;
font-size: 0.65rem;
color: rgba(255,255,255,0.7);
}
.standings-list .rank-row.leader {
color: #f4a460;
}
.standings-list .rank-row.leader .rank-val {
color: #f4a460;
}
/* Score table */
.side-panel table {
width: 100%;
border-collapse: collapse;
font-size: 0.75rem;
}
.side-panel th,
.side-panel td {
padding: 4px 3px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.08);
}
.side-panel th {
font-weight: 600;
background: rgba(0,0,0,0.25);
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 0.03em;
color: rgba(255, 255, 255, 0.6);
}
.side-panel td:first-child {
text-align: left;
font-weight: 500;
}
.side-panel tr.winner {
background: linear-gradient(90deg, rgba(244, 164, 96, 0.35) 0%, rgba(244, 164, 96, 0.15) 100%);
}
.side-panel tr.current-player {
background: rgba(244, 164, 96, 0.15);
box-shadow: inset 3px 0 0 #f4a460;
}
.game-buttons {
margin-bottom: 10px;
display: flex;
flex-direction: column;
gap: 8px;
}
.game-buttons .scores-divider {
border: none;
border-top: 1px solid rgba(255, 255, 255, 0.2);
margin: 4px 0 0 0;
width: 100%;
}
.game-buttons .btn {
font-size: 0.8rem;
padding: 6px 8px;
width: 100%;
}
/* Rankings Announcement */
.rankings-announcement {
background: linear-gradient(135deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.25) 100%);
border-radius: 10px;
padding: 10px;
margin-bottom: 12px;
border: 1px solid rgba(244, 164, 96, 0.3);
overflow: hidden;
}
.rankings-announcement h3 {
font-size: 0.95rem;
text-align: center;
margin: 0 0 10px 0;
color: #f4a460;
}
.rankings-announcement h4 {
font-size: 0.8rem;
text-align: center;
margin: 0 0 6px 0;
opacity: 0.8;
}
.rankings-columns {
display: flex;
gap: 8px;
}
.ranking-section {
flex: 1;
min-width: 0;
background: rgba(0,0,0,0.2);
border-radius: 6px;
padding: 6px;
overflow: hidden;
}
.rank-row {
display: flex;
align-items: center;
font-size: 0.8rem;
padding: 3px 0;
gap: 3px;
flex-wrap: nowrap;
}
.rank-row.leader {
color: #f4a460;
font-weight: 600;
}
.rank-pos {
width: 18px;
text-align: center;
flex-shrink: 0;
}
.rank-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.rank-val {
font-size: 0.7rem;
opacity: 0.9;
flex-shrink: 0;
white-space: nowrap;
text-align: right;
}
/* Double Victory */
.double-victory {
background: linear-gradient(135deg, #ffd700 0%, #f4a460 50%, #ffd700 100%);
color: #1a472a;
text-align: center;
padding: 10px;
border-radius: 8px;
font-weight: 700;
font-size: 1rem;
margin-bottom: 10px;
animation: victoryPulse 1s ease-in-out infinite alternate;
text-shadow: 0 1px 0 rgba(255,255,255,0.3);
}
@keyframes victoryPulse {
from { box-shadow: 0 0 5px rgba(255, 215, 0, 0.5); }
to { box-shadow: 0 0 15px rgba(255, 215, 0, 0.8); }
}
/* Responsive */
@media (max-width: 700px) {
.game-layout {
flex-direction: column;
}
.scoreboard-panel {
width: 100%;
order: -1;
margin-bottom: 8px;
padding: 8px;
}
.scoreboard-panel > h4 {
cursor: pointer;
user-select: none;
padding: 4px;
margin: -4px -4px 0 -4px;
border-radius: 4px;
}
.scoreboard-panel > h4:hover {
background: rgba(255,255,255,0.1);
}
.scoreboard-panel > h4::after {
content: ' ▼';
font-size: 0.6rem;
}
.scoreboard-panel.collapsed > h4::after {
content: ' ▶';
}
.scoreboard-panel.collapsed > *:not(h4):not(.game-buttons) {
display: none;
}
.scoreboard-panel table {
max-width: 100%;
margin: 8px auto 0;
}
.rankings-announcement {
padding: 8px;
margin-bottom: 8px;
}
.rankings-columns {
flex-direction: row;
gap: 6px;
}
.ranking-section {
padding: 5px;
}
.game-buttons {
flex-direction: row;
justify-content: center;
}
}
@media (max-width: 500px) {
#app {
padding: 6px;
}
h1 {
font-size: 2rem;
}
.card {
width: 55px;
height: 77px;
font-size: 1.2rem;
}
.card-grid {
grid-template-columns: repeat(3, 55px);
gap: 6px;
}
.opponent-area .card {
width: 38px;
height: 53px;
font-size: 0.75rem;
}
.opponent-area .card-grid {
grid-template-columns: repeat(3, 38px);
gap: 3px;
}
.opponents-row {
min-height: 100px;
gap: 8px;
}
/* Reduce arch heights and rotations on mobile */
.opponents-row .opponent-area:first-child:nth-last-child(2),
.opponents-row .opponent-area:first-child:nth-last-child(2) ~ .opponent-area {
transform: rotate(0deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(3) {
transform: rotate(-4deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(3) ~ .opponent-area:not(:last-child) {
margin-bottom: 22px;
}
.opponents-row .opponent-area:first-child:nth-last-child(3) ~ .opponent-area:last-child {
transform: rotate(4deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(4) {
transform: rotate(-5deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(4) ~ .opponent-area:nth-child(2),
.opponents-row .opponent-area:first-child:nth-last-child(4) ~ .opponent-area:nth-child(3) {
margin-bottom: 18px;
transform: rotate(0deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(4) ~ .opponent-area:last-child {
transform: rotate(5deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(5) {
transform: rotate(-6deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:nth-child(2),
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:nth-child(4) {
margin-bottom: 15px;
transform: rotate(0deg);
}
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:nth-child(3) {
margin-bottom: 25px;
}
.opponents-row .opponent-area:first-child:nth-last-child(5) ~ .opponent-area:last-child {
transform: rotate(6deg);
}
.game-header {
display: flex;
flex-direction: column;
text-align: center;
gap: 3px;
}
.header-col-right {
justify-content: center;
}
#game-logout-btn,
#leave-game-btn {
padding: 3px 6px;
font-size: 0.7rem;
}
.game-username {
font-size: 0.7rem;
max-width: 60px;
}
.table-center {
padding: 10px 15px;
}
.player-row {
flex-direction: column;
gap: 10px;
}
}
/* Suit symbols */
.suit-hearts::after { content: "♥"; }
.suit-diamonds::after { content: "♦"; }
.suit-clubs::after { content: "♣"; }
.suit-spades::after { content: "♠"; }
/* ============================================
New Card System - Persistent Card Elements
============================================ */
/* Card Layer - container for all persistent cards */
#card-layer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 500;
perspective: 1000px;
}
#card-layer .real-card {
pointer-events: auto;
}
/* Card Slot - only used when USE_NEW_CARD_SYSTEM is true */
.card-slot {
display: none;
}
/* Real Card - persistent card element with 3D flip */
.real-card {
position: fixed;
border-radius: 6px;
z-index: 501;
cursor: pointer;
transition: box-shadow 0.3s ease-out, opacity 0.3s ease-out;
perspective: 800px;
}
.real-card:hover {
z-index: 510;
}
.real-card .card-inner {
position: relative;
width: 100%;
height: 100%;
border-radius: 6px;
transform-style: preserve-3d;
/* transition removed - anime.js handles all flip animations */
}
.real-card .card-inner.flipped {
transform: rotateY(180deg);
}
.real-card .card-face {
position: absolute;
width: 100%;
height: 100%;
border-radius: 6px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-weight: bold;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
backface-visibility: hidden;
}
/* Card Front */
.real-card .card-face-front {
background: linear-gradient(145deg, #fff 0%, #f5f5f5 100%);
border: 2px solid #ddd;
color: #333;
font-size: clamp(1.8rem, 2.2vw, 2.8rem);
line-height: 0.95;
}
.real-card .card-face-front.red {
color: #c0392b;
}
.real-card .card-face-front.black {
color: #2c3e50;
}
.real-card .card-face-front.joker {
color: #9b59b6;
}
.real-card .card-face-front .joker-icon {
font-size: 1.5em;
line-height: 1;
}
.real-card .card-face-front .joker-label {
font-size: 0.4em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Card Back */
.real-card .card-face-back {
background-color: #c41e3a;
background-image:
linear-gradient(45deg, rgba(255,255,255,0.25) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255,255,255,0.25) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.25) 75%),
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.25) 75%);
background-size: 8px 8px;
background-position: 0 0, 0 4px, 4px -4px, -4px 0;
border: 3px solid #8b1528;
color: rgba(255, 255, 255, 0.5);
font-size: clamp(1.8rem, 2.5vw, 3rem);
transform: rotateY(180deg);
}
/* Card States */
.real-card.moving,
.real-card.anim-card.moving {
z-index: 600;
/* transition removed - anime.js handles animations */
}
/* Animation card - temporary cards used for animations */
.real-card.anim-card {
z-index: 700;
pointer-events: none;
}
.real-card.anim-card .card-inner {
/* transition removed - anime.js handles all flip animations */
}
.real-card.holding {
z-index: 550;
box-shadow: 0 0 20px rgba(244, 164, 96, 0.6),
0 4px 15px rgba(0, 0, 0, 0.4);
}
.real-card.clickable {
box-shadow: 0 0 0 2px rgba(244, 164, 96, 0.5);
}
.real-card.clickable:hover {
box-shadow: 0 0 0 3px #f4a460,
0 4px 12px rgba(0, 0, 0, 0.3);
}
/* Disable hover effects when not player's turn */
#game-screen.not-my-turn .real-card {
cursor: default;
}
#game-screen.not-my-turn .real-card:hover {
transform: none;
}
}
.real-card.selected {
box-shadow: 0 0 0 4px #fff, 0 0 15px 5px #f4a460;
z-index: 520;
}
.real-card.drawing {
z-index: 590;
}
/* Deck card styling in new system */
#deck.new-system {
cursor: pointer;
position: relative;
}
/* Discard styling for new system */
#discard.new-system.holding {
box-shadow: none;
background: rgba(255, 255, 255, 0.1);
border: 2px dashed rgba(244, 164, 96, 0.5);
}
/* Modal */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 200;
}
.modal.hidden {
display: none;
}
.modal-content {
background: linear-gradient(135deg, #1a472a 0%, #2d5a3d 100%);
border-radius: 12px;
padding: 15px;
max-width: 700px;
width: 95%;
max-height: 90vh;
overflow-y: auto;
}
.modal-content h3 {
text-align: center;
margin-bottom: 10px;
font-size: 1.1rem;
}
/* CPU Profile Grid */
.profiles-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin-bottom: 12px;
}
.profile-card {
background: rgba(255, 255, 255, 0.1);
border: 2px solid transparent;
border-radius: 6px;
padding: 8px 6px;
cursor: pointer;
transition: all 0.2s;
text-align: center;
position: relative;
}
.profile-card:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(244, 164, 96, 0.5);
}
.profile-card.unavailable {
opacity: 0.4;
cursor: not-allowed;
}
.profile-card.unavailable:hover {
background: rgba(255, 255, 255, 0.1);
border-color: transparent;
}
.profile-card.selected {
background: rgba(244, 164, 96, 0.3);
border-color: #f4a460;
box-shadow: 0 0 8px rgba(244, 164, 96, 0.5);
}
.profile-card.selected:hover {
background: rgba(244, 164, 96, 0.4);
}
.profile-checkbox {
position: absolute;
top: 6px;
right: 6px;
width: 20px;
height: 20px;
border: 2px solid rgba(255,255,255,0.4);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: bold;
color: #1a472a;
background: rgba(255,255,255,0.1);
}
.profile-card.selected .profile-checkbox {
background: #f4a460;
border-color: #f4a460;
}
.profile-avatar {
width: 50px;
height: 50px;
margin: 0 auto 8px;
border-radius: 50%;
background: rgba(255,255,255,0.15);
overflow: hidden;
}
.profile-avatar svg {
width: 100%;
height: 100%;
}
.profile-card .profile-name {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 4px;
}
.profile-card .profile-style {
font-size: 0.85rem;
opacity: 0.8;
font-style: italic;
}
.profile-card .profile-in-game {
font-size: 0.75rem;
color: #f4a460;
margin-top: 4px;
}
.modal-buttons {
display: flex;
gap: 10px;
justify-content: center;
}
.modal-buttons .btn {
min-width: 100px;
}
/* Advanced Options Section */
.advanced-options-section {
background: rgba(0, 0, 0, 0.15);
border-radius: 8px;
margin: 15px 0;
overflow: hidden;
}
.advanced-options-section summary {
padding: 12px 15px;
cursor: pointer;
font-weight: 600;
font-size: 0.95rem;
background: rgba(0, 0, 0, 0.2);
list-style: none;
display: flex;
align-items: center;
gap: 8px;
}
.advanced-options-section summary::-webkit-details-marker {
display: none;
}
.advanced-options-section summary::before {
content: "▸";
font-size: 0.8rem;
transition: transform 0.2s;
}
.advanced-options-section[open] summary::before {
transform: rotate(90deg);
}
.advanced-options-section summary:hover {
background: rgba(0, 0, 0, 0.3);
}
/* Two-column grid for options */
.advanced-options-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
}
.options-column {
padding: 8px 12px;
}
.options-column:first-child {
border-right: 1px solid rgba(255, 255, 255, 0.1);
}
.options-category {
padding: 8px 0;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.options-category:first-child {
border-top: none;
}
.options-category h4 {
font-size: 0.8rem;
margin-bottom: 8px;
opacity: 0.9;
color: #f4a460;
}
.options-category .checkbox-group {
gap: 4px;
}
.options-category .checkbox-label {
font-size: 0.95rem;
font-weight: 500;
padding: 3px 0;
flex-wrap: wrap;
}
/* Inline checkbox labels - description on same line */
.checkbox-label.inline {
flex-wrap: nowrap;
}
.checkbox-label.inline .rule-desc {
width: auto;
margin-left: 0;
}
/* Suit separators for rule descriptions */
.suit {
font-size: 0.95em;
margin-right: 6px;
}
.suit-red { color: #e74c3c; }
.suit-black { color: #888; }
/* Combo note for stacking rules */
.combo-note {
font-size: 0.8rem;
color: #ffd700;
background: rgba(255, 215, 0, 0.1);
border-left: 2px solid #ffd700;
padding: 4px 8px;
margin: 4px 0 8px 0;
border-radius: 0 4px 4px 0;
}
.combo-note.hidden {
display: none;
}
/* Rule description */
.rule-desc {
width: 100%;
font-size: 0.8rem;
opacity: 0.6;
margin-left: 30px;
margin-top: 1px;
}
/* Compact form group for options */
.options-category .form-group.compact {
margin: 0;
}
.options-category .form-group.compact select {
width: 100%;
font-size: 0.75rem;
padding: 5px 6px;
}
/* Eagle Eye option under joker dropdown */
.eagle-eye-option {
margin-top: 6px;
}
/* Disabled checkbox styling */
.checkbox-label:has(input:disabled) {
opacity: 0.5;
cursor: not-allowed;
}
.checkbox-label input:disabled {
cursor: not-allowed;
}
/* Mobile: stack columns */
@media (max-width: 500px) {
.advanced-options-grid {
grid-template-columns: 1fr;
}
.options-column:first-child {
border-right: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
}
/* Final Results Modal */
.final-results-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 300;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.final-results-content {
background: linear-gradient(145deg, #1a472a 0%, #0d3320 100%);
border-radius: 20px;
padding: 30px 40px;
max-width: 550px;
width: 90%;
text-align: center;
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.6),
0 0 80px rgba(244, 164, 96, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
border: 2px solid rgba(244, 164, 96, 0.3);
animation: modalSlideIn 0.4s ease;
}
@keyframes modalSlideIn {
from {
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.final-results-content h2 {
font-size: 2rem;
margin-bottom: 20px;
color: #f4a460;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.double-victory-banner {
background: linear-gradient(135deg, #ffd700 0%, #f4a460 50%, #ffd700 100%);
color: #1a472a;
padding: 12px 20px;
border-radius: 10px;
font-size: 1.2rem;
font-weight: 700;
margin-bottom: 20px;
animation: victoryPulse 1s ease-in-out infinite alternate;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
}
.final-rankings {
display: flex;
gap: 20px;
margin-bottom: 25px;
}
.final-ranking-section {
flex: 1;
background: rgba(0, 0, 0, 0.25);
border-radius: 12px;
padding: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.final-ranking-section h3 {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.final-rank-row {
display: flex;
align-items: center;
padding: 8px 10px;
margin-bottom: 4px;
border-radius: 6px;
font-size: 1rem;
transition: background 0.2s;
}
.final-rank-row:nth-child(2) {
background: linear-gradient(90deg, rgba(244, 164, 96, 0.3) 0%, rgba(244, 164, 96, 0.1) 100%);
font-weight: 600;
color: #f4a460;
}
.final-rank-row:nth-child(3) {
background: rgba(192, 192, 192, 0.15);
}
.final-rank-row:nth-child(4) {
background: rgba(205, 127, 50, 0.12);
}
.final-rank-row .rank-pos {
width: 28px;
font-weight: 700;
font-size: 1.1rem;
}
.final-rank-row .rank-name {
flex: 1;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.final-rank-row .rank-val {
font-weight: 600;
font-size: 0.95rem;
opacity: 0.9;
}
.final-actions {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
}
.final-actions .btn {
min-width: 140px;
padding: 14px 24px;
font-size: 1rem;
}
.final-actions .btn-primary {
box-shadow: 0 4px 15px rgba(244, 164, 96, 0.4);
}
/* Mobile adjustments for final results modal */
@media (max-width: 500px) {
.final-results-content {
padding: 20px 25px;
}
.final-results-content h2 {
font-size: 1.5rem;
}
.final-rankings {
flex-direction: column;
gap: 15px;
}
.final-rank-row {
font-size: 0.9rem;
padding: 6px 8px;
}
.final-actions .btn {
min-width: 120px;
padding: 12px 20px;
}
}
/* ===========================================
RULES SCREEN
=========================================== */
#rules-screen {
max-width: 800px;
margin: 0 auto;
padding: 10px 20px;
width: auto;
margin-left: auto;
margin-right: auto;
}
#rules-screen.active {
display: block;
}
#rules-screen h1 {
margin-top: 0;
}
.rules-container {
position: relative;
background: rgba(0, 0, 0, 0.3);
border-radius: 12px;
padding: 20px 35px;
border: 1px solid rgba(255, 255, 255, 0.1);
margin-top: 0;
scroll-behavior: smooth;
}
/* Rules back button */
.rules-back-btn {
padding: 4px 12px;
font-size: 0.8rem;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.3);
color: rgba(255, 255, 255, 0.7);
margin-bottom: 15px;
}
.rules-back-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-color: rgba(255, 255, 255, 0.5);
}
.golfer-logo {
display: inline-block;
transform: scaleX(-1);
}
/* Rules header */
.rules-header {
text-align: center;
margin-bottom: 25px;
padding-bottom: 20px;
border-bottom: 2px solid rgba(244, 164, 96, 0.3);
}
.rules-header h1 {
color: #f4a460;
font-size: 2rem;
margin-bottom: 8px;
}
.rules-subtitle {
color: rgba(255, 255, 255, 0.7);
font-size: 1rem;
margin: 0;
}
/* Table of Contents */
.rules-toc {
background: linear-gradient(135deg, rgba(244, 164, 96, 0.15) 0%, rgba(244, 164, 96, 0.05) 100%);
border: 1px solid rgba(244, 164, 96, 0.3);
border-radius: 10px;
padding: 18px 22px;
margin-bottom: 30px;
}
.toc-title {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 1.5px;
color: rgba(244, 164, 96, 0.9);
margin-bottom: 14px;
font-weight: 600;
}
.toc-links {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.toc-link {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 20px;
color: rgba(255, 255, 255, 0.85);
text-decoration: none;
font-size: 0.9rem;
transition: all 0.2s ease;
}
.toc-link:hover {
background: rgba(244, 164, 96, 0.25);
border-color: rgba(244, 164, 96, 0.4);
color: #fff;
transform: translateY(-1px);
}
.toc-icon {
font-size: 1rem;
}
.toc-text {
font-weight: 500;
}
.rules-section {
margin-bottom: 35px;
padding-bottom: 25px;
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
scroll-margin-top: 20px;
}
.rules-section:last-child {
border-bottom: none;
margin-bottom: 0;
}
.rules-section h2 {
color: #f4a460;
font-size: 1.4rem;
margin-bottom: 15px;
padding-bottom: 8px;
border-bottom: 2px solid rgba(244, 164, 96, 0.3);
}
.rules-section h3 {
color: #e8d8c8;
font-size: 1.1rem;
margin: 20px 0 10px 0;
}
.rules-section h4 {
color: #d4c4b4;
font-size: 1rem;
margin: 15px 0 8px 0;
}
.rules-section p {
line-height: 1.7;
margin-bottom: 12px;
color: rgba(255, 255, 255, 0.9);
}
.rules-section ul {
margin-left: 20px;
margin-bottom: 15px;
}
.rules-section li {
line-height: 1.7;
margin-bottom: 8px;
color: rgba(255, 255, 255, 0.85);
}
/* Rules table */
.rules-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
overflow: hidden;
}
.rules-table th,
.rules-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.rules-table th {
background: rgba(244, 164, 96, 0.2);
color: #f4a460;
font-weight: 600;
}
.rules-table tr:last-child td {
border-bottom: none;
}
.value-negative {
color: #4ade80;
font-weight: 700;
}
.value-low {
color: #86efac;
}
.value-zero {
color: #fbbf24;
}
.value-high {
color: #f87171;
font-weight: 600;
}
/* Rules example box */
.rules-example {
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 8px;
padding: 15px 20px;
margin: 15px 0;
}
.rules-example h4 {
margin-top: 0;
color: #f4a460;
}
.rules-example pre {
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.5;
color: rgba(255, 255, 255, 0.9);
white-space: pre-wrap;
margin: 0;
}
.rules-warning {
background: rgba(239, 68, 68, 0.15);
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 6px;
padding: 12px 15px;
color: #fca5a5;
}
/* Rules case boxes */
.rules-case {
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 15px;
margin: 15px 0;
border-left: 3px solid rgba(244, 164, 96, 0.5);
}
.rules-case h4 {
margin-top: 0;
}
/* Flip mode boxes */
.rules-mode {
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
padding: 20px;
margin: 20px 0;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.rules-mode h3 {
margin-top: 0;
color: #f4a460;
}
.mode-summary {
background: rgba(244, 164, 96, 0.15);
border-radius: 6px;
padding: 10px 15px;
font-weight: 600;
color: #f4a460;
margin-bottom: 15px;
}
/* House rule items */
.house-rule {
background: rgba(0, 0, 0, 0.15);
border-radius: 6px;
padding: 12px 16px;
margin: 12px 0;
border-left: 3px solid rgba(244, 164, 96, 0.5);
}
.house-rule h4 {
margin: 0 0 6px 0;
color: #f4a460;
font-size: 1rem;
}
.house-rule p {
margin: 0 0 8px 0;
line-height: 1.5;
}
.house-rule p:last-child {
margin-bottom: 0;
}
.strategic-impact {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.75);
font-style: italic;
}
.combo-note {
background: rgba(244, 164, 96, 0.1);
border-radius: 4px;
padding: 10px 14px;
margin-top: 15px;
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.8);
}
/* FAQ items */
.faq-item {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 15px 20px;
margin: 15px 0;
border-left: 3px solid #3b82f6;
}
.faq-item h4 {
margin: 0 0 10px 0;
color: #93c5fd;
}
.faq-item p {
margin: 0;
color: rgba(255, 255, 255, 0.85);
}
/* Rules button in lobby */
.btn-rules {
background: rgba(244, 164, 96, 0.2);
border: 1px solid #f4a460;
color: #ffb366;
cursor: pointer;
font-size: 0.65rem;
padding: 2px 8px;
margin-left: 8px;
vertical-align: middle;
border-radius: 3px;
font-weight: 600;
transition: background 0.2s, border-color 0.2s;
}
.btn-rules:hover {
background: rgba(244, 164, 96, 0.35);
border-color: #ffb366;
color: #ffc880;
}
/* Select option styling in advanced options */
.select-option {
margin-bottom: 12px;
}
.select-option label {
display: block;
margin-bottom: 5px;
color: #f4a460;
font-size: 0.9rem;
}
.select-option select {
width: 100%;
padding: 8px 10px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
color: white;
font-size: 0.85rem;
}
.select-option .rule-desc {
display: block;
margin-top: 4px;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.5);
}
/* Mobile adjustments for rules */
@media (max-width: 600px) {
.rules-container {
padding: 15px;
}
.rules-header h1 {
font-size: 1.5rem;
}
.rules-subtitle {
font-size: 0.9rem;
}
.rules-toc {
padding: 14px 16px;
}
.toc-title {
font-size: 0.75rem;
}
.toc-links {
gap: 8px;
}
.toc-link {
padding: 6px 10px;
font-size: 0.8rem;
}
.toc-icon {
font-size: 0.9rem;
}
.rules-section h2 {
font-size: 1.2rem;
}
.rules-table th,
.rules-table td {
padding: 8px 10px;
font-size: 0.9rem;
}
.rules-example pre {
font-size: 0.8rem;
}
}
/* ===========================================
AUTH COMPONENTS
=========================================== */
/* Auth bar (top right when logged in) */
.auth-bar {
position: fixed;
top: 10px;
right: 15px;
display: flex;
align-items: center;
gap: 10px;
background: rgba(0, 0, 0, 0.4);
padding: 6px 12px;
border-radius: 20px;
font-size: 0.85rem;
z-index: 100;
}
.auth-bar.hidden {
display: none;
}
/* Hide global auth-bar when game screen is active */
#app:has(#game-screen.active) > .auth-bar {
display: none !important;
}
#auth-username {
color: #f4a460;
font-weight: 500;
}
/* Username in game header */
.game-username {
color: #f4a460;
font-weight: 500;
font-size: 0.75rem;
max-width: 80px;
overflow: hidden;
text-overflow: ellipsis;
}
.game-username.hidden {
display: none;
}
/* Auth prompt in lobby (shown when not logged in) */
.auth-prompt {
text-align: center;
margin: 20px 0;
padding: 20px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.05);
}
.auth-prompt p {
margin-bottom: 15px;
color: #ccc;
font-size: 1.1em;
}
.auth-prompt .button-group {
display: flex;
justify-content: center;
gap: 12px;
}
.auth-prompt.hidden {
display: none;
}
/* Auth modal */
.modal-auth {
max-width: 320px;
padding: 25px;
}
.modal-auth h3 {
text-align: center;
margin-bottom: 20px;
color: #f4a460;
font-size: 1.3rem;
}
.modal-auth .form-group {
margin-bottom: 15px;
}
.modal-auth input {
width: 100%;
padding: 12px 15px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: white;
font-size: 1rem;
}
.modal-auth input::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.modal-auth input:focus {
outline: none;
border-color: #f4a460;
}
.btn-full {
width: 100%;
}
.auth-switch {
text-align: center;
margin-top: 15px;
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.6);
}
.auth-switch a {
color: #f4a460;
text-decoration: none;
}
.auth-switch a:hover {
text-decoration: underline;
}
.modal-close-btn {
position: absolute;
top: 10px;
right: 12px;
background: none;
border: none;
color: rgba(255, 255, 255, 0.5);
font-size: 1.5rem;
cursor: pointer;
line-height: 1;
padding: 0;
}
.modal-close-btn:hover {
color: white;
}
.modal-auth .error {
color: #f87171;
font-size: 0.85rem;
margin: 10px 0;
text-align: center;
}
.modal-auth .success {
color: #4ade80;
font-size: 0.85rem;
margin: 10px 0;
text-align: center;
}
.modal-auth .auth-hint {
color: #94a3b8;
font-size: 0.85rem;
margin-bottom: 15px;
text-align: center;
}
/* ===========================================
MATCHMAKING SCREEN
=========================================== */
#matchmaking-screen {
text-align: center;
padding: 40px 20px;
}
#matchmaking-screen h2 {
color: #f4a460;
margin-bottom: 20px;
}
.matchmaking-spinner {
width: 48px;
height: 48px;
border: 4px solid rgba(255, 255, 255, 0.1);
border-top-color: #f4a460;
border-radius: 50%;
margin: 20px auto;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.matchmaking-timer {
font-size: 2em;
font-weight: bold;
color: #fff;
margin: 15px 0;
font-variant-numeric: tabular-nums;
}
.matchmaking-info {
color: rgba(255, 255, 255, 0.6);
font-size: 0.9em;
margin: 10px 0 20px;
}
/* ===========================================
LEADERBOARD COMPONENTS
=========================================== */
/* Leaderboard button in lobby */
.leaderboard-btn {
background: rgba(244, 164, 96, 0.2);
border: 1px solid #f4a460;
color: #ffb366;
cursor: pointer;
font-size: 0.65rem;
padding: 2px 8px;
margin-left: 8px;
vertical-align: middle;
border-radius: 3px;
font-weight: 600;
transition: background 0.2s, border-color 0.2s;
}
.leaderboard-btn:hover {
background: rgba(244, 164, 96, 0.35);
border-color: #ffb366;
color: #ffc880;
}
/* Leaderboard Screen */
#leaderboard-screen {
max-width: 800px;
margin: 0 auto;
padding: 10px 20px;
}
.leaderboard-container {
background: rgba(0, 0, 0, 0.3);
border-radius: 12px;
padding: 20px 25px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.leaderboard-header {
text-align: center;
margin-bottom: 20px;
}
.leaderboard-header h1 {
color: #f4a460;
font-size: 1.8rem;
margin-bottom: 5px;
}
.leaderboard-subtitle {
color: rgba(255, 255, 255, 0.6);
font-size: 0.9rem;
margin: 0;
}
/* Leaderboard back button */
.leaderboard-back-btn {
padding: 4px 12px;
font-size: 0.8rem;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.3);
color: rgba(255, 255, 255, 0.7);
margin-bottom: 15px;
}
.leaderboard-back-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-color: rgba(255, 255, 255, 0.5);
}
/* Metric tabs */
.leaderboard-tabs {
display: flex;
gap: 8px;
margin-bottom: 20px;
flex-wrap: wrap;
justify-content: center;
}
.leaderboard-tab {
padding: 10px 18px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 0.9rem;
font-weight: 500;
transition: all 0.2s;
}
.leaderboard-tab:hover {
background: rgba(244, 164, 96, 0.15);
border-color: rgba(244, 164, 96, 0.3);
color: #fff;
}
.leaderboard-tab.active {
background: rgba(244, 164, 96, 0.25);
border-color: #f4a460;
color: #f4a460;
font-weight: 600;
}
/* Leaderboard table */
.leaderboard-table {
width: 100%;
border-collapse: collapse;
}
.leaderboard-table th,
.leaderboard-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.leaderboard-table th {
background: rgba(244, 164, 96, 0.15);
color: #f4a460;
font-weight: 600;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.leaderboard-table tbody tr:hover {
background: rgba(255, 255, 255, 0.05);
}
.leaderboard-table .rank-col {
width: 50px;
text-align: center;
font-weight: 700;
font-size: 1rem;
}
.leaderboard-table .rank-col .medal {
font-size: 1.2rem;
}
.leaderboard-table .username-col {
font-weight: 500;
}
.leaderboard-table .value-col {
text-align: right;
font-weight: 600;
color: #f4a460;
}
.leaderboard-table .games-col {
text-align: right;
color: rgba(255, 255, 255, 0.6);
font-size: 0.85rem;
}
/* Player profile link */
.player-link {
color: inherit;
text-decoration: none;
cursor: pointer;
}
.player-link:hover {
color: #f4a460;
text-decoration: underline;
}
/* Empty state */
.leaderboard-empty {
text-align: center;
padding: 40px 20px;
color: rgba(255, 255, 255, 0.5);
}
.leaderboard-empty p {
margin-bottom: 10px;
}
/* Loading state */
.leaderboard-loading {
text-align: center;
padding: 40px 20px;
color: rgba(255, 255, 255, 0.6);
}
.leaderboard-loading::after {
content: '';
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid rgba(244, 164, 96, 0.3);
border-top-color: #f4a460;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-left: 10px;
vertical-align: middle;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Player Stats Modal */
.player-stats-modal .modal-content {
max-width: 450px;
}
.player-stats-header {
text-align: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.player-stats-header h3 {
color: #f4a460;
margin: 0 0 5px 0;
}
.player-stats-header .rank-badge {
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.6);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
margin-bottom: 20px;
}
.stat-item {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 12px;
text-align: center;
}
.stat-value {
font-size: 1.4rem;
font-weight: 700;
color: #f4a460;
margin-bottom: 4px;
}
.stat-label {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.6);
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Achievements section in player stats */
.achievements-section {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.achievements-section h4 {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.achievements-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.achievement-badge {
display: flex;
align-items: center;
gap: 6px;
background: rgba(244, 164, 96, 0.15);
border: 1px solid rgba(244, 164, 96, 0.3);
border-radius: 20px;
padding: 6px 12px;
font-size: 0.85rem;
}
.achievement-badge .icon {
font-size: 1rem;
}
.achievement-badge .name {
color: #f4a460;
font-weight: 500;
}
.achievement-badge.locked {
background: rgba(0, 0, 0, 0.2);
border-color: rgba(255, 255, 255, 0.1);
opacity: 0.5;
}
.achievement-badge.locked .icon {
filter: grayscale(1);
}
.achievement-badge.locked .name {
color: rgba(255, 255, 255, 0.5);
}
/* My stats badge in leaderboard */
.my-row {
background: rgba(244, 164, 96, 0.1) !important;
border-left: 3px solid #f4a460;
}
/* Mobile adjustments */
@media (max-width: 600px) {
#leaderboard-screen {
padding: 10px;
}
.leaderboard-container {
padding: 15px;
}
.leaderboard-tabs {
gap: 6px;
}
.leaderboard-tab {
padding: 8px 14px;
font-size: 0.85rem;
}
.leaderboard-table th,
.leaderboard-table td {
padding: 10px 8px;
font-size: 0.9rem;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.stat-item {
padding: 10px 8px;
}
.stat-value {
font-size: 1.2rem;
}
}
/* ===========================================
REPLAY VIEWER
=========================================== */
#replay-screen {
max-width: 900px;
margin: 0 auto;
padding: 15px 20px;
}
.replay-header {
text-align: center;
margin-bottom: 20px;
}
#replay-title {
color: #f4a460;
font-size: 1.5rem;
margin-bottom: 8px;
}
.replay-meta {
color: rgba(255, 255, 255, 0.7);
font-size: 0.9rem;
}
.replay-meta span {
display: inline-block;
}
/* Replay Board */
.replay-board-container {
background: rgba(0, 0, 0, 0.3);
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
min-height: 300px;
}
.replay-players {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
margin-bottom: 20px;
}
.replay-player {
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
padding: 12px;
min-width: 180px;
border: 2px solid transparent;
transition: border-color 0.2s;
}
.replay-player.is-current {
border-color: #f4a460;
box-shadow: 0 0 15px rgba(244, 164, 96, 0.3);
}
.replay-player-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.replay-player-name {
font-weight: 600;
color: #fff;
}
.replay-player-score {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.6);
}
.replay-cards-grid {
display: flex;
flex-direction: column;
gap: 4px;
}
.replay-cards-row {
display: flex;
gap: 4px;
justify-content: center;
}
/* Replay cards - smaller version */
.replay-board-container .card {
width: 45px;
height: 63px;
font-size: 0.9rem;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-weight: bold;
}
.replay-board-container .card-back {
background-color: #c41e3a;
background-image:
linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255,255,255,0.15) 25%, transparent 25%);
background-size: 6px 6px;
border: 2px solid #8b1528;
}
.replay-board-container .card-red {
background: linear-gradient(145deg, #fff 0%, #f5f5f5 100%);
border: 1px solid #ddd;
color: #c0392b;
}
.replay-board-container .card-black {
background: linear-gradient(145deg, #fff 0%, #f5f5f5 100%);
border: 1px solid #ddd;
color: #2c3e50;
}
.replay-board-container .card-empty {
background: rgba(255, 255, 255, 0.1);
border: 1px dashed rgba(255, 255, 255, 0.2);
}
/* Replay center area */
.replay-center {
display: flex;
justify-content: center;
align-items: center;
gap: 30px;
padding: 20px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
margin-bottom: 15px;
}
.replay-deck .card,
.replay-discard .card {
width: 55px;
height: 77px;
}
.replay-deck .deck-count {
position: absolute;
bottom: -20px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.6);
}
.replay-deck {
position: relative;
}
.replay-drawn {
display: flex;
align-items: center;
gap: 8px;
}
.drawn-label {
font-size: 0.8rem;
color: #f4a460;
}
/* Replay info */
.replay-info {
display: flex;
justify-content: center;
gap: 20px;
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.6);
}
/* Event description */
.event-description {
text-align: center;
padding: 12px;
margin-bottom: 15px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
.event-time {
font-family: monospace;
color: rgba(255, 255, 255, 0.5);
font-size: 0.85rem;
}
.event-text {
font-size: 1rem;
color: #fff;
}
/* Replay Controls */
.replay-controls {
display: flex;
align-items: center;
gap: 12px;
padding: 15px;
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
flex-wrap: wrap;
justify-content: center;
margin-bottom: 15px;
}
.replay-btn {
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: rgba(244, 164, 96, 0.2);
color: #f4a460;
cursor: pointer;
font-size: 1rem;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.replay-btn:hover {
background: rgba(244, 164, 96, 0.4);
transform: scale(1.05);
}
.replay-btn-play {
width: 50px;
height: 50px;
font-size: 1.3rem;
background: #f4a460;
color: #1a472a;
}
.replay-btn-play:hover {
background: #ffb366;
}
/* Timeline */
.timeline {
flex: 1;
min-width: 200px;
display: flex;
align-items: center;
gap: 10px;
}
.timeline-slider {
flex: 1;
height: 8px;
-webkit-appearance: none;
appearance: none;
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
cursor: pointer;
}
.timeline-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
background: #f4a460;
border-radius: 50%;
cursor: pointer;
transition: transform 0.1s;
}
.timeline-slider::-webkit-slider-thumb:hover {
transform: scale(1.2);
}
.timeline-slider::-moz-range-thumb {
width: 18px;
height: 18px;
background: #f4a460;
border-radius: 50%;
cursor: pointer;
border: none;
}
.frame-counter {
font-family: monospace;
min-width: 70px;
text-align: right;
color: rgba(255, 255, 255, 0.7);
font-size: 0.85rem;
}
/* Speed control */
.speed-control {
display: flex;
align-items: center;
gap: 8px;
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.7);
}
.speed-select {
padding: 6px 10px;
border-radius: 6px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
cursor: pointer;
}
/* Replay Actions */
.replay-actions {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
}
/* Replay Error */
.replay-error {
text-align: center;
padding: 60px 20px;
color: rgba(255, 255, 255, 0.7);
}
.replay-error p {
margin-bottom: 20px;
font-size: 1.1rem;
}
/* Share link container */
.share-link-container {
display: flex;
gap: 10px;
margin-top: 10px;
}
.share-link-container input {
flex: 1;
padding: 10px 12px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
color: #fff;
font-size: 0.9rem;
}
/* Modal actions */
.modal-actions {
display: flex;
gap: 12px;
justify-content: center;
margin-top: 20px;
}
/* Spectator badge */
.spectator-count {
position: fixed;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.7);
color: white;
padding: 8px 16px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9rem;
z-index: 100;
}
.spectator-count::before {
content: '👁';
}
/* Mobile adjustments for replay */
@media (max-width: 600px) {
#replay-screen {
padding: 10px;
}
.replay-board-container {
padding: 12px;
}
.replay-players {
gap: 12px;
}
.replay-player {
min-width: 150px;
padding: 10px;
}
.replay-board-container .card {
width: 38px;
height: 53px;
font-size: 0.75rem;
}
.replay-center {
gap: 15px;
padding: 12px;
}
.replay-controls {
padding: 10px;
gap: 8px;
}
.replay-btn {
width: 36px;
height: 36px;
font-size: 0.9rem;
}
.replay-btn-play {
width: 44px;
height: 44px;
font-size: 1.1rem;
}
.timeline {
min-width: 150px;
}
.replay-actions {
flex-direction: column;
align-items: stretch;
}
.replay-actions .btn {
width: 100%;
}
}
/* ============================================================
V3 FEATURES
============================================================ */
/* --- V3_01: Dealer Indicator --- */
.dealer-chip {
position: absolute;
bottom: -10px;
left: -10px;
width: 38px;
height: 38px;
background: linear-gradient(145deg, #ffc078 0%, #f4a460 40%, #d4884a 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: bold;
color: #1a1a2e;
border: 3px solid rgba(255, 255, 255, 0.9);
box-shadow:
0 4px 8px rgba(0, 0, 0, 0.4),
0 2px 4px rgba(0, 0, 0, 0.3),
inset 0 2px 4px rgba(255, 255, 255, 0.4),
inset 0 -2px 4px rgba(0, 0, 0, 0.2);
z-index: 10;
pointer-events: none;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.3);
}
/* --- V3_03: Round End Reveal --- */
.reveal-prompt {
position: fixed;
top: 20%;
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, #f4a460 0%, #d4845a 100%);
color: white;
padding: 15px 30px;
border-radius: 12px;
text-align: center;
z-index: 200;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
animation: prompt-entrance 0.3s ease-out;
}
.reveal-prompt.fading {
animation: prompt-fade 0.3s ease-out forwards;
}
@keyframes prompt-entrance {
0% { transform: translateX(-50%) translateY(-20px); opacity: 0; }
100% { transform: translateX(-50%) translateY(0); opacity: 1; }
}
@keyframes prompt-fade {
0% { opacity: 1; }
100% { opacity: 0; }
}
.reveal-prompt-text {
font-size: 1.1em;
margin-bottom: 8px;
}
.reveal-prompt-countdown {
font-size: 2em;
font-weight: bold;
}
/* Cards clickable during voluntary reveal */
.player-area.voluntary-flip .card.can-flip {
cursor: pointer;
}
/* Player area highlight during reveal */
.player-area.revealing,
.opponent-area.revealing {
box-shadow: 0 0 10px 5px rgba(244, 164, 96, 0.3);
transition: box-shadow 0.2s ease-out;
}
/* --- V3_05: Final Turn Urgency --- */
.final-turn-icon {
font-size: 1.1em;
}
.final-turn-remaining {
font-size: 0.85em;
opacity: 0.9;
}
/* Game area border pulse during final turn */
#game-screen.final-turn-active {
animation: game-area-urgency 2s ease-in-out infinite;
}
@keyframes game-area-urgency {
0%, 100% {
box-shadow: inset 0 0 0 0 rgba(255, 107, 53, 0);
}
50% {
box-shadow: inset 0 0 30px 0 rgba(255, 107, 53, 0.12);
}
}
/* Knocker highlight */
.player-area.is-knocker,
.opponent-area.is-knocker {
border: 2px solid #ff6b35;
border-radius: 8px;
}
.knocker-badge {
position: absolute;
top: -10px;
right: -10px;
background: #ff6b35;
color: white;
padding: 2px 8px;
border-radius: 10px;
font-size: 0.7em;
font-weight: bold;
z-index: 10;
pointer-events: none;
}
/* --- V3_08: Card Hover Selection Preview --- */
.player-area.can-swap .card {
cursor: pointer;
}
/* Swap card hover state - anime.js handles transform, CSS handles box-shadow only */
@media (hover: hover) {
.player-area.can-swap .card:hover {
/* Transform handled by anime.js cardHover methods */
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
}
.player-area.can-swap .card.card-back:hover {
box-shadow: 0 8px 20px rgba(244, 164, 96, 0.4);
}
}
/* --- V3_09: Knock Early Drama --- */
.knock-confirm-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 300;
animation: modal-fade-in 0.2s ease-out;
}
@keyframes modal-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
.knock-confirm-content {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
padding: 30px;
border-radius: 15px;
text-align: center;
max-width: 320px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
animation: modal-scale-in 0.2s ease-out;
}
@keyframes modal-scale-in {
0% { transform: scale(0.9); }
100% { transform: scale(1); }
}
.knock-confirm-icon {
font-size: 3em;
margin-bottom: 10px;
}
.knock-confirm-content h3 {
margin: 0 0 15px;
color: #f4a460;
}
.knock-confirm-content p {
margin: 0 0 10px;
color: rgba(255, 255, 255, 0.8);
}
.knock-warning {
color: #e74c3c !important;
font-size: 0.9em;
}
.knock-confirm-buttons {
display: flex;
gap: 10px;
margin-top: 20px;
}
.knock-confirm-buttons .btn {
flex: 1;
}
/* V3_17: Knock status message - golden gradient with pulsing glow */
.status-message.knock {
background: linear-gradient(135deg, #f4a460 0%, #e67e22 50%, #d4750e 100%);
color: #1a1a2e;
font-size: 1.3em;
font-weight: 900;
letter-spacing: 0.08em;
text-transform: uppercase;
animation: knock-pulse 0.6s ease-in-out 3;
box-shadow: 0 0 15px rgba(244, 164, 96, 0.5);
}
@keyframes knock-pulse {
0%, 100% { box-shadow: 0 0 15px rgba(244, 164, 96, 0.5); }
50% { box-shadow: 0 0 25px rgba(244, 164, 96, 0.8), 0 0 40px rgba(230, 126, 34, 0.3); }
}
@keyframes screen-shake {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-3px); }
40% { transform: translateX(3px); }
60% { transform: translateX(-2px); }
80% { transform: translateX(2px); }
}
body.screen-shake {
animation: screen-shake 0.3s ease-out;
}
/* opponent-knock-banner removed in V3_17 - knock uses status bar now */
/* --- V3_07: Score Tallying Animation --- */
.card-value-overlay {
position: fixed;
transform: translate(-50%, -50%) scale(0.5);
background: rgba(20, 20, 36, 0.95);
color: #fff;
padding: 6px 12px;
border-radius: 6px;
font-size: 1.3em;
font-weight: 800;
letter-spacing: 0.02em;
opacity: 0;
transition: transform 0.15s ease-out, opacity 0.12s ease-out;
z-index: 200;
pointer-events: none;
border: 2px solid rgba(255, 255, 255, 0.25);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
.card-value-overlay.visible {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
.card-value-overlay.negative {
background: linear-gradient(135deg, #1b944f 0%, #166b3a 100%);
border-color: rgba(39, 174, 96, 0.5);
}
.card-value-overlay.zero {
background: linear-gradient(135deg, #8b6914 0%, #6b5010 100%);
border-color: rgba(180, 140, 40, 0.5);
color: #f5e6b8;
}
.card.tallying {
box-shadow: 0 0 15px rgba(244, 164, 96, 0.6) !important;
transform: scale(1.05);
/* No CSS transition - tallying effect handled by JS */
}
.pair-cancel-overlay {
position: fixed;
transform: translate(-50%, -50%);
font-size: 1.1em;
font-weight: 800;
color: #ffe082;
background: rgba(20, 20, 36, 0.92);
padding: 5px 12px;
border-radius: 6px;
border: 2px solid rgba(255, 215, 0, 0.4);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
animation: pair-cancel 0.5s ease-out forwards;
z-index: 200;
pointer-events: none;
}
@keyframes pair-cancel {
0% { transform: translate(-50%, -50%) scale(0.5); opacity: 0; }
25% { transform: translate(-50%, -50%) scale(1.1); opacity: 1; }
100% { transform: translate(-50%, -60%) scale(1); opacity: 0; }
}
.pair-cancel-overlay.negative {
color: #81d4fa;
border-color: rgba(100, 181, 246, 0.4);
}
/* --- V3_10: Column Pair Indicator --- */
.card.paired {
box-shadow: 0 0 8px rgba(244, 164, 96, 0.3);
}
.card.pair-top {
border-bottom: 2px solid rgba(244, 164, 96, 0.5);
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.card.pair-bottom {
border-top: 2px solid rgba(244, 164, 96, 0.5);
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.opponent-area .card.paired {
box-shadow: 0 0 5px rgba(244, 164, 96, 0.2);
}
.opponent-area .card.pair-top {
border-bottom-width: 1px;
}
.opponent-area .card.pair-bottom {
border-top-width: 1px;
}
/* --- V3_06: Opponent Thinking Indicator --- */
.thinking-indicator {
display: inline-block;
margin-right: 4px;
font-size: 0.9em;
}
.thinking-indicator.hidden {
display: none;
}
/* --- V3_15: Discard Pile History Depth --- */
#discard[data-depth="2"] {
box-shadow:
2px 2px 0 0 rgba(255, 255, 255, 0.08),
0 4px 12px rgba(0, 0, 0, 0.3);
}
#discard[data-depth="3"] {
box-shadow:
2px 2px 0 0 rgba(255, 255, 255, 0.08),
4px 4px 0 0 rgba(255, 255, 255, 0.04),
0 4px 12px rgba(0, 0, 0, 0.3);
}
/* --- V3_14: Active Rules Context --- */
.rule-tag.rule-highlighted {
background: rgba(244, 164, 96, 0.3);
box-shadow: 0 0 10px rgba(244, 164, 96, 0.4);
animation: rule-pulse 0.5s ease-out;
}
@keyframes rule-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.rule-message {
margin-left: 8px;
padding-left: 8px;
border-left: 1px solid rgba(255, 255, 255, 0.3);
font-weight: bold;
color: #f4a460;
animation: rule-message-in 0.3s ease-out;
}
@keyframes rule-message-in {
0% { opacity: 0; transform: translateX(-5px); }
100% { opacity: 1; transform: translateX(0); }
}
/* --- V3_13: Card Value Tooltips --- */
.card-value-tooltip {
position: fixed;
transform: translateX(-50%);
background: rgba(26, 26, 46, 0.95);
color: white;
padding: 6px 12px;
border-radius: 8px;
font-size: 0.85em;
text-align: center;
z-index: 500;
pointer-events: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
transition: opacity 0.15s;
}
.card-value-tooltip.hidden {
opacity: 0;
}
.card-value-tooltip::before {
content: '';
position: absolute;
top: -6px;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-bottom-color: rgba(26, 26, 46, 0.95);
}
.tooltip-value {
display: block;
font-size: 1.2em;
font-weight: bold;
}
.tooltip-value.negative {
color: #27ae60;
}
.tooltip-note {
display: block;
font-size: 0.85em;
color: rgba(255, 255, 255, 0.7);
margin-top: 2px;
}
/* --- V3_17: Scoresheet Modal --- */
.scoresheet-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
z-index: 300;
animation: fadeInBg 0.3s ease;
}
@keyframes fadeInBg {
from { opacity: 0; }
to { opacity: 1; }
}
.scoresheet-content {
background: linear-gradient(145deg, #1a472a 0%, #0d3320 100%);
border-radius: 16px;
padding: 24px 28px;
max-width: 520px;
width: 92%;
max-height: 85vh;
overflow-y: auto;
box-shadow:
0 16px 50px rgba(0, 0, 0, 0.6),
0 0 60px rgba(244, 164, 96, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
border: 2px solid rgba(244, 164, 96, 0.25);
animation: modalSlideIn 0.4s ease;
}
.ss-header {
text-align: center;
font-size: 1.1rem;
font-weight: 700;
color: #f4a460;
margin-bottom: 18px;
letter-spacing: 0.05em;
}
.ss-players {
display: flex;
flex-direction: column;
gap: 14px;
}
.ss-player-row {
background: rgba(0, 0, 0, 0.2);
border-radius: 10px;
padding: 12px 14px;
border: 1px solid rgba(255, 255, 255, 0.06);
}
.ss-player-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
.ss-player-name {
font-weight: 700;
font-size: 0.95rem;
color: #e8e8e8;
}
.ss-badge {
font-size: 0.65rem;
font-weight: 800;
padding: 2px 7px;
border-radius: 4px;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.ss-badge-knock {
background: linear-gradient(135deg, #f4a460 0%, #e67e22 100%);
color: #1a1a2e;
}
.ss-badge-low {
background: linear-gradient(135deg, #27ae60 0%, #1e8449 100%);
color: #fff;
}
.ss-columns {
display: flex;
gap: 8px;
justify-content: center;
margin-bottom: 6px;
}
.ss-column {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
padding: 4px 6px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.03);
}
.ss-column-paired {
background: rgba(244, 164, 96, 0.08);
border: 1px solid rgba(244, 164, 96, 0.15);
}
.ss-column-paired.ss-pair-glow {
animation: ss-pair-glow-pulse 0.5s ease-out;
}
@keyframes ss-pair-glow-pulse {
0% { box-shadow: 0 0 0 rgba(244, 164, 96, 0); }
50% { box-shadow: 0 0 12px rgba(244, 164, 96, 0.4); }
100% { box-shadow: 0 0 0 rgba(244, 164, 96, 0); }
}
.ss-mini-card {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 28px;
border-radius: 3px;
font-size: 0.72rem;
font-weight: 700;
line-height: 1;
background: #f5f0e8;
color: #1a1a2e;
border: 1px solid rgba(0, 0, 0, 0.15);
letter-spacing: -0.02em;
}
.ss-mini-card.ss-red {
color: #c0392b;
}
.ss-mini-card.ss-black {
color: #1a1a2e;
}
.ss-mini-card.ss-mini-paired {
opacity: 0.5;
text-decoration: line-through;
}
.ss-mini-card.ss-mini-back {
background: linear-gradient(135deg, #2c5f8a 0%, #1a3a5c 100%);
color: transparent;
}
.ss-col-score {
font-size: 0.7rem;
font-weight: 700;
color: rgba(255, 255, 255, 0.7);
text-align: center;
margin-top: 1px;
}
.ss-col-score.ss-pair {
color: #f4a460;
}
.ss-col-score.ss-negative {
color: #27ae60;
}
.ss-bonuses {
margin: 4px 0 2px;
text-align: center;
}
.ss-bonus {
display: inline-block;
font-size: 0.7rem;
font-weight: 800;
color: #81d4fa;
background: rgba(100, 181, 246, 0.15);
padding: 2px 8px;
border-radius: 4px;
margin: 0 4px;
}
.ss-scores {
display: flex;
justify-content: flex-end;
gap: 16px;
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.6);
margin-top: 4px;
}
.ss-scores strong {
color: #fff;
}
.ss-next-btn {
display: block;
width: 100%;
margin-top: 18px;
padding: 10px;
font-size: 0.95rem;
}
/* --- V3_11: Swap Animation --- */
.traveling-card {
position: fixed;
border-radius: 6px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
}
/* ============================================
MOBILE PORTRAIT LAYOUT
============================================
All rules scoped under body.mobile-portrait.
Triggered by JS matchMedia on narrow portrait screens.
Desktop layout is completely untouched.
============================================ */
/* Mobile bottom bar - hidden on desktop */
#mobile-bottom-bar {
display: none;
}
body.mobile-portrait {
height: var(--app-height, 100vh);
overflow: hidden;
overscroll-behavior: contain;
touch-action: manipulation;
}
body.mobile-portrait #app {
padding: 0;
height: var(--app-height, 100vh);
overflow: hidden;
}
/* --- Mobile: Game screen fills viewport --- */
/* IMPORTANT: Must include .active to avoid overriding .screen { display: none } */
body.mobile-portrait #game-screen.active {
height: var(--app-height, 100vh);
max-height: var(--app-height, 100vh);
overflow: hidden;
margin-left: 0;
width: 100%;
display: flex;
flex-direction: column;
}
body.mobile-portrait .game-layout {
flex: 1 1 0%;
flex-direction: column;
overflow: hidden;
min-height: 0;
max-height: 100%;
}
body.mobile-portrait .game-main {
flex: 1;
gap: 0;
justify-content: flex-start;
overflow: hidden;
min-height: 0;
}
/* --- Mobile: Compact header (single row) --- */
body.mobile-portrait .game-header {
display: flex;
flex-direction: row;
align-items: center;
padding: 4px 8px;
padding-top: calc(4px + env(safe-area-inset-top, 0px));
font-size: 0.75rem;
min-height: 32px;
width: 100%;
margin-left: 0;
gap: 4px;
margin-bottom: 4px;
}
body.mobile-portrait .header-col-left {
flex: 0 0 auto;
gap: 6px;
}
body.mobile-portrait .header-col-center {
flex: 1;
min-width: 0;
}
body.mobile-portrait .header-col-right {
flex: 0 0 auto;
gap: 4px;
}
/* Hide items moved to bottom bar on mobile */
body.mobile-portrait .active-rules-bar,
body.mobile-portrait .game-username,
body.mobile-portrait #game-logout-btn,
body.mobile-portrait .game-header .round-info,
body.mobile-portrait .game-header #leave-game-btn {
display: none !important;
}
body.mobile-portrait .status-message {
font-size: 1.02rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
body.mobile-portrait .round-info {
font-size: 0.75rem;
white-space: nowrap;
}
body.mobile-portrait #leave-game-btn {
padding: 2px 6px;
font-size: 0.6rem;
white-space: nowrap;
}
body.mobile-portrait .mute-btn {
font-size: 0.85rem;
padding: 2px;
}
body.mobile-portrait .final-turn-badge {
font-size: 0.6rem;
padding: 2px 6px;
white-space: nowrap;
}
/* --- Mobile: Game table — opponents pinned top, rest centered in remaining space --- */
body.mobile-portrait .game-table {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
gap: 0 !important;
flex: 1 1 0%;
overflow: hidden;
padding: 0 4px 36px;
min-height: 0;
max-height: 100%;
}
/* --- Mobile: Opponents wrap at 3 per row (max 5 opponents = 3+2) --- */
body.mobile-portrait .opponents-row {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: flex-start;
gap: 4px 10px;
min-height: 0 !important;
padding: 2px 4px 6px;
overflow: hidden;
flex-shrink: 0;
}
/* --- Mobile: Player row gets remaining space, centered vertically --- */
body.mobile-portrait .player-row {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
flex: 1 1 0%;
min-height: 0;
max-height: 100%;
overflow: hidden;
}
/* Remove all arch rotation and margin on mobile */
body.mobile-portrait .opponents-row .opponent-area {
margin-bottom: 0 !important;
transform: none !important;
}
body.mobile-portrait .opponent-area {
padding: 3px 4px 4px;
border-radius: 6px;
min-width: 0;
flex: 0 0 calc((100% - 20px) / 3);
position: relative;
overflow: visible;
}
body.mobile-portrait .opponent-area .dealer-chip {
width: 20px;
height: 20px;
font-size: 10px;
border-width: 2px;
bottom: -6px;
left: -6px;
}
body.mobile-portrait .opponent-area h4 {
font-size: 0.85rem;
margin: 0 0 2px 0;
padding: 2px 4px;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
}
body.mobile-portrait .opponent-area .card-grid {
grid-template-columns: repeat(3, 35px) !important;
gap: 2px !important;
}
body.mobile-portrait .opponent-area .card {
width: 35px !important;
height: 49px !important;
font-size: 1.05rem !important;
line-height: 1.05;
border-radius: 3px;
}
body.mobile-portrait .opponent-showing {
font-size: 0.85rem;
padding: 0px 3px;
margin-left: 3px;
}
/* --- Mobile: Deck/Discard area centered --- */
body.mobile-portrait .table-center {
padding: 5px 10px;
border-radius: 8px;
margin: auto 0;
}
body.mobile-portrait .deck-area {
gap: 10px;
align-items: flex-start;
}
body.mobile-portrait .deck-area > .card,
body.mobile-portrait #deck,
body.mobile-portrait #discard {
width: 64px !important;
height: 90px !important;
font-size: 1.4rem !important;
}
/* Held card floating should NOT be constrained to deck/discard size */
body.mobile-portrait .held-card-floating {
width: 64px !important;
height: 90px !important;
}
body.mobile-portrait .discard-stack {
gap: 6px;
}
/* Discard button - horizontal on mobile instead of vertical tab */
body.mobile-portrait #discard-btn {
position: fixed;
writing-mode: horizontal-tb;
text-orientation: initial;
padding: 8px 16px;
font-size: 0.8rem;
border-radius: 8px;
}
/* --- Mobile: Player cards — explicit sizes for reliable layout --- */
body.mobile-portrait .player-section {
width: auto;
padding: 0;
}
body.mobile-portrait .player-area {
padding: 5px 8px 9px;
border-radius: 8px;
width: auto;
display: inline-block;
}
body.mobile-portrait .player-area .dealer-chip {
width: 22px;
height: 22px;
font-size: 11px;
border-width: 2px;
bottom: auto;
top: -8px;
left: -8px;
}
body.mobile-portrait .player-area h4 {
font-size: 0.8rem;
padding: 3px 8px;
margin-bottom: 4px;
}
body.mobile-portrait .player-showing {
font-size: 0.75rem;
}
/* Player hand: fixed-size cards */
body.mobile-portrait .player-section .card-grid {
grid-template-columns: repeat(3, 64px) !important;
gap: 5px !important;
justify-content: center;
}
body.mobile-portrait .player-section .card {
width: 64px !important;
height: 90px !important;
font-size: 1.4rem !important;
}
/* Real cards: font-size is now set inline by card-manager.js (proportional to card width).
Override the desktop clamp values to inherit from the element. */
body.mobile-portrait .real-card .card-face-front,
body.mobile-portrait .real-card .card-face-back {
font-size: inherit;
line-height: 1;
}
/* --- Mobile: Side panels become bottom drawers --- */
body.mobile-portrait .side-panel {
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 100%;
max-height: 55vh;
border-radius: 16px 16px 0 0;
padding: 12px 16px;
padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
z-index: 600;
transform: translateY(100%);
transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
overflow-y: auto;
-webkit-overflow-scrolling: touch;
box-shadow: 0 -4px 30px rgba(0, 0, 0, 0.4);
}
body.mobile-portrait .side-panel.left-panel,
body.mobile-portrait .side-panel.right-panel {
left: 0;
right: 0;
}
body.mobile-portrait .side-panel.drawer-open {
transform: translateY(0);
}
/* Drawer handle */
body.mobile-portrait .side-panel::before {
content: '';
display: block;
width: 40px;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
margin: 0 auto 10px;
}
/* Drawer backdrop */
body.mobile-portrait .drawer-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 599;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}
body.mobile-portrait .drawer-backdrop.visible {
opacity: 1;
pointer-events: auto;
}
/* Score table in drawer: full width */
body.mobile-portrait .side-panel table {
width: 100%;
font-size: 0.85rem;
}
body.mobile-portrait .side-panel th,
body.mobile-portrait .side-panel td {
padding: 4px 8px;
}
/* Standings list in drawer */
body.mobile-portrait .standings-list .rank-row {
font-size: 0.85rem;
padding: 3px 0;
}
/* Game buttons in drawer */
body.mobile-portrait .game-buttons {
display: flex;
gap: 8px;
justify-content: center;
padding: 8px 0;
}
/* --- Mobile: Bottom bar --- */
body.mobile-portrait #mobile-bottom-bar {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
background: none;
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 6px 12px;
padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px));
z-index: 900;
}
/* Hole indicator — pinned left with pill background */
body.mobile-portrait #mobile-bottom-bar .mobile-round-info {
margin-right: auto;
color: rgba(255, 255, 255, 0.9);
font-size: 0.77rem;
font-weight: 700;
white-space: nowrap;
letter-spacing: 0.03em;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 20px;
padding: 4px 9px;
}
/* End Game — pinned right */
body.mobile-portrait #mobile-bottom-bar .mobile-leave-btn {
margin-left: auto;
background: rgba(180, 60, 60, 0.3) !important;
border-color: rgba(220, 80, 80, 0.4) !important;
color: rgba(255, 120, 120, 0.9) !important;
}
body.mobile-portrait #mobile-bottom-bar .mobile-leave-btn:active {
background: rgba(180, 60, 60, 0.5) !important;
}
body.mobile-portrait #mobile-bottom-bar .mobile-bar-btn {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.65);
font-size: 0.7rem;
font-weight: 600;
padding: 5px 10px;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 0.1em;
border-radius: 20px;
transition: all 0.25s ease;
position: relative;
-webkit-tap-highlight-color: transparent;
}
body.mobile-portrait #mobile-bottom-bar .mobile-bar-btn:active {
transform: scale(0.95);
}
body.mobile-portrait #mobile-bottom-bar .mobile-bar-btn.active {
color: #1a1a2e;
background: linear-gradient(135deg, #f4a460, #e8935a);
border-color: transparent;
box-shadow: 0 2px 12px rgba(244, 164, 96, 0.4);
}
/* --- Mobile: Non-game screens --- */
body.mobile-portrait #lobby-screen {
padding: 55px 12px 15px;
overflow-y: auto;
max-height: 100dvh;
}
body.mobile-portrait #waiting-screen {
padding: 10px 12px;
overflow-y: auto;
max-height: 100dvh;
}
/* --- Mobile: Very short screens (e.g. iPhone SE) --- */
@media (max-height: 600px) {
body.mobile-portrait .opponents-row {
padding: 2px 8px 0;
}
body.mobile-portrait .opponent-area .card-grid {
grid-template-columns: repeat(3, 29px) !important;
gap: 1px !important;
}
body.mobile-portrait .opponent-area .card {
width: 29px !important;
height: 40px !important;
font-size: 0.8rem !important;
line-height: 1.05;
}
body.mobile-portrait .table-center {
padding: 3px 8px;
}
body.mobile-portrait .deck-area > .card,
body.mobile-portrait #deck,
body.mobile-portrait #discard {
width: 60px !important;
height: 84px !important;
font-size: 1.3rem !important;
}
body.mobile-portrait .held-card-floating {
width: 60px !important;
height: 84px !important;
}
body.mobile-portrait .player-row {
gap: 6px;
}
body.mobile-portrait .player-area {
padding: 3px 5px;
}
body.mobile-portrait .player-section .card-grid {
grid-template-columns: repeat(3, 60px) !important;
gap: 4px !important;
}
body.mobile-portrait .player-section .card {
width: 60px !important;
height: 84px !important;
font-size: 1.3rem !important;
}
body.mobile-portrait .player-area h4 {
font-size: 0.7rem;
padding: 2px 6px;
margin-bottom: 3px;
}
}