/* =========================================================
   SRS auth shell - login, register, OTP challenge.
   Bootstrap 5 / CoreUI v4 only. No Tailwind.
   Scoped via body:has(.auth-shell) so other plain-layout
   auth pages (password reset / verify email) keep their
   existing chrome.
   ========================================================= */

:root {
    /* --brand-primary / -accent / -grad-from / -grad-to are NOT set here.
       They vary per deployment and are emitted as a nonced <style> block by
       partials/brand-tokens.blade.php. Every var() below carries the
       shipped default as its fallback, so this stylesheet still renders
       correctly on its own. */

    --auth-violet:        #7c69ef;
    --auth-violet-600:    #5a45d6;
    --auth-violet-700:    #4730b8;
    --auth-violet-50:     #f1eefe;
    --auth-navy:          #1e1b4b;
    --auth-ink:           #111827;
    --auth-ink-soft:      #1f2937;
    --auth-muted:         #6b7280;
    --auth-border:        #e5e7eb;
    --auth-border-strong: #d1d5db;
    --auth-bg-subtle:     #f9fafb;
    --auth-danger:        #b91c1c;
    --auth-ring:          color-mix(in srgb, var(--auth-violet) 18%, transparent);
}

/* Hide the plain-layout container/footer chrome only when an auth shell is mounted. */
body:has(.auth-shell) {
    background: #fff;
    min-height: 100vh;
}
body:has(.auth-shell) > .container {
    max-width: none;
    width: 100%;
    padding: 0;
    margin: 0;
}
body:has(.auth-shell) > .app-footer.sticky-footer {
    display: none;
}

/* Fallback for browsers without :has() - overlay the shell on top of body chrome. */
.auth-shell {
    position: fixed;
    inset: 0;
    z-index: 1000;
    overflow-y: auto;
    overflow-x: hidden;
    background: #fff;
    color: var(--auth-ink);
    display: grid;
    grid-template-columns: 1fr;
    min-height: 100vh;
}
@media (min-width: 992px) {
    .auth-shell {
        grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    }
}

/* One continuous backdrop for the whole split shell.

   Deliberately on .auth-shell--split and not .auth-shell: the fullbg layout
   already owns .auth-shell--fullbg::before for its scrim, and the centered
   layout paints a gradient over the whole shell. Scoping to the split layout
   keeps those untouched.

   The panels sit on top of this and add only their own treatment - the brand
   panel its gradient, the form panel its white wash - so the photograph runs
   unbroken across the split instead of being cover-cropped twice. */
@media (min-width: 992px) {
    .auth-shell--split::before {
        content: "";
        /* FIXED, not absolute. The shell scrolls (overflow-y: auto) for forms
           taller than the viewport, so an absolutely positioned layer carrying
           scale(1.05) - and animating to 1.18 - is not clipped: it enlarges the
           scrollable area instead, and the backdrop ends up visibly bigger than
           the page it sits behind. Pinned to the viewport it can never add
           scroll height, and the Ken Burns zoom is clipped by the viewport
           edge, which is what the brand panel's overflow: hidden used to do for
           it. Same reason portal-shell.css pins the portal watermark. */
        position: fixed;
        inset: 0;
        background-image: var(--brand-bg-image, none);
        background-size: cover;
        background-position: center;
        z-index: -1;
        pointer-events: none;
    }
}

/* ======================================================
   BRAND PANEL (left)
   ====================================================== */
.auth-brand {
    position: relative;
    display: none;
    flex-direction: column;
    /* 20% inset each side, so the logo and the copy sit in the middle 60% of the
       panel rather than running to its edges. Percentage, not a fixed value, so
       the inset tracks the panel as the viewport widens. */
    padding: 2.5rem 20%;
    color: #fff;
    overflow: hidden;
    isolation: isolate;
}
@media (min-width: 992px) {
    /* min-height: the panel clips its overflow (for the FX layers), which also
       drops its automatic minimum height to zero, so on a short viewport the
       grid row stayed at 100vh and cut off the feature list and footer. Sizing
       it to its content lets the shell scroll to them instead. */
    .auth-brand { display: flex; min-height: max-content; }
}
/* The photograph is NOT painted here. It is one layer on .auth-shell--split so
   a single image spans both panels; this panel only lays its gradient over the
   shared backdrop. Painting it here as well gave each panel its own cover-crop
   of the same file, so the two halves never lined up. */
.auth-brand::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(135deg,
            rgba(30, 27, 75, 0.94) 0%,
            rgba(49, 46, 129, 0.82) 45%,
            rgba(124, 105, 239, 0.62) 100%);
    /* Text-bearing brand surface: opaque, and held to the dark end of the
       brand gradient (no more than 35% of grad-to at the far corner), so the
       copy and the footer clear WCAG AA wherever they wrap. With the IEMS
       palette the panel runs #00615E to #007774; white at 0.92 on the lightest
       point is 4.8:1. */
    background:
        linear-gradient(135deg,
            var(--brand-grad-from, #1e1b4b) 0%,
            color-mix(in srgb, var(--brand-grad-from, #1e1b4b) 80%, var(--brand-grad-to, #7c69ef)) 55%,
            color-mix(in srgb, var(--brand-grad-from, #1e1b4b) 65%, var(--brand-grad-to, #7c69ef)) 100%);
    z-index: -1;
}
/* The logo is centred; everything below it stays left-aligned. The hairline
   and the space around it are what keep the mark reading as its own block
   rather than as the first line of the copy. */
.auth-brand-logo {
    width: 100%;
    display: flex;
    justify-content: center;
    padding-block-end: 2rem;
    /* Percentage block margins resolve against the INLINE size, so 20% here is
       20% of the panel width - which is what drops the mark down the panel
       rather than sitting it against the top edge. */
    margin-block: 20% 2rem;
    border-block-end: 1px solid rgba(255, 255, 255, 0.16);
}
/* A share of the content column width, with no height cap fighting it. That
   is only an honest size for TRIMMED artwork: transparent canvas around the
   mark counts towards the 70% and shrinks it. The shipped logo_brand.png is
   trimmed to the mark; a deployment that uploads its own should trim it too.
   display:block removes the inline baseline gap under the image. */
.auth-brand-logo img {
    display: block;
    width: 70%;
    height: auto;
    object-fit: contain;
    filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.28));
}
.auth-brand-body {
    /* The divider under the logo owns the separation, so no top margin stacks
       on it. Full width of the inset column, so the copy lines up with the
       logo block above. margin-bottom:auto keeps the footer pinned to the
       bottom of the panel. */
    margin-top: 0;
    margin-bottom: auto;
    max-width: 100%;
    text-align: left;
}
.auth-brand-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.75rem;
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.92);
    background: rgba(255, 255, 255, 0.08);
    backdrop-filter: blur(8px);
    margin-bottom: 1.25rem;
}
.auth-brand-eyebrow .auth-brand-dot {
    width: 6px;
    height: 6px;
    border-radius: 999px;
    background: var(--brand-accent, #6ee7b7);
    box-shadow: 0 0 0 4px rgba(110, 231, 183, 0.22);
}
.auth-brand-title {
    font-size: clamp(2rem, 2.6vw, 2.625rem);
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.02em;
    margin: 0 0 1rem;
}
/* The product's long name, under the short one. */
.auth-brand-longname {
    font-size: 1.125rem;
    font-weight: 600;
    line-height: 1.35;
    color: #fff;
    margin: -0.5rem 0 1rem;
}
.auth-brand-sub {
    font-size: 1.0625rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.88);
    margin: 0;
    max-width: 100%;
}
.auth-brand-features {
    margin-top: 2rem;
    display: grid;
    gap: 1.125rem;
    max-width: 100%;
}
.auth-brand-feature {
    display: flex;
    /* Top-aligned, not centred: a line that wraps keeps its icon beside the
       first line instead of drifting into the middle of the paragraph. */
    align-items: flex-start;
    gap: 0.75rem;
    font-size: 0.9375rem;
    color: rgba(255, 255, 255, 0.92);
}
/* The text is one flex child - see brand-panel.blade.php. The top padding
   sets a single line on the 30px icon's centre line. */
.auth-brand-feature-text {
    line-height: 1.5;
    padding-top: 0.25rem;
}
.auth-brand-feature-icon {
    flex-shrink: 0;
    margin-top: 1px;
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.14);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: #fff;
    font-size: 0.875rem;
}
.auth-brand-footer {
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.92);
    letter-spacing: 0.3px;
    /* Copyright on the left, the vendor credit on the right, wrapping under
       each other when the column is too narrow for both. */
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.25rem 1rem;
}

/* ======================================================
   FORM PANEL (right)
   ====================================================== */
.auth-form {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 2.25rem 1.25rem 2.5rem;
    background: #fff;
}

/* A mostly-opaque white over the deployment's backdrop, so the photograph is
   sensed through the form side rather than stopping dead at the brand panel.

   The amount is per deployment: BRANDING_FORM_WASH in .env, emitted as
   --brand-form-wash by partials/brand-tokens.blade.php. The 0.35 here is only
   the fallback for when that token is absent. It is a light wash, so after
   lowering it - or swapping in a darker backdrop - re-check the muted helper
   text (--auth-muted, #6b7280), which is the first thing to lose contrast.

   A translucent background rather than `opacity` on purpose: opacity fades a
   whole subtree, so the labels, inputs and buttons would fade with it and the
   form would fail contrast. Only the panel's fill is translucent; the content
   on top stays fully opaque.

   Split-only (>=992px). Below that the brand panel is hidden and this element
   IS the page, so there is no "white side" to see through - and a photo behind
   a full-bleed mobile form costs legibility for nothing. The solid `background`
   above stays as the base, so a deployment with no background_image, or a
   browser that skips these layers, still gets a plain white panel. */
@media (min-width: 992px) {
    .auth-form--seethrough {
        background: rgba(255, 255, 255, var(--brand-form-wash, 0.35));
    }
}
@media (min-width: 576px) {
    .auth-form { padding: 2.5rem 2rem; }
}
@media (min-width: 992px) {
    .auth-form { padding: 3rem 3rem; }
}
@media (min-width: 1200px) {
    .auth-form { padding: 3.5rem 4rem; }
}
.auth-form-inner {
    width: 100%;
    max-width: 26rem;
    margin: 0 auto;
}

/* Wide variant, for the multi-section registration form.
   26rem is sized for a two-field sign-in. The registration form is an accordion
   of full organisation details with labels like "Identification / NRIC Number
   (New IC, Police, Army or Passport)", which wrap badly at that width while the
   pane around them sits half empty. This gives the form the room the pane
   already has: a wider inner cap, less side padding, and a larger share of the
   split. Paired with .auth-shell--wide-form on the shell. */
.auth-form--wide .auth-form-inner {
    max-width: 46rem;
}
@media (min-width: 992px) {
    .auth-form--wide { padding: 3rem 2.5rem; }
}
@media (min-width: 1200px) {
    .auth-form--wide { padding: 3.5rem 3rem; }
}
@media (min-width: 992px) {
    .auth-shell--wide-form {
        grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
    }
    /* The brand panel is a third narrower here, so the 20% inset would squeeze
       the copy into a column too thin for the title. 12% gives back the same
       content width the sign-in page has. */
    .auth-shell--wide-form .auth-brand {
        padding-inline: 12%;
    }
}
.auth-form-mobile-logo {
    display: block;
    text-align: center;
    margin-bottom: 1.75rem;
}
.auth-form-mobile-logo img {
    display: block;
    height: 120px;
    width: auto;
    max-width: 100%;
    margin-inline: auto;
}
/* The product name on its own centred line under the mark, so a long name
   wraps as a line of text instead of breaking up beside the image. */
.auth-form-mobile-logo .brand-wordmark {
    display: block;
    margin-top: 0.5rem;
    font-size: 1rem;
    font-weight: 600;
    color: var(--auth-ink);
}
@media (min-width: 992px) {
    .auth-form-mobile-logo { display: none; }
}
.auth-form-heading {
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.015em;
    color: var(--auth-ink);
    margin: 0 0 0.375rem;
}
.auth-form-sub {
    color: var(--auth-muted);
    font-size: 0.9375rem;
    margin: 0 0 1.75rem;
}

/* Field block */
.auth-field { margin-bottom: 1rem; }
.auth-field-label-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 0.375rem;
    gap: 0.5rem;
}
.auth-field label {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--auth-ink);
    margin: 0;
}
.auth-field-hint-link {
    font-size: 0.8125rem;
    color: var(--auth-violet-600);
    text-decoration: none;
    font-weight: 500;
}
.auth-field-hint-link:hover,
.auth-field-hint-link:focus-visible {
    color: var(--auth-violet-700);
    text-decoration: underline;
}

/* Input */
.auth-input {
    display: block;
    width: 100%;
    height: 46px;
    padding: 0.625rem 0.875rem;
    background: #fff;
    border: 1px solid var(--auth-border);
    border-radius: 10px;
    color: var(--auth-ink);
    font-size: 0.9375rem;
    line-height: 1.4;
    transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.auth-input::placeholder { color: #9ca3af; }
.auth-input:hover { border-color: var(--auth-border-strong); }
.auth-input:focus,
.auth-input:focus-visible {
    outline: none;
    border-color: var(--auth-violet-600);
    box-shadow: 0 0 0 4px var(--auth-ring);
    background: #fff;
}
.auth-input.is-invalid {
    border-color: #ef4444;
    box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.14);
}
.auth-input-error {
    display: block;
    margin-top: 0.375rem;
    font-size: 0.8125rem;
    color: var(--auth-danger);
    font-weight: 500;
}

/* Input affordances - leading icon (decorative) and trailing action (eye toggle). */
.auth-input-group { position: relative; }
.auth-input--leading { padding-left: 2.75rem; }
.auth-input--trailing { padding-right: 2.75rem; }
.auth-input-leading {
    position: absolute;
    top: 50%;
    left: 0.875rem;
    transform: translateY(-50%);
    color: #9ca3af;
    font-size: 1.0625rem;
    pointer-events: none;
    transition: color .15s ease;
}
.auth-input-group:focus-within .auth-input-leading { color: var(--auth-violet-600); }
.auth-input-action {
    position: absolute;
    top: 50%;
    right: 0.375rem;
    transform: translateY(-50%);
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--auth-muted);
    border-radius: 8px;
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
}
.auth-input-action:hover { background: var(--auth-bg-subtle); color: var(--auth-ink); }
.auth-input-action:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--auth-violet-600);
    color: var(--auth-ink);
}

/* Bootstrap strength-meter plugin output containers (bar + label). */
.auth-reset-strength-widget {
    font-weight: bold;
    padding: 6px 12px;
}

/* Checkbox */
.auth-check {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.25rem 0 1.25rem;
    font-size: 0.875rem;
    color: var(--auth-ink);
    user-select: none;
    cursor: pointer;
}
.auth-check input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 18px;
    height: 18px;
    border: 1px solid var(--auth-border-strong);
    border-radius: 5px;
    background: #fff;
    cursor: pointer;
    display: inline-grid;
    place-content: center;
    transition: background-color .15s ease, border-color .15s ease;
    flex-shrink: 0;
}
.auth-check input[type="checkbox"]::before {
    content: "";
    width: 10px;
    height: 10px;
    transform: scale(0);
    transition: transform .12s ease;
    box-shadow: inset 1em 1em #fff;
    clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.auth-check input[type="checkbox"]:checked {
    background: var(--auth-violet);
    border-color: var(--auth-violet);
}
.auth-check input[type="checkbox"]:checked::before { transform: scale(1); }
.auth-check input[type="checkbox"]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--auth-violet-600);
}

/* Primary submit */
.auth-submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    height: 46px;
    padding: 0 1rem;
    border: none;
    border-radius: 10px;
    /* The label sits on the -600/-700 ink steps, never the raw brand. */
    background: linear-gradient(135deg, var(--auth-violet-600) 0%, var(--auth-violet-700) 100%);
    color: var(--auth-on-violet, #fff);
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    cursor: pointer;
    box-shadow: 0 8px 22px -10px color-mix(in srgb, var(--auth-violet) 60%, transparent), 0 1px 2px rgba(15, 23, 42, 0.08);
    transition: transform .12s ease, box-shadow .15s ease, filter .15s ease;
}
.auth-submit:hover {
    color: var(--auth-on-violet, #fff);
    transform: translateY(-1px);
    box-shadow: 0 14px 28px -10px color-mix(in srgb, var(--auth-violet) 72%, transparent), 0 1px 2px rgba(15, 23, 42, 0.08);
    filter: brightness(1.03);
}
.auth-submit:active { transform: translateY(0); }
.auth-submit:focus-visible {
    outline: none;
    /* A solid ring: the 18% brand wash alone was about 1.2:1 on white. */
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--auth-violet-600), 0 8px 20px -10px color-mix(in srgb, var(--auth-violet) 55%, transparent);
}
.auth-submit:disabled {
    opacity: 0.55;
    cursor: not-allowed;
    transform: none;
    filter: none;
    box-shadow: none;
}
.auth-submit i { font-size: 1rem; transition: transform .15s ease; }
.auth-submit:hover i { transform: translateX(2px); }

/* Meta links (forgot password, register) */
.auth-meta-links {
    margin-top: 1.125rem;
    font-size: 0.875rem;
    color: var(--auth-muted);
    text-align: center;
}
.auth-meta-links a {
    color: var(--auth-violet-600);
    font-weight: 500;
    text-decoration: none;
}
.auth-meta-links a:hover,
.auth-meta-links a:focus-visible {
    color: var(--auth-violet-700);
    text-decoration: underline;
}

/* ======================================================
   OTP page specifics
   ====================================================== */
.auth-otp-meta {
    color: var(--auth-muted);
    font-size: 0.9375rem;
    margin: 0 0 1.25rem;
}
.auth-otp-meta strong {
    color: var(--auth-ink);
    font-weight: 600;
}
.auth-otp-input {
    height: 64px;
    text-align: center;
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: 0.4em;
    padding: 0.5rem 0.5rem 0.5rem 0.9rem; /* slight left bias to balance letter-spacing */
}
.auth-otp-countdown {
    text-align: center;
    font-size: 0.8125rem;
    color: var(--auth-muted);
    margin: 0.875rem 0 1rem;
}
.auth-otp-countdown .text-danger { color: var(--auth-danger) !important; }
/* "Remember this browser" tick and its warning line, indented to the label text. */
.auth-trust {
    margin: 0 0 1.25rem;
}
.auth-trust .auth-check {
    margin: 0;
}
.auth-trust-hint {
    margin: 0.25rem 0 0 calc(18px + 0.5rem);
    font-size: 0.8125rem;
    color: var(--auth-muted);
}
.auth-otp-resend {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    width: 100%;
    height: 42px;
    padding: 0 1rem;
    background: #fff;
    border: 1px solid var(--auth-border);
    border-radius: 10px;
    color: var(--auth-ink);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.auth-otp-resend:hover:not(:disabled) {
    background: var(--auth-bg-subtle);
    border-color: var(--auth-border-strong);
}
.auth-otp-resend:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--auth-violet-600);
    border-color: var(--auth-violet-600);
}
.auth-otp-resend:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}
.auth-otp-cancel {
    display: inline-block;
    margin-top: 0.875rem;
    background: none;
    border: none;
    color: var(--auth-muted);
    font-size: 0.8125rem;
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    border-radius: 6px;
}
.auth-otp-cancel:hover {
    color: var(--auth-ink);
    text-decoration: underline;
}
.auth-otp-cancel-row {
    text-align: center;
}

/* ======================================================
   REGISTER page - modernize Bootstrap form-control / form-select,
   accordion (.card based), and primary/secondary buttons. Scoped to
   .auth-shell so the styling never leaks to other parts of the app.
   ====================================================== */
.auth-shell .form-control,
.auth-shell .form-select {
    height: 44px;
    border-radius: 10px;
    border: 1px solid var(--auth-border);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
    transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
    font-size: 0.9375rem;
    padding: 0.5rem 0.875rem;
    color: var(--auth-ink);
    background-color: #fff;
}
.auth-shell .form-control::placeholder { color: #9ca3af; }
.auth-shell .form-control:hover,
.auth-shell .form-select:hover {
    border-color: var(--auth-border-strong);
}
.auth-shell .form-control:focus,
.auth-shell .form-select:focus {
    outline: none;
    border-color: var(--auth-violet-600);
    box-shadow: 0 0 0 4px var(--auth-ring);
}
.auth-shell .form-control.is-invalid,
.auth-shell .form-select.is-invalid {
    border-color: #ef4444;
    box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.14);
}
.auth-shell textarea.form-control {
    height: auto;
    min-height: 96px;
    padding: 0.625rem 0.875rem;
    line-height: 1.5;
}
.auth-shell input[type="file"].form-control {
    padding: 0.5rem 0.625rem;
    line-height: 1.5;
}
.auth-shell input[type="file"].form-control::file-selector-button {
    background: var(--auth-violet-50);
    border: none;
    color: var(--auth-violet-700);
    padding: 0.375rem 0.75rem;
    border-radius: 6px;
    margin-right: 0.625rem;
    font-weight: 500;
    cursor: pointer;
}
.auth-shell input[type="file"].form-control::file-selector-button:hover {
    background: #e6dff8;
}
.auth-shell .form-group { margin-bottom: 0.875rem; }
.auth-shell .form-group label,
.auth-shell label.control-label {
    display: inline-block;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--auth-ink);
    margin-bottom: 0.375rem;
}
.auth-shell .form-group .form-text,
.auth-shell .form-group span.small,
.auth-shell .form-group small {
    font-size: 0.8125rem;
    color: var(--auth-muted);
    margin-top: 0.25rem;
    display: block;
}
.auth-shell .invalid-feedback {
    display: block;
    font-size: 0.8125rem;
    color: var(--auth-danger);
    margin-top: 0.375rem;
}

/* Red asterisk on labels whose field is required. Works for plain inputs,
   selects (including select2 - the original <select> stays in the DOM as a
   hidden sibling so the adjacent-sibling selector still matches), textareas,
   and file inputs. */
.auth-shell .form-group label:has(+ input[required])::after,
.auth-shell .form-group label:has(+ select[required])::after,
.auth-shell .form-group label:has(+ textarea[required])::after,
.auth-shell .form-group label:has(+ .input-group input[required])::after {
    content: " *";
    color: var(--auth-danger);
    font-weight: 700;
    margin-left: 0.125rem;
}

/* Accordion using the legacy .card/.card-header structure */
.auth-shell .accordion { margin-bottom: 0; }
.auth-shell .accordion .card {
    border: 1px solid var(--auth-border);
    border-radius: 12px;
    margin-bottom: 0.75rem;
    background: #fff;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
    overflow: hidden;
}
.auth-shell .accordion .card-header {
    background: var(--auth-bg-subtle);
    border-bottom: 1px solid var(--auth-border);
    padding: 0;
}
.auth-shell .accordion .card-header h2 { margin: 0; }
.auth-shell .accordion .card-header .btn-link {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
    padding: 0.875rem 1rem;
    color: var(--auth-ink);
    text-decoration: none;
    font-weight: 600;
    font-size: 0.9375rem;
    background: transparent;
    border: none;
    text-align: left;
    line-height: 1.35;
    position: relative;
    transition: background-color .15s ease, color .15s ease;
}
.auth-shell .accordion .card-header .btn-link:hover {
    background: #f3f4f6;
    color: var(--auth-violet-700);
    text-decoration: none;
}
.auth-shell .accordion .card-header .btn-link::after {
    content: "\f107"; /* la la-angle-down via Line Awesome */
    font-family: "Line Awesome Free", "Line Awesome Pro", sans-serif;
    font-weight: 900;
    position: absolute;
    right: 1rem;
    top: 50%;
    transform: translateY(-50%) rotate(0deg);
    color: var(--auth-muted);
    font-size: 0.875rem;
    transition: transform .2s ease, color .15s ease;
}
.auth-shell .accordion .card-header .btn-link.collapsed::after {
    transform: translateY(-50%) rotate(-90deg);
}
.auth-shell .accordion .card-header .btn-link medium {
    display: block;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--auth-muted);
    margin-top: 0.125rem;
    padding-right: 2rem;
}
.auth-shell .accordion .card-body { padding: 1.25rem 1rem; }
@media (min-width: 576px) {
    .auth-shell .accordion .card-body { padding: 1.5rem; }
}

/* Bootstrap primary / secondary buttons inside the shell */
.auth-shell .btn-primary {
    height: 44px;
    border-radius: 10px;
    border: none;
    background: linear-gradient(135deg, var(--auth-violet-600) 0%, var(--auth-violet-700) 100%);
    color: var(--auth-on-violet, #fff);
    font-weight: 600;
    font-size: 0.9375rem;
    padding: 0 1.5rem;
    box-shadow: 0 8px 22px -10px color-mix(in srgb, var(--auth-violet) 60%, transparent);
    transition: transform .12s ease, box-shadow .15s ease, filter .15s ease;
}
.auth-shell .btn-primary:hover,
.auth-shell .btn-primary:focus {
    background: var(--auth-violet-700);
    color: var(--auth-on-violet, #fff);
    transform: translateY(-1px);
    box-shadow: 0 14px 28px -10px color-mix(in srgb, var(--auth-violet) 72%, transparent);
    filter: brightness(1.02);
}
.auth-shell .btn-primary:active { transform: translateY(0); }
.auth-shell .btn-secondary {
    height: 44px;
    border-radius: 10px;
    border: 1px solid var(--auth-border-strong);
    background: #fff;
    color: var(--auth-ink);
    font-weight: 500;
    font-size: 0.9375rem;
    padding: 0 1.25rem;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.auth-shell .btn-secondary:hover,
.auth-shell .btn-secondary:focus {
    background: var(--auth-bg-subtle);
    color: var(--auth-ink);
    border-color: var(--auth-border-strong);
}

/* select2 inside the auth-shell - bring up to match form-control. */
.auth-shell .select2-container .select2-selection--single {
    height: 44px !important;
    border-radius: 10px !important;
    border: 1px solid var(--auth-border) !important;
    padding: 0.5rem 0.875rem !important;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.auth-shell .select2-container--focus .select2-selection--single,
.auth-shell .select2-container--open .select2-selection--single {
    border-color: var(--auth-violet-600) !important;
    box-shadow: 0 0 0 4px var(--auth-ring);
}
.auth-shell .select2-container--default .select2-selection--single .select2-selection__rendered {
    color: var(--auth-ink);
    line-height: 1.4;
    padding-left: 0;
    margin-top: 0.125rem;
}
.auth-shell .select2-container--default .select2-selection--single .select2-selection__arrow {
    height: 100%;
    right: 0.5rem;
}
.auth-shell .select2-container--default .select2-selection.is-invalid {
    border-color: #ef4444 !important;
}

/* Submit row for the register page */
.auth-register-actions {
    display: flex;
    gap: 0.75rem;
    margin-top: 1.5rem;
    flex-wrap: wrap;
}
.auth-register-actions .btn-primary,
.auth-register-actions .btn-secondary {
    flex: 1 1 auto;
    min-width: 8rem;
}

/* Email-code step under the administrator email (register page).
   Markup: portal/auth/partials/register-email-code.blade.php. */
.auth-email-code {
    margin: -0.25rem 0 1rem;
    padding: 0.875rem 1rem;
    background: var(--auth-bg-subtle);
    border: 1px solid var(--auth-border);
    border-radius: 10px;
}
.auth-email-code-intro,
.auth-email-code-done {
    margin: 0 0 0.625rem;
    font-size: 0.875rem;
    color: var(--auth-muted);
}
.auth-email-code-done {
    margin: 0;
    color: var(--auth-success, #15803d);
    font-weight: 600;
}
.auth-email-code-entry label {
    display: block;
    margin-bottom: 0.375rem;
    font-size: 0.875rem;
    font-weight: 500;
}
.auth-email-code-row {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.auth-shell .auth-email-code-row .form-control.auth-email-code-input {
    flex: 0 1 11rem;
    text-align: center;
    font-size: 1.125rem;
    font-weight: 600;
    letter-spacing: 0.3em;
}
.auth-email-code-resend {
    margin-top: 0.5rem;
    padding: 0.25rem 0;
    background: none;
    border: 0;
    color: var(--auth-violet-600);
    font-size: 0.8125rem;
    cursor: pointer;
}
.auth-email-code-resend:hover:not(:disabled) { text-decoration: underline; }
.auth-email-code-resend:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--auth-violet-600);
    border-radius: 6px;
}
.auth-email-code-resend:disabled {
    color: var(--auth-muted);
    cursor: not-allowed;
}
.auth-email-code-status {
    margin: 0.5rem 0 0;
    font-size: 0.8125rem;
    color: var(--auth-muted);
}
.auth-email-code-status:empty { display: none; }
.auth-email-code-status.is-error { color: var(--auth-danger); }

/* The ALTCHA check card on the register page follows the auth accent. */
.auth-shell .srs-altcha { --cap-accent: var(--auth-violet); }

/* The compact ALTCHA strip on the sign-in form takes the form's own ink,
   field border and error colour, and sits just above Sign in. */
.auth-shell .srs-altcha--compact {
    --cap-ink: var(--auth-ink);
    --cap-muted: var(--auth-muted);
    --cap-line: var(--auth-border-strong);
    --cap-danger: var(--auth-danger);
    --cap-danger-text: var(--auth-danger);
    margin: 0 0 1rem;
}

/* ======================================================
   BRAND PANEL FX - aurora light fields + faded radar sweep
   + cinematic Ken Burns / grain / light leak. Markup lives
   in partials/auth-brand-fx.blade.php, included inside the
   .auth-brand of every auth page (login, register, forgot
   password, OTP challenge). All motion freezes under
   prefers-reduced-motion.
   ====================================================== */

/* Lift brand content above the FX layers. */
.auth-brand-logo,
.auth-brand-body,
.auth-brand-footer {
    position: relative;
    z-index: 2;
}

/* Progressive enhancement: FX layers stay hidden unless the browser
   supports every modern feature the effects rely on (aspect-ratio,
   inset, conic-gradient, mask-image). Browsers that fail the gate -
   or predate @supports entirely - render the clean static panel. */
.auth-fx { display: none; }

@supports (aspect-ratio: 1) and (inset: 0)
      and (background: conic-gradient(#000 0deg, #fff 360deg))
      and ((mask-image: radial-gradient(#000, transparent)) or (-webkit-mask-image: radial-gradient(#000, transparent))) {

/* -- everything to "end @supports FX gate" is inside the gate -- */

.auth-fx {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}
/* Radar drops below the aurora light fields (z-index 0 vs 1) and is
   well faded, so the sweep reads as a hint rather than a feature. */
.auth-fx-radar {
    opacity: .38;
    z-index: 0;
}

/* Ken Burns pan-zoom on the shared backdrop, so the whole photograph drifts as
   one. On the brand panel alone it would pan the left half against a static
   right half and tear the image down the split. */
.auth-shell--split::before {
    animation: fxKenburns 46s ease-in-out infinite alternate;
}
@keyframes fxKenburns {
    from { transform: scale(1.05); }
    to   { transform: scale(1.18) translate(-2.4%, -1.8%); }
}

/* --- aurora: blurred light fields drifting + twinkling stars --- */
.fx-blob {
    position: absolute;
    aspect-ratio: 1;
    border-radius: 50%;
    filter: blur(64px);
    mix-blend-mode: screen;
}
.fx-blob-1 {
    width: 58%;
    left: -14%;
    top: 4%;
    background: radial-gradient(circle, color-mix(in srgb, var(--auth-violet) 62%, transparent), transparent 70%);
    animation: fxBlob1 38s ease-in-out infinite alternate;
}
.fx-blob-2 {
    width: 50%;
    right: -12%;
    top: 26%;
    background: radial-gradient(circle, rgba(56, 189, 248, .42), transparent 70%);
    animation: fxBlob2 52s ease-in-out infinite alternate;
}
.fx-blob-3 {
    width: 44%;
    left: 14%;
    bottom: -16%;
    background: radial-gradient(circle, rgba(110, 231, 183, .34), transparent 70%);
    animation: fxBlob3 64s ease-in-out infinite alternate;
}
@keyframes fxBlob1 { to { transform: translate(20%, 14%) scale(1.18); } }
@keyframes fxBlob2 { to { transform: translate(-16%, 18%) scale(1.12); } }
@keyframes fxBlob3 { to { transform: translate(14%, -16%) scale(1.22); } }

.fx-starfield,
.fx-starfield-b {
    position: absolute;
    inset: 0;
    background-repeat: no-repeat;
    animation: fxTwinkle 5.5s ease-in-out infinite;
}
.fx-starfield {
    background-image:
        radial-gradient(1.7px 1.7px at 6% 16%,  rgba(255,255,255,.95), transparent 60%),
        radial-gradient(1.2px 1.2px at 14% 8%,  rgba(255,255,255,.75), transparent 60%),
        radial-gradient(1px 1px     at 24% 22%, rgba(255,255,255,.65), transparent 60%),
        radial-gradient(1.7px 1.7px at 31% 12%, rgba(255,255,255,.9),  transparent 60%),
        radial-gradient(1px 1px     at 42% 6%,  rgba(255,255,255,.6),  transparent 60%),
        radial-gradient(1.2px 1.2px at 52% 18%, rgba(255,255,255,.8),  transparent 60%),
        radial-gradient(1.7px 1.7px at 61% 10%, rgba(255,255,255,.9),  transparent 60%),
        radial-gradient(1px 1px     at 70% 24%, rgba(255,255,255,.6),  transparent 60%),
        radial-gradient(1.2px 1.2px at 78% 8%,  rgba(255,255,255,.8),  transparent 60%),
        radial-gradient(1.7px 1.7px at 86% 16%, rgba(255,255,255,.9),  transparent 60%),
        radial-gradient(1px 1px     at 93% 28%, rgba(255,255,255,.6),  transparent 60%),
        radial-gradient(1.2px 1.2px at 17% 34%, rgba(255,255,255,.7),  transparent 60%),
        radial-gradient(1px 1px     at 47% 30%, rgba(255,255,255,.6),  transparent 60%),
        radial-gradient(1.2px 1.2px at 66% 36%, rgba(255,255,255,.7),  transparent 60%),
        radial-gradient(1px 1px     at 88% 40%, rgba(255,255,255,.55), transparent 60%),
        radial-gradient(1.2px 1.2px at 9% 44%,  rgba(255,255,255,.65), transparent 60%),
        radial-gradient(1px 1px     at 37% 42%, rgba(255,255,255,.55), transparent 60%);
}
.fx-starfield-b {
    animation-duration: 7s;
    animation-delay: -3.2s;
    background-image:
        radial-gradient(1.2px 1.2px at 10% 26%, rgba(255,255,255,.75), transparent 60%),
        radial-gradient(1.7px 1.7px at 20% 14%, rgba(255,255,255,.9),  transparent 60%),
        radial-gradient(1px 1px     at 28% 6%,  rgba(255,255,255,.6),  transparent 60%),
        radial-gradient(1.2px 1.2px at 36% 20%, rgba(255,255,255,.75), transparent 60%),
        radial-gradient(1.7px 1.7px at 45% 12%, rgba(255,255,255,.9),  transparent 60%),
        radial-gradient(1px 1px     at 57% 26%, rgba(255,255,255,.6),  transparent 60%),
        radial-gradient(1.7px 1.7px at 64% 6%,  rgba(255,255,255,.9),  transparent 60%),
        radial-gradient(1.2px 1.2px at 74% 16%, rgba(255,255,255,.75), transparent 60%),
        radial-gradient(1px 1px     at 82% 30%, rgba(255,255,255,.6),  transparent 60%),
        radial-gradient(1.7px 1.7px at 90% 10%, rgba(255,255,255,.9),  transparent 60%),
        radial-gradient(1.2px 1.2px at 96% 20%, rgba(255,255,255,.7),  transparent 60%),
        radial-gradient(1px 1px     at 5% 36%,  rgba(255,255,255,.55), transparent 60%),
        radial-gradient(1.2px 1.2px at 50% 38%, rgba(255,255,255,.65), transparent 60%),
        radial-gradient(1px 1px     at 72% 42%, rgba(255,255,255,.55), transparent 60%),
        radial-gradient(1.2px 1.2px at 30% 32%, rgba(255,255,255,.65), transparent 60%);
}
@keyframes fxTwinkle {
    0%, 100% { opacity: .35; }
    50%      { opacity: .95; }
}

/* --- radar: sweep arm, range rings, pinging blips. --p is the sweep
   period; blip delays are fractions of it (blip angle / 360°) so each
   contact pings as the beam passes over it. --- */
.fx-scope {
    --p: 14s;
    position: absolute;
    left: 50%;
    top: 56%;
    width: 160%;
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
}
.fx-rings {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background:
        repeating-radial-gradient(circle at 50% 50%,
            rgba(255, 255, 255, .13) 0 1px, transparent 1px 10.5%);
    -webkit-mask-image: radial-gradient(circle, #000 0 30%, transparent 56%);
    mask-image: radial-gradient(circle, #000 0 30%, transparent 56%);
}
.fx-sweep {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: conic-gradient(from 0deg,
        transparent 0deg 295deg,
        rgba(167, 139, 250, .05) 320deg,
        rgba(196, 181, 253, .28) 354deg,
        rgba(255, 255, 255, .55) 360deg);
    -webkit-mask-image: radial-gradient(circle, #000 0 32%, transparent 54%);
    mask-image: radial-gradient(circle, #000 0 32%, transparent 54%);
    animation: fxSweep var(--p) linear infinite;
}
@keyframes fxSweep { to { transform: rotate(360deg); } }
.fx-blip {
    position: absolute;
    width: 7px;
    height: 7px;
    margin: -3.5px 0 0 -3.5px;
    border-radius: 50%;
    background: #ece8ff;
    box-shadow: 0 0 10px 2px rgba(196, 181, 253, .8);
    opacity: 0;
    animation: fxBlip var(--p) linear infinite;
}
/* angle 50° / 130° / 205° / 300° clockwise from 12 o'clock */
.fx-blip-1 { left: 71.4%; top: 32%;   animation-delay: calc(var(--p) * .139); }
.fx-blip-2 { left: 65.3%; top: 62.9%; animation-delay: calc(var(--p) * .361); }
.fx-blip-3 { left: 36%;   top: 79.9%; animation-delay: calc(var(--p) * .569); }
.fx-blip-4 { left: 29.2%; top: 38%;   animation-delay: calc(var(--p) * .833); }
@keyframes fxBlip {
    0%   { opacity: 0; transform: scale(.7); }
    2%   { opacity: 1; transform: scale(1); }
    16%  { opacity: 0; transform: scale(1.7); }
    100% { opacity: 0; transform: scale(.7); }
}

/* --- cinema: film grain + soft recurring light leak --- */
.fx-grain {
    position: absolute;
    inset: -40px;
    opacity: .06;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.6'/%3E%3C/svg%3E");
    animation: fxGrain 1s steps(1, end) infinite;
}
@keyframes fxGrain {
    0%   { transform: translate(0, 0); }
    20%  { transform: translate(-14px, 9px); }
    40%  { transform: translate(9px, -13px); }
    60%  { transform: translate(-7px, -8px); }
    80%  { transform: translate(12px, 11px); }
    100% { transform: translate(0, 0); }
}
.fx-leak {
    position: absolute;
    width: 60%;
    aspect-ratio: 1;
    right: -18%;
    top: -18%;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255, 180, 120, .32), transparent 65%);
    mix-blend-mode: screen;
    opacity: 0;
    animation: fxLeak 17s ease-in-out infinite;
}
@keyframes fxLeak {
    0%, 62% { opacity: 0; }
    74%     { opacity: .5; }
    86%     { opacity: .15; }
    100%    { opacity: 0; }
}


/* ======================================================
   FX: CONTRAILS
   Vapour trails drifting across the panel, each with a
   brighter head. Transform/opacity only.
   ====================================================== */
.auth-fx-contrails { position: absolute; inset: 0; overflow: hidden; }
.fx-trail {
    position: absolute;
    left: -46%;
    width: 44%;
    height: 2px;
    border-radius: 999px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(255, 255, 255, .05) 22%,
        rgba(255, 255, 255, .30) 76%,
        rgba(255, 255, 255, .58) 100%);
    opacity: 0;
    animation: fxTrail var(--p, 17s) linear infinite;
}
.fx-trail::after {
    content: "";
    position: absolute;
    right: -2px;
    top: 50%;
    width: 5px;
    height: 5px;
    margin-top: -2.5px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .85);
    box-shadow: 0 0 9px 2px rgba(255, 255, 255, .4);
}
@keyframes fxTrail {
    0%   { transform: translate3d(0, 0, 0) rotate(-19deg); opacity: 0; }
    10%  { opacity: 1; }
    72%  { opacity: .85; }
    100% { transform: translate3d(240%, -52%, 0) rotate(-19deg); opacity: 0; }
}
.fx-trail-1 { top: 26%; --p: 15s; animation-delay: -2s;  }
.fx-trail-2 { top: 46%; --p: 21s; animation-delay: -9s;  transform-origin: left center; }
.fx-trail-3 { top: 63%; --p: 18s; animation-delay: -14s; }
.fx-trail-4 { top: 80%; --p: 25s; animation-delay: -5s;  }

/* ======================================================
   FX: BEACON
   An aerodrome beacon - a slow rotating light arm over a
   breathing core glow.
   ====================================================== */
.auth-fx-beacon { position: absolute; inset: 0; overflow: hidden; }
.fx-beacon-core {
    position: absolute;
    left: 50%;
    top: 46%;
    width: min(48%, 340px);
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(circle,
        rgba(255, 255, 255, .30) 0%,
        rgba(255, 255, 255, .09) 40%,
        transparent 70%);
    animation: fxBeaconPulse 5.5s ease-in-out infinite;
}
@keyframes fxBeaconPulse {
    0%, 100% { opacity: .55; transform: translate(-50%, -50%) scale(1); }
    50%      { opacity: 1;   transform: translate(-50%, -50%) scale(1.12); }
}
.fx-beacon-arm {
    position: absolute;
    left: 50%;
    top: 46%;
    width: min(160%, 940px);
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: conic-gradient(from 0deg,
        rgba(255, 255, 255, .40) 0deg,
        rgba(255, 255, 255, .09) 13deg,
        transparent 36deg 360deg);
    -webkit-mask-image: radial-gradient(circle, #000 0 60%, transparent 78%);
    mask-image: radial-gradient(circle, #000 0 60%, transparent 78%);
    animation: fxBeaconSpin 9s linear infinite;
}
.fx-beacon-arm-b { opacity: .55; animation-delay: -4.5s; }
@keyframes fxBeaconSpin { to { transform: translate(-50%, -50%) rotate(360deg); } }

/* ======================================================
   FX: ISOBARS
   Drifting meteorological contour bands.
   ====================================================== */
.auth-fx-isobars { position: absolute; inset: 0; overflow: hidden; }
.fx-iso {
    position: absolute;
    inset: -35%;
    animation: fxIso var(--p, 30s) ease-in-out infinite alternate;
}
.fx-iso-1 {
    --p: 28s;
    background: repeating-radial-gradient(circle at 28% 34%,
        transparent 0 34px, rgba(255, 255, 255, .11) 34px 35px, transparent 35px 70px);
}
.fx-iso-2 {
    --p: 37s;
    opacity: .7;
    background: repeating-radial-gradient(circle at 74% 62%,
        transparent 0 46px, rgba(255, 255, 255, .09) 46px 47px, transparent 47px 94px);
}
.fx-iso-3 {
    --p: 46s;
    opacity: .5;
    background: repeating-radial-gradient(circle at 52% 88%,
        transparent 0 60px, rgba(255, 255, 255, .07) 60px 61px, transparent 61px 122px);
}
@keyframes fxIso {
    0%   { transform: translate3d(-2%, -1%, 0) scale(1); }
    100% { transform: translate3d(3%, 2%, 0) scale(1.08); }
}

/* ======================================================
   FX: HORIZON
   An attitude-indicator band with a slow roll, over a
   drifting scale of ticks.
   ====================================================== */
.auth-fx-horizon { position: absolute; inset: 0; overflow: hidden; }
.fx-horizon-band {
    position: absolute;
    left: -30%;
    right: -30%;
    top: 52%;
    height: 46%;
    transform-origin: 50% 0;
    background: linear-gradient(180deg,
        rgba(255, 255, 255, .13) 0%,
        rgba(255, 255, 255, .02) 42%,
        transparent 100%);
    animation: fxRoll 15s ease-in-out infinite alternate;
}
.fx-horizon-line {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: linear-gradient(90deg,
        transparent 0%,
        rgba(255, 255, 255, .55) 50%,
        transparent 100%);
}
@keyframes fxRoll {
    0%   { transform: rotate(-4.5deg) translateY(1%); }
    100% { transform: rotate(4.5deg) translateY(-2%); }
}
.fx-horizon-ticks {
    position: absolute;
    left: -20%;
    right: -20%;
    top: 44%;
    height: 6%;
    background: repeating-linear-gradient(90deg,
        transparent 0 54px, rgba(255, 255, 255, .22) 54px 56px, transparent 56px 110px);
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
    animation: fxTicks 24s linear infinite;
}
@keyframes fxTicks {
    0%   { transform: translateX(0); }
    100% { transform: translateX(-110px); }
}

} /* end @supports FX gate */

/* ======================================================
   FX INTENSITY MODIFIERS
   Applied via auth-fx--{low|medium|high} on the fx wrapper.
   Scales overall opacity of the entire FX container.
   ====================================================== */
.auth-fx--low    { opacity: .4; }
.auth-fx--medium { opacity: .7; }
.auth-fx--high   { opacity: 1; }

/* ======================================================
   TREATMENT: PATTERN
   .auth-treatment--pattern + .auth-pattern--{style}
   Renders an SVG-style tiled background via CSS gradients.
   ====================================================== */
.auth-treatment--pattern {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

.auth-pattern--dots {
    background-image: radial-gradient(circle, rgba(255,255,255,.22) 1px, transparent 1px);
    background-size: 22px 22px;
}

.auth-pattern--grid {
    background-image:
        linear-gradient(rgba(255,255,255,.12) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,.12) 1px, transparent 1px);
    background-size: 32px 32px;
}

.auth-pattern--rings {
    background-image: repeating-radial-gradient(
        circle at 50% 50%,
        rgba(255,255,255,.08) 0 1px,
        transparent 1px 48px
    );
}

.auth-pattern--contours {
    background-image: repeating-linear-gradient(
        -45deg,
        rgba(255,255,255,.06) 0 1px,
        transparent 1px 18px
    );
}

/* ======================================================
   TREATMENT: VIDEO
   Full-cover looping background video on the brand panel.
   ====================================================== */
.auth-treatment--video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 0;
    pointer-events: none;
}

/* ======================================================
   CANVAS FX (waves / gridscan styles)
   ====================================================== */
.fx-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

/* ======================================================
   REDUCE MOTION + PRINT - new treatment elements
   ====================================================== */
@media (prefers-reduced-motion: reduce) {
    .fx-sweep,
    .fx-blob,
    .fx-starfield,
    .fx-starfield-b {
        animation: none !important;
    }

    .auth-treatment--video {
        display: none;
    }

    .fx-canvas {
        display: none;
    }
}

@media print {
    .auth-treatment--video,
    .auth-treatment--pattern,
    .fx-canvas {
        display: none !important;
    }
}

/* ======================================================
   ENTRANCE CHOREOGRAPHY - staggered reveal on both panels.
   .auth-entrance sits on .auth-shell of every auth page.
   Form side is generic (nth-child stagger over the inner
   column, then over the form's direct children) so it works
   on login, register, forgot password and OTP alike.
   ====================================================== */
@keyframes fxRise {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}
@keyframes fxRiseSoft {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}
.auth-entrance .auth-form-inner > *,
.auth-entrance .auth-form-inner form > * {
    animation: fxRise .55s cubic-bezier(.22, 1, .36, 1) both;
}
/* The form fades via its children, not as a block. */
.auth-entrance .auth-form-inner > form { animation: none; }
.auth-entrance .auth-form-inner > :nth-child(1) { animation-delay: .04s; }
.auth-entrance .auth-form-inner > :nth-child(2) { animation-delay: .12s; }
.auth-entrance .auth-form-inner > :nth-child(3) { animation-delay: .2s; }
.auth-entrance .auth-form-inner > :nth-child(4) { animation-delay: .28s; }
.auth-entrance .auth-form-inner > :nth-child(5) { animation-delay: .36s; }
.auth-entrance .auth-form-inner > :nth-child(6) { animation-delay: .44s; }
.auth-entrance .auth-form-inner > :nth-child(7) { animation-delay: .52s; }
.auth-entrance .auth-form-inner > :nth-child(8) { animation-delay: .6s; }
.auth-entrance .auth-form-inner form > :nth-child(1)  { animation-delay: .2s; }
.auth-entrance .auth-form-inner form > :nth-child(2)  { animation-delay: .28s; }
.auth-entrance .auth-form-inner form > :nth-child(3)  { animation-delay: .36s; }
.auth-entrance .auth-form-inner form > :nth-child(4)  { animation-delay: .44s; }
.auth-entrance .auth-form-inner form > :nth-child(5)  { animation-delay: .52s; }
.auth-entrance .auth-form-inner form > :nth-child(6)  { animation-delay: .6s; }
.auth-entrance .auth-form-inner form > :nth-child(7)  { animation-delay: .68s; }
.auth-entrance .auth-form-inner form > :nth-child(8)  { animation-delay: .76s; }
.auth-entrance .auth-form-inner form > :nth-child(9)  { animation-delay: .84s; }
.auth-entrance .auth-form-inner form > :nth-child(10) { animation-delay: .92s; }
.auth-entrance .auth-brand-logo                 { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) 0s both; }
.auth-entrance .auth-brand-eyebrow              { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .18s both; }
.auth-entrance .auth-brand-title                { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .28s both; }
.auth-entrance .auth-brand-longname             { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .33s both; }
.auth-entrance .auth-brand-sub                  { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .38s both; }
.auth-entrance .auth-brand-feature:nth-child(1) { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .5s both; }
.auth-entrance .auth-brand-feature:nth-child(2) { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .58s both; }
.auth-entrance .auth-brand-feature:nth-child(3) { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .66s both; }
.auth-entrance .auth-brand-footer               { animation: fxRiseSoft .7s cubic-bezier(.22, 1, .36, 1) .74s both; }

/* Accessibility - freeze everything for reduced motion. */
@media (prefers-reduced-motion: reduce) {
    .auth-fx,
    .auth-fx *,
    .auth-shell--split::before,
    .auth-entrance .auth-form-inner > *,
    .auth-entrance .auth-form-inner form > *,
    .auth-entrance [class^="auth-brand-"],
    .auth-entrance .auth-brand-feature {
        animation: none !important;
    }
}

/* Print - drop the FX and un-stage the entrance so nothing prints
   mid-animation (fill-mode: both would otherwise print opacity 0). */
@media print {
    .auth-fx { display: none !important; }
    .auth-shell--split::before,
    .auth-entrance .auth-form-inner > *,
    .auth-entrance .auth-form-inner form > *,
    .auth-entrance [class^="auth-brand-"],
    .auth-entrance .auth-brand-feature {
        animation: none !important;
    }
}

/* ======================================================
   TENANT LAYOUT VARIANTS
   Modifier classes emitted by login.blade.php onto .auth-shell:
     auth-shell--{layout}   : split (default) | centered | fullbg | minimal | stacked
     auth-brand--{side}     : left (default)  | right
     auth-card--{style}     : elevated (default) | glass | outlined | flat
     auth-treatment--{t}    : emitted for completeness; visual rules added per-treatment later
   .auth-shell is a CSS Grid (desktop: 5fr/7fr columns, mobile: 1fr).
   ====================================================== */

/* -- brand side: right ------------------------------- */
/* Swap column order: form first, brand second.         */
@media (min-width: 992px) {
    .auth-brand--right .auth-brand { order: 2; }
    .auth-brand--right .auth-form  { order: 1; }
}

/* -- centered ---------------------------------------- */
/* Collapse to 1 column, hide the brand panel, centre  */
/* the form card over the gradient, reveal in-form logo */
.auth-shell--centered {
    grid-template-columns: 1fr;
    background: linear-gradient(160deg, var(--brand-grad-from, #1e1b4b), var(--brand-grad-to, #7c69ef));
    place-items: center;
}
.auth-shell--centered .auth-brand { display: none; }
.auth-shell--centered .auth-form {
    max-width: 440px;
    width: 100%;
    border-radius: 14px;
    box-shadow: 0 16px 40px rgba(0, 0, 0, .28);
}
.auth-shell--centered .auth-form-mobile-logo { display: block; }

/* -- fullbg ------------------------------------------ */
/* Brand bg-image fills the shell with a dark scrim;   */
/* form card floats centred on top.                    */
.auth-shell--fullbg {
    position: relative;
    grid-template-columns: 1fr;
    place-items: center;
    background: var(--brand-bg-image, linear-gradient(160deg, var(--brand-grad-from, #1e1b4b), var(--brand-grad-to, #7c69ef)));
    background-size: cover;
    background-position: center;
}
.auth-shell--fullbg::before {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(8, 12, 24, .55);
    pointer-events: none;
}
.auth-shell--fullbg .auth-brand { display: none; }
.auth-shell--fullbg .auth-form {
    position: relative;
    max-width: 440px;
    width: 100%;
    border-radius: 14px;
    box-shadow: 0 16px 40px rgba(0, 0, 0, .45);
}
.auth-shell--fullbg .auth-form-mobile-logo { display: block; }

/* -- minimal ----------------------------------------- */
/* Near-white page, no brand panel, just form + logo.  */
.auth-shell--minimal {
    grid-template-columns: 1fr;
    place-items: center;
    background: #fafbfc;
}
.auth-shell--minimal .auth-brand { display: none; }
.auth-shell--minimal .auth-form {
    max-width: 420px;
    width: 100%;
}
.auth-shell--minimal .auth-form-mobile-logo { display: block; }

/* -- stacked ----------------------------------------- */
/* Single column; brand panel becomes a short top band */
/* and the form sits below it.                         */
.auth-shell--stacked {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
}
.auth-shell--stacked .auth-brand {
    display: flex;
    min-height: auto;
    padding: 1.5rem 2rem;
}

/* -- card styles ------------------------------------- */
/* elevated = default (.auth-form already has bg:#fff + shadow). */
.auth-card--glass .auth-form {
    background: color-mix(in srgb, #fff 14%, transparent);
    backdrop-filter: blur(6px);
    border: 1px solid color-mix(in srgb, #fff 30%, transparent);
}
.auth-card--outlined .auth-form {
    background: #fff;
    border: 2px solid #cdd6e3;
    box-shadow: none;
}
.auth-card--flat .auth-form {
    background: transparent;
    box-shadow: none;
}

/* -- Branding-editor live-preview iframe ---------------------------------- */
/* Sized by CSS class - no inline style="" to stay strict-CSP compatible.   */
.brand-preview-frame {
    display: block;
    width: 100%;
    min-height: 560px;
    border: 1px solid #d0d5dd;
    border-radius: 10px;
    background: #f9fafb;
}

/* -- Brand remap (was portal/auth/partials/css-vars) ----------------------- */
/* The brand tokens themselves come from partials/brand-tokens, a nonced
   <style> the layout emits because they vary per deployment, so the sign-in
   page and the portal cannot disagree about the product's colour. What is
   specific to here is this remap: the file above was written against its own
   --auth-violet scale, and pointing that at --brand-primary is what carries a
   deployment's colour into the buttons, links, focus rings and checkbox
   rather than just the brand panel gradient.

   --auth-violet is the raw brand colour, for decoration only (checkbox ring,
   card border, glow). The -600 and -700 steps carry text and sit behind
   white labels, so they come from --brand-primary-ink, which
   App\Branding\ContrastInk keeps at >= 5.5:1 on white for any brand; a raw
   CAAM violet button label was 4.1:1. --auth-on-violet is the label colour on
   those fills, picked by contrast when the palette is emitted. This :root
   comes after the one at the top of the file, so it wins. */
:root{
--auth-violet: var(--brand-primary);
--auth-violet-600: var(--brand-primary-ink);
--auth-violet-700: color-mix(in srgb, var(--brand-primary-ink) 80%, #000);
--auth-on-violet: var(--brand-primary-ink-contrast);
--auth-violet-50: color-mix(in srgb, var(--brand-primary) 10%, #fff);
--auth-ring: color-mix(in srgb, var(--brand-primary) 18%, transparent);
}

/* -- Registration page (auth/register) ------------------------------------ */
textarea.auto-expand {
    overflow-y: hidden;
    resize: none;
}
.reg-invite-note { display: flex; gap: 0.75rem; align-items: flex-start; padding: 0.875rem 1rem; margin-bottom: 1rem; border-radius: 10px; border: 1px solid var(--auth-border); background: var(--auth-bg-subtle); font-size: 0.875rem; color: var(--auth-ink-soft); }
.reg-invite-note > .la { font-size: 1.25rem; color: var(--auth-violet-700); margin-top: 0.0625rem; }
.reg-invite-note strong { color: var(--auth-ink); }
.reg-alert { padding: 0.75rem 1rem; border-radius: 10px; margin-bottom: 1rem; font-size: 0.875rem; color: var(--auth-danger); background: color-mix(in srgb, var(--auth-danger) 7%, white); border: 1px solid color-mix(in srgb, var(--auth-danger) 30%, white); }
.form-control[readonly].reg-locked { background-color: var(--auth-bg-subtle); color: var(--auth-ink); cursor: default; }
