diff --git a/public/assets/chaching.mp3 b/public/assets/chaching.mp3 new file mode 100644 index 0000000..04abc8e Binary files /dev/null and b/public/assets/chaching.mp3 differ diff --git a/src/app/(frontend)/styles.css b/src/app/(frontend)/styles.css index 303943e..4121c07 100644 --- a/src/app/(frontend)/styles.css +++ b/src/app/(frontend)/styles.css @@ -565,6 +565,126 @@ 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; @@ -1217,6 +1337,47 @@ 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%); +} + .poker-felt-fullscreen { min-height: max(76vh, 42rem); border-radius: 50% / 48%; @@ -1262,8 +1423,8 @@ .poker-seat-avatar { display: flex; - width: 1.9rem; - height: 1.9rem; + width: 2.4rem; + height: 2.4rem; align-items: center; justify-content: center; border: 2px solid color-mix(in srgb, white 52%, transparent); @@ -1273,7 +1434,7 @@ 0 0.2rem 0.45rem rgb(0 0 0 / 34%); color: white; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.8rem; + font-size: 1rem; font-weight: 700; } @@ -1316,15 +1477,15 @@ gap: 0.3rem; color: #f3d28b; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 1.1rem; + font-size: 1.35rem; font-variant-numeric: tabular-nums; font-weight: 700; line-height: 1.15; } .poker-seat-stack-chip { - width: 0.7rem; - height: 0.7rem; + width: 0.85rem; + height: 0.85rem; flex: 0 0 auto; border: 1px solid #d6b36a; border-radius: 50%; @@ -1379,10 +1540,10 @@ .poker-seat-action-badge { border: 1px solid color-mix(in srgb, var(--poker-trim) 60%, transparent); border-radius: 9999px; - padding: 0.08rem 0.35rem; + padding: 0.12rem 0.45rem; background: color-mix(in srgb, var(--poker-trim) 14%, transparent); color: #f3d28b; - font-size: 0.55rem; + font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em; line-height: 1.2; @@ -1893,6 +2054,75 @@ 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. */ @@ -2547,9 +2777,315 @@ 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 { + display: inline-flex; + align-items: flex-end; + gap: 0.3rem; +} + +.bj-chip-pile { + position: relative; + display: inline-block; + width: 1.6rem; +} + +.bj-chip { + position: absolute; + left: 0; + width: 1.6rem; + height: 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; + } +}