/* Theme-neutral reset + shell. The actual look comes from noir.css / material.css,
   scoped under .theme-noir / .theme-material (applied by PublicLayout / AdminLayout). */

*, *::before, *::after {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    /* Dark from the very first paint (before any component exists) so there is never a white flash;
       light-mode visitors get the matching light colour via the tiny script in index.html. */
    background: #0b0620;
    /* Safety net so no single overflowing element can force the whole page to scroll
       sideways on a narrow viewport - anything that needs horizontal scroll (tables) opts
       into it explicitly via .table-wrap instead. */
    overflow-x: hidden;
}

#app {
    min-height: 100vh;
}

a {
    text-decoration: none;
}

/* Wrap any <table> in this on a page that might be viewed on a phone - lets the table
   scroll horizontally on its own instead of squeezing columns unreadably or breaking layout. */
.table-wrap {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.validation-message {
    color: #ff4d6d;
    font-size: 0.85rem;
    display: block;
    margin-top: 0.25rem;
}

.boot-screen {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    background: #0b0620;
}

.boot-spinner {
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, 0.15);
    border-top-color: #ffffff;
    animation: boot-spin 0.8s linear infinite;
}

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

#blazor-error-ui {
    background: #2a0a12;
    color: #ffb3c1;
    border-top: 2px solid #ff4d6d;
    bottom: 0;
    box-shadow: 0 -2px 12px rgba(255, 77, 109, 0.4);
    display: none;
    left: 0;
    padding: 0.75rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
    font-family: 'Inter', -apple-system, sans-serif;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.6rem;
        color: #ffb3c1;
    }

    #blazor-error-ui .reload {
        color: #ffffff;
        text-decoration: underline;
    }

.blazor-error-boundary {
    background: #2a0a12;
    border: 1px solid #ff4d6d;
    padding: 1rem;
    color: #ffb3c1;
    border-radius: 4px;
}

    .blazor-error-boundary::after {
        content: "Something went wrong.";
    }

code {
    font-family: ui-monospace, 'Cascadia Code', Consolas, monospace;
}

/* Theme-neutral avatar circle: works under both .theme-noir and .theme-material without
   duplication because both themes define --surface/--border/--text with the same names.
   The initial-letter <span> is always rendered; the <img> (when present) covers it and hides
   itself via a plain onerror attribute if the avatar URL 404s, revealing the letter behind -
   no JS interop, no "does an avatar exist" check needed first. */
.avatar-circle {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border-radius: 50%;
    overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    font-weight: 600;
}

.avatar-circle img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Avatar images fade in once decoded instead of popping over the initial letter. */
.avatar-circle img { opacity: 0; transition: opacity 0.35s ease; }
.avatar-circle img.loaded { opacity: 1; }

/* Light-mode visitors: same no-flash rule (html.light is set by the inline script in index.html). */
html.light, html.light body { background: #eef6fb; }
html.light .boot-screen { background: #eef6fb; }
html.light .boot-spinner { border-color: rgba(0, 0, 0, 0.12); border-top-color: #4a3aa8; }

/* Boot screen (before Blazor renders): Aurelia's ghost bobbing, same look as AuraLoader. */
.boot-spinner {
    width: 4.5rem; height: 5.25rem; border: none; border-radius: 0;
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 140'><path d='M60 10C33 10 19 31 22 56C23 70 14 86 12 108C11 121 21 128 29 123C35 130 44 130 50 124C55 130 65 130 70 124C76 130 85 130 91 123C99 128 109 121 108 108C106 86 97 70 98 56C101 31 87 10 60 10Z' fill='%233b2390' stroke='%23a889ff' stroke-opacity='.6' stroke-width='1.6'/><circle cx='25' cy='98' r='7' fill='%234a2fb0'/><circle cx='95' cy='98' r='7' fill='%234a2fb0'/><ellipse cx='60' cy='58' rx='28' ry='26' fill='%2305020f'/><ellipse cx='49' cy='57' rx='7' ry='8.5' fill='%237cefff'/><ellipse cx='71' cy='57' rx='7' ry='8.5' fill='%237cefff'/><circle cx='51.6' cy='53.6' r='2.6' fill='white'/><circle cx='73.6' cy='53.6' r='2.6' fill='white'/></svg>") center / contain no-repeat;
    filter: drop-shadow(0 6px 14px rgba(140, 100, 255, 0.45));
    animation: boot-bob 1.6s ease-in-out infinite;
}
@keyframes boot-bob { 0%, 100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-12px) rotate(2deg); } }
@media (prefers-reduced-motion: reduce) { .boot-spinner { animation: none; } }
