/* The splash index.html shows from first paint until the app is ready: through the WebAssembly download and then
   through sign-in. It lives outside #app, so Blazor's first render leaves it alone; see "Phases" below for how it
   follows the app without any JS.

   Colours, type and the circuit-trace motif follow the AES brand guidelines: Cobalt and Cyan dominate, Graphite
   only darkens, traces are thin Cyan lines on Cobalt. The guidelines keep the solo 'A' mark to favicons, so the
   splash names the product in type rather than showing the favicon large.

   The progress bar and percentage read --blazor-load-percentage and --blazor-load-percentage-text, which the
   Blazor boot script sets on <html> as each boot resource arrives.

   The SVG is drawn on a 1000 x 460 grid and the stage keeps that aspect ratio, so the HTML chip overlay can be
   placed in percentages and sized in cqw (1cqw = 10 grid units) and still line up with the pins at any size.
   Every element's resting style is its finished state; the entrance animations start from hidden via
   backwards fill, so dropping the animations (reduced motion) leaves the finished picture. */

/* z-index: above MainLayout, which renders underneath during sign-in, and MudBlazor's app bar (1300); below its
   dialogs and snackbars (1400+), so a startup error message still shows. #blazor-error-ui sits higher still (aes.css). */
.app-loading {
    --aes-cobalt: #0B54D6;
    --aes-cyan: #58CBF9;
    --aes-graphite: #161616;
    --aes-deep: color-mix(in srgb, var(--aes-cobalt) 45%, var(--aes-graphite));
    --aes-chip: color-mix(in srgb, var(--aes-cobalt) 26%, var(--aes-graphite));
    position: fixed;
    inset: 0;
    z-index: 1350;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    background: radial-gradient(ellipse 80% 70% at 50% 50%, var(--aes-cobalt) 0%, var(--aes-deep) 100%);
    color: #fff;
    font-family: "DM Sans", Tahoma, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    transition: opacity 0.45s ease-out, display 0.45s allow-discrete;
}

    /* MudBlazor's reset supplies border-box in the running app; the splash sets its own so its layout never depends
       on that stylesheet (the chip overlay's padding would otherwise widen it off the SVG chip). */
    .app-loading *,
    .app-loading *::before,
    .app-loading *::after {
        box-sizing: border-box;
    }

    /* Board texture: a faint via grid that fades out toward the centre so it never competes with the chip. */
    .app-loading::before {
        content: "";
        position: absolute;
        inset: 0;
        background-image: radial-gradient(circle, rgb(255 255 255 / 0.1) 1px, transparent 1.6px);
        background-size: 24px 24px;
        background-position: center;
        mask-image: radial-gradient(ellipse 65% 60% at 50% 50%, transparent 30%, #000 100%);
    }

/* On a phone the stage is wider than the screen so the chip stays legible; the outer traces run off both edges
   (flex: none keeps it from shrinking, and a centred flex item overflows evenly on both sides). */
.app-loading__stage {
    position: relative;
    flex: none;
    width: min(1000px, 140vw, 210vh);
    aspect-ratio: 1000 / 460;
    container-type: inline-size;
    animation: app-loading-fade-in 0.5s ease-out backwards;
    transition: transform 0.45s cubic-bezier(0.4, 0, 1, 1);
}

    /* The chip's glow. It lives on the stage rather than the chip overlay, which clips its own shine sweep. */
    .app-loading__stage::before {
        content: "";
        position: absolute;
        left: 24%;
        top: 25%;
        width: 52%;
        height: 50%;
        border-radius: 1.4cqw;
        box-shadow: 0 0 5cqw 0.8cqw rgb(88 203 249 / 0.35);
        animation: app-loading-fade-in 0.8s ease-out 0.8s backwards, app-loading-breathe 3.2s ease-in-out 1.6s infinite alternate;
    }

.app-loading__circuit {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

/* Every trace is shorter than 300 grid units, so one 300-unit dash drawn in at a constant rate gives every trace
   the same drawing speed; --d staggers the starts. */
.app-loading__traces use {
    fill: none;
    stroke: var(--aes-cyan);
    stroke-opacity: 0.45;
    stroke-width: 1.5;
    stroke-linejoin: round;
    stroke-dasharray: 300;
    stroke-dashoffset: 0;
    animation: app-loading-trace-draw 0.9s linear var(--d) backwards;
}

/* Signals run from each via into the chip: a 22-unit dash slides from past the trace's far end to just before its
   start, then waits out the rest of the cycle. --p is the first run, --c the cycle; differing cycles keep the
   pulses from falling into step. Resting just before the start (not at 0) matters: a round cap on a zero-length
   dash still paints a dot. */
.app-loading__pulses {
    filter: drop-shadow(0 0 2px var(--aes-cyan)) drop-shadow(0 0 6px rgb(88 203 249 / 0.7));
}

    .app-loading__pulses use {
        fill: none;
        stroke: #E6F7FE;
        stroke-width: 2.5;
        stroke-linecap: round;
        stroke-dasharray: 22 600;
        stroke-dashoffset: 24;
        animation: app-loading-pulse var(--c) var(--p) infinite;
    }

.app-loading__vias circle {
    fill: var(--aes-deep);
    stroke: var(--aes-cyan);
    stroke-width: 1.5;
    transform-box: fill-box;
    transform-origin: center;
    animation: app-loading-via-pop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) var(--v) backwards;
}

.app-loading__pins {
    fill: var(--aes-cyan);
    fill-opacity: 0.7;
    animation: app-loading-fade-in 0.4s ease-out 0.45s backwards;
}

.app-loading__chip-body {
    fill: var(--aes-chip);
    stroke: var(--aes-cyan);
    stroke-width: 1.5;
    stroke-dasharray: 100;
    stroke-dashoffset: 0;
    animation: app-loading-chip-draw 0.8s cubic-bezier(0.65, 0, 0.35, 1) 0.1s backwards;
}

.app-loading__die {
    fill: none;
    stroke: var(--aes-cyan);
    stroke-opacity: 0.16;
    animation: app-loading-fade-in 0.6s ease-out 0.7s backwards;
}

.app-loading__pin-one {
    fill: var(--aes-cyan);
    fill-opacity: 0.55;
    animation: app-loading-fade-in 0.6s ease-out 0.7s backwards;
}

/* The chip overlay sits exactly on the SVG chip body (x 240, y 115, 520 x 230 on the grid). */
.app-loading__chip {
    position: absolute;
    left: 24%;
    top: 25%;
    width: 52%;
    height: 50%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0 4cqw;
    border-radius: 1.4cqw;
    overflow: hidden;
    text-align: center;
}

    .app-loading__chip::after {
        content: "";
        position: absolute;
        inset: 0;
        background: linear-gradient(105deg, transparent 38%, rgb(255 255 255 / 0.07) 50%, transparent 62%);
        transform: translateX(-100%);
        animation: app-loading-shine 5s ease-in-out 1.4s infinite;
    }

.app-loading__title {
    margin: 0;
    font-family: "Uni Neue", Tahoma, Helvetica, Arial, sans-serif;
    font-size: 4.6cqw;
    font-weight: 600;
    line-height: 1.1;
    color: #fff;
    animation: app-loading-rise 0.6s cubic-bezier(0.2, 0.7, 0.2, 1) 0.35s backwards;
}

    .app-loading__title span {
        font-weight: 400;
        color: var(--aes-cyan);
    }

/* Letter spacing trails the last letter too; the matching left padding keeps the line optically centred. */
.app-loading__tagline {
    margin: 0.9cqw 0 0;
    padding-left: 0.24em;
    font-size: max(8px, 1.15cqw);
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: rgb(255 255 255 / 0.7);
    animation: app-loading-rise 0.6s cubic-bezier(0.2, 0.7, 0.2, 1) 0.45s backwards;
}

/* The download meter and the sign-in panel share one grid cell, so switching between them crossfades in place
   and the title never shifts. */
.app-loading__slot {
    display: grid;
    margin-top: 2.2cqw;
}

    .app-loading__slot > * {
        grid-area: 1 / 1;
        align-self: center;
        transition: opacity 0.35s ease-out, visibility 0.35s;
    }

.app-loading__progress {
    position: relative;
    height: max(2px, 0.25cqw);
    border-radius: 999px;
    background: rgb(88 203 249 / 0.18);
    animation: app-loading-fade-in 0.6s ease-out 0.55s backwards;
}

.app-loading__progress-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--blazor-load-percentage, 0%);
    border-radius: inherit;
    background: linear-gradient(90deg, rgb(88 203 249 / 0.3), var(--aes-cyan) 75%, #fff);
    box-shadow: 0 0 0.8cqw rgb(88 203 249 / 0.8);
    transition: width 0.3s ease-out;
}

    /* The signal head riding the leading edge of the bar. */
    .app-loading__progress-fill::after {
        content: "";
        position: absolute;
        right: 0;
        top: 50%;
        width: max(5px, 0.6cqw);
        aspect-ratio: 1;
        border-radius: 50%;
        background: #fff;
        box-shadow: 0 0 0.6cqw 0.2cqw var(--aes-cyan);
        transform: translate(50%, -50%);
    }

.app-loading__status {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-top: 1.2cqw;
    font-size: max(10.5px, 1.25cqw);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: rgb(255 255 255 / 0.75);
    animation: app-loading-fade-in 0.6s ease-out 0.55s backwards;
}

.app-loading__percent::after {
    content: var(--blazor-load-percentage-text, "");
    color: var(--aes-cyan);
    font-variant-numeric: tabular-nums;
}

.app-loading__phase-signing-in {
    display: none;
}

/* The login screen's panel (Login.razor). It fades in on the same beat as the meter when the splash first
   appears, and crossfades in when the splash switches over from "Signing you in". */
.app-loading__sign-in-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    opacity: 0;
    visibility: hidden;
    animation: app-loading-fade-in 0.6s ease-out 0.55s backwards;
}

.app-loading__sign-in {
    display: inline-flex;
    align-items: center;
    gap: max(8px, 0.9cqw);
    min-height: max(36px, 4.2cqw);
    padding: 0 max(16px, 2.4cqw);
    border: 1px solid rgb(88 203 249 / 0.6);
    border-radius: max(6px, 0.6cqw);
    background: var(--aes-cobalt);
    box-shadow: 0 0 1.2cqw rgb(88 203 249 / 0.25);
    color: #fff;
    font: 600 max(12px, 1.4cqw) / 1 "DM Sans", Tahoma, Helvetica, Arial, sans-serif;
    letter-spacing: 0.02em;
    cursor: pointer;
    transition: background-color 0.2s, border-color 0.2s, box-shadow 0.2s, opacity 0.2s;
}

    .app-loading__sign-in:hover:not(:disabled) {
        border-color: var(--aes-cyan);
        background: color-mix(in srgb, var(--aes-cobalt) 85%, #fff);
        box-shadow: 0 0 1.8cqw rgb(88 203 249 / 0.45);
    }

    .app-loading__sign-in:focus-visible {
        outline: 2px solid var(--aes-cyan);
        outline-offset: 3px;
    }

    /* The delay keeps a pop-up that the browser blocks at once from flashing the pending look. */
    .app-loading__sign-in:disabled {
        opacity: 0.55;
        box-shadow: none;
        cursor: progress;
        transition-delay: 0.4s;
    }

    .app-loading__sign-in svg {
        width: 1em;
        height: 1em;
        fill: currentColor;
    }

/* Always takes its line, so the panel's height doesn't change when the hint comes and goes. */
.app-loading__sign-in-hint {
    margin: max(6px, 0.8cqw) 0 0;
    font-size: max(10.5px, 1.2cqw);
    color: rgb(255 255 255 / 0.75);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease-out, visibility 0.3s;
}

/* Phases. Blazor renders App into #app, the splash's sibling, so the splash reads the app's state from there:
   - #app empty: the runtime is still downloading or starting, and the bar shows download progress.
   - .app-loading-hold present: rendered by App's Authorizing view (App.razor), RedirectToLogin and the sign-in steps
     of the Authentication page. The label reads "Signing you in" and, with no measurable progress, the bar becomes
     a repeating sweep.
   - .app-loading--sign-in on the splash itself: the login screen. Login.razor sets it through ShowSplashSignIn
     (scripts/jsFunctions.js), and the meter gives way to the Sign in button.
   - anything else in #app: the app is ready. The splash fades out, the stage eases forward, and display: none
     then takes the splash out of layout and stops its animations. Browsers without allow-discrete transitions
     just skip the fade. */
body:has(.app-loading-hold) .app-loading__phase-loading,
body:has(.app-loading-hold) .app-loading__percent {
    display: none;
}

body:has(.app-loading-hold) .app-loading__phase-signing-in {
    display: inline;
}

/* Clip the sweep at the track's ends without clipping the head's glow above and below it. */
body:has(.app-loading-hold) .app-loading__progress {
    clip-path: inset(-1cqw 0);
}

/* The resting style (a dim full bar) is what reduced motion sees; the sweep keyframes set the moving segment. */
body:has(.app-loading-hold) .app-loading__progress-fill {
    width: 100%;
    opacity: 0.5;
    animation: app-loading-sweep 1.6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

.app-loading--sign-in .app-loading__meter {
    opacity: 0;
    visibility: hidden;
}

.app-loading--sign-in .app-loading__sign-in-panel {
    opacity: 1;
    visibility: visible;
}

/* Only once the pop-up has stayed open a moment; see the button's disabled style. */
.app-loading--sign-in-pending .app-loading__sign-in-hint {
    opacity: 1;
    visibility: visible;
    transition-delay: 0.4s;
}

body:has(#app > *):not(:has(.app-loading-hold)) .app-loading:not(.app-loading--sign-in) {
    display: none;
    opacity: 0;
    pointer-events: none;
}

body:has(#app > *):not(:has(.app-loading-hold)) .app-loading:not(.app-loading--sign-in) .app-loading__stage {
    transform: scale(1.04);
}

/* Without :has() (Firefox before 121, Chrome before 105) none of the :has() phase rules match, and the splash would
   cover the app for good. Those browsers drop it the moment Blazor renders anything, skipping "Signing you in";
   the login screen still works because its class is set by script. This relies on the splash following #app in
   index.html. */
@supports not selector(:has(*)) {
    #app:not(:empty) ~ .app-loading:not(.app-loading--sign-in) {
        display: none;
    }
}

@keyframes app-loading-fade-in {
    from {
        opacity: 0;
    }
}

@keyframes app-loading-breathe {
    from {
        opacity: 1;
    }

    to {
        opacity: 0.45;
    }
}

@keyframes app-loading-rise {
    from {
        opacity: 0;
        transform: translateY(0.8cqw);
    }
}

@keyframes app-loading-chip-draw {
    from {
        stroke-dashoffset: 100;
        fill-opacity: 0;
    }

    60% {
        fill-opacity: 0;
    }
}

@keyframes app-loading-trace-draw {
    from {
        stroke-dashoffset: 300;
    }
}

@keyframes app-loading-via-pop {
    from {
        opacity: 0;
        transform: scale(0);
    }
}

@keyframes app-loading-pulse {
    0% {
        stroke-dashoffset: -300;
        animation-timing-function: cubic-bezier(0.45, 0, 0.8, 0.9);
    }

    30%, 100% {
        stroke-dashoffset: 24;
    }
}

@keyframes app-loading-sweep {
    from {
        left: -30%;
        width: 30%;
        opacity: 1;
    }

    to {
        left: 100%;
        width: 30%;
        opacity: 1;
    }
}

@keyframes app-loading-shine {
    40%, 100% {
        transform: translateX(100%);
    }
}

@media (prefers-reduced-motion: reduce) {
    .app-loading *,
    .app-loading *::before,
    .app-loading *::after {
        animation: none !important;
        transition: none !important;
    }
}
