/* Plus Jakarta Sans — used by the merged-in modern UI below. @import MUST stay at the very
   top of this file (before any rule) or the browser ignores it. */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400;1,500&display=swap');

html:not([dir=rtl]) .app-brand-text {
    margin-left: 0.325rem !important;
}

.app-brand-logo.demo svg {
    width: 45px !important;
    height: 25px !important;
}

.app-brand-logo.demo {
    width: 45px !important;
    height: 25px !important;
}

.custom-option-icon .custom-option-body svg {
    all: revert !important;
}

.form-floating.input-group label {
    z-index: 10;
}

.light-style .form-floating > label {
    color: var(--m-primary, #004a80);
    opacity: 0.75;
    transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}

.light-style .select2-container--default .select2-selection--single .select2-selection__rendered {
    color: #6f6b7d;
    padding-left: 0.875rem;
    padding-top: 1.100rem;
    padding-bottom: 0.625rem;
}

.light-style .select2-container--default .select2-selection--single .select2-selection__arrow {
    height: calc(3.5rem + 2px);
    position: absolute;
    width: 2rem;
    right: 1px;
    top: 0;   /* full-height box -> caret sits at the TRUE vertical centre (top:10px pushed it low,
                 so it stuck to the field's bottom border line) */
}

.light-style .select2-results__option[role=option][aria-selected=true] {
    background-color: var(--m-primary, #004a80);
    color: #fff;
}

.light-style .select2-container--default .select2-results__option--highlighted:not([aria-selected=true]) {
    background-color: rgba(0, 74, 128, 0.08) !important;
    color: var(--m-primary, #004a80) !important;
}

.light-style .select2-container--default .select2-selection--single {
    height: calc(3.5rem + 2px);
}

.light-style .input-group .select2-selection.select2-selection--single {
    border-right: none;
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.dark-style .form-floating > label {
    color: var(--sem-primary-text, #6db9ec);   /* brightened brand blue — legible on dark cards */
    opacity: 0.95;
    transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}

.dark-style .select2-container--default .select2-selection--single .select2-selection__rendered {
    color: #d9dee3;
    padding-left: 0.875rem;
    padding-top: 1.100rem;
    padding-bottom: 0.625rem;
}
/* select2 "Select value" placeholder — the default #5e6692 is near-invisible on dark cards */
.dark-style .select2-selection__placeholder {
    color: #9aa6bf !important;
}

.dark-style .select2-container--default .select2-selection--single .select2-selection__arrow {
    height: calc(3.5rem + 2px);
    position: absolute;
    width: 2rem;
    right: 1px;
    top: 0;   /* full-height box -> caret centred vertically (top:10px pushed it low / onto the border) */
}

.dark-style .select2-results__option[role=option][aria-selected=true] {
    background-color: var(--m-primary, #3a8fd0);
    color: #fff;
}

.dark-style .select2-container--default .select2-results__option--highlighted:not([aria-selected=true]) {
    background-color: rgba(77, 163, 221, 0.12) !important;
    color: var(--sem-primary-text, #6db9ec) !important;
}

.dark-style .select2-container--default .select2-selection--single {
    height: calc(3.5rem + 2px);
}

.dark-style .input-group .select2-selection.select2-selection--single {
    border-right: none;
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

/* MULTI select2 inside a .form-floating (report-builder filters, mgmt_accounts Access Countries —
   2026-07-06 floating-label consistency pass). Selects always "have a value" so the label floats
   permanently; give the multi box the standard tall field height + top padding so the tag chips sit
   BELOW the floated label instead of under it. */
.form-floating .select2-container--default .select2-selection--multiple {
    min-height: calc(3.5rem + 2px);
    padding-top: 1.35rem;
}
.form-floating .select2-container--default .select2-selection--multiple .select2-selection__rendered {
    padding-top: 0.1rem;
}
/* Report-builder filters: the placeholder carries a usage HINT ("Contains.." / "Any"), distinct from
   the floating label. The app's floating labels are ALWAYS floated (the transform rules above), so the
   hint can show permanently below the label — same look as the select pickers' "Select value" ghost
   text. The #id scope beats the app-wide transparent-placeholder rule. */
#reportFilters .form-floating > .form-control::placeholder {
    color: var(--m-text-muted, #6a6d70);
    opacity: 0.75;
}
/* …and keep the input's floated-label padding even while empty/unfocused, so the always-visible hint
   sits BELOW the label instead of vertically centred over it (mirrors the typeahead always-float fix). */
#reportFilters .form-floating > .form-control {
    padding-top: 1.625rem;
    padding-bottom: 0.625rem;
}

.form-control.typeahead.tt-input, .form-control.typeahead.tt-hint {
    height: calc(3.5rem + 2px);
    line-height: 1.25;
    padding: 1rem 0.875rem;
}
  
.form-control.typeahead.tt-input::placeholder {
    color: transparent;
}

.form-control.typeahead.tt-input:focus, .form-control.typeahead.tt-input:not(:placeholder-shown),
.form-control.typeahead.tt-hint:focus, .form-control.typeahead.tt-hint:not(:placeholder-shown) {
    padding-top: 1.625rem;
    padding-bottom: 0.625rem;
}

.form-control.typeahead.tt-input:focus ~ label,
.form-control.typeahead.tt-input:not(:placeholder-shown) ~ label {
    opacity: 0.75;
    transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}

.light-style .twitter-typeahead .tt-suggestion:hover, .light-style .twitter-typeahead .tt-suggestion:focus {
    text-decoration: none;
    color: var(--m-primary, #004a80);
    background-color: rgba(0, 74, 128, 0.08);
}

.dark-style .twitter-typeahead .tt-suggestion:hover, .light-style .twitter-typeahead .tt-suggestion:focus {
    text-decoration: none;
    background-color: rgba(77, 163, 221, 0.12) !important;
    color: #6db9ec !important;
}

/* Edit Stock dialog: wider than the default modal-xl so the two tall Packed With / Remarks textareas
   (which hold large amounts of data) have room side by side. Only widens on >=lg so small screens keep
   the Bootstrap default. #editModal .modal-dialog beats .modal-xl on specificity (id + class). */
@media (min-width: 992px) {
  #editModal .modal-dialog { max-width: min(1380px, 94vw); }
}

.sticky-actions {
    /* MUST stay sticky: the glass `.ui-glass .card{position:relative}` rule (added for the card
       light-edge) is more specific and would otherwise demote this to relative — which both breaks
       the pin-on-scroll AND lets the JS-set `top` (navbar.bottom + 1.5rem) shove the card down,
       misaligning it from the form. `!important` keeps it sticky; at rest it sits at the column top
       (aligned with the form), and on scroll it pins just below the fixed navbar. */
    position: sticky !important;
    top: 10px; /* fallback; loan_create.js / loan_edit.js set the real offset = navbar.bottom + 1.5rem */
    align-self: flex-start;  /* don't stretch to the row height (the col is a flex item) */
}

/* ── Primary-save block (loan_edit rail) ───────────────────────────────────────────────────────
   The rail's Save button used to be the app's only `btn-dark`, which the dark theme renders as a
   pale neutral — it read as disabled and was drowned by the five saturated Loan Progress buttons
   below it ("save loan button not obvious"). Save is now `btn-primary btn-lg` (the app-wide
   primary-save convention) inside this tinted, captioned panel: the panel + the size are what keep
   it distinct from "Mark Collected", which is also btn-primary but default-size and in its own
   section. Token-driven only — never hardcode hexes here (see docs/beta-ui-theme.md). */
.save-block {
    background: rgba(var(--m-primary-rgb), 0.06);
    border: 1px solid var(--m-border-strong);
    border-radius: var(--m-radius-sm);
    padding: 0.75rem;
}

/* Unsaved-changes state — set by the dirty tracker in loan_edit.js. */
.save-block.is-dirty {
    background: rgba(var(--m-primary-rgb), 0.12);
    border-color: rgba(var(--m-primary-rgb), 0.55);
}
.save-block.is-dirty #btnSaveLoan {
    box-shadow: 0 0 0 3px var(--m-ring), 0 4px 14px rgba(var(--m-primary-rgb), 0.35);
}

/* Short attention pulse when the form FIRST becomes dirty (3 iterations, then it rests on the
   steady ring above). Motion is opt-in so reduced-motion users just get the ring. */
@media (prefers-reduced-motion: no-preference) {
    .save-block.is-dirty #btnSaveLoan {
        animation: saveBlockPulse 1.1s ease-out 3;
    }
}
@keyframes saveBlockPulse {
    0%   { box-shadow: 0 0 0 0 rgba(var(--m-primary-rgb), 0.55), 0 4px 14px rgba(var(--m-primary-rgb), 0.35); }
    70%  { box-shadow: 0 0 0 10px rgba(var(--m-primary-rgb), 0),  0 4px 14px rgba(var(--m-primary-rgb), 0.35); }
    100% { box-shadow: 0 0 0 3px var(--m-ring),                   0 4px 14px rgba(var(--m-primary-rgb), 0.35); }
}

/* ⚠ The `.ui-glass .card p` selector in the glass skin (specificity 0,2,1) beats a bare
   `.save-dirty-note` (0,1,0), so the amber was silently losing to the card's default text colour —
   caught on a rendered page, invisible in the CSS. The second selector (0,3,1) is what actually
   carries the colour under the glass skin; the first keeps it working if glass is ever off.
   Any new coloured `<p>` inside a card needs the same treatment. */
.save-dirty-note,
.ui-glass .card p.save-dirty-note {
    margin: 0.5rem 0 0;
    font-size: 0.8125rem;
    font-weight: 600;
    line-height: 1.3;
    text-align: center;
    color: var(--sem-warning-text, var(--bs-warning));
}
/* Leading dot so the cue isn't colour-only. */
.save-dirty-note::before {
    content: "\2022\00a0";
}

/* Terminal / read-only loans: processLoanEdit() disables Save, so drop the emphasis entirely. */
.save-block:has(#btnSaveLoan:disabled) {
    background: transparent;
    border-color: var(--m-border);
}
/* The GLOW on a disabled primary is now killed app-wide (see "DISABLED primary buttons must not glow"
   in the glass-skin section). What stays here is the bit that is specific to this block: cancelling
   the `.is-dirty` attention PULSE, which no global rule knows about. `!important` for the same reason
   the global one needs it — the glass `.btn-primary` rules are `!important`. */
.save-block:has(#btnSaveLoan:disabled) #btnSaveLoan {
    box-shadow: none !important;
    animation: none !important;
}

.swal2-modal.swal2-popup .swal2-title {
    margin: 0 0 0 0 !important;
}

.tooltip-inner {
    white-space: pre-wrap;
}


.light-style .bootstrap-select .dropdown-menu a:not([href]):not(.active):not(:active):not(.selected):hover {
    color: var(--m-primary, #004a80) !important;
}
.dark-style .bootstrap-select .dropdown-menu a:not([href]):not(.active):not(:active):not(.selected):hover {
    color: var(--sem-primary-text, #6db9ec) !important;
}

/* Stat-circle cards (Stock / Loan / Training summaries) — enlarge the circle and
   shrink the count so 4-digit numbers (e.g. 2049) fit inside. Applies app-wide. */
.avatar.avatar-xl {
    width: 5rem;
    height: 5rem;
}
.avatar.avatar-xl .avatar-initial {
    font-size: 1.15rem;
    font-weight: 600;
}

/* ---- Dark-mode contrast fixes for custom-styled pages ----
   Some pages (e.g. the loan "invoice" create form) and inputs render with
   muted/light-assuming colours. Lift their text to a readable tone in dark mode. */
.dark-style .invoice-preview-card,
.dark-style .invoice-preview-card p,
.dark-style .invoice-preview-card h1,
.dark-style .invoice-preview-card h2,
.dark-style .invoice-preview-card h3,
.dark-style .invoice-preview-card h4,
.dark-style .invoice-preview-card h5,
.dark-style .invoice-preview-card h6,
.dark-style .invoice-preview-card td,
.dark-style .invoice-preview-card th {
    color: #d9dee3;
}
/* Crestron logo on the loan create/edit "invoice" card: its SVG paths are navy (#2e5786),
   nearly invisible on the dark card — recolour them white in dark mode (matches the nav-bar
   logo). Targets the swirl + wordmark inside the invoice card's .app-brand. */
.dark-style .invoice-preview-card .app-brand svg path[fill] {
    fill: #ffffff !important;
}
/* Readable input / date-picker text */
.dark-style input.form-control,
.dark-style textarea.form-control,
.dark-style .flatpickr-input,
.dark-style .dataTable td,
.dark-style .dataTable th {
    color: #d9dee3;
}
/* Switch/toggle that was near-white on the dark card */
.dark-style .form-check-input {
    background-color: var(--m-surface-2, #262b31);
    border-color: #4a525b;
}
.dark-style .form-check-input:checked {
    background-color: var(--m-primary, #3a8fd0);
    border-color: var(--m-primary, #3a8fd0);
}

/* FullCalendar event text — the theme renders event links in blue, which is
   unreadable on the coloured fills. Force white on the month/week event blocks
   (these always have a coloured background). The list view is left alone so its
   row text stays theme-correct in light mode. */
.fc-daygrid-event .fc-event-title,
.fc-daygrid-event .fc-event-time,
.fc-daygrid-block-event .fc-event-title,
.fc-timegrid-event .fc-event-title,
.fc-timegrid-event .fc-event-time,
.fc-daygrid-event .fc-event-title sup,
.fc-timegrid-event .fc-event-title sup {
    color: #fff !important;
}

/* Month-view event spacing — the vendor `fullcalendar.css` adds `margin-top:0.3rem` between stacked
   day-grid events (`.fc-daygrid-event-harness + .fc-daygrid-event-harness`). That margin only applies to
   the IN-FLOW single-day bars; FullCalendar positions multi-day bars ABSOLUTELY with a top computed as
   level×event-height that IGNORES the margin, so a multi-day bar (e.g. a class spanning several days) sits
   flush against the event above it while the single-day bars have a 0.3rem gap → uneven spacing. Zero the
   margin so every bar stacks uniformly at level×height (the same basis the absolute bars already use).
   Paired with `eventOrder:'duration,start,title'` in cti_calendar.js (single-day first, multi-day last). */
.fc .fc-daygrid-event-harness + .fc-daygrid-event-harness {
    margin-top: 0 !important;
}

/* Dark-mode FullCalendar grid lines — the day-cell borders wash out against the dark/glass
   backdrop, so the grid is invisible. Set the FC border variable AND force the colour directly
   on the cells/scrollgrid (the glass skin's translucent cells were blending the default away).
   Light mode is left to the theme default. */
.dark-style .fc {
    --fc-border-color: rgba(255, 255, 255, 0.18);
    --fc-today-bg-color: rgba(255, 255, 255, 0.05);
}
.dark-style .fc-theme-standard td,
.dark-style .fc-theme-standard th,
.dark-style .fc-theme-standard .fc-scrollgrid,
.dark-style .fc-scrollgrid-section > * {
    border-color: rgba(255, 255, 255, 0.18) !important;
}

/* Dark-mode FullCalendar toolbar buttons (today / prev-next / Month-Week-List). The rebranded Sneat
   dark theme (theme-default-dark.css) tints the view buttons with a pale primary fill via
   `.fc .fc-button-primary:not(.fc-prev-button):not(.fc-next-button){ background:rgba(primary,.16)
   !important }` (specificity 0,4,0) + mid-blue text — readable on a white light canvas but washed out
   on the dark/glass backdrop; the "today" button is also disabled (opacity .65) whenever the current
   view already contains today, making it fainter still. Beat that rule with the always-present
   `.ui-modern.ui-glass.dark-style` triple-class (0,5,0) so EVERY toolbar button gets a solid surface +
   visible border + legible text; the ACTIVE view button gets a solid brand fill and the disabled
   "today" a muted-but-legible look. Light mode keeps the theme default. */
.ui-modern.ui-glass.dark-style .fc .fc-button-primary {
    background-color: var(--m-surface-2, #262b31) !important;
    border: 1px solid var(--m-border-strong, #414851) !important;
    color: var(--m-text, #e2e5e8) !important;
    opacity: 1 !important;
    box-shadow: none !important;
}
.ui-modern.ui-glass.dark-style .fc .fc-button-primary:hover:not(:disabled) {
    background-color: var(--m-surface, #1f2328) !important;
    border-color: var(--m-primary, #3a8fd0) !important;
    color: var(--m-text, #e2e5e8) !important;
}
.ui-modern.ui-glass.dark-style .fc .fc-button-primary:not(:disabled).fc-button-active {
    background-color: var(--m-primary, #3a8fd0) !important;
    border-color: var(--m-primary, #3a8fd0) !important;
    color: #fff !important;
}
/* "today" when disabled (already viewing today): keep it clearly legible, just muted. */
.ui-modern.ui-glass.dark-style .fc .fc-button-primary:disabled {
    background-color: var(--m-surface-2, #262b31) !important;
    border-color: var(--m-border, #32383f) !important;
    color: var(--m-text-muted, #9aa1a8) !important;
    opacity: 1 !important;
}

/* FullCalendar toolbar button GEOMETRY + label (both themes). The vendor gives the ‹ › icon buttons a
   shorter height (38px) and the prev button an asymmetric 0 left-padding, so they don't line up with the
   today / view buttons (40px); it also `text-transform:capitalize`s the view buttons but leaves "today"
   lowercase. Normalize every toolbar button to one height + centered content + capitalize (so "today"
   reads "Today", matching Month/Week/List). */
.fc .fc-toolbar .fc-button {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-height: 40px;
    padding-top: 0.45rem !important;
    padding-bottom: 0.45rem !important;
    text-transform: capitalize !important;
}
.fc .fc-toolbar .fc-prev-button,
.fc .fc-toolbar .fc-next-button {
    padding-left: 0.7rem !important;
    padding-right: 0.7rem !important;
}
/* Equal-width view buttons + legend badges — the labels differ in length (Month/Week/List,
   Zoom/Classroom/Overseas) but should read as one uniform set of boxes. Fit the widest with a
   min-width and centre the text (the toolbar buttons already centre via the inline-flex rule above;
   the `.cal-legend` container is on the calendar page's legend row). */
.fc .fc-toolbar .fc-dayGridMonth-button,
.fc .fc-toolbar .fc-timeGridWeek-button,
.fc .fc-toolbar .fc-listMonth-button {
    min-width: 5.5rem;
}
.cal-legend .badge {
    min-width: 6rem;
    display: inline-flex;      /* center the label BOTH axes — the badge has 0 vertical padding, so
                                  inline-block left the text top-aligned ("floating") in the 24px box */
    align-items: center;
    justify-content: center;
}

/* ---- Global navbar search (cassets/js/common.js) ---- */
.gs-wrap { position: relative; min-width: 260px; }
.gs-input {
    border: 0;
    box-shadow: none;
    background: transparent;
    padding-left: 0;
    height: 38px;
}
.gs-input:focus { border: 0; box-shadow: none; background: transparent; }
.global-search-results {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    min-width: 340px;
    background: #fff;
    border: 1px solid #d9dee3;
    border-top: none;
    border-radius: 0 0 0.375rem 0.375rem;
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.18);
    max-height: 60vh;
    overflow-y: auto;
    overflow-x: hidden;
    z-index: 1090;
    padding-bottom: 0.25rem;
}
.dark-style .global-search-results { background: var(--m-surface, #1f2328); border-color: var(--m-border, #32383f); }
.gsr-group-title {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #a1acb8;
    padding: 0.55rem 0.95rem 0.2rem;
    font-weight: 600;
}
.gsr-item {
    display: flex;
    align-items: center;
    padding: 0.5rem 0.95rem;
    color: #566a7f;
    text-decoration: none;
    line-height: 1.2;
    overflow: hidden;               /* a single row never causes horizontal scroll */
}
.gsr-item > i { flex: 0 0 auto; }   /* icon keeps its size */
.dark-style .gsr-item { color: #d9dee3; }
.gsr-item:hover { background: #f5f5f9; color: #566a7f; }
.dark-style .gsr-item:hover { background: #2c3238; color: #fff; }
.gsr-label {
    font-weight: 500;
    flex: 1 1 auto;
    min-width: 0;                   /* allow the label to shrink + ellipsize */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.gsr-sub {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 50%;                 /* never let the sub crowd out the label */
    margin-left: auto;
    padding-left: 1rem;
    font-size: 0.8rem;
    color: #a1acb8;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: right;
}
.gsr-empty { padding: 0.75rem 0.95rem; color: #a1acb8; }

/* Floating hover-preview card for a search result (appended to <body>, position:fixed). */
.gsr-preview {
    position: fixed;
    z-index: 1095;                  /* above .global-search-results (1090) */
    width: 260px;
    max-width: 80vw;
    background: #fff;
    border: 1px solid #d9dee3;
    border-radius: 0.375rem;
    box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.22);
    padding: 0.55rem 0.7rem;
    font-size: 0.82rem;
    color: #566a7f;
    pointer-events: none;           /* never steal hover/clicks from the result list */
}
.gsr-prev-head {
    display: flex;
    align-items: center;
    font-weight: 600;
    color: #384551;
    padding-bottom: 0.4rem;
    margin-bottom: 0.4rem;
    border-bottom: 1px solid #eceef1;
    white-space: nowrap;
    overflow: hidden;
}
.gsr-prev-head > span { overflow: hidden; text-overflow: ellipsis; }
.gsr-prev-row { display: flex; gap: 0.75rem; padding: 0.12rem 0; }
.gsr-prev-k { flex: 0 0 38%; color: #a1acb8; }
.gsr-prev-v {
    flex: 1 1 auto;
    min-width: 0;
    text-align: right;
    word-break: break-word;
    font-weight: 500;
}
/* Muted second line under a value — mirrors the datatable's stacked cell (e.g. model / part_no). */
.gsr-prev-sub { display: block; font-weight: 400; font-size: 0.76rem; color: #a1acb8; line-height: 1.25; }
.dark-style .gsr-prev-sub { color: #9aa1a8; }
.ui-modern .gsr-prev-sub { color: var(--m-text-muted, #a1acb8) !important; }
.dark-style .gsr-preview { background: var(--m-surface, #1f2328); border-color: var(--m-border, #32383f); color: #d9dee3; }
.dark-style .gsr-prev-head { color: #fff; border-bottom-color: var(--m-border, #32383f); }
.dark-style .gsr-prev-k { color: #9aa1a8; }
.ui-modern .gsr-preview {
    background: var(--m-surface) !important;
    border-color: var(--m-border) !important;
    color: var(--m-text) !important;
    border-radius: var(--m-radius, 0.5rem) !important;
}
.ui-modern .gsr-prev-head { color: var(--m-text) !important; border-bottom-color: var(--m-border) !important; }
.ui-modern .gsr-prev-k { color: var(--m-text-muted, #a1acb8) !important; }

/* ============================================================================
   Sidebar collapse/pin toggle + brand alignment + collapsed-rail centering.
   Theme-level (not .ui-modern-scoped) so it holds regardless; the modern look's
   navy-sidebar toggle hover colour is in the MODERN UI block further below.
   (2026-06-22)
   ============================================================================ */

/* 1) Pin/collapse control — replace the theme's stray radio-dot with a clear
      directional chevron + a real button affordance (hover background). */
.layout-menu-toggle .menu-toggle-icon::before { content: "\ea60"; }                       /* chevron-left  = collapse */
.layout-menu-collapsed .layout-menu-toggle .menu-toggle-icon::before { content: "\ea61"; } /* chevron-right = expand   */
/* Scope the 30x30 rounded box ONLY to the SIDEBAR toggle (#layout-menu). The navbar
   hamburger in nav_bar.php is ALSO a `.layout-menu-toggle` (a <div> wrapping an
   <a class="nav-link"> + ti-menu-2 icon); the forced 30x30 box clipped that larger
   link and left the hover background off-centre from the icon. */
#layout-menu .layout-menu-toggle {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    transition: background-color .15s ease, color .15s ease;
}
#layout-menu .layout-menu-toggle .menu-toggle-icon { font-size: 1.125rem !important; }
.light-style #layout-menu .layout-menu-toggle:hover { background-color: rgba(67, 89, 113, 0.08); }
.dark-style #layout-menu .layout-menu-toggle:hover  { background-color: rgba(255, 255, 255, 0.08); }

/* Navbar hamburger (mobile/tablet, .navbar .layout-menu-toggle) — give it its OWN
   centred hover affordance on the link, so the rounded box is aligned around the icon. */
.navbar .layout-menu-toggle { display: flex; align-items: center; }
.navbar .layout-menu-toggle .nav-link {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0 !important;
    border-radius: 8px;
    line-height: 1;
    transition: background-color .15s ease, color .15s ease;
}
.light-style .navbar .layout-menu-toggle .nav-link:hover { background-color: rgba(67, 89, 113, 0.08); }
.dark-style .navbar .layout-menu-toggle .nav-link:hover  { background-color: rgba(255, 255, 255, 0.08); }

/* 2) Line the brand (logo + pin) up with the detached navbar's search bar. Move the
      WHOLE brand block down ~15px (margin-top) rather than just the logo, so the menu
      shifts with it and the spacing below the logo stays even. */
#layout-menu .app-brand { margin-top: 15px; }

/* 2b) Menu items must FILL the rail width, not the vendor's fixed `width:16.25rem` (=260px, the full
      rail width) — combined with the modern skin's 0.65rem side margins that pushed each item ~10px
      PAST the right edge (clipped by the rail's overflow:hidden), so the active pill's right gap
      collapsed to ~4px while its left gap stayed ~24px (the "pill extends to the right" bug). The rail's
      .menu-inner is a flex column that doesn't stretch its children, so force `align-self:stretch` +
      `width:auto`: the item then fills the rail minus its margins and the pill is symmetric both sides.
      Skin-agnostic (the fixed width is vendor). Collapsed rail sets its own item/link sizing, unaffected. */
#layout-menu .menu-inner > .menu-item { width: auto !important; align-self: stretch !important; }

/* 3) Collapsed (icon-only) rail — center each item as a fixed-size pill so the
      active highlight and icons sit dead-centre. The theme otherwise anchors the
      pill to the right (tiny right gap, heavy left gap). Turned off while hovering,
      when the collapsed menu temporarily expands to show its labels. */
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-inner > .menu-item {
    margin-left: 0 !important;
    margin-right: 0 !important;
}
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-inner > .menu-item > .menu-link {
    width: 2.625rem !important;
    height: 2.625rem !important;
    margin: 0.15rem auto !important;
    padding: 0 !important;
    justify-content: center !important;
}
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-inner > .menu-item > .menu-link > div { display: none !important; }
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-inner .menu-icon { margin-right: 0 !important; }
/* ...and center the brand logo (hide the wordmark) so the swirl sits dead-centre too. */
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .app-brand {
    justify-content: center !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    /* The theme gives collapsed .app-brand a FIXED `width:5.25rem` (core.css) AND an asymmetric
       1rem-left margin, so the box (the modern divider is a border-bottom on it) overflowed the
       narrow rail to the RIGHT. Drop the fixed width to auto so the brand fills the rail, then
       equal margins centre the divider line. */
    width: auto !important;
    margin-left: 0.6rem !important;
    margin-right: 0.6rem !important;
}
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .app-brand-text { display: none !important; }
/* Hide the collapse/expand toggle in the collapsed rail. The theme only sets it to opacity:0,
   but our modern skin gives it a fixed 30px width, so it still takes flex space — and with
   justify-content:center on .app-brand that pushes the centred swirl logo off to the LEFT.
   display:none removes it from the layout so the logo sits dead-centre. Scoped to
   :not(.layout-menu-hover), so the toggle returns when the menu is hovered/expanded. */
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .app-brand .layout-menu-toggle { display: none !important; }

/* ============================================================================
   Sidebar CHROME consolidated into the rail (2026-07-10).
   The old top navbar's utilities now live inside #layout-menu: the global
   search pinned at the top, and a user card pinned at the bottom (avatar +
   name/role, notifications bell, and an up-popping profile menu with the
   Dark Mode switch + Log Out). Colours use the existing --m-sidebar-*/--g-sidebar-*
   tokens, which already flip per light/dark, so most rules need no mode variant.
   ============================================================================ */

/* Make the rail a column so the search sits at top, the scrollable menu grows,
   and the user card pins to the bottom. (.menu-inner is already flex:1 in Sneat.) */
#layout-menu.layout-menu { display: flex; flex-direction: column; }
#layout-menu .menu-inner { flex: 1 1 auto; }

/* ---- Search at the top of the rail ---- */
#layout-menu .menu-search { position: relative; z-index: 6; flex: 0 0 auto; }
#layout-menu .menu-search .gs-wrap { min-width: 0; width: 100%; }
#layout-menu .menu-search .gs-input {
  height: 40px;
  width: 100%;
  padding: 0 0.85rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.10);
  color: var(--m-sidebar-text, #dde6ee);
}
.ui-glass #layout-menu .menu-search .gs-input { background: rgba(255, 255, 255, 0.12); color: var(--g-sidebar-text, #e9eef3); }
/* Placeholder must stay legible on the translucent pill — mut(#9fb1c1) was too faint,
   so use the brighter sidebar-text tone dimmed slightly rather than the muted grey. */
#layout-menu .menu-search .gs-input::placeholder { color: var(--m-sidebar-text, #dde6ee); opacity: 0.85; }
.ui-glass #layout-menu .menu-search .gs-input::placeholder { color: var(--g-sidebar-text, #e9eef3); opacity: 0.85; }
#layout-menu .menu-search .gs-input:focus {
  background: rgba(255, 255, 255, 0.16);
  color: var(--m-sidebar-text, #dde6ee);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.12);
}
/* The results dropdown is injected inside .gs-wrap; keep it within the rail width. */
#layout-menu .menu-search .global-search-results { min-width: 0; left: 0; right: 0; }

/* ---- User card pinned at the bottom ---- */
#layout-menu .menu-user {
  flex: 0 0 auto;
  margin-top: auto;                 /* safety: pin to bottom even if .menu-inner isn't flex:1 */
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.6rem 0.75rem;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
#layout-menu .menu-user-card { flex: 1 1 auto; min-width: 0; order: 1; }   /* card left (avatar+name) */
#layout-menu .menu-user-toggle {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.5rem;
  border-radius: var(--m-radius-sm, 0.5rem);
  text-decoration: none;
  transition: background-color .18s ease;
}
#layout-menu .menu-user-toggle:hover { background: var(--m-sidebar-hover, rgba(255,255,255,0.06)); }
#layout-menu .menu-user-meta { min-width: 0; line-height: 1.2; }
#layout-menu .menu-user-name { color: var(--m-sidebar-text, #dde6ee); font-size: 0.9rem; }
.ui-glass #layout-menu .menu-user-name { color: var(--g-sidebar-text, #e9eef3); }
#layout-menu .menu-user-sub { color: var(--m-sidebar-mut, #9fb1c1); font-size: 0.75rem; }
.ui-glass #layout-menu .menu-user-sub { color: var(--g-sidebar-mut, #b3c1cd); }
#layout-menu .menu-user-caret { color: var(--m-sidebar-mut, #9fb1c1); font-size: 1rem; flex: 0 0 auto; transition: transform .18s ease; }
#layout-menu .menu-user-card.show .menu-user-caret { transform: rotate(180deg); }

/* Notifications bell in the card */
#layout-menu .menu-user-notif { flex: 0 0 auto; order: 2; }   /* bell to the right of the card */
#layout-menu .menu-user-notif-toggle {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  color: var(--m-sidebar-mut, #9fb1c1);
  text-decoration: none;
  transition: background-color .18s ease, color .18s ease;
}
.ui-glass #layout-menu .menu-user-notif-toggle { color: var(--g-sidebar-mut, #b3c1cd); }
#layout-menu .menu-user-notif-toggle:hover { background: var(--m-sidebar-hover, rgba(255,255,255,0.06)); color: #fff; }
#layout-menu .menu-user-notif-toggle .ti-bell { font-size: 1.25rem; }
#layout-menu .menu-user-notif-toggle .badge-notifications {
  position: absolute;
  top: 4px;
  right: 4px;
  transform: translate(35%, -35%);
  /* Force a CIRCLE (the default .badge padding made a tall 17x24 oval for a 2-digit count). */
  min-width: 18px;
  height: 18px;
  padding: 0 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  font-size: 0.62rem;
  line-height: 1;
}

/* The up-popping menus (profile + notifications) — .dropdown-menu is already themed by the
   .ui-modern/.ui-glass .dropdown-menu rules. ⚠ The rail is .menu-vertical{overflow:hidden} (vendor
   core.css) and can't be unset (the collapse width-transition needs it to clip labels), so BOTH menus
   must stay NARROWER than the 16.25rem rail or their far edge is clipped. Popper auto-flips the bell
   menu upward (it sits at the very bottom) and the profile menu is an explicit .dropup. */
#layout-menu .menu-user-card .dropdown-menu { min-width: 210px; max-width: 15rem; }
#layout-menu .menu-user-notif .dropdown-menu { min-width: 256px; max-width: 16rem; width: 16rem; }
/* The notifications list can be long (one row per overdue loan); cap it so the dropup stays on-screen
   and scrolls internally (the theme's perfect-scrollbar doesn't init on this relocated element). */
#layout-menu .menu-user-notif .dropdown-notifications-list { max-height: 44vh; overflow-y: auto; }
#layout-menu .menu-user-notif .dropdown-notifications-list .list-group-item { white-space: normal; }
/* Dark Mode switch row: the whole row is the .style-switcher-toggle trigger, so the switch is
   visual-only (the row's click flips the theme via assets/js/main.js). */
#layout-menu .menu-user .style-switcher-toggle .form-check-input { pointer-events: none; cursor: pointer; }
#layout-menu .menu-user .style-switcher-toggle { cursor: pointer; }

/* Collapsed icon-rail: hide the search + the card's text/caret/bell; show only the avatar. */
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-search,
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-user-meta,
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-user-caret,
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-user-notif { display: none !important; }
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-user { justify-content: center; padding-inline: 0; }
.layout-menu-collapsed:not(.layout-menu-hover) #layout-menu .menu-user-toggle { justify-content: center; gap: 0; }

/* ---- Slim MOBILE-ONLY top bar (nav_bar.php is d-xl-none) ---- */
.layout-navbar .mobile-brand { text-decoration: none; }
.layout-navbar .mobile-brand .app-brand-logo svg path[fill] { fill: var(--m-primary, #004a80); }
.dark-style .layout-navbar .mobile-brand .app-brand-logo svg path[fill] { fill: #fff; }

/* ============================================================================
   Responsive / mobile fixes (theme-level, not skin-scoped). 2026-06-22.
   ============================================================================ */

/* (a) Off-canvas menu backdrop: the theme can leave `.layout-overlay` stuck as a
       ~30px, 0.5-opacity grey strip down the left edge on narrow screens. Force it
       fully hidden when the menu is closed, and full-screen when it's open. */
html:not(.layout-menu-expanded) .layout-overlay {
    opacity: 0 !important;
    width: 0 !important;
    pointer-events: none !important;
}
html.layout-menu-expanded .layout-overlay {
    display: block !important;
    width: 100% !important;
    height: 100% !important;
    opacity: 1 !important;
}

/* Dashboard "My Loans" cards are clickable links to the pre-filtered loan list — give them a
   pointer + subtle hover lift so it's obvious they're tappable (same intent as the statCard links). */
.dash-card-link { display: block; cursor: pointer; transition: transform .15s ease, box-shadow .15s ease; }
.dash-card-link:hover { transform: translateY(-2px); box-shadow: 0 0.5rem 1.25rem rgba(0,0,0,0.12); }

/* (a2) Off-screen `.offcanvas-end` panels (e.g. the mgmt_accounts add/edit forms) sit
        translated past the right edge and cause a horizontal scrollbar on phones. Clip the
        page horizontally — scoped to mobile so desktop `position: sticky` is unaffected.
        (`overflow-x: clip` and html-only don't clip the fixed panel; html+body hidden does.) */
@media (max-width: 767.98px) {
    html, body { overflow-x: hidden; }
}

/* (b) Compact KPI/stat tiles on phones: the full-width "big circle centred over a
       label" cards are far too tall stacked on mobile. Reflow them to a short
       horizontal row (circle left, label right). Targets any card-body holding an
       avatar-xl stat circle (dashboard, stock, loan, cti summary tiles). */
@media (max-width: 767.98px) {
    .card-body:has(.avatar-xl) {
        display: flex;
        align-items: center;
        gap: 1rem;
        padding: 1rem 1.25rem;
    }
    .card-body:has(.avatar-xl) > .d-flex {
        margin-bottom: 0 !important;
        justify-content: flex-start !important;
        flex: 0 0 auto;
    }
    .card-body:has(.avatar-xl) > .text-center { text-align: left !important; }
    .card-body:has(.avatar-xl) h5 { margin-bottom: 0; }
    .avatar.avatar-xl { width: 3.25rem !important; height: 3.25rem !important; }
    .avatar.avatar-xl .avatar-initial { font-size: 0.95rem !important; }
}

/* (c) DataTables top controls (length / search / Export / Add) misalign below the theme's xl
       breakpoint: they live in a nested grid (`.col-md-2` length + `.col-md-10 > .dt-action-buttons`,
       the latter `flex-direction:column`), so items in different containers never line up. FLATTEN
       the whole thing into ONE flex row with `display:contents` — length/search/buttons become
       siblings of the row and align uniformly; the search grows, and it all wraps into clean,
       aligned rows when it can't fit on one line. Covers everything below xl (the theme only goes
       inline at >=1200). */
@media (max-width: 1199.98px) {
    .dataTables_wrapper .row:has(.dataTables_length, .dt-action-buttons) {
        display: flex !important;
        flex-wrap: wrap !important;
        align-items: center !important;
        gap: .5rem !important;
        margin: 0 !important;
        padding: .6rem .25rem .8rem !important;   /* vertical breathing room */
    }
    /* dissolve the grid/wrapper layers so their children join the row's flex context */
    .dataTables_wrapper .row:has(.dataTables_length, .dt-action-buttons) > [class*="col-"],
    .dataTables_wrapper .col-md-2 .me-3,
    .dt-action-buttons { display: contents !important; }
    .dataTables_length { display: flex !important; align-items: center; margin: 0 !important; }
    .dataTables_length label { margin: 0; display: flex; align-items: center; }
    .dataTables_length select { width: auto; min-width: 52px; }
    .dataTables_filter { flex: 1 1 140px !important; min-width: 130px; margin: 0 !important; }
    .dataTables_filter label { width: 100%; margin: 0; }
    .dataTables_filter input { width: 100% !important; margin: 0 !important; }
    /* buttons must NOT grow (display:contents lets the theme's flex:1 1 auto make "Add" stretch
       to fill the row) — keep them compact so they sit inline / wrap as tidy chips */
    .dataTables_wrapper .dt-buttons,
    .dataTables_wrapper .add-new,
    .dataTables_wrapper .btn { flex: 0 0 auto !important; white-space: nowrap; }
}

/* (c3) DataTables Buttons "Export" popover: vendor buttons.bootstrap5.css paints the WRAPPER
       (div.dt-button-collection) as a hard-coded white panel (bg + border + 0.5rem padding) and
       flattens the inner .dropdown-menu to transparent. Inside the card the white frame was mostly
       clipped away; since the popover is portaled to <body> (buttons-popover.dt handler in
       common.js) it shows fully — a white halo around the menu in dark mode. Make the wrapper
       invisible and give the panel look back to the inner .dropdown-menu via the theme's own
       dropdown tokens (--bs-dropdown-*, defined on .dropdown-menu by core/core-dark — so light and
       dark both follow). !important: the per-page *_css.inc vendor links load AFTER common.css, so
       plain equal-specificity overrides would lose on cascade order. */
div.dt-button-collection {
    background-color: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    padding: 0 !important;
}
div.dt-button-collection div.dropdown-menu {
    background-color: var(--bs-dropdown-bg) !important;
    border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color) !important;
    border-radius: var(--bs-dropdown-border-radius) !important;
    box-shadow: var(--bs-dropdown-box-shadow) !important;
    padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x) !important;
}

/* (c2) Navbar global-search: its `.gs-wrap{min-width:260px}` is too wide on phones — it pushes the
        right-side icons (incl. the profile avatar) off the edge. Let it shrink on small screens. */
@media (max-width: 575.98px) {
    .gs-wrap { min-width: 0 !important; }
}

/* (d) Admin merge tools (mgmt_companies/contacts/models) — the two card-header action
       buttons ("Merge Selected into Main" btn-danger + "Suggest Duplicates"
       btn-outline-info) are inline-flex of different widths, so they wrap awkwardly on
       phones. Match them: both full-width, stacked, on mobile. The :has() combo is unique
       to those merge headers, so it won't touch ordinary card-header buttons. */
@media (max-width: 767.98px) {
    .card-header:has(.btn-danger):has(.btn-outline-info) > .btn {
        display: flex !important;
        width: 100% !important;
        margin-left: 0 !important;
        justify-content: center;
    }
    .card-header:has(.btn-danger):has(.btn-outline-info) > .btn + .btn { margin-top: .5rem !important; }
}

/* ============================================================================
   MODERN UI (merged in 2026-06-22, now the DEFAULT look — formerly the opt-in
   redesign/redesign.css skin). main.php / login.php hard-add the `.ui-modern`
   class to <html> (login also `page-login`), so every rule below always applies.
   Crestron brand palette (derived from www.crestron.com, 2026-07-06): silver
   #f1f1f1 canvas, charcoal #32363a type, slate-blue #27455c sidebar, logo-blue
   #004a80 actions; dark mode = matching charcoal family with brightened blue.
   Plus Jakarta Sans, soft radii, refined elevation, light + dark.
   To extend: add rules under `.ui-modern[.light-style|.dark-style]` using the
   tokens defined just below.
   ============================================================================ */

/* ---- 1. Design tokens (per theme) ---- */
.ui-modern.light-style {
  --m-bg:            #f1f1f1;   /* crestron.com body silver */
  --m-surface:       #ffffff;
  --m-surface-2:     #f6f7f8;
  --m-border:        #e4e6e8;
  --m-border-strong: #c8ccd0;
  --m-text:          #32363a;   /* Crestron charcoal (site-exact) */
  --m-text-muted:    #6a6d70;   /* Crestron gray (site-exact) */
  --m-heading:       #23272b;
  --m-primary:       #004a80;   /* Crestron logo blue (pixel-exact) */
  --m-primary-rgb:   0, 74, 128;
  --m-primary-hover: #003a66;
  --m-accent:        #0072ce;
  --m-sidebar-bg:    #27455c;   /* Crestron dark slate-blue (site-exact) */
  --m-sidebar-grad:  linear-gradient(180deg, #2d4f6b 0%, #223d52 100%);
  --m-sidebar-text:  #dde6ee;   /* non-active item label — legible on slate */
  --m-sidebar-mut:   #9fb1c1;   /* icons / submenu / toggle */
  --m-sidebar-hover: rgba(255, 255, 255, 0.06);
  --m-sidebar-active:#0072ce;
  --m-shadow-sm:     0 1px 2px rgba(35, 39, 43, 0.06), 0 1px 3px rgba(35, 39, 43, 0.05);
  --m-shadow-md:     0 4px 12px rgba(35, 39, 43, 0.08), 0 2px 4px rgba(35, 39, 43, 0.04);
  --m-shadow-lg:     0 12px 32px rgba(35, 39, 43, 0.14);
  --m-radius:        0.75rem;
  --m-radius-sm:     0.5rem;
  --m-ring:          rgba(0, 74, 128, 0.25);
}
.ui-modern.dark-style {
  --m-bg:            #16191d;   /* charcoal family derived from Crestron #32363a / #41484f */
  --m-surface:       #1f2328;
  --m-surface-2:     #262b31;
  --m-border:        #32383f;
  --m-border-strong: #414851;   /* echoes crestron.com steel #41484f */
  --m-text:          #e2e5e8;
  --m-text-muted:    #9aa1a8;
  --m-heading:       #f2f4f6;
  --m-primary:       #3a8fd0;   /* brightened logo blue for dark surfaces */
  --m-primary-rgb:   58, 143, 208;
  --m-primary-hover: #4da3dd;
  --m-accent:        #59c2ff;
  --m-sidebar-bg:    #14181c;
  --m-sidebar-grad:  linear-gradient(180deg, #1a2027 0%, #101418 100%);
  --m-sidebar-text:  #d9dee3;   /* non-active item label — legible on charcoal */
  --m-sidebar-mut:   #8f9aa5;   /* icons / submenu / toggle */
  --m-sidebar-hover: rgba(255, 255, 255, 0.05);
  --m-sidebar-active:#3a8fd0;
  --m-shadow-sm:     0 1px 2px rgba(0, 0, 0, 0.4);
  --m-shadow-md:     0 4px 14px rgba(0, 0, 0, 0.45);
  --m-shadow-lg:     0 16px 40px rgba(0, 0, 0, 0.6);
  --m-radius:        0.75rem;
  --m-radius-sm:     0.5rem;
  --m-ring:          rgba(77, 163, 221, 0.35);
}

/* ---- 2. Typography + canvas ---- */
.ui-modern,
.ui-modern body,
.ui-modern .menu,
.ui-modern .btn,
.ui-modern .form-control,
.ui-modern .form-select {
  font-family: 'Plus Jakarta Sans', -apple-system, 'Segoe UI', Roboto, system-ui, sans-serif;
}
.ui-modern body { background: var(--m-bg) !important; color: var(--m-text); letter-spacing: -0.002em; }
.ui-modern h1, .ui-modern h2, .ui-modern h3,
.ui-modern h4, .ui-modern h5, .ui-modern h6 { color: var(--m-heading); font-weight: 700; letter-spacing: -0.01em; }
.ui-modern .text-muted { color: var(--m-text-muted) !important; }
.ui-modern .table td,
.ui-modern .table th,
.ui-modern .avatar-initial,
.ui-modern .card .display-6,
.ui-modern .card h3 { font-variant-numeric: tabular-nums; }

/* ---- 3. Sidebar (deep navy, both modes) ---- */
.ui-modern #layout-menu.layout-menu,
.ui-modern .bg-menu-theme {
  background: var(--m-sidebar-grad) !important;
  border-right: 1px solid rgba(255, 255, 255, 0.04) !important;
  box-shadow: 0 0 24px rgba(0, 0, 0, 0.18);
}
.ui-modern #layout-menu .menu-inner-shadow { display: none; }
.ui-modern #layout-menu .app-brand {
  padding-block: 1.1rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  margin-bottom: 0.35rem;
}
.ui-modern #layout-menu .app-brand svg path[fill] { fill: #ffffff !important; }
.ui-modern #layout-menu .menu-toggle-icon,
.ui-modern #layout-menu .layout-menu-toggle i { color: var(--m-sidebar-mut) !important; }
.ui-modern #layout-menu .layout-menu-toggle:hover { background: var(--m-sidebar-hover) !important; color: #fff !important; }
.ui-modern #layout-menu .menu-inner > .menu-item { margin: 0.1rem 0.65rem; }
.ui-modern #layout-menu .menu-link {
  color: var(--m-sidebar-text) !important;
  border-radius: var(--m-radius-sm);
  font-weight: 500;
  transition: background-color .18s ease, color .18s ease;
}
.ui-modern #layout-menu .menu-inner > .menu-item > .menu-link { padding: 0.6rem 0.85rem; }
.ui-modern #layout-menu .menu-link .menu-icon { color: var(--m-sidebar-mut) !important; }
.ui-modern #layout-menu .menu-item:not(.active) > .menu-link:hover { background: var(--m-sidebar-hover) !important; color: #ffffff !important; }
.ui-modern #layout-menu .menu-item:not(.active) > .menu-link:hover .menu-icon { color: #fff !important; }
.ui-modern #layout-menu .menu-item.active > .menu-link {
  background: var(--m-sidebar-active) !important;
  color: #ffffff !important;
  font-weight: 600;
  box-shadow: 0 4px 12px rgba(0, 114, 206, 0.30);
}
.ui-modern #layout-menu .menu-item.active > .menu-link::before { display: none; }
.ui-modern.light-style #layout-menu .menu-item.active > .menu-link { background: var(--m-sidebar-active) !important; }
.ui-modern #layout-menu .menu-item.active > .menu-link .menu-icon { color: #fff !important; }
.ui-modern #layout-menu .menu-sub .menu-link { color: var(--m-sidebar-mut) !important; padding-block: 0.45rem; }
/* sub-item bullet = outline circle (active = filled brand dot) */
.ui-modern #layout-menu .menu-sub .menu-link::before {
  content: "" !important;
  width: 7px !important;
  height: 7px !important;
  border-radius: 50% !important;
  background: transparent !important;
  border: 1.5px solid var(--m-sidebar-mut) !important;
  top: 50% !important;
  left: 1.35rem !important;
  transform: translateY(-50%) !important;
}
.ui-modern #layout-menu .menu-sub .menu-link:hover::before { border-color: #fff !important; }
/* Active SUB-item (under an open parent): a SUBTLE highlight + the filled dot — NOT the
   full active pill/glow (that heavy treatment is for top-level items; on an indented
   sub-item it read as a weird floating glow). Kill the box-shadow explicitly. */
.ui-modern #layout-menu .menu-item.active .menu-sub .menu-item.active > .menu-link { color: #ffffff !important; background: var(--m-sidebar-hover) !important; box-shadow: none !important; }
.ui-modern #layout-menu .menu-sub .menu-item.active > .menu-link::before { display: inline-block !important; background: var(--m-sidebar-active) !important; border-color: var(--m-sidebar-active) !important; }
.ui-modern #layout-menu .menu-sub .menu-link:hover { color: #fff !important; }
.ui-modern #layout-menu .menu-item.open:not(.active) > .menu-toggle { background: var(--m-sidebar-hover) !important; color: #fff !important; }

/* ---- 4. Navbar ---- */
.ui-modern .layout-navbar .navbar-detached,
.ui-modern .navbar-detached {
  background: var(--m-surface) !important;
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius);
  box-shadow: var(--m-shadow-sm);
  backdrop-filter: saturate(140%) blur(6px);
}
.ui-modern .layout-navbar { box-shadow: none !important; }
.ui-modern .navbar .nav-item .nav-link,
.ui-modern .navbar .search-toggler { color: var(--m-text-muted) !important; }
.ui-modern .navbar .nav-item .nav-link:hover { color: var(--m-primary) !important; }
.ui-modern #gsInput,
.ui-modern .gs-input {
  background: var(--m-surface-2) !important;
  border: 1px solid var(--m-border) !important;
  border-radius: 999px !important;
  height: 40px;
  padding-left: 0.95rem !important;
  color: var(--m-text);
}
.ui-modern #gsInput:focus,
.ui-modern .gs-input:focus { border-color: var(--m-primary) !important; box-shadow: 0 0 0 3px var(--m-ring) !important; background: var(--m-surface) !important; }
.ui-modern .global-search-results {
  background: var(--m-surface) !important;
  border-color: var(--m-border) !important;
  border-radius: var(--m-radius) !important;
  box-shadow: var(--m-shadow-lg) !important;
  margin-top: 0.4rem;
}
.ui-modern .gsr-item { color: var(--m-text) !important; }
.ui-modern .gsr-item:hover { background: var(--m-surface-2) !important; color: var(--m-primary) !important; }
.ui-modern .avatar-online::after { box-shadow: 0 0 0 2px var(--m-surface); }

/* ---- 5. Cards ---- */
.ui-modern .card {
  background: var(--m-surface) !important;
  border: 1px solid var(--m-border) !important;
  border-radius: var(--m-radius) !important;
  box-shadow: var(--m-shadow-sm) !important;
  transition: box-shadow .2s ease, transform .2s ease;
}
.ui-modern .card .card-header {
  background: transparent !important;
  border-bottom: 1px solid var(--m-border);
  font-weight: 600;
  color: var(--m-heading);
  padding: 1.1rem 1.25rem;
}
/* A card-body directly under a card-header was rendering with padding-top:0 (its bottom padding stayed
   1.5rem — asymmetric), so the first form field sat right ON the header's border line. Restore the top
   padding so content clears the header separator. */
.ui-modern .card .card-header + .card-body { padding-top: 1.5rem !important; }
.ui-modern .card .card-title { color: var(--m-heading); font-weight: 700; }
.ui-modern a.card:hover,
.ui-modern .card.cursor-pointer:hover,
.ui-modern .card-hover:hover { box-shadow: var(--m-shadow-md) !important; transform: translateY(-2px); }
.ui-modern .avatar .avatar-initial { border-radius: var(--m-radius-sm); font-weight: 700; }

/* ---- 6. Tables (incl. DataTables) ---- */
.ui-modern .table { color: var(--m-text); }
.ui-modern .table > :not(caption) > * > * { background: transparent; }
.ui-modern .table thead th,
.ui-modern table.dataTable thead th,
.ui-modern table.dataTable thead td {
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  font-weight: 700;
  color: var(--m-text-muted) !important;
  border-bottom: 1px solid var(--m-border-strong) !important;
  background: var(--m-surface-2) !important;
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
}
.ui-modern .table tbody td { border-bottom: 1px solid var(--m-border); padding-top: 0.8rem; padding-bottom: 0.8rem; vertical-align: middle; }
.ui-modern .table-hover tbody tr:hover > *,
.ui-modern table.dataTable.table-hover tbody tr:hover > * { background: rgba(var(--m-primary-rgb), 0.05) !important; }
.ui-modern table.dataTable td { color: var(--m-text); }
.ui-modern .dataTables_wrapper .form-control,
.ui-modern .dataTables_wrapper .form-select { border-radius: 999px; }
.ui-modern .dataTables_wrapper .pagination .page-link { border-radius: var(--m-radius-sm); margin: 0 2px; border: 1px solid var(--m-border); color: var(--m-text-muted); }
.ui-modern .dataTables_wrapper .pagination .page-item.active .page-link { background: var(--m-primary); border-color: var(--m-primary); color: #fff; }

/* ---- 7. Buttons ---- */
.ui-modern .btn { border-radius: var(--m-radius-sm); font-weight: 600; letter-spacing: 0.01em; transition: transform .12s ease, box-shadow .15s ease, background-color .15s ease, border-color .15s ease; }
.ui-modern .btn:active { transform: translateY(0); }
.ui-modern .btn:not(.btn-link):hover { transform: translateY(-1px); }
.ui-modern .btn:focus-visible { box-shadow: 0 0 0 3px var(--m-ring) !important; outline: none; }
.ui-modern .btn-primary { background: var(--m-primary); border-color: var(--m-primary); box-shadow: 0 2px 8px rgba(var(--m-primary-rgb), 0.3); }
.ui-modern .btn-primary:hover,
.ui-modern .btn-primary:focus { background: var(--m-primary-hover); border-color: var(--m-primary-hover); box-shadow: 0 6px 18px rgba(var(--m-primary-rgb), 0.4); }
.ui-modern .btn-outline-primary { color: var(--m-primary); border-color: var(--m-primary); }
.ui-modern .btn-outline-primary:hover { background: var(--m-primary); border-color: var(--m-primary); }

/* ---- 8. Forms / inputs / select2 ---- */
.ui-modern .form-control,
.ui-modern .form-select,
.ui-modern .select2-container--default .select2-selection--single,
.ui-modern .input-group-text {
  border-radius: var(--m-radius-sm) !important;
  border-color: var(--m-border-strong);
  background-color: var(--m-surface);
  color: var(--m-text);
}
.ui-modern .form-control:focus,
.ui-modern .form-select:focus { border-color: var(--m-primary); box-shadow: 0 0 0 3px var(--m-ring); }
.ui-modern.dark-style .form-control,
.ui-modern.dark-style .form-select { background-color: var(--m-surface-2); }
.ui-modern .form-control::placeholder { color: var(--m-text-muted); }
/* floating-label inputs need a transparent placeholder (the label doubles as it) */
.ui-modern .form-floating > .form-control::placeholder { color: transparent; }
.ui-modern .form-label,
.ui-modern .col-form-label { color: var(--m-text); font-weight: 600; }
.ui-modern .select2-container--default.select2-container--focus .select2-selection--single,
.ui-modern .select2-container--default.select2-container--open .select2-selection--single { border-color: var(--m-primary) !important; box-shadow: 0 0 0 3px var(--m-ring); }
.ui-modern .select2-dropdown { border-color: var(--m-border-strong); border-radius: var(--m-radius-sm); box-shadow: var(--m-shadow-md); }
/* Input-group seam: the radius above re-rounds ALL corners of select2 selections / form-controls
   (and buttons via `.ui-modern .btn`), which breaks the square INNER corners an input-group needs —
   so a field and its trailing "New" button join with mismatched sharp-vs-round edges (edit-stock
   modal). Re-square the inner corners: the field's RIGHT side, the trailing button's LEFT side. */
.ui-modern .input-group > .select2-container:not(:last-child) .select2-selection--single,
.ui-modern .input-group > .form-control:not(:last-child),
.ui-modern .input-group > .form-select:not(:last-child) {
  border-top-right-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
}
.ui-modern .input-group > .btn:last-child,
.ui-modern .input-group > .input-group-text:last-child {
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
}
/* LEADING-addon case (the trailing rules above only handle a trailing button/addon): e.g. the
   loan-edit "Loan #" field = a `#` `.input-group-text` BEFORE the value `.form-control`. Square the
   addon's RIGHT corners and the following field's LEFT corners so the badge and the box meet flush
   instead of each keeping the modern full radius. */
.ui-modern .input-group > .input-group-text:not(:last-child) {
  border-top-right-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
}
.ui-modern .input-group > .form-control:not(:first-child),
.ui-modern .input-group > .form-select:not(:first-child) {
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
}
/* select2 inside an input-group (e.g. the Add/Link-Participant modal's contact picker + "New
   Participant" button) must STRETCH to meet the trailing button. KEY GOTCHA: common.js wraps the
   <select> in a `<div class="position-relative">` BEFORE calling .select2(), so the input-group's
   flex child is that WRAPPER — not the `.select2-container`/`.form-select` — which is why the
   direct-child seam rules (and an earlier `.select2-container` width fix) never reached it and the
   field sat narrow with a gap before the button. Target the wrapper: make it the growing flex item,
   the select2 fill it, and square the wrapper's inner (right) corners so the edges sit flush. Theme-
   level (not skin-scoped) so it holds under modern + glass; the button's left corner is squared by
   the `.ui-modern .input-group > .btn:last-child` rule above. */
.input-group > .position-relative {
  flex: 1 1 auto;
  width: 1%;
  min-width: 0;
}
.input-group > .position-relative > .select2-container { width: 100% !important; }
.input-group > .position-relative:not(:last-child) .select2-selection--single {
  border-top-right-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
}
/* Fallback for a select2 that is a DIRECT input-group child (not wrapped). */
.input-group > .select2-container { flex: 1 1 auto; width: auto !important; }
.ui-modern .form-check-input:checked { background-color: var(--m-primary); border-color: var(--m-primary); }
.ui-modern .form-check-input:focus { box-shadow: 0 0 0 3px var(--m-ring); }

/* ---- 9. Badges ---- */
.ui-modern .badge { border-radius: 0.4rem; font-weight: 600; letter-spacing: 0.01em; }
.ui-modern .badge.rounded-pill { border-radius: 999px; }

/* ============================================================================
   SEMANTIC STATUS COLORS (2026-07-06) — corporate retune of the Sneat defaults
   (success #28c76f / danger #ea5455 / warning #ff9f43 / info #00cfe8 read neon
   against the Crestron palette). Redefining --bs-*(-rgb) here fixes the .bg-* /
   .text-* utilities automatically (Bootstrap computes them from the rgb vars);
   .btn-* / *-label-* / .alert-* are compiled with literal hexes in the vendor
   CSS, so they're overridden explicitly below. Vendor-internal uses (validation
   icons etc.) keep the stock hues — same family, accepted. Status MEANINGS are
   unchanged (stock/loan/training JS maps reference classes, not hex).
   Dark-label text (--sem-*-text dark values) is brightened for AA contrast on
   the dark tinted fills — supersedes the old "DARK-MODE label contrast" block.
   ============================================================================ */
.ui-modern.light-style {
  --bs-success: #2e9e5b;  --bs-success-rgb: 46, 158, 91;
  --bs-danger:  #d64545;  --bs-danger-rgb:  214, 69, 69;
  --bs-warning: #e08e00;  --bs-warning-rgb: 224, 142, 0;
  --bs-info:    #0a9cc0;  --bs-info-rgb:    10, 156, 192;
  --sem-primary-text: #004a80;
  --sem-success-text: #27884e;
  --sem-danger-text:  #c13c3c;
  --sem-warning-text: #a96b00;
  --sem-info-text:    #0989a9;
}
.ui-modern.dark-style {
  --bs-success: #37b06a;  --bs-success-rgb: 55, 176, 106;
  --bs-danger:  #e05858;  --bs-danger-rgb:  224, 88, 88;
  --bs-warning: #efa11a;  --bs-warning-rgb: 239, 161, 26;
  --bs-info:    #17b1d6;  --bs-info-rgb:    23, 177, 214;
  --sem-primary-text: #6db9ec;
  --sem-success-text: #62d392;
  --sem-danger-text:  #ff8a8a;
  --sem-warning-text: #ffb95e;
  --sem-info-text:    #4cc9de;
}
/* Solid semantic buttons (vendor hexes are literal — override; hover = darken) */
.ui-modern .btn-success { background-color: var(--bs-success) !important; border-color: var(--bs-success) !important; }
.ui-modern .btn-danger  { background-color: var(--bs-danger)  !important; border-color: var(--bs-danger)  !important; }
.ui-modern .btn-warning { background-color: var(--bs-warning) !important; border-color: var(--bs-warning) !important; }
.ui-modern .btn-info    { background-color: var(--bs-info)    !important; border-color: var(--bs-info)    !important; }
.ui-modern .btn-success:hover, .ui-modern .btn-success:focus, .ui-modern .btn-success:active,
.ui-modern .btn-danger:hover,  .ui-modern .btn-danger:focus,  .ui-modern .btn-danger:active,
.ui-modern .btn-warning:hover, .ui-modern .btn-warning:focus, .ui-modern .btn-warning:active,
.ui-modern .btn-info:hover,    .ui-modern .btn-info:focus,    .ui-modern .btn-info:active { filter: brightness(0.92); }
.ui-modern .btn-outline-info { color: var(--bs-info) !important; border-color: var(--bs-info) !important; }
.ui-modern .btn-outline-info:hover { color: #fff !important; background-color: var(--bs-info) !important; }
/* Tinted "label" badges + buttons: fill from the per-mode rgb, text from --sem-*-text */
.ui-modern .badge.bg-label-primary, .ui-modern .btn-label-primary { background-color: rgba(var(--m-primary-rgb), 0.16) !important; color: var(--sem-primary-text) !important; }
.ui-modern .badge.bg-label-success, .ui-modern .btn-label-success { background-color: rgba(var(--bs-success-rgb), 0.16) !important; color: var(--sem-success-text) !important; }
.ui-modern .badge.bg-label-danger,  .ui-modern .btn-label-danger  { background-color: rgba(var(--bs-danger-rgb), 0.16)  !important; color: var(--sem-danger-text)  !important; }
.ui-modern .badge.bg-label-warning, .ui-modern .btn-label-warning { background-color: rgba(var(--bs-warning-rgb), 0.16) !important; color: var(--sem-warning-text) !important; }
.ui-modern .badge.bg-label-info,    .ui-modern .btn-label-info    { background-color: rgba(var(--bs-info-rgb), 0.16)    !important; color: var(--sem-info-text)    !important; }
/* label-button hover = solid fill + white text (the !important fills above would
   otherwise pin the tint and kill the vendor hover feedback) */
.ui-modern .btn-label-primary:hover { background-color: var(--m-primary)  !important; color: #fff !important; }
.ui-modern .btn-label-success:hover { background-color: var(--bs-success) !important; color: #fff !important; }
.ui-modern .btn-label-danger:hover  { background-color: var(--bs-danger)  !important; color: #fff !important; }
.ui-modern .btn-label-warning:hover { background-color: var(--bs-warning) !important; color: #fff !important; }
.ui-modern .btn-label-info:hover    { background-color: var(--bs-info)    !important; color: #fff !important; }
/* Neutral label variants — keep vendor fills, just fix dark text contrast */
.ui-modern.dark-style .btn-label-secondary, .ui-modern.dark-style .badge.bg-label-secondary { color: #b9c0cc !important; }
.ui-modern.dark-style .btn-label-dark,      .ui-modern.dark-style .badge.bg-label-dark      { color: #c5cad6 !important; }
/* Alerts (compiled with literal tints in vendor CSS) */
.ui-modern .alert-success { background-color: rgba(var(--bs-success-rgb), 0.16) !important; border-color: rgba(var(--bs-success-rgb), 0.3) !important; color: var(--sem-success-text) !important; }
.ui-modern .alert-danger  { background-color: rgba(var(--bs-danger-rgb), 0.16)  !important; border-color: rgba(var(--bs-danger-rgb), 0.3)  !important; color: var(--sem-danger-text)  !important; }
.ui-modern .alert-warning { background-color: rgba(var(--bs-warning-rgb), 0.16) !important; border-color: rgba(var(--bs-warning-rgb), 0.3) !important; color: var(--sem-warning-text) !important; }
.ui-modern .alert-info    { background-color: rgba(var(--bs-info-rgb), 0.16)    !important; border-color: rgba(var(--bs-info-rgb), 0.3)    !important; color: var(--sem-info-text)    !important; }

/* ---- 10. Dropdowns / modals / alerts (incl. SweetAlert) ---- */
.ui-modern .dropdown-menu { background: var(--m-surface) !important; border: 1px solid var(--m-border); border-radius: var(--m-radius); box-shadow: var(--m-shadow-lg); padding: 0.4rem; }
.ui-modern .dropdown-item { border-radius: var(--m-radius-sm); color: var(--m-text); padding: 0.5rem 0.75rem; }
.ui-modern .dropdown-item:hover { background: var(--m-surface-2); color: var(--m-primary); }
.ui-modern .dropdown-divider { border-color: var(--m-border); }
.ui-modern .modal-content { background: var(--m-surface); border: 1px solid var(--m-border); border-radius: calc(var(--m-radius) + 0.25rem); box-shadow: var(--m-shadow-lg); }
.ui-modern .modal-header,
.ui-modern .modal-footer { border-color: var(--m-border); }
.ui-modern .modal-backdrop.show { -webkit-backdrop-filter: blur(6px) saturate(120%); backdrop-filter: blur(6px) saturate(120%); opacity: 0.55; }
.ui-modern .swal2-popup { border-radius: var(--m-radius) !important; background: var(--m-surface) !important; box-shadow: var(--m-shadow-lg) !important; }
.ui-modern .swal2-popup .swal2-title,
.ui-modern .swal2-popup .swal2-html-container { color: var(--m-text) !important; }
.ui-modern .swal2-styled.swal2-confirm { border-radius: var(--m-radius-sm) !important; background: var(--m-primary) !important; }
.ui-modern .alert { border-radius: var(--m-radius-sm); }
.ui-modern hr,
.ui-modern .border,
.ui-modern .border-top,
.ui-modern .border-bottom { border-color: var(--m-border) !important; }

/* ---- 11. Busy overlay (inline-styled in main.php) ---- */
.ui-modern #appBusyOverlay { background: rgba(10, 13, 16, 0.62) !important; backdrop-filter: blur(4px) saturate(120%); }
.ui-modern #appBusyMsg { font-family: 'Plus Jakarta Sans', system-ui, sans-serif; font-weight: 600 !important; letter-spacing: 0.01em; }

/* ---- 12. Login page (page-login, always light-style) ---- */
.ui-modern.page-login,
.ui-modern.page-login body { background: radial-gradient(1200px 600px at 50% -10%, #2a4a63 0%, #111c26 60%) fixed !important; min-height: 100vh; }
.ui-modern.page-login .authentication-wrapper.authentication-basic { min-height: 100vh; }
.ui-modern.page-login .authentication-inner { width: 100%; max-width: 420px; }
.ui-modern.page-login .card { background: var(--m-surface, #ffffff) !important; border: 1px solid var(--m-border) !important; border-radius: 1.1rem !important; box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45) !important; }
.ui-modern.page-login .card-body { padding: 2.25rem 2rem; }
.ui-modern.page-login .app-brand svg { max-width: 100%; height: auto; }
.ui-modern.page-login .divider-text { color: #9aa1a8; font-size: 0.72rem; letter-spacing: 0.06em; }
.ui-modern.page-login .btn-primary { background: var(--m-primary); border-color: var(--m-primary); padding-block: 0.7rem; font-size: 1rem; }

/* ---- 13. Accessibility — respect reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  .ui-modern *,
  .ui-modern *::before,
  .ui-modern *::after { transition-duration: 0.001ms !important; animation-duration: 0.001ms !important; }
}

/* ============================================================================
   APPLE GLASSMORPHISM (merged as DEFAULT 2026-06-22; formerly the opt-in
   glass/ skin). main.php/login.php hard-add the .ui-glass class, so every rule
   below always applies, layered on top of the modern look. Translucent
   backdrop-filter panels + hairline borders over a vibrant ambient body gradient.
   Extend under .ui-glass[.light-style|.dark-style] using the --g-* tokens.
   ============================================================================ */
/* ---- tokens ---- */
.ui-glass.light-style {
  --g-blur: 22px;
  --g-radius: 20px;
  --g-panel:        rgba(255, 255, 255, 0.55);
  --g-panel-2:      rgba(255, 255, 255, 0.42);   /* slightly clearer (nested) */
  --g-border:       rgba(255, 255, 255, 0.65);
  --g-border-soft:  rgba(255, 255, 255, 0.35);
  --g-shadow:       0 10px 34px rgba(39, 69, 92, 0.14);
  --g-shadow-lg:    0 20px 60px rgba(39, 69, 92, 0.22);
  --g-text:         #23272b;
  --g-text-muted:   #5b6167;
  --g-sidebar:      rgba(39, 69, 92, 0.60);   /* frosted Crestron slate-blue */
  --g-sidebar-text: #e9eef3;
  --g-sidebar-mut:  #b3c1cd;
}
.ui-glass.dark-style {
  --g-blur: 26px;
  --g-radius: 20px;
  --g-panel:        rgba(255, 255, 255, 0.09);
  --g-panel-2:      rgba(255, 255, 255, 0.06);
  --g-border:       rgba(255, 255, 255, 0.18);
  --g-border-soft:  rgba(255, 255, 255, 0.10);
  --g-shadow:       0 10px 34px rgba(0, 0, 0, 0.45);
  --g-shadow-lg:    0 22px 60px rgba(0, 0, 0, 0.6);
  --g-text:         #e8edf2;
  --g-text-muted:   #a8b2bb;
  --g-sidebar:      rgba(16, 20, 24, 0.55);   /* frosted charcoal */
  --g-sidebar-text: #e9eef3;
  --g-sidebar-mut:  #9fadb8;
}

/* ---- 1. Ambient backdrop (the vibrant surface the glass blurs) ---- */
/* All-blue Crestron glows (retuned 2026-07-06; formerly blue/purple/teal). */
.ui-glass.light-style body {
  background:
    radial-gradient(900px 520px at 12% -8%, rgba(0, 114, 206, 0.28), transparent 60%),
    radial-gradient(820px 520px at 92% 4%, rgba(39, 69, 92, 0.20), transparent 60%),
    radial-gradient(900px 600px at 70% 108%, rgba(14, 165, 233, 0.18), transparent 60%),
    linear-gradient(160deg, #eef3fc 0%, #e7ecf7 100%) !important;
  background-attachment: fixed !important;
}
.ui-glass.dark-style body {
  background:
    radial-gradient(820px 480px at 8% -6%, rgba(0, 114, 206, 0.35), transparent 58%),
    radial-gradient(760px 480px at 96% 0%, rgba(77, 163, 221, 0.24), transparent 58%),
    radial-gradient(900px 600px at 74% 112%, rgba(30, 120, 180, 0.26), transparent 58%),
    linear-gradient(160deg, #10161d 0%, #0b0f14 100%) !important;
  background-attachment: fixed !important;
}

/* ---- 2. Sidebar — frosted glass ---- */
.ui-glass #layout-menu.layout-menu,
.ui-glass .bg-menu-theme {
  background: var(--g-sidebar) !important;
  -webkit-backdrop-filter: blur(var(--g-blur)) saturate(160%);
  backdrop-filter: blur(var(--g-blur)) saturate(160%);
  border-right: 1px solid var(--g-border-soft) !important;
  box-shadow: var(--g-shadow);
}
.ui-glass #layout-menu .menu-link { color: var(--g-sidebar-text) !important; }
.ui-glass #layout-menu .menu-link .menu-icon,
.ui-glass #layout-menu .menu-sub .menu-link { color: var(--g-sidebar-mut) !important; }
.ui-glass #layout-menu .app-brand { border-bottom-color: var(--g-border-soft) !important; }
/* active item — luminous glass pill (Crestron blue; was blue→indigo) */
.ui-glass #layout-menu .menu-item.active > .menu-link {
  background: linear-gradient(135deg, rgba(0,114,206,0.92), rgba(0,74,128,0.88)) !important;
  box-shadow: 0 6px 20px rgba(0, 114, 206, 0.4) !important;
  color: #fff !important;
}
.ui-glass.dark-style #layout-menu .menu-item.active > .menu-link {
  background: linear-gradient(135deg, rgba(77,163,221,0.92), rgba(47,111,168,0.88)) !important;
  box-shadow: 0 6px 20px rgba(77, 163, 221, 0.35) !important;
}
/* But an active SUB-item (menu-sub) must NOT get that luminous gradient pill + glow — on an
   indented sub-row it reads as a weird floating glow. Give it a subtle highlight instead (the
   filled brand dot from the modern ::before still marks it active). */
.ui-glass #layout-menu .menu-item.active .menu-sub .menu-item.active > .menu-link {
  background: rgba(255, 255, 255, 0.08) !important;
  box-shadow: none !important;
  color: #fff !important;
}

/* ---- 3. Navbar — frosted ---- */
.ui-glass .layout-navbar .navbar-detached,
.ui-glass .navbar-detached {
  background: var(--g-panel) !important;
  -webkit-backdrop-filter: blur(var(--g-blur)) saturate(180%);
  backdrop-filter: blur(var(--g-blur)) saturate(180%);
  border: 1px solid var(--g-border) !important;
  border-radius: var(--g-radius) !important;
  box-shadow: var(--g-shadow) !important;
}
.ui-glass #gsInput,
.ui-glass .gs-input {
  background: var(--g-panel-2) !important;
  border: 1px solid var(--g-border-soft) !important;
}

/* ---- 4. Cards / panels — the headline frosted-glass surface ---- */
.ui-glass .card {
  background: var(--g-panel) !important;
  -webkit-backdrop-filter: blur(var(--g-blur)) saturate(180%);
  backdrop-filter: blur(var(--g-blur)) saturate(180%);
  border: 1px solid var(--g-border) !important;
  border-radius: var(--g-radius) !important;
  box-shadow: var(--g-shadow) !important;
  color: var(--g-text);
  transition: transform .25s cubic-bezier(0.16,1,0.3,1), box-shadow .25s ease;
}
.ui-glass .card .card-header { border-bottom-color: var(--g-border-soft) !important; color: var(--g-text); }
.ui-glass a.card:hover,
.ui-glass .card.cursor-pointer:hover,
.ui-glass .card-hover:hover {
  transform: translateY(-3px) scale(1.01);
  box-shadow: var(--g-shadow-lg) !important;
}
/* a subtle top "light reflection" edge on cards */
.ui-glass .card::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  padding-top: 1px;
  background: linear-gradient(180deg, rgba(255,255,255,0.5), transparent 28%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  pointer-events: none; opacity: 0.6;
}
.ui-glass .card { position: relative; }
.ui-glass.dark-style .card::before { background: linear-gradient(180deg, rgba(255,255,255,0.18), transparent 26%); }

/* readable text on the glass */
.ui-glass .card, .ui-glass .card p, .ui-glass .card small,
.ui-glass .table { color: var(--g-text); }
.ui-glass .text-muted { color: var(--g-text-muted) !important; }

/* ---- 5. Tables: let the card glass show through ---- */
.ui-glass .table,
.ui-glass table.dataTable { background: transparent !important; }
.ui-glass .table thead th,
.ui-glass table.dataTable thead th {
  background: var(--g-panel-2) !important;
  border-bottom-color: var(--g-border-soft) !important;
  color: var(--g-text-muted) !important;
}
.ui-glass .table tbody td { border-bottom-color: var(--g-border-soft) !important; }
.ui-glass .table-hover tbody tr:hover > *,
.ui-glass table.dataTable.table-hover tbody tr:hover > * {
  background: rgba(0, 114, 206, 0.10) !important;
}

/* ---- 6. Buttons ---- */
.ui-glass .btn { border-radius: 12px; }
.ui-glass .btn-primary {
  background: linear-gradient(135deg, var(--m-accent, #0072ce), var(--m-primary, #004a80)) !important;   /* Crestron blue (was blue→indigo) */
  border: 0 !important;
  box-shadow: 0 6px 18px rgba(0, 114, 206, 0.4) !important;
}
.ui-glass .btn-primary:hover { box-shadow: 0 10px 26px rgba(0, 114, 206, 0.5) !important; transform: translateY(-1px); }
.ui-glass.dark-style .btn-primary {
  background: linear-gradient(135deg, #4da3dd, #2f6fa8) !important;
  box-shadow: 0 6px 18px rgba(77, 163, 221, 0.35) !important;
}
.ui-glass.dark-style .btn-primary:hover { box-shadow: 0 10px 26px rgba(77, 163, 221, 0.45) !important; }

/* ⭐ DISABLED primary buttons must not glow (app-wide, 2026-07-29).
   The four rules above set the glow with `!important`, which beats even the vendor theme's OWN
   `.btn-primary:disabled { box-shadow: none !important }` (theme-default.css:298 — same specificity,
   lost on source order). Net effect before this: EVERY disabled btn-primary in the app kept a full
   Crestron-blue glow and read as clickable; Bootstrap's `opacity:.65` was the only hint it wasn't.
   Found by measuring a rendered page — the CSS looks correct in isolation.
   Specificity must clear the highest glow rule (`.ui-glass.dark-style .btn-primary:hover` = 0,4,0),
   hence the explicit `:disabled:hover` / `.disabled:hover` pairs at 0,5,0. `.disabled` (class) is
   listed alongside `:disabled` (attribute) because Bootstrap uses the class form on `<a>` buttons,
   which cannot carry the attribute. `transform` is reset too so a disabled button never lifts. */
.ui-glass .btn-primary:disabled,
.ui-glass .btn-primary.disabled,
.ui-glass .btn-primary:disabled:hover,
.ui-glass .btn-primary.disabled:hover,
.ui-glass.dark-style .btn-primary:disabled,
.ui-glass.dark-style .btn-primary.disabled,
.ui-glass.dark-style .btn-primary:disabled:hover,
.ui-glass.dark-style .btn-primary.disabled:hover {
  box-shadow: none !important;
  transform: none !important;
}
/* tinted "label" buttons (e.g. the Cancel buttons) — give them the VISIBLE `--m-border-strong` border
   APP-WIDE, not just on opaque surfaces. The faint `--g-border-soft` was invisible on both light glass
   cards (the Class Details form's Cancel) AND the opaque white modal/popup, so a `btn-label-secondary`
   Cancel read as plain borderless text everywhere. One global rule keeps every Cancel/secondary button
   clearly bordered and consistent (cards + modals + swal popups). */
.ui-glass [class*="btn-label-"] {
  background: var(--g-panel-2) !important;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border: 1px solid var(--m-border-strong) !important;
}

/* ---- 7. Inputs / selects ----
   ALL form fields follow the change-password / opaque-modal field style APP-WIDE (decided 2026-06-23):
   the original glass field used a translucent bg + faint `--g-border-soft` border that, on light glass
   CARDS (e.g. the Class Details form), was barely visible ("can't see the fields"). Route every field
   to the visible modern tokens — solid `--m-surface-2` bg + `--m-border-strong` border + `--m-text` —
   exactly like `.ui-glass .modal-content .form-control`. This makes the `.modal-content`/`.swal2-popup`
   field overrides redundant (kept, harmless) and means NEW fields get a visible border automatically. */
.ui-glass .form-control,
.ui-glass .form-select,
.ui-glass .select2-container--default .select2-selection--single,
.ui-glass .input-group-text {
  background-color: var(--m-surface-2) !important;
  border: 1px solid var(--m-border-strong) !important;
  color: var(--m-text) !important;
  border-radius: 12px !important;
}
.ui-glass .form-control:focus,
.ui-glass .form-select:focus,
.ui-glass .select2-container--default.select2-container--focus .select2-selection--single,
.ui-glass .select2-container--default.select2-container--open .select2-selection--single {
  border-color: var(--m-primary, #004a80) !important;
  box-shadow: 0 0 0 3px var(--m-ring, rgba(0, 74, 128, 0.25)) !important;
}
/* `input-group-merge` ("#" addon + value rendered as ONE merged field, e.g. the loan-edit "Loan #"):
   drop the INNER border between adjacent items so there's no divider line. The global field/addon
   `--m-border-strong` border above draws a seam otherwise. More specific than the base field rule
   (`.ui-glass .input-group-merge .…` = 3 classes) so it wins; the OUTER border is kept. */
.ui-glass .input-group-merge > .input-group-text:not(:last-child),
.ui-glass .input-group-merge > .form-control:not(:last-child) {
  border-right: 0 !important;
}
.ui-glass .input-group-merge > .input-group-text:not(:first-child),
.ui-glass .input-group-merge > .form-control:not(:first-child) {
  border-left: 0 !important;
}

/* Uniform badge height + vertically-centered content so text badges, icon-only badges, and the
   capacity badges all line up (avoids the status / bed-icon / capacity height mismatch). A FIXED height
   (not min-height) is required: an icon like ti-bed has a larger font-size than the badge text, so with
   min-height the icon badge ends up a hair taller. Vertical padding is zeroed (height + centering owns
   the box); horizontal padding is kept. Applies app-wide; badge content is short/single-line. */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 1.5rem;
  padding-top: 0;
  padding-bottom: 0;
  line-height: 1;
  vertical-align: middle;
}
.badge > .ti { font-size: 0.95rem; }

/* ---- 8. Badges ---- */
.ui-glass .badge[class*="bg-label-"] {
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 1px solid var(--g-border-soft);
}
/* The pale bg-label-* tint "flushes" into the page because --g-border-soft is a translucent WHITE edge
   (invisible on the light panel). Give the badge a CONTRASTING edge per theme so it's clearly delineated
   from the background in both light and dark mode. */
.ui-glass.light-style .badge[class*="bg-label-"] { border-color: rgba(35, 39, 43, 0.16); }
.ui-glass.dark-style  .badge[class*="bg-label-"] { border-color: rgba(255, 255, 255, 0.22); }

/* ---- 9. Dropdowns / modals — heavy frosted glass ---- */
.ui-glass .dropdown-menu {
  /* dropdown MENUS carry readable content, so they must be near-opaque — the translucent
     --g-panel let them blend into the page behind. Keep a light frost + the shadow. */
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  backdrop-filter: blur(12px) saturate(160%);
  border: 1px solid var(--g-border) !important;
  border-radius: 16px !important;
  box-shadow: var(--g-shadow-lg) !important;
}
.ui-glass.light-style .dropdown-menu { background: rgba(255, 255, 255, 0.97) !important; }
.ui-glass.dark-style  .dropdown-menu { background: rgba(31, 35, 40, 0.97) !important; }
.ui-glass .dropdown-item { color: var(--g-text); border-radius: 10px; }
.ui-glass .modal-content {
  /* Modals are CONTENT surfaces (edit forms) — they must be OPAQUE, same rule as the
     swal2 popup + dropdown menu below. A translucent panel + backdrop-filter:blur made every
     edit modal look frosted/washed-out ("all edit modals blurry"). Keep the glass border/radius/
     shadow, but a solid background and NO backdrop-filter so the form is crisp and readable. */
  background: var(--m-surface, #ffffff) !important;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  border: 1px solid var(--g-border) !important;
  border-radius: 24px !important;
  box-shadow: var(--g-shadow-lg) !important;
  color: var(--g-text);
}
.ui-glass.light-style .modal-content { background: var(--m-surface, #ffffff) !important; }
.ui-glass.dark-style  .modal-content { background: var(--m-surface, #1f2328) !important; }
/* Inputs INSIDE the now-opaque modal: the glass skin gives form fields a translucent-WHITE
   background + border (fine over a frosted panel) — but on the solid modal they're white-on-white
   and the field looks borderless. Force a solid background + a visible border (modern tokens). More
   specific than `.ui-glass .form-control`, so it wins. */
.ui-glass .modal-content .form-control,
.ui-glass .modal-content .form-select,
.ui-glass .modal-content .select2-container--default .select2-selection--single,
.ui-glass .modal-content .input-group-text {
  background-color: var(--m-surface-2) !important;
  border: 1px solid var(--m-border-strong) !important;
  color: var(--m-text) !important;
}
.ui-glass .modal-content .form-control:focus,
.ui-glass .modal-content .form-select:focus,
.ui-glass .modal-content .select2-container--default.select2-container--focus .select2-selection--single,
.ui-glass .modal-content .select2-container--default.select2-container--open .select2-selection--single {
  border-color: var(--m-primary, #004a80) !important;
}
.ui-glass .modal-header, .ui-glass .modal-footer { border-color: var(--g-border-soft) !important; }
/* Frosted blur behind modals (re-enabled 2026-06-23 by request). The earlier "edit modal is blurry"
   bug was NOT this blur per se — it was modals TRAPPED in a card's stacking context so they got blurred
   too; that's fixed by the move-to-body `show.bs.modal` handler (common.js), so the modal now paints
   ABOVE the backdrop and stays crisp while only the page behind is frosted. Keep the dim scrim + blur. */
.ui-glass .modal-backdrop.show { background: rgba(10, 16, 30, 0.45); -webkit-backdrop-filter: blur(6px) saturate(120%); backdrop-filter: blur(6px) saturate(120%); opacity: 1; }

/* SweetAlert backdrop — match the Bootstrap .modal-backdrop frosted scrim above so EVERY dialog
   (Swal alerts + confirms like "Mark as collected?") blurs the page behind it, consistent with the
   Bootstrap modals (e.g. Add New CTI Class). The swal2 popup itself stays fully opaque (below).
   Global (not skin-scoped) so it applies to every page; the body always carries ui-glass anyway. */
.swal2-container.swal2-backdrop-show {
  background: rgba(10, 16, 30, 0.45) !important;
  -webkit-backdrop-filter: blur(6px) saturate(120%);
  backdrop-filter: blur(6px) saturate(120%);
}

.ui-glass .swal2-popup {
  /* Alerts must be FULLY OPAQUE: a translucent popup (a) lets the page bleed through and
     (b) makes the SweetAlert success-icon mask pieces translucent too, so the green ring
     shows through as a "line cutting across the circle". Solid bg = clean icon + readable. */
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  border: 1px solid var(--g-border) !important;
  border-radius: 22px !important;
}
.ui-glass.light-style .swal2-popup { background: var(--m-surface, #ffffff) !important; }
.ui-glass.dark-style  .swal2-popup { background: var(--m-surface, #1f2328) !important; }

/* SweetAlert auto-dismiss countdown bar (every ShowAlert sets timerProgressBar:true). SweetAlert's
   default rgba(0,0,0,.2) strip is ~invisible on the white/glass popup, so theme it: a clearly-visible
   primary-coloured bar, a touch taller, with its ends tucked into the popup's rounded bottom corners. */
.swal2-timer-progress-bar {
  height: 0.35em !important;
  background: var(--m-primary, #004a80) !important;   /* brand blue in both modes (was --bs-primary/#696cff) */
  border-bottom-left-radius: 22px;
  border-bottom-right-radius: 22px;
}
/* Bootstrap form fields injected into a custom-`html` SweetAlert (e.g. the Reservation reserve
   dialog's seats + Remarks inputs) need the SAME visible-border treatment as MODAL fields: the
   swal2 popup is opaque (above), but the glass skin's `.form-control` uses a translucent-white bg +
   border that is invisible white-on-white on the solid popup -> the field looks "flushed out"
   (borderless). Mirror the `.ui-glass .modal-content .form-control` fix for `.swal2-popup`. */
.ui-glass .swal2-popup .form-control,
.ui-glass .swal2-popup .form-select,
.ui-glass .swal2-popup .input-group-text {
  background-color: var(--m-surface-2) !important;
  border: 1px solid var(--m-border-strong) !important;
  color: var(--m-text) !important;
}
.ui-glass .swal2-popup .form-control:focus,
.ui-glass .swal2-popup .form-select:focus {
  border-color: var(--m-primary, #004a80) !important;
}
/* belt-and-braces: pin the success-icon mask pieces to the popup colour (they normally track
   it, but make sure so no ring-line shows through) */
.ui-glass.light-style .swal2-success-circular-line-left,
.ui-glass.light-style .swal2-success-circular-line-right,
.ui-glass.light-style .swal2-success-fix { background-color: var(--m-surface, #ffffff) !important; }
.ui-glass.dark-style .swal2-success-circular-line-left,
.ui-glass.dark-style .swal2-success-circular-line-right,
.ui-glass.dark-style .swal2-success-fix { background-color: var(--m-surface, #1f2328) !important; }

/* ---- 10. KPI stat circles get a soft glow ---- */
.ui-glass .avatar.avatar-xl .avatar-initial,
.ui-glass .avatar .avatar-initial { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); }

/* ---- 11. Busy overlay — frosted ---- */
.ui-glass #appBusyOverlay {
  background: rgba(10, 16, 30, 0.45) !important;
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  backdrop-filter: blur(10px) saturate(140%);
}

/* ---- 12. Login page — single floating glass card on the ambient backdrop ---- */
.ui-glass.page-login .card {
  background: var(--g-panel) !important;
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  backdrop-filter: blur(30px) saturate(180%);
  border: 1px solid var(--g-border) !important;
  border-radius: 26px !important;
  box-shadow: var(--g-shadow-lg) !important;
}

/* ---- 13. Accessibility: drop blur where motion/transparency is unwanted ---- */
@media (prefers-reduced-transparency: reduce) {
  .ui-glass .card,
  .ui-glass .navbar-detached,
  .ui-glass .dropdown-menu,
  .ui-glass .modal-content { -webkit-backdrop-filter: none; backdrop-filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  .ui-glass .card { transition: none; }
}

/* ---- 14. Print: always print LIGHT, even from dark mode (2026-07-06). Forces the
   light token values + a white canvas so dark surfaces don't waste toner / print
   as unreadable blocks. Vendor core-dark still styles some chrome, but cards,
   tables and text (token-driven) dominate what actually prints. ---- */
@media print {
  .ui-glass body,
  .ui-modern body { background: #ffffff !important; }
  .ui-modern.dark-style {
    --m-bg: #ffffff; --m-surface: #ffffff; --m-surface-2: #f6f7f8;
    --m-border: #e4e6e8; --m-border-strong: #c8ccd0;
    --m-text: #32363a; --m-text-muted: #6a6d70; --m-heading: #23272b;
    --m-primary: #004a80; --m-primary-rgb: 0, 74, 128;
  }
  .ui-glass.dark-style {
    --g-panel: #ffffff; --g-panel-2: #f6f7f8;
    --g-text: #23272b; --g-text-muted: #5b6167;
    --g-border: #e4e6e8; --g-border-soft: #e4e6e8;
  }
  .ui-modern.dark-style .card,
  .ui-glass.dark-style .card { background: #ffffff !important; color: #32363a !important; box-shadow: none !important; }
  .ui-modern.dark-style .table td,
  .ui-modern.dark-style .table th,
  .ui-modern.dark-style .dataTable td,
  .ui-modern.dark-style .dataTable th { color: #32363a !important; }
}
