.login-body {
    /* Stacks a 65% black overlay on top of the background image */
    background: linear-gradient(rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0.65)), url('/images/login-bg.png') no-repeat fixed center / cover;
    min-height: 100vh;
}

/* Optional frosted glass effect on the login card */
.login-card {
    background-color: rgba(33, 37, 41, 0.8) !important; /* Semi-transparent dark background */
    backdrop-filter: blur(12px);                          /* Blurs the image directly behind the card */
    border: 1px solid rgba(255, 255, 255, 0.15) !important;
}

/* Honours newlines in flash messages so guidance sits on its own line.
   Wrap only the interpolated text, never the surrounding markup, or the
   template's own indentation would render as blank lines. */
.flash-message {
    white-space: pre-line;
}

/* Circular Logo inside Card */
.card-logo-container {
    width: 80px;
    height: 80px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.15);
}

.card-logo {
    max-width: 65%;
    height: auto;
}

/* Full-screen background layout for dashboard */
.dashboard-body {
    /* Stacks a 65% black overlay on top of the background image */
    background: linear-gradient(rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0.65)), url('/images/login-bg.png') no-repeat fixed center / cover;
    min-height: 100vh;
}

/* Reusable Frosted Glass Class */
.frosted-glass {
    background-color: rgba(33, 37, 41, 0.75) !important; /* Semi-transparent dark background */
    backdrop-filter: blur(12px) saturate(180%);          /* Blurs elements directly behind this container */
    -webkit-backdrop-filter: blur(12px) saturate(180%);  /* Safari browser support */
    border: 1px solid rgba(255, 255, 255, 0.125) !important; /* Subtle light border for depth */
    box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
}

/* Mobile standard */
.modal-sub-img {
    max-height: 280px;
}

/* Tablet & Desktop (md breakpoint) */
@media (min-width: 768px) {
    .modal-sub-img {
        max-height: 380px;
    }
}

.nav-tabs .nav-link.tab-has-error {
    color: var(--bs-danger) !important;
    border-color: var(--bs-danger);
}
.nav-tabs .nav-link.tab-has-error::after {
    content: "\F33A"; /* bi-exclamation-circle-fill */
    font-family: "bootstrap-icons";
    margin-left: 0.35rem;
    font-size: 0.75em;
}

/* autoSizeTextarea() sets an inline height matching the content, which an
   unusually long description could stretch past the modal. Cap the growth and
   let the textarea scroll from there. */
.modal textarea.form-control {
    max-height: 40vh;
}

/* Submit blocked by client-side validation. Matches Bootstrap's :disabled look,
   but must NOT set pointer-events:none - the button stays clickable so the click
   can still report which required fields are missing. */
.btn.btn-blocked {
    opacity: 0.65;
    filter: grayscale(35%);
    cursor: not-allowed;
}

/* Application-wide confirm dialog, driven by public/js/confirm.js. A native
   <dialog> receives no Bootstrap styling, so its width, backdrop and open
   animation live here; the colours come from utility classes on the element. */
.app-confirm {
    width: 100%;
    max-width: min(30rem, calc(100vw - 2rem));
}

/* Bodies are plain strings set via innerText, so honour any newlines a caller
   puts in them rather than collapsing the text into one run. */
.app-confirm-body {
    white-space: pre-line;
}

.app-confirm::backdrop {
    background-color: rgba(0, 0, 0, 0.6);
}

/* Opening only: a <dialog> leaves the top layer the instant it closes, so there
   is no closing frame left to animate without @starting-style. */
@media (prefers-reduced-motion: no-preference) {
    .app-confirm[open] {
        animation: app-confirm-in 0.15s ease-out;
    }
}

@keyframes app-confirm-in {
    from { opacity: 0; transform: translateY(-0.5rem); }
    to   { opacity: 1; transform: none; }
}
