/* NIOL — the anonymous screens (login, password recovery, MFA), brand "Onda".
   Loaded after app.css: it only adds this section's own components, like history.css does.

   The FORM markup belongs to Scarnas (LoginTwoSteps / InputPassword / ResetPassword) and cannot be edited
   here, so the rules below reach it from the outside, through the Bootstrap classes the package emits
   (.form-floating, .form-control, .btn-primary, .btn-link, .callout, .input-group). They are all scoped
   under .niol-login, so nothing leaks onto the rest of the app.

   Both themes: the shell replaces the package's container, which forced data-bs-theme="light" on itself —
   here the tokens follow whatever theme the visitor already had. */

/* ---------- layout ---------- */

.niol-login {
    /* Also the containing block for the trace, which is positioned against the whole screen. */
    position: relative;
    display: grid;
    grid-template-columns: 55% 45%;
    min-height: 100vh;
    min-height: 100dvh;
    /* One field across BOTH columns, so the form side continues the panel instead of butting against
       it. It is also the fallback while no image is installed. */
    background:
        radial-gradient(90% 70% at 8% 8%, color-mix(in srgb, var(--niol-accent) 16%, transparent) 0%, transparent 55%),
        radial-gradient(46% 42% at 97% 3%, color-mix(in srgb, var(--niol-accent) 13%, transparent) 0%, transparent 62%),
        radial-gradient(70% 60% at 55% 100%, color-mix(in srgb, var(--niol-accent) 9%, transparent) 0%, transparent 60%),
        linear-gradient(160deg, var(--niol-panel-2) 0%, var(--niol-bg) 70%);
    background-attachment: fixed;
}

@media (max-width: 1200px) {
    .niol-login {
        grid-template-columns: 50% 50%;
    }
}

/* Same 900px threshold the configuration tree uses to drop its side panel — not a new breakpoint. */
@media (max-width: 900px) {
    .niol-login {
        grid-template-columns: 1fr;
    }

    .niol-login__visual {
        display: none;
    }
}

/* ---------- the image panel ---------- */

/* Transparent: the gradient it used to own now belongs to the whole screen, and shows through both
   here (until an image loads) and beside the form. The images fade out towards the form so the two
   halves meet on that shared field instead of on a hard edge. */
.niol-login__visual {
    position: relative;
    overflow: hidden;
    /* Eased, not a straight ramp: a linear fade has a perceptual corner at each end, and against a light
       theme (dark photo meeting a pale page) those corners read as a hard edge. The stops below trace an
       S-curve — flat, steep, flat — over half the panel, so neither end has a visible start. */
    -webkit-mask-image: linear-gradient(90deg,
            rgba(0, 0, 0, 1) 0%,
            rgba(0, 0, 0, 1) 46%,
            rgba(0, 0, 0, .985) 54%,
            rgba(0, 0, 0, .95) 61%,
            rgba(0, 0, 0, .88) 67%,
            rgba(0, 0, 0, .77) 73%,
            rgba(0, 0, 0, .62) 79%,
            rgba(0, 0, 0, .45) 85%,
            rgba(0, 0, 0, .28) 90%,
            rgba(0, 0, 0, .13) 95%,
            rgba(0, 0, 0, .04) 98%,
            rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(90deg,
            rgba(0, 0, 0, 1) 0%,
            rgba(0, 0, 0, 1) 46%,
            rgba(0, 0, 0, .985) 54%,
            rgba(0, 0, 0, .95) 61%,
            rgba(0, 0, 0, .88) 67%,
            rgba(0, 0, 0, .77) 73%,
            rgba(0, 0, 0, .62) 79%,
            rgba(0, 0, 0, .45) 85%,
            rgba(0, 0, 0, .28) 90%,
            rgba(0, 0, 0, .13) 95%,
            rgba(0, 0, 0, .04) 98%,
            rgba(0, 0, 0, 0) 100%);
}

.niol-login__frame {
    position: absolute;
    inset: 0;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    /* A still panel shows the one image; a cycling stack starts hidden and is revealed by the animation. */
    opacity: 1;
}

.niol-login__frame.is-cycling {
    opacity: 0;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
    /* duration and delay are inline: they depend on how many images are configured. */
}

/* One keyframe set per image count, because a CSS percentage cannot be computed: with N images each one
   owns 100/N% of the cycle, so the stops are 20/N (faded in), 120/N (starts leaving) and 140/N (gone).
   The outgoing image only starts to fade AFTER the incoming one is fully opaque, so the crossfade never
   dips toward the gradient underneath; the last image fades out over the first, which by then is already
   opaque, so the wrap is seamless too. Enumerated 2..8 — LoginImages.MaxLayers bounds it. */
.niol-login__visual--n2 .niol-login__frame {
    animation-name: niol-login-fade-2;
}

@keyframes niol-login-fade-2 {
    0% {
        opacity: 0;
    }

    10% {
        opacity: 1;
    }

    60% {
        opacity: 1;
    }

    70% {
        opacity: 0;
    }

    100% {
        opacity: 0;
    }
}

.niol-login__visual--n3 .niol-login__frame {
    animation-name: niol-login-fade-3;
}

@keyframes niol-login-fade-3 {
    0% {
        opacity: 0;
    }

    6.667% {
        opacity: 1;
    }

    40% {
        opacity: 1;
    }

    46.667% {
        opacity: 0;
    }

    100% {
        opacity: 0;
    }
}

.niol-login__visual--n4 .niol-login__frame {
    animation-name: niol-login-fade-4;
}

@keyframes niol-login-fade-4 {
    0% {
        opacity: 0;
    }

    5% {
        opacity: 1;
    }

    30% {
        opacity: 1;
    }

    35% {
        opacity: 0;
    }

    100% {
        opacity: 0;
    }
}

.niol-login__visual--n5 .niol-login__frame {
    animation-name: niol-login-fade-5;
}

@keyframes niol-login-fade-5 {
    0% {
        opacity: 0;
    }

    4% {
        opacity: 1;
    }

    24% {
        opacity: 1;
    }

    28% {
        opacity: 0;
    }

    100% {
        opacity: 0;
    }
}

.niol-login__visual--n6 .niol-login__frame {
    animation-name: niol-login-fade-6;
}

@keyframes niol-login-fade-6 {
    0% {
        opacity: 0;
    }

    3.333% {
        opacity: 1;
    }

    20% {
        opacity: 1;
    }

    23.333% {
        opacity: 0;
    }

    100% {
        opacity: 0;
    }
}

.niol-login__visual--n7 .niol-login__frame {
    animation-name: niol-login-fade-7;
}

@keyframes niol-login-fade-7 {
    0% {
        opacity: 0;
    }

    2.857% {
        opacity: 1;
    }

    17.143% {
        opacity: 1;
    }

    20% {
        opacity: 0;
    }

    100% {
        opacity: 0;
    }
}

.niol-login__visual--n8 .niol-login__frame {
    animation-name: niol-login-fade-8;
}

@keyframes niol-login-fade-8 {
    0% {
        opacity: 0;
    }

    2.5% {
        opacity: 1;
    }

    15% {
        opacity: 1;
    }

    17.5% {
        opacity: 0;
    }

    100% {
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .niol-login__frame.is-cycling {
        animation: none;
        opacity: 0;
    }

    /* Without motion, the first image simply stays. */
    .niol-login__frame.is-cycling:first-child {
        opacity: 1;
    }
}

/* ---------- the occasional trace ---------- */

/* A fragment, not a rule across the screen: left, width and top are all set by login-pulse.js on every
   pass — CSS can stagger a fixed rotation, it cannot pick a new place and size each time. The box IS the
   fragment, which is what lets the mask below fade exactly its two ends rather than the screen's. */
.niol-login__trace {
    position: absolute;
    left: 0;
    width: 0;
    height: 64px;
    margin-top: -32px;
    z-index: 1;
    opacity: 0;
    pointer-events: none;
    /* Both ends fade out, so the fragment reads as a window onto something continuous instead of a line
       someone cut. The 14% quiet leader the path carries is there to give this plain line to work on. */
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 13%, #000 87%, transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 13%, #000 87%, transparent 100%);
    /* How bright the trace gets at its peak. It is a variable because the two themes need different
       answers: 55% of a bright lime reads over a dark photo, while the same value over the light theme's
       near-white page is invisible — measured, the line simply vanished past the image panel. */
    --niol-trace-peak: .55;
}

[data-bs-theme="light"] .niol-login__trace,
:root[data-theme="light"] .niol-login__trace {
    --niol-trace-peak: .9;
}

.niol-login__trace svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
}

.niol-login__trace-path {
    fill: none;
    stroke: var(--niol-accent);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    /* The dash pair is set inline by the script from the measured path length; until a pass runs there is
       nothing to hide, so the path starts fully dashed off via the offset it also writes. */
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--niol-accent) 55%, transparent));
}

/* A glow spends itself on a white page: the light theme gets weight instead, on the darker lime the
   tokens already switch to. */
[data-bs-theme="light"] .niol-login__trace-path,
:root[data-theme="light"] .niol-login__trace-path {
    stroke-width: 2.5;
    filter: none;
}

/* Added by the script for one pass, then removed. The opacity is now flat for the whole run: the line has
   nothing to fade, because it arrives and leaves by drawing. */
.niol-login__trace.is-running {
    opacity: var(--niol-trace-peak);
}

/* The duration is written inline by the script, from the fragment's own measured length: the pen keeps a
   constant speed, so a short piece is written quickly and a long one slowly. The value here is only the
   fallback for the frame before the script has spoken. */
.niol-login__trace.is-running .niol-login__trace-path {
    animation: niol-login-trace-sweep 4s ease-in-out forwards;
}

/* One continuous offset, from +len through 0 to -len, and that single sign change is the whole trick.
   The dash is exactly one path long with a gap just as long, so the offset places it like this:
     +len  the dash sits entirely BEFORE the path — nothing drawn
        0  the dash covers the path exactly — fully drawn
     -len  the dash has moved entirely PAST the path — nothing drawn again
   Between the first two the head advances left to right; between the last two the tail follows it in the
   same direction. So it leaves the way it arrived, instead of dissolving on the spot.
   The length arrives as --niol-trace-len, written by the script after it measures the new path. */
@keyframes niol-login-trace-sweep {
    0% {
        stroke-dashoffset: var(--niol-trace-len);
    }

    45% {
        stroke-dashoffset: 0;
    }

    55% {
        stroke-dashoffset: 0;
    }

    100% {
        stroke-dashoffset: calc(var(--niol-trace-len) * -1);
    }
}

/* Absent, not slower — the same posture the cross-fade takes. The script checks this too, so the timer
   never even starts. */
@media (prefers-reduced-motion: reduce) {
    .niol-login__trace {
        display: none;
    }
}

/* ---------- the form panel ---------- */

.niol-login__panel {
    position: relative;
    /* Above the trace (z-index 1): the two bands keep it clear of the card, but a short viewport must not
       let it draw across the card's face. */
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--niol-space-6) var(--niol-space-5);
}

/* Pinned rather than stacked under the card: the card stays optically centred in its column, which a
   flow-positioned footer would push off. */
.niol-login__powered {
    position: absolute;
    bottom: var(--niol-space-5);
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: var(--niol-space-2);
    color: var(--niol-text-subtle);
    font-size: 12px;
    letter-spacing: .04em;
    white-space: nowrap;
}

.niol-login__powered img {
    height: 2rem;
    width: auto;
    opacity: .75;
}

/* Under 900px the image column is gone and the form owns the screen: keep the mark clear of the card. */
@media (max-width: 900px) {
    .niol-login__panel {
        padding-bottom: var(--niol-space-7);
    }
}

.niol-login__card {
    width: 100%;
    max-width: 460px;
    padding: var(--niol-space-7) var(--niol-space-6);
    background: color-mix(in srgb, var(--niol-panel) 82%, transparent);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius);
    box-shadow: var(--niol-shadow);
    backdrop-filter: blur(18px);
    color: var(--niol-text);
}

.niol-login__brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--niol-space-3);
    margin-bottom: var(--niol-space-7);
}

/* The brand pair is app.css's .niol-logo (theme swap included); here it just gets its own size. */
.niol-login__logo {
    height: 46px;
}

.niol-login__title {
    font-family: var(--niol-font-display);
    font-size: var(--niol-fs-h2);
    font-weight: 600;
    color: var(--niol-text);
    word-break: break-word;
}

/* ---------- the package's form, restyled from the outside ---------- */

.niol-login .form-floating {
    position: relative;
}

.niol-login .form-control {
    width: 100%;
    padding: 22px var(--niol-space-4) 8px;
    background: var(--niol-panel-2);
    border: 1px solid var(--niol-border);
    border-radius: var(--niol-radius-sm);
    color: var(--niol-text);
    font-family: var(--niol-font-body);
    font-size: var(--niol-fs-body);
    line-height: 1.4;
    transition: border-color .15s ease, box-shadow .15s ease;
}

.niol-login .form-control:focus {
    outline: none;
    border-color: var(--niol-accent);
    box-shadow: 0 0 0 3px var(--niol-accent-soft);
}

.niol-login .form-control:-webkit-autofill {
    /* Chrome paints its own pale yellow over the field; a large inset shadow is the only way back. */
    -webkit-text-fill-color: var(--niol-text);
    -webkit-box-shadow: 0 0 0 1000px var(--niol-panel-2) inset;
}

/* The floating label: the package always renders it, so it is styled rather than hidden. */
.niol-login .form-floating > .form-label,
.niol-login .form-floating > label {
    position: absolute;
    top: 6px;
    left: calc(var(--niol-space-4) + 1px);
    margin: 0;
    padding: 0;
    color: var(--niol-text-subtle);
    font-size: var(--niol-fs-label);
    letter-spacing: .04em;
    text-transform: uppercase;
    pointer-events: none;
}

/* Bootstrap animates its floating label into the field: on focus (and once filled) it applies a
   transform, repaints the input's padding, and draws label::after — an opaque rectangle in
   --bs-body-bg meant to punch a hole in the input's border. Our label is already parked at the top of
   the field and never travels, so all three are pure artefacts: the badge flashes a light block under
   the label and the text jumps. Each selector below mirrors Bootstrap's own, to outrank it. */
.niol-login .form-floating > .form-control:focus ~ label,
.niol-login .form-floating > .form-control:not(:placeholder-shown) ~ label,
.niol-login .form-floating > .form-control:-webkit-autofill ~ label {
    color: var(--niol-text-subtle);
    transform: none;
}

.niol-login .form-floating > .form-control:focus ~ label::after,
.niol-login .form-floating > .form-control:not(:placeholder-shown) ~ label::after,
.niol-login .form-floating > .form-control:-webkit-autofill ~ label::after {
    content: none;
}

.niol-login .form-floating > .form-control:focus,
.niol-login .form-floating > .form-control:not(:placeholder-shown),
.niol-login .form-floating > .form-control:-webkit-autofill {
    padding: 22px var(--niol-space-4) 8px;
}

/* Password field: input + reveal button share one row. */
.niol-login .input-group {
    display: flex;
    align-items: stretch;
}

.niol-login .input-group > .form-floating {
    flex: 1 1 auto;
}

.niol-login .input-group > .form-floating > .form-control {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    border-right: 0;
}

.niol-login .input-group > .btn {
    flex: 0 0 auto;
    padding: 0 var(--niol-space-4);
    background: var(--niol-panel-2);
    border: 1px solid var(--niol-border);
    border-left: 0;
    border-radius: 0 var(--niol-radius-sm) var(--niol-radius-sm) 0;
    color: var(--niol-text-muted);
    cursor: pointer;
}

.niol-login .input-group > .btn:hover {
    color: var(--niol-text);
}

/* The password field is two joined elements but one control: only the input can take focus, so left to
   itself the ring would light up three of the four sides and stop against the reveal button. The group
   carries the ring instead, and both halves take the accent border. */
.niol-login .input-group:focus-within {
    border-radius: var(--niol-radius-sm);
    box-shadow: 0 0 0 3px var(--niol-accent-soft);
}

.niol-login .input-group:focus-within > .form-floating > .form-control,
.niol-login .input-group:focus-within > .btn {
    border-color: var(--niol-accent);
}

.niol-login .input-group > .form-floating > .form-control:focus {
    box-shadow: none;
}

/* Primary action: one accent per composition (tokens.css) — the lime fill is it. */
.niol-login .btn.btn-primary {
    display: block;
    width: 100%;
    padding: 14px var(--niol-space-4);
    background: var(--niol-accent);
    border: 0;
    border-radius: var(--niol-radius-sm);
    color: var(--niol-on-accent);
    font-family: var(--niol-font-display);
    font-size: 16px;
    font-weight: 600;
    letter-spacing: .01em;
    cursor: pointer;
    transition: background-color .15s ease, transform .05s ease;
}

.niol-login .btn.btn-primary:hover {
    background: var(--niol-accent-hover);
}

.niol-login .btn.btn-primary:active {
    transform: translateY(1px);
}

.niol-login .btn.btn-primary:focus-visible {
    outline: 2px solid var(--niol-accent);
    outline-offset: 3px;
}

/* Secondary actions (password recovery, "no account yet", the step-back arrow) stay quiet. */
/* Direct children of the form are the standalone links (password recovery); the step-back arrow sits
   inside its own flex row, so it keeps its place on the left. */
.niol-login form > .btn.btn-link,
.niol-login .niol-login__card > p,
.niol-login form > p {
    display: block;
    text-align: center;
}

.niol-login .btn.btn-link {
    display: inline-block;
    padding: var(--niol-space-2) 0;
    background: none;
    border: 0;
    color: var(--niol-text-muted);
    font-size: 14px;
    text-decoration: none;
    cursor: pointer;
}

.niol-login .btn.btn-link:hover {
    color: var(--niol-accent);
    text-decoration: underline;
}

/* The chosen username, echoed above the password step. */
.niol-login .text-break {
    color: var(--niol-text-muted);
    word-break: break-word;
}

/* Error callout (wrong credentials, locked account, …). */
.niol-login .callout {
    padding: var(--niol-space-3) var(--niol-space-4);
    background: var(--niol-alarm-soft);
    border: 1px solid color-mix(in srgb, var(--niol-alarm) 45%, transparent);
    border-radius: var(--niol-radius-sm);
    color: var(--niol-text);
    font-size: 14px;
    text-align: left;
}

.niol-login .callout .fa-triangle-exclamation {
    color: var(--niol-alarm);
}

/* The package closes with a footer paragraph (version / copyright). */
.niol-login .niol-login__card p {
    color: var(--niol-text-subtle);
    font-size: 13px;
}

.niol-login .niol-login__card a {
    color: var(--niol-text-muted);
}

.niol-login .niol-login__card a:hover {
    color: var(--niol-accent);
}
