/* ============================================================
   NEW UI — LOADING INDICATORS
   One consistent loading language under myIncite. Two halves:

   1) CANONICAL FAMILY (.nui-spin) — use this for all new New UI
      code. Dependency-free ring spinner, single <span>.

   2) BLANKET RESKIN — zero-template-change normalization of the
      legacy loading patterns still in use throughout the app
      (FontAwesome fa-spin icons, [id^="spinner"] jQuery-toggled
      blocks, the full-page #loading-app-spinner) so they read as
      part of the same system whenever myIncite is active.

   SCOPING — two different scopes are used on purpose:

   - `.is-myincite-ui <selector>` for anything guaranteed to render
     as a DOM descendant of #InciteWrapper (in-page widgets, FA
     icons in buttons/panels, etc).

   - `body:has(#InciteWrapper.is-myincite-ui) <selector>` (the same
     precedent new-ui-theme.css uses for the phoenix mascot on
     #loading-app-spinner) for anything that can render OUTSIDE the
     wrapper. Verified two such cases:
       a) #loading-app-spinner itself — it's a sibling of
          #InciteWrapper in public/index.html (line ~235, wrapper is
          line ~516), not a descendant.
       b) uib-modal windows — public/js/scripts-96658822f7.js explicitly calls
          `$uibModal.appendTo('body')`, so any spinner markup used
          inside a modal is also a body-level sibling, not a
          descendant of the wrapper.
     The canonical .nui-spin family is scoped with body:has(...) for
     this second reason — new code may well use it inside a modal.
   ============================================================ */

/* ------------------------------------------------------------
   1. CANONICAL FAMILY — .nui-spin
   Ring spinner: border track + brand-colored arc, single element,
   no markup beyond one <span class="nui-spin"></span>.
   ------------------------------------------------------------ */
body:has(#InciteWrapper.is-myincite-ui) .nui-spin {
    display: inline-block;
    vertical-align: middle;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid var(--nui-border, #e2e8f0);
    border-top-color: var(--nui-brand, #0074c5);
    animation: nui-spin-rotate 0.8s linear infinite;
    flex-shrink: 0;
}
body:has(#InciteWrapper.is-myincite-ui) .nui-spin--sm {
    width: 14px;
    height: 14px;
    border-width: 2px;
}
body:has(#InciteWrapper.is-myincite-ui) .nui-spin--lg {
    width: 32px;
    height: 32px;
    border-width: 3px;
}
body:has(#InciteWrapper.is-myincite-ui) .nui-spin--teal {
    border-top-color: #00AEC4;
}
@keyframes nui-spin-rotate {
    to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
    /* Slow, don't freeze — a static ring reads as a broken/stuck
       loader rather than a considerate animation reduction. */
    body:has(#InciteWrapper.is-myincite-ui) .nui-spin {
        animation-duration: 2s;
    }
}

/* ------------------------------------------------------------
   2a. FONTAWESOME SPINNERS — blanket color + speed normalization
   Covers .fa-spin (any icon using the standard FA spin animation),
   plus .fa-spinner / .fa-circle-notch / .fa-sync.fa-spin by name so
   the icon reads brand-colored even in the handful of places it's
   used without the .fa-spin class (e.g. "ai-loading-spinner" pairs
   fa-spinner with its own custom keyframe; a bare .fa-circle-notch
   appears as a static icon in a couple of templates). Font-size is
   deliberately untouched — buttons size these contextually and
   there's no reliable way to detect "unstyled default" globally.
   ------------------------------------------------------------ */
.is-myincite-ui .fa-spin,
.is-myincite-ui .fa-spinner,
.is-myincite-ui .fa-circle-notch,
.is-myincite-ui .fa-sync.fa-spin {
    color: var(--nui-brand) !important;
    animation-duration: 0.9s !important;
    animation-timing-function: linear !important;
}
@media (prefers-reduced-motion: reduce) {
    .is-myincite-ui .fa-spin,
    .is-myincite-ui .fa-spinner,
    .is-myincite-ui .fa-circle-notch,
    .is-myincite-ui .fa-sync.fa-spin {
        animation-duration: 2s !important;
    }
}

/* ==========================================================================
   2b. ONE INDICATOR EVERYWHERE — three bouncing dots
   Every legacy loading hook in the app used to draw its own mark (ei.svg,
   blinking .dot spans, the aa-spinner SVG) and the first New UI pass layered
   a phoenix mascot on top of each, so pages showed two loaders at once.
   Under the New UI each hook now shows exactly one thing: the same
   three-dot bouncer the AI chat and the new Grading Center already use.
   Single pseudo-element, no markup change: three radial-gradient layers
   whose background-position is animated in turn.
   Hooks covered:
     - #loading-app-spinner           full-page app loader
     - div[id*="spinner"|"loader"]    jQuery-toggled overlay blocks
     - .aa-loading (<aa-spinner>)     widget loader
     - .inline-loader / .grid-loader  ei.svg box loaders
   Not touched: .fa-spin icons inside buttons (2a colours them), and
   .load-percent, which is a real progress bar.
   ========================================================================== */
@keyframes nui-dots-bounce {
    20% { background-position: 0%   0%, 50%  50%, 100%  50%; }
    40% { background-position: 0% 100%, 50%   0%, 100%  50%; }
    60% { background-position: 0%  50%, 50% 100%, 100%   0%; }
    80% { background-position: 0%  50%, 50%  50%, 100% 100%; }
}
/* the dots themselves - shared by every hook's pseudo-element below */
body:has(#InciteWrapper.is-myincite-ui) #loading-app-spinner::after,
body.nui-on #loading-app-spinner::after,
.is-myincite-ui div[id*="Loader"]::after,
.is-myincite-ui div[id*="Spinner"]::after,
.is-myincite-ui div[id*="loader"]::after,
.is-myincite-ui div[id*="spinner"]::after,
body:has(#InciteWrapper.is-myincite-ui) .aa-loading > div::before,
.is-myincite-ui .inline-loader::after,
.is-myincite-ui .grid-loader::after {
    content: "";
    display: block;
    width: 44px;
    height: 22px;
    --nui-dot: no-repeat radial-gradient(circle closest-side, var(--nui-brand, #0074c5) 92%, #0000);
    background: var(--nui-dot) 0% 50%, var(--nui-dot) 50% 50%, var(--nui-dot) 100% 50%;
    background-size: 10px 10px;
    animation: nui-dots-bounce 1s infinite linear;
    pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
    body:has(#InciteWrapper.is-myincite-ui) #loading-app-spinner::after,
    body.nui-on #loading-app-spinner::after,
    .is-myincite-ui div[id*="Loader"]::after,
    .is-myincite-ui div[id*="Spinner"]::after,
    .is-myincite-ui div[id*="loader"]::after,
    .is-myincite-ui div[id*="spinner"]::after,
    body:has(#InciteWrapper.is-myincite-ui) .aa-loading > div::before,
    .is-myincite-ui .inline-loader::after,
    .is-myincite-ui .grid-loader::after {
        animation-duration: 2.5s;
    }
}

/* ---- #loading-app-spinner: soft wash, dots above the centre, message below.
   ::before was the phoenix (new-ui-theme.css used to draw it); the legacy
   blinking .dot spans go too. The <p> main.js injects ("Switching to …")
   stays, under the dots. */
body:has(#InciteWrapper.is-myincite-ui) #loading-app-spinner,
body.nui-on #loading-app-spinner {
    background-image: none !important;
    background-color: rgba(248, 250, 252, 0.72) !important;
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}
body:has(#InciteWrapper.is-myincite-ui) #loading-app-spinner::before,
body.nui-on #loading-app-spinner::before,
body:has(#InciteWrapper.is-myincite-ui) #loading-app-spinner .dot,
body.nui-on #loading-app-spinner .dot {
    display: none;
}
body:has(#InciteWrapper.is-myincite-ui) #loading-app-spinner::after,
body.nui-on #loading-app-spinner::after {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -48px);
}
body:has(#InciteWrapper.is-myincite-ui) #loading-app-spinner p,
body.nui-on #loading-app-spinner p {
    color: var(--nui-text-muted, #64748b);
    font-size: 14px;
    text-align: center;
}

/* ---- jQuery-toggled overlay blocks: legacy draws ei.svg at center/33%;
   the dots take that spot. */
.is-myincite-ui div[id*="Loader"],
.is-myincite-ui div[id*="Spinner"],
.is-myincite-ui div[id*="loader"],
.is-myincite-ui div[id*="spinner"] {
    background-image: none !important;
}
.is-myincite-ui div[id*="Loader"]::after,
.is-myincite-ui div[id*="Spinner"]::after,
.is-myincite-ui div[id*="loader"]::after,
.is-myincite-ui div[id*="spinner"]::after {
    position: absolute;
    left: 50%;
    top: 33%;
    transform: translate(-50%, 0);
}
/* the full-page loader matches [id*="loader"] too; keep its own placement */
.is-myincite-ui #loading-app-spinner::after {
    position: fixed;
    top: 50%;
    transform: translate(-50%, -48px);
}

/* ---- <aa-spinner>: hide its SVG, dots in its place */
body:has(#InciteWrapper.is-myincite-ui) .aa-loading svg,
body:has(#InciteWrapper.is-myincite-ui) .aa-loading img {
    display: none;
}
body:has(#InciteWrapper.is-myincite-ui) .aa-loading > div::before {
    margin: 0 auto 8px;
    flex: none;
}
body:has(#InciteWrapper.is-myincite-ui) .bottom-loading .aa-loading > div::before,
body:has(#InciteWrapper.is-myincite-ui) .aa-loading.bottom-loading > div::before {
    width: 32px;
    height: 16px;
    background-size: 7px 7px;
}

/* ---- ei.svg box loaders */
.is-myincite-ui .inline-loader,
.is-myincite-ui .grid-loader {
    background-image: none !important;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ---- uib-progressbar / .progress: brand colour, unchanged behaviour */
.is-myincite-ui .progress-bar {
    background-color: var(--nui-brand, #0074c5);
}

/* ---- Boot / sign-in screen (.nui-boot): #nui-boot in index.html before Angular, and
   #nuiSignin on the New UI login the moment the form is submitted. Same look so the
   hand-off reads as one continuous moment. ----------------------------------------- */
.nui-boot {
    position: fixed; inset: 0; z-index: 100000;
    align-items: center; justify-content: center;
    background: radial-gradient(120% 90% at 50% 0%, #EAF4FB 0%, #F7F9FA 55%, #F3F6F8 100%);
    font-family: "Montserrat", "Open Sans", sans-serif;
    color: #222831;
    opacity: 1;
    transition: opacity .4s ease;
}
.nui-boot.is-leaving { opacity: 0; pointer-events: none; }
.nui-signin { display: none; opacity: 0; }
.nui-signin.is-on { display: flex; animation: nui-boot-in .35s ease forwards; }
@keyframes nui-boot-in { to { opacity: 1; } }
.nui-boot-card { display: flex; flex-direction: column; align-items: center; gap: 14px; transform: translateY(-4vh); }
.nui-boot-mark { font-size: 34px; font-weight: 800; letter-spacing: -.6px; color: #222831; display: flex; align-items: center; gap: 4px; }
.nui-boot-mark span {
    display: inline-flex; align-items: center; justify-content: center;
    width: 54px; height: 54px; border-radius: 50%;
    background: linear-gradient(135deg, #0074C5, #00AEC4); color: #fff;
    font-size: 22px; font-weight: 700; font-style: italic; letter-spacing: 0;
    box-shadow: 0 12px 30px rgba(0, 116, 197, .28);
}
.nui-boot-dots {
    width: 44px; height: 22px; margin-top: 6px;
    --nui-dot: no-repeat radial-gradient(circle closest-side, #0074C5 92%, #0000);
    background: var(--nui-dot) 0% 50%, var(--nui-dot) 50% 50%, var(--nui-dot) 100% 50%;
    background-size: 10px 10px;
    animation: nui-dots-bounce 1s infinite linear;
}
.nui-boot-msg { font-size: 18px; font-weight: 700; letter-spacing: -.2px; }
.nui-boot-msg::after { content: "…"; }
.nui-boot-sub { font-size: 13px; color: #7B8E92; font-weight: 500; margin-top: -8px; }
.nui-boot-bar { width: 220px; height: 4px; border-radius: 999px; background: rgba(0, 116, 197, .12); overflow: hidden; margin-top: 10px; }
.nui-boot-bar i { display: block; width: 40%; height: 100%; border-radius: 999px; background: linear-gradient(90deg, #0074C5, #00AEC4); animation: nui-boot-slide 1.4s cubic-bezier(.4, 0, .2, 1) infinite; }
@keyframes nui-boot-slide { from { transform: translateX(-120%); } to { transform: translateX(300%); } }
@media (prefers-reduced-motion: reduce) {
    .nui-boot-dots { animation-duration: 2.5s; }
    .nui-boot-bar i { animation-duration: 3s; }
}
