/* Smooth, springy transitions */
button {
    transition: transform 0.12s cubic-bezier(0.34, 1.56, 0.64, 1),
                filter 0.12s ease,
                box-shadow 0.15s ease,
                background 0.15s ease,
                opacity 0.15s ease;
}

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

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

/* Custom scrollbar */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

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

::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 3px;
}

::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.2);
}

/* Selection */
::selection {
    background: rgba(59, 130, 246, 0.3);
}

/* Blazor error UI */
#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    width: 100%;
    padding: 0.75rem 1rem;
    background: var(--accent-red, #ef4444);
    color: white;
    z-index: 1000;
    font-size: 0.875rem;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 1rem;
    top: 50%;
    transform: translateY(-50%);
}

#blazor-error-ui .reload {
    color: white;
    text-decoration: underline;
    margin-left: 0.5rem;
}

/* ===========================================================================
   Motion toolkit: reusable entrance and idle animations, used by the base
   components (Modal) and the game UI through the .animate-* utility classes.
   Spring easing gives a slight overshoot so things land with weight.
   =========================================================================== */
:root {
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

    /* The app's stacking order, declared in one place because the layers that contend are written in
       different stylesheets and belong to different owners. Without this the order between a game's dialog
       and the connection shell is decided by which component happens to render last, which is not something
       either of them can see.

       The connection shell sits above everything a game can raise, and that is the rule rather than a
       preference: a game screen that outranked it would put its own spinner over "Connection lost" and leave
       the player with two contradictory stories about whether the app is live. */
    --layer-topbar:       50;
    --layer-game-overlay: 60;   /* a game's own overlay, over the screen it belongs to */
    --layer-dialog:       65;   /* a modal question, over a game overlay */
    --layer-conn-pill:    75;
    --layer-conn-scrim:   80;
}

* {
    font-family: 'Inter', system-ui, sans-serif;
}

/* Chunky rounded display face for hero numbers, titles and primary CTAs. Body copy stays Inter. */
.font-display {
    font-family: 'Lilita One', 'Inter', system-ui, sans-serif;
    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-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-6px); }
}

.animate-pop-in    { animation: mp-pop-in 0.30s var(--ease-spring) both; }
.animate-fade-in   { animation: mp-fade-in 0.20s ease both; }
.animate-float     { animation: mp-float 4.5s ease-in-out infinite; }

/* Accessibility: honour reduced-motion. Entrance animations resolve instantly
   (both/forwards keeps the final state); idle loops are silenced. */
@media (prefers-reduced-motion: reduce) {
    .animate-pop-in,
    .animate-fade-in {
        animation-duration: 0.001s;
    }
    .animate-float {
        animation: none;
    }
}

/* ===========================================================================
   Connection shell — what the app shows when the link to the server is not
   working (ConnectionShell.razor). Two shapes: a quiet pill for a lapse around
   a tab switch, and a modal that covers the app, including the top bar, so a
   board that is no longer live cannot be tapped.
   =========================================================================== */
.mp-conn-pill {
    position: fixed;
    top: calc(env(safe-area-inset-top) + 3.25rem);
    left: 50%;
    transform: translateX(-50%);
    z-index: var(--layer-conn-pill, 75);
    display: flex;
    align-items: center;
    gap: 0.4rem;
    max-width: calc(100vw - 2rem);
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(6px);
    color: var(--text-secondary, #a1a1aa);
    font-size: 0.75rem;
    white-space: nowrap;
    pointer-events: none;
}

.mp-conn-pill__dot {
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 999px;
    background: var(--accent-amber, #f59e0b);
    animation: mp-conn-blink 1.4s ease-in-out infinite;
}

@keyframes mp-conn-blink {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.25; }
}

.mp-conn-scrim {
    position: fixed;
    inset: 0;
    z-index: var(--layer-conn-scrim, 80);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, 0.78);
    backdrop-filter: blur(2px);
}

.mp-conn-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    width: min(22rem, 100%);
    max-height: 100%;
    overflow-y: auto;
    padding: 1.5rem 1.25rem;
    border-radius: 1rem;
    border: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
    background: var(--bg-card, #0c141f);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
    text-align: center;
}

.mp-conn-card__title {
    font-size: 1.25rem;
    color: var(--text-primary, #fafafa);
}

.mp-conn-card__body {
    font-size: 0.875rem;
    line-height: 1.4;
    color: var(--text-secondary, #a1a1aa);
}

.mp-conn-card__button {
    margin-top: 0.4rem;
    padding: 0.55rem 1.25rem;
    border-radius: 0.6rem;
    background: var(--accent-blue, #1f7fff);
    color: white;
    font-size: 0.875rem;
    font-weight: 600;
}

.mp-conn-spinner {
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 999px;
    border: 2px solid var(--border-color, rgba(255, 255, 255, 0.15));
    border-top-color: var(--accent-blue, #1f7fff);
    animation: mp-conn-spin 0.9s linear infinite;
}

@keyframes mp-conn-spin {
    to { transform: rotate(360deg); }
}

/* The spinner and the pill's dot carry the "still working on it" meaning, so with reduced motion they are
   stilled rather than removed: what goes is the movement, not the mark. */
@media (prefers-reduced-motion: reduce) {
    .mp-conn-spinner,
    .mp-conn-pill__dot {
        animation: none;
    }
}
