/* ============================================
   Loading — the two sanctioned wait treatments
   ============================================

   Both reuse .global-spinner (components/spinner.css, ADR-131), so there is
   still exactly one spinner in the product. See docs/guides/SHARED_COMPONENTS.md
   for which one to reach for. */

/* Nothing to read here, so the ring carries the brand rather than the muted
   body colour a line of text would have wanted. Sized between the inline --sm
   and the full 2rem: standing alone it has to be findable, but it drops in over
   rows that are already on screen and must not shout over them. */
.global-loading-panel {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-md);
    color: var(--color-action);
}

.global-loading-panel .global-spinner {
    --global-spinner-size: 1.5rem;
    --global-spinner-width: 0.15rem;
}

/* For an empty container with nothing else in it yet (a modal body before its
   first swap), so the ring is not pinned to the top edge. */
.global-loading-panel--tall {
    padding: var(--spacing-xl) var(--spacing-md);
}

.global-loading-screen {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-card-bg);
    z-index: var(--z-modal);
    transition: opacity 0.45s ease;
}

/* Scoped to a positioned ancestor instead of the viewport — the stream player
   covers its own canvas, not the page chrome around it. Stacking is left to the
   caller: an inset screen has to sit above its container's content but below
   that container's own controls, and only the caller knows where that line is. */
.global-loading-screen--inset {
    position: absolute;
    z-index: auto;
}

/* The page-ready gate (shared/js/page-ready.js). Covers the content region and
   not the whole viewport: the sidebar and header are already correct when the
   page paints, and blanking them would make a routine navigation look like a
   full reload. Sits on the page background rather than the card surface for the
   same reason — it is standing in for the page, not for a card.

   Positioning is scoped through :has() so only a page that actually includes
   the gate gets a positioned parent; every other page keeps the static flow
   that drawers and sticky headers were built against. Two parents, because a
   page's content block lands in a different box depending on its shell: the
   settings pages have their own nav beside the content, and the gate must not
   cover it. Browsers without :has() fall back to sizing the sheet against a
   further ancestor — it overshoots the content region rather than vanishing,
   which is why this is a progressive enhancement and not a hard dependency. */
.global-app-content:has(> [data-page-gate]),
.core-settings-hub-content-inner:has(> [data-page-gate]) {
    position: relative;
}

/* One tier below the modal z-index the base sheet uses: this gate covers page
   content, but a modal opened over a still-booting page has to stay on top. */
.global-loading-screen--gate {
    background: var(--color-page-bg);
    z-index: var(--z-sticky);

    /* Not the box's height, which is what an inset sheet takes by default.
       Being absolute it adds nothing back to that box, so anything landing
       behind it grew the box, grew the sheet, and slid the centred mark down:
       314px in four visible steps on Developers, 63px on a worker profile.

       Subtracting the box's own offset makes the sheet reach the fold with no
       half-built page showing beneath it. Deeper of the two shells on purpose:
       running over instead would add scrollable overflow, and a scrollbar
       appearing for the sheet's life would move the mark sideways to stop it
       moving down.

       dvh, not vh, for the same reason stream.css uses it: Safari resolves vh
       against the viewport with the toolbar retracted, so a landscape phone —
       wide enough to miss the stacked query below and still showing its
       toolbar — would get a sheet taller than the screen and a mark centred
       below true centre. A smaller helping of the exact bug above. */
    height: calc(100dvh - var(--drawer-top) - var(--page-gutter));
}

/* The full-bleed sheet this component already ships, once the layout stacks:
   below here the settings nav is a wrapping row ON TOP of the content, so the
   box starts a third of the way down the screen and no longer frames anything —
   centring in it put the mark at y=1241 on an 844px phone, a loading screen the
   reader never saw.

   The desktop gate leaves the header alone; that stops being true once the
   layout stacks. The sidebar is off-canvas, the nav is content, and the header's
   search row overflows its 40px box mid-boot then vanishes once the page
   settles — exactly the half-built chrome the gate is for, and a sheet stopping
   below the header left it ghosted on top instead. Covering the header takes the
   sidebar toggle with it, so a boot that hangs to the 5s ceiling strands a phone
   reader: accepted, because the ghost showed on every load and a hang is rare.

   One tier down from the modal panels so a real modal still wins. .global-app-header is
   a raw 1050 (main.css) and ties, losing on document order — pinned by a test,
   because a tie broken that way is invisible here.

   Same query the hub stacks on (settings-hub.css), also pinned, so the two
   cannot drift and no new breakpoint enters the ladder. */
@media (max-width: 768px) {
    .global-loading-screen--gate {
        position: fixed;
        inset: 0;
        height: auto;
        z-index: var(--z-modal-backdrop);
    }
}

/* Removed from the DOM once faded — see reveal() in page-ready.js. Pointer
   events go first so a reader who aims at a control during the fade is not
   swallowed by a sheet that is on its way out. */
.global-loading-screen--gate.is-ready {
    opacity: 0;
    pointer-events: none;
}

/* Faded rather than removed, so the reveal is not a hard cut. Callers flip
   this class and leave the node in the DOM. */
.global-loading-screen.is-hidden {
    opacity: 0;
    pointer-events: none;
}

/* The seal and the rows under it (the error line, or the bar and its words)
   as one centred column. In a plain block box the badge's bottom margin
   collapsed into the bar's top margin and the bar sat 8px under the ring
   ("too close to the loading icon", the owner, 2026-10-02); flex margins add.
   A hidden row takes no gap. The gap is one step wider than a card's, to
   match the big seal above it. The seal's sizes live here so the bar under
   it can be as wide as the ring. */
.global-loading-screen__body {
    --mark-size: min(220px, 56vw);
    --mark-ring-gap: var(--spacing-md);
    --seal-size: calc(var(--mark-size) + 2 * var(--mark-ring-gap));
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-xl);
}

.global-loading-screen__badge {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-action);
}

.global-loading-screen__mark {
    display: block;
    max-width: 100%;
    max-height: 100%;
}

/* Circle-cropped to fill the badge. Orgs upload whatever they have — square
   icons, wide wordmarks, screenshots with letterboxing — and letting each keep
   its own outline made the ring look wrapped around a different shape on every
   account. `cover` takes the crop off the long edge, so every mark reads as
   the same seal.

   The cost is real and deliberate: a mark far off square loses its ends, so a
   wordmark uploaded here shows its middle. The role is "Loading mark" and
   wants an icon; an org whose only asset is a wordmark should assign it to
   Sidebar and leave this one unset for the plain ring. */
.global-loading-screen__badge--mark .global-loading-screen__mark {
    width: 100%;
    height: 100%;
    border-radius: var(--radius-pill);
    object-fit: cover;
}

/* Seal treatment: with a mark assigned the ring wraps it rather than sitting
   beside it. The badge box is forced square so the wrapping ring stays a true
   circle for any logo aspect — a wide logo would otherwise give an oval.

   The ring is drawn past this box by the ring gap on every side (the inset
   below), so the box gives that overflow back as margin, and the rows under
   it measure their step from the ring's outer edge. */
.global-loading-screen__badge--mark {
    width: var(--mark-size);
    aspect-ratio: 1;
    margin: var(--mark-ring-gap);
}

/* inset, not a translate-centering: global-spin's rotate() shares the transform
   property and would clobber it. Grown through the spinner's own size variable
   rather than width/height, which spinner.css owns. */
.global-loading-screen__badge--mark .global-spinner {
    --global-spinner-size: var(--seal-size);
    position: absolute;
    inset: calc(-1 * var(--mark-ring-gap));
}

/* The message line is for terminal errors only. A blocking wait shows no
   progress text — it either resolves or it explains itself. It reads in the
   same ink as the bar's words, so an error under the seal is as easy to read
   as the wait it replaced. */
.global-loading-screen__message {
    color: var(--color-text-secondary);
    font-size: var(--font-size-md);
    text-align: center;
}

/* The catalog staging bar (loading_screen progress=True): the one wait that
   reports how far it has got, so it gets the role page's bar under the ring,
   with clear words under it (the owner, 2026-10-02). Under a mark the bar is
   as wide as the ring, so the two stack as one shape; the ring alone is too
   small to set a width, so the bar keeps its own there. */
.global-loading-screen__progress {
    width: min(320px, 70vw);
}

.global-loading-screen__badge--mark ~ .global-loading-screen__progress {
    width: var(--seal-size);
}

.global-loading-screen__progress-label {
    margin-top: var(--spacing-smd);
    color: var(--color-text-secondary);
    font-size: var(--font-size-md);
    text-align: center;
}

.global-loading-screen__progress-note {
    display: block;
    margin-top: var(--spacing-xs);
    color: var(--color-text-secondary);
    font-size: var(--font-size-xs);
    text-align: center;
}

.global-loading-screen__progress-note[hidden] {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    .global-loading-screen {
        transition: none;
    }
}
