/* ============================================================
   RONIN Edge — startup / loading experience
   Professional loader on a deep brand-blue canvas: faint dot
   grid, fine-line geometry, a thin arc spinner, the white
   wordmark with a shimmer sweep and a progress bar driven by
   Blazor's --blazor-load-percentage variables. The blue base
   keeps the hand-off gentle whether the app resolves to the
   light or dark theme.

   Brand palette (Admin.Domain/UI/BrandColours.cs):
     VividBlue #2e38b6 · Purple #5b1896 · Cream #f7f5f2
     Mist #e4eff3 · DarkGrey #333
   ============================================================ */

/* ---------- full-screen backdrop while the app boots ---------- */

.app-starting {
    position: fixed;
    inset: 0;
    overflow: hidden;
    background-color: #232a8f;
    background-image:
        /* faint dot grid over a deep blue→violet gradient */
        radial-gradient(rgba(255, 255, 255, .05) 1px, transparent 1.5px),
        linear-gradient(160deg, #2e38b6 0%, #232a8f 55%, #2c1a5e 100%);
    background-size: 26px 26px, cover;
}

/* Two soft colour washes on pseudo-elements so they survive Blazor
   wiping #app's children when the runtime starts. */
.app-starting::before,
.app-starting::after {
    content: "";
    position: absolute;
    width: 60vmax;
    height: 60vmax;
    border-radius: 50%;
    filter: blur(80px);
    pointer-events: none;
    will-change: transform;
}

.app-starting::before {
    top: -30vmax;
    left: -18vmax;
    background: radial-gradient(circle at 45% 45%, rgba(109, 124, 255, .28) 0%, rgba(228, 239, 243, .05) 55%, transparent 70%);
    animation: wash-drift 18s ease-in-out infinite alternate;
}

.app-starting::after {
    bottom: -32vmax;
    right: -20vmax;
    background: radial-gradient(circle at 55% 55%, rgba(91, 24, 150, .35) 0%, rgba(91, 24, 150, .06) 55%, transparent 70%);
    animation: wash-drift 22s ease-in-out infinite alternate-reverse;
}

@keyframes wash-drift {
    from { transform: translate(0, 0) scale(1); }
    to   { transform: translate(5vmax, 3vmax) scale(1.1); }
}

.loader-backdrop {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* cursor spotlight — a very gentle lift that follows the pointer */
.loader-backdrop::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(360px circle at var(--cx, 50%) var(--cy, 40%),
        rgba(255, 255, 255, .07) 0%, rgba(255, 255, 255, .03) 45%, transparent 70%);
    transition: background .2s ease-out;
}

/* ---------- fine-line background geometry (parallax via --px / --py) ---------- */

.geo {
    position: absolute;
    border-radius: 50%;
    transform: translate(calc(var(--px, 0) * var(--depth, 0) * 1px),
                         calc(var(--py, 0) * var(--depth, 0) * 1px));
    transition: transform .5s cubic-bezier(.2, .8, .3, 1);
    will-change: transform;
}

/* large dashed ring, rotating imperceptibly slowly */
.geo-1 {
    width: 46vmax;
    height: 46vmax;
    top: -18vmax;
    right: -14vmax;
    border: 1px dashed rgba(255, 255, 255, .16);
}

.geo-1::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: inherit;
    animation: geo-spin 120s linear infinite;
}

/* concentric thin rings, bottom-left */
.geo-2 {
    width: 34vmax;
    height: 34vmax;
    bottom: -16vmax;
    left: -10vmax;
    border: 1px solid rgba(255, 255, 255, .1);
}

.geo-2::before,
.geo-2::after {
    content: "";
    position: absolute;
    inset: 12%;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .07);
}

.geo-2::after {
    inset: 26%;
    border-color: rgba(255, 255, 255, .09);
    border-style: dashed;
    animation: geo-spin 90s linear infinite reverse;
}

/* small accent ring with a single orbiting tick */
.geo-3 {
    width: 11vmax;
    height: 11vmax;
    top: 22%;
    left: 16%;
    border: 1px solid rgba(255, 255, 255, .14);
}

.geo-3::before {
    content: "";
    position: absolute;
    inset: -3px;
    border-radius: 50%;
    border: 1px solid transparent;
    border-top-color: rgba(255, 255, 255, .55);
    animation: geo-spin 14s linear infinite;
}

@keyframes geo-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* ---------- click ripple (created by the boot script) ---------- */

.ripple {
    position: absolute;
    width: 16px;
    height: 16px;
    margin: -8px 0 0 -8px;
    border: 1.5px solid rgba(255, 255, 255, .5);
    border-radius: 50%;
    animation: ripple-grow 1.1s cubic-bezier(.2, .7, .3, 1) forwards;
}

.ripple::after {
    content: "";
    position: absolute;
    inset: -1.5px;
    border-radius: 50%;
    border: 1.5px solid rgba(255, 255, 255, .3);
    animation: ripple-inner 1.1s cubic-bezier(.2, .7, .3, 1) forwards;
}

@keyframes ripple-grow {
    from { transform: scale(1);  opacity: 1; }
    to   { transform: scale(10); opacity: 0; }
}

@keyframes ripple-inner {
    from { transform: scale(1);   opacity: 1; }
    60%  { transform: scale(.45); opacity: .8; }
    to   { transform: scale(.2);  opacity: 0; }
}

/* ---------- shared loader container ----------
   Used by both the boot loader in index.html and the in-app
   loaders (App.razor / MainLayout / auth pages). */

.loading-progress {
    position: fixed;
    inset: 0;
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 1.75rem;
    text-align: center;
    animation: loader-enter .7s cubic-bezier(.16, 1, .3, 1) both;
}

@keyframes loader-enter {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* inner stage tilts almost imperceptibly toward the cursor */
.stage {
    display: grid;
    justify-items: center;
    gap: 2rem;
    transform: perspective(1100px)
               rotateX(calc(var(--py, 0) * -1.5deg))
               rotateY(calc(var(--px, 0) * 2deg));
    transition: transform .35s ease-out;
}

.loading-progress-text {
    font-family: 'Roboto', system-ui, sans-serif;
    font-size: .75rem;
    font-weight: 500;
    letter-spacing: .4em;
    text-transform: uppercase;
    color: #5b1896;
    animation: text-breathe 2.8s ease-in-out infinite;
}

.app-starting .loading-progress-text {
    color: rgba(255, 255, 255, .8);
}

/* Blazor writes "Loading" / "NN%" into this variable during boot.
   Scoped to .boot so the literal text in the Razor loaders is untouched. */
.loading-progress.boot .loading-progress-text::after {
    content: var(--blazor-load-percentage-text, "Loading");
}

@keyframes text-breathe {
    0%, 100% { opacity: .55; }
    50%      { opacity: 1; }
}

/* ---------- halo: thin arc spinner (no centre mark) ---------- */

.halo {
    position: relative;
    width: 64px;
    height: 64px;
}

/* static track */
.halo::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, .15);
}

/* rotating gradient arc */
.halo span {
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    background: conic-gradient(from 0deg,
        transparent 0%, transparent 62%,
        rgba(255, 255, 255, .25) 74%, rgba(255, 255, 255, .8) 88%, #ffffff 100%);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px));
    mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px));
    animation: geo-spin 1.6s cubic-bezier(.45, .1, .55, .9) infinite;
}

/* ---------- wordmark: white with a shimmer sweep ----------
   The white SVG is used as a mask, so a light band can sweep
   through the letterforms themselves. */

.brand-shimmer {
    width: 280px;
    height: 48px;
    -webkit-mask: url('/img/logos/ronin-edge.svg') center / contain no-repeat;
    mask: url('/img/logos/ronin-edge.svg') center / contain no-repeat;
    background:
        linear-gradient(110deg, transparent 42%, rgba(178, 190, 255, .9) 50%, transparent 58%),
        linear-gradient(100deg, #ffffff, #ffffff);
    background-size: 250% 100%, 100% 100%;
    animation: brand-sweep 4s ease-in-out infinite;
}

@keyframes brand-sweep {
    0%, 25%   { background-position: 120% 0, 0 0; }
    70%, 100% { background-position: -60% 0, 0 0; }
}

/* ---------- progress bar (boot loader only) ---------- */

.progress-track {
    position: relative;
    width: 260px;
    height: 4px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .18);
    overflow: hidden;
}

.progress-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--blazor-load-percentage, 3%);
    min-width: 3%;
    border-radius: 999px;
    background: linear-gradient(90deg, #cdd4ff, #ffffff);
    transition: width .2s ease-out;
}

/* soft sheen travelling along the filled part */
.progress-fill::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 999px;
    background: linear-gradient(90deg, transparent 30%, rgba(46, 56, 182, .25) 50%, transparent 70%);
    background-size: 200% 100%;
    animation: fill-sheen 2.2s ease-in-out infinite;
}

@keyframes fill-sheen {
    from { background-position: 150% 0; }
    to   { background-position: -50% 0; }
}

/* ---------- in-app SVG spinner (App.razor / MainLayout / auth pages) ----------
   Markup: two <circle r="40%"> elements inside an 8rem svg.
   Brand blue/purple on the app background; switched to white
   while the blue boot backdrop is still up. */

.loading-progress svg {
    width: 8rem;
    height: 8rem;
    overflow: visible;
}

.loading-progress circle {
    fill: none;
    stroke-linecap: round;
    transform-origin: 50% 50%;
}

.loading-progress circle:first-child {
    stroke: rgba(46, 56, 182, .12);
    stroke-width: 3px;
}

.loading-progress circle:last-child {
    stroke: #2e38b6;
    stroke-width: 3px;
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transform: rotate(-90deg);
    transition: stroke-dasharray 0.05s ease-in-out;
}

/* Indeterminate: two counter-rotating arcs, no percentage. */
.loading-progress.indeterminate circle:first-child {
    stroke: rgba(91, 24, 150, .55);
    stroke-width: 2px;
    stroke-dasharray: 30 292;
    animation: spinner-rotate 3s linear infinite reverse;
}

.loading-progress.indeterminate circle:last-child {
    stroke-dasharray: 85 237;
    transition: none;
    animation: spinner-rotate 1.6s linear infinite,
               spinner-stretch 3.4s ease-in-out infinite;
}

/* white variant while the blue boot backdrop is present */
.app-starting .loading-progress circle:first-child {
    stroke: rgba(255, 255, 255, .35);
}

.app-starting .loading-progress circle:last-child {
    stroke: #ffffff;
}

.app-starting .loading-progress.indeterminate circle:first-child {
    stroke: rgba(255, 255, 255, .45);
}

@keyframes spinner-rotate {
    from { transform: rotate(-90deg); }
    to   { transform: rotate(270deg); }
}

@keyframes spinner-stretch {
    0%, 100% { stroke-dasharray: 45 277; }
    50%      { stroke-dasharray: 120 202; }
}

.loading-progress.indeterminate .loading-progress-text::after {
    content: "";
}

/* ---------- accessibility ---------- */

@media (prefers-reduced-motion: reduce) {
    .app-starting::before,
    .app-starting::after,
    .geo,
    .geo-1::before,
    .geo-2::after,
    .geo-3::before,
    .halo span,
    .stage,
    .brand-shimmer,
    .progress-fill::after,
    .loading-progress-text {
        animation: none;
        transition: none;
    }

    .brand-shimmer { background: #ffffff; }
}
