/*
 * Loading feedback, both faces (core/static/js/busy.js): the bar along the top, the busy
 * cursor, the spinner on the control that was pressed -- and the stage placeholder of the
 * board viewers on the shop face, which until now had the portal's gull markup and none of
 * its styles: a gray box, saying nothing, for as long as the board took.
 *
 * Colors are Bootstrap's own variables, so the light and dark themes and the maintenance
 * graying (portal.css) apply as they already do. prefers-reduced-motion stills everything.
 */

/* ── The bar ─────────────────────────────────────────────────────────────── */
.busy-bar {
    position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 2000;
    overflow: hidden; pointer-events: none;
    opacity: 0; transition: opacity 0.15s ease-out;
}
html.is-busy .busy-bar { opacity: 1; }
.busy-bar::before {
    content: ""; position: absolute; top: 0; bottom: 0; left: -35%; width: 35%;
    background: var(--bs-primary);
    box-shadow: 0 0 6px rgba(var(--bs-primary-rgb), 0.6);
    animation: busy-slide 1.1s ease-in-out infinite;
}
@keyframes busy-slide { from { left: -35%; } to { left: 100%; } }

/* The cursor says it everywhere, without taking the page away: `progress`, not `wait`. */
html.is-busy, html.is-busy * { cursor: progress !important; }

/* ── The control that was pressed ───────────────────────────────────────── */
.busy-spinner {
    display: none;
    width: 0.85em; height: 0.85em; margin-left: 0.4em; vertical-align: -0.1em;
    border: 0.15em solid currentColor; border-right-color: transparent; border-radius: 50%;
    animation: busy-spin 0.7s linear infinite;
}
.busy-control.busy-shown .busy-spinner { display: inline-block; }
input.busy-control.busy-shown { opacity: 0.65; }
@keyframes busy-spin { to { transform: rotate(360deg); } }

/* ── A board stage with nothing on it yet (the shop face; the portal draws its gull) ── */
html:not(.portal) .gull-loader {
    position: absolute; inset: 0; z-index: 3;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.5rem;
    pointer-events: none;
}
html:not(.portal) .gull-loader .gull-spinner { display: none; }
html:not(.portal) .gull-loader::before {
    content: ""; width: 2.25rem; height: 2.25rem;
    /* The theme's text color, so it reads on the light stage and the dark one alike. */
    border: 0.22rem solid rgba(var(--bs-emphasis-color-rgb), 0.55); border-right-color: transparent;
    border-radius: 50%;
    animation: busy-spin 0.8s linear infinite;
}
html:not(.portal) .gull-loader-text {
    color: #fff; background: rgba(0, 0, 0, 0.45);
    padding: 0.1rem 0.5rem; border-radius: 0.25rem;
}
/* Without script the stage never draws; it says nothing rather than spinning forever. */
html:not(.busy-js) .gull-loader { display: none !important; }

/* ── A stage that could not be drawn: both faces ──────────────────────────── */
/* `html body` so these outrank the shop's spinner above and the reduced-motion pulse below. */
html body .gull-loader.is-error { pointer-events: auto; }
html body .gull-loader.is-error .gull-spinner { display: none; }
html body .gull-loader.is-error::before {
    content: "\26A0"; /* ⚠ */
    width: auto; height: auto; border: 0; animation: none;
    font-size: 2rem; line-height: 1; color: var(--bs-warning);
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}
html body .gull-loader.is-error .gull-loader-text { max-width: 32rem; text-align: center; }

@media (prefers-reduced-motion: reduce) {
    .busy-bar { transition: none; }
    .busy-bar::before { animation: none; left: 0; width: 100%; opacity: 0.55; }
    .busy-spinner, html:not(.portal) .gull-loader::before { animation: busy-pulse 2s ease-in-out infinite; }
}
@keyframes busy-pulse { 50% { opacity: 0.35; } }

/* While the placeholder is up it says what the stage's status line would: once is enough. */
.fab-stage:has(> .gull-loader:not([hidden])) > .fab-status,
.view3d-stage:has(> .gull-loader:not([hidden])) > .fab-status { visibility: hidden; }
