/* =========================================================================
   SBI global page loader — the ONE loader for admin and frontend alike.
   =========================================================================

   Why this exists
   ---------------
   Both layouts restore UI state *after* first paint: the admin layout replays
   the remembered tab with a synthetic btn.click(), the per-page hubs open the
   tab named in the URL hash, and the sidebar reads its collapsed flag out of
   localStorage. Each of those lands a frame or more after the browser has
   already painted the server-rendered default, so a plain load visibly flipped
   from one panel to another — the "shuffle" this file exists to remove.

   The sidebar flag is fixed at the source (it is now stamped onto <html>
   before paint). The tab replays are left alone — they work — and simply
   happen underneath this overlay, which covers the page from the first frame
   until the page has stopped moving.

   The no-JS contract
   ------------------
   The overlay is display:none until page-loader.js adds .pl-busy to <html>.
   If the script never runs — blocked, errored, JS off — there is no overlay
   and nothing to be trapped behind. The visible state is always opt-IN.

   z-index
   -------
   Below the accessibility widget (2147483000+), above everything else. That
   widget is an accessibility control and must never be sealed off by a
   loader; nothing else on either side needs to outrank this.
   ========================================================================= */

.pl-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 2147482900;
    align-items: center;
    justify-content: center;
    /* The page underneath must not scroll by proxy when the wheel is spun
       over the overlay — but html/body overflow is deliberately NOT locked,
       because removing the scrollbar is itself a layout shift, and this file
       exists to remove layout shifts. */
    overscroll-behavior: contain;
    opacity: 1;
    transition: opacity .18s ease;
}

html.pl-busy .pl-overlay {
    display: flex;
}

/* Fading out. The rule above keeps it displayed until the transition ends. */
html.pl-busy.pl-leaving .pl-overlay {
    opacity: 0;
}

/* ---------- The two modes ------------------------------------------------
   boot: a fresh document is painting and reshuffling underneath, so the
   backdrop is opaque — there is nothing worth showing yet.
   nav:  the current page is still perfectly good and the user only asked to
   leave it, so it stays visible under a light wash. That is the difference
   between "visually stable" and "blank".                                    */

.pl-overlay[data-mode="boot"] {
    background: var(--pl-boot-bg, #f1f5f9);
}

.pl-overlay[data-mode="nav"] {
    background: var(--pl-nav-bg, rgba(241, 245, 249, .62));
    -webkit-backdrop-filter: saturate(140%) blur(2px);
    backdrop-filter: saturate(140%) blur(2px);
}

/* ---------- The indicator ------------------------------------------------ */

.pl-box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    /* Nudged up: dead-centre reads as low on a tall viewport. */
    transform: translateY(-4vh);
}

.pl-ring {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    border: 3px solid var(--pl-ring-track, rgba(15, 23, 42, .12));
    border-top-color: var(--pl-ring, #2563eb);
    animation: pl-spin .7s linear infinite;
}

.pl-label {
    font: 500 13px/1.4 Inter, system-ui, -apple-system, "Segoe UI", sans-serif;
    letter-spacing: .01em;
    color: var(--pl-label, #475569);
}

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

/* ---------- Top progress bar ---------------------------------------------
   A spinner says "busy"; it does not say "still busy, and moving". On a slow
   navigation the bar is the part that keeps the wait from feeling hung.     */

.pl-bar {
    /* absolute, not fixed: the overlay is already the pinned box, and a second
       fixed element would need the same containing-block compensation the
       overlay gets in JS when the accessibility widget filters <html>. */
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    overflow: hidden;
    background: transparent;
}

.pl-bar::before {
    content: "";
    position: absolute;
    inset: 0;
    width: 40%;
    border-radius: 0 3px 3px 0;
    background: linear-gradient(90deg,
                transparent 0%,
                var(--pl-ring, #2563eb) 50%,
                transparent 100%);
    animation: pl-sweep 1.15s cubic-bezier(.45, 0, .25, 1) infinite;
}

@keyframes pl-sweep {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(350%); }
}

/* ---------- Reduced motion -----------------------------------------------
   The overlay still does its job — covering the reshuffle — with nothing
   spinning or sweeping. Only the movement is dropped, not the affordance.   */

@media (prefers-reduced-motion: reduce) {

    .pl-overlay,
    .pl-ring,
    .pl-bar::before {
        transition: none;
        animation: none;
    }

    .pl-ring {
        border-color: var(--pl-ring, #2563eb);
        border-top-color: var(--pl-ring-track, rgba(15, 23, 42, .12));
    }

    .pl-bar::before {
        width: 100%;
        opacity: .55;
    }
}

/* ---------- Dark surfaces ------------------------------------------------
   The page builder is dark editor chrome; a white flash between screens
   there is worse than no loader at all. A layout opts in by setting
   data-theme="dark" on the overlay rather than by this file knowing its
   name.                                                                     */

.pl-overlay[data-theme="dark"] {
    --pl-boot-bg: #0f172a;
    --pl-nav-bg: rgba(15, 23, 42, .66);
    --pl-ring-track: rgba(255, 255, 255, .16);
    --pl-ring: #60a5fa;
    --pl-label: #cbd5e1;
}
