/* Smooth, springy transitions for the app's CONTROLS. A stage piece is excluded: a keyed piece is moved by the
   stage engine, which writes its transform and its in-flight cloak as instantaneous style changes and reads them
   straight back (js/tat-stage.js). Unexcluded, this reached every piece that happens to be a button — Azul's
   source tiles, 6 nimmt!'s hand cards — and turned each of those writes into a cross-fade the engine never asked
   for. See docs/design/client-animation.md → Pieces are not transitioned.

   THE EXCLUSION IS WRAPPED IN :where(), AND IT HAS TO BE. This is a blanket rule that every more specific
   selector in the app is written to beat: a type selector is (0,0,1), and each of the dozens of single-class
   button transitions below it — .tat-piece--interactive's motion-gated one in app.css, .sa-space's, .dom-pile's,
   .game-room__undo's — is (0,1,0) and wins. A bare `:not([data-piece-id])` takes its argument's specificity, so
   the rule becomes (0,1,1) and silently outranks all of them: measured, .sa-space lost its own border-color and
   background-color easing and gained a transform one, and every motion-gated transition stopped collapsing under
   reduced motion. :where() contributes nothing, so the rule keeps its place at the bottom of the cascade and only
   its subject narrows. */
button:where(:not([data-piece-id])) {
    transition: transform var(--tat-ui-quick) var(--tat-ease-spring),
                filter var(--tat-ui-quick) ease,
                box-shadow var(--tat-ui-base) ease,
                background var(--tat-ui-base) ease,
                opacity var(--tat-ui-base) ease;
}

button:active:not(:disabled) {
    transform: scale(0.96);
}

button:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* Custom scrollbar. The thumb reads its colour from the canvas it is on — a navy wash is invisible on the
   shell's navy and a pale one is invisible on the table's white — so both tones are tokens (app.css), and a
   scroll surface inside the match surface picks up the table's pair by inheritance. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb) transparent;
}

::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb);
    border-radius: 3px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--scrollbar-thumb-hover);
}

/* Selection */
::selection {
    background: color-mix(in srgb, var(--accent-blue) 22%, transparent);
}

/* Blazor reconnect dialog */
#components-reconnect-modal {
    display: none;
}

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-rejecting {
    display: block;
}

#components-reconnect-modal.components-reconnect-hide {
    display: none;
}

#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: block;
}

/* Show/hide reconnect content based on state */
#components-reconnect-modal .reconnect-attempting {
    display: flex;
}

#components-reconnect-modal .reconnect-failed {
    display: none;
}

#components-reconnect-modal.components-reconnect-failed .reconnect-attempting,
#components-reconnect-modal.components-reconnect-rejected .reconnect-attempting {
    display: none;
}

#components-reconnect-modal.components-reconnect-failed .reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected .reconnect-failed {
    display: flex;
}

/* Blazor error UI.
   When the Blazor WebAssembly runtime reveals this bar (inline display:block) it spans the full bottom band
   at the top of the stacking order, which would intercept pointer events on content scrolled beneath it and
   let one error cascade into unrelated click timeouts. Click-through (pointer-events:none) except its own
   Reload/Dismiss actions below, so a click on underlying content lands on that content (#844). */
#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    width: 100%;
    padding: 0.75rem 1rem;
    background: var(--accent-red, #ef4444);
    color: var(--text-on-accent);
    /* Framework error bar: a hard ceiling above the z-index tier scale, kept literal so it beats every overlay unconditionally. */
    z-index: 1000;
    font-size: 0.875rem;
    pointer-events: none;
}

/* Re-enable hit-testing on this action against the click-through bar (#844). */
#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 1rem;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: auto;
}

/* Re-enable hit-testing on this action against the click-through bar (#844). */
#blazor-error-ui .reload {
    color: var(--text-on-accent);
    text-decoration: underline;
    margin-left: 0.5rem;
    pointer-events: auto;
}

/* ===========================================================================
   Motion toolkit — named entrance + idle keyframes (Supercell-style juice)
   Spring easing gives a slight overshoot so things "land" with weight. Keep
   durations snappy; UIs should feel alive, never sluggish. The @keyframes below
   are applied by name throughout the chrome and game stylesheets; .animate-fade-in
   is the sole convenience class.
   =========================================================================== */
:root {
    /* The overshoot easing, under the name the chrome sheets read. Its value is --tat-ease-spring (app.css), so
       the whole easing family is retuned in one place; substitution is lazy, so this resolves against :root at
       use whichever sheet loaded first. */
    --ease-spring: var(--tat-ease-spring);
    /* Chunky rounded display face for hero numbers, titles and primary CTAs. Exposed as a token so the
       framework stylesheets (the animation stage's popup/banner) can share the same stack. */
    --font-display: 'Lilita One', 'Inter', system-ui, sans-serif;
    /* Monospace stack for keycap/shortcut hints (e.g. the room's menu-key glyph). Consumed by app.css
       via var(--font-mono). */
    --font-mono: ui-monospace, 'SFMono-Regular', 'Cascadia Mono', 'Consolas', monospace;
    /* Z-index tier scale — the global stacking order for chrome, overlays, dialogs and tooltips.
       Declared in the first-loaded static sheet so every consumer resolves the same tier. A
       hand-written global-tier z-index reads one of these tokens; an integer offset that must sit
       one rung above/below a tier is calc() against it (e.g. calc(var(--z-room-overlay) + 1)).
       Board-local stacking (z-index < 10 inside a game's own transformed board) stays literal — it
       never participates in the shell's global context. See docs/design/theming.md → Layering. */
    --z-chrome: 20;
    --z-menu: 30;
    --z-mobile-nav: 40;
    --z-modal: 50;
    --z-match: 54;
    --z-room: 55;
    --z-room-overlay: 60;
    --z-tip: 66;
    --z-reading: 70;
    --z-modal-top: 100;
}

/* Chunky rounded display face for hero numbers, titles and primary CTAs.
   Body copy stays Inter (set globally in App.razor). */
.font-display {
    font-family: var(--font-display);
    letter-spacing: 0.01em;
}

@keyframes mp-pop-in {
    0%   { opacity: 0; transform: scale(0.85); }
    60%  { opacity: 1; transform: scale(1.03); }
    100% { opacity: 1; transform: scale(1); }
}

@keyframes mp-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes mp-slide-up {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes mp-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-6px); }
}

@keyframes mp-count-pop {
    0%   { transform: scale(1.18); }
    100% { transform: scale(1); }
}

@keyframes mp-shake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-5px); }
    40% { transform: translateX(5px); }
    60% { transform: translateX(-3px); }
    80% { transform: translateX(3px); }
}

.animate-fade-in   { animation: mp-fade-in 0.20s ease both; }

/* Accessibility: honour reduced-motion. The fade-in entrance resolves instantly
   (the both fill keeps the final state). */
@media (prefers-reduced-motion: reduce) {
    .animate-fade-in {
        animation-duration: 0.001s;
    }
}
