/* ============================================================================
   The loading state, for every page in the product.

   See resources/js/lib/route-loading.ts for what this replaced and why. In
   short: the old indicator was a grey hairline on the top edge of the window,
   and tenants read a screen that had not changed yet as a button that did not
   work.

   Built in layers rather than as one flat panel, so it reads as sitting above
   the page instead of being part of it:

     scrim    the page dimmed and blurred back, using the theme's own overlay
     card     lifted on the pop plane with the theme's pop shadow
     rail     the only saturated thing on screen, so the eye lands on the part
              that is actually moving

   Every colour is a workspace theme custom property. There are five themes,
   two of them with a light canvas under dark chrome, and a hard-coded panel
   would be wrong in at least three of them.
   ============================================================================ */
.vp-route-loading {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;

    background: var(--vpw-overlay, rgba(8, 20, 34, 0.42));
    -webkit-backdrop-filter: blur(3px) saturate(112%);
    backdrop-filter: blur(3px) saturate(112%);

    opacity: 0;
    visibility: hidden;
    transition: opacity 180ms ease, visibility 0s linear 180ms;
}

.vp-route-loading.is-open {
    opacity: 1;
    visibility: visible;
    transition: opacity 180ms ease, visibility 0s;
}

.vp-route-loading__card {
    width: min(22rem, calc(100vw - 2.5rem));
    padding: 1.35rem 1.5rem 1.4rem;
    border-radius: 1.125rem;
    border: 1px solid var(--vpw-line, rgba(120, 140, 165, 0.28));
    background: var(--vpw-pop, #16233a);
    box-shadow: var(--vpw-pop-shadow, 0 24px 56px -18px rgba(0, 0, 0, 0.55));

    /* Lifted, then settling. The card arrives from slightly below and slightly
       small, which reads as a response to the press rather than as something
       that was always there and only just became visible. */
    transform: translateY(0.5rem) scale(0.97);
    transition: transform 200ms cubic-bezier(0.18, 0.9, 0.28, 1);
}

.vp-route-loading.is-open .vp-route-loading__card {
    transform: translateY(0) scale(1);
}

.vp-route-loading__label {
    font-size: 0.9375rem;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: -0.012em;
    color: var(--vpw-strong, #eaf1fb);
}

.vp-route-loading__rail {
    position: relative;
    height: 6px;
    margin-top: 0.85rem;
    border-radius: 999px;
    overflow: hidden;
    background: var(--vpw-line-soft, rgba(120, 140, 165, 0.2));
}

.vp-route-loading__rail > i {
    display: block;
    position: relative;
    height: 100%;
    width: 100%;
    border-radius: inherit;

    /* Both of these are load-bearing for the sheen below: without position it
       anchors to the whole track and lights up the empty part too, and without
       overflow it travels out past the end of what has actually been claimed. */
    overflow: hidden;
    background: var(--vpw-accent, #3b82f6);

    /* Grown from the left, not sized, so the whole rail is one composited layer
       and a slow server never costs a layout pass a frame. */
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 260ms cubic-bezier(0.25, 0.8, 0.35, 1);
}

/*
 * A sheen travelling along the filled part.
 *
 * The rail's own advance is deliberately slow — after the first couple of
 * seconds it is barely moving, which is honest but looks identical to frozen.
 * This is the part that says "still alive" in the seconds where the number
 * genuinely is not changing. It is inside the fill, so it only ever travels the
 * distance actually claimed, and it stops existing once the fill does.
 */
.vp-route-loading__rail > i::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(
        90deg,
        transparent 0%,
        rgba(255, 255, 255, 0.55) 50%,
        transparent 100%
    );
    animation: vp-route-loading-sheen 1250ms linear infinite;
}

@keyframes vp-route-loading-sheen {
    from { transform: translateX(-100%); }
    to   { transform: translateX(100%); }
}

.vp-route-loading__patience {
    margin-top: 0.7rem;
    min-height: 1rem;
    font-size: 0.75rem;
    line-height: 1.35;
    color: var(--vpw-muted, #93a6bf);
}

.vp-route-loading__patience:empty {
    /* Holds its own height from the start so the card does not jump a line
       taller two and a half seconds in, under the reader's eye. */
    margin-top: 0.7rem;
}

@media (prefers-reduced-motion: reduce) {
    .vp-route-loading,
    .vp-route-loading__card,
    .vp-route-loading__rail > i {
        transition-duration: 1ms;
    }

    .vp-route-loading__card {
        transform: none;
    }

    /* The sheen is the one thing here that moves for its own sake. Somebody who
       has asked for less of that keeps the rail and the words, which carry the
       whole message anyway. */
    .vp-route-loading__rail > i::after {
        animation: none;
        opacity: 0;
    }
}

/*
 * The boot gap, before the app bundle has mounted anything.
 *
 * Same scrim as a navigation, painted rather than composited: the overlay colour
 * over the theme's own canvas, which is the exact tone a translucent scrim would
 * resolve to against the page that is about to arrive. It has to be opaque here
 * because at boot there is nothing underneath yet -- a see-through scrim would
 * show bare document white through it for a moment on a dark theme -- and the
 * blur is stood down for the same reason: there is nothing to blur.
 */
.vp-route-loading--boot {
    background:
        linear-gradient(
            var(--vpw-overlay, rgba(8, 20, 34, 0.42)),
            var(--vpw-overlay, rgba(8, 20, 34, 0.42))
        ),
        var(--vpw-bg, #0b1524);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}
