1
0
Fork 0
polaris-task-force/src/app/(frontend)/styles.css
Z8MB1E e113a33c87 feat(story): add two-page flip book view reader
Fullscreen reader alongside Immersive view: paginated two-page spread
with no vertical scroll, chevron and arrow-key page turns, and a page
indicator. Chapters always render in reading order (Chapter 1 first).
Layout knobs (spine gap, single-column breakpoint, book max width) live
in lib/story/bookLayout with unit-tested paging math.
2026-09-24 19:23:42 -04:00

3594 lines
76 KiB
CSS

@import url("https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap");
@import "tailwindcss";
@import "tw-animate-css";
/*
---break---
*/
@custom-variant dark (&:is(.dark *));
/*
---break---
*/
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
}
/*
---break---
*/
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--font-sans: "Geist", sans-serif;
--font-mono: "Geist Mono", monospace;
}
/*
---break---
*/
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
/*
---break---
*/
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
/*
---break---
*/
/* Top-down ORBAT-style tree connectors */
.org-tree-child {
position: relative;
padding-top: 1rem;
}
.org-tree-child::before {
content: "";
position: absolute;
top: 0;
left: 50%;
height: 1rem;
border-left: 2px solid var(--color-border);
}
.org-tree-child::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
border-top: 2px solid var(--color-border);
}
.org-tree-child:first-child::after {
left: 50%;
}
.org-tree-child:last-child::after {
right: 50%;
}
.org-tree-child:only-child::after {
display: none;
}
/* Locker search-result highlight */
@keyframes locker-search-pulse {
0%,
100% {
box-shadow: 0 0 0 0 rgb(251 191 36 / 0.7);
}
50% {
box-shadow: 0 0 0 6px rgb(251 191 36 / 0);
}
}
@keyframes xp-gain {
0% {
opacity: 0;
transform: translateX(0.35rem);
}
15% {
opacity: 1;
}
75% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateX(-1.1rem);
}
}
.locker-search-highlight {
animation: locker-search-pulse 1.1s ease-in-out infinite;
box-shadow: 0 0 0 3px rgb(251 191 36 / 0.9);
}
.app-background-texture {
background-image: repeating-linear-gradient(
135deg,
rgb(255 255 255 / 0.018) 0 1px,
transparent 1px 7px
);
}
.inventory-frame {
background-color: rgb(7 10 12 / 0.78);
background-image:
linear-gradient(rgb(255 255 255 / 0.018) 1px, transparent 1px),
linear-gradient(90deg, rgb(255 255 255 / 0.018) 1px, transparent 1px),
radial-gradient(circle at 50% 0%, rgb(120 150 145 / 0.09), transparent 55%);
/*background-size:
48px 48px,
48px 48px,
100% 100%;*/
box-shadow:
inset 0 1px rgb(255 255 255 / 0.04),
0 12px 35px rgb(0 0 0 / 0.18);
}
@media (prefers-reduced-motion: reduce) {
.locker-search-highlight {
animation: none;
}
}
@property --medal-border-angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
@keyframes border-spin-animate {
from {
--medal-border-angle: 0deg;
}
to {
--medal-border-angle: 360deg;
}
}
.medal-card {
--medal-border-angle: 0deg;
background: rgb(193 161 79 / 0.28);
position: relative;
isolation: isolate;
}
.medal-card::before {
content: "";
position: absolute;
inset: -0.125rem;
z-index: 0;
border-radius: inherit;
border: 2px solid transparent;
background:
linear-gradient(var(--background), var(--background)) padding-box,
repeating-conic-gradient(
from var(--medal-border-angle),
transparent 0deg 150deg,
peru 159deg,
rgb(253 224 71) 168deg,
white 178deg,
transparent 178deg
)
border-box;
animation: border-spin-animate 10s linear infinite;
pointer-events: none;
}
@keyframes border-spin-animate {
from {
--medal-border-angle: 0deg;
}
to {
--medal-border-angle: 360deg;
}
}
.medal-card * {
position: relative;
z-index: 1;
}
.medal-card::after {
content: "";
z-index: 2;
background: radial-gradient(circle, transparent, rgb(253 224 71 / 0.1));
position: absolute;
inset: 0;
animation: pulse 5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
pointer-events: none;
}
.medal-card > .medal-card-bg,
.medal-card > .medal-card-scrim {
position: absolute !important;
inset: 0;
z-index: 0 !important;
}
.medal-card > .medal-card-bg {
width: 100%;
height: 100%;
border-radius: inherit;
}
.medal-card > .medal-card-scrim {
border-radius: inherit;
background-color: rgb(0 0 0 / 0.42);
box-shadow: inset 0 0 2.25rem rgb(0 0 0 / 0.38);
}
.medal-card.medal-card-overflow {
overflow: visible;
}
.award-xp-high {
background: linear-gradient(
110deg,
rgb(245 158 11),
rgb(254 249 195),
rgb(250 204 21),
rgb(245 158 11)
);
background-size: 200% auto;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
filter: drop-shadow(0 0 0.3rem rgb(250 204 21 / 0.35));
animation: award-xp-shimmer 2.5s linear infinite;
}
@keyframes award-xp-shimmer {
to {
background-position: 200% center;
}
}
@media (prefers-reduced-motion: reduce) {
.award-xp-high {
animation: none;
}
}
.commendation-card {
align-self: start;
gap: 0.5rem;
padding: 0.625rem 0.75rem;
}
/* Scrollbars — styled to match the dark frontend theme */
* {
scrollbar-width: thin;
scrollbar-color: color-mix(in oklch, var(--foreground) 20%, transparent) transparent;
}
*::-webkit-scrollbar {
width: 10px;
height: 10px;
}
*::-webkit-scrollbar-track {
background: transparent;
}
*::-webkit-scrollbar-thumb {
background-color: color-mix(in oklch, var(--foreground) 20%, transparent);
border-radius: 9999px;
border: 2px solid transparent;
background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
background-color: color-mix(in oklch, var(--foreground) 32%, transparent);
}
*::-webkit-scrollbar-corner {
background: transparent;
}
/* Wiki markdown — directives, footnotes, callouts (scoped to .wiki-markdown) */
.wiki-markdown .columns {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
gap: 1rem;
}
.wiki-markdown .columns.cols-2 {
grid-template-columns: repeat(2, 1fr);
}
.wiki-markdown .columns.cols-3 {
grid-template-columns: repeat(3, 1fr);
}
.wiki-markdown .column {
min-width: 0;
}
.wiki-markdown .footnotes {
font-size: 0.875rem;
color: var(--color-muted-foreground);
margin-top: 2rem;
border-top: 1px solid var(--color-border);
padding-top: 1rem;
}
.wiki-markdown .note,
.wiki-markdown .warning {
position: relative;
border-left: 3px solid var(--color-border);
border-radius: 0.375rem;
padding: 0.75rem 1rem 0.75rem 2.5rem;
}
.wiki-markdown .note > :first-child,
.wiki-markdown .warning > :first-child {
margin-top: 0;
}
.wiki-markdown .note .callout-icon {
position: absolute;
left: 0.75rem;
top: 1.125rem;
color: var(--color-primary);
}
.wiki-markdown .warning .callout-icon {
position: absolute;
left: 0.75rem;
top: 1.125rem;
color: var(--color-destructive);
}
.wiki-markdown .note {
border-left-color: var(--color-primary);
background: color-mix(in oklch, var(--color-primary) 8%, transparent);
}
.wiki-markdown .warning {
border-left-color: var(--color-destructive);
background: color-mix(in oklch, var(--color-destructive) 8%, transparent);
}
/* Lexical rich-text typography (scoped to .lexical-content); Tailwind preflight resets headings */
.lexical-content {
font-size: 0.875rem;
line-height: 1.7;
color: var(--color-muted-foreground);
}
.lexical-content > :first-child {
margin-top: 0;
}
.lexical-content > :last-child {
margin-bottom: 0;
}
.lexical-content h1,
.lexical-content h2,
.lexical-content h3,
.lexical-content h4 {
font-weight: 600;
line-height: 1.3;
color: var(--color-foreground);
}
.lexical-content h1 {
font-size: 1.25rem;
margin: 1.25rem 0 0.5rem;
}
.lexical-content h2 {
font-size: 1.125rem;
margin: 1.125rem 0 0.4375rem;
}
.lexical-content h3 {
font-size: 1rem;
margin: 1rem 0 0.375rem;
}
.lexical-content h4 {
font-size: 0.9375rem;
margin: 0.875rem 0 0.25rem;
}
.lexical-content p {
margin: 0.5rem 0;
}
.lexical-content ul,
.lexical-content ol {
margin: 0.5rem 0;
padding-left: 1.5rem;
}
.lexical-content ul {
list-style: disc;
}
.lexical-content ol {
list-style: decimal;
}
.lexical-content li + li {
margin-top: 0.25rem;
}
.lexical-content li > ul,
.lexical-content li > ol {
margin: 0.25rem 0;
}
.lexical-content blockquote {
margin: 0.75rem 0;
border-left: 3px solid var(--color-border);
padding-left: 1rem;
color: var(--color-muted-foreground);
}
.lexical-content hr {
margin: 1.25rem 0;
border: 0;
border-top: 1px solid var(--color-border);
}
.lexical-content strong {
color: var(--color-foreground);
}
.lexical-content a {
color: var(--color-primary);
text-decoration: underline;
text-underline-offset: 2px;
}
.lexical-content a:hover {
text-decoration-color: var(--color-primary);
}
.lexical-content :not(pre) > code {
background: var(--color-muted);
border-radius: 0.25rem;
padding: 0.125rem 0.375rem;
font-size: 0.8125rem;
}
.lexical-content pre {
background: var(--color-muted);
border: 1px solid var(--color-border);
border-radius: 0.5rem;
padding: 0.875rem 1rem;
overflow-x: auto;
margin: 0.75rem 0;
}
.lexical-content pre code {
background: transparent;
padding: 0;
font-size: 0.8125rem;
}
/* Wiki prose-article typography (scoped to .wiki-markdown) */
.wiki-markdown {
font-size: 0.9375rem;
line-height: 1.75;
color: var(--color-foreground);
}
.wiki-markdown > :first-child {
margin-top: 0;
}
.wiki-markdown h1,
.wiki-markdown h2,
.wiki-markdown h3,
.wiki-markdown h4 {
font-weight: 600;
line-height: 1.3;
color: var(--color-foreground);
}
.wiki-markdown h1 {
font-size: 1.5rem;
margin: 2rem 0 0.75rem;
}
.wiki-markdown h2 {
font-size: 1.25rem;
margin: 1.75rem 0 0.5rem;
padding-bottom: 0.375rem;
border-bottom: 1px solid var(--color-border);
}
.wiki-markdown h3 {
font-size: 1.0625rem;
margin: 1.5rem 0 0.375rem;
}
.wiki-markdown h4 {
font-size: 0.9375rem;
margin: 1.25rem 0 0.25rem;
}
.wiki-markdown p {
margin: 0.75rem 0;
}
.wiki-markdown ul,
.wiki-markdown ol {
margin: 0.75rem 0;
padding-left: 1.5rem;
}
.wiki-markdown ul {
list-style: disc;
}
.wiki-markdown ol {
list-style: decimal;
}
.wiki-markdown li + li {
margin-top: 0.25rem;
}
.wiki-markdown li > ul,
.wiki-markdown li > ol {
margin: 0.25rem 0;
}
.wiki-markdown blockquote {
margin: 1rem 0;
border-left: 3px solid var(--color-border);
padding-left: 1rem;
color: var(--color-muted-foreground);
}
.wiki-markdown hr {
margin: 1.75rem 0;
border: 0;
border-top: 1px solid var(--color-border);
}
.wiki-markdown table {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
margin: 1rem 0;
}
.wiki-markdown th,
.wiki-markdown td {
border: 1px solid var(--color-border);
padding: 0.5rem 0.75rem;
text-align: left;
}
.wiki-markdown th {
background: var(--color-muted);
font-weight: 600;
}
.wiki-markdown img {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
margin: 1rem 0;
}
.wiki-markdown a {
color: var(--color-primary);
text-decoration: underline;
text-underline-offset: 2px;
}
.wiki-markdown a:hover {
text-decoration-color: var(--color-primary);
}
.wiki-markdown :not(pre) > code {
background: var(--color-muted);
border-radius: 0.25rem;
padding: 0.125rem 0.375rem;
font-size: 0.8125rem;
}
.wiki-markdown pre {
background: var(--color-muted);
border: 1px solid var(--color-border);
border-radius: 0.5rem;
padding: 0.875rem 1rem;
overflow-x: auto;
margin: 1rem 0;
}
.wiki-markdown pre code {
background: transparent;
padding: 0;
font-size: 0.8125rem;
}
/* Wiki infobox — Wikipedia-style right-hand info card (scoped to .wiki-markdown) */
.wiki-markdown .infobox {
float: right;
clear: right;
width: 18rem;
margin: 0.25rem 0 1rem 1.5rem;
border: 1px solid var(--color-border);
border-radius: 0.5rem;
background: var(--color-card);
padding: 0.875rem 1rem;
font-size: 0.8125rem;
line-height: 1.55;
}
.wiki-markdown .infobox > p:first-child {
margin: 0 0 0.5rem;
text-align: center;
font-size: 0.9375rem;
font-weight: 600;
color: var(--color-foreground);
}
.wiki-markdown .infobox img {
display: block;
margin: 0.25rem auto 0.75rem;
border-radius: 0.375rem;
}
.wiki-markdown .infobox > div {
border: 0;
border-radius: 0;
overflow: visible;
}
.wiki-markdown .infobox table {
width: 100%;
min-width: 0;
font-size: 0.8125rem;
margin: 0;
}
.wiki-markdown .infobox th {
text-align: center;
}
.wiki-markdown .infobox td {
padding: 0.375rem 0.5rem;
vertical-align: top;
}
.wiki-markdown .infobox td:first-child {
width: 38%;
font-weight: 500;
white-space: nowrap;
}
@media (max-width: 640px) {
.wiki-markdown .infobox {
float: none;
width: 100%;
margin: 1rem 0;
}
}
/* Syntax-highlighted code blocks (rehype-highlight / highlight.js). */
.wiki-markdown pre code {
background: transparent;
padding: 0;
color: var(--muted-foreground);
}
.wiki-markdown pre code.hljs {
padding: 0;
background: transparent;
}
.wiki-markdown .hljs-keyword,
.wiki-markdown .hljs-selector-tag,
.wiki-markdown .hljs-literal,
.wiki-markdown .hljs-section,
.wiki-markdown .hljs-doctag,
.wiki-markdown .hljs-name {
color: #c792ea;
}
.wiki-markdown .hljs-string,
.wiki-markdown .hljs-regexp,
.wiki-markdown .hljs-addition,
.wiki-markdown .hljs-attribute,
.wiki-markdown .hljs-meta .hljs-string {
color: #a5d6a7;
}
.wiki-markdown .hljs-number,
.wiki-markdown .hljs-symbol,
.wiki-markdown .hljs-bullet,
.wiki-markdown .hljs-link,
.wiki-markdown .hljs-selector-attr,
.wiki-markdown .hljs-selector-pseudo {
color: #f78c6c;
}
.wiki-markdown .hljs-comment,
.wiki-markdown .hljs-quote {
color: #64748b;
font-style: italic;
}
.wiki-markdown .hljs-variable,
.wiki-markdown .hljs-template-variable,
.wiki-markdown .hljs-type,
.wiki-markdown .hljs-selector-class,
.wiki-markdown .hljs-selector-id,
.wiki-markdown .hljs-title,
.wiki-markdown .hljs-title.class_,
.wiki-markdown .hljs-title.function_ {
color: #82aaff;
}
.wiki-markdown .hljs-built_in,
.wiki-markdown .hljs-class .hljs-title,
.wiki-markdown .hljs-params {
color: #ffcb6b;
}
.wiki-markdown .hljs-deletion {
color: #ef5350;
}
.wiki-markdown .hljs-emphasis {
font-style: italic;
}
.wiki-markdown .hljs-strong {
font-weight: 600;
}
/* Glitch hover effect for official campaign card titles: fires once (~1s) on hover, then reverts. */
.glitch-hover {
position: relative;
}
.glitch-ghost {
position: absolute;
inset: 0;
text-wrap: balance;
opacity: 0;
pointer-events: none;
}
.glitch-shard-a {
color: #22d3ee;
}
.glitch-shard-b {
color: #f43f5e;
}
.glitch-shard-c {
color: #ffffff;
}
.group:hover .glitch-hover {
animation: glitch-jitter 1s ease-out 1;
}
.group:hover .glitch-shard-a {
animation: glitch-shard-a 1s ease-out 1;
}
.group:hover .glitch-shard-b {
animation: glitch-shard-b 1s ease-out 1;
}
.group:hover .glitch-shard-c {
animation: glitch-shard-c 1s ease-out 1;
}
@keyframes glitch-jitter {
0% {
transform: translate(3px, -2px) skewX(2deg);
text-shadow:
-4px 0 rgba(34, 211, 238, 0.7),
4px 0 rgba(244, 63, 94, 0.7);
animation-timing-function: steps(1, end);
}
5% {
transform: translate(-3px, 2px) skewX(-2deg);
text-shadow:
3px 0 rgba(34, 211, 238, 0.7),
-3px 0 rgba(244, 63, 94, 0.7);
animation-timing-function: steps(1, end);
}
10% {
transform: translate(2px, -1px) skewX(1deg);
text-shadow:
-2px 0 rgba(34, 211, 238, 0.6),
2px 0 rgba(244, 63, 94, 0.6);
animation-timing-function: steps(1, end);
}
15% {
transform: translate(-1px, 1px);
text-shadow:
1px 0 rgba(34, 211, 238, 0.5),
-1px 0 rgba(244, 63, 94, 0.5);
animation-timing-function: ease-out;
}
25% {
transform: translate(0, 0) skewX(0deg);
text-shadow: none;
}
100% {
transform: translate(0, 0) skewX(0deg);
text-shadow: none;
}
}
@keyframes glitch-shard-a {
0% {
opacity: 0.9;
clip-path: polygon(0 0, 62% 0, 54% 34%, 0 34%);
transform: translate(-8px, -4px);
animation-timing-function: steps(1, end);
}
8% {
clip-path: polygon(6% 4%, 68% 0, 58% 38%, 0 30%);
transform: translate(8px, 4px);
animation-timing-function: steps(1, end);
}
16% {
opacity: 0.35;
clip-path: polygon(0 0, 50% 6%, 44% 30%, 0 38%);
transform: translate(-3px, -2px);
animation-timing-function: ease-out;
}
35% {
opacity: 0;
clip-path: polygon(0 0, 62% 0, 54% 34%, 0 34%);
transform: translate(-5px, -3px);
}
100% {
opacity: 0;
clip-path: polygon(0 0, 62% 0, 54% 34%, 0 34%);
transform: translate(-5px, -3px);
}
}
@keyframes glitch-shard-b {
0% {
opacity: 0.9;
clip-path: polygon(38% 66%, 100% 58%, 100% 100%, 30% 100%);
transform: translate(8px, 4px);
animation-timing-function: steps(1, end);
}
8% {
clip-path: polygon(32% 62%, 100% 66%, 100% 100%, 24% 96%);
transform: translate(-8px, -4px);
animation-timing-function: steps(1, end);
}
16% {
opacity: 0.35;
clip-path: polygon(50% 70%, 100% 62%, 100% 100%, 44% 100%);
transform: translate(3px, 2px);
animation-timing-function: ease-out;
}
35% {
opacity: 0;
clip-path: polygon(38% 66%, 100% 58%, 100% 100%, 30% 100%);
transform: translate(5px, 3px);
}
100% {
opacity: 0;
clip-path: polygon(38% 66%, 100% 58%, 100% 100%, 30% 100%);
transform: translate(5px, 3px);
}
}
@keyframes glitch-shard-c {
0% {
opacity: 0.95;
clip-path: polygon(8% 42%, 96% 38%, 88% 62%, 0 66%);
transform: translate(6px, -4px);
animation-timing-function: steps(1, end);
}
6% {
transform: translate(-6px, 4px);
animation-timing-function: steps(1, end);
}
12% {
clip-path: polygon(0 46%, 88% 40%, 96% 58%, 4% 62%);
transform: translate(4px, 2px);
animation-timing-function: steps(1, end);
}
20% {
transform: translate(-2px, -1px);
animation-timing-function: ease-out;
}
35% {
opacity: 0;
clip-path: polygon(8% 42%, 96% 38%, 88% 62%, 0 66%);
transform: translate(1px, 2px);
}
100% {
opacity: 0;
clip-path: polygon(8% 42%, 96% 38%, 88% 62%, 0 66%);
transform: translate(1px, 2px);
}
}
.group:hover .glitch-soft {
animation: glitch-soft 1s ease-out 1;
}
.group:hover .glitch-soft-late {
animation-delay: 0.1s;
}
@keyframes glitch-soft {
0% {
transform: translate(1px, -1px);
text-shadow:
1px 0 rgba(34, 211, 238, 0.45),
-1px 0 rgba(244, 63, 94, 0.45);
animation-timing-function: steps(1, end);
}
8% {
transform: translate(-1px, 1px);
text-shadow:
-1px 0 rgba(34, 211, 238, 0.45),
1px 0 rgba(244, 63, 94, 0.45);
animation-timing-function: steps(1, end);
}
16% {
transform: translate(1px, 1px);
animation-timing-function: ease-out;
}
30% {
transform: translate(0, 0);
text-shadow: none;
}
100% {
transform: translate(0, 0);
text-shadow: none;
}
}
.glitch-ghost-img {
opacity: 0;
}
.group:hover .glitch-ghost-img-a {
animation: glitch-img-band-a 2s ease-out 1;
}
.group:hover .glitch-ghost-img-b {
animation: glitch-img-band-b 2s ease-out 1;
}
@keyframes glitch-img-band-a {
0% {
opacity: 0.45;
clip-path: inset(0 0 55% 0);
transform: translate(-4px, 0);
animation-timing-function: steps(1, end);
}
6% {
clip-path: inset(5% 0 60% 0);
transform: translate(4px, 2px);
animation-timing-function: steps(1, end);
}
12% {
opacity: 0.35;
clip-path: inset(10% 0 62% 0);
transform: translate(-2px, 0);
}
100% {
opacity: 0;
clip-path: inset(0 0 55% 0);
transform: translate(0, 0);
}
}
@keyframes glitch-img-band-b {
0% {
opacity: 0.45;
clip-path: inset(45% 0 0 0);
transform: translate(4px, 0);
animation-timing-function: steps(1, end);
}
6% {
clip-path: inset(40% 0 5% 0);
transform: translate(-4px, -2px);
animation-timing-function: steps(1, end);
}
12% {
opacity: 0.35;
clip-path: inset(38% 0 10% 0);
transform: translate(2px, 0);
}
100% {
opacity: 0;
clip-path: inset(45% 0 0 0);
transform: translate(0, 0);
}
}
@media (prefers-reduced-motion: reduce) {
.group:hover .glitch-hover {
animation: none;
}
.group:hover .glitch-ghost {
animation: none;
opacity: 0;
}
.group:hover .glitch-soft {
animation: none;
}
.group:hover .glitch-ghost-img {
animation: none;
opacity: 0;
}
}
/* Poker surface: the felt is the game's one custom material. */
.poker-table-stage {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.75rem;
}
.poker-felt-table {
--poker-felt-deep: #071c15;
--poker-felt-mid: #0e3b29;
--poker-felt-light: #1c5b3d;
--poker-rim: #06130f;
--poker-trim: #b08a44;
--poker-chip-clay: #9b6248;
isolation: isolate;
width: 100%;
border: 0.65rem solid var(--poker-rim);
border-radius: 50% / 48%;
background:
radial-gradient(
circle at 50% 44%,
color-mix(in srgb, var(--poker-felt-light) 72%, transparent),
transparent 55%
),
radial-gradient(ellipse at 50% 50%, var(--poker-felt-mid) 0%, var(--poker-felt-deep) 78%);
box-shadow:
0 0 0 1px var(--poker-trim),
0 0 0 0.2rem color-mix(in srgb, var(--poker-trim) 36%, transparent),
inset 0 0 0 0.12rem color-mix(in srgb, var(--poker-trim) 70%, transparent),
inset 0 0 2.5rem color-mix(in srgb, var(--poker-felt-deep) 80%, black),
0 0.75rem 2rem rgb(0 0 0 / 35%);
}
.poker-felt-table::after {
position: absolute;
inset: 1.05rem;
z-index: -1;
border: 1px solid color-mix(in srgb, var(--poker-trim) 52%, transparent);
border-radius: 50% / 46%;
content: "";
pointer-events: none;
}
.poker-seat-arc-left {
top: 50%;
left: 1.5%;
transform: translateY(-50%);
}
.poker-seat-arc-center {
top: 2%;
left: 50%;
transform: translateX(-50%);
}
.poker-seat-arc-right {
top: 50%;
right: 1.5%;
transform: translateY(-50%);
}
.poker-seat-arc-far-left {
top: 34%;
left: 5%;
transform: translateY(-50%);
}
.poker-seat-arc-center-left {
top: 6%;
left: 27%;
transform: translateX(-50%);
}
.poker-seat-arc-center-right {
top: 6%;
right: 27%;
transform: translateX(50%);
}
.poker-seat-arc-far-right {
top: 34%;
right: 5%;
transform: translateY(-50%);
}
/* Blackjack seats hug the bottom two-thirds of the felt so the top third
stays clear for the dealer. Six slots push out to the rim of the 50%/48%
ellipse (three per side, bulging outward at the middle) with margins that
keep even the widest seat panel fully inside the curved edge, spaced for
breathing room between neighbors. */
.blackjack-seat-arc-corner-right {
top: 29%;
left: 74%;
transform: translate(-50%, -50%);
}
.blackjack-seat-arc-corner-left {
top: 29%;
left: 26%;
transform: translate(-50%, -50%);
}
.blackjack-seat-arc-mid-right {
top: 50%;
left: 83.5%;
transform: translate(-50%, -50%);
}
.blackjack-seat-arc-mid-left {
top: 50%;
left: 16.5%;
transform: translate(-50%, -50%);
}
.blackjack-seat-arc-low-right {
top: 71%;
left: 74%;
transform: translate(-50%, -50%);
}
.blackjack-seat-arc-low-left {
top: 71%;
left: 26%;
transform: translate(-50%, -50%);
}
/* Blackjack seats share the poker seat panel CSS, whose commit pill and hand
reveal are absolutely positioned via poker-only arc classes that blackjack
slots never carry. Keep the pill in the panel row and push corner-seat
reveals outward, clear of the dealer block. */
.blackjack-felt .poker-seat-commit {
position: static;
transform: none;
}
.blackjack-felt .blackjack-seat-arc-corner-right .poker-seat-reveal {
left: calc(50% + 4.5rem);
}
.blackjack-felt .blackjack-seat-arc-corner-left .poker-seat-reveal {
left: calc(50% - 4.5rem);
}
/* Top-anchor the log below the dealer block (bottom edge reaches ~44% with
the turn clock) so the newest line can never climb over the time bar. */
.blackjack-felt .poker-action-log {
top: 46%;
bottom: auto;
}
/* Same fix for poker: bottom-anchoring let the newest line climb into the
center block's turn clock. The clock's bottom edge sits around 65% of the
4:3 felt during betting, so the log is top-anchored at 66.5% and capped
to three visible lines (about 0.95rem each plus gaps) so it ends before
the viewer card spot; the mask fades the capped tail. */
.poker-felt .poker-action-log {
top: 66.5%;
bottom: auto;
max-height: 2.95rem;
overflow: hidden;
}
/* Poker chip flight: every chip starts stacked at one origin and launches
one after another (130ms stagger, fill-mode both holds unlaunched chips
in the pile). Wins drop from the pot plaque (~40% of the felt) down to
the viewer card spot (bottom 7rem, so about 14rem of travel); losses fly
the reverse leg from the card spot up to the pot. Chips are the shared
.bj-chip pieces; the geometry is poker-scoped because blackjack flies
between different anchors. */
.poker-chip-flight {
position: absolute;
left: 50%;
z-index: 35;
width: 1.6rem;
height: 0.95rem;
transform: translateX(-50%);
pointer-events: none;
}
.poker-chip-flight-win {
top: 40%;
}
.poker-chip-flight-loss {
bottom: 7.6rem;
}
.poker-chip-flight .bj-chip {
bottom: auto;
left: 0;
}
.poker-chip-flight-win .bj-chip {
animation: poker-chip-fly-win 700ms ease-in both;
}
.poker-chip-flight-loss .bj-chip {
animation: poker-chip-fly-loss 700ms ease-in both;
}
@keyframes poker-chip-fly-win {
0% {
opacity: 1;
transform: translateY(0) scale(1);
}
70% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(14rem) scale(0.7);
}
}
@keyframes poker-chip-fly-loss {
0% {
opacity: 1;
transform: translateY(0) scale(1);
}
70% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(-14rem) scale(0.7);
}
}
@media (prefers-reduced-motion: reduce) {
.poker-chip-flight {
display: none;
}
}
/* Fullscreen contain-fit: absolute with zero insets + auto margins sizes the
felt to the largest 4:3 rectangle inside the viewport area (the same rules
browsers use for replaced elements), so it can never collapse to content
size and never overflow. Must stay after .poker-felt-table to win the
width tie. */
.poker-felt-fullscreen {
position: absolute;
inset: 0;
margin: auto;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
aspect-ratio: 4 / 3;
}
.poker-seat-panel {
position: relative;
border: 1px solid color-mix(in srgb, white 16%, transparent);
border-radius: 0.75rem;
background: rgb(0 0 0 / 38%);
box-shadow: 0 0.3rem 0.6rem rgb(0 0 0 / 22%);
}
.poker-waiting-seat {
border-color: color-mix(in srgb, white 24%, transparent);
border-radius: 0.65rem;
background: rgb(0 0 0 / 28%);
color: white;
text-align: center;
}
.poker-waiting-seat-occupied {
border-color: color-mix(in srgb, var(--poker-trim) 62%, transparent);
}
.poker-waiting-seat-south {
bottom: 1.5%;
left: 50%;
transform: translateX(-50%);
}
.poker-waiting-name,
.poker-waiting-status {
max-width: 8rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.poker-seat-avatar {
display: flex;
width: 2.4rem;
height: 2.4rem;
align-items: center;
justify-content: center;
border: 2px solid color-mix(in srgb, white 52%, transparent);
border-radius: 50%;
box-shadow:
inset 0 1px rgb(255 255 255 / 20%),
0 0.2rem 0.45rem rgb(0 0 0 / 34%);
color: white;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 1rem;
font-weight: 700;
}
.poker-seat-avatar-tone-0 {
background: #2f6f5a;
}
.poker-seat-avatar-tone-1 {
background: #7b543c;
}
.poker-seat-avatar-tone-2 {
background: #465d87;
}
.poker-seat-avatar-tone-3 {
background: #76506f;
}
.poker-seat-avatar-open {
border-style: dashed;
background: color-mix(in srgb, var(--poker-rim) 78%, transparent);
color: rgb(255 255 255 / 58%);
}
.poker-console-avatar {
width: 2.1rem;
height: 2.1rem;
flex: 0 0 auto;
}
.poker-seat-nameplate {
min-width: 0;
padding-inline: 0.1rem;
}
.poker-seat-stack {
display: inline-flex;
align-items: center;
gap: 0.3rem;
color: #f3d28b;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 1.35rem;
font-variant-numeric: tabular-nums;
font-weight: 700;
line-height: 1.15;
}
.poker-seat-stack-chip {
width: 0.85rem;
height: 0.85rem;
flex: 0 0 auto;
border: 1px solid #d6b36a;
border-radius: 50%;
background:
radial-gradient(
circle at center,
color-mix(in srgb, var(--poker-chip-clay) 60%, white) 0 0.16rem,
transparent 0.17rem
),
var(--poker-chip-clay);
box-shadow: inset 0 1px rgb(255 255 255 / 25%);
}
.poker-seat-stack-flash-up {
animation: poker-stack-flash-up 900ms ease-out;
}
.poker-seat-stack-flash-down {
animation: poker-stack-flash-down 900ms ease-out;
}
@keyframes poker-stack-flash-up {
0% {
color: #6ee7b7;
transform: scale(1.16);
}
100% {
color: #f3d28b;
transform: scale(1);
}
}
@keyframes poker-stack-flash-down {
0% {
color: #fda4af;
transform: scale(0.92);
}
100% {
color: #f3d28b;
transform: scale(1);
}
}
.poker-seat-status-row {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 0.2rem;
margin-top: 0.15rem;
}
.poker-seat-action-badge {
border: 1px solid color-mix(in srgb, var(--poker-trim) 60%, transparent);
border-radius: 9999px;
padding: 0.12rem 0.45rem;
background: color-mix(in srgb, var(--poker-trim) 14%, transparent);
color: #f3d28b;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.08em;
line-height: 1.2;
text-transform: uppercase;
}
.poker-seat-action-winner {
border-color: color-mix(in srgb, #d6b36a 72%, transparent);
background: color-mix(in srgb, #d6b36a 20%, transparent);
}
.poker-seat-cardbacks {
position: absolute;
top: calc(100% - 0.15rem);
left: 50%;
z-index: 3;
display: block;
width: 1.7rem;
height: 1rem;
transform: translateX(-50%);
pointer-events: none;
}
.poker-seat-cardbacks span {
position: absolute;
top: 0.1rem;
display: block;
width: 0.78rem;
height: 1.12rem;
border: 1px solid color-mix(in srgb, white 30%, transparent);
border-radius: 0.14rem;
background-color: #101a33;
background-image:
linear-gradient(45deg, rgb(190 42 58 / 34%) 25%, transparent 25%),
linear-gradient(-45deg, rgb(190 42 58 / 34%) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgb(190 42 58 / 34%) 75%),
linear-gradient(-45deg, transparent 75%, rgb(190 42 58 / 34%) 75%);
background-position:
0 0,
0.2rem 0,
0.2rem -0.2rem,
-0.2rem 0.2rem;
background-size: 0.4rem 0.4rem;
box-shadow: 0 0.15rem 0.3rem rgb(0 0 0 / 30%);
}
.poker-seat-cardbacks span:first-child {
left: 0;
transform: rotate(-10deg);
}
.poker-seat-cardbacks span:last-child {
right: 0;
transform: rotate(10deg);
}
.poker-seat-reveal {
position: absolute;
top: calc(100% + 0.25rem);
left: 50%;
z-index: 5;
display: flex;
gap: 0.2rem;
transform: translateX(-50%);
}
.poker-seat-reveal .poker-card:first-child {
--poker-card-resting-transform: rotate(-6deg);
}
.poker-seat-reveal .poker-card:last-child {
--poker-card-resting-transform: rotate(6deg);
}
.poker-seat-commit {
position: absolute;
z-index: 4;
display: flex;
align-items: center;
gap: 0.25rem;
border: 1px solid color-mix(in srgb, var(--poker-trim) 50%, transparent);
border-radius: 9999px;
padding: 0.18rem 0.45rem 0.18rem 0.25rem;
background: color-mix(in srgb, var(--poker-rim) 88%, transparent);
color: #f3d28b;
box-shadow: 0 0.2rem 0.4rem rgb(0 0 0 / 28%);
white-space: nowrap;
}
.poker-seat-commit-label {
color: rgb(255 255 255 / 55%);
font-size: 0.65rem;
font-weight: 500;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.poker-seat-arc-left .poker-seat-commit {
top: 50%;
right: -4.5rem;
transform: translateY(-50%);
}
.poker-seat-arc-right .poker-seat-commit {
top: 50%;
left: -4.5rem;
transform: translateY(-50%);
}
.poker-seat-arc-center .poker-seat-commit {
right: 50%;
bottom: -2.5rem;
transform: translateX(50%);
}
.poker-seat-arc-far-left .poker-seat-commit,
.poker-seat-arc-center-left .poker-seat-commit {
right: -4.5rem;
bottom: -1.25rem;
}
.poker-seat-arc-far-right .poker-seat-commit,
.poker-seat-arc-center-right .poker-seat-commit {
bottom: -1.25rem;
left: -4.5rem;
}
.poker-dealer-button {
border-color: color-mix(in srgb, var(--poker-trim) 85%, white);
background: var(--poker-trim);
color: #20170b;
box-shadow: 0 0.15rem 0.3rem rgb(0 0 0 / 30%);
}
.poker-deck-origin {
position: absolute;
top: -2.1rem;
left: 50%;
z-index: 2;
display: block;
width: 1.9rem;
height: 1.4rem;
opacity: 0.85;
transform: translateX(-50%);
pointer-events: none;
}
.poker-deck-card {
position: absolute;
bottom: 0;
display: block;
width: 1.15rem;
height: 1.6rem;
border: 1px solid color-mix(in srgb, white 35%, transparent);
border-radius: 0.18rem;
background-color: #101a33;
background-image:
linear-gradient(45deg, rgb(190 42 58 / 34%) 25%, transparent 25%),
linear-gradient(-45deg, rgb(190 42 58 / 34%) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgb(190 42 58 / 34%) 75%),
linear-gradient(-45deg, transparent 75%, rgb(190 42 58 / 34%) 75%);
background-position:
0 0,
0.25rem 0,
0.25rem -0.25rem,
-0.25rem 0.25rem;
background-size: 0.5rem 0.5rem;
box-shadow: 0 0.16rem 0.3rem rgb(0 0 0 / 28%);
}
.poker-deck-card-back {
left: 0.2rem;
transform: translateY(-0.12rem) rotate(-9deg);
}
.poker-deck-card-middle {
left: 0.38rem;
transform: translateY(-0.2rem) rotate(-3deg);
}
.poker-deck-card-top {
left: 0.56rem;
transform: translateY(-0.26rem) rotate(4deg);
}
.poker-pot-plaque {
display: flex;
min-width: 5.5rem;
flex-direction: column;
align-items: center;
gap: 0.1rem;
border: 1px solid color-mix(in srgb, var(--poker-trim) 74%, white);
border-radius: 0.45rem;
padding: 0.25rem 0.85rem 0.3rem;
background: color-mix(in srgb, var(--poker-rim) 92%, transparent);
box-shadow:
inset 0 1px rgb(255 255 255 / 10%),
0 0.25rem 0.5rem rgb(0 0 0 / 24%);
}
/* Big pots break down into up to seven denomination piles; let the pile row
wrap so the plaque can never outgrow the center block on narrow felts. */
.poker-pot-plaque .bj-chip-stack {
max-width: 100%;
justify-content: center;
flex-wrap: wrap;
}
.poker-pot-plaque .poker-console-kicker {
color: #f3d28b;
}
.poker-table-turn-clock {
width: min(12rem, 80%);
color: rgb(255 255 255 / 72%);
}
.poker-action-console {
position: relative;
display: grid;
grid-template-columns: minmax(9rem, 1.1fr) minmax(6rem, 0.8fr) minmax(13rem, 1.6fr);
align-items: stretch;
gap: 0.75rem;
width: min(40rem, 96%);
min-height: 9rem;
margin-inline: auto;
padding: 0.7rem;
border: 1px solid var(--poker-trim);
border-radius: 0.35rem;
background:
linear-gradient(135deg, rgb(0 0 0 / 34%), transparent 48%),
color-mix(in srgb, var(--poker-rim) 92%, var(--poker-felt-deep));
box-shadow:
inset 0 1px rgb(255 255 255 / 10%),
inset 0 0.2rem 1rem rgb(0 0 0 / 24%),
0 0.35rem 1.4rem rgb(0 0 0 / 24%);
color: white;
}
.poker-action-console-waiting {
opacity: 0.86;
}
.poker-sound-toggle {
display: flex;
align-items: center;
gap: 0.4rem;
}
.poker-sound-volume {
width: 4.5rem;
accent-color: var(--poker-trim);
}
.poker-action-console:not(.poker-action-console-complete) {
grid-template-columns: minmax(0, 1.5fr) minmax(10rem, 0.8fr);
}
.poker-viewer-cards {
position: absolute;
bottom: 7rem;
left: 50%;
z-index: 14;
display: flex;
transform: translateX(-50%);
pointer-events: none;
}
.poker-viewer-cards .poker-card-viewer-right {
margin-left: -0.45rem;
}
.poker-viewer-cards .poker-card-folded {
opacity: 0.5;
}
.poker-action-toolbar {
position: absolute;
bottom: 1.15rem;
left: 50%;
z-index: 15;
display: flex;
width: min(30rem, calc(100% - 2rem));
justify-content: center;
padding: 0.45rem;
border: 1px solid color-mix(in srgb, var(--poker-trim) 64%, transparent);
border-radius: 0.5rem;
background: color-mix(in srgb, var(--poker-rim) 88%, transparent);
box-shadow:
inset 0 1px rgb(255 255 255 / 10%),
0 0.35rem 0.9rem rgb(0 0 0 / 30%);
transform: translateX(-50%);
}
.poker-action-toolbar-waiting {
width: fit-content;
max-width: calc(100% - 2rem);
padding-inline: 0.75rem;
color: rgb(255 255 255 / 64%);
font-size: 0.7rem;
}
/* Below-felt variant: on narrow screens the toolbar renders in normal flow
under the felt because the felt's overflow-hidden clips the overlay row
there; static positioning cancels the overlay anchor. */
.poker-action-toolbar-below {
position: static;
transform: none;
width: min(30rem, 100%);
}
.poker-action-toolbar .poker-betting-controls-compact {
width: 100%;
gap: 0.4rem;
}
.poker-betting-controls-compact .poker-compact-actions {
justify-content: center;
gap: 0.4rem;
}
.poker-betting-controls-compact .poker-chip {
min-height: 2rem;
padding-inline: 0.7rem;
}
.poker-betting-controls-compact .poker-betting-message {
justify-content: center;
min-height: 1rem;
}
.poker-wager-panel-compact {
position: absolute;
bottom: calc(100% + 0.5rem);
left: 50%;
z-index: 16;
width: min(26rem, calc(100% - 1rem));
max-width: calc(100% - 1rem);
max-height: min(18rem, calc(100dvh - 8rem));
margin: 0;
overflow-y: auto;
transform: translateX(-50%);
}
.poker-wager-panel-compact .poker-compact-wager-line {
flex-wrap: wrap;
gap: 0.45rem;
}
.poker-wager-panel-compact .poker-wager-range {
flex: 1 1 100%;
}
.poker-wager-panel-compact .poker-wager-input {
width: 7rem;
flex: 0 1 7rem;
}
.poker-wager-panel-compact .poker-compact-quick-row {
align-items: center;
gap: 0.3rem;
}
.poker-wager-panel-compact .poker-quick-chip {
min-height: 1.65rem;
padding-inline: 0.55rem;
}
.poker-console-player,
.poker-console-economy,
.poker-console-actions {
min-width: 0;
}
.poker-console-player {
display: grid;
grid-template-columns: auto auto minmax(0, 1fr);
align-items: center;
gap: 0.6rem;
}
.poker-console-identity {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 0.45rem;
}
.poker-console-identity > .flex {
flex-direction: column;
align-items: flex-start;
gap: 0.35rem;
min-width: 0;
}
.poker-console-identity > .flex > .min-w-0 {
width: 100%;
}
.poker-console-role-row {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 0.2rem;
max-width: 100%;
}
.poker-console-role-row > * {
border-color: color-mix(in srgb, var(--poker-trim) 70%, transparent);
background: color-mix(in srgb, var(--poker-trim) 18%, transparent);
color: #f3d28b;
font-size: 0.58rem;
}
.poker-hand-placard {
display: block;
width: fit-content;
max-width: 100%;
border: 1px solid color-mix(in srgb, var(--poker-trim) 78%, white);
border-radius: 0.15rem;
padding: 0.28rem 0.45rem;
background: linear-gradient(180deg, rgb(176 138 68 / 32%), rgb(176 138 68 / 12%));
color: #f3d28b;
font-size: 0.68rem;
line-height: 1.2;
overflow-wrap: anywhere;
}
/* Progressive hand-rank ladder: each tier a step brighter and glowier, from
brass hints through silver (straight, flush) and gold (full house, quads)
up to the royal flush prismatic treatment. */
.poker-rank-tier-1 {
border-color: rgb(138 109 59 / 80%);
}
.poker-rank-tier-2 {
border-color: rgb(163 130 63 / 85%);
box-shadow: inset 0 0 0.4rem rgb(213 179 106 / 16%);
}
.poker-rank-tier-3 {
border-color: rgb(185 138 62 / 90%);
box-shadow:
inset 0 0 0.5rem rgb(213 179 106 / 24%),
0 0 0.35rem rgb(213 179 106 / 18%);
}
.poker-rank-tier-4 {
border-color: rgb(201 201 212 / 85%);
box-shadow:
inset 0 0 0.55rem rgb(226 232 240 / 22%),
0 0 0.45rem rgb(226 232 240 / 20%);
}
.poker-rank-tier-5 {
border-color: rgb(214 214 228 / 95%);
box-shadow:
inset 0 0 0.7rem rgb(226 232 240 / 32%),
0 0 0.7rem rgb(226 232 240 / 30%);
}
.poker-rank-tier-6 {
border-color: #cfa95c;
box-shadow:
inset 0 0 0.7rem rgb(214 179 106 / 30%),
0 0 0.6rem rgb(214 179 106 / 28%);
}
.poker-rank-tier-7 {
border-color: #e0bd6f;
box-shadow:
inset 0 0 0.8rem rgb(224 189 111 / 44%),
0 0 0.85rem rgb(224 189 111 / 42%);
}
.poker-rank-tier-8 {
border-color: #79a7f5;
box-shadow:
inset 0 0 0.9rem rgb(121 167 245 / 46%),
0 0 0.95rem rgb(121 167 245 / 48%);
}
.poker-rank-tier-9 {
position: relative;
border-color: #ffd700;
background-image: linear-gradient(
115deg,
rgb(255 77 77 / 7%),
rgb(255 184 77 / 7%) 18%,
rgb(255 249 77 / 7%) 36%,
rgb(77 255 136 / 7%) 54%,
rgb(77 195 255 / 7%) 72%,
rgb(184 77 255 / 7%) 88%,
rgb(255 77 77 / 7%)
);
background-size: 300% 100%;
box-shadow:
inset 0 0 1rem rgb(255 215 0 / 46%),
0 0 1.1rem rgb(255 215 0 / 55%);
animation: poker-royal-wash 6s ease-in-out infinite alternate;
}
/* The acting seat's halo: a translucent emerald glow that breathes behind the
pill, rendered by a transparent pseudo so the ring never covers content. */
.poker-action-log {
position: absolute;
bottom: 12.5rem;
left: 50%;
z-index: 10;
display: flex;
width: min(24rem, 80%);
flex-direction: column;
gap: 0.2rem;
transform: translateX(-50%);
pointer-events: none;
-webkit-mask-image: linear-gradient(to bottom, black 45%, transparent 100%);
mask-image: linear-gradient(to bottom, black 45%, transparent 100%);
}
.poker-action-log-line {
text-align: center;
color: rgb(255 255 255 / 70%);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.7rem;
letter-spacing: 0.03em;
overflow-wrap: anywhere;
}
.poker-log-wager {
color: rgb(253 230 138 / 80%);
}
.poker-log-win {
color: rgb(167 243 208 / 85%);
}
.poker-log-fold,
.poker-log-leave {
color: rgb(255 255 255 / 45%);
}
.poker-log-action {
color: rgb(191 219 254 / 80%);
}
/* The acting seat's halo: a translucent emerald glow that breathes behind the
pill, rendered by a transparent pseudo so the ring never covers content. */
.poker-seat-active {
border-color: rgb(52 211 153 / 85%);
}
.poker-seat-active::after {
content: "";
position: absolute;
inset: -0.3rem;
z-index: 1;
border-radius: 0.9rem;
pointer-events: none;
animation: poker-seat-pulse 2s ease-in-out infinite;
}
@keyframes poker-seat-pulse {
0%,
100% {
box-shadow: 0 0 0.4rem 0 rgb(52 211 153 / 18%);
}
50% {
box-shadow: 0 0 1.4rem 0.25rem rgb(52 211 153 / 55%);
}
}
/* The rainbow lives only on the border ring (masked pseudo-element) and the
near-transparent background wash, never on the element's content: the hue
filter animates the pseudo itself, so cards and text keep their true colors. */
.poker-rank-tier-9::before {
content: "";
position: absolute;
inset: 0;
z-index: 1;
border-radius: inherit;
padding: 1px;
background: linear-gradient(
115deg,
#ff4d4d,
#ffb84d,
#fff94d,
#4dff88,
#4dc3ff,
#b84dff,
#ff4d4d
);
background-size: 300% 100%;
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
mask-composite: exclude;
animation: poker-royal-border-hue 3.2s linear infinite;
pointer-events: none;
}
@keyframes poker-royal-wash {
0% {
background-position: 0% 0;
}
100% {
background-position: 100% 0;
}
}
@keyframes poker-royal-border-hue {
0% {
filter: hue-rotate(0deg);
background-position: 0% 0;
}
100% {
filter: hue-rotate(360deg);
background-position: 300% 0;
}
}
.poker-console-economy {
display: flex;
flex-direction: column;
justify-content: center;
gap: 0.7rem;
border-inline: 1px solid color-mix(in srgb, var(--poker-trim) 26%, transparent);
padding-inline: 0.7rem;
}
.poker-chip-readout {
display: flex;
align-items: center;
gap: 0.5rem;
}
.poker-chip-stack {
position: relative;
display: block;
width: 1.7rem;
height: 1.1rem;
flex: 0 0 auto;
border: 2px solid #d6b36a;
border-radius: 50%;
background: var(--poker-chip-clay);
box-shadow: 0 0.16rem 0 rgb(0 0 0 / 35%);
transform: rotate(-6deg);
}
.poker-chip-stack::before,
.poker-chip-stack::after {
position: absolute;
left: -0.12rem;
width: 1.7rem;
height: 1.1rem;
border: 2px solid color-mix(in srgb, #d6b36a 85%, white);
border-radius: 50%;
content: "";
}
.poker-chip-stack::before {
top: -0.32rem;
background: color-mix(in srgb, var(--poker-chip-clay) 82%, white);
}
.poker-chip-stack::after {
top: -0.64rem;
background: color-mix(in srgb, var(--poker-chip-clay) 72%, var(--poker-trim));
}
.poker-console-readout-copy {
display: flex;
min-width: 0;
flex-direction: column;
gap: 0.15rem;
}
.poker-console-kicker {
color: rgb(255 255 255 / 54%);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.58rem;
letter-spacing: 0.12em;
line-height: 1.1;
text-transform: uppercase;
}
.poker-console-timer {
min-width: 0;
}
.poker-console-timer [role="timer"] {
color: rgb(255 255 255 / 72%);
}
.poker-console-timer [aria-label] > div:last-child {
background: rgb(255 255 255 / 12%);
}
.poker-console-timer [aria-label] > div:last-child > div {
background: var(--poker-trim);
}
.poker-console-actions {
display: flex;
flex-direction: column;
justify-content: center;
}
.poker-betting-controls {
min-width: 0;
}
.poker-chip {
min-height: 2.2rem;
border-radius: 9999px;
padding-inline: 0.8rem;
border-width: 1px;
box-shadow:
inset 0 1px rgb(255 255 255 / 16%),
0 0.16rem 0 rgb(0 0 0 / 28%);
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.02em;
transition:
transform 150ms ease,
filter 150ms ease,
background-color 150ms ease;
}
.poker-chip:hover:not(:disabled) {
filter: brightness(1.1);
transform: translateY(-1px);
}
.poker-chip:active:not(:disabled) {
transform: translateY(1px) scale(0.98);
}
.poker-chip-fold {
border-color: color-mix(in srgb, white 26%, transparent);
background: var(--poker-rim);
color: rgb(255 255 255 / 78%);
}
.poker-chip-call {
border-color: color-mix(in srgb, var(--poker-chip-clay) 90%, white);
background: var(--poker-chip-clay);
color: #fff4ed;
}
.poker-chip-wager {
border-color: #e0bd6f;
background: linear-gradient(180deg, #d6b36a, var(--poker-trim));
color: #20170b;
}
/* The complete-console primary action must stay readable even if the
gradient's var chain or the shadcn background utilities win the cascade. */
.poker-complete-actions .poker-chip-wager {
color: #20170b;
background-color: #d6b36a;
background-image: linear-gradient(180deg, #d6b36a, color-mix(in srgb, #d6b36a 72%, #20170b));
}
.poker-wager-panel {
border-color: color-mix(in srgb, var(--poker-trim) 48%, transparent);
background: color-mix(in srgb, var(--poker-felt-deep) 86%, black);
box-shadow: inset 0 1px rgb(255 255 255 / 8%);
}
.poker-wager-range {
accent-color: var(--poker-trim);
}
.poker-wager-input {
border-color: color-mix(in srgb, var(--poker-trim) 45%, transparent);
background: rgb(0 0 0 / 24%);
}
.poker-quick-chip {
min-height: 1.8rem;
border-color: color-mix(in srgb, var(--poker-trim) 45%, transparent);
border-radius: 9999px;
background: rgb(255 255 255 / 5%);
color: rgb(255 255 255 / 74%);
font-size: 0.65rem;
}
.poker-betting-message {
color: rgb(255 255 255 / 54%);
}
.poker-complete-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
}
.poker-complete-copy {
display: flex;
min-width: 0;
flex-direction: column;
gap: 0.2rem;
}
.poker-card {
--poker-card-resting-transform: none;
border-radius: 0.3rem;
perspective: 45rem;
vertical-align: middle;
transform: var(--poker-card-resting-transform);
transform-origin: center bottom;
}
.poker-card-motion {
display: block;
width: 100%;
height: 100%;
transform-origin: center bottom;
transform-style: preserve-3d;
}
.poker-card-viewer-left {
--poker-card-resting-transform: perspective(40rem) rotate(-7deg) rotateX(5deg) translateY(0.12rem);
}
.poker-card-viewer-right {
--poker-card-resting-transform: perspective(40rem) rotate(7deg) rotateX(5deg) translateY(0.12rem);
}
.poker-seat-arc-left .poker-card:nth-child(1) {
--poker-card-resting-transform: rotate(7deg) rotateX(-4deg);
}
.poker-seat-arc-left .poker-card:nth-child(2) {
--poker-card-resting-transform: rotate(11deg) rotateX(-4deg);
}
.poker-seat-arc-center .poker-card {
--poker-card-resting-transform: rotate(1deg) rotateX(-3deg);
}
.poker-seat-arc-right .poker-card:nth-child(1) {
--poker-card-resting-transform: rotate(-7deg) rotateX(-4deg);
}
.poker-seat-arc-right .poker-card:nth-child(2) {
--poker-card-resting-transform: rotate(-11deg) rotateX(-4deg);
}
.poker-seat-arc-far-left .poker-card,
.poker-seat-arc-center-left .poker-card {
--poker-card-resting-transform: rotate(8deg) rotateX(-4deg);
}
.poker-seat-arc-far-right .poker-card,
.poker-seat-arc-center-right .poker-card {
--poker-card-resting-transform: rotate(-8deg) rotateX(-4deg);
}
.poker-card-community {
--poker-card-resting-transform: rotate(-1.5deg) rotateX(2deg) translateY(0.08rem);
}
.poker-card-community:nth-child(2n) {
--poker-card-resting-transform: rotate(1.5deg) rotateX(2deg) translateY(-0.04rem);
}
.poker-card-community:nth-child(3n) {
--poker-card-resting-transform: rotate(-2deg) rotateX(2deg) translateY(0.12rem);
}
.poker-card-inner {
position: relative;
display: block;
width: 100%;
height: 100%;
transform: rotateY(180deg);
transform-style: preserve-3d;
transition: transform 260ms ease;
}
.poker-card-is-face-up {
transform: rotateY(0deg);
}
.poker-card-face {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.25rem;
backface-visibility: hidden;
border: 1px solid color-mix(in srgb, white 36%, transparent);
}
.poker-card-front {
flex-direction: column;
justify-content: space-between;
padding: 0.25rem;
background: var(--card);
color: var(--foreground);
}
.poker-card-center {
font-size: 1.25rem;
line-height: 1;
}
.poker-card-face--light {
background: #f8f6ef;
color: #1d2733;
border-color: rgb(0 0 0 / 30%);
}
.poker-card-front--svg {
padding: 0;
background: transparent;
border-color: transparent;
overflow: hidden;
}
.poker-card-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: fill;
pointer-events: none;
}
.poker-card-back {
transform: rotateY(180deg);
background-color: #101a33;
background-image:
linear-gradient(45deg, rgb(190 42 58 / 34%) 25%, transparent 25%),
linear-gradient(-45deg, rgb(190 42 58 / 34%) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgb(190 42 58 / 34%) 75%),
linear-gradient(-45deg, transparent 75%, rgb(190 42 58 / 34%) 75%);
background-position:
0 0,
0.25rem 0,
0.25rem -0.25rem,
-0.25rem 0.25rem;
background-size: 0.5rem 0.5rem;
}
.poker-card-deal {
--poker-deal-x: 0rem;
--poker-deal-y: -5rem;
animation: poker-card-deal-in 260ms ease both;
}
.poker-seat-arc-left .poker-card-deal,
.poker-seat-arc-far-left .poker-card-deal,
.poker-seat-arc-center-left .poker-card-deal {
--poker-deal-x: clamp(5rem, 18vw, 16rem);
--poker-deal-y: 0rem;
}
.poker-seat-arc-right .poker-card-deal,
.poker-seat-arc-far-right .poker-card-deal,
.poker-seat-arc-center-right .poker-card-deal {
--poker-deal-x: clamp(-16rem, -18vw, -5rem);
--poker-deal-y: 0rem;
}
.poker-seat-arc-center .poker-card-deal {
--poker-deal-y: clamp(4rem, 8vw, 9rem);
}
.poker-card-community .poker-card-deal {
--poker-deal-y: -2rem;
}
.poker-card-viewer-left.poker-card-deal {
--poker-deal-x: clamp(1rem, 4vw, 4rem);
--poker-deal-y: clamp(-20rem, -25vw, -8rem);
}
.poker-card-viewer-right.poker-card-deal {
--poker-deal-x: clamp(-4rem, -4vw, -1rem);
--poker-deal-y: clamp(-20rem, -25vw, -8rem);
}
.poker-countdown-fill {
min-width: 0;
}
.poker-popup-stack {
position: absolute;
top: -0.9rem;
left: 50%;
z-index: 30;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
transform: translateX(-50%);
pointer-events: none;
}
.poker-popup-viewer-spot {
top: auto;
bottom: 13%;
}
.poker-popup-stack-below {
top: calc(100% + 0.5rem);
}
.poker-popup-stack-below .poker-popup {
animation-name: poker-popup-float-down;
}
.poker-popup-announcements {
top: 24%;
z-index: 25;
}
.poker-popup-announcements .poker-popup {
animation-name: poker-popup-float-down;
}
.poker-popup-leave {
color: rgb(255 255 255 / 0.85);
border-color: rgb(255 255 255 / 0.35);
background: rgb(23 23 23 / 0.92);
animation: poker-popup-float-down 3s ease-out forwards;
}
.poker-popup {
animation: poker-popup-float 1.5s ease-out forwards;
border: 1px solid;
border-radius: 0.375rem;
padding: 0.1rem 0.5rem;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap;
}
.poker-popup-action {
color: #bfdbfe;
border-color: rgb(147 197 253 / 0.5);
background: rgb(30 58 138 / 0.85);
}
.poker-popup-wager {
color: #fde68a;
border-color: rgb(252 211 77 / 0.55);
background: rgb(120 53 15 / 0.88);
}
.poker-popup-win {
color: #a7f3d0;
border-color: rgb(110 231 183 / 0.55);
background: rgb(6 78 59 / 0.9);
}
.poker-popup-fold {
color: rgb(255 255 255 / 0.75);
border-color: rgb(255 255 255 / 0.25);
background: rgb(0 0 0 / 0.7);
}
@keyframes poker-popup-float {
0% {
opacity: 0;
transform: translateY(0.6rem) scale(0.7);
}
15% {
opacity: 1;
transform: translateY(0) scale(1.1);
}
30% {
transform: translateY(-0.3rem) scale(1);
}
100% {
opacity: 0;
transform: translateY(-2.75rem) scale(1);
}
}
@keyframes poker-popup-float-down {
0% {
opacity: 0;
transform: translateY(-0.6rem) scale(0.7);
}
15% {
opacity: 1;
transform: translateY(0) scale(1.1);
}
30% {
transform: translateY(0.3rem) scale(1);
}
100% {
opacity: 0;
transform: translateY(2.75rem) scale(1);
}
}
@keyframes poker-popup-fade {
0% {
opacity: 0;
}
20% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes poker-card-deal-in {
from {
opacity: 0;
transform: translate3d(var(--poker-deal-x), var(--poker-deal-y), 0) scale(0.84);
}
to {
opacity: 1;
transform: var(--poker-card-resting-transform);
}
}
@media (max-width: 640px) {
.poker-felt-table::after {
inset: 0.8rem 0.7rem;
}
.poker-seat-arc-left {
left: 4.5%;
}
.poker-seat-arc-right {
right: 4.5%;
}
.poker-seat-arc-far-left {
left: 4%;
}
.poker-seat-arc-far-right {
right: 4%;
}
}
@media (max-width: 760px) {
.poker-action-console {
grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
}
.poker-console-actions {
grid-column: 1 / -1;
border-top: 1px solid color-mix(in srgb, var(--poker-trim) 26%, transparent);
padding-top: 0.65rem;
}
.poker-complete-actions {
align-items: flex-start;
}
}
@media (max-width: 480px) {
.poker-action-console {
grid-template-columns: 1fr;
gap: 0.55rem;
}
.poker-action-console:not(.poker-action-console-complete) {
grid-template-columns: 1fr;
}
.poker-console-economy {
flex-direction: row;
align-items: center;
justify-content: space-between;
border-inline: 0;
border-block: 1px solid color-mix(in srgb, var(--poker-trim) 26%, transparent);
padding-block: 0.55rem;
padding-inline: 0;
}
.poker-console-actions {
grid-column: auto;
border-top: 0;
padding-top: 0;
}
.poker-complete-actions {
flex-direction: column;
}
}
@media (prefers-reduced-motion: reduce) {
.poker-chip {
transition: none;
}
.poker-popup {
animation: poker-popup-fade 1.5s ease-out forwards;
}
.poker-popup-stack-below .poker-popup,
.poker-popup-announcements .poker-popup {
animation-name: poker-popup-fade;
animation-duration: 3s;
}
.poker-seat-stack-flash-up,
.poker-seat-stack-flash-down {
animation: none;
}
.poker-rank-tier-9,
.poker-rank-tier-9::before {
animation: none;
}
.poker-seat-active::after {
animation: none;
box-shadow: 0 0 0.9rem 0.1rem rgb(52 211 153 / 40%);
}
.poker-card-inner,
.poker-card-deal {
animation: none;
transition: none;
}
}
/* Blackjack chip stacks: denomination-colored CSS chip piles. Amounts are
broken into piles (1000/500/100/25/10/5/1), max 5 discs per pile; the
exact number always rides alongside in a text label. Pile heights are set
inline from the chip count so nothing clips or overlaps. */
.bj-chip-stack {
--bj-chip-w: 1.6rem;
--bj-chip-h: 0.95rem;
--bj-chip-step: 0.3rem;
display: inline-flex;
align-items: flex-end;
gap: 0.3rem;
}
/* Compact piles (sm) share the same markup with smaller discs so dense
surfaces like poker's bet pills and console readouts keep their height. */
.bj-chip-stack-sm {
--bj-chip-w: 1.15rem;
--bj-chip-h: 0.68rem;
--bj-chip-step: 0.2rem;
gap: 0.22rem;
}
.bj-chip-pile {
position: relative;
display: inline-block;
width: var(--bj-chip-w, 1.6rem);
}
.bj-chip {
position: absolute;
left: 0;
width: var(--bj-chip-w, 1.6rem);
height: var(--bj-chip-h, 0.95rem);
border: 2px dashed rgb(255 255 255 / 72%);
border-radius: 50%;
box-shadow:
0 2px 3px rgb(0 0 0 / 38%),
inset 0 -2px 3px rgb(0 0 0 / 22%);
animation: bj-chip-pop 180ms ease-out;
transition:
box-shadow 150ms ease,
border-color 150ms ease;
}
/* Hover feedback on a pile: a subtle glow in the chip's own color, hinting
at the denomination tooltip. */
.bj-chip-pile:hover .bj-chip {
border-color: color-mix(in srgb, white 82%, var(--bj-chip-glow, white));
box-shadow:
0 2px 3px rgb(0 0 0 / 38%),
inset 0 -2px 3px rgb(0 0 0 / 22%),
0 0 0.5rem var(--bj-chip-glow, rgb(255 255 255 / 55%)),
0 0 0.2rem var(--bj-chip-glow, rgb(255 255 255 / 55%));
}
/* Inner face ring so the disc reads as a chip, not a squashed circle. */
.bj-chip::after {
content: "";
position: absolute;
inset: 2px 4px;
border: 1.5px solid rgb(255 255 255 / 35%);
border-radius: 50%;
}
.bj-chip-gold {
--bj-chip-glow: rgb(217 164 65 / 60%);
background: #d9a441;
}
.bj-chip-purple {
--bj-chip-glow: rgb(139 92 246 / 60%);
background: #8b5cf6;
}
.bj-chip-black {
--bj-chip-glow: rgb(255 255 255 / 38%);
background: #27272a;
border-color: rgb(255 255 255 / 5%);
}
.bj-chip-green {
--bj-chip-glow: rgb(47 133 90 / 65%);
background: #2f855a;
}
.bj-chip-blue {
--bj-chip-glow: rgb(59 110 165 / 60%);
background: #3b6ea5;
}
.bj-chip-red {
--bj-chip-glow: rgb(185 67 65 / 60%);
background: #b94341;
}
.bj-chip-white {
--bj-chip-glow: rgb(231 224 212 / 55%);
background: #e7e0d4;
border-color: rgb(120 110 95 / 62%);
}
.bj-chip-white::after {
border-color: rgb(120 110 95 / 40%);
}
@keyframes bj-chip-pop {
0% {
opacity: 0;
transform: translateY(-40%) scale(0.6);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* Blackjack readability: bump shared poker-* gameplay text inside the
blackjack felt only; poker tables keep their original sizing. */
.blackjack-felt .poker-console-kicker {
font-size: 0.7rem;
letter-spacing: 0.16em;
}
.blackjack-felt .poker-pot-plaque {
min-width: 6.5rem;
gap: 0.2rem;
padding: 0.35rem 1rem 0.4rem;
}
.blackjack-felt .poker-pot-plaque strong {
font-size: 1.4rem;
}
.blackjack-felt .poker-popup {
padding: 0.15rem 0.65rem;
font-size: 0.8rem;
}
.blackjack-felt .poker-action-log {
gap: 0.3rem;
}
.blackjack-felt .poker-action-log-line {
font-size: 0.82rem;
}
.blackjack-felt .poker-seat-action-badge {
padding: 0.16rem 0.55rem;
font-size: 0.72rem;
}
/* Lift the viewer's hand cluster clear of the bankroll + controls column
(the cluster peaks near 9.7rem when the bankroll pill carries chip piles). */
.blackjack-felt .poker-viewer-cards {
bottom: 10rem;
}
/* Chip flight: every chip starts stacked at one origin (the hand spot) and
launches one after another: payouts toward the bankroll pill, lost bets
toward the dealer. Fill-mode both holds unlaunched chips in the pile. */
.bj-chip-flight {
position: absolute;
bottom: 11rem;
left: 50%;
z-index: 35;
width: 1.6rem;
height: 0.95rem;
transform: translateX(-50%);
pointer-events: none;
}
.bj-chip-flight .bj-chip {
bottom: auto;
left: 0;
animation: bj-chip-fly 700ms ease-in both;
}
.bj-chip-flight-out .bj-chip {
animation-name: bj-chip-fly-out;
}
@keyframes bj-chip-fly {
0% {
opacity: 1;
transform: translateY(0) scale(1);
}
70% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(9rem) scale(0.7);
}
}
@keyframes bj-chip-fly-out {
0% {
opacity: 1;
transform: translateY(0) scale(1);
}
70% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(-18rem) scale(0.7);
}
}
@media (prefers-reduced-motion: reduce) {
.bj-chip-flight {
display: none;
}
}
/* Fullscreen contain-fit: absolute with zero insets + auto margins sizes the
felt to the largest 4:3 rectangle inside the viewport area (the same rules
browsers use for replaced elements), so it can never collapse to content
size and never overflow. Must stay after .poker-felt-table to win the
width tie. */
.blackjack-felt-fullscreen {
position: absolute;
inset: 0;
margin: auto;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
aspect-ratio: 4 / 3;
}
/* Fixed Chip Bank card: dark panel, delta popup that lands large and scales
down before fading, and a sign-colored flash across the card on movement. */
.chip-bank-card {
background: rgb(0 0 0 / 55%);
backdrop-filter: blur(6px);
}
.chip-bank-delta {
animation: chip-bank-delta-pop 2.5s ease-out forwards;
}
@keyframes chip-bank-delta-pop {
0% {
opacity: 1;
transform: scale(1.7);
}
30% {
opacity: 1;
transform: scale(1);
}
75% {
opacity: 1;
}
100% {
opacity: 0;
transform: scale(1);
}
}
.chip-bank-flash-up {
animation: chip-bank-flash-up-anim 1.2s ease-out;
}
.chip-bank-flash-down {
animation: chip-bank-flash-down-anim 1.2s ease-out;
}
@keyframes chip-bank-flash-up-anim {
0% {
background: rgb(16 185 129 / 30%);
border-color: #10b981;
}
100% {
background: rgb(0 0 0 / 55%);
border-color: var(--border);
}
}
@keyframes chip-bank-flash-down-anim {
0% {
background: rgb(244 63 94 / 28%);
border-color: #f43f5e;
}
100% {
background: rgb(0 0 0 / 55%);
border-color: var(--border);
}
}
@media (prefers-reduced-motion: reduce) {
.chip-bank-delta {
animation: none;
}
.chip-bank-flash-up,
.chip-bank-flash-down {
animation: none;
}
}
/* Chip Bank card: small spinning indicator shown while waiting for the chip
bank to refresh after the player leaves a table. */
.chip-bank-spinner {
@apply size-5 animate-spin rounded-full border-[3px] border-white/20 border-t-white/80;
}
@media (prefers-reduced-motion: reduce) {
.bj-chip {
animation: none;
}
}
/* World map: dossier marker family (structure / node / shipment pins). */
/* World map: give the Leaflet container its own stacking context so its panes
(z-index 200-1000, applied in the root stacking context otherwise) stop
painting over Radix dropdown portals (body-level, z-50) on the map page. */
.leaflet-container {
isolation: isolate;
}
/* Map placement/authoring modes: crosshair cursor while a click mode is
active (MapContainer className is mount-only, so this lives on CSS). */
.map-host[data-mode="crosshair"] .leaflet-container {
cursor: crosshair;
}
/* World map: structure / construction-site markers (status-colored pin with an
inline SVG icon and a progress bar for sites under construction). The same
pin base is shared by the node and shipment badges. */
.map-structure-icon {
background: transparent;
border: none;
}
.map-structure-pin {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
border-radius: 2px;
background: rgb(8 10 14 / 90%);
border: 1.5px solid rgb(255 255 255 / 25%);
box-shadow:
0 0 0 1.5px rgb(0 0 0 / 65%),
0 1px 4px rgb(0 0 0 / 60%);
}
.map-structure-pin.map-pin-sm {
width: 20px;
height: 20px;
border-radius: 1.5px;
}
.map-structure-pin.map-pin-md {
width: 32px;
height: 32px;
}
.map-structure-pin.map-pin-lg {
width: 38px;
height: 38px;
}
.map-structure-ribbon {
position: absolute;
left: calc(100% + 6px);
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 1px;
padding: 2px 8px 3px;
border: 1px solid rgb(255 255 255 / 15%);
border-left-width: 2px;
border-radius: 1px;
background: rgb(8 10 14 / 88%);
box-shadow: 0 1px 4px rgb(0 0 0 / 50%);
white-space: nowrap;
}
.map-structure-ribbon .ribbon-name {
font-size: 11px;
font-weight: 600;
color: rgb(255 255 255 / 95%);
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
}
.map-structure-ribbon .ribbon-meta {
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.12em;
color: rgb(255 255 255 / 65%);
}
.map-tooltip-dossier .map-tooltip-name {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.08em;
color: rgb(255 255 255 / 95%);
overflow-wrap: anywhere;
}
/* Must out-rank leaflet.css, which styles the SAME selector later (it loads
in the MapClient chunk after styles.css) with a white background. */
.map-host .leaflet-container .leaflet-control-attribution {
background: rgb(8 10 14 / 92%);
color: rgb(255 255 255 / 55%);
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 9px;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 1px 6px;
border: 1px solid rgb(255 255 255 / 15%);
border-radius: 0;
}
.leaflet-container .leaflet-control-attribution a {
color: rgb(148 163 184 / 85%);
}
.map-tooltip-dossier .map-tooltip-meta {
margin-top: 1px;
font-size: 9px;
letter-spacing: 0.12em;
color: rgb(255 255 255 / 60%);
}
/* A hovered structure (pin or ribbon) jumps above every other marker. The
!important is required to beat Leaflet's inline z-index management. */
.map-structure-icon.leaflet-marker-icon:hover {
z-index: 1200 !important;
}
.map-vertex-handle {
width: 12px;
height: 12px;
border: 1.5px solid #f8fafc;
border-radius: 2px;
background: rgb(8 10 14 / 75%);
box-shadow: 0 0 0 1px rgb(0 0 0 / 60%);
}
.map-midpoint-handle {
width: 8px;
height: 8px;
border: 1.5px solid #22d3ee;
border-radius: 1px;
background: rgb(8 10 14 / 40%);
opacity: 0.85;
}
.map-snap-preview {
width: 12px;
height: 12px;
margin-left: -6px;
margin-top: -6px;
border-radius: 2px;
border: 1.5px solid;
pointer-events: none;
}
.map-snap-vertex {
border-color: #34d399;
background: rgb(52 211 153 / 25%);
}
.map-snap-edge {
border-color: #22d3ee;
background: rgb(34 211 238 / 25%);
}
.map-structure-progress {
position: absolute;
left: 3px;
right: 3px;
bottom: -6px;
height: 3px;
overflow: hidden;
border-radius: 2px;
background: rgb(255 255 255 / 30%);
}
.map-structure-progress > div {
height: 100%;
background: #ffffff;
}
/* World map: dossier-style Leaflet chrome. Tooltips and popups are Leaflet DOM
(not shadcn), so they get the tactical card treatment here: dark glass,
hairline borders, squared corners, mono uppercase labels. */
.leaflet-container .map-tooltip-dossier.leaflet-tooltip {
background: rgb(0 0 0 / 82%);
backdrop-filter: blur(6px);
border: 1px solid rgb(255 255 255 / 25%);
border-radius: 0;
box-shadow: 0 2px 8px rgb(0 0 0 / 65%);
color: rgb(255 255 255 / 85%);
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 10px;
letter-spacing: 0.16em;
padding: 4px 8px;
text-transform: uppercase;
width: max-content;
max-width: min(280px, 60vw);
white-space: normal;
overflow-wrap: anywhere;
}
.leaflet-container .map-tooltip-dossier.leaflet-tooltip-top::before {
border-top-color: rgb(255 255 255 / 25%);
}
.leaflet-container .map-tooltip-dossier.leaflet-tooltip-bottom::before {
border-bottom-color: rgb(255 255 255 / 25%);
}
.leaflet-container .map-tooltip-dossier.leaflet-tooltip-left::before {
border-left-color: rgb(255 255 255 / 25%);
}
.leaflet-container .map-tooltip-dossier.leaflet-tooltip-right::before {
border-right-color: rgb(255 255 255 / 25%);
}
.leaflet-container .leaflet-popup-content-wrapper {
background: rgb(8 10 14 / 92%);
backdrop-filter: blur(8px);
border: 1px solid rgb(255 255 255 / 20%);
border-radius: 0;
box-shadow: 0 4px 16px rgb(0 0 0 / 65%);
color: rgb(255 255 255 / 90%);
}
.leaflet-container .leaflet-popup-content {
margin: 10px 12px;
line-height: 1.45;
}
.leaflet-container .leaflet-popup-tip {
background: rgb(8 10 14 / 92%);
box-shadow: none;
}
.leaflet-container a.leaflet-popup-close-button {
color: rgb(255 255 255 / 50%);
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #ffffff;
}
/* Dossier popups (structure/shipment cards): zero wrapper padding so the
classification strip and readout footer run edge to edge. */
.leaflet-container .map-popup-dossier .leaflet-popup-content {
margin: 0;
width: 100% !important;
}
/* World map: persistent map labels (structures, zones, roads) in the dossier
style: mono uppercase, tracked, dark halo instead of a box. */
.leaflet-container .map-label.leaflet-tooltip {
background: rgb(8 10 14 / 62%);
border: none;
box-shadow: none;
border-radius: 1px;
padding: 1px 5px;
color: rgb(255 255 255 / 92%);
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
text-shadow:
0 0 3px rgb(0 0 0 / 95%),
0 0 6px rgb(0 0 0 / 85%);
white-space: nowrap;
}
.leaflet-container .map-label.leaflet-tooltip::before {
display: none;
}
.map-zone-label-icon,
.map-road-label-icon {
background: transparent;
border: none;
}
.map-zone-label {
color: rgb(255 255 255 / 90%);
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 10px;
letter-spacing: 0.2em;
text-transform: uppercase;
white-space: nowrap;
/* Size to the text itself: inside the 0x0 divIcon container a block div
resolves to 0 width, which makes the percentage centering transform a
no-op and leaves the label hanging off the road. */
width: max-content;
background: rgb(8 10 14 / 62%);
padding: 1px 5px;
border-radius: 1px;
text-shadow:
0 0 3px rgb(0 0 0 / 95%),
0 0 7px rgb(0 0 0 / 90%),
0 0 2px rgb(0 0 0 / 90%);
pointer-events: none;
transform: translate(-50%, -50%);
}
.map-road-label-svg {
overflow: visible;
}
.map-road-label {
color: rgb(255 255 255 / 94%);
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 11px;
letter-spacing: 0.2em;
text-transform: uppercase;
white-space: nowrap;
/* Size to the text itself: inside the 0x0 divIcon container a block div
resolves to 0 width, which makes the percentage centering transform a
no-op and leaves the label hanging off the road. */
width: max-content;
background: rgb(8 10 14 / 62%);
padding: 1px 5px;
border-radius: 1px;
text-shadow:
0 0 3px rgb(0 0 0 / 95%),
0 0 7px rgb(0 0 0 / 90%),
0 0 2px rgb(0 0 0 / 90%);
pointer-events: none;
}
.map-road-label-path {
fill: none;
stroke: none;
}
.map-road-label-text {
fill: rgb(255 255 255 / 94%);
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
font-size: 11px;
letter-spacing: 0.2em;
/* Dark casing behind the glyphs (painted under the fill) for contrast on
any terrain. */
stroke: rgb(0 0 0 / 82%);
stroke-width: 4px;
paint-order: stroke;
stroke-linejoin: round;
}
.leaflet-container .map-zone-label-icon,
.leaflet-container .map-road-label-icon {
pointer-events: none;
}
/* Radix floating panels (Select/DropdownMenu/Popover/Tooltip) portal to <body>
and would otherwise paint below the map page's high-z overlays (authoring
toolbar z-[1000], structure drawer z-[1200]). Pin every popper wrapper above
them. */
[data-radix-popper-content-wrapper] {
z-index: 1300 !important;
}
/* Book view (story reader): body prose reads left-aligned like a real book.
Center alignment authored on paragraphs in the Lexical editor renders as
inline text-align styles, which only an !important rule can override, so
the rule is scoped to the paginated Book view body (the immersive reader
keeps the authored alignment). */
.book-view-body,
.book-view-body [style*="text-align"] {
text-align: left !important;
}