/* ── Global helpers mirrored from the Figma "Online Mahjong" design ──────────────
   Colors are driven by the MudBlazor theme (MahjongTheme). These utilities add the
   textures/animations the Mud theme can't express: felt background, the live
   pulse-ring, the floating decorative tiles, and themed browser surfaces. */

:root {
    /* Shared felt tokens (single source of truth; also referenced by the game board). */
    --felt-bg: #0a1510;
    --felt-center: #162b1c;
    --felt-edge: #0a1510;
    --table-rim: #06120c;
    --gold: #c9a227;
    /* Corner radius of a mahjong tile face (the tile SVGs round their face at ~13% of the
       tile width). Buttons reuse it so they read as tiles; the board restates it locally. */
    --tile-radius: 0.4rem;
    /* Raised felt surface for bars, headers and inputs (the Mud "secondary" is a light
       sage foreground colour — never use it as a background). */
    --felt-surface: #162b1c;
    /* Seating/pre-game felt, a shade lighter than the play felt. */
    --felt-seat: #1a3a22;
    /* The "whose turn" highlight on seats, countdowns and the drawn tile. */
    --turn-amber: #ffc742;
    /* Minimum touch target (WCAG 2.5.5 / Apple HIG). */
    --tap-min: 44px;
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 16px;
    --font-serif: "Noto Serif Display", Georgia, serif;
    --font-mono: "JetBrains Mono", ui-monospace, monospace;
}

/* Deep-green felt with soft radial lighting. */
.felt-texture {
    background-color: var(--felt-bg);
    background-image:
        radial-gradient(ellipse at 20% 50%, rgba(22, 43, 28, 0.6) 0%, transparent 60%),
        radial-gradient(ellipse at 80% 20%, rgba(18, 32, 24, 0.8) 0%, transparent 50%);
}

/* ── Browser surfaces ────────────────────────────────────────────────────────
   Selection, caret and keyboard focus carry the felt/gold palette instead of the
   browser's blue defaults. The gold ring (#c9a227 ≈ 7:1 on the felt) is the single
   site-wide :focus-visible indicator for links and buttons; MudBlazor inputs keep
   their own outlined/underline focus state. With that ring guaranteed, the grey
   mobile tap flash is suppressed. */
::selection {
    background: rgba(201, 162, 39, 0.35);
    color: var(--mud-palette-text-primary, #e8dfc8);
}

input,
textarea,
[contenteditable="true"] {
    caret-color: var(--gold);
}

a,
button,
summary,
[role="button"],
label[for] {
    -webkit-tap-highlight-color: transparent;
}

a:focus-visible,
button:focus-visible,
summary:focus-visible,
[role="button"]:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* Skip link (MainLayout): off-screen until focused, then pinned top-left above the header. */
.skip-link {
    position: absolute;
    top: 0.5rem;
    left: 0.5rem;
    z-index: 1400;
    display: inline-flex;
    align-items: center;
    min-height: var(--tap-min);
    padding: 0 1rem;
    border-radius: var(--tile-radius);
    background: var(--mud-palette-primary, #c9a227);
    color: var(--mud-palette-primary-text, #0a1510);
    font-weight: 600;
    text-decoration: none;
    transform: translateY(-200%);
}

.skip-link:focus,
.skip-link:focus-visible {
    transform: none;
    outline: 2px solid var(--mud-palette-text-primary, #e8dfc8);
    outline-offset: 2px;
}

/* The skip-link target is focusable only programmatically; no ring on the whole page. */
#main-content:focus {
    outline: none;
}

/* Visually hidden, still announced (labels for icon-only controls and table headers). */
.visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Pulsing ring for "live"/active-turn indicators. */
.live-dot {
    animation: mj-pulse-ring 2s ease-out infinite;
}

@keyframes mj-pulse-ring {
    0% { box-shadow: 0 0 0 0 rgba(201, 162, 39, 0.4); }
    70% { box-shadow: 0 0 0 12px rgba(201, 162, 39, 0); }
    100% { box-shadow: 0 0 0 0 rgba(201, 162, 39, 0); }
}

/* No focus/selection outline on the (non-interactive) hero heading. */
.hero-title,
.hero-title span {
    outline: none;
    border: none;
    -webkit-tap-highlight-color: transparent;
}

.hero-title:focus,
.hero-title:focus-visible {
    outline: none;
}

/* Gentle bobbing for decorative tiles; --r lets each tile keep a fixed rotation. */
@keyframes mj-float {
    0%, 100% { transform: translateY(0) rotate(var(--r, 0deg)); }
    50% { transform: translateY(-8px) rotate(var(--r, 0deg)); }
}

.tile-float { animation: mj-float 3s ease-in-out infinite; }
.tile-float-2 { animation: mj-float 3.7s ease-in-out infinite 0.5s; }
.tile-float-3 { animation: mj-float 4.2s ease-in-out infinite 1s; }

/* Slow, ambient drift for the scattered background tiles — gentle multi-axis
   movement with a slight rotation wobble around each tile's base --r. These
   .hero-tile.* selectors outrank the bare .tile-float* rules above, so only the
   background tiles drift; the centerpiece fan keeps the calmer mj-float bob. */
@keyframes mj-drift-1 {
    0%, 100% { transform: translate(0, 0) rotate(var(--r, 0deg)); }
    33%      { transform: translate(6px, -12px) rotate(calc(var(--r, 0deg) + 2deg)); }
    66%      { transform: translate(-5px, -6px) rotate(calc(var(--r, 0deg) - 1.5deg)); }
}

@keyframes mj-drift-2 {
    0%, 100% { transform: translate(0, 0) rotate(var(--r, 0deg)); }
    30%      { transform: translate(-7px, -9px) rotate(calc(var(--r, 0deg) - 2deg)); }
    70%      { transform: translate(4px, -14px) rotate(calc(var(--r, 0deg) + 1.5deg)); }
}

@keyframes mj-drift-3 {
    0%, 100% { transform: translate(0, 0) rotate(var(--r, 0deg)); }
    40%      { transform: translate(5px, -8px) rotate(calc(var(--r, 0deg) + 1deg)); }
    75%      { transform: translate(-6px, -13px) rotate(calc(var(--r, 0deg) - 1deg)); }
}

.hero-tile.tile-float   { animation: mj-drift-1 13s ease-in-out infinite; }
.hero-tile.tile-float-2 { animation: mj-drift-2 16s ease-in-out infinite 1s; }
.hero-tile.tile-float-3 { animation: mj-drift-3 11s ease-in-out infinite 0.5s; }

/* Monospace, wide-tracked uppercase eyebrow labels. */
.mono-eyebrow {
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: 0.2em;
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--gold);
}

@media (prefers-reduced-motion: reduce) {
    .live-dot, .tile-float, .tile-float-2, .tile-float-3,
    .hero-tile.tile-float, .hero-tile.tile-float-2, .hero-tile.tile-float-3 {
        animation: none;
    }
}

/* Screen-reader mode (App.razor stamps data-sr-mode="true" on <html> from the srMode cookie) forces
   reduced motion everywhere, scoped component animations and transitions included, so pulsing tiles
   and sliding panels don't compete with the spoken announcements. */
html[data-sr-mode="true"] *,
html[data-sr-mode="true"] *::before,
html[data-sr-mode="true"] *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
}

/* ── Snackbars ───────────────────────────────────────────────────────────────
   MudBlazor renders each snackbar with a severity class (mud-alert-filled/text-*)
   whose bright, fully-saturated fill (or the grey "normal" default) clashes with
   the muted dark-green/parchment UI. Re-skin every severity onto the app surface
   with parchment text and a severity-coloured left accent, so notifications read
   as part of the same theme. Colours all come from the emitted --mud-palette-*. */
.mud-snackbar[class*="mud-alert-"] {
    background-color: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border: 1px solid var(--mud-palette-lines-default);
    border-left-width: 4px;
    border-radius: var(--mud-default-borderradius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}

.mud-snackbar[class*="-info"]    { border-left-color: var(--mud-palette-info); }
.mud-snackbar[class*="-success"] { border-left-color: var(--mud-palette-success); }
.mud-snackbar[class*="-warning"] { border-left-color: var(--mud-palette-warning); }
.mud-snackbar[class*="-error"]   { border-left-color: var(--mud-palette-error); }
.mud-snackbar[class*="-normal"],
.mud-snackbar[class*="-dark"]    { border-left-color: var(--mud-palette-primary); }

/* The leading severity icon keeps the accent colour; the close button inherits text. */
.mud-snackbar[class*="-info"]    .mud-snackbar-icon { color: var(--mud-palette-info); }
.mud-snackbar[class*="-success"] .mud-snackbar-icon { color: var(--mud-palette-success); }
.mud-snackbar[class*="-warning"] .mud-snackbar-icon { color: var(--mud-palette-warning); }
.mud-snackbar[class*="-error"]   .mud-snackbar-icon { color: var(--mud-palette-error); }
.mud-snackbar[class*="-normal"]  .mud-snackbar-icon,
.mud-snackbar[class*="-dark"]    .mud-snackbar-icon { color: var(--mud-palette-primary); }

/* On the in-play board, snackbars (move announcements, server notices) match the seat
   nameplates (Side.razor.css): a translucent dark pill with a hairline border, the severity
   carried only by the leading icon. A win gets the active seat's gold edge and glow. */
body:has(.board-frame:not(.embedded)) .mud-snackbar[class*="mud-alert-"] {
    width: auto;
    min-width: 0;
    max-width: min(26rem, 90vmax);
    padding: 0.3rem 0.9rem 0.3rem 0.7rem;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: rgba(0, 0, 0, 0.6);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    color: rgba(255, 255, 255, 0.88);
    font-size: 0.85rem;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}

body:has(.board-frame:not(.embedded)) .mud-snackbar[class*="-success"] {
    border-color: var(--gold);
    box-shadow: 0 0 14px rgba(201, 162, 39, 0.45);
}

body:has(.board-frame:not(.embedded)) .mud-snackbar .mud-snackbar-icon {
    margin-inline-end: 0.5rem;
}

body:has(.board-frame:not(.embedded)) .mud-snackbar .mud-snackbar-icon .mud-icon-root {
    font-size: 1.1rem;
    width: 1.1rem;
    height: 1.1rem;
}

body:has(.board-frame:not(.embedded)) .mud-snackbar .mud-snackbar-content-message {
    padding: 0;
}

/* ── Rotated game board overlays ─────────────────────────────────────────────
   In portrait the in-play board rotates 90° clockwise into a landscape felt
   (Board.razor.css). The connection pill and the snackbars are position:fixed
   outside that frame, so turn them with it: each keeps its landscape corner and
   reads along the felt. Keyed on the rotated board being on the page, so the
   upright table page is untouched. Each rotates about the corner it is anchored
   to, and the pre-rotation translate swings the box back on-screen. */
@media (orientation: portrait) {
    /* Landscape bottom-right → screen bottom-left. */
    body:has(.board-frame:not(.embedded)) .conn-status {
        right: auto;
        bottom: calc(env(safe-area-inset-bottom, 0px) + 0.75rem);
        left: calc(env(safe-area-inset-left, 0px) + 0.5rem);
        transform-origin: bottom left;
        transform: rotate(90deg) translateX(-100%);
    }

    /* Any corner → landscape top-right, which is screen bottom-right. */
    body:has(.board-frame:not(.embedded)) #mud-snackbar-container {
        top: auto;
        left: auto;
        right: 24px;
        bottom: 24px;
        transform-origin: bottom right;
        transform: rotate(90deg) translateY(100%);
    }
}

/* ── Account menu ────────────────────────────────────────────────────────────
   Lock background scrolling while the account-menu popover is open, the way
   YouTube freezes the page behind its menus. The native popover has a stable id,
   so a global :has() rule can react to :popover-open without any JavaScript. */
html:has(#usermenu-pop:popover-open) {
    overflow: hidden;
}

/* ── Buttons ─────────────────────────────────────────────────────────────────
   Every text button is rounded like a mahjong tile (--tile-radius). Scoped to buttons
   only: the theme's 4px DefaultBorderRadius still rounds inputs, cards and snackbars.
   MudIconButton (.mud-icon-button) is already circular; button.btn covers the native
   submit buttons on the Identity pages that carry leftover Bootstrap classes. */
.mud-button-root.mud-button,
button.btn {
    border-radius: var(--tile-radius);
}

/* ── Dialogs ─────────────────────────────────────────────────────────────────
   MudBlazor rounds the dialog surface with the theme's small default radius (4px).
   Give every dialog a softer, more modern rounded corner by default. */
.mud-dialog {
    border-radius: 12px;
}

/* ── Table settings popover ──────────────────────────────────────────────────
   The Table route's game-config form lives inside a MudPopover, which portals its
   content out of the page's DOM subtree. Scoped .razor.css can't reach MudBlazor
   component roots there (MudPaper, MudText), so their styling is global. */
.table-settings {
    box-sizing: border-box;
    padding: clamp(0.75rem, 3vw, 1.25rem);
    text-align: left;
    background: var(--mud-palette-surface);
}

.table-settings-title {
    font-family: var(--font-serif);
    font-weight: 700;
    display: block;
    margin-bottom: 0.25rem;
}

/* A submit held by <turnstile-widget> until its bot-check token arrives (Turnstile.razor.js). */
form.turnstile-pending [aria-busy="true"] {
    cursor: progress;
    opacity: 0.7;
}

/* Pages with a back-to-top link (ScrollToTop.razor) scroll to it smoothly, unless the visitor asks for less motion.
   Scoped to those pages so the rest of the app, the game board included, keeps its instant scrolling. */
@media (prefers-reduced-motion: no-preference) {
    html:has(.scroll-to-top) {
        scroll-behavior: smooth;
    }
}
