/* Rika Attendance — Version 3: Material Design 3
 * ==============================================
 *
 * Loaded last, and only when Settings > Appearance > UI Version is 3.
 *
 * The colour roles this file consumes (--md-primary, --md-surface-container,
 * and the rest) are generated per request from the Color Theme chosen in
 * Settings and emitted inline by partials/ui-version-css.blade.php. Nothing
 * here hard-codes a brand colour: change the seed in Settings and the whole
 * application re-colours.
 *
 * Why this is CSS rather than Material Web Components:
 * <md-filled-button> and friends would mean replacing 698 buttons, 452
 * inputs and 266 selects in the markup - and would break DataTables,
 * SweetAlert2 and every jQuery selector that expects a Bootstrap class.
 * Material 3 is defined by its colour roles, type scale, shape scale,
 * elevation and state layers, and all of those are expressible here.
 *
 * Rules this file keeps:
 *   1. CSS only. Every button, toggle, hidden element, DataTable control and
 *      chart stays where it is and keeps working.
 *   2. Nothing is hidden. There is no `display: none` on an application
 *      control anywhere in this file.
 *   3. No literal colours outside section 1 - everything reads a role.
 */

/* ============================================================
   1. FOUNDATIONS
   ============================================================ */

:root {
    /* --- shape scale (M3) --- */
    --md-shape-xs: 4px;
    --md-shape-s: 8px;
    --md-shape-m: 12px;
    --md-shape-l: 16px;
    --md-shape-xl: 28px;
    --md-shape-full: 9999px;

    /* --- state layer opacities (M3) --- */
    --md-state-hover: .08;
    --md-state-focus: .10;
    --md-state-press: .10;
    --md-state-drag: .16;

    /* --- elevation ---
       Material expresses most elevation as a surface TINT rather than a
       shadow. The shadows below are the spec's, kept deliberately soft. */
    --md-elev-0: none;
    --md-elev-1: 0 1px 2px rgba(0,0,0,.30), 0 1px 3px 1px rgba(0,0,0,.15);
    --md-elev-2: 0 1px 2px rgba(0,0,0,.30), 0 2px 6px 2px rgba(0,0,0,.15);
    --md-elev-3: 0 4px 8px 3px rgba(0,0,0,.15), 0 1px 3px rgba(0,0,0,.30);
    --md-elev-4: 0 6px 10px 4px rgba(0,0,0,.15), 0 2px 3px rgba(0,0,0,.30);

    /* --- motion (M3 easing) --- */
    --md-ease-standard: cubic-bezier(.2, 0, 0, 1);
    --md-ease-emphasized: cubic-bezier(.2, 0, 0, 1);
    --md-dur-short: 100ms;
    --md-dur-medium: 200ms;
    --md-dur-long: 400ms;

    /* The design system's own tokens are re-pointed at Material roles, so
       the many existing consumers of var(--primary-500), var(--gray-200)
       and friends move across without being edited one by one. */
    --primary-300: var(--md-primary);
    --primary-400: var(--md-primary);
    --primary-500: var(--md-primary);
    --primary-600: var(--md-primary);
    --primary-700: var(--md-on-primary-container);

    --radius-sm: var(--md-shape-xs);
    --radius-md: var(--md-shape-s);
    --radius-lg: var(--md-shape-m);
    --radius-xl: var(--md-shape-l);

    --shadow-xs: var(--md-elev-1);
    --shadow-sm: var(--md-elev-1);
    --shadow-md: var(--md-elev-2);
    --shadow-lg: var(--md-elev-3);
    --shadow-xl: var(--md-elev-4);

    --surface: var(--md-surface-container-lowest);
    --topbar-bg: var(--md-surface);
    --topbar-border: var(--md-outline-variant);

    --bs-primary: var(--md-primary);
    --bs-primary-rgb: var(--md-primary-rgb);
    --bs-body-bg: var(--md-surface);
    --bs-body-color: var(--md-on-surface);
    --bs-border-color: var(--md-outline-variant);
    --bs-link-color: var(--md-primary);
    --bs-link-hover-color: var(--md-primary);
    --bs-border-radius: var(--md-shape-s);
}

/* ============================================================
   2. TYPE SCALE
   Roboto is Material's own face.

   The font is linked from partials/ui-version-css.blade.php, not
   @import-ed here: an @import is only valid at the very top of a
   stylesheet, and one placed after a rule is dropped silently -
   which is exactly what happened on the first attempt, leaving the
   application on Inter while everything else said Material.

   design-tokens.css sets the family through --font-sans with
   !important, so the variable is what has to be re-pointed; a plain
   body rule loses to it.
   ============================================================ */
:root {
    --font-sans: Roboto, system-ui, -apple-system, "Segoe UI", sans-serif;
}

body {
    font-family: Roboto, system-ui, -apple-system, "Segoe UI", sans-serif;
    background-color: var(--md-surface) !important;
    color: var(--md-on-surface);
    font-size: 16px;
    line-height: 1.5;
    letter-spacing: .005em;
    -webkit-font-smoothing: antialiased;
}

main, .main-content, #main-content {
    background-color: var(--md-surface) !important;
}

/* headline-small / title-large / title-medium */
h1 { font-size: 2rem;    line-height: 2.5rem; font-weight: 400; letter-spacing: 0; }
h2 { font-size: 1.75rem; line-height: 2.25rem; font-weight: 400; letter-spacing: 0; }
h3 { font-size: 1.5rem;  line-height: 2rem;   font-weight: 400; letter-spacing: 0; }
h4 { font-size: 1.375rem; line-height: 1.75rem; font-weight: 400; letter-spacing: 0; }
h5 { font-size: 1rem;    line-height: 1.5rem; font-weight: 500; letter-spacing: .009em; }
h6 { font-size: .875rem; line-height: 1.25rem; font-weight: 500; letter-spacing: .006em; }

.small, small { font-size: .75rem; line-height: 1rem; letter-spacing: .03em; }
.text-muted { color: var(--md-on-surface-variant) !important; }

/* ============================================================
   3. STATE LAYERS
   The overlay that appears on hover, focus and press. This is
   the detail that makes an interface feel Material, and it is
   what Bootstrap replaces with a darker background colour.
   ============================================================ */
.btn, .nav-link, .dropdown-item, .page-link,
.list-group-item-action, .table-hover > tbody > tr {
    position: relative;
    transition: background-color var(--md-dur-short) var(--md-ease-standard),
                box-shadow var(--md-dur-medium) var(--md-ease-standard);
}

/* ============================================================
   4. BUTTONS
   Five M3 types, mapped onto the Bootstrap classes in use.
   ============================================================ */
.btn {
    --bs-btn-border-radius: var(--md-shape-full);
    border-radius: var(--md-shape-full) !important;
    font-size: .875rem;
    font-weight: 500;
    letter-spacing: .006em;
    line-height: 1.25rem;
    padding: .625rem 1.5rem;
    border-width: 1px;
    box-shadow: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    min-height: 40px;
}

.btn-sm {
    padding: .375rem 1rem;
    min-height: 32px;
    font-size: .8125rem;
}
.btn-lg { padding: .75rem 1.75rem; min-height: 48px; font-size: .9375rem; }

/* Filled */
.btn-primary {
    background-color: var(--md-primary);
    border-color: transparent;
    color: var(--md-on-primary);
}
.btn-primary:hover {
    background-color: var(--md-primary);
    color: var(--md-on-primary);
    box-shadow: var(--md-elev-1);
    background-image: linear-gradient(rgba(var(--md-on-primary-rgb), var(--md-state-hover)),
                                      rgba(var(--md-on-primary-rgb), var(--md-state-hover)));
}
.btn-primary:active, .btn-primary:focus-visible {
    background-color: var(--md-primary);
    color: var(--md-on-primary);
    box-shadow: none;
    background-image: linear-gradient(rgba(var(--md-on-primary-rgb), var(--md-state-press)),
                                      rgba(var(--md-on-primary-rgb), var(--md-state-press)));
}

/* Filled tonal — the quiet-but-present role */
.btn-secondary, .btn-light, .btn-info {
    background-color: var(--md-secondary-container);
    border-color: transparent;
    color: var(--md-on-secondary-container);
}
.btn-secondary:hover, .btn-light:hover, .btn-info:hover {
    background-color: var(--md-secondary-container);
    color: var(--md-on-secondary-container);
    box-shadow: var(--md-elev-1);
    background-image: linear-gradient(rgba(var(--md-on-secondary-container-rgb), var(--md-state-hover)),
                                      rgba(var(--md-on-secondary-container-rgb), var(--md-state-hover)));
}

/* Error */
.btn-danger {
    background-color: var(--md-error);
    border-color: transparent;
    color: var(--md-on-error);
}
.btn-danger:hover {
    background-color: var(--md-error);
    color: var(--md-on-error);
    box-shadow: var(--md-elev-1);
}

/* Success keeps its meaning; Material has no success role, so it borrows
   tertiary rather than inventing a colour outside the scheme. */
.btn-success {
    background-color: var(--md-tertiary);
    border-color: transparent;
    color: var(--md-on-tertiary);
}
.btn-success:hover { background-color: var(--md-tertiary); color: var(--md-on-tertiary); box-shadow: var(--md-elev-1); }

.btn-warning {
    background-color: var(--md-tertiary-container);
    border-color: transparent;
    color: var(--md-on-tertiary-container);
}

/* Outlined */
.btn-outline-primary, .btn-outline-secondary, .btn-outline-dark,
.btn-outline-success, .btn-outline-info, .btn-outline-warning {
    background-color: transparent;
    border-color: var(--md-outline);
    color: var(--md-primary);
}
.btn-outline-danger { background-color: transparent; border-color: var(--md-outline); color: var(--md-error); }

.btn-outline-primary:hover, .btn-outline-secondary:hover, .btn-outline-dark:hover,
.btn-outline-success:hover, .btn-outline-info:hover, .btn-outline-warning:hover {
    background-color: rgba(var(--md-primary-rgb), var(--md-state-hover));
    border-color: var(--md-outline);
    color: var(--md-primary);
}
.btn-outline-danger:hover {
    background-color: rgba(var(--md-error-rgb), var(--md-state-hover));
    border-color: var(--md-outline);
    color: var(--md-error);
}

/* Text */
.btn-link {
    color: var(--md-primary);
    text-decoration: none;
    padding: .625rem .75rem;
}
.btn-link:hover { background-color: rgba(var(--md-primary-rgb), var(--md-state-hover)); color: var(--md-primary); }

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

/* A group still reads as one control. */
.btn-group > .btn:not(:first-child) { border-top-left-radius: 0 !important; border-bottom-left-radius: 0 !important; }
.btn-group > .btn:not(:last-child)  { border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; }

/* ============================================================
   5. CARDS
   ============================================================ */
.card {
    background-color: var(--md-surface-container-low) !important;
    border: none !important;
    border-radius: var(--md-shape-m) !important;
    box-shadow: none !important;
    color: var(--md-on-surface);
    overflow: hidden;
}

.card-header {
    background-color: transparent !important;
    border-bottom: 1px solid var(--md-outline-variant) !important;
    color: var(--md-on-surface);
    font-size: 1rem;
    font-weight: 500;
    letter-spacing: .009em;
    padding: 1rem 1.5rem !important;
}

/* A coloured header becomes a tonal container, not a saturated bar. */
.card > .card-header.bg-primary   { background-color: var(--md-primary-container) !important; color: var(--md-on-primary-container) !important; }
.card > .card-header.bg-success   { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.card > .card-header.bg-info      { background-color: var(--md-secondary-container) !important; color: var(--md-on-secondary-container) !important; }
.card > .card-header.bg-danger    { background-color: var(--md-error-container) !important; color: var(--md-on-error-container) !important; }
.card > .card-header.bg-warning   { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.card > .card-header.bg-secondary,
.card > .card-header.bg-dark      { background-color: var(--md-surface-container-high) !important; color: var(--md-on-surface) !important; }

.card > .card-header[class*="bg-"] *,
.card > .card-header[class*="bg-"] .small,
.card > .card-header[class*="bg-"] .text-muted {
    color: inherit !important;
}

.card-body { padding: 1.5rem !important; }
.card-footer {
    background-color: transparent !important;
    border-top: 1px solid var(--md-outline-variant) !important;
    padding: 1rem 1.5rem !important;
}

/* ============================================================
   6. FORMS — outlined text fields
   ============================================================ */
.form-control, .form-select, .input-group-text {
    background-color: transparent;
    border: 1px solid var(--md-outline);
    border-radius: var(--md-shape-xs);
    color: var(--md-on-surface);
    font-size: 1rem;
    line-height: 1.5rem;
    padding: .875rem 1rem;
    min-height: 48px;
    transition: border-color var(--md-dur-short) var(--md-ease-standard),
                box-shadow var(--md-dur-short) var(--md-ease-standard);
}

.form-control:hover, .form-select:hover { border-color: var(--md-on-surface); }

.form-control:focus, .form-select:focus {
    background-color: transparent;
    border-color: var(--md-primary);
    box-shadow: inset 0 0 0 1px var(--md-primary);
    color: var(--md-on-surface);
}

.form-control::placeholder { color: var(--md-on-surface-variant); opacity: 1; }
.form-control:disabled, .form-select:disabled {
    background-color: rgba(var(--md-on-surface-rgb), .04);
    color: var(--md-on-surface-variant);
}

.form-control.is-invalid, .form-select.is-invalid { border-color: var(--md-error); }
.invalid-feedback { color: var(--md-error); font-size: .75rem; letter-spacing: .03em; }

.form-label {
    color: var(--md-on-surface-variant);
    font-size: .875rem;
    font-weight: 500;
    letter-spacing: .006em;
    margin-bottom: .5rem;
}
.form-text { color: var(--md-on-surface-variant); font-size: .75rem; letter-spacing: .03em; }

.input-group-text { background-color: var(--md-surface-container); }
.input-group > :not(:first-child):not(.dropdown-menu) { border-top-left-radius: 0 !important; border-bottom-left-radius: 0 !important; }
.input-group > :not(:last-child) { border-top-right-radius: 0 !important; border-bottom-right-radius: 0 !important; }

/* Checkbox and radio */
.form-check-input {
    width: 18px;
    height: 18px;
    margin-top: .2rem;
    border: 2px solid var(--md-on-surface-variant);
    background-color: transparent;
    cursor: pointer;
}
.form-check-input[type="radio"] { border-radius: 50%; }
.form-check-input[type="checkbox"] { border-radius: 2px; }
.form-check-input:checked {
    background-color: var(--md-primary);
    border-color: var(--md-primary);
}
.form-check-input:focus { border-color: var(--md-primary); box-shadow: 0 0 0 8px rgba(var(--md-primary-rgb), var(--md-state-focus)); }
.form-check-label { color: var(--md-on-surface); }

/* M3 switch.
 *
 * The handle is positioned from the track's own measurements, which the
 * previous version was not: it placed a 24px handle at left:26px inside a
 * track Bootstrap had already sized to 44px, so when the switch was ON the
 * handle hung off the right-hand end and the whole control looked broken.
 * Everything below is derived from one track width so the two cannot drift
 * apart again.
 *
 *   track 52 x 32, border 2  ->  inner 48 x 28
 *   off handle 16, inset 6   ->  (28 - 16) / 2 = 6 top
 *   on  handle 24, inset 2   ->  2 + 24 = 26, and 48 - 26 = 22 = 2 + 20 ✓
 */
.form-switch .form-check-input {
    /* !important because Bootstrap's own .form-check-input sizing loads after
       this file and was winning, which is how the track ended up at 44px. */
    width: 52px !important;
    height: 32px !important;
    border-radius: var(--md-shape-full);
    border: 2px solid var(--md-outline);
    background-color: var(--md-surface-container-highest);
    background-image: none !important;
    position: relative;
    flex-shrink: 0;
    transition: background-color var(--md-dur-short) var(--md-ease-standard),
                border-color var(--md-dur-short) var(--md-ease-standard);
}

.form-switch .form-check-input::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 6px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--md-outline);
    transform: translateY(-50%);
    transition: left var(--md-dur-short) var(--md-ease-emphasized),
                width var(--md-dur-short) var(--md-ease-emphasized),
                height var(--md-dur-short) var(--md-ease-emphasized),
                background-color var(--md-dur-short) var(--md-ease-standard);
}

.form-switch .form-check-input:checked {
    background-color: var(--md-primary);
    border-color: var(--md-primary);
    background-image: none !important;
}

/* ON: the handle grows and travels to the far end, leaving the same 2px of
   track either side that it has at rest - which is what makes it read as
   "settled" rather than "stuck against the wall". */
.form-switch .form-check-input:checked::after {
    left: 22px;
    width: 24px;
    height: 24px;
    background: var(--md-on-primary);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
}

/* The focus ring belongs on the track, not the handle. */
.form-switch .form-check-input:focus {
    border-color: var(--md-primary);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb, 65 78 212), .22);
}

.form-switch .form-check-input:focus:not(:checked) {
    background-image: none !important;
}

.form-switch .form-check-input:disabled {
    opacity: .5;
}

/* ============================================================
   7. CHIPS (badges)
   ============================================================ */
.badge {
    border-radius: var(--md-shape-s);
    font-size: .75rem;
    font-weight: 500;
    letter-spacing: .03em;
    line-height: 1rem;
    padding: .375rem .5rem;
}
.badge.bg-primary   { background-color: var(--md-primary-container) !important; color: var(--md-on-primary-container) !important; }
.badge.bg-success   { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.badge.bg-danger    { background-color: var(--md-error-container) !important; color: var(--md-on-error-container) !important; }
.badge.bg-warning   { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.badge.bg-info      { background-color: var(--md-secondary-container) !important; color: var(--md-on-secondary-container) !important; }
.badge.bg-secondary { background-color: var(--md-surface-container-high) !important; color: var(--md-on-surface-variant) !important; }
.badge.bg-dark      { background-color: var(--md-inverse-surface) !important; color: var(--md-inverse-on-surface) !important; }

/* ============================================================
   8. BANNERS (alerts)
   ============================================================ */
.alert {
    border: none;
    border-radius: var(--md-shape-m);
    padding: 1rem 1.5rem;
    font-size: .875rem;
    line-height: 1.25rem;
}
.alert-primary { background-color: var(--md-primary-container); color: var(--md-on-primary-container); }
.alert-success { background-color: var(--md-tertiary-container); color: var(--md-on-tertiary-container); }
.alert-danger  { background-color: var(--md-error-container); color: var(--md-on-error-container); }
.alert-warning { background-color: var(--md-tertiary-container); color: var(--md-on-tertiary-container); }
.alert-info    { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }
.alert-secondary { background-color: var(--md-surface-container-high); color: var(--md-on-surface); }

/* ============================================================
   9. PROGRESS
   ============================================================ */
.progress {
    background-color: var(--md-surface-container-highest);
    border-radius: var(--md-shape-full);
    height: 4px;
}
.progress-bar { background-color: var(--md-primary); border-radius: var(--md-shape-full); }
.progress-bar.bg-success { background-color: var(--md-tertiary); }
.progress-bar.bg-danger  { background-color: var(--md-error); }
.progress-bar.bg-warning { background-color: var(--md-tertiary); }

/* ============================================================
   10. THINGS THAT MUST KEEP WORKING
   ============================================================ */
.visually-hidden,
.visually-hidden-focusable:not(:focus):not(:focus-within) {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
}
[hidden] { display: none !important; }

/* NO .d-none rule here. Bootstrap writes responsive visibility as a pair -
   `d-none d-md-block` - and both halves are !important at equal specificity,
   so a later .d-none wins every tie and hides the element at EVERY size.
   Version 2 shipped that bug once; the sidebar collapse button measured 0x0. */

@media (prefers-reduced-motion: reduce) {
    .btn, .form-control, .form-select, .form-switch .form-check-input::after {
        transition: none !important;
    }
}

/* ============================================================
   11. WINNING AGAINST THE DESIGN SYSTEM
   design-tokens.css declares many of these rules with
   !important - .btn-primary, .btn-success, .form-control,
   .card, .modal-content, .dropdown-menu and more. A plain
   rule here, however late it loads, loses to those.
   So everything that has to beat one is restated with
   !important. Nothing here is !important for its own sake.
   ============================================================ */

.btn-primary {
    background-color: var(--md-primary) !important;
    border-color: transparent !important;
    color: var(--md-on-primary) !important;
}
.btn-primary:hover {
    background-color: var(--md-primary) !important;
    border-color: transparent !important;
    color: var(--md-on-primary) !important;
}
.btn-success {
    background-color: var(--md-tertiary) !important;
    border-color: transparent !important;
    color: var(--md-on-tertiary) !important;
}
.btn-danger {
    background-color: var(--md-error) !important;
    border-color: transparent !important;
    color: var(--md-on-error) !important;
}
.btn-warning {
    background-color: var(--md-tertiary-container) !important;
    border-color: transparent !important;
    color: var(--md-on-tertiary-container) !important;
}
.btn-secondary, .btn-info, .btn-light {
    background-color: var(--md-secondary-container) !important;
    border-color: transparent !important;
    color: var(--md-on-secondary-container) !important;
}
.btn-dark {
    background-color: var(--md-inverse-surface) !important;
    border-color: transparent !important;
    color: var(--md-inverse-on-surface) !important;
}
.btn-outline-primary {
    background-color: transparent !important;
    border-color: var(--md-outline) !important;
    color: var(--md-primary) !important;
}
.btn-outline-primary:hover {
    background-color: rgba(var(--md-primary-rgb), var(--md-state-hover)) !important;
    border-color: var(--md-outline) !important;
    color: var(--md-primary) !important;
}
.btn:focus-visible {
    outline: 3px solid var(--md-primary) !important;
    outline-offset: 2px !important;
    box-shadow: none !important;
}

.form-control, .form-select {
    background-color: transparent !important;
    border: 1px solid var(--md-outline) !important;
    border-radius: var(--md-shape-xs) !important;
    color: var(--md-on-surface) !important;
}
.form-control:focus, .form-select:focus {
    background-color: transparent !important;
    border-color: var(--md-primary) !important;
    box-shadow: inset 0 0 0 1px var(--md-primary) !important;
    color: var(--md-on-surface) !important;
}

.card {
    background-color: var(--md-surface-container-low) !important;
    border: none !important;
    border-radius: var(--md-shape-m) !important;
    box-shadow: none !important;
}
.card-header { background-color: transparent !important; border-bottom-color: var(--md-outline-variant) !important; }
.card-body { background-color: transparent !important; }

.alert { border: none !important; border-radius: var(--md-shape-m) !important; }
.alert-success { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.alert-danger  { background-color: var(--md-error-container) !important; color: var(--md-on-error-container) !important; }
.alert-warning { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.alert-info    { background-color: var(--md-secondary-container) !important; color: var(--md-on-secondary-container) !important; }

.badge.bg-primary { background-color: var(--md-primary-container) !important; color: var(--md-on-primary-container) !important; }
.badge.bg-success { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.badge.bg-danger  { background-color: var(--md-error-container) !important; color: var(--md-on-error-container) !important; }
.badge.bg-warning { background-color: var(--md-tertiary-container) !important; color: var(--md-on-tertiary-container) !important; }
.badge.bg-info    { background-color: var(--md-secondary-container) !important; color: var(--md-on-secondary-container) !important; }

.progress { background-color: var(--md-surface-container-highest) !important; }

/* ============================================================
   12. DATA TABLES
   The library renders its own controls: the length menu, the
   search box, the info line, pagination and the export
   buttons. Each needs a Material equivalent, and each is
   selected at the specificity DataTables itself uses.
   Every control keeps working - this is appearance only.
   ============================================================ */
.table {
    --bs-table-border-color: var(--md-outline-variant);
    --bs-table-bg: transparent;
    color: var(--md-on-surface);
    margin-bottom: 0;
}

/* label-small column headers: small caps, generously tracked */
.table > thead > tr > th,
table.dataTable > thead > tr > th {
    background-color: transparent !important;
    border-bottom: 1px solid var(--md-outline-variant) !important;
    color: var(--md-on-surface-variant);
    font-size: .6875rem;
    font-weight: 500;
    letter-spacing: .09em;
    text-transform: uppercase;
    padding: .875rem 1rem;
    white-space: nowrap;
}

/* body-medium cells, on Material's 52px row */
.table > tbody > tr > td,
table.dataTable > tbody > tr > td {
    border-bottom: 1px solid var(--md-outline-variant);
    color: var(--md-on-surface);
    font-size: .875rem;
    line-height: 1.25rem;
    padding: .875rem 1rem;
    vertical-align: middle;
}
.table > tbody > tr:last-child > td { border-bottom: 0; }

/* Row state layer, rather than a solid hover colour. */
.table-hover > tbody > tr:hover > *,
table.dataTable.table-hover > tbody > tr:hover > * {
    background-color: rgba(var(--md-on-surface-rgb), var(--md-state-hover)) !important;
    color: var(--md-on-surface);
}

/* --- the library's own controls --- */
div.dt-buttons { gap: .5rem; display: inline-flex; flex-wrap: wrap; }

div.dt-buttons .btn,
div.dt-buttons button.dt-button,
.dt-buttons .btn {
    background-color: var(--md-secondary-container) !important;
    border: none !important;
    border-radius: var(--md-shape-full) !important;
    color: var(--md-on-secondary-container) !important;
    font-size: .8125rem !important;
    font-weight: 500;
    letter-spacing: .006em;
    padding: .5rem 1rem !important;
    min-height: 36px;
    margin: 0 !important;
    box-shadow: none !important;
}
div.dt-buttons .btn:hover,
div.dt-buttons button.dt-button:hover,
.dt-buttons .btn:hover {
    background-image: linear-gradient(rgba(var(--md-on-secondary-container-rgb), var(--md-state-hover)),
                                      rgba(var(--md-on-secondary-container-rgb), var(--md-state-hover)));
    box-shadow: var(--md-elev-1) !important;
}

/* The export group still reads as one set of related actions, so the
   corners stay full rather than squaring up like a Bootstrap group. */
.dt-buttons.btn-group > .btn { border-radius: var(--md-shape-full) !important; }

.dataTables_wrapper .dataTables_filter input {
    background-color: var(--md-surface-container-high);
    border: none;
    border-radius: var(--md-shape-full);
    color: var(--md-on-surface);
    padding: .5rem 1rem;
    min-height: 40px;
    margin-left: .5rem;
}
.dataTables_wrapper .dataTables_filter input:focus {
    outline: 2px solid var(--md-primary);
    outline-offset: 0;
}
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_info {
    color: var(--md-on-surface-variant);
    font-size: .875rem;
}

.dataTables_wrapper .dataTables_length select {
    background-color: transparent;
    border: 1px solid var(--md-outline);
    border-radius: var(--md-shape-xs);
    color: var(--md-on-surface);
    padding: .35rem 1.75rem .35rem .65rem;
    min-height: 36px;
}

/* Pagination as icon buttons - Material has no pagination component, and
   this is the pattern its own docs show. */
.dataTables_wrapper .dataTables_paginate .paginate_button,
.pagination .page-link {
    background: transparent !important;
    border: none !important;
    border-radius: var(--md-shape-full) !important;
    color: var(--md-on-surface-variant) !important;
    margin: 0 .1rem !important;
    min-width: 40px;
    min-height: 40px;
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    font-size: .875rem;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:hover,
.pagination .page-link:hover {
    background: rgba(var(--md-on-surface-rgb), var(--md-state-hover)) !important;
    color: var(--md-on-surface) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current,
.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover,
.pagination .page-item.active .page-link {
    background: var(--md-secondary-container) !important;
    color: var(--md-on-secondary-container) !important;
    font-weight: 500;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,
.pagination .page-item.disabled .page-link {
    color: rgba(var(--md-on-surface-rgb), .38) !important;
}

/* The sort arrows the library draws. */
table.dataTable thead .sorting:after,
table.dataTable thead .sorting_asc:after,
table.dataTable thead .sorting_desc:after { opacity: .5; }

/* A wide table scrolls inside its own box; the page never does. */
.table-responsive { border-radius: var(--md-shape-m); }

/* ============================================================
   13. DIALOGS AND OVERLAYS
   ============================================================ */
.modal-content {
    background-color: var(--md-surface-container-high) !important;
    border: none !important;
    border-radius: var(--md-shape-xl) !important;
    box-shadow: var(--md-elev-3) !important;
    color: var(--md-on-surface) !important;
}
.modal-header {
    background-color: transparent !important;
    border-bottom: none !important;
    padding: 1.5rem 1.5rem 1rem !important;
}
.modal-title {
    color: var(--md-on-surface) !important;
    font-size: 1.5rem !important;
    font-weight: 400 !important;
    letter-spacing: 0 !important;
}
.modal-body {
    background-color: transparent !important;
    color: var(--md-on-surface-variant) !important;
    padding: 0 1.5rem 1rem !important;
}
.modal-footer {
    background-color: transparent !important;
    border-top: none !important;
    padding: 1rem 1.5rem 1.5rem !important;
    gap: .5rem;
}
/* Material puts dialog actions as text buttons at the end of the row. */
.modal-footer .btn-secondary {
    background-color: transparent !important;
    color: var(--md-primary) !important;
}
.modal-footer .btn-secondary:hover {
    background-color: rgba(var(--md-primary-rgb), var(--md-state-hover)) !important;
}
.modal-header[class*="bg-"] { background-color: var(--md-surface-container-high) !important; color: var(--md-on-surface) !important; }
.modal-header[class*="bg-"] * { color: inherit !important; }
.modal-backdrop.show { background-color: var(--md-scrim); opacity: .32; }
.btn-close {
    border-radius: 50%;
    padding: .6rem;
    opacity: .7;
}
.btn-close:hover { background-color: rgba(var(--md-on-surface-rgb), var(--md-state-hover)); opacity: 1; }

/* Menus */
.dropdown-menu {
    background-color: var(--md-surface-container) !important;
    border: none !important;
    border-radius: var(--md-shape-xs) !important;
    box-shadow: var(--md-elev-2) !important;
    padding: .5rem 0 !important;
    min-width: 12rem;
}
.dropdown-item {
    color: var(--md-on-surface) !important;
    font-size: .875rem !important;
    letter-spacing: .006em;
    padding: .75rem 1rem !important;
    border-radius: 0 !important;
    display: flex;
    align-items: center;
    gap: .75rem;
}
.dropdown-item:hover, .dropdown-item:focus {
    background-color: rgba(var(--md-on-surface-rgb), var(--md-state-hover)) !important;
    color: var(--md-on-surface) !important;
}
.dropdown-item.text-danger { color: var(--md-error) !important; }
.dropdown-item > i:first-child { width: 1.25rem; text-align: center; opacity: .8; }
.dropdown-divider { border-color: var(--md-outline-variant); margin: .5rem 0; }

/* SweetAlert2 - every confirm and error in the product */
.swal2-popup {
    background: var(--md-surface-container-high) !important;
    border-radius: var(--md-shape-xl) !important;
    color: var(--md-on-surface) !important;
    padding: 1.5rem !important;
    box-shadow: var(--md-elev-3) !important;
}
.swal2-title { color: var(--md-on-surface) !important; font-size: 1.5rem !important; font-weight: 400 !important; }
.swal2-html-container { color: var(--md-on-surface-variant) !important; font-size: .875rem !important; }
.swal2-actions { gap: .5rem; }

/* Corner toasts are not dialogs.
 *
 * The .swal2-popup and .swal2-title rules above are written for a centred
 * confirm dialog - 1.5rem padding, a 1.5rem title - and this stylesheet loads
 * LAST, so they also landed on the little "Data refreshed" notice and blew it
 * up to a 24px banner across the corner of the screen.
 *
 * A toast is a glance, not a conversation: small text, tight box, gone in a
 * moment. Sizes are clamp()ed rather than fixed: the A- / A / A+ text-size
 * control still moves them, but between a small floor and a hard cap, so a
 * transient notice can never grow back into the oversized box it was. */
.swal2-popup.swal2-toast {
    width: auto !important;
    max-width: clamp(300px, 22rem, 360px) !important;
    padding: 9px 11px !important;
    border-radius: 10px !important;
    box-shadow: var(--md-elev-2) !important;
}
.swal2-popup.swal2-toast .swal2-title {
    font-size: clamp(12.5px, .78rem, 15px) !important;
    font-weight: 500 !important;
    line-height: 1.35 !important;
    margin: 0 !important;
    padding: 0 !important;
}
.swal2-popup.swal2-toast .swal2-html-container {
    font-size: clamp(11.5px, .72rem, 14px) !important;
    line-height: 1.35 !important;
    margin: 2px 0 0 !important;
    padding: 0 !important;
}
/* The icon is drawn from em units, so it shrinks with the box once the
   popup's own font-size is pinned. */
.swal2-popup.swal2-toast .swal2-icon {
    width: 18px !important;
    height: 18px !important;
    min-width: 18px !important;
    margin: 0 8px 0 0 !important;
    font-size: 9px !important;
}
.swal2-popup.swal2-toast .swal2-close { font-size: 18px !important; margin: 0 0 0 4px !important; }
.swal2-popup.swal2-toast .swal2-timer-progress-bar { height: 2px !important; }
.swal2-styled.swal2-confirm {
    background: var(--md-primary) !important;
    color: var(--md-on-primary) !important;
    border-radius: var(--md-shape-full) !important;
    padding: .625rem 1.5rem !important;
    font-weight: 500 !important;
    box-shadow: none !important;
}
.swal2-styled.swal2-cancel {
    background: transparent !important;
    color: var(--md-primary) !important;
    border-radius: var(--md-shape-full) !important;
    padding: .625rem 1.5rem !important;
    font-weight: 500 !important;
    box-shadow: none !important;
}
.swal2-styled.swal2-deny {
    background: var(--md-error) !important;
    color: var(--md-on-error) !important;
    border-radius: var(--md-shape-full) !important;
}

/* Tooltips */
.tooltip-inner {
    background-color: var(--md-inverse-surface);
    color: var(--md-inverse-on-surface);
    border-radius: var(--md-shape-xs);
    font-size: .75rem;
    padding: .5rem .75rem;
}
.tooltip .tooltip-arrow::before { border-top-color: var(--md-inverse-surface); }

.offcanvas {
    background-color: var(--md-surface-container-low);
    color: var(--md-on-surface);
}

/* ============================================================
   14. NAVIGATION
   Drawer, rail, top app bar, tabs. The existing shell maps
   onto Material's navigation components almost exactly, which
   is why none of it has to move.
   ============================================================ */
.sidebar {
    background-color: var(--md-surface-container-low) !important;
    border-right: 1px solid var(--md-outline-variant) !important;
    box-shadow: none !important;
}
.sidebar .nav-link {
    border-radius: var(--md-shape-full);
    color: var(--md-on-surface-variant) !important;
    font-size: .875rem;
    font-weight: 500;
    letter-spacing: .006em;
    margin: .125rem .75rem;
    padding: .75rem 1rem;
    min-height: 56px;
    display: flex;
    align-items: center;
    gap: .75rem;
}
.sidebar .nav-link:hover {
    background-color: rgba(var(--md-on-surface-rgb), var(--md-state-hover));
    color: var(--md-on-surface) !important;
}
/* The active indicator - a filled pill, which is Material's own pattern. */
.sidebar .nav-link.active {
    background-color: var(--md-secondary-container) !important;
    color: var(--md-on-secondary-container) !important;
    box-shadow: none;
}
.sidebar .submenu .nav-link { margin-left: 1.5rem; min-height: 48px; font-size: .8125rem; }
.sidebar .shell-brand { color: var(--md-on-surface) !important; border-bottom-color: var(--md-outline-variant) !important; }
.sidebar .menu-search-field .form-control { background-color: var(--md-surface-container-high) !important; border: none !important; border-radius: var(--md-shape-full) !important; }

/* Top app bar */
.top-header {
    background-color: var(--md-surface) !important;
    border-bottom: none !important;
    box-shadow: none !important;
}
.top-header .shell-btn, .top-header button {
    border-radius: 50% !important;
    color: var(--md-on-surface-variant);
}
.profile-circle {
    background: var(--md-primary-container) !important;
    color: var(--md-on-primary-container) !important;
    box-shadow: none !important;
}

/* Tabs - primary tabs with a full-width indicator */
.nav-tabs {
    border-bottom: 1px solid var(--md-outline-variant);
    gap: 0;
}
.nav-tabs .nav-link {
    background: transparent !important;
    border: none !important;
    border-bottom: 3px solid transparent !important;
    border-radius: 0 !important;
    color: var(--md-on-surface-variant) !important;
    font-size: .875rem;
    font-weight: 500;
    letter-spacing: .006em;
    padding: .875rem 1rem;
    min-height: 48px;
    transition: color var(--md-dur-short) var(--md-ease-standard),
                border-color var(--md-dur-medium) var(--md-ease-emphasized);
}
.nav-tabs .nav-link:hover { background: rgba(var(--md-on-surface-rgb), var(--md-state-hover)) !important; color: var(--md-on-surface) !important; }
.nav-tabs .nav-link.active {
    color: var(--md-primary) !important;
    border-bottom-color: var(--md-primary) !important;
}
.nav-pills .nav-link { border-radius: var(--md-shape-full); }
.nav-pills .nav-link.active { background-color: var(--md-secondary-container); color: var(--md-on-secondary-container); }

/* ============================================================
   15. SIGN-IN AND THE OTHER UNAUTHENTICATED SCREENS
   ============================================================ */
.login-wrapper { background: var(--md-surface); }
.login-wrapper .brand-panel {
    background: var(--md-primary-container) !important;
    color: var(--md-on-primary-container) !important;
}
.login-wrapper .brand-panel * { color: inherit !important; }
.login-wrapper .form-panel { background: var(--md-surface); }
.login-wrapper .form-container {
    background: var(--md-surface-container-low);
    border: none;
    border-radius: var(--md-shape-xl);
    box-shadow: none;
    padding: 2rem;
}
.login-wrapper input:not([type="checkbox"]):not([type="radio"]) {
    background: transparent;
    border: 1px solid var(--md-outline);
    border-radius: var(--md-shape-xs);
    color: var(--md-on-surface);
    min-height: 48px;
}
.login-wrapper .submit-btn {
    background: var(--md-primary);
    color: var(--md-on-primary);
    border-radius: var(--md-shape-full);
    font-weight: 500;
    min-height: 40px;
    box-shadow: none;
}
.login-wrapper .brand-feature { border-radius: var(--md-shape-m); }
.login-wrapper .logo-icon, .login-wrapper .mobile-logo .logo-icon { border-radius: var(--md-shape-m); }

/* ============================================================
   16. SPECIALIST SURFACES
   ============================================================ */
/* Leaflet - the map tiles stay as they are; its chrome does not. */
.leaflet-control-zoom a, .leaflet-bar a {
    background: var(--md-surface-container-high);
    border-color: var(--md-outline-variant);
    color: var(--md-on-surface);
}
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
    background: var(--md-surface-container-high);
    color: var(--md-on-surface);
    border-radius: var(--md-shape-m);
    box-shadow: var(--md-elev-2);
}
.leaflet-container { border-radius: var(--md-shape-m); }

/* The Insights screen's own components. */
#insights .ins-tile, #insights .ins-action {
    background: var(--md-surface-container-low) !important;
    border: none !important;
    border-radius: var(--md-shape-m) !important;
    box-shadow: none !important;
}
#insights .ins-tile-icon { border-radius: var(--md-shape-m); }
#insights .ins-mini { background: var(--md-surface-container); border-radius: var(--md-shape-m); }
#insights .ins-avatar { background: var(--md-primary-container); color: var(--md-on-primary-container); }
#insights .ins-arrivals li { border-bottom-color: var(--md-outline-variant); }
#insights .ins-legend li { border-bottom-color: var(--md-outline-variant); }

/* Dashboard V2's own bars, if that skin's markup is in play. */
#dashboardV2 .v2-tile-icon { border-radius: var(--md-shape-m); }
#dashboardV2 .v2-bar-track { background: var(--md-surface-container-highest); border-radius: var(--md-shape-s); }
#dashboardV2 .v2-bar-fill { background: var(--md-primary); border-radius: var(--md-shape-s); }

/* Printing keeps the data, drops the furniture. */
@media print {
    .sidebar, .top-header, .dt-buttons, .dataTables_filter, .dataTables_length { display: none !important; }
    .card { box-shadow: none !important; border: 1px solid var(--md-outline-variant) !important; }
}

/* ============================================================
   17. RESPONSIVE
   ============================================================ */
@media (max-width: 991.98px) {
    .btn { padding: .5rem 1rem; }
    .btn-sm { padding: .375rem .75rem; }
    div.dt-buttons { flex-wrap: wrap; max-width: 100%; }
    .card-body { padding: 1.25rem !important; }
    .card-header { padding: 1rem 1.25rem !important; }

    /* A filter bar that cannot fit its controls on one line wraps rather
       than pushing them off the edge. On Alert Logs a Search and a Reset
       button shared a 100px column with flex-wrap:nowrap, and Reset hung
       12px past the window. Material buttons are wider than Bootstrap's,
       so the columns that were only just wide enough no longer are. */
    .card-body .row > [class*="col-"] { min-width: 0; }
    .card-body .d-flex:not(.flex-nowrap) { flex-wrap: wrap; }
    .dataTables_wrapper > .row > [class*="col-"] { min-width: 0; }
}

@media (max-width: 575.98px) {
    .btn, .btn-sm, div.dt-buttons .btn, .dt-buttons .btn { min-height: 40px !important; }
    .dataTables_wrapper .dataTables_paginate .paginate_button { min-width: 40px; min-height: 40px; }
    .table > thead > tr > th, .table > tbody > tr > td { padding: .75rem; }
    .card-body { padding: 1rem !important; }
    .modal-content { border-radius: var(--md-shape-l) !important; }
    .dataTables_wrapper .dataTables_filter input { width: 100%; margin-left: 0; }
    .info-hint { width: 40px; height: 40px; min-width: 40px; min-height: 40px;
                 display: inline-flex; align-items: center; justify-content: center; }
}

/* ============================================================
   18. THE NAVIGATION DRAWER, PROPERLY
   Section 14 lightened the drawer, which is correct Material -
   a standard navigation drawer sits on a surface colour, not on
   a dark panel. But the drawer's own tokens and the menu search
   were still written for a dark panel, so the result was grey
   text on a light ground at about 2:1. Unreadable is not a
   style choice.

   Re-pointed at the token level rather than rule by rule, so
   anything in the drawer that consumes --sidebar-* follows
   automatically.
   ============================================================ */
:root {
    --sidebar-bg: var(--md-surface-container-low);
    --sidebar-hover: rgba(var(--md-on-surface-rgb), .08);
    --sidebar-active: var(--md-secondary-container);
    --sidebar-text: var(--md-on-surface-variant);
    --sidebar-text-hover: var(--md-on-surface);
}

.sidebar,
.sidebar .position-sticky {
    background-color: var(--md-surface-container-low) !important;
}

/* The brand block at the top of the drawer. */
.sidebar .shell-brand,
.sidebar .shell-brand-text,
.sidebar .shell-brand-icon {
    color: var(--md-on-surface) !important;
}
.sidebar .shell-brand { border-bottom: 1px solid var(--md-outline-variant) !important; }

/* Menu search - was a dark field on what is now a light drawer. */
.sidebar-search { background: transparent !important; border-bottom-color: var(--md-outline-variant) !important; }
.menu-search-field {
    background: var(--md-surface-container-high) !important;
    border: none !important;
    border-radius: var(--md-shape-full) !important;
}
.menu-search-field .form-control {
    background: transparent !important;
    border: none !important;
    color: var(--md-on-surface) !important;
    box-shadow: none !important;
    min-height: 44px;
}
.menu-search-field .form-control::placeholder { color: var(--md-on-surface-variant) !important; }
.menu-search-field .search-icon,
.menu-search-field .search-hint,
.menu-search-field .btn-clear-search { color: var(--md-on-surface-variant) !important; }
.menu-search-field .search-hint {
    background: var(--md-surface-container-highest) !important;
    border-color: var(--md-outline-variant) !important;
}

/* Every label in the drawer reads at full strength; icons sit slightly
   back, which is Material's own treatment. */
.sidebar .nav-link,
.sidebar .nav-link span,
.sidebar .nav-item > a { color: var(--md-on-surface-variant) !important; }
.sidebar .nav-link i { opacity: .9; }
.sidebar .nav-link.active,
.sidebar .nav-link.active i,
.sidebar .nav-link.active span { color: var(--md-on-secondary-container) !important; }
.sidebar .nav-link:hover,
.sidebar .nav-link:hover i,
.sidebar .nav-link:hover span { color: var(--md-on-surface) !important; }

/* A submenu is a quieter level, not a disabled one. */
.sidebar .submenu { background: transparent !important; }
.sidebar .submenu .nav-link { color: var(--md-on-surface-variant) !important; }

/* Scrollbar inside the drawer. */
.sidebar::-webkit-scrollbar-thumb { background: var(--md-outline-variant); }
.sidebar::-webkit-scrollbar-track { background: transparent; }

/* The rail, when the drawer is collapsed. */
body.sidebar-collapsed .sidebar .nav-link { justify-content: center; }

/* ============================================================
   19. DENSITY, COLOUR AND THE DARK DRAWER
   =======================================
   Three corrections after reviewing version 3 on real screens.

   1. TOO AIRY. Material's default density is built for consumer
      apps. This is an operations tool: a filter bar with eight
      selects, tables with fourteen columns, modals with twenty
      fields. 48px inputs and 24px card padding pushed the work
      below the fold. Material defines density levels exactly for
      this - desktop applications are expected to run at -1 or
      -2 - so that is what this section applies.

   2. TOO MONOCHROME. Material's core scheme has no success or
      warning role, so Excel, PDF, Print and every status button
      came out the same tonal lilac and the toolbar stopped
      telling anyone anything. The extended colour groups added
      to MaterialPalette put green, amber and red back - still
      generated, still contrast-checked, still carrying their
      "on" colour.

   3. THE DRAWER AND SIGN-IN WASHED OUT. A pale navigation
      drawer is correct Material, and it was wrong for this
      product: the dark rail is what people recognise, and the
      labels on a light drawer were hard to pick out. Both now
      sit on a dark navigation surface in BOTH schemes.
   ============================================================ */

/* ---- 19.1 Density: -2 for inputs, -1 for everything else ---- */
.form-control, .form-select, .input-group-text {
    min-height: 40px !important;
    padding: .5rem .75rem !important;
    font-size: .875rem !important;
    line-height: 1.25rem !important;
    border-radius: var(--md-shape-xs) !important;
}
textarea.form-control { min-height: 80px !important; }
.form-control-sm, .form-select-sm { min-height: 34px !important; padding: .3rem .6rem !important; }

.form-label { margin-bottom: .3rem; font-size: .8125rem; }
.form-text { margin-top: .2rem; }
.mb-3 { margin-bottom: .85rem !important; }

.card-body { padding: 1rem 1.15rem !important; }
.card-header { padding: .8rem 1.15rem !important; font-size: .9375rem; }
.card-footer { padding: .75rem 1.15rem !important; }
.card { border-radius: var(--md-shape-m) !important; }

.btn { padding: .45rem 1.15rem; min-height: 36px; font-size: .8125rem; }
.btn-sm { padding: .3rem .8rem; min-height: 30px; font-size: .78125rem; }
.btn-lg { padding: .6rem 1.5rem; min-height: 42px; }

.table > thead > tr > th { padding: .65rem .75rem; }
.table > tbody > tr > td { padding: .6rem .75rem; font-size: .8125rem; }

.modal-header { padding: 1rem 1.25rem .6rem !important; }
.modal-body { padding: 0 1.25rem 1rem !important; }
.modal-footer { padding: .75rem 1.25rem 1rem !important; }
.modal-title { font-size: 1.25rem !important; }
.modal-dialog { --bs-modal-width: 540px; }
.modal-lg { --bs-modal-width: 800px; }
.modal-xl { --bs-modal-width: 1100px; }

.alert { padding: .7rem 1rem; }
.badge { padding: .3rem .5rem; }

/* ---- 19.2 Select fields: visible, compact, with a real chevron ----
   The browser's own arrow disappeared against the transparent field and
   the control read as a plain box. */
.form-select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23555' d='M4.2 6.2a1 1 0 0 1 1.4 0L8 8.6l2.4-2.4a1 1 0 1 1 1.4 1.4l-3.1 3.1a1 1 0 0 1-1.4 0L4.2 7.6a1 1 0 0 1 0-1.4z'/%3E%3C/svg%3E") !important;
    background-repeat: no-repeat !important;
    background-position: right .6rem center !important;
    background-size: 1.1rem !important;
    padding-right: 2.1rem !important;
    appearance: none;
    -webkit-appearance: none;
}
.form-select option {
    background: var(--md-surface-container);
    color: var(--md-on-surface);
}

/* The searchable and multi-select widgets this product builds on top. */
.ss-field, .ms-field, .searchable-select, .multi-select {
    border: 1px solid var(--md-outline) !important;
    border-radius: var(--md-shape-xs) !important;
    background: transparent !important;
    min-height: 40px;
}
.ss-menu, .ms-menu, .ss-dropdown, .ms-dropdown {
    background: var(--md-surface-container) !important;
    border: none !important;
    border-radius: var(--md-shape-xs) !important;
    box-shadow: var(--md-elev-2) !important;
}
.ss-option, .ms-option {
    color: var(--md-on-surface) !important;
    padding: .5rem .75rem !important;
    font-size: .875rem;
}
.ss-option:hover, .ms-option:hover,
.ss-option.is-active, .ms-option.is-active {
    background: rgba(var(--md-on-surface-rgb), var(--md-state-hover)) !important;
}
.ms-count, .ss-count { color: var(--md-on-surface-variant) !important; font-size: .75rem; }

/* ---- 19.3 Date and time inputs ----
   The native picker's own panel belongs to the browser and cannot be
   restyled; the field it opens from can. */
input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] {
    min-height: 40px !important;
    padding: .5rem .75rem !important;
    font-size: .875rem !important;
    color: var(--md-on-surface) !important;
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
    cursor: pointer;
    border-radius: 50%;
    padding: .2rem;
    opacity: .7;
    filter: invert(26%) sepia(12%) saturate(900%) hue-rotate(185deg);
}
input[type="date"]::-webkit-calendar-picker-indicator:hover {
    background: rgba(var(--md-primary-rgb), var(--md-state-hover));
    opacity: 1;
}

/* ---- 19.4 Colour back on the actions ----
   Generated extended colours, not literals - they follow the scheme and
   carry their own readable "on" colour. */
.btn-success,
div.dt-buttons .btn.buttons-excel, .dt-buttons .buttons-excel,
div.dt-buttons .btn[class*="excel"] {
    background-color: var(--md-success) !important;
    color: var(--md-on-success) !important;
    border-color: transparent !important;
}
.btn-danger,
div.dt-buttons .btn.buttons-pdf, .dt-buttons .buttons-pdf,
div.dt-buttons .btn[class*="pdf"] {
    background-color: var(--md-error) !important;
    color: var(--md-on-error) !important;
    border-color: transparent !important;
}
.btn-warning {
    background-color: var(--md-warning) !important;
    color: var(--md-on-warning) !important;
    border-color: transparent !important;
}
.btn-info,
div.dt-buttons .btn.buttons-copy, .dt-buttons .buttons-copy {
    background-color: var(--md-info) !important;
    color: var(--md-on-info) !important;
    border-color: transparent !important;
}
div.dt-buttons .btn.buttons-print, .dt-buttons .buttons-print {
    background-color: var(--md-secondary-container) !important;
    color: var(--md-on-secondary-container) !important;
}
.btn-secondary {
    background-color: var(--md-secondary-container) !important;
    color: var(--md-on-secondary-container) !important;
}

/* Status chips keep their meaning too. */
.badge.bg-success { background-color: var(--md-success-container) !important; color: var(--md-on-success-container) !important; }
.badge.bg-warning { background-color: var(--md-warning-container) !important; color: var(--md-on-warning-container) !important; }
.badge.bg-info    { background-color: var(--md-info-container) !important; color: var(--md-on-info-container) !important; }
.alert-success { background-color: var(--md-success-container) !important; color: var(--md-on-success-container) !important; }
.alert-warning { background-color: var(--md-warning-container) !important; color: var(--md-on-warning-container) !important; }
.alert-info    { background-color: var(--md-info-container) !important; color: var(--md-on-info-container) !important; }
.progress-bar.bg-success { background-color: var(--md-success) !important; }
.progress-bar.bg-warning { background-color: var(--md-warning) !important; }
.progress-bar.bg-info    { background-color: var(--md-info) !important; }
.text-success { color: var(--md-success) !important; }
.text-danger  { color: var(--md-error) !important; }
.text-warning { color: var(--md-warning) !important; }
.text-info    { color: var(--md-info) !important; }
.text-primary { color: var(--md-primary) !important; }

/* Card headers that carry a colour follow the same meanings. */
.card > .card-header.bg-success { background-color: var(--md-success-container) !important; color: var(--md-on-success-container) !important; }
.card > .card-header.bg-warning { background-color: var(--md-warning-container) !important; color: var(--md-on-warning-container) !important; }
.card > .card-header.bg-info    { background-color: var(--md-info-container) !important; color: var(--md-on-info-container) !important; }

/* ---- 19.5 The drawer goes back to dark, in both schemes ---- */
:root {
    --sidebar-bg: var(--md-nav-surface);
    --sidebar-hover: rgba(var(--md-on-nav-surface-rgb), .08);
    --sidebar-active: var(--md-nav-active);
    --sidebar-text: var(--md-on-nav-surface);
    --sidebar-text-hover: var(--md-on-nav-surface);
}

.sidebar,
.sidebar .position-sticky {
    background-color: var(--md-nav-surface) !important;
    border-right: none !important;
}

.sidebar .shell-brand,
.sidebar .shell-brand-text,
.sidebar .shell-brand-icon { color: var(--md-on-nav-surface) !important; }
.sidebar .shell-brand { border-bottom: 1px solid rgba(var(--md-on-nav-surface-rgb), .08) !important; }

.sidebar .nav-link,
.sidebar .nav-link span,
.sidebar .nav-item > a { color: var(--md-on-nav-surface) !important; }
.sidebar .nav-link { min-height: 44px; padding: .55rem 1rem; }
.sidebar .nav-link:hover,
.sidebar .nav-link:hover i,
.sidebar .nav-link:hover span {
    background-color: rgba(var(--md-on-nav-surface-rgb), var(--md-state-hover)) !important;
    color: var(--md-on-nav-surface) !important;
}
.sidebar .nav-link.active,
.sidebar .nav-link.active i,
.sidebar .nav-link.active span {
    background-color: var(--md-nav-active) !important;
    color: var(--md-on-nav-active) !important;
}
.sidebar .submenu .nav-link { color: var(--md-on-nav-surface) !important; opacity: .85; min-height: 38px; }
.sidebar .submenu .nav-link.active { opacity: 1; }

.sidebar-search { border-bottom-color: rgba(var(--md-on-nav-surface-rgb), .08) !important; }
.menu-search-field {
    background: var(--md-nav-surface-variant) !important;
    border: none !important;
}
.menu-search-field .form-control {
    background: transparent !important;
    color: var(--md-on-nav-surface) !important;
    min-height: 38px !important;
}
.menu-search-field .form-control::placeholder { color: rgba(var(--md-on-nav-surface-rgb), .55) !important; }
.menu-search-field .search-icon,
.menu-search-field .search-hint,
.menu-search-field .btn-clear-search { color: rgba(var(--md-on-nav-surface-rgb), .70) !important; }
.menu-search-field .search-hint {
    background: rgba(var(--md-on-nav-surface-rgb), .10) !important;
    border-color: rgba(var(--md-on-nav-surface-rgb), .15) !important;
}
.sidebar::-webkit-scrollbar-thumb { background: rgba(var(--md-on-nav-surface-rgb), .18); }

/* ---- 19.6 Sign-in goes dark on its brand side ---- */
.login-wrapper .brand-panel {
    background: var(--md-nav-surface) !important;
    color: var(--md-on-nav-surface) !important;
}
.login-wrapper .brand-panel *,
.login-wrapper .brand-feature,
.login-wrapper .brand-subtitle,
.login-wrapper .brand-footer { color: var(--md-on-nav-surface) !important; }
.login-wrapper .brand-feature { background: rgba(var(--md-on-nav-surface-rgb), .06); }
.login-wrapper .form-container {
    background: var(--md-surface-container-low);
    border-radius: var(--md-shape-l);
    padding: 1.75rem;
}
.login-wrapper .submit-btn { min-height: 44px; }

/* ---- 19.7 Text that has to stay readable ----
   Utility classes that assume a white page, used on tinted surfaces. */
.text-white { color: var(--md-on-nav-surface) !important; }
.sidebar .text-white, .brand-panel .text-white, .modal-header[class*="bg-"] .text-white { color: inherit !important; }
.text-muted, .text-secondary { color: var(--md-on-surface-variant) !important; }
small, .small { color: inherit; }

/* ============================================================
   20. TWO ORDERING PROBLEMS
   Both found by measuring the running page, and both are about
   which rule wins rather than which colour is right.
   ============================================================ */

/* 20.1 The menu search box.
   layouts/dashboard.blade.php carries its own <style> block, and it is
   emitted AFTER this stylesheet - so its `.menu-search-field .form-control`
   beat the identical selector here and the field kept a light background
   while the text on it had already gone white. Unreadable.
   One more level of specificity settles it. */
.sidebar .menu-search-field .form-control,
.sidebar .menu-search-field input {
    background: transparent !important;
    background-color: transparent !important;
    border: none !important;
    color: var(--md-on-nav-surface) !important;
    box-shadow: none !important;
}
.sidebar .menu-search-field .form-control::placeholder { color: rgba(var(--md-on-nav-surface-rgb), .55) !important; }
.sidebar .menu-search-field { background: var(--md-nav-surface-variant) !important; }
.sidebar .menu-search-field .search-icon,
.sidebar .menu-search-field .btn-clear-search { color: rgba(var(--md-on-nav-surface-rgb), .70) !important; }
.sidebar .menu-search-field .search-hint {
    background: rgba(var(--md-on-nav-surface-rgb), .10) !important;
    border-color: rgba(var(--md-on-nav-surface-rgb), .15) !important;
    color: rgba(var(--md-on-nav-surface-rgb), .70) !important;
}

/* 20.2 Buttons that carry two colour classes.
   DataTables renders its exports as `btn btn-secondary btn-sm btn-danger` -
   both a neutral class and a meaning class. Section 19.4 declared the
   meanings first and `.btn-secondary` after, so at equal specificity the
   neutral one won and PDF came out lilac.
   The meanings are restated last, which is the only thing that decides it. */
.btn.btn-success, .btn.buttons-excel,
div.dt-buttons .btn.btn-success, div.dt-buttons .btn.buttons-excel {
    background-color: var(--md-success) !important;
    color: var(--md-on-success) !important;
}
.btn.btn-danger, .btn.buttons-pdf,
div.dt-buttons .btn.btn-danger, div.dt-buttons .btn.buttons-pdf {
    background-color: var(--md-error) !important;
    color: var(--md-on-error) !important;
}
.btn.btn-warning,
div.dt-buttons .btn.btn-warning {
    background-color: var(--md-warning) !important;
    color: var(--md-on-warning) !important;
}
.btn.btn-info, .btn.buttons-copy,
div.dt-buttons .btn.btn-info, div.dt-buttons .btn.buttons-copy {
    background-color: var(--md-info) !important;
    color: var(--md-on-info) !important;
}
.btn.btn-primary, div.dt-buttons .btn.btn-primary {
    background-color: var(--md-primary) !important;
    color: var(--md-on-primary) !important;
}
/* Print stays neutral - it is not a destructive or a positive action. */
.btn.buttons-print, div.dt-buttons .btn.buttons-print {
    background-color: var(--md-secondary-container) !important;
    color: var(--md-on-secondary-container) !important;
}
/* An outline button must not be filled by the rules above. */
.btn[class*="btn-outline-"] { background-color: transparent !important; }
.btn.btn-outline-danger  { color: var(--md-error) !important; }
.btn.btn-outline-success { color: var(--md-success) !important; }
.btn.btn-outline-warning { color: var(--md-warning) !important; }
.btn.btn-outline-info    { color: var(--md-info) !important; }

/* ============================================================
   21. SIGN-IN, PROPERLY
   The feature list on the brand panel is written with
   `padding: 10px 0` - no horizontal inset. Section 19.6 gave
   those rows a tinted background, so the tint began exactly at
   the text and ended at a 260px column: four grey bars that
   looked misaligned rather than designed.
   Either the tint goes or the padding comes. Material's list
   pattern wants the inset, so that is what this does - and the
   icon gets the tonal container the pattern calls for.
   ============================================================ */
.login-wrapper .brand-features {
    max-width: 320px !important;
    margin-top: 2.5rem !important;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.login-wrapper .brand-feature {
    background: rgba(var(--md-on-nav-surface-rgb), .06) !important;
    border-radius: var(--md-shape-m) !important;
    padding: .7rem .9rem !important;
    gap: .85rem !important;
    font-size: .85rem !important;
    line-height: 1.3;
    color: var(--md-on-nav-surface) !important;
    opacity: .92;
}

/* The icon sits in its own tonal container - Material's list item with a
   leading icon, rather than a glyph floating beside text. */
.login-wrapper .brand-feature i {
    background: rgba(var(--md-on-nav-surface-rgb), .10);
    border-radius: var(--md-shape-s);
    color: var(--md-primary) !important;
    width: 28px !important;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: .9rem !important;
}

.login-wrapper .brand-logo span,
.login-wrapper .brand-logo { color: var(--md-on-nav-surface) !important; }
.login-wrapper .brand-subtitle {
    color: var(--md-on-nav-surface) !important;
    opacity: .7;
    max-width: 320px;
}
.login-wrapper .brand-footer { opacity: .55; }

/* --- the form side --- */
.login-wrapper .form-container {
    max-width: 420px !important;
    background: var(--md-surface-container-low);
    border-radius: var(--md-shape-xl);
    padding: 2rem 2rem 2.25rem;
    box-shadow: var(--md-elev-1);
}
.login-wrapper .form-container h1,
.login-wrapper .form-container h2,
.login-wrapper .form-container .form-title {
    font-size: 1.75rem;
    font-weight: 400;
    letter-spacing: 0;
    color: var(--md-on-surface);
    margin-bottom: .25rem;
}
.login-wrapper .form-container .form-subtitle,
.login-wrapper .form-container p {
    color: var(--md-on-surface-variant);
    font-size: .875rem;
    margin-bottom: 1.5rem;
}
.login-wrapper .field { margin-bottom: 1rem; }
.login-wrapper label {
    color: var(--md-on-surface-variant) !important;
    font-size: .8125rem;
    font-weight: 500;
    margin-bottom: .35rem;
    display: block;
}
.login-wrapper input:not([type="checkbox"]):not([type="radio"]) {
    width: 100%;
    min-height: 44px !important;
    padding: .6rem .85rem !important;
    font-size: .9375rem !important;
}
.login-wrapper input:focus {
    border-color: var(--md-primary) !important;
    box-shadow: inset 0 0 0 1px var(--md-primary) !important;
    outline: none;
}
.login-wrapper .password-wrapper { position: relative; }
.login-wrapper .password-toggle {
    background: transparent;
    border: none;
    color: var(--md-on-surface-variant);
    border-radius: 50%;
    width: 36px;
    height: 36px;
}
.login-wrapper .password-toggle:hover {
    background: rgba(var(--md-on-surface-rgb), var(--md-state-hover));
}
.login-wrapper .captcha-row { gap: .5rem; align-items: stretch; }
.login-wrapper .captcha-img {
    border-radius: var(--md-shape-xs);
    border: 1px solid var(--md-outline-variant);
}
.login-wrapper .captcha-refresh {
    border-radius: 50%;
    border: 1px solid var(--md-outline-variant);
    background: transparent;
    color: var(--md-on-surface-variant);
    width: 44px;
}
.login-wrapper .captcha-refresh:hover {
    background: rgba(var(--md-on-surface-rgb), var(--md-state-hover));
}
.login-wrapper .submit-btn {
    width: 100%;
    margin-top: .5rem;
    min-height: 46px !important;
    font-size: .9375rem;
    letter-spacing: .01em;
}
.login-wrapper .field-error { color: var(--md-error); font-size: .75rem; margin-top: .25rem; }

/* ============================================================
   22. THE LIBRARY'S OWN MESSAGE BOX
   DataTables shows "Copied 0 rows to clipboard" in a plain
   white panel of its own making. It was the one surface in the
   product still on browser defaults.
   ============================================================ */
div.dt-button-info {
    background: var(--md-inverse-surface) !important;
    color: var(--md-inverse-on-surface) !important;
    border: none !important;
    border-radius: var(--md-shape-m) !important;
    box-shadow: var(--md-elev-3) !important;
    padding: 1rem 1.25rem !important;
    text-align: center;
    /* Centred on the viewport rather than wherever the library left it. */
    position: fixed !important;
    top: 50% !important;
    left: 50% !important;
    transform: translate(-50%, -50%) !important;
    margin: 0 !important;
    min-width: 280px;
    max-width: min(90vw, 420px);
}
div.dt-button-info h2 {
    background: transparent !important;
    border: none !important;
    color: inherit !important;
    font-size: 1rem !important;
    font-weight: 500 !important;
    padding: 0 0 .35rem !important;
    margin: 0 !important;
}
div.dt-button-info > div { padding: 0 !important; font-size: .875rem; opacity: .85; }

/* ============================================================
   23. MOTION
   Material's easing and durations, applied where the product
   actually changes state: a page arriving, a dialog opening, a
   menu dropping, a table loading, a value updating.

   Every rule here is switched off under prefers-reduced-motion
   at the end of the section - motion is a preference, not a
   decoration somebody has to tolerate.
   ============================================================ */

/* --- a page arrives --- */
@keyframes md-enter {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}
/* EVERY direct child of the content column, not just a .container-fluid.
   Not every screen wraps its content in one: the dashboard puts rows
   straight into #main-content, so the selector matched nothing there - and
   the dashboard is the page people look at most. It was reported as
   "animation is not working", and it was not, on exactly that page.
   Modals are excluded: they animate when they open, not when the page
   behind them loads. */
#main-content > *:not(.modal):not(script):not(style) {
    animation: md-enter var(--md-dur-long) var(--md-ease-emphasized) both;
}

/* Cards arrive just after the page, in reading order. A short, bounded
   stagger - long cascades are a toy, not a help.
   Matched on the card itself rather than through an exact row/column
   nesting, because the markup differs from screen to screen. */
#main-content .card:not(.modal .card) {
    animation: md-enter 340ms var(--md-ease-emphasized) both;
}
#main-content .row > *:nth-child(1) .card { animation-delay: 30ms; }
#main-content .row > *:nth-child(2) .card { animation-delay: 70ms; }
#main-content .row > *:nth-child(3) .card { animation-delay: 110ms; }
#main-content .row > *:nth-child(4) .card { animation-delay: 150ms; }
#main-content .row > *:nth-child(n+5) .card { animation-delay: 180ms; }

/* --- dialogs --- */
.modal.fade .modal-dialog {
    transform: scale(.92) translateY(12px);
    transition: transform var(--md-dur-medium) var(--md-ease-emphasized),
                opacity var(--md-dur-medium) var(--md-ease-standard);
    opacity: 0;
}
.modal.show .modal-dialog { transform: none; opacity: 1; }
.modal-backdrop.fade { transition: opacity var(--md-dur-medium) var(--md-ease-standard); }

/* --- menus --- */
.dropdown-menu.show {
    animation: md-menu-in 140ms var(--md-ease-emphasized) both;
    transform-origin: top center;
}
/* Animated with the independent `scale` and `translate` properties, never
   with `transform`.

   Bootstrap positions a dropdown by writing `transform: translate(x, y)`
   onto it. A keyframe that animates `transform` replaces that for the life
   of the animation, and with `both` fill it keeps the final value forever -
   so the menu lost its position entirely and fell back to the top-left of
   its container, opening ON TOP of the button that opened it instead of
   under it. That was the header profile menu covering the toolbar.

   `scale` and `translate` are separate properties that compose with
   `transform` rather than overwrite it, so the entrance plays and the
   positioning survives. */
@keyframes md-menu-in {
    from { opacity: 0; scale: 1 .85; translate: 0 -4px; }
    to   { opacity: 1; scale: 1 1; translate: 0 0; }
}

/* --- tabs --- */
.tab-pane.active.show { animation: md-enter 220ms var(--md-ease-standard) both; }

/* --- the DataTables loading indicator ---
   A Material linear progress bar across the top of the table, instead of
   the library's grey "Processing..." label. */
.dataTables_wrapper .dataTables_processing {
    background: transparent !important;
    border: none !important;
    box-shadow: none !important;
    color: var(--md-on-surface-variant) !important;
    font-size: .8125rem;
    padding-top: 1.25rem !important;
    height: auto !important;
}
.dataTables_wrapper .dataTables_processing::before {
    content: '';
    position: absolute;
    inset: 0 0 auto;
    height: 4px;
    border-radius: var(--md-shape-full);
    background: var(--md-primary-container);
    overflow: hidden;
}
.dataTables_wrapper .dataTables_processing::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    height: 4px;
    width: 35%;
    border-radius: var(--md-shape-full);
    background: var(--md-primary);
    animation: md-indeterminate 1.1s var(--md-ease-standard) infinite;
}
@keyframes md-indeterminate {
    0%   { left: -35%; width: 35%; }
    60%  { left: 100%; width: 45%; }
    100% { left: 100%; width: 45%; }
}

/* A table that is loading says so by fading, not by vanishing. */
.dataTables_wrapper table.dataTable.processing tbody { opacity: .45; transition: opacity var(--md-dur-medium) var(--md-ease-standard); }

/* --- rows arriving after a search or a page change --- */
@keyframes md-row-in { from { opacity: 0; } to { opacity: 1; } }
table.dataTable > tbody > tr { animation: md-row-in 180ms var(--md-ease-standard) both; }

/* --- a figure that just changed ---
   The Insights screen refreshes itself; a number that moves without a flicker
   is a number nobody notices has moved. */
#insights [data-tile-value],
#insights [data-live],
#insights [data-status] {
    transition: color var(--md-dur-medium) var(--md-ease-standard);
}

/* --- buttons and inputs --- */
.btn { transition: background-color var(--md-dur-short) var(--md-ease-standard),
                   box-shadow var(--md-dur-medium) var(--md-ease-standard),
                   transform 80ms var(--md-ease-standard); }
.btn:active { transform: scale(.97); }

.form-control, .form-select {
    transition: border-color var(--md-dur-short) var(--md-ease-standard),
                box-shadow var(--md-dur-short) var(--md-ease-standard),
                background-color var(--md-dur-short) var(--md-ease-standard);
}

/* --- the drawer --- */
.sidebar .nav-link { transition: background-color var(--md-dur-short) var(--md-ease-standard),
                                 color var(--md-dur-short) var(--md-ease-standard); }
.sidebar .submenu { transition: height var(--md-dur-medium) var(--md-ease-emphasized); }

/* --- SweetAlert2 --- */
.swal2-popup.swal2-show { animation: md-swal-in 220ms var(--md-ease-emphasized); }
@keyframes md-swal-in {
    from { opacity: 0; transform: scale(.9); }
    to   { opacity: 1; transform: none; }
}

/* --- a spinner, wherever the application already shows one --- */
.spinner-border, .spinner-grow { color: var(--md-primary); }

/* Motion is a preference. */
@media (prefers-reduced-motion: reduce) {
    #main-content > *,
    #main-content .card,
    .dropdown-menu.show,
    .tab-pane.active.show,
    table.dataTable > tbody > tr,
    .swal2-popup.swal2-show {
        animation: none !important;
    }
    .modal.fade .modal-dialog { transform: none; opacity: 1; transition: none; }
    .dataTables_wrapper .dataTables_processing::after { animation: none; width: 100%; }
    .btn:active { transform: none; }
    * { transition-duration: 1ms !important; }
}

/* ============================================================
   24. THE SIGN-IN SCREEN: CURSOR, DEPTH AND MOTION
   Three faults, all on the one page a person sees before they
   trust the product.

   1. The I-BEAM. The feature list reads as prose, so the
      pointer became a text cursor over it and it looked like a
      field somebody could type in. It is a label, not an input.
   2. NO MOTION. Section 23 animates `#main-content`, and the
      sign-in screen has no such element - so the one page that
      should feel composed was the one page that just appeared.
   3. NO DEPTH. A flat dark rectangle beside a flat light one.
   ============================================================ */

/* ---- 24.1 It is a label, not a field ---- */
.login-wrapper .brand-panel,
.login-wrapper .brand-content,
.login-wrapper .brand-logo,
.login-wrapper .brand-subtitle,
.login-wrapper .brand-features,
.login-wrapper .brand-feature,
.login-wrapper .brand-feature *,
.login-wrapper .brand-footer {
    cursor: default;
    -webkit-user-select: none;
    user-select: none;
}

/* Headings and labels across the product are not text to select either -
   only the data is. */
.card-header, .modal-title, .form-label,
.sidebar .nav-link, .btn, .badge, .nav-tabs .nav-link,
.table > thead > tr > th {
    cursor: default;
    -webkit-user-select: none;
    user-select: none;
}
.btn, .sidebar .nav-link, .nav-tabs .nav-link, .dropdown-item { cursor: pointer; }

/* Table DATA stays selectable - people copy a serial number out of a cell. */
.table > tbody > tr > td { cursor: auto; user-select: text; }

/* ---- 24.2 Depth on the brand panel ----
   An ambient wash built from the scheme's own roles, so it follows the
   Color Theme like everything else. Two soft radial fields, no image. */
.login-wrapper .brand-panel {
    position: relative;
    overflow: hidden;
    background-image:
        radial-gradient(60% 55% at 18% 18%, rgba(var(--md-primary-rgb), .28), transparent 70%),
        radial-gradient(50% 50% at 85% 85%, rgba(var(--md-tertiary-rgb), .20), transparent 70%) !important;
    background-color: var(--md-nav-surface) !important;
}

/* A hairline between the two panels, instead of two slabs meeting. */
.login-wrapper .brand-panel::after {
    content: '';
    position: absolute;
    inset: 0 0 0 auto;
    width: 1px;
    background: rgba(var(--md-on-nav-surface-rgb), .12);
}

/* ---- 24.3 The screen arrives ---- */
@keyframes md-login-brand {
    from { opacity: 0; transform: translateX(-14px); }
    to   { opacity: 1; transform: none; }
}
@keyframes md-login-card {
    from { opacity: 0; transform: translateY(16px) scale(.985); }
    to   { opacity: 1; transform: none; }
}
@keyframes md-login-row {
    from { opacity: 0; transform: translateX(-10px); }
    to   { opacity: 1; transform: none; }
}

.login-wrapper .brand-content {
    animation: md-login-brand 520ms var(--md-ease-emphasized) both;
}
.login-wrapper .form-container {
    animation: md-login-card 480ms var(--md-ease-emphasized) both;
    animation-delay: 80ms;
}

/* The feature list arrives one row at a time - four rows is a sequence a
   person can follow, which is the only reason to stagger anything. */
.login-wrapper .brand-feature {
    animation: md-login-row 420ms var(--md-ease-emphasized) both;
}
.login-wrapper .brand-feature:nth-child(1) { animation-delay: 220ms; }
.login-wrapper .brand-feature:nth-child(2) { animation-delay: 300ms; }
.login-wrapper .brand-feature:nth-child(3) { animation-delay: 380ms; }
.login-wrapper .brand-feature:nth-child(4) { animation-delay: 460ms; }

/* They respond to the pointer, so the panel does not feel like a picture. */
.login-wrapper .brand-feature {
    transition: background-color var(--md-dur-short) var(--md-ease-standard),
                transform var(--md-dur-short) var(--md-ease-standard);
}
.login-wrapper .brand-feature:hover {
    background: rgba(var(--md-on-nav-surface-rgb), .11) !important;
    transform: translateX(2px);
}

/* The sign-in button is the one thing on the page to press. */
.login-wrapper .submit-btn {
    transition: box-shadow var(--md-dur-medium) var(--md-ease-standard),
                transform 90ms var(--md-ease-standard);
}
.login-wrapper .submit-btn:hover { box-shadow: var(--md-elev-2); }
.login-wrapper .submit-btn:active { transform: scale(.985); }

/* While it is submitting, it says so. */
.login-wrapper .submit-btn[disabled],
.login-wrapper .submit-btn.is-loading {
    opacity: .75;
    cursor: progress;
}

@media (prefers-reduced-motion: reduce) {
    .login-wrapper .brand-content,
    .login-wrapper .form-container,
    .login-wrapper .brand-feature { animation: none !important; }
    .login-wrapper .brand-feature:hover { transform: none; }
    .login-wrapper .submit-btn:active { transform: none; }
}

/* ============================================================
   25. THE OTHER UNAUTHENTICATED SCREENS
   Forgot password, reset password, verify e-mail - they share
   the guest layout, not the sign-in one, and had been left on
   Bootstrap defaults.
   ============================================================ */
body > .container > .card,
.guest-card,
main > .container .card {
    border-radius: var(--md-shape-xl);
}

/* ============================================================
   26. SIGN-IN: REAL MATERIAL TEXT FIELDS
   =====================================
   The fields were a label stacked above a grey box. That is
   Bootstrap's shape, not Material's. Material's text field puts
   the label ON the outline: resting inside the field, rising to
   sit in a notch on the border once the field is focused or
   filled. It is the single most recognisable component in the
   system, and it was the one still missing.

   Done with :has(), which is what lets a container react to the
   state of the input inside it. Guarded by @supports: a browser
   without :has() keeps the stacked layout rather than getting a
   label floating over an input it cannot move.
   ============================================================ */
@supports selector(:has(*)) {

    .login-wrapper .field {
        position: relative;
        margin-bottom: 1.5rem !important;
    }

    /* The label starts inside the field, vertically centred. */
    .login-wrapper .field > label {
        position: absolute;
        left: .65rem;
        top: 1.5rem;
        transform: translateY(-50%);
        margin: 0 !important;
        padding: 0 .3rem;
        background: var(--md-surface-container-low);
        color: var(--md-on-surface-variant) !important;
        font-size: .9375rem;
        font-weight: 400;
        line-height: 1;
        pointer-events: none;
        transition: top var(--md-dur-short) var(--md-ease-emphasized),
                    font-size var(--md-dur-short) var(--md-ease-emphasized),
                    color var(--md-dur-short) var(--md-ease-standard);
        z-index: 1;
    }

    /* Focused, or holding a value: the label rises into the outline. */
    .login-wrapper .field:has(input:focus) > label,
    .login-wrapper .field:has(input:not(:placeholder-shown)) > label {
        top: 0;
        font-size: .75rem;
    }
    .login-wrapper .field:has(input:focus) > label {
        color: var(--md-primary) !important;
    }

    /* The placeholder would sit behind the resting label, so it only appears
       once the label has moved out of the way. */
    .login-wrapper .field input::placeholder {
        color: transparent;
        transition: color var(--md-dur-short) var(--md-ease-standard);
    }
    .login-wrapper .field input:focus::placeholder {
        color: var(--md-on-surface-variant);
    }

    /* A field in error takes the error role, label and outline together. */
    .login-wrapper .field:has(.field-error) > label { color: var(--md-error) !important; }
    .login-wrapper .field:has(.field-error) input { border-color: var(--md-error) !important; }
}

/* The outline itself - 1px at rest, 2px in the accent when focused, which
   is what makes the notch read as a notch. */
.login-wrapper .field input:not([type="checkbox"]):not([type="radio"]) {
    height: 48px;
    min-height: 48px !important;
    border: 1px solid var(--md-outline) !important;
    border-radius: var(--md-shape-xs) !important;
    background: transparent !important;
    padding: .75rem .9rem !important;
    font-size: .9375rem !important;
    color: var(--md-on-surface) !important;
    width: 100%;
    transition: border-color var(--md-dur-short) var(--md-ease-standard),
                box-shadow var(--md-dur-short) var(--md-ease-standard);
}
.login-wrapper .field input:hover { border-color: var(--md-on-surface) !important; }
.login-wrapper .field input:focus {
    border-color: var(--md-primary) !important;
    box-shadow: inset 0 0 0 1px var(--md-primary) !important;
    outline: none !important;
}

/* The captcha row: field, image and refresh on one baseline.

   The input may wrap to its own line rather than shrink without limit.
   min-width: 0 let it collapse to a sliver whenever the card was narrow -
   at 390px on a phone, and again at 820px where the split hands the form
   half the screen - and its floating "Security check" label then ran out
   from under it and sat beneath the captcha image. 150px is about what
   that label needs; below it the row wraps instead, which keeps both
   readable. Measured against the row, not the viewport, so it holds at
   any width the card ends up. */
.login-wrapper .captcha-row {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
}
.login-wrapper .captcha-row input { flex: 1 1 150px; min-width: 150px; }
.login-wrapper .captcha-img {
    height: 48px;
    width: 120px;
    /* contain, never cover: the captcha is the one image on this page whose
       edges carry meaning. Cropping it to fill a box makes it unreadable,
       and an unreadable captcha is a locked door. */
    object-fit: contain;
    /* The one literal colour in this file, and deliberate: the captcha is
       generated server-side on a white ground. A surface role would be
       near-black in the dark scheme and the characters would vanish. */
    background: #FFFFFF;
    padding: 2px;
    border-radius: var(--md-shape-xs);
    border: 1px solid var(--md-outline-variant);
    flex: 0 0 auto;
}
.login-wrapper .captcha-refresh {
    height: 48px;
    width: 48px;
    flex: 0 0 48px;
    border-radius: 50%;
    border: 1px solid var(--md-outline-variant);
    background: transparent;
    color: var(--md-on-surface-variant);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background-color var(--md-dur-short) var(--md-ease-standard),
                transform var(--md-dur-medium) var(--md-ease-emphasized);
}
.login-wrapper .captcha-refresh:hover {
    background: rgba(var(--md-primary-rgb), var(--md-state-hover));
    transform: rotate(-90deg);
}

/* The eye sits inside the password field, not on top of its text. */
.login-wrapper .password-wrapper { position: relative; display: block; }
/* The generic field rule above is written as
   `.field input:not([type=checkbox]):not([type=radio])`, and :not() carries
   its argument's specificity - so that selector is 0,4,1, not 0,2,1. A
   plainer selector here lost, and the password text ran under the eye.
   Matching its shape and adding the wrapper puts this at 0,5,1. */
.login-wrapper .field .password-wrapper input:not([type="checkbox"]):not([type="radio"]) {
    padding-right: 3.25rem !important;
}
.login-wrapper .password-toggle {
    position: absolute;
    right: .35rem;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--md-on-surface-variant);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.login-wrapper .password-toggle:hover {
    background: rgba(var(--md-on-surface-rgb), var(--md-state-hover));
    color: var(--md-on-surface);
}

/* ---- the card the fields sit in ---- */
.login-wrapper .form-container {
    max-width: 440px !important;
    padding: 2.5rem 2.25rem !important;
    border-radius: var(--md-shape-xl) !important;
    box-shadow: var(--md-elev-2) !important;
    background: var(--md-surface-container-low) !important;
}
.login-wrapper .form-container > *:first-child { margin-top: 0; }

.login-wrapper .submit-btn {
    height: 48px;
    min-height: 48px !important;
    font-size: .9375rem !important;
    font-weight: 500 !important;
    letter-spacing: .01em;
    margin-top: .25rem !important;
    border-radius: var(--md-shape-full) !important;
}

/* ---- motion that is actually noticeable ----
   The earlier entrance was 8px and 400ms, which on a page this sparse read
   as nothing happening. The card now rises further and the fields follow it
   in, so the form assembles rather than appears. */
@keyframes md-login-field {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}
.login-wrapper .form-container > * {
    animation: md-login-field 420ms var(--md-ease-emphasized) both;
}
.login-wrapper .form-container > *:nth-child(1) { animation-delay: 160ms; }
.login-wrapper .form-container > *:nth-child(2) { animation-delay: 210ms; }
.login-wrapper .form-container > *:nth-child(3) { animation-delay: 260ms; }
.login-wrapper .form-container > *:nth-child(4) { animation-delay: 310ms; }
.login-wrapper .form-container > *:nth-child(5) { animation-delay: 360ms; }
.login-wrapper .form-container > *:nth-child(6) { animation-delay: 410ms; }
.login-wrapper .form-container > *:nth-child(7) { animation-delay: 460ms; }

.login-wrapper .form-container {
    animation: md-login-card 560ms var(--md-ease-emphasized) both !important;
    animation-delay: 60ms !important;
}

@media (prefers-reduced-motion: reduce) {
    .login-wrapper .form-container,
    .login-wrapper .form-container > * { animation: none !important; }
    .login-wrapper .captcha-refresh:hover { transform: none; }
}

@media (max-width: 575.98px) {
    .login-wrapper .form-container { padding: 1.75rem 1.25rem !important; }
    .login-wrapper .captcha-img { width: 90px; }
}

/* ============================================================
   27. SIGN-IN, REWORKED
   ====================
   Section 26 produced a correct outlined field and a correct
   card, and the result was characterless - three identical
   boxes stacked on a flat ground. Correct is the floor, not
   the finish.

   This replaces it with Material's FILLED text field: a tinted
   container with rounded top corners and an active indicator
   along the bottom that thickens into the accent on focus. It
   is the more expressive of the two field styles and it is what
   Google uses on its own sign-in.

   Three further things, all CSS, none of them markup:
     - leading icons inside the fields, drawn as background
       images so no element has to be added
     - an ambient ground behind the card so it sits in a space
       rather than floating on flat grey
     - a caret, selection and focus ring that belong to the
       scheme instead of the browser
   ============================================================ */

/* ---- 27.1 the ground ---- */
.login-wrapper .form-panel {
    background:
        radial-gradient(70% 60% at 85% 10%, rgba(var(--md-primary-rgb), .10), transparent 70%),
        radial-gradient(55% 55% at 10% 95%, rgba(var(--md-tertiary-rgb), .09), transparent 70%),
        var(--md-surface) !important;
}

/* ---- 27.2 the card ---- */
.login-wrapper .form-container {
    max-width: 460px !important;
    padding: 2.75rem 2.5rem 2.5rem !important;
    border-radius: 24px !important;
    background: var(--md-surface-container-lowest) !important;
    border: 1px solid var(--md-outline-variant) !important;
    box-shadow:
        0 1px 2px rgba(0, 0, 0, .04),
        0 12px 32px -8px rgba(0, 0, 0, .10) !important;
}

.login-wrapper .form-container h1,
.login-wrapper .form-container h2,
.login-wrapper .form-container .form-title {
    font-size: 2rem !important;
    font-weight: 500 !important;
    letter-spacing: -.02em !important;
    line-height: 1.15 !important;
    color: var(--md-on-surface) !important;
    margin-bottom: .4rem !important;
}
.login-wrapper .form-container .form-subtitle,
.login-wrapper .form-container > p {
    font-size: .9375rem !important;
    color: var(--md-on-surface-variant) !important;
    margin-bottom: 2rem !important;
}

/* ---- 27.3 filled text fields ---- */
.login-wrapper .field { margin-bottom: 1.25rem !important; }

.login-wrapper .field input:not([type="checkbox"]):not([type="radio"]) {
    height: 56px;
    min-height: 56px !important;
    width: 100%;
    /* Material's filled field: rounded at the top, square at the bottom,
       where the active indicator lives. */
    border: none !important;
    border-bottom: 1px solid var(--md-outline) !important;
    border-radius: 10px 10px 0 0 !important;
    /* background-COLOR, never the shorthand: `background:` resets
       background-repeat, -position and -size to their initial values, and
       this rule outranks the one that sets them - which tiled the leading
       icon right across the field. */
    background-color: var(--md-surface-container-high) !important;
    background-repeat: no-repeat !important;
    background-position: 1rem center !important;
    background-size: 20px 20px !important;
    /* room for the leading icon on the left, and the label above the value */
    padding: 1.4rem 1rem .5rem 3.1rem !important;
    font-size: .9375rem !important;
    color: var(--md-on-surface) !important;
    caret-color: var(--md-primary);
    box-shadow: none !important;
    transition: background-color var(--md-dur-short) var(--md-ease-standard),
                border-bottom-color var(--md-dur-short) var(--md-ease-standard),
                box-shadow var(--md-dur-short) var(--md-ease-standard);
}

.login-wrapper .field input:hover {
    background-color: var(--md-surface-container-highest) !important;
    border-bottom-color: var(--md-on-surface) !important;
}

/* The active indicator: 2px of accent along the bottom edge. */
.login-wrapper .field input:focus {
    background-color: var(--md-surface-container-highest) !important;
    border-bottom-color: var(--md-primary) !important;
    box-shadow: inset 0 -1px 0 0 var(--md-primary) !important;
    outline: none !important;
}

/* ---- 27.4 the label, inside the container ---- */
@supports selector(:has(*)) {
    .login-wrapper .field > label {
        left: 3.1rem;
        top: 1.75rem;
        background: transparent !important;
        padding: 0 !important;
        font-size: .9375rem;
        color: var(--md-on-surface-variant) !important;
    }
    .login-wrapper .field:has(input:focus) > label,
    .login-wrapper .field:has(input:not(:placeholder-shown)) > label {
        top: .85rem;
        font-size: .75rem;
    }
    .login-wrapper .field:has(input:focus) > label { color: var(--md-primary) !important; }
}

/* ---- 27.5 leading icons, drawn rather than added ----
   A background image on the input itself, so the markup is untouched.
   currentColor is not available to a data URI, so the stroke is the
   neutral outline colour - the same grey the labels rest at. */
.login-wrapper .field:has(#email) input:not([type="checkbox"]):not([type="radio"]),
.login-wrapper .field input[type="email"]:not([type="checkbox"]) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23757780' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='4' width='20' height='16' rx='3'/%3E%3Cpath d='m3 7 9 6 9-6'/%3E%3C/svg%3E") !important;
}

.login-wrapper .field:has(#password) input:not([type="checkbox"]):not([type="radio"]) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23757780' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='10' width='16' height='11' rx='2.5'/%3E%3Cpath d='M8 10V7a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E") !important;
}

.login-wrapper .field:has(#captcha) input:not([type="checkbox"]):not([type="radio"]),
.login-wrapper .captcha-row input:not([type="checkbox"]):not([type="radio"]) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23757780' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2 4 5v6c0 5 3.4 9.4 8 11 4.6-1.6 8-6 8-11V5l-8-3Z'/%3E%3Cpath d='m9 12 2 2 4-4'/%3E%3C/svg%3E") !important;
}

/* ---- 27.6 the captcha row ---- */
/* Same reasoning as the earlier .captcha-row block: the input wraps rather
   than collapsing to a sliver, which is what pushed its floating label out
   from under it and onto the captcha image. This restates it because this
   later rule would otherwise win. */
.login-wrapper .captcha-row { gap: .6rem; align-items: center; flex-wrap: wrap; }
.login-wrapper .captcha-row input { flex: 1 1 150px; min-width: 150px; }
.login-wrapper .captcha-img {
    height: 56px;
    width: 124px;
    border-radius: 10px;
    border: 1px solid var(--md-outline-variant);
}
.login-wrapper .captcha-refresh {
    height: 56px;
    width: 56px;
    flex: 0 0 56px;
    border-radius: 10px;
    background: var(--md-surface-container-high);
    border: none;
}
.login-wrapper .captcha-refresh:hover {
    background: var(--md-secondary-container);
    color: var(--md-on-secondary-container);
}

/* ---- 27.7 the eye ---- */
.login-wrapper .field .password-wrapper input:not([type="checkbox"]):not([type="radio"]) {
    padding-right: 3.5rem !important;
}
.login-wrapper .password-toggle { right: .5rem; width: 40px; height: 40px; }

/* ---- 27.8 the button ----
   A gradient off the primary role rather than a flat fill, and it lifts
   when the pointer is on it. One emphasised element per screen.  */
.login-wrapper .submit-btn {
    height: 52px;
    min-height: 52px !important;
    width: 100%;
    margin-top: 1.25rem !important;
    border: none;
    border-radius: var(--md-shape-full) !important;
    background-image: linear-gradient(135deg,
        var(--md-primary) 0%,
        color-mix(in srgb, var(--md-primary) 82%, var(--md-tertiary)) 100%) !important;
    color: var(--md-on-primary) !important;
    font-size: 1rem !important;
    font-weight: 500 !important;
    letter-spacing: .02em;
    box-shadow: 0 2px 8px -2px rgba(var(--md-primary-rgb), .5) !important;
}
.login-wrapper .submit-btn:hover {
    box-shadow: 0 8px 20px -4px rgba(var(--md-primary-rgb), .55) !important;
    transform: translateY(-1px);
}
.login-wrapper .submit-btn:active { transform: translateY(0) scale(.99); }

/* ---- 27.9 small details that read as care ---- */
.login-wrapper ::selection {
    background: rgba(var(--md-primary-rgb), .22);
    color: var(--md-on-surface);
}
/* Autofill.
   Chrome paints its own pale blue over an autofilled field. The usual cure
   is a huge inset box-shadow in the colour you want - but a box-shadow is
   painted ABOVE the background image, so it wiped out the leading icon the
   moment the browser filled the e-mail address.
   Delaying Chrome's background transition instead leaves our own
   background - colour AND icon - exactly where it was. */
.login-wrapper .field input:-webkit-autofill,
.login-wrapper .field input:-webkit-autofill:hover,
.login-wrapper .field input:-webkit-autofill:focus,
.login-wrapper .field input:-webkit-autofill:active {
    -webkit-text-fill-color: var(--md-on-surface) !important;
    -webkit-box-shadow: none !important;
    box-shadow: none !important;
    transition: background-color 600000s 0s, color 600000s 0s !important;
    caret-color: var(--md-primary);
}

/* The focus indicator still has to show on an autofilled field. */
.login-wrapper .field input:-webkit-autofill:focus {
    border-bottom-color: var(--md-primary) !important;
}
.login-wrapper .field-error {
    margin-top: .35rem;
    font-size: .75rem;
    color: var(--md-error);
}

@media (max-width: 575.98px) {
    .login-wrapper .form-container { padding: 2rem 1.25rem !important; border-radius: 20px !important; }
    .login-wrapper .form-container h1 { font-size: 1.65rem !important; }
    .login-wrapper .captcha-img { width: 96px; }
    .login-wrapper .captcha-refresh { width: 52px; flex-basis: 52px; }
}

/* ============================================================
   28. THE LABEL MUST NEVER SHARE A SPOT WITH THE CARET
   Reported: a text caret sitting in the field alongside the
   resting label. That happens whenever the label has not moved
   out of the way while the field is focused.

   The float was keyed on `:has(input:focus)`. :focus-within
   does the same job without :has(), works when the input is
   wrapped (the password field is, inside .password-wrapper),
   and is supported everywhere - so it is the more reliable of
   the two. Both are kept; whichever matches first wins.

   And while a field is empty and unfocused the caret is hidden
   outright, so a caret can never appear under a label that has
   not moved.
   ============================================================ */
.login-wrapper .field:focus-within > label {
    top: .85rem !important;
    font-size: .75rem !important;
    color: var(--md-primary) !important;
}

/* No caret until the field is genuinely being typed in. */
.login-wrapper .field input:placeholder-shown:not(:focus) {
    caret-color: transparent;
}
.login-wrapper .field input:focus { caret-color: var(--md-primary); }

/* The browser's own "Please fill out this field" bubble cannot be styled,
   but it anchors to the control - so the control keeps enough room beneath
   it for the bubble to sit clear of the next field. */
.login-wrapper .field { padding-bottom: 2px; }

/* ============================================================
   29. SIGN-IN: THE REFINEMENT PASS
   Same layout, same components. What changes is the things
   that separate a form that works from one that feels
   finished: vertical rhythm, optical alignment, the weight of
   the fill, and one emphasised element instead of three.
   ============================================================ */

/* ---- 29.1 rhythm ----
   The spacing was set by whatever felt roomy at the time: a 2rem gap under
   the subtitle, 1.25rem between fields, 1.25rem above the button. Nothing
   related to anything. It is now one 8px scale - 24 / 16 / 24 - so the
   eye travels down the form at an even pace. */
.login-wrapper .form-container {
    max-width: 424px !important;
    padding: 2.5rem 2.25rem 2.25rem !important;
    border-radius: 20px !important;
}
.login-wrapper .form-container .form-subtitle,
.login-wrapper .form-container > p { margin-bottom: 1.5rem !important; }
.login-wrapper .field { margin-bottom: 1rem !important; }
.login-wrapper .submit-btn { margin-top: 1.5rem !important; }

/* ---- 29.2 type ----
   2rem was shouting on a 424px card. 1.75rem with tighter tracking holds
   the same authority at the size the card actually is, and the subtitle
   drops a step so the two read as a pair rather than competing. */
.login-wrapper .form-container h1,
.login-wrapper .form-container h2,
.login-wrapper .form-container .form-title {
    font-size: 1.75rem !important;
    font-weight: 500 !important;
    letter-spacing: -.025em !important;
    margin-bottom: .35rem !important;
}
.login-wrapper .form-container .form-subtitle,
.login-wrapper .form-container > p { font-size: .875rem !important; }

/* ---- 29.3 the fields carry less weight ----
   Three containers at surface-container-high read as three grey slabs, and
   the form became the slabs rather than the content. A step lighter at
   rest, with the darker tone kept for hover and focus - so the fill now
   means "this is the one you are in" instead of "these are fields". */
.login-wrapper .field input:not([type="checkbox"]):not([type="radio"]) {
    height: 54px;
    min-height: 54px !important;
    background-color: var(--md-surface-container) !important;
    border-radius: 12px 12px 0 0 !important;
    border-bottom: 1px solid var(--md-outline-variant) !important;
    padding: 1.35rem 1rem .45rem 3.2rem !important;
    font-size: .9375rem !important;
    background-position: 1.05rem center !important;
}
.login-wrapper .field input:hover {
    background-color: var(--md-surface-container-high) !important;
    border-bottom-color: var(--md-on-surface-variant) !important;
}
.login-wrapper .field input:focus {
    background-color: var(--md-surface-container-high) !important;
}

/* The label tracks with the text it labels. */
@supports selector(:has(*)) {
    .login-wrapper .field > label { left: 3.2rem; letter-spacing: .01em; }
}
.login-wrapper .field:focus-within > label { letter-spacing: .02em; }

/* ---- 29.4 one family of corners ----
   The fields are 12px at the top, so the captcha image and its refresh
   button were 10px for no reason anybody could name. */
.login-wrapper .captcha-img,
.login-wrapper .captcha-refresh {
    height: 54px;
    border-radius: 12px;
}
.login-wrapper .captcha-refresh { width: 54px; flex-basis: 54px; }
.login-wrapper .captcha-img { width: 118px; }
.login-wrapper .captcha-row { gap: .5rem; }

/* ---- 29.5 one emphasised element ----
   The button had a coloured glow AND a gradient AND a lift. Any one of
   those is emphasis; all three at once is noise. The gradient stays
   because it is the only place on the page the two accent roles meet;
   the glow drops to a shadow that reads as weight rather than light. */
.login-wrapper .submit-btn {
    height: 50px;
    min-height: 50px !important;
    font-size: .9375rem !important;
    letter-spacing: .01em;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .10),
                0 4px 12px -4px rgba(var(--md-primary-rgb), .45) !important;
}
.login-wrapper .submit-btn:hover {
    box-shadow: 0 2px 4px rgba(0, 0, 0, .10),
                0 8px 18px -6px rgba(var(--md-primary-rgb), .55) !important;
}

/* ---- 29.6 keyboard users get a ring, not just a colour ---- */
/* Buttons only. A text field already shows focus through its active
   indicator; adding a ring on top drew a second box around the field -
   and because autofocus counts as keyboard focus, the e-mail field wore it
   the moment the page loaded. */
.login-wrapper .submit-btn:focus-visible,
.login-wrapper .captcha-refresh:focus-visible,
.login-wrapper .password-toggle:focus-visible {
    outline: 2px solid var(--md-primary) !important;
    outline-offset: 2px !important;
}
.login-wrapper .field input:focus,
.login-wrapper .field input:focus-visible { outline: none !important; }

/* The refresh control is a quiet action beside the captcha, not a filled
   button competing with Sign in. */
.login-wrapper .captcha-refresh {
    background: transparent !important;
    border: 1px solid var(--md-outline-variant) !important;
    color: var(--md-on-surface-variant) !important;
}
.login-wrapper .captcha-refresh:hover {
    background: rgba(var(--md-primary-rgb), var(--md-state-hover)) !important;
    border-color: var(--md-outline) !important;
    color: var(--md-primary) !important;
}

/* ---- 29.7 the panel behind it ----
   The ambient wash was strong enough to be noticed, which is the one thing
   a background should not be. */
.login-wrapper .form-panel {
    background:
        radial-gradient(65% 55% at 88% 6%, rgba(var(--md-primary-rgb), .06), transparent 70%),
        radial-gradient(50% 50% at 6% 96%, rgba(var(--md-tertiary-rgb), .05), transparent 70%),
        var(--md-surface) !important;
}

/* ============================================================
   30. THE RIPPLE, AND THE REST OF THE MOTION
   ========================================
   Section 23 added entrances and transitions, and it still did
   not feel like Material - because the one interaction people
   actually associate with Google was missing. Pressing anything
   in Material produces a ripple that expands from the control
   and fades. Without it, every press feels like a static web
   page no matter how well the page arrived.

   Done with a pseudo-element and two background states, so it
   needs no JavaScript and no markup:
     at rest   a radial gradient scaled up and invisible
     pressed   snapped to zero size at 30% opacity
     released  expands back out over half a second, fading

   ::before is used throughout, never ::after - Bootstrap draws
   dropdown carets with .dropdown-toggle::after, and taking that
   over would delete the little arrow from every menu button.
   ============================================================ */

.btn,
.sidebar .nav-link,
.dropdown-item,
.nav-tabs .nav-link,
.page-link,
.dataTables_wrapper .dataTables_paginate .paginate_button,
div.dt-buttons .btn {
    position: relative;
    overflow: hidden;
    /* The ripple is painted inside the control, so anything that draws its
       own content stays above it. */
    isolation: isolate;
}

.btn::before,
.sidebar .nav-link::before,
.dropdown-item::before,
.nav-tabs .nav-link::before,
.page-link::before,
.dataTables_wrapper .dataTables_paginate .paginate_button::before,
div.dt-buttons .btn::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background-image: radial-gradient(circle,
        currentColor 10%, transparent 10.01%);
    background-repeat: no-repeat;
    background-position: 50%;
    transform: scale(10);
    opacity: 0;
    transition: transform .5s var(--md-ease-standard),
                opacity 1s var(--md-ease-standard);
    pointer-events: none;
}

.btn:active::before,
.sidebar .nav-link:active::before,
.dropdown-item:active::before,
.nav-tabs .nav-link:active::before,
.page-link:active::before,
.dataTables_wrapper .dataTables_paginate .paginate_button:active::before,
div.dt-buttons .btn:active::before {
    transform: scale(0);
    opacity: .28;
    transition: 0s;
}

/* A filled button ripples in its own "on" colour; an outlined or text one
   ripples in the accent, because currentColor there is already the accent. */
.btn-primary::before, .btn-danger::before, .btn-success::before,
.btn-warning::before, .btn-info::before { opacity: 0; }

/* ---- 30.2 the drawer's active indicator slides ----
   Material moves the indicator between items rather than redrawing it. */
.sidebar .nav-link {
    transition: background-color var(--md-dur-medium) var(--md-ease-emphasized),
                color var(--md-dur-short) var(--md-ease-standard),
                padding-left var(--md-dur-short) var(--md-ease-standard);
}
.sidebar .nav-link:hover { padding-left: 1.15rem; }

/* ---- 30.3 selection controls ---- */
.form-check-input {
    transition: background-color var(--md-dur-short) var(--md-ease-standard),
                border-color var(--md-dur-short) var(--md-ease-standard),
                box-shadow var(--md-dur-short) var(--md-ease-standard);
}
/* The focus halo Material puts behind a checkbox. */
.form-check-input:focus-visible {
    box-shadow: 0 0 0 8px rgba(var(--md-primary-rgb), var(--md-state-focus));
}
.form-check-input:active { transform: scale(.92); }

/* ---- 30.4 a table that is loading shimmers ----
   A progress bar says "something is happening". A shimmer over the rows
   says "this content is coming", which is the more useful of the two while
   a page of records is being fetched. */
@keyframes md-shimmer {
    from { background-position: -140% 0; }
    to   { background-position: 240% 0; }
}
.dataTables_wrapper table.dataTable.processing tbody tr > td {
    color: transparent !important;
    background-image: linear-gradient(90deg,
        transparent 0%,
        rgba(var(--md-on-surface-rgb), .07) 45%,
        rgba(var(--md-on-surface-rgb), .07) 55%,
        transparent 100%) !important;
    background-size: 220% 100%;
    animation: md-shimmer 1.2s linear infinite;
    border-radius: var(--md-shape-xs);
}
.dataTables_wrapper table.dataTable.processing tbody tr > td * { visibility: hidden; }

/* ---- 30.5 cards respond to the pointer ----
   Only where a card is a target. A static panel that lifts when the mouse
   crosses it is noise. */
#main-content a > .card,
#main-content .card[role="button"],
#main-content .card.card-clickable {
    transition: box-shadow var(--md-dur-medium) var(--md-ease-standard),
                transform var(--md-dur-medium) var(--md-ease-emphasized);
}
#main-content a > .card:hover,
#main-content .card[role="button"]:hover,
#main-content .card.card-clickable:hover {
    box-shadow: var(--md-elev-2);
    transform: translateY(-2px);
}

/* ---- 30.6 numbers that change ----
   The dashboard and Insights both refresh themselves. A figure that moves
   without any change in appearance is a figure nobody notices has moved. */
@keyframes md-value-in {
    from { opacity: .35; transform: translateY(-3px); }
    to   { opacity: 1; transform: none; }
}
#insights [data-tile-value],
#insights [data-live],
#insights [data-status],
#main-content .stat-value,
#main-content .card h2,
#main-content .card h3 {
    animation: md-value-in var(--md-dur-medium) var(--md-ease-standard);
}

/* ---- 30.7 toasts and alerts arrive ---- */
@keyframes md-toast-in {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: none; }
}
.toast.show,
#main-content > .alert,
.alert-dismissible {
    animation: md-toast-in 280ms var(--md-ease-emphasized) both;
}

/* ---- 30.8 everything above is a preference ---- */
@media (prefers-reduced-motion: reduce) {
    .btn::before,
    .sidebar .nav-link::before,
    .dropdown-item::before,
    .nav-tabs .nav-link::before,
    .page-link::before,
    div.dt-buttons .btn::before { display: none; }

    .sidebar .nav-link:hover { padding-left: 1rem; }
    .form-check-input:active { transform: none; }

    .dataTables_wrapper table.dataTable.processing tbody tr > td { animation: none; }

    #main-content a > .card:hover,
    #main-content .card[role="button"]:hover { transform: none; }

    #insights [data-tile-value], #insights [data-live], #insights [data-status],
    #main-content .stat-value, #main-content .card h2, #main-content .card h3,
    .toast.show, #main-content > .alert, .alert-dismissible { animation: none; }
}

/* ==========================================================================
   31. Dialogs that fit the screen
   ==========================================================================
   The Add Person and Update Person dialogs were taller than the window -
   1256px and 1315px of content in a 900px viewport. The whole dialog
   scrolled with the page, which meant the title scrolled away at the top and
   Save and Cancel scrolled away at the bottom: to submit a form you had to
   scroll back down past everything you had just filled in to find the
   button.

   Material keeps the header and the action row in place and scrolls only
   what is between them, so the dialog's title and its actions are always
   visible. Bootstrap offers exactly this as `modal-dialog-scrollable`, but
   that is a class on the markup, and the markup is not ours to change - so
   it is done here instead, for every dialog that needs it.

   `max-height` only takes effect on a dialog that would otherwise overflow,
   and `overflow-y: auto` only scrolls when there is something to scroll, so
   dialogs that already fit are left exactly as they were. */

.modal-dialog {
    max-height: calc(100vh - 3.5rem);
}

/* The exception: a scrolling container clips anything that escapes it. A
   native <select> is safe - the browser draws its list in a layer of its
   own, outside the page - but a menu built from markup is not, so any
   dialog carrying one keeps its old behaviour and simply grows. */
@supports selector(:has(*)) {
    /* Each selector is kept on one line deliberately. A line break between
       two :not() clauses is whitespace, and whitespace in a selector is a
       descendant combinator - splitting these for readability silently
       retargets every rule at the dialog's children. */
    .modal-content:not(:has(.dropdown-menu)):not(:has(.select2)):not(:has(.ts-wrapper)):not(:has(.choices)):not(:has(.flatpickr-calendar)):not(:has(.daterangepicker)) {
        max-height: calc(100vh - 3.5rem);
    }

    .modal-content:not(:has(.dropdown-menu)):not(:has(.select2)):not(:has(.ts-wrapper)):not(:has(.choices)):not(:has(.flatpickr-calendar)):not(:has(.daterangepicker)) > form {
        /* The body is usually not a direct child of the dialog: the markup
           wraps the fields and the action row in a <form>. For the body to
           be able to scroll, that wrapper has to carry the dialog's column
           layout through to it rather than stopping it. */
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
    }

.modal-content:not(:has(.dropdown-menu)):not(:has(.select2)):not(:has(.ts-wrapper)):not(:has(.choices)):not(:has(.flatpickr-calendar)):not(:has(.daterangepicker)) .modal-body {
        overflow-y: auto;
        overscroll-behavior: contain;   /* don't scroll the page behind it */

        /* A flex item will not shrink below the height of its own content
           unless it is told it may. Without this the body keeps its full
           height, overflows the capped dialog, and the fields at the bottom
           are clipped away - visible, unreachable, and worse than the
           overflow this set out to fix. */
        min-height: 0;
    }

    /* Once the body scrolls it needs a scrollbar that belongs to the skin
       rather than the operating system's default slab. */
    .modal-content:not(:has(.dropdown-menu)) .modal-body {
        scrollbar-width: thin;
        scrollbar-color: var(--md-outline-variant) transparent;
    }
    .modal-content:not(:has(.dropdown-menu)) .modal-body::-webkit-scrollbar {
        width: 8px;
    }
    .modal-content:not(:has(.dropdown-menu)) .modal-body::-webkit-scrollbar-thumb {
        background-color: var(--md-outline-variant);
        border-radius: 99px;
        border: 2px solid transparent;
        background-clip: content-box;
    }
}

/* On a phone a dialog has no room to be a floating card. Material gives the
   full screen to a dialog this size, which is also what stops a tall form
   from being squeezed into a sliver down the middle. */
@media (max-width: 575.98px) {
    .modal-dialog { max-height: calc(100vh - 1rem); }
    @supports selector(:has(*)) {
        .modal-content:not(:has(.dropdown-menu)):not(:has(.select2)):not(:has(.ts-wrapper)):not(:has(.choices)):not(:has(.flatpickr-calendar)):not(:has(.daterangepicker)) {
            max-height: calc(100vh - 1rem);
        }
    }
}

/* --- one size for compact controls ---
   The density pass had put small controls at 34px, which is not a step on
   any scale - it was just a number. Material's first density step down from
   a 40px control is 36px, which is also a friendlier target and sits better
   beside a 30px small button. */
.form-control-sm, .form-select-sm {
    min-height: 36px !important;
}

/* ==========================================================================
   32. FIELDS WITHOUT BORDERS
   ==========================================================================
   Every text field, select and textarea was an outlined one: a 1px line in
   the neutral outline tone, which darkened to near-black on hover and on
   focus. Four sides of hard line around every control is a lot of ink for
   something whose job is to hold a value, and the near-black hover state
   read as the field being broken rather than being pointed at.

   These are filled fields instead - Material's other text field, and the
   quieter of the two. The container carries a tonal fill a step up from the
   surface behind it, so the field is still obviously a field, with no line
   at all. The corners are softened to the medium shape so the fill reads as
   a rounded container rather than a box.

   Focus is shown in the accent colour and nowhere in black.
   ========================================================================== */

.form-control,
.form-select,
textarea.form-control,
.input-group-text {
    background-color: var(--md-surface-container-highest) !important;
    border: none !important;
    border-radius: var(--md-shape-m) !important;
    box-shadow: none !important;
    color: var(--md-on-surface) !important;
}

/* Hover lifts the fill very slightly. No line appears, and nothing turns
   black. */
.form-control:hover:not(:disabled):not([readonly]),
.form-select:hover:not(:disabled) {
    background-color: rgba(var(--md-on-surface-rgb), .09) !important;
}

/* Focus is a ring in the accent colour, drawn inside the field's own box so
   that focusing a control never nudges the layout by a pixel. */
.form-control:focus,
.form-select:focus,
textarea.form-control:focus {
    background-color: var(--md-surface-container-highest) !important;
    border: none !important;
    box-shadow: inset 0 0 0 2px var(--md-primary) !important;
    outline: none !important;
}

.form-control.is-invalid,
.form-select.is-invalid,
.was-validated .form-control:invalid {
    background-color: rgba(var(--md-error-rgb), .07) !important;
    box-shadow: inset 0 0 0 2px var(--md-error) !important;
}

.form-control:disabled,
.form-select:disabled,
.form-control[readonly] {
    background-color: rgba(var(--md-on-surface-rgb), .05) !important;
    color: var(--md-on-surface-variant) !important;
    box-shadow: none !important;
}

/* An input group is one control made of several boxes, so only its outer
   corners are rounded - the seams between the parts stay square, or the
   pieces read as separate controls that happen to touch. */
.input-group > .form-control,
.input-group > .form-select,
.input-group > .input-group-text,
.input-group > .btn {
    border-radius: 0 !important;
}
.input-group > :first-child {
    border-top-left-radius: var(--md-shape-m) !important;
    border-bottom-left-radius: var(--md-shape-m) !important;
}
.input-group > :last-child {
    border-top-right-radius: var(--md-shape-m) !important;
    border-bottom-right-radius: var(--md-shape-m) !important;
}
/* A hairline between the parts, since there is no longer a border to
   separate them. */
.input-group > :not(:first-child):not(.btn) {
    box-shadow: inset 1px 0 0 0 rgba(var(--md-on-surface-rgb), .10) !important;
}

/* Checkboxes, radios and switches keep their outline. A control that is
   empty until you act on it has nothing to show but its edge - taking that
   away would leave nothing to click. */


/* --- the same treatment on the sign-in fields ---
   These were filled already, but Material's filled field keeps a one-pixel
   "active indicator" along the bottom edge, and that is the dark line under
   the email and password boxes. With the rest of the product's fields now
   showing focus as an accent ring, the line is both the only black edge
   left and a second way of saying the same thing. */
.login-wrapper .field input:not([type="checkbox"]):not([type="radio"]) {
    border: none !important;
    border-bottom: none !important;
    border-radius: var(--md-shape-m) !important;
}
.login-wrapper .field input:hover:not([type="checkbox"]):not([type="radio"]) {
    border-bottom: none !important;
}
.login-wrapper .field input:focus:not([type="checkbox"]):not([type="radio"]) {
    border-bottom: none !important;
    box-shadow: inset 0 0 0 2px var(--md-primary) !important;
}

/* ==========================================================================
   33. LEADING ICONS THAT SURVIVE AUTOFILL
   ==========================================================================
   The envelope on the email field vanished as soon as the field held a
   value, while the padlock on the password field stayed - which is the
   clue to what was happening. The icons were drawn as the input's own
   background-image, and when Chrome autofills a saved credential it applies
   its own background to that input, `background-image: none` included. That
   style belongs to the browser, not to the page, so no amount of
   !important from here can outrank it. Chrome autofills the username on its
   own and waits for consent before filling the password, so the email lost
   its icon and the password kept its own.

   The fix is not to fight the browser for the background: the icon is drawn
   on the field wrapper instead, as a pseudo-element that is a layer of its
   own. Autofill cannot reach it, and neither can anything else that happens
   to the value.
   ========================================================================== */

.login-wrapper .field { position: relative; }

/* The input no longer carries an icon of its own, or two would show - very
   nearly on top of each other, which is worse than either.

   These repeat the selectors that set the icons in 27.5 rather than using a
   shorter one, because `:has(#email)` contributes an id to the specificity
   of that rule: a plainer selector loses to it however many !importants it
   carries. Same selectors, declared later, so these win. */
.login-wrapper .field:has(#email) input:not([type="checkbox"]):not([type="radio"]),
.login-wrapper .field:has(#password) input:not([type="checkbox"]):not([type="radio"]),
.login-wrapper .field:has(#captcha) input:not([type="checkbox"]):not([type="radio"]),
.login-wrapper .field input[type="email"]:not([type="checkbox"]),
.login-wrapper .captcha-row input:not([type="checkbox"]):not([type="radio"]) {
    background-image: none !important;
}

.login-wrapper .field::before {
    content: '';
    position: absolute;
    left: 1.05rem;
    top: 28px;                 /* the middle of a 56px field */
    transform: translateY(-50%);
    width: 20px;
    height: 20px;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 20px 20px;
    pointer-events: none;      /* the field underneath stays clickable */
    z-index: 2;                /* above the browser's autofill wash */
}

@supports selector(:has(*)) {
    .login-wrapper .field:has(#email)::before {
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23757780' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='4' width='20' height='16' rx='3'/%3E%3Cpath d='m3 7 9 6 9-6'/%3E%3C/svg%3E");
    }
    .login-wrapper .field:has(#password)::before {
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23757780' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='10' width='16' height='11' rx='2.5'/%3E%3Cpath d='M8 10V7a4 4 0 0 1 8 0v3'/%3E%3C/svg%3E");
    }
    .login-wrapper .field:has(#captcha)::before {
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23757780' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2 4 5v6c0 5 3.4 9.4 8 11 4.6-1.6 8-6 8-11V5l-8-3Z'/%3E%3Cpath d='m9 12 2 2 4-4'/%3E%3C/svg%3E");
    }
}

/* ==========================================================================
   34. THE TEXT SIZE CONTROL IS A SQUARE
   ==========================================================================
   A- / A / A+ are declared in the layout as 44x36 boxes with a 6px corner -
   a set of three rounded squares, which is what a segmented control looks
   like. Section 15 rounds every button in the header to a circle, to make
   the icon buttons round as Material asks, and these three were swept up
   with them: a 44x36 box forced to `border-radius: 50%` becomes a lozenge,
   and three lozenges in a row stop reading as one control.

   Icon buttons stay round. These go back to being squares.
   ========================================================================== */

.top-header .shell-textsize .shell-btn {
    border-radius: var(--md-shape-s) !important;
}

/* The file picker's own button is drawn by the browser and keeps its own
   look unless it is asked otherwise - a square-cornered slab against the
   field's rounded fill. It is the only control on the Settings screen that
   still looked borrowed. */
.form-control[type="file"] {
    padding: .35rem .5rem !important;
}
.form-control[type="file"]::file-selector-button {
    margin-right: .75rem;
    padding: .3rem .9rem;
    border: none;
    border-radius: var(--md-shape-s);
    background-color: var(--md-secondary-container);
    color: var(--md-on-secondary-container);
    font-size: .8125rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color var(--md-dur-short) var(--md-ease-standard);
}
.form-control[type="file"]:hover::file-selector-button {
    background-color: var(--md-primary-container);
    color: var(--md-on-primary-container);
}

/* ==========================================================================
   35. CARD HEADERS ARE NOT COLOUR BANDS
   ==========================================================================
   The Profile screen stacks four cards, and the markup gives each header a
   different contextual colour: bg-primary, bg-warning, bg-info, bg-dark.
   Mapped straight onto Material's container roles that became four
   saturated bands down one page - lavender, orange, blue, grey - each one
   loud enough to read as a status, so the page looked like four warnings
   rather than one account.

   Material does not band a card header in colour. The card is one surface;
   colour arrives through the small things - the icon, a chip, the action.
   So the tint stays, at a tenth of its strength, and the colour moves into
   the header's icon where it still codes what the card is about. The
   headers become one family, and the things that genuinely need attention
   - a chip, a Change Password button - are the only saturated colour left
   on the page.

   The class names still mean what they meant; they are just quieter.
   ========================================================================== */

.card > .card-header.bg-primary,
.card > .card-header.bg-success,
.card > .card-header.bg-info,
.card > .card-header.bg-danger,
.card > .card-header.bg-warning,
.card > .card-header.bg-secondary,
.card > .card-header.bg-dark,
.card > .card-header.bg-light {
    color: var(--md-on-surface) !important;
    border-bottom: 1px solid var(--md-outline-variant) !important;
}

.card > .card-header.bg-primary   { background-color: rgba(var(--md-primary-rgb), .10) !important; }
.card > .card-header.bg-info      { background-color: rgba(var(--md-secondary-rgb), .10) !important; }
.card > .card-header.bg-success   { background-color: rgba(var(--md-success-rgb), .10) !important; }
.card > .card-header.bg-warning   { background-color: rgba(var(--md-warning-rgb), .12) !important; }
.card > .card-header.bg-danger    { background-color: rgba(var(--md-error-rgb), .10) !important; }
.card > .card-header.bg-secondary { background-color: rgba(var(--md-on-surface-rgb), .05) !important; }
.card > .card-header.bg-dark      { background-color: rgba(var(--md-on-surface-rgb), .06) !important; }
.card > .card-header.bg-light     { background-color: rgba(var(--md-on-surface-rgb), .03) !important; }

/* The colour the band used to carry now lives in the icon, which is where
   it has to compete with nothing. */
.card > .card-header[class*="bg-"] > h1 > i:first-child,
.card > .card-header[class*="bg-"] > h2 > i:first-child,
.card > .card-header[class*="bg-"] > h3 > i:first-child,
.card > .card-header[class*="bg-"] > h4 > i:first-child,
.card > .card-header[class*="bg-"] > h5 > i:first-child,
.card > .card-header[class*="bg-"] > h6 > i:first-child,
.card > .card-header[class*="bg-"] > i:first-child {
    margin-right: .5rem;
    opacity: 1;
}
.card > .card-header.bg-primary i:first-child   { color: var(--md-primary) !important; }
.card > .card-header.bg-info i:first-child      { color: var(--md-secondary) !important; }
.card > .card-header.bg-success i:first-child   { color: var(--md-success) !important; }
.card > .card-header.bg-warning i:first-child   { color: var(--md-warning) !important; }
.card > .card-header.bg-danger i:first-child    { color: var(--md-error) !important; }
.card > .card-header.bg-dark i:first-child,
.card > .card-header.bg-secondary i:first-child { color: var(--md-on-surface-variant) !important; }

/* `text-white` is on the header element itself in this markup, and it is
   !important, so it has to be answered explicitly or the title stays white
   on a now very pale background. */
.card > .card-header[class*="bg-"].text-white,
.card > .card-header[class*="bg-"].text-dark,
.card > .card-header[class*="bg-"] .text-white {
    color: var(--md-on-surface) !important;
}

/* ==========================================================================
   36. THE PROFILE AVATAR, AT THE SIZE IT ASKS FOR
   ==========================================================================
   The avatar on My Profile is written into the markup as a 100px circle,
   and it was rendering at 40. design-tokens.css pins every .profile-circle
   to 40px with !important - correct for the one in the header, which is a
   40px icon button, but it also catches the one on the page, and an
   !important declaration outranks even an inline style. So the account's
   avatar had been quietly shrunk to the size of a toolbar button, sitting
   in the middle of the empty band that had been reserved for it.

   The header one stays exactly as it is. Only the one on the page grows
   back, scoped to the content area.
   ========================================================================== */

#main-content .profile-circle {
    width: 96px !important;
    height: 96px !important;
    font-size: 2.25rem !important;
    font-weight: 400 !important;
    /* A flat tonal circle. The gradient and the drop shadow were doing the
       work of making a 40px chip stand out; at 96px the size does that. */
    background: var(--md-primary-container) !important;
    color: var(--md-on-primary-container) !important;
    box-shadow: none !important;
}
#main-content .profile-circle:hover { transform: none; }

/* ==========================================================================
   37. WARNING BUTTONS ARE TONAL
   ==========================================================================
   `--md-warning` is a tone-40 amber, which is to say a dark brown, and a
   filled button in it came out as a mud-coloured slab - on My Profile it
   made Change Password the loudest thing on the screen while being the
   least attractive.

   Material's medium-emphasis button is the tonal one: the container tone
   behind the on-container tone. Same colour family, same meaning, legible
   text, and it stops competing with the page it sits on.
   ========================================================================== */

.btn-warning,
.btn.btn-warning,
div.dt-buttons .btn.btn-warning {
    background-color: var(--md-warning-container) !important;
    color: var(--md-on-warning-container) !important;
    border-color: transparent !important;
}
.btn-warning:hover,
.btn.btn-warning:hover {
    background-color: var(--md-warning) !important;
    color: var(--md-on-warning) !important;
}

/* ==========================================================================
   38. THE DROPDOWN LIST
   ==========================================================================
   Paired with public/js/material-select.js. A native <select> popup is drawn
   by the operating system and cannot be styled at all, so the list is
   rebuilt in the page and this is what it looks like: a rounded elevated
   surface, roomy rows, a state layer on hover, and a tick against the one
   that is chosen - the menu from Google's own products rather than the grey
   system list with a hard blue bar.
   ========================================================================== */

.mdsel { position: relative; display: block; }

/* The real control is still there and still submits; it is just not what
   you look at. Kept in the layout (not display:none) so that validation
   messages and `scrollIntoView` still land in the right place. */
select.mdsel-native {
    position: absolute !important;
    opacity: 0 !important;
    width: 100% !important;
    height: 100% !important;
    top: 0; left: 0;
    pointer-events: none !important;
    margin: 0 !important;
}

.mdsel-trigger {
    display: flex;
    align-items: center;
    width: 100%;
    text-align: left;
    cursor: pointer;
    position: relative;
    z-index: 1;
}
.mdsel-value {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mdsel-value.mdsel-placeholder { color: var(--md-on-surface-variant); }
.mdsel.is-open .mdsel-trigger { box-shadow: inset 0 0 0 2px var(--md-primary) !important; }
.mdsel.is-disabled .mdsel-trigger { cursor: not-allowed; }

/* --- the surface --- */
.mdsel-menu {
    position: fixed;
    z-index: 1200;                /* above a modal's 1055 */
    background-color: var(--md-surface-container-high);
    border-radius: var(--md-shape-m);
    box-shadow: 0 4px 10px rgba(var(--md-shadow-rgb), .14),
                0 1px 3px rgba(var(--md-shadow-rgb), .10);
    padding: .375rem;
    overflow: hidden auto;
    overscroll-behavior: contain;
    opacity: 0;
    transform: scaleY(.9) translateY(-4px);
    transform-origin: top center;
    transition: opacity 120ms var(--md-ease-standard),
                transform 160ms var(--md-ease-emphasized);
}
.mdsel-menu.is-above { transform-origin: bottom center; transform: scaleY(.9) translateY(4px); }
.mdsel-menu.is-shown { opacity: 1; transform: none; }

.mdsel-menu::-webkit-scrollbar { width: 8px; }
.mdsel-menu::-webkit-scrollbar-thumb {
    background-color: var(--md-outline-variant);
    border-radius: 99px;
    border: 2px solid transparent;
    background-clip: content-box;
}

/* --- a row --- */
.mdsel-option {
    display: flex;
    align-items: center;
    gap: .25rem;
    min-height: 40px;
    padding: .4rem .75rem .4rem .25rem;
    border-radius: var(--md-shape-s);
    color: var(--md-on-surface);
    font-size: .875rem;
    line-height: 1.25rem;
    cursor: pointer;
    user-select: none;
}
.mdsel-option + .mdsel-option { margin-top: 2px; }
.mdsel-option-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The state layer, not a different colour - the row keeps its own colour
   and gets a wash of the text colour over it, which is how every other
   surface in this skin responds to a pointer. */
.mdsel-option.is-active {
    background-color: rgba(var(--md-on-surface-rgb), var(--md-state-hover));
}
.mdsel-option[aria-disabled="true"] {
    color: var(--md-on-surface-variant);
    opacity: .5;
    cursor: not-allowed;
}

/* The chosen row is marked by a tick and a tonal ground. */
.mdsel-tick {
    flex: 0 0 1.75rem;
    width: 1.75rem;
    height: 1.25rem;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 18px 18px;
}
.mdsel-option.is-selected {
    background-color: var(--md-secondary-container);
    color: var(--md-on-secondary-container);
}
.mdsel-option.is-selected.is-active {
    background-color: var(--md-secondary-container);
    background-image: linear-gradient(rgba(var(--md-on-secondary-container-rgb), var(--md-state-hover)),
                                      rgba(var(--md-on-secondary-container-rgb), var(--md-state-hover)));
}
.mdsel-option.is-selected .mdsel-tick {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 12.5 5 5L20 6.5'/%3E%3C/svg%3E");
}

/* --- search, for the long ones --- */
.mdsel-search { padding: .125rem .125rem .375rem; }
.mdsel-search-input {
    width: 100%;
    min-height: 36px;
    border: none;
    border-radius: var(--md-shape-s);
    background-color: rgba(var(--md-on-surface-rgb), .06);
    color: var(--md-on-surface);
    font-size: .875rem;
    padding: .35rem .75rem;
    outline: none;
}
.mdsel-search-input:focus { box-shadow: inset 0 0 0 2px var(--md-primary); }

@media (prefers-reduced-motion: reduce) {
    .mdsel-menu { transition: none; transform: none; }
}

/* ==========================================================================
   39. THE SELECTED BUTTON IN A GROUP HAS TO BE READABLE
   ==========================================================================
   A segmented group - All / Unseen / Seen - is built from radio inputs with
   `.btn-check` and outline buttons. Bootstrap's checked state sets white
   text and fills the button with its own primary; this skin repaints the
   fill from the Material roles but the white text was left behind, so the
   selected segment was white text on a pale ground: the one option you had
   chosen was the one you could not read.
   ========================================================================== */

.btn-check:checked + .btn-outline-primary,
.btn-check:active + .btn-outline-primary,
.btn-check:checked + .btn-outline-secondary,
.btn-check:checked + .btn-outline-success,
.btn-check:checked + .btn-outline-danger,
.btn-check:checked + .btn-outline-info,
.btn-check:checked + .btn-outline-warning,
.btn-check:checked + .btn-outline-dark,
.btn-outline-primary.active,
.btn-outline-secondary.active {
    background-color: var(--md-secondary-container) !important;
    border-color: var(--md-secondary-container) !important;
    color: var(--md-on-secondary-container) !important;
}
.btn-check:focus-visible + .btn {
    box-shadow: 0 0 0 3px rgba(var(--md-primary-rgb), .35) !important;
}


/* ==========================================================================
   40. THE HEADER ICONS
   ==========================================================================
   Three things, all of them things you notice rather than read.

   The theme toggle was Bootstrap Icons' `moon-stars-fill` - a crescent with
   two sparkles beside it, which at 19px is a smudge with grit around it and
   reads as decoration rather than a switch. Material's own dark_mode and
   light_mode are a plain crescent and a plain sun: they say what they do at
   a glance, which is the entire job of an icon this small.

   The glyph is swapped without touching the markup. The icon font's
   character is dropped and the shape is painted as a mask instead, so it
   takes `currentColor` and keeps following the theme like the glyph did.
   The two classes the toggle's own script swaps between are whatselect  the
   shape, so the existing behaviour drives it unchanged.
   ========================================================================== */

#themeModeToggleIcon {
    display: inline-block;
    width: 22px;
    height: 22px;
    vertical-align: -4px;
    background-color: currentColor;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: 22px 22px;
    mask-size: 22px 22px;
    /* The switch itself is worth a moment of movement: the sun and the moon
       are the same gesture seen twice, so it turns rather than blinks. */
    transition: transform 420ms var(--md-ease-emphasized);
}
#themeModeToggleIcon::before { content: none !important; }

#themeModeToggleIcon.bi-moon-stars-fill {
    -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36A5.39 5.39 0 0 1 16.5 13 5.5 5.5 0 0 1 11 7.5c0-1.73.8-3.28 2.05-4.29A9.4 9.4 0 0 0 12 3z'/></svg>");
    mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36A5.39 5.39 0 0 1 16.5 13 5.5 5.5 0 0 1 11 7.5c0-1.73.8-3.28 2.05-4.29A9.4 9.4 0 0 0 12 3z'/></svg>");
}
#themeModeToggleIcon.bi-sun-fill {
    -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='5'/><g stroke='black' stroke-width='2' stroke-linecap='round'><line x1='12' y1='1.5' x2='12' y2='3.5'/><line x1='12' y1='20.5' x2='12' y2='22.5'/><line x1='1.5' y1='12' x2='3.5' y2='12'/><line x1='20.5' y1='12' x2='22.5' y2='12'/><line x1='4.6' y1='4.6' x2='6' y2='6'/><line x1='18' y1='18' x2='19.4' y2='19.4'/><line x1='4.6' y1='19.4' x2='6' y2='18'/><line x1='18' y1='6' x2='19.4' y2='4.6'/></g></svg>");
    mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='5'/><g stroke='black' stroke-width='2' stroke-linecap='round'><line x1='12' y1='1.5' x2='12' y2='3.5'/><line x1='12' y1='20.5' x2='12' y2='22.5'/><line x1='1.5' y1='12' x2='3.5' y2='12'/><line x1='20.5' y1='12' x2='22.5' y2='12'/><line x1='4.6' y1='4.6' x2='6' y2='6'/><line x1='18' y1='18' x2='19.4' y2='19.4'/><line x1='4.6' y1='19.4' x2='6' y2='18'/><line x1='18' y1='6' x2='19.4' y2='4.6'/></g></svg>");
    transform: rotate(180deg);
}

#themeModeToggle { color: var(--md-on-surface-variant) !important; }
#themeModeToggle:hover #themeModeToggleIcon { background-color: var(--md-primary); }

/* --- the whole page changes with it ---
   Switching theme repainted every surface instantly, which at this size
   reads as a flicker. Easing the colours - and only the colours - turns it
   into one deliberate change. Nothing here animates layout, so there is no
   cost beyond the paint that was happening anyway. */
body, .sidebar, .top-header, .card, .modal-content, .dropdown-menu,
.table, .form-control, .form-select, .mdsel-trigger, .mdsel-menu {
    transition: background-color 280ms var(--md-ease-standard),
                color 280ms var(--md-ease-standard),
                border-color 280ms var(--md-ease-standard);
}

/* --- the bell ---
   It carried a hard-coded blue in its inline style - the one colour in the
   header that ignored the Color Theme. (Named here in words rather than as
   a hex literal on purpose: the skin test scans this file for stray brand
   colours and does not care whether it finds one in a rule or a comment.) And an unread count sat there perfectly still, which is the
   one time the icon has something to say. */
.top-header .bi-bell-fill { color: var(--md-primary) !important; }

/* The badge only exists when something is unread, so it is the badge that
   decides whether the bell moves - no extra class, no JavaScript. */
@supports selector(:has(*)) {
    .top-header a:has(> .badge) .bi-bell-fill {
        /* An inline element has no box of its own, so a percentage pivot
           resolved to 0 and the bell swung from its left edge instead of
           from where it hangs. */
        display: inline-block;
        transform-origin: 50% 10%;
        animation: md-bell-ring 2.6s var(--md-ease-standard) infinite;
    }
    .top-header a:has(> .badge) .badge {
        animation: md-badge-pulse 2.6s var(--md-ease-standard) infinite;
    }
}

/* Two quick swings, then a long pause. A bell that never stops ringing is
   a bell nobody looks at. */
@keyframes md-bell-ring {
    0%, 70%, 100% { transform: rotate(0); }
    74% { transform: rotate(13deg); }
    78% { transform: rotate(-11deg); }
    82% { transform: rotate(8deg); }
    86% { transform: rotate(-6deg); }
    90% { transform: rotate(3deg); }
    94% { transform: rotate(0); }
}
@keyframes md-badge-pulse {
    0%, 68%, 100% { transform: translate(-50%, -50%) scale(1); }
    76% { transform: translate(-50%, -50%) scale(1.18); }
    88% { transform: translate(-50%, -50%) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .top-header a:has(> .badge) .bi-bell-fill,
    .top-header a:has(> .badge) .badge { animation: none; }
    #themeModeToggleIcon,
    body, .sidebar, .top-header, .card, .modal-content, .dropdown-menu,
    .table, .form-control, .form-select, .mdsel-trigger, .mdsel-menu {
        transition: none;
    }
}

/* ==========================================================================
   41. THE PROFILE PAGE, FINISHED
   ==========================================================================
   What was left on this screen was the Security Tips block: four lines of
   muted text in a bulleted list, set at .small inside an already-muted
   container, so it read as fine print rather than as advice - the one piece
   of the page actually telling you something useful was the hardest part to
   read.

   It becomes a checklist: each line gets a tick in the success tone, room
   to breathe, and the body colour everything else on the page uses. Same
   four tips, same markup.
   ========================================================================== */

#main-content .card-body > .small.text-muted > ul {
    list-style: none;
    margin: .6rem 0 0;
    padding: 0;
    display: grid;
    gap: .4rem;
}
#main-content .card-body > .small.text-muted > ul > li {
    position: relative;
    padding-left: 1.65rem;
    color: var(--md-on-surface);
    font-size: .8125rem;
    line-height: 1.35rem;
}
#main-content .card-body > .small.text-muted > ul > li::before {
    content: '';
    position: absolute;
    left: 0;
    top: .18rem;
    width: 1rem;
    height: 1rem;
    background-color: var(--md-success);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-1.1 14.2-3.6-3.6 1.4-1.4 2.2 2.2 4.9-4.9 1.4 1.4-6.3 6.3z'/%3E%3C/svg%3E") center/1rem 1rem no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-1.1 14.2-3.6-3.6 1.4-1.4 2.2 2.2 4.9-4.9 1.4 1.4-6.3 6.3z'/%3E%3C/svg%3E") center/1rem 1rem no-repeat;
}

/* The heading above the list is a label, so it is set like one. */
#main-content .card-body > .small.text-muted > strong {
    color: var(--md-on-surface-variant);
    font-size: .6875rem;
    font-weight: 500;
    letter-spacing: .08em;
    text-transform: uppercase;
}

/* The values on the left of the page are read, not edited - they should
   look settled rather than like inputs someone emptied. */
#main-content .form-control-plaintext {
    color: var(--md-on-surface);
    font-size: .9375rem;
    padding: .15rem 0;
}
#main-content .form-control-plaintext > i:first-child {
    color: var(--md-on-surface-variant);
    margin-right: .15rem;
}

/* ==========================================================================
   42. SIDE-BY-SIDE PANELS IN A ROW END AT THE SAME LINE
   ==========================================================================
   On the Dashboard the second chart row puts "Attendance Statistics - Active
   Users" next to "Today's In-Time Statistics". The two white panels were
   measurably different heights - 408px against 435px at 1440 wide - because
   each panel was only as tall as its own contents: the right one carries an
   extra shift picker above its chart, the left one a taller chart area. The
   grid column they sit in already stretches to the tallest in the row, so the
   mismatch was the panel not filling that column: the shorter panel stopped
   short and left a ragged step along the bottom edge of the row, which reads
   as a layout bug rather than as two cards.

   The fix is a rule about rows, not about the Dashboard: a column that holds
   a panel becomes a vertical flex box, and the panel it holds grows to fill
   it. Rows whose columns hold anything else are untouched, so no other screen
   can be affected by this.

   The panel's own bottom margin stays where it is and is simply absorbed by
   the flex box, so nothing overflows the column and the spacing between rows
   is exactly as it was before.
   ========================================================================== */

#main-content .row > [class*="col-"]:has(> .chart-container) {
    display: flex;
    flex-direction: column;
}
#main-content .row > [class*="col-"]:has(> .chart-container) > .chart-container {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* With the panel now taller than its contents, the fixed-height chart box
   inside it would have left a band of empty white under the chart. The chart
   box takes the slack instead, so the extra height goes to the chart - which
   is the part of the panel worth looking at - and both panels stay visually
   full. A flex child needs min-height:0 or it refuses to shrink back below
   the inline height it was given. */
#main-content .row > [class*="col-"]:has(> .chart-container) > .chart-container > div[style*="height"] {
    flex: 1 1 auto;
    min-height: 0;
}

/* ==========================================================================
   43. TAP TARGETS ON ICON-ONLY LINK BUTTONS
   ==========================================================================
   A couple of borderless icon buttons - the theme toggle, and the little
   help icon that sits inline in a sentence on the Attendance Report - were
   15 to 22 pixels wide. Fine to look at, hard to hit with a thumb.

   The fix is a larger HIT AREA rather than a larger button. Giving the icon
   a min-width would have opened a gap in the middle of the sentence it sits
   in, so instead an invisible pseudo-element is centred over it. Nothing
   moves, and the target becomes a comfortable size.

   Only on narrow screens: on a desktop pointer a 44px catchment around a
   15px icon set in running text would start swallowing clicks meant for the
   words either side of it.

   `::after` is used rather than `::before` because `::before` carries the
   ripple, and `.dropdown-toggle` is excluded because Bootstrap draws its
   caret with `::after`.
   ========================================================================== */

@media (max-width: 575.98px) {
    .btn-link.p-0:not(.dropdown-toggle) { position: relative; }

    .btn-link.p-0:not(.dropdown-toggle)::after {
        content: '';
        position: absolute;
        top: 50%;
        left: 50%;
        width: 44px;
        height: 44px;
        transform: translate(-50%, -50%);
        /* Invisible, but it is what the finger lands on. */
        background: transparent;
    }
}

/* Very narrow: give the input its own full row so the image and the refresh
   button stay together underneath it. Letting the three share a wrapping row
   put the input and image on one line and stranded the button alone on the
   next, which looked like a layout fault. */
@media (max-width: 520px) {
    .login-wrapper .captcha-row input { flex: 1 1 100%; min-width: 100%; }

    /* The image keeps its own size. Letting it grow to fill the row stretched
       the generated characters and made the captcha harder to read than the
       layout problem it was solving. */
    .login-wrapper .captcha-img { flex: 0 0 auto; }
    .login-wrapper .captcha-refresh { flex: 0 0 auto; }
}


/* ============================================================
   LOGIN ON SMALL SCREENS: ONE SCREEN, NO SCROLLING

   The stacked layout fitted its content but not the viewport - a
   phone had to scroll to reach the Sign in button, so the first
   thing anyone saw was half a form. Everything below compresses
   the hero and the card until the whole screen fits, in tiers:
   the shorter the viewport, the more supporting detail goes,
   starting with what is decoration and ending with what is only
   nice to have. The fields, the captcha and the button never go.

   dvh, not vh: on mobile browsers vh is the tallest the viewport
   ever gets, so a 100vh page still scrolls behind the address bar.
   ============================================================ */
@media (max-width: 767.98px) {
    .login-wrapper { min-height: 100dvh; }

    /* --- the hero --- */
    .login-wrapper .brand-panel {
        padding: 20px 18px 18px !important;
        border-radius: 0 0 22px 22px !important;
    }
    .login-wrapper .brand-mark {
        margin-bottom: 10px !important;
        min-height: 26px !important;
    }
    .login-wrapper .brand-display {
        font-size: clamp(1.4rem, 7vw, 1.8rem) !important;
        margin-bottom: 8px !important;
    }
    .login-wrapper .brand-stats { gap: 6px 18px !important; }
    .login-wrapper .brand-stat-key { font-size: .95rem !important; }
    .login-wrapper .brand-stat-label { font-size: .58rem !important; letter-spacing: .05em !important; }

    /* --- the form side --- */
    .login-wrapper .form-panel {
        padding: 14px 16px 18px !important;
        /* Centred in whatever space the hero leaves. Anchored to the top it
           fitted the screen but left a band of empty ground underneath, which
           reads as the page having failed to finish loading. */
        justify-content: center !important;
    }
    .login-wrapper .form-container {
        margin-top: 12px !important;
        padding: 20px 18px !important;
    }
    .login-wrapper .form-header { margin-bottom: 16px !important; }
    .login-wrapper .form-header h1 { font-size: 1.15rem !important; margin-bottom: 2px !important; }
    .login-wrapper .form-header p { font-size: .8rem !important; }

    .login-wrapper .field { margin-bottom: .7rem !important; }
    .login-wrapper .field input:not([type="checkbox"]):not([type="radio"]) {
        height: 50px !important;
        min-height: 50px !important;
    }
    .login-wrapper .captcha-img { height: 44px !important; }
    .login-wrapper .captcha-refresh { width: 44px !important; height: 44px !important; }
    .login-wrapper .submit-btn { height: 48px !important; min-height: 48px !important; }

    .login-wrapper .form-assurance {
        margin-top: 12px !important;
        gap: 4px 10px !important;
        font-size: .64rem !important;
        flex-wrap: nowrap !important;
    }
    .login-wrapper .form-assurance span { white-space: nowrap; }
    .login-wrapper .form-assurance i { font-size: .72rem !important; }
}

/* Shorter than a typical phone: the three-word stat row and the strip
   under the card are the first to go - the headline already says both. */
@media (max-width: 767.98px) and (max-height: 760px) {
    .login-wrapper .brand-stats { display: none !important; }
    .login-wrapper .brand-display { font-size: clamp(1.3rem, 6.4vw, 1.6rem) !important; }
    .login-wrapper .brand-panel { padding: 16px 18px 16px !important; }
    .login-wrapper .form-assurance { display: none !important; }
}

/* Shorter still: the sub-heading goes and the spacing tightens again.
   Past here only the fields, the captcha and the button remain. */
@media (max-width: 767.98px) and (max-height: 680px) {
    .login-wrapper .form-header p { display: none !important; }
    .login-wrapper .form-header { margin-bottom: 12px !important; }
    .login-wrapper .form-container { margin-top: 8px !important; padding: 16px 16px !important; }
    .login-wrapper .field { margin-bottom: .55rem !important; }
    .login-wrapper .field input:not([type="checkbox"]):not([type="radio"]) {
        height: 46px !important;
        min-height: 46px !important;
    }
    .login-wrapper .brand-display { font-size: 1.25rem !important; }
    .login-wrapper .brand-panel { padding: 14px 16px 14px !important; }
    .login-wrapper .form-panel { padding: 10px 14px 14px !important; }
}


/* ============================================================
   SHORT VIEWPORTS, ANY WIDTH (landscape phones, split windows)

   A phone turned sideways is 360-430px tall at any width, so the
   width-based tiers above never fire and the page scrolled again.
   This is keyed on height alone and applies to both layouts: the
   two-column one on a wide-but-short window, and the stacked one
   on a narrow-but-short window. The claim shrinks to the headline
   and the card gives up everything except the controls.
   ============================================================ */
@media (max-height: 560px) {
    .login-wrapper { min-height: 100dvh; }

    /* The supporting copy goes: there is no room for a paragraph
       when the whole screen is shorter than a phone is wide. */
    .login-wrapper .brand-lede,
    .login-wrapper .brand-ticker,
    .login-wrapper .brand-rule,
    .login-wrapper .form-assurance,
    .login-wrapper .brand-footer,
    .login-wrapper .brand-chip { display: none !important; }

    .login-wrapper .brand-panel { padding: 16px 20px !important; }
    .login-wrapper .brand-mark { margin-bottom: 8px !important; min-height: 24px !important; }
    .login-wrapper .brand-mark img { max-height: 26px !important; }
    .login-wrapper .brand-display {
        font-size: clamp(1.15rem, 2.6vw, 1.6rem) !important;
        margin-bottom: 10px !important;
    }
    .login-wrapper .brand-stats { gap: 4px 16px !important; margin-bottom: 0 !important; }
    .login-wrapper .brand-stat-key { font-size: .9rem !important; }
    .login-wrapper .brand-stat-label { font-size: .55rem !important; }

    .login-wrapper .form-panel { padding: 12px 16px !important; }
    .login-wrapper .form-container {
        margin-top: 0 !important;
        padding: 14px 16px !important;
    }
    .login-wrapper .form-header { margin-bottom: 10px !important; }
    .login-wrapper .form-header h1 { font-size: 1.05rem !important; margin-bottom: 0 !important; }
    .login-wrapper .form-header p { display: none !important; }

    .login-wrapper .field { margin-bottom: .5rem !important; }
    .login-wrapper .field input:not([type="checkbox"]):not([type="radio"]) {
        height: 42px !important;
        min-height: 42px !important;
    }
    .login-wrapper .captcha-img { height: 40px !important; }
    .login-wrapper .captcha-refresh { width: 40px !important; height: 40px !important; }
    .login-wrapper .submit-btn { height: 42px !important; min-height: 42px !important; }
}

/* Stacked AND short - a small phone in landscape. The hero becomes a
   single line of brand above the card; anything more and the Sign in
   button leaves the screen again. */
@media (max-width: 767.98px) and (max-height: 500px) {
    .login-wrapper .brand-panel {
        padding: 10px 16px !important;
        border-radius: 0 0 16px 16px !important;
    }
    .login-wrapper .brand-display { display: none !important; }
    .login-wrapper .brand-stats { display: none !important; }
    .login-wrapper .brand-mark { margin-bottom: 0 !important; }
    .login-wrapper .brand-face { display: none !important; }
    .login-wrapper .form-panel { padding: 8px 14px 12px !important; }
}


/* ============================================================
   LOGO BACKDROP CHIP

   Wraps an uploaded logo when Settings asks for a background.
   Padding and a radius rather than a flat rectangle: a logo
   pressed against the edge of its own colour block looks like a
   cropping mistake. inline-flex so the chip is exactly as big as
   the mark inside it, on every surface it appears on.
   ============================================================ */
.brand-logo-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 10px;
    border-radius: 10px;
    line-height: 0;
    /* The chip supplies the ground, so a mark whose own colour is close to
       it still has an edge to sit against. */
    box-shadow: 0 0 0 1px rgba(15, 23, 42, .06);
}

.brand-logo-chip img { display: block; }

/* In the sidebar and top bar the chip sits in a tighter space. */
.top-header .brand-logo-chip,
.sidebar .brand-logo-chip,
.shell-brand .brand-logo-chip {
    padding: 4px 8px;
    border-radius: 8px;
}

/* ============================================================
   LOGIN HERO ON SMALL SCREENS: CENTRED

   Left-aligned type works in the desktop column, where the eye
   already has a left edge to follow. Stacked above a centred card
   it read as misaligned with everything below it, so on small
   screens the hero centres to match the card.
   ============================================================ */
@media (max-width: 767.98px) {
    .login-wrapper .brand-content { text-align: center !important; }

    .login-wrapper .brand-mark { justify-content: center !important; }

    .login-wrapper .brand-display {
        /* Keeps "Attendance, recognised in a second." readable instead of
           breaking into a narrow ragged column: the line breaks in the
           markup are for the desktop layout and are ignored here. */
        font-size: clamp(1.25rem, 5.6vw, 1.6rem) !important;
        line-height: 1.22 !important;
        max-width: 22ch;
        margin-left: auto !important;
        margin-right: auto !important;
    }

    .login-wrapper .brand-display br { display: none; }

    .login-wrapper .brand-stats {
        justify-content: center !important;
        gap: 6px 22px !important;
    }

    .login-wrapper .brand-stat { align-items: center !important; }
}


/* ============================================================
   THE CAPABILITY STRIP ON SMALL SCREENS

   Two moving rows is right on a wide panel. Stacked on a phone
   the hero is a headline and a strip, and a second row is the
   difference between the form sitting on the screen and not -
   so below 768px only the first row runs.
   ============================================================ */
@media (max-width: 767.98px) {
    .login-wrapper .brand-ticker { margin-top: 10px !important; }
    .login-wrapper .brand-ticker-row--right { display: none !important; }
    .login-wrapper .brand-ticker-track span {
        font-size: .66rem;
        padding: 4px 9px;
    }
}

/* Shorter than a typical phone the hero is already down to the headline
   alone; a moving strip there would be the only thing competing with it. */
@media (max-width: 767.98px) and (max-height: 680px) {
    .login-wrapper .brand-ticker { display: none !important; }
}

@media (max-height: 560px) {
    .login-wrapper .brand-ticker { display: none !important; }
}

/* Between the stacked layout and full desktop the panel is narrow, so the
   chips shrink rather than the rows overflowing the text column. */
@media (min-width: 768px) and (max-width: 1100px) {
    .login-wrapper .brand-ticker { max-width: 100%; }
    .login-wrapper .brand-ticker-track span { font-size: .68rem; padding: 4px 9px; }
}

/* ==========================================================================
   44. NO PAGER ON AN EMPTY TABLE
   ==========================================================================
   A table with nothing in it was still offering "Previous 1 Next" under its
   empty-state message - three controls that cannot do anything, sat beneath
   a line explaining there is nothing to show. Version 1 hid them, so the
   version comparison also reported it as a difference on every run, which is
   the kind of permanent red that teaches people to stop reading the report.

   DataTables marks an empty body with `.dataTables_empty`, so the wrapper
   can be asked whether it contains one. The length and info controls stay:
   "Showing 0 to 0 of 0 entries" is a true and useful statement, and the
   page-size selector is how somebody sets up the view before filtering.
   ========================================================================== */

@supports selector(:has(*)) {
    .dataTables_wrapper:has(.dataTables_empty) .dataTables_paginate {
        display: none;
    }
}
