/* input(1082,9): run-time error CSS1030: Expected identifier, found '#scan-log-modal'
input(1082,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1087,9): run-time error CSS1030: Expected identifier, found '#scan-log-modal'
input(1087,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1137,25): run-time error CSS1030: Expected identifier, found '#open-scan-button'
input(1137,62): run-time error CSS1025: Expected comma or open brace, found ')'
input(1274,9): run-time error CSS1030: Expected identifier, found '#scan-log-modal'
input(1274,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1280,9): run-time error CSS1030: Expected identifier, found '#scan-log-modal'
input(1280,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1289,9): run-time error CSS1030: Expected identifier, found '#scan-log-modal'
input(1289,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1297,9): run-time error CSS1030: Expected identifier, found '#scan-log-modal'
input(1297,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1447,25): run-time error CSS1030: Expected identifier, found '>'
input(1447,54): run-time error CSS1025: Expected comma or open brace, found ')'
input(1459,25): run-time error CSS1030: Expected identifier, found '>'
input(1459,54): run-time error CSS1025: Expected comma or open brace, found ')'
input(1465,25): run-time error CSS1030: Expected identifier, found '>'
input(1465,66): run-time error CSS1025: Expected comma or open brace, found ')'
input(1470,25): run-time error CSS1030: Expected identifier, found '>'
input(1470,66): run-time error CSS1025: Expected comma or open brace, found ')'
input(1476,17): run-time error CSS1030: Expected identifier, found '>'
input(1476,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1480,17): run-time error CSS1030: Expected identifier, found '>'
input(1480,42): run-time error CSS1025: Expected comma or open brace, found ')'
input(1488,61): run-time error CSS1030: Expected identifier, found '>'
input(1488,86): run-time error CSS1025: Expected comma or open brace, found ')'
input(1492,28): run-time error CSS1030: Expected identifier, found '>'
input(1492,53): run-time error CSS1025: Expected comma or open brace, found ')'
input(1596,62): run-time error CSS1030: Expected identifier, found '~'
input(1596,95): run-time error CSS1025: Expected comma or open brace, found ')'
input(1601,62): run-time error CSS1030: Expected identifier, found '~'
input(1601,112): run-time error CSS1025: Expected comma or open brace, found ')'
input(1606,44): run-time error CSS1030: Expected identifier, found '~'
input(1606,77): run-time error CSS1025: Expected comma or open brace, found ')'
input(1635,37): run-time error CSS1030: Expected identifier, found '>'
input(1635,62): run-time error CSS1025: Expected comma or open brace, found ')'
input(1844,46): run-time error CSS1030: Expected identifier, found ':'
input(1844,54): run-time error CSS1025: Expected comma or open brace, found ')'
input(1865,22): run-time error CSS1030: Expected identifier, found '.'
input(1865,49): run-time error CSS1025: Expected comma or open brace, found ')'
input(1872,22): run-time error CSS1030: Expected identifier, found '.'
input(1872,49): run-time error CSS1025: Expected comma or open brace, found ')'
input(1881,22): run-time error CSS1030: Expected identifier, found '.'
input(1881,49): run-time error CSS1025: Expected comma or open brace, found ')'
input(1888,22): run-time error CSS1030: Expected identifier, found '.'
input(1888,49): run-time error CSS1025: Expected comma or open brace, found ')'
input(1893,22): run-time error CSS1030: Expected identifier, found '.'
input(1893,49): run-time error CSS1025: Expected comma or open brace, found ')'
input(2019,9): run-time error CSS1030: Expected identifier, found '.'
input(2019,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(2025,9): run-time error CSS1030: Expected identifier, found '.'
input(2025,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(2032,9): run-time error CSS1030: Expected identifier, found '.'
input(2032,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(2036,9): run-time error CSS1030: Expected identifier, found '.'
input(2036,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(2041,9): run-time error CSS1030: Expected identifier, found '.'
input(2041,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(2045,9): run-time error CSS1030: Expected identifier, found '.'
input(2045,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(2050,9): run-time error CSS1030: Expected identifier, found '.'
input(2050,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(2058,9): run-time error CSS1030: Expected identifier, found '.'
input(2058,37): run-time error CSS1025: Expected comma or open brace, found ')'
input(3068,43): run-time error CSS1030: Expected identifier, found '.'
input(3068,59): run-time error CSS1025: Expected comma or open brace, found ')' */
/* mobile.css - mobile-only UI styles (home screen, grid cards, forms, header, user menu, sidebar, toasts).
   loaded right after custom.css; the rules are unchanged from where they were added in custom.css, in the same order. */

@media (max-width: 991.98px) {
    /* thin scrollbars: custom.css gives every div scrollbar-width: thin + a scrollbar-color on mobile, and on Android a
       scrollbar colour replaces the phone's own thin overlay scrollbar with a solid ~8px bar that takes width from the
       content (the strip down the right edge, the thick dark thumb in forms). back to auto so the ::-webkit-scrollbar
       styling below applies instead: a 2px rounded thumb on a transparent track. the :hover line matches custom.css's
       own :hover colour (a tap leaves an element "hovered" on a phone). elements that hide their scrollbar
       (scrollbar-width: none / ::-webkit-scrollbar display: none) use more specific rules, which still win */
    span, ol, ul, pre, div {
        scrollbar-width: auto;
        scrollbar-color: auto;
    }

    span:hover, ol:hover, ul:hover, pre:hover, div:hover {
        scrollbar-color: auto;
    }

    span::-webkit-scrollbar, ol::-webkit-scrollbar, ul::-webkit-scrollbar, pre::-webkit-scrollbar, div::-webkit-scrollbar,
    div ::-webkit-scrollbar {
        width: 2px;
        height: 2px;
    }

    span::-webkit-scrollbar-track, ol::-webkit-scrollbar-track, ul::-webkit-scrollbar-track, pre::-webkit-scrollbar-track,
    div::-webkit-scrollbar-track, div ::-webkit-scrollbar-track {
        background: transparent;
    }

    span::-webkit-scrollbar-thumb, ol::-webkit-scrollbar-thumb, ul::-webkit-scrollbar-thumb, pre::-webkit-scrollbar-thumb,
    div::-webkit-scrollbar-thumb, div ::-webkit-scrollbar-thumb,
    span:hover::-webkit-scrollbar-thumb, ol:hover::-webkit-scrollbar-thumb, ul:hover::-webkit-scrollbar-thumb,
    pre:hover::-webkit-scrollbar-thumb, div:hover::-webkit-scrollbar-thumb {
        background-color: #A1A5B7;
        border-radius: 2px;
    }

    /* full width: custom.css caps the page at 99%, which left a strip down the right edge of every screen */
    #kt_wrapper {
        max-width: none;
    }

    /* mobile home screen: counter labels wrap to 2 lines instead of being clipped */
    .mobile-home-counter-label {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }

    /* mobile home screen: long quick link labels wrap inside the tile instead of widening it */
    .mobile-home-link {
        min-width: 0;
        overflow-wrap: break-word;
    }

    /* mobile home screen: counters and quick links share one card style - white cards on a light-grey page,
       icon badge top-left, label underneath (counters add their number top-right) */
    /* light grey under the whole home screen (the content area fills the screen below the header), not just behind the card */
    /* body.mobile-home-active is set by openMenu (Index.js) while the home screen is open - a plain class instead of
       :has(#mobile-home-notifications), which made every DOM change on every page re-check body/cards (measured ~20ms per change) */
    body.mobile-home-active #kt_content {
        background-color: #F5F8FA;
    }

    body.mobile-home-active #kt_content_container > .card {
        background-color: transparent;
        box-shadow: none;
    }

    .mobile-home-tile {
        display: block;
        background: #fff;
        border: 1px solid #EEF0F4;
        border-radius: 12px;
        padding: 14px;
        box-shadow: 0 1px 3px rgba(24, 28, 50, 0.06);
        text-align: left;
    }

    .mobile-home-skeleton.mobile-home-skeleton-badge {
        width: 40px;
        height: 40px;
        border-radius: 10px;
    }

    /* mobile home screen: skeleton counter cards shown while the counter API is loading */
    /* pulses opacity (not a moving gradient): opacity runs on the GPU compositor, so the placeholder doesn't
       repaint every frame while the phone is busy loading the page */
    .mobile-home-skeleton {
        border-radius: 4px;
        background-color: #E9ECF1;
        animation: mobileHomeSkeleton 1.2s ease-in-out infinite alternate;
    }

    .mobile-home-skeleton-number {
        width: 38%;
        height: 24px;
    }

    .mobile-home-skeleton-label {
        width: 75%;
        height: 12px;
    }



    .ag-paging-button {
        display: flex !important;
        align-items: center;
        justify-content: center;
        width: 36px !important;
        height: 36px !important;
        background-color: #F5F8FA !important;
        border-radius: 6px !important;
    }

    .ag-paging-button .ag-icon {
        color: #7E8299 !important;
    }

    /* narrow grids (e.g. inside a modal): wrap the row count onto its own line instead of squeezing the buttons over the text */
    .ag-paging-panel {
        flex-wrap: wrap;
        height: auto !important;
        min-height: var(--ag-header-height);
        row-gap: 4px;
    }

    .ag-paging-page-summary-panel, .ag-paging-button {
        flex-shrink: 0;
    }

    /* mobile toasts: compact dark "pill" at the top right with a coloured dot for the type (success/warning/error/info).
       message only - title and close button are hidden, tap to dismiss. div.toast outranks toastr's/Metronic's !important toast styles.
       each rule also names the toastr build in plugins.bundle_1.js (#toastr-container / .toastr-*) - pages on that bundle got the stock toast */
    #toast-container.toast-top-right, #toast-container.toast-top-center,
    #toastr-container.toast-top-right, #toastr-container.toast-top-center {
        top: 12px;
        left: 12px;
        right: 12px;
        width: auto;
        display: flex;
        flex-direction: column;
        align-items: center; 
    }

    #toast-container > div.toast, #toastr-container > div.toastr {
        position: relative;
        width: auto !important;
        max-width: 92% !important;
        margin: 0 0 8px;
        padding: 9px 16px 9px 42px !important;
        background-color: #1E1E2D !important;
        background-image: none !important;
        color: #fff;
        opacity: 1 !important;
        border-radius: 22px !important; /* one line = full pill, longer messages wrap with rounded corners */
        box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25) !important;
        animation: mobileToastIn .25s ease-out;
    }

    #toast-container > div.toast::before, #toastr-container > div.toastr::before {
        position: absolute;
        left: 11px;
        top: 50%;
        width: 22px;
        height: 22px;
        margin-top: -11px;
        border-radius: 50%;
        color: #fff;
        font-size: 12px;
        font-weight: 800;
        line-height: 22px;
        text-align: center;
    }

    #toast-container > div.toast-success::before, #toastr-container > div.toastr-success::before { content: '\2713'; background-color: #50CD89; }
    #toast-container > div.toast-warning::before, #toastr-container > div.toastr-warning::before { content: '!'; background-color: #FFC700; }
    #toast-container > div.toast-error::before, #toastr-container > div.toastr-error::before { content: '\2715'; background-color: #F1416C; }
    #toast-container > div.toast-info::before, #toastr-container > div.toastr-info::before { content: 'i'; background-color: #009EF7; }

    #toast-container > div.toast .toast-title, #toast-container > div.toast .toast-message,
    #toastr-container > div.toastr .toastr-title, #toastr-container > div.toastr .toastr-message {
        font-size: 13px;
        font-weight: 600;
        line-height: 1.35;
    }

    /* a toast with both a title ("Done!") and a message shows just the message */
    /* toastr appends the title before the message (and the mobile pill has no close button or progress bar),
       so a title that isn't the last child has a message after it - no :has() needed */
    #toast-container > div.toast .toast-title:not(:last-child), #toastr-container > div.toastr .toastr-title:not(:last-child) {
        display: none;
    }

    #toast-container > div.toast .toast-close-button, #toast-container > div.toast .toast-progress,
    #toastr-container > div.toastr .toastr-close-button, #toastr-container > div.toastr .toastr-progress {
        display: none !important;
    }


    /* start-up skeleton (option 6): while the app starts after login, the full-screen loader shows a blue bar running along the
       top over a grey outline of the home screen (Index.cshtml .startup-skeleton) instead of the spinner. Index.js removes
       .startup-loader the first time the loader is hidden, so every later use of the loader keeps the spinner. only two
       moving layers (the bar and one light band sweeping across the whole outline), both transform-only: the GPU runs them
       even while start-up keeps the main thread busy */
    .body-blocker.startup-loader .body-spinner,
    .body-blocker:not(.startup-loader) .startup-skeleton {
        display: none !important;
    }

    .startup-skeleton {
        position: absolute;
        inset: 0;
        overflow: hidden;
        background-color: #F5F8FA;
    }

    .startup-skeleton::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        z-index: 2;
        width: 35%;
        height: 3px;
        border-radius: 2px;
        background-color: #009EF7;
        animation: startupBar 1.2s ease-in-out infinite;
    }

    .startup-skeleton::after {
        content: '';
        position: absolute;
        inset: 0;
        z-index: 1;
        background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, 0.6) 50%, transparent 70%);
        transform: translateX(-100%);
        animation: startupShimmer 1.5s ease-in-out infinite;
    }

    /* same sizes as the home screen (header 55px, tiles 101px, 16px sides, 10px gaps) so it swaps in without a jump */
    .startup-skeleton-header {
        display: flex;
        align-items: center;
        gap: 10px;
        height: 55px;
        padding: 0 15px 0 5px;
        border-top: 3px solid rgba(0, 158, 247, 0.12);
        background-color: #fff;
        box-shadow: 0 1px 6px rgba(24, 28, 50, 0.06);
    }

    .startup-skeleton-header i {
        width: 38px;
        height: 38px;
        border-radius: 10px;
        background-color: #E9EDF3;
    }

    .startup-skeleton-header span {
        flex: 1;
    }

    .startup-skeleton-tiles {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 10px;
        padding: 26px 16px 0;
    }

    .startup-skeleton-label + .startup-skeleton-tiles {
        padding-top: 12px;
    }

    .startup-skeleton-tiles > div {
        position: relative;
        height: 101px;
        border: 1px solid #EEF0F4;
        border-radius: 12px;
        background-color: #fff;
    }

    .startup-skeleton-tiles > div::before,
    .startup-skeleton-tiles > div::after {
        content: '';
        position: absolute;
        left: 15px;
        background-color: #E9EDF3;
    }

    .startup-skeleton-tiles > div::before {
        top: 15px;
        width: 40px;
        height: 40px;
        border-radius: 6px;
    }

    .startup-skeleton-tiles > div::after {
        bottom: 18px;
        width: 60%;
        height: 10px;
        border-radius: 5px;
    }

    .startup-skeleton-label {
        width: 90px;
        height: 10px;
        margin: 22px 16px 0;
        border-radius: 5px;
        background-color: #E4E8EE;
    }

    /* "reduce motion" devices (also many remote / multi-session Windows desktops): no sliding, a slow fade pulse instead so
       it never looks frozen */
    @media (prefers-reduced-motion: reduce) {
        .startup-skeleton::after {
            display: none;
        }

        .startup-skeleton::before {
            width: 100%;
            animation: none;
        }

        .startup-skeleton-tiles,
        .startup-skeleton-label {
            animation: startupPulse 1.4s ease-in-out infinite;
        }
    }

    /* mobile header (option A): every header button becomes a soft rounded square with one icon colour - same buttons, order and actions */
    #kt_header {
        box-shadow: 0 1px 6px rgba(24, 28, 50, 0.06) !important;
    }

    #kt_header .btn.btn-icon.w-30px {
        width: 38px !important;
        height: 38px !important;
        border-radius: 11px !important;
        background-color: #F3F5F9 !important;
    }

    #kt_header .btn.btn-icon.w-30px i,
    #kt_header .btn.btn-icon.w-30px .svg-icon {
        color: #3F4254 !important;
        margin-right: 0 !important; /* some icons carry me-2, which pushed them off-centre */
    }

    #kt_header .btn.btn-icon.w-30px i.fs-2x {
        font-size: 1.35rem !important;
    }

    /* the offline warning keeps its warning colour */
    #kt_header #offline_button i {
        color: #FFC700 !important;
    }

    /* home button in the brand blue while the home screen is showing */
    body.mobile-home-active #kt_header #kt_aside_mobile_home {
        background-color: #E8F6FF !important;
    }

    body.mobile-home-active #kt_header #kt_aside_mobile_home i {
        color: #009EF7 !important;
    }

    /* ===== form sections S1 (section cards) - START. CSS only; delete down to "form sections S1 - END" to revert.
       each collapsible form section (.accordion-item-repeatable) becomes a white rounded card, the arrow becomes a round chevron
       on the right, and the open section's title turns blue with its fields inside the card. same sections, same tap to open/close,
       same show/hide rules. no overflow:hidden on the card, so dropdown panels inside fields (e.g. tree view) are never cut off */
    .accordion-item-repeatable {
        background-color: #fff;
        border: 1px solid #EEF0F4;
        border-radius: 14px;
        box-shadow: 0 1px 3px rgba(24, 28, 50, 0.06);
        margin-bottom: 10px !important;
    }

    .accordion-item-repeatable > .accordion-header {
        min-height: 54px;
        align-items: center;
        padding: 0 16px !important;
        margin-bottom: 0 !important;
        cursor: pointer;
    }

    /* title first, chevron button pushed to the right */
    .accordion-item-repeatable > .accordion-header > .fs-4 {
        order: 1;
        flex: 1;
        min-width: 0;
        margin-left: 0 !important;
        font-size: 14.5px !important;
        font-weight: 700;
        color: #181C32;
    }

    .accordion-item-repeatable > .accordion-header > .accordion-icon {
        order: 2;
        flex: none;
        width: 28px;
        height: 28px;
        margin-left: 10px;
        border-radius: 50%;
        background-color: #F3F5F9;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .accordion-item-repeatable > .accordion-header > .accordion-icon i {
        font-size: 11px !important;
        color: #7E8299 !important;
    }

    .accordion-item-repeatable > .accordion-header > .accordion-icon i::before {
        content: "\f054"; /* chevron-right instead of arrow-right */
    }

    .accordion-item-repeatable > .accordion-header > .accordion-icon i {
        transition: transform .2s ease;
    }

    /* points down while the section is open */
    .accordion-item-repeatable > .accordion-header:not(.collapsed) > .accordion-icon i {
        transform: rotate(90deg);
    }

    /* the open section */
    .accordion-item-repeatable > .accordion-header:not(.collapsed) {
        border-bottom: 1px solid #F1F1F4;
    }

    .accordion-item-repeatable > .accordion-header:not(.collapsed) > .fs-4 {
        color: #009EF7;
    }

    .accordion-item-repeatable > .accordion-header:not(.collapsed) > .accordion-icon {
        background-color: #E8F6FF;
    }

    .accordion-item-repeatable > .accordion-header:not(.collapsed) > .accordion-icon i {
        color: #009EF7 !important;
    }

    /* the section's fields sit inside the card. the padding goes on the inner wrapper, not on .collapse/.collapsing itself:
       Bootstrap animates that element's height from 0, and padding on it made the open/close animation start late and jump at the end */
    .accordion-item-repeatable > .collapse > div,
    .accordion-item-repeatable > .collapsing > div {
        padding: 14px 16px 2px;
    }

    /* an existing style also puts ~20px top padding on the animated element - remove it so the height animates cleanly from 0 */
    .accordion-item-repeatable > .collapse,
    .accordion-item-repeatable > .collapsing {
        padding-top: 0 !important;
        padding-bottom: 0 !important;
    }
    /* ===== form sections S1 (section cards) - END ===== */

    /* ===== sidebar N1 (Android / Material 3 drawer) - START. CSS only; delete everything down to "sidebar N1 - END" to revert
       (the previous design F is saved in Documents\Mobile-Home-UI\sidebar-F-block.css).
       rounded drawer edge on a soft blue-grey background, pill-shaped search and rows, the open module as a light-blue pill.
       same accordion, search, Quick Links, permissions and taps - only the look of the mobile drawer changes */
    #kt_aside.drawer {
        /* Metronic sets "width: 250px !important" inline, which no stylesheet can beat - min-width still wins over width */
        min-width: min(85vw, 320px) !important;
        background-color: #F7F9FC !important;
        border-radius: 0 18px 18px 0;
    }

    #kt_aside .aside-logo {
        background-color: transparent;
        border-bottom: 0 !important;
    }

    #kt_aside #menu-search {
        background-color: #E9EEF5;
        border: 0;
        border-radius: 23px;
        min-height: 46px;
        font-size: 16px; /* 16px keeps iPhones from zooming in when the search is tapped */
    }

    #kt_aside_menu_wrapper > .menu {
        padding: 0 10px;
    }

    /* the separator after Quick Links becomes a thin divider between Quick Links and the modules */
    #kt_aside_menu_wrapper > .menu > .menu-separator .separator {
        border-color: #E1E5EC !important;
        margin: 6px 14px !important;
    }

    /* no divider lines between rows: custom.css draws a border-bottom under every sidebar link, which curves on the rounded pills */
    #kt_aside .aside-menu .menu .menu-item .menu-link,
    #kt_aside .aside-menu .menu .menu-item .menu-content {
        border-bottom: 0 !important;
    }

    /* module rows (and the Quick Links group, which sits one level deeper): 50px pills */
    #kt_aside_menu_wrapper > .menu > .menu-item > .menu-link,
    #kt_aside #quick-links-menu > .menu-item > .menu-link {
        min-height: 50px;
        align-items: center !important; /* the menu links otherwise align to the top (flex-start) */
        margin: 0 !important;
        padding: 0 16px !important;
        border-radius: 25px !important;
        font-size: 14.5px;
        font-weight: 500;
    }

    #kt_aside_menu_wrapper > .menu > .menu-item > .menu-link .menu-icon i,
    #kt_aside #quick-links-menu > .menu-item > .menu-link .menu-icon i {
        color: #5E6278 !important;
    }

    /* custom.css pushes every menu icon down 4px (.menu-icon i { padding-top: 4px !important }) - fine for the desktop
       sidebar's top-aligned rows, but here the rows are centred, so it left each icon ~3px below its label */
    /* the 1px lift lines the icon up with the centre of the label's capital letters (measured from the drawn glyphs:
       centred boxes left the icon 0.4-0.9px below the caps, which reads as "text higher than icon" on Title Case labels) */
    #kt_aside_menu_wrapper .menu-icon i {
        padding-top: 0 !important;
        position: relative;
        top: -1px;
    }

    /* icons were pinned to the left of their 1.3rem column, and Font Awesome glyphs range from ~7px ($) to ~16px (laptop)
       wide, so their centres wandered ~4px from row to row - centred in the column (like fa-fw) they line up vertically */
    #kt_aside_menu_wrapper .menu-icon {
        justify-content: center;
    }

    /* the open module: light-blue pill with dark-blue text and icon */
    #kt_aside_menu_wrapper > .menu > .menu-item.show > .menu-link,
    #kt_aside #quick-links-menu > .menu-item.show > .menu-link {
        background-color: #D6ECFF !important;
    }

    #kt_aside_menu_wrapper > .menu > .menu-item.show > .menu-link .menu-title,
    #kt_aside_menu_wrapper > .menu > .menu-item.show > .menu-link .menu-icon i,
    #kt_aside #quick-links-menu > .menu-item.show > .menu-link .menu-title,
    #kt_aside #quick-links-menu > .menu-item.show > .menu-link .menu-icon i {
        color: #003A63 !important;
        font-weight: 700;
    }

    /* sub-menu items: indented pills, the current page highlighted */
    #kt_aside_menu_wrapper .menu-sub {
        background-color: transparent !important;
    }

    /* the rows were flush, so an open sub-module's pill and the highlighted page under it (or the module pill and its
       first highlighted row) ran together into one block - a small gap keeps each pill separate */
    #kt_aside_menu_wrapper .menu-sub .menu-link {
        margin-top: 4px;
        min-height: 42px;
        align-items: center !important;
        border-radius: 21px !important;
        font-size: 13.5px;
    }

    #kt_aside_menu_wrapper .menu-sub .menu-link.active {
        background-color: #E9F4FF !important;
    }

    #kt_aside_menu_wrapper .menu-sub .menu-link.active .menu-title {
        color: #003A63 !important;
        font-weight: 600;
    }
    /* ===== sidebar N1 (Android / Material 3 drawer) - END ===== */

    /* mobile sidebar: no scrollbar thumb, like a phone app's drawer - it still scrolls by swiping */
    #kt_aside.drawer {
        scrollbar-width: none;
    }

    #kt_aside.drawer::-webkit-scrollbar {
        display: none;
    }

    /* ⋮ user menu (option B): the same dropdown shown as a bottom sheet over a dimmed screen - same items, actions and open/close.
       matched by its w-275px class (the only header dropdown with it) - a :has(a[onclick*="logout"]) here slowed down every page
       the huge box-shadow spread is the dimmed backdrop; tapping outside still closes the menu as before */
    #kt_header .menu-sub-dropdown.w-275px.show {
        position: fixed !important;
        inset: auto 0 0 0 !important;
        transform: none !important;
        width: 100% !important;
        max-height: 85vh;
        overflow-y: auto;
        margin: 0 !important;
        padding: 22px 12px 16px !important;
        border-radius: 20px 20px 0 0 !important;
        box-shadow: 0 0 0 100vmax rgba(24, 28, 50, 0.35), 0 -8px 24px rgba(0, 0, 0, 0.15) !important;
        animation: mobileSheetUp .22s ease-out;
    }

    /* grab handle */
    #kt_header .menu-sub-dropdown.w-275px.show::before {
        content: '';
        position: absolute;
        top: 8px;
        left: 50%;
        width: 40px;
        height: 4px;
        margin-left: -20px;
        border-radius: 2px;
        background: #DEE2E6;
    }

    #kt_header .menu-sub-dropdown.w-275px .menu-item > .menu-link {
        min-height: 46px;
        border-radius: 10px;
        font-size: 14px;
        font-weight: 600;
    }

    /* user block at the top of the sheet: avatar badge, name, email, organization as a small pill */
    #kt_header .menu-sub-dropdown.w-275px .mobile-user-badge > .menu-content {
        gap: 12px;
        padding: 4px 40px 14px 6px !important; /* right: room for the X */
        border-bottom: 1px solid #F1F1F4;
        margin-bottom: 6px;
    }

    #kt_header .menu-sub-dropdown.w-275px .mobile-user-badge > .menu-content::before {
        content: "\f007"; /* user */
        flex: none;
        width: 46px;
        height: 46px;
        border-radius: 13px;
        background-color: #E8F6FF;
        color: #009EF7;
        font-family: "Font Awesome 5 Free";
        font-weight: 900;
        font-size: 18px;
        line-height: 46px;
        text-align: center;
    }

    #kt_header .menu-sub-dropdown.w-275px .mobile-user-badge > .menu-content > .d-flex {
        flex: 1;
        min-width: 0;
    }

    #kt_header .menu-sub-dropdown.w-275px .badge-username {
        font-size: 16px !important;
        font-weight: 700 !important;
        color: #181C32;
        line-height: 1.3;
    }

    #kt_header .menu-sub-dropdown.w-275px .badge-email {
        font-size: 12.5px !important;
        color: #7E8299 !important;
        overflow-wrap: anywhere;
    }

    #kt_header .menu-sub-dropdown.w-275px .badge-organization {
        align-self: flex-start;
        margin-top: 5px;
        padding: 2px 10px;
        border-radius: 12px;
        background-color: #E8F6FF;
        color: #0079BF !important;
        font-size: 11.5px !important;
        font-weight: 600 !important;
    }

    /* the divider is now the border under the user block */
    #kt_header .menu-sub-dropdown.w-275px .separator.mobile-user-badge {
        display: none !important;
    }

    /* an icon badge in front of each item - CSS only (Font Awesome 5, already loaded), so the menu markup and its
       translated text are untouched; each item is matched by the id it already has */
    #kt_header .menu-sub-dropdown.w-275px .settings-menu > .menu-link::before {
        flex: none;
        width: 32px;
        height: 32px;
        margin-right: 12px;
        border-radius: 9px;
        background-color: #F1F4F8;
        color: #7E8299;
        font-family: "Font Awesome 5 Free";
        font-weight: 900;
        font-size: 13px;
        line-height: 32px;
        text-align: center;
    }

    #kt_header #myProfile_menu > .menu-link::before { content: "\f007"; }          /* user */
    #kt_header #change-password-menu::before { content: "\f084"; }                 /* key */
    #kt_header #settings_menu > .menu-link::before { content: "\f1ad"; }           /* building */
    #kt_header #pricing_menu > .menu-link::before { content: "\f02c"; }            /* tags */
    #kt_header #billing_menu > .menu-link::before { content: "\f570"; }            /* file-invoice */
    #kt_header #product-selector-button > .menu-link::before { content: "\f009"; } /* th-large */
    #kt_header #support_menu > .menu-link::before { content: "\f059"; }            /* question-circle */
    #kt_header #signOut_menu > .menu-link::before { content: "\f2f5"; background-color: #fff; color: #F1416C; } /* sign-out-alt */

    /* Sign Out as a clear red button at the end */
    #kt_header #signOut_menu {
        margin-top: 8px;
    }

    #kt_header .menu-sub-dropdown.w-275px a[onclick*="logout"] {
        justify-content: center;
        background-color: #FFF5F8 !important;
        color: #F1416C !important;
        font-weight: 700;
    }

    /* custom view picker: same bottom-sheet look as the ⋮ menu above - same elements, show/hide (inline display) and tap handling */
    .cdOptionContainer {
        z-index: 9100; /* above the fixed header (9000) so the dimmed backdrop covers it too */
        max-height: 85vh;
        padding: 22px 12px 16px;
        border-top: 0;
        border-radius: 20px 20px 0 0;
        box-shadow: 0 0 0 100vmax rgba(24, 28, 50, 0.35), 0 -8px 24px rgba(0, 0, 0, 0.15);
        animation: mobileSheetUp .22s ease-out;
    }

    /* grab handle, same as the ⋮ sheet */
    .cdOptionContainer > .downSlider {
        position: absolute;
        top: 8px;
        left: 50%;
        width: 40px;
        height: 4px;
        margin-left: -20px;
        border-radius: 2px;
        background-color: #DEE2E6;
    }

    /* close (X) button at the top right of both bottom sheets (⋮ menu and view picker) */
    .mobile-sheet-close {
        position: absolute;
        top: 12px;
        right: 12px;
        z-index: 1;
        width: 32px;
        height: 32px;
        padding: 0;
        border: 0;
        border-radius: 16px;
        background-color: #F1F4F8;
        color: #7E8299;
        font-size: 14px;
        line-height: 32px;
        text-align: center;
    }

    .mobile-sheet-close > i {
        color: inherit;
        font-size: inherit;
    }

    .mobile-sheet-close:active {
        background-color: #E4E6EF;
    }

    .cdOptionContainer > .cdOptionLabel {
        margin: 0 0 6px !important;
        padding: 4px 40px 12px 6px !important; /* right: room for the X */
        border-bottom: 1px solid #F1F1F4;
        color: #181C32;
        font-size: 16px;
        font-weight: 700;
    }

    .cdOptionContainer .dropdownList {
        max-height: calc(85vh - 90px);
        scrollbar-width: none;
    }

    .cdOptionContainer .dropdownList::-webkit-scrollbar {
        display: none;
    }

    .cdOptionContainer .dropdownOpt {
        display: flex;
        align-items: center;
        min-height: 46px;
        padding: 0 12px !important;
        border-radius: 10px;
        color: #3F4254;
        font-size: 14px;
        font-weight: 600;
    }

    .cdOptionContainer .dropdownOpt:active {
        background-color: #F5F8FA;
    }

    /* selected view: blue tint with a check at the end */
    .cdOptionContainer .dropdownOpt.optActive {
        background-color: #E8F6FF;
        color: #009EF7;
    }

    .cdOptionContainer .dropdownOpt.optActive::after {
        content: "\f00c"; /* check */
        margin-left: auto;
        padding-left: 12px;
        font-family: "Font Awesome 5 Free";
        font-weight: 900;
        font-size: 13px;
    }

    /* animates "translate" (not "transform", which is pinned to none above to cancel the dropdown's own positioning) */

    /* selection bar (option A): white bar with a soft shadow, grey round ✕, the count, and a labelled "Actions ▲" button.
       same elements and taps (Index.cshtml #button_tray, shown/hidden by grid-builder.js) */
    .bottom-button-tray {
        align-items: center;
        gap: 10px;
        padding: 10px 12px 10px;
        background-color: #fff;
        box-shadow: 0 -6px 18px rgba(24, 28, 50, 0.08);
    }

    /* the "+" add button (DynamicPage.js, appended to body after #button_tray) sits at bottom:60px - flush on the 60px bar,
       or over its Actions button when the bar is taller - lift it above the bar while rows are selected */
    #button_tray[style*="flex"] ~ .floating-button {
        bottom: 72px;
    }

    #mobile_rows_unselect_button {
        flex: none;
        width: 36px !important;
        height: 36px !important;
        border-radius: 18px;
        background-color: #F1F4F8 !important;
        color: #7E8299 !important;
    }

    #mobile_rows_unselect_button i {
        color: #7E8299 !important;
        font-size: 14px;
    }

    #mobile_rows_selected {
        text-align: left;
        color: #3F4254;
        font-size: 13.5px !important;
        font-weight: 600 !important;
    }

    #open_popup_button {
        flex: none;
        gap: 8px;
        width: auto !important;
        height: 40px !important;
        padding: 0 16px !important;
        border-radius: 10px;
        font-size: 13.5px;
        font-weight: 600;
    }

    #open_popup_button i {
        color: #fff;
        font-size: 11px;
    }

    /* actions menu (option B): a bottom sheet of 3-column icon tiles - same buttons, icons, order, show/hide (inline
       display set per selection by grid-builder.js) and taps. custom.css flattens every div in this modal
       (display: contents), so the sheet parts are set back explicitly */
    #buttons-modal .modal-dialog {
        display: flex;
        align-items: flex-end;
        min-height: 100%;
    }

    #buttons-modal .modal-content {
        width: 100%;
        max-height: 85vh;
        max-height: 85dvh;
        padding: 0 12px 12px !important;
        border-radius: 20px 20px 0 0;
    }

    #buttons-modal .modal-content > .buttons-sheet-header {
        display: flex !important;
        flex: none;
        align-items: center;
        padding: 0 2px 10px;
        margin-bottom: 10px;
        border-bottom: 1px solid #F1F1F4;
    }

    #buttons-modal .buttons-sheet-title {
        color: #181C32;
        font-size: 16px;
        font-weight: 700;
    }

    #buttons-modal .buttons-sheet-header > .mobile-sheet-close {
        position: static;
        margin-left: auto;
    }

    #buttons-modal .modal-content > .modal-body {
        display: grid !important;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 8px;
        padding: 0;
        overflow-y: auto;
        scrollbar-width: none;
    }

    #buttons-modal .buttons-body > a.btn:not([style*="none"]) {
        display: flex !important;
    }

    #buttons-modal .buttons-body > a.btn {
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 7px;
        min-height: 78px;
        margin: 0 !important;
        padding: 10px 4px !important;
        border-radius: 12px;
        background-color: #F5F8FA !important;
        color: #3F4254 !important;
        font-size: 11.5px;
        font-weight: 600;
        line-height: 1.25;
        text-align: center;
        white-space: normal;
        overflow-wrap: anywhere;
    }

    #buttons-modal .buttons-body > a.btn:active {
        background-color: #E8F6FF !important;
    }

    #buttons-modal .buttons-body > a.btn > i {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 34px;
        height: 34px;
        margin: 0 !important;
        padding: 0;
        border-radius: 17px;
        background-color: #fff;
        color: #009EF7 !important;
        font-size: 14px;
    }

    /* the ✕ in the header closes the sheet, so the separate Cancel button isn't needed here */
    #buttons-modal .cancel-btn {
        display: none !important;
    }

    /* mobile forms (option F): white, outlined fields with the label above - CSS only, same controls and behaviour.
       scoped to .fv-row (the wrapper form-builder puts around every form control) so other inputs on the page aren't touched */
    .fv-row > label {
        color: #5E6278;
        font-size: 13px !important;
        margin-bottom: 6px;
    }

    .fv-row .form-control.form-control-solid,
    .fv-row .form-select.form-select-solid,
    .fv-row .select2-container .select2-selection {
        background-color: #fff !important;
        border: 1px solid #DDE1E9 !important;
        border-radius: 10px !important;
        min-height: 46px;
        color: #181C32;
        font-size: 16px !important; /* add-on 2: 16px or more stops iPhones zooming in when a field is tapped */
    }

    .fv-row .form-control.form-control-solid:focus,
    .fv-row .form-select.form-select-solid:focus,
    .fv-row .select2-container--focus .select2-selection,
    .fv-row .select2-container--open .select2-selection {
        border-color: #009EF7 !important;
        box-shadow: 0 0 0 3px rgba(0, 158, 247, 0.12) !important;
    }

    /* disabled fields keep a grey fill so they still read as not editable */
    .fv-row .form-control.form-control-solid:disabled,
    .fv-row .form-select.form-select-solid:disabled {
        background-color: #F5F8FA !important;
    }

    /* add-on 1: Save / Cancel stay visible at the bottom while a long form scrolls */
    /* the bar floats over the scrolling form (soft shadow instead of a hairline). fixed bottom padding, no
       env(safe-area-inset-bottom): the app already sits above the phone's nav bar, so that inset doubled the gap */
    .buttons-div {
        position: sticky;
        bottom: 0;
        z-index: 4; /* above the field icons (3), below an open tree-view list (inline z-index 5) */
        background: #fff;
        box-shadow: 0 -6px 16px rgba(24, 28, 50, 0.06);
        padding: 10px 0 10px;
        margin-bottom: 0 !important;
    }

    /* the form popup's bottom padding (pb-15 = 3.75rem) would leave the sticky buttons floating ~50px above the bottom edge:
       sticky offsets are measured inside that padding, so the bar sticks 3.75rem lower, and the form wrapper is pulled down
       over the padding so the bar also reaches the edge at the end of the scroll. replaces a :has(.buttons-div) on the
       modal body (re-checked on every form change, and it stopped this file being minified) */
    .modal-body.pb-15 > .modal-form-parent {
        margin-bottom: -3.75rem;
    }

    .modal-body.pb-15 > .modal-form-parent .buttons-div {
        bottom: -3.75rem;
    }

    /* the bar spans the full width of its card / popup instead of stopping at their side padding; the buttons keep a
       1rem gutter so they don't touch the screen edge */
    .p-4 > .buttons-div,                                     /* custom pages, e.g. Availability Calendar (p-4 = 1rem) */
    .card-body > form > .buttons-div {                       /* form-builder page forms (card-body padding 1rem) */
        margin-left: -1rem;
        margin-right: -1rem;
        padding-left: 1rem;
        padding-right: 1rem;
    }

    .modal-body.px-10 > .modal-form-parent > form > .buttons-div { /* form-builder popup forms (px-10 = 2.5rem) */
        margin-left: -2.5rem;
        margin-right: -2.5rem;
        padding-left: 1rem;
        padding-right: 1rem;
    }

    /* action buttons (option B "soft tint"): 46px thumb-sized buttons, 14px text; Save stays solid blue, Cancel/Clear
       keeps its red as a light tint so it no longer competes with Save. same buttons, text, icons and actions */
    .buttons-div > .btn {
        flex: 1;
        padding: 12.5px 16px !important;
        border-radius: 10px;
        font-size: 14px;
        font-weight: 600;
        transition: transform .1s ease;
    }

    /* short pressed feedback - a GPU transform, no repaint */
    .buttons-div > .btn:active {
        transform: scale(0.98);
    }

    /* Metronic forces solid red on hover/focus/press with !important, so every state is covered - otherwise the button
       would flash solid red when tapped */
    .buttons-div > .btn.btn-danger,
    .buttons-div > .btn.btn-danger:hover:not(.btn-active),
    .buttons-div > .btn.btn-danger:focus:not(.btn-active),
    .buttons-div > .btn.btn-danger.active {
        background-color: #FFF5F8 !important;
        border-color: #FFF5F8 !important;
        color: #F1416C !important;
    }

    .buttons-div > .btn.btn-danger:active:not(.btn-active) {
        background-color: #FFE2EA !important;
        border-color: #FFE2EA !important;
        color: #F1416C !important;
    }

    .buttons-div > .btn.btn-danger i,
    .buttons-div > .btn.btn-danger:hover:not(.btn-active) i,
    .buttons-div > .btn.btn-danger:focus:not(.btn-active) i,
    .buttons-div > .btn.btn-danger:active:not(.btn-active) i {
        color: #F1416C !important;
    }

    /* ===== scan popups (Search Asset Codes #scan-log-modal, Count Number of Tags #count-tag-modal) - START: their
       hand-written markup (Index.cshtml) gets the same mobile look as the forms - white outlined field, soft buttons,
       checkbox row card, full-width Clear / Done. CSS only: same elements, ids and actions ===== */

    /* the card inside the popup padded the content twice (popup + card px-10) - the content uses the popup's width */
    :is(#scan-log-modal, #count-tag-modal) .modal-body > .card {
        box-shadow: none;
        margin-bottom: 0 !important;
    }

    :is(#scan-log-modal, #count-tag-modal) .modal-body > .card > .card-body {
        padding: 6px 0 4px !important;
    }

    #scan-log-modal label[for="scan-log"] {
        display: flex;
        align-items: center;
        margin-bottom: 6px;
        color: #5E6278;
        font-size: 13px;
        font-weight: 600;
    }

    /* the scan code empties the count when nothing is scanned yet - the chip still shows 0 */
    #scan-log-modal #scan-count:empty::before {
        content: '0';
    }

    #scan-log-modal #scan-count {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 24px;
        height: 22px;
        margin-left: 4px;
        padding: 0 8px;
        border-radius: 11px;
        background-color: #E8F6FF;
        color: #009EF7;
        font-size: 12px;
        font-weight: 700;
    }

    /* scan log: white outlined field like the form fields; 16px text stops iPhones zooming in */
    #scan-log-modal #scan-log.form-control {
        margin-bottom: 12px !important;
        padding: 10px 12px;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
        background-color: #fff;
        color: #181C32;
        font-size: 16px;
    }

    #scan-log-modal #scan-log.form-control:focus {
        border-color: #009EF7;
        box-shadow: 0 0 0 3px rgba(0, 158, 247, 0.12);
    }

    /* Hide / Open Scan Log: a soft secondary button instead of solid purple (display stays inline, set by scan-log.js) */
    #scan-log-modal :is(#open-scan-button, #close-scan-button) {
        width: auto !important;
        align-self: flex-start;
        margin-bottom: 14px !important;
        padding: 8px 16px !important;
        border: 0;
        border-radius: 9px;
        background-color: #F1F4F8 !important;
        color: #5E6278 !important;
        font-size: 13px;
        font-weight: 600;
    }

    /* "Scanning HEX codes?": a checkbox row card, same as form checkboxes; the label is linked to the box, so its empty
       ::after stretched over the card makes the whole row tappable */
    #scan-log-modal .modal-body .card-body > div > .form-check {
        position: relative;
        display: flex;
        align-items: center;
        min-height: 50px;
        margin: 0;
        padding: 4px 12px;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
        background-color: #fff;
    }

    #scan-log-modal .modal-body .card-body > div > .form-check > .form-check-input {
        float: none;
        flex: none;
        order: 2;
        width: 22px;
        height: 22px;
        margin: 0 0 0 auto;
        border: 2px solid #C4CAD4;
        border-radius: 7px;
        background-color: #fff;
    }

    #scan-log-modal .modal-body .card-body > div > .form-check > .form-check-input:checked {
        border-color: #009EF7;
        background-color: #009EF7;
    }

    #scan-log-modal .modal-body .card-body > div > .form-check > .form-check-label {
        color: #3F4254;
        font-size: 13px;
        font-weight: 600;
    }

    #scan-log-modal .modal-body .card-body > div > .form-check > .form-check-label::after {
        content: '';
        position: absolute;
        inset: 0;
    }

    #scan-log-modal .modal-body .card-body > div > .form-check > .form-check-input:checked + .form-check-label::before {
        content: '';
        position: absolute;
        inset: -1px;
        border: 1px solid #9FD8FB;
        border-radius: 10px;
        background-color: rgba(0, 158, 247, 0.06);
        pointer-events: none;
    }

    /* fixed-reader dropdown: same outlined look as the form dropdowns, with room before "Scanned" */
    #scan-log-modal #dropdown-fixed-reader {
        margin-bottom: 14px;
    }

    #scan-log-modal #dropdown-fixed-reader .select2-container .select2-selection {
        min-height: 46px;
        border: 1px solid #DDE1E9 !important;
        border-radius: 10px !important;
        background-color: #fff !important;
        font-size: 16px !important;
    }

    /* RFID reader choice (handheld / fixed reader): two equal option cards side by side (the chosen one tinted blue) and
       the fixed reader's Start / Stop on its own full-width row under both. a grid while index.js shows the row
       (inline display:flex); #FixedStartBtn (always shown on mobile, Index.js) is unwrapped so its card and button
       become grid items */
    #scan-log-modal #rfidImg[style*="flex"] {
        display: grid !important;
        grid-template-columns: 1fr 1fr;
        align-items: stretch !important;
        gap: 10px;
        margin-bottom: 14px !important;
    }

    #scan-log-modal #rfidImg #FixedStartBtn {
        display: contents !important;
    }

    #scan-log-modal #rfidImg .form-check {
        position: relative;
        margin: 0 !important;
        padding: 10px 10px 10px 36px;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
        background-color: #fff;
        font-size: 12px;
        line-height: 1.35;
    }

    #scan-log-modal #rfidImg #start-stop-btn {
        grid-column: 1 / -1;
        margin-left: 0 !important;
    }

    #scan-log-modal #rfidImg #start-stop-btn > .btn {
        width: 100%;
        padding: 12.5px 16px !important;
        border-radius: 10px;
        font-size: 14px;
        font-weight: 600;
    }

    #scan-log-modal #rfidImg .form-check-input {
        position: absolute;
        top: 12px;
        left: 10px;
        margin: 0;
    }

    #scan-log-modal #rfidImg .form-check-input:checked + .form-check-label::before {
        content: '';
        position: absolute;
        inset: -1px;
        border: 1px solid #9FD8FB;
        border-radius: 10px;
        background-color: rgba(0, 158, 247, 0.06);
        pointer-events: none;
    }

    /* Clear / Done: the same two full-width 46px buttons as the forms' Cancel / Save - Clear as a soft red tint */
    :is(#scan-log-modal, #count-tag-modal) .text-center.mt-10 {
        display: flex;
        gap: 10px;
        margin-top: 18px !important;
    }

    :is(#scan-log-modal, #count-tag-modal) .text-center.mt-10 > .btn {
        flex: 1;
        margin: 0 !important;
        padding: 12.5px 16px !important;
        border-radius: 10px;
        font-size: 14px;
        font-weight: 600;
    }

    :is(#scan-log-modal, #count-tag-modal) .text-center.mt-10 > .btn.btn-danger,
    :is(#scan-log-modal, #count-tag-modal) .text-center.mt-10 > .btn.btn-danger:hover:not(.btn-active),
    :is(#scan-log-modal, #count-tag-modal) .text-center.mt-10 > .btn.btn-danger:focus:not(.btn-active) {
        border-color: #FFF5F8 !important;
        background-color: #FFF5F8 !important;
        color: #F1416C !important;
    }

    :is(#scan-log-modal, #count-tag-modal) .text-center.mt-10 > .btn.btn-danger:active:not(.btn-active) {
        border-color: #FFE2EA !important;
        background-color: #FFE2EA !important;
    }

    /* ===== scan popups - END ===== */

    /* add-on 3: bigger dropdown (Select2) rows and search box, easy to hit with a thumb */
    .select2-container .select2-results__option {
        padding: 11px 14px;
        font-size: 15px;
    }

    .select2-dropdown .select2-search__field {
        min-height: 40px;
        font-size: 16px !important;
    }

    /* typing inside a multi-select: 16px too, so iPhones don't zoom in (size unchanged otherwise) */
    .fv-row .select2-search--inline .select2-search__field {
        font-size: 16px !important;
    }

    /* file, image and signature controls: same rounded corners and border colour as the outlined fields */
    .fv-row .image-input.border-dashed {
        border: 1.5px dashed #C9CED9 !important;
        border-radius: 10px !important;
        background-color: #fff;
    }

    .fv-row .image-input .image-input-wrapper {
        border-radius: 10px;
    }

    .fv-row canvas.signature-pad {
        border: 1px solid #DDE1E9 !important;
        border-radius: 10px !important;
        background-color: #fff;
    }

    /* "Attach files" (same button, same action) shown as a full-width dashed field */
    .fv-row .dropzone-select.btn-primary {
        width: 100%;
        min-height: 46px;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-right: 0 !important;
        background-color: #fff !important;
        color: #009EF7 !important;
        border: 1.5px dashed #C9CED9 !important;
        border-radius: 10px !important;
        font-size: 14px !important;
    }

    /* tapped / focused / hovered: Metronic turns .btn-primary solid blue (with !important), which hid the blue text -
       keep it readable with a light-blue pressed look instead */
    .fv-row .dropzone-select.btn.btn-primary:hover:not(.btn-active),
    .fv-row .dropzone-select.btn.btn-primary:focus:not(.btn-active),
    .fv-row .dropzone-select.btn.btn-primary:active:not(.btn-active),
    .fv-row .dropzone-select.btn.btn-primary.active {
        background-color: #E8F6FF !important;
        border-color: #009EF7 !important;
        color: #009EF7 !important;
    }

    /* add-on 4: more room around checkboxes, radios and switches so they aren't mis-tapped */
    .fv-row .form-check {
        min-height: 36px;
        margin-right: 18px;
        align-items: center;
    }

    /* ===== forms option 1 - START. icons inside the fields + restyled controls. CSS only (plus a tree-view placeholder
       set once in form-builder.js); same fields, order, data, validation and icon-button actions. delete down to
       "forms option 1 - END" to revert. performance: static styles only, no animations; the only :has() rules (extra
       icon-button room) look at sibling buttons, so they re-check only when buttons are added, never on show/hide ===== */

    /* one spacing rhythm: form-builder groups fields two per "row g-8 mb-8" (side by side on desktop, stacked here), and
       the group's mb-8 added an extra 26px after every second field (13 / 39 / 65px gaps) - now 16px after every field */
    .row.g-8.mb-8 {
        margin-bottom: 0 !important;
    }

    .row.g-8.mb-8 > .fv-row,
    #separate-cal-div > .fv-row { /* Availability Calendar's hand-written fields (fv-row added on mobile in its JS) */
        margin-bottom: 16px !important;
    }

    /* the icon buttons next to a field (barcode, map pin, clear ✕, + add) sit inside the field's right end, so every field
       is full width and all right edges line up. the buttons are positioned against their wrapper; fields keep their own
       border, so focus and validation colours are unchanged */
    .fv-row > .mb-2,
    .fv-row > .mb-2 > div:not(.treeview) {
        position: relative;
        width: 100%;
        margin-bottom: 0 !important;
    }

    .fv-row > .mb-2 > .select2-container,
    .fv-row > .mb-2 > div > .select2-container {
        width: 100% !important;
        flex: 1 1 auto;
    }

    .fv-row .mb-2 .form-control ~ a.btn.btn-icon,
    .fv-row .mb-2 .select2-container ~ a.btn.btn-icon {
        position: absolute;
        right: 6px;
        bottom: 6px;
        z-index: 3;
        width: 34px !important;
        height: 34px !important;
        margin: 0 !important;
        border-radius: 8px;
        background-color: transparent !important;
        color: #7E8299 !important;
    }

    .fv-row .mb-2 .form-control ~ a.btn.btn-icon i,
    .fv-row .mb-2 .select2-container ~ a.btn.btn-icon i {
        color: #7E8299 !important;
        font-size: 15px !important;
    }

    /* the barcode icon is a large 3x glyph - same size as the others */
    .fv-row .mb-2 .form-control ~ a.btn.btn-icon.btn-barcode-scan i,
    .fv-row .mb-2 .select2-container ~ a.btn.btn-icon.btn-barcode-scan i {
        font-size: 20px !important;
    }

    /* dropdown with a barcode button (form-builder.js gives its select a "scanner..." id): the button sat on top of the
       dropdown's ▾ - the ▾ moves left of it, as for "+ add new" below */
    .fv-row .mb-2 select[id^="scanner"] + .select2-container .select2-selection {
        padding-right: 84px !important;
        background-position: right 50px center !important;
    }

    /* its clear ✕ (shown once a value is picked) stays left of the moved ▾ instead of on top of it */
    .fv-row .mb-2 select[id^="scanner"] + .select2-container .select2-selection--clearable {
        padding-right: 96px !important;
    }

    .fv-row .mb-2 select[id^="scanner"] + .select2-container .select2-selection__clear {
        right: 74px !important;
    }

    /* ...with an info "i" (form-builder.js wraps the barcode field, so the button lands outside the field's 100% wrapper
       and was squeezed off its right edge): it sits inside the field left of the barcode, and while it's shown (a value
       is picked) the ▾ and ✕ move left of it */
    .fv-row > .mb-2:has(> div > select[id^="scanner"]) > a.btn.btn-icon.view-info-button {
        position: absolute;
        right: 40px;
        bottom: 6px;
        z-index: 3;
        width: 34px !important;
        height: 34px !important;
        margin: 0 !important;
        border-radius: 8px;
        background-color: transparent !important;
    }

    .fv-row > .mb-2:has(> div > select[id^="scanner"]) > a.btn.btn-icon.view-info-button i {
        margin: 0 !important;
        color: #7E8299 !important;
        font-size: 15px !important;
    }

    .fv-row > .mb-2:has(> a.view-info-button:not([style*="none"])) select[id^="scanner"] + .select2-container .select2-selection {
        padding-right: 130px !important;
        background-position: right 84px center !important;
    }

    .fv-row > .mb-2:has(> a.view-info-button:not([style*="none"])) select[id^="scanner"] + .select2-container .select2-selection__clear {
        right: 108px !important;
    }

    /* field with a validation message (form-builder.js fills its .invalid-feedback, and empties it once fixed): red label,
       red border with a light tint on the field (text box, dropdown or tree picker), and the message gets a "!" icon */
    .fv-row:has(> .invalid-feedback > div) > label {
        color: #F1416C !important;
    }

    .fv-row:has(> .invalid-feedback > div) > :is(.mb-2, .select2-container) :is(.form-control, .select2-selection),
    .fv-row:has(> .invalid-feedback > div) > .form-control {
        border-color: #F1416C !important;
        background-color: #FFF8FA !important;
        box-shadow: 0 0 0 3px rgba(241, 65, 108, 0.10) !important;
    }

    /* sub-form cell: its column name is the cell's ::before (no <label>), and a tree picker sits one div down (no .mb-2) */
    div.sub-form .sub-form-row.row > .sub-form-row-cell:has(> .invalid-feedback > div)::before {
        color: #F1416C;
    }

    .sub-form-row-cell:has(> .invalid-feedback > div) > div:not(.invalid-feedback) > .form-control {
        border-color: #F1416C !important;
        background-color: #FFF8FA !important;
        box-shadow: 0 0 0 3px rgba(241, 65, 108, 0.10) !important;
    }

    .fv-row > .invalid-feedback > div {
        display: flex;
        align-items: flex-start;
        gap: 6px;
        margin-top: 2px;
        font-size: 12.5px;
        font-weight: 500;
        line-height: 1.4;
        text-align: left;
    }

    .fv-row > .invalid-feedback > div::before {
        content: '!';
        flex: none;
        width: 15px;
        height: 15px;
        margin-top: 1px;
        border-radius: 50%;
        background-color: #F1416C;
        color: #fff;
        font-size: 10px;
        font-weight: 800;
        line-height: 15px;
        text-align: center;
    }

    /* room for the icon so typed text doesn't run under it */
    .fv-row .mb-2 > .form-control:not(:last-child),
    .fv-row .mb-2 > div > .form-control:not(:last-child) {
        padding-right: 44px !important;
    }

    /* "+ add new" next to a dropdown: stays blue, after a thin divider; the dropdown's own ▾ moves left of it */
    .fv-row .mb-2 select.dropdown-with-add-icon ~ a.btn.btn-icon,
    .fv-row .mb-2 select.dropdown-with-add-icon ~ a.btn.btn-icon i {
        color: #009EF7 !important;
    }

    .fv-row .mb-2 select.dropdown-with-add-icon ~ a.btn.btn-icon::before {
        content: '';
        position: absolute;
        left: -4px;
        top: 6px;
        bottom: 6px;
        width: 1px;
        background-color: #EEF0F4;
    }

    .fv-row .mb-2 select.dropdown-with-add-icon + .select2-container .select2-selection {
        padding-right: 84px !important;
        background-position: right 50px center !important;
    }

    /* tree-view fields look like pickers: a tree icon, the "Select an option" placeholder (form-builder.js) and the
       dropdown's ▾; the clear ✕ / barcode sit just left of the ▾ */
    .fv-row .mb-2 > textarea.treeview-input.form-control,
    div.sub-form .sub-form-row-cell > div > textarea.treeview-input.form-control {
        padding-left: 40px !important;
        padding-right: 76px !important;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237E8299' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 4v14h5M5 9h5'/%3E%3Crect x='10' y='6' width='9' height='6' rx='1.5'/%3E%3Crect x='10' y='15' width='9' height='6' rx='1.5'/%3E%3C/svg%3E"), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%237E8299' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3E%3C/svg%3E") !important;
        background-repeat: no-repeat !important;
        background-position: left 13px center, right 14px center !important;
        background-size: 17px 17px, 12px 12px !important;
    }

    .fv-row .mb-2 > textarea.treeview-input ~ a.btn.btn-icon {
        right: 34px;
    }

    /* a field with more than one icon button (tree-view clear ✕ + info i / + add / edit ✎): each shown button sits left of
       the one before it instead of on top of it. form-builder.js / treeview.js hide them with an inline display:none, so
       only the shown ones count */
    .fv-row .mb-2 .form-control ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]),
    .fv-row .mb-2 .select2-container ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) {
        right: 40px;
    }

    .fv-row .mb-2 .form-control ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]),
    .fv-row .mb-2 .select2-container ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) {
        right: 74px;
    }

    .fv-row .mb-2 > textarea.treeview-input ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) {
        right: 68px;
    }

    .fv-row .mb-2 > textarea.treeview-input ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) {
        right: 102px;
    }

    /* the info button is shown with an inline display:block (treeview.js / form-builder.js), which drops the btn-icon
       centring - keep its "i" centred like the other buttons */
    .fv-row .mb-2 a.btn.btn-icon.view-info-button:not([style*="none"]) {
        display: flex !important;
    }

    /* room for the extra buttons so the text doesn't run under them - counts the buttons the field has (not which are
       shown), so it only re-checks when buttons are added, never on show/hide */
    .fv-row .mb-2 > textarea.treeview-input.form-control:has(~ a.btn.btn-icon ~ a.btn.btn-icon),
    div.sub-form .sub-form-row-cell > div > textarea.treeview-input.form-control:has(~ a.btn.btn-icon ~ a.btn.btn-icon) {
        padding-right: 110px !important;
    }

    .fv-row .mb-2 > textarea.treeview-input.form-control:has(~ a.btn.btn-icon ~ a.btn.btn-icon ~ a.btn.btn-icon) {
        padding-right: 144px !important;
    }

    /* multi-select dropdown (select-all ✓ + clear-all ✕): the dropdown's ▾ moves left of both buttons */
    .fv-row .mb-2 > .select2-container:has(~ a.btn.btn-icon ~ a.btn.btn-icon) .select2-selection {
        padding-right: 118px !important;
        background-position: right 84px center !important;
    }

    .fv-row .mb-2 > textarea.treeview-input::placeholder,
    div.sub-form .sub-form-row-cell > div > textarea.treeview-input::placeholder {
        color: #A1A5B7;
    }

    /* tree-view picker: same look as the dropdown panel (form-builder.js gives it inline square/grey-border styles) */
    .fv-row .treeview.ag-theme-material {
        margin-top: 50px !important;
        border: 0 !important;
        border-radius: 12px;
        box-shadow: 0 8px 24px rgba(24, 28, 50, 0.14);
        --ag-font-family: Poppins, Helvetica, sans-serif;
        --ag-font-size: 14px;
        --ag-row-border-color: transparent;
        --ag-selected-row-background-color: #E8F6FF;
        --ag-row-hover-color: #F5F8FA;
    }

    /* in a popup the form is its own scroll box, so a floating (absolute) list near the bottom was cut off at the popup's
       edge; on a page form it floated over the sticky Save bar and ran off screen. in both the open list sits in the flow
       under its field instead - it pushes the fields below down and the popup / page grows to fit. the field's icon
       buttons are pinned to the field's top (its wrapper now also holds the list). not on dashboards: their filter widgets
       have a fixed height. :has(> textarea...) only looks at the wrapper's own children, so it re-checks only when those
       are added */
    .modal-body .fv-row > .mb-2:has(> textarea.treeview-input),
    .card-body > form .fv-row > .mb-2:has(> textarea.treeview-input) {
        flex-wrap: wrap;
    }

    .modal-body .fv-row > .mb-2 > .treeview.ag-theme-material,
    .card-body > form .fv-row > .mb-2 > .treeview.ag-theme-material {
        position: relative !important;
        flex: 0 0 100%;
        width: 100% !important;
        margin-top: 6px !important;
    }

    .modal-body .fv-row .mb-2 > textarea.treeview-input ~ a.btn.btn-icon,
    .card-body > form .fv-row .mb-2 > textarea.treeview-input ~ a.btn.btn-icon {
        top: 6px;
        bottom: auto;
    }

    /* switch rows (label + switch on one line): a row card like the other fields, label in the normal label style */
    .fv-row.d-flex {
        align-items: center;
        justify-content: space-between;
        /* the card's padding replaces the column's gutter padding, so it is inset by the gutter instead - same left/right
           edges as the other fields */
        width: calc(100% - var(--bs-gutter-x));
        margin-left: calc(var(--bs-gutter-x) * .5);
        margin-right: calc(var(--bs-gutter-x) * .5);
        min-height: 52px;
        padding: 4px 12px;
        background-color: #fff;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
    }

    .fv-row.d-flex label.fs-6 {
        margin-bottom: 0;
        color: #3F4254;
        font-size: 13px !important;
        font-weight: 600 !important;
    }

    /* the label takes the free space, the switch keeps its own width on the right (it stretched to 330px and squeezed
       the label onto two lines) */
    .fv-row.d-flex > div:first-child {
        flex: 1 1 auto;
        min-width: 0;
    }

    .fv-row.d-flex > label.form-check {
        flex: none;
        width: auto !important;
        margin-right: 0;
        gap: 8px;
    }

    .fv-row.d-flex .form-check-label {
        color: #7E8299;
        font-size: 12px;
        font-weight: 500 !important;
    }

    /* ===== checkboxes option A - START: the whole row is tappable, clearer checkbox / bigger switch, rows that are on get a
       light blue tint. CSS only - the control is a <label> around its input (form-builder.js), so stretching an empty
       pseudo-element of that label over the row makes a tap anywhere on the row toggle it, exactly like tapping the
       control. same inputs, values, rules and save. scoped to .fv-row, so grid / tree-view checkboxes are untouched ===== */

    /* checkbox: white with a clear outline instead of the faint grey fill; checked keeps Metronic's blue + tick */
    .fv-row .form-check-custom.form-check-solid:not(.form-switch) .form-check-input {
        width: 22px;
        height: 22px;
        border: 2px solid #C4CAD4;
        border-radius: 7px;
        background-color: #fff !important; /* custom.css forces #d0d0d4 !important on every solid checkbox */
    }

    .fv-row .form-check-custom.form-check-solid:not(.form-switch) .form-check-input[type="radio"] {
        border-radius: 50%;
    }

    .fv-row .form-check-custom.form-check-solid:not(.form-switch) .form-check-input:checked {
        border-color: #009EF7;
        background-color: #009EF7 !important;
    }

    /* switch: phone-sized 46x28 */
    .fv-row .form-switch.form-check-custom.form-check-solid .form-check-input {
        width: 46px;
        height: 28px;
        border-radius: 14px;
        background-color: #DDE1E9 !important;
    }

    .fv-row .form-switch.form-check-custom.form-check-solid .form-check-input:checked {
        background-color: #009EF7 !important;
    }

    /* the switch's extra "Yes" text is hidden (the switch already says it); its element is kept for the tint below */
    .fv-row.d-flex > label.form-switch > .form-check-label {
        font-size: 0;
        margin: 0;
    }

    .fv-row.d-flex > label.form-check {
        gap: 0;
    }

    /* the row's label is centred: form-builder.js gives labels without help text an inline margin-bottom ...!important
       (can't be overridden), which pushed them up - an inline label ignores vertical margins */
    .fv-row.d-flex > div:first-child > label.fs-6 {
        display: inline;
    }

    /* whole row tappable: the label's empty ::after covers the row card */
    .fv-row.d-flex {
        position: relative;
    }

    .fv-row.d-flex > label.form-check {
        position: static;
    }

    .fv-row.d-flex > label.form-check::after {
        content: '';
        position: absolute;
        inset: 0;
    }

    /* on: light blue tint + border over the row card (drawn from the element right after the ticked input) */
    .fv-row.d-flex > label.form-check > .form-check-input:checked + .form-check-label::before {
        content: '';
        position: absolute;
        inset: -1px;
        border: 1px solid #9FD8FB;
        border-radius: 10px;
        background-color: rgba(0, 158, 247, 0.06);
        pointer-events: none;
    }

    /* checkbox / radio lists (dropdown "list" type): one card, a row per option with a divider, whole row tappable
       (each option's label is linked to its input), ticked rows tinted */
    .fv-row .list-control {
        overflow: hidden;
        background-color: #fff;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
    }

    .fv-row .list-control > .form-check {
        position: relative;
        gap: 10px;
        min-height: 44px;
        margin: 0 !important;
        padding: 0 12px;
        border-bottom: 1px solid #EFF2F5;
    }

    .fv-row .list-control > .form-check:last-child {
        border-bottom: 0;
    }

    .fv-row .list-control > .form-check > .form-check-label {
        margin: 0;
        color: #3F4254;
        font-size: 14px;
    }

    .fv-row .list-control > .form-check > .form-check-label::after {
        content: '';
        position: absolute;
        inset: 0;
    }

    .fv-row .list-control > .form-check > .form-check-input:checked + .form-check-label::before {
        content: '';
        position: absolute;
        inset: 0;
        background-color: rgba(0, 158, 247, 0.06);
        pointer-events: none;
    }

    /* RFID popup "Scan Log / Connect Bluetooth RFID" (openRfidScannerModal, Index.js - the only radio SweetAlert):
       the same option card as the radio lists above - one row per option, whole row tappable, picked row tinted */
    .swal2-popup .swal2-radio {
        flex-direction: column;
        align-items: stretch;
        overflow: hidden;
        margin: 16px 0 0;
        padding: 0;
        background-color: #fff;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
    }

    .swal2-popup .swal2-radio label {
        display: flex;
        align-items: center;
        gap: 10px;
        min-height: 48px;
        margin: 0;
        padding: 0 14px;
        border-bottom: 1px solid #EFF2F5;
        cursor: pointer;
    }

    .swal2-popup .swal2-radio label:last-child {
        border-bottom: 0;
    }

    .swal2-popup .swal2-radio label:has(input:checked) {
        background-color: rgba(0, 158, 247, 0.06);
    }

    .swal2-popup .swal2-radio input {
        flex: 0 0 auto;
        width: 18px;
        height: 18px;
        margin: 0;
        accent-color: #009EF7;
    }

    .swal2-popup .swal2-radio .swal2-label {
        margin: 0;
        color: #3F4254;
        font-size: 14px;
        text-align: left;
    }

    /* its Cancel / Continue: the same two full-width 46px buttons as the scan popup's Clear / Done - Cancel first, as a
       soft red tint, Continue solid blue (SweetAlert shows the radio box as display:flex only in this popup) */
    .swal2-popup:has(.swal2-radio[style*="flex"]) .swal2-actions {
        flex-wrap: nowrap;
        gap: 10px;
        width: 100%;
        margin-top: 18px;
    }

    .swal2-popup:has(.swal2-radio[style*="flex"]) .swal2-actions > .btn {
        flex: 1;
        margin: 0 !important;
        padding: 12.5px 16px !important;
        border-radius: 10px;
        font-size: 14px;
        font-weight: 600;
    }

    .swal2-popup:has(.swal2-radio[style*="flex"]) .swal2-actions > .swal2-cancel {
        order: -1;
        border-color: #FFF5F8 !important;
        background-color: #FFF5F8 !important;
        color: #F1416C !important;
    }

    .swal2-popup:has(.swal2-radio[style*="flex"]) .swal2-actions > .swal2-cancel:active {
        border-color: #FFE2EA !important;
        background-color: #FFE2EA !important;
    }

    .swal2-popup:has(.swal2-radio[style*="flex"]) .swal2-actions > .swal2-confirm {
        border-color: #009EF7 !important;
        background-color: #009EF7 !important;
        color: #fff !important;
    }

    /* ===== checkboxes option A - END ===== */

    /* help text under a label: quiet grey instead of bold purple-ish */
    .fv-row > .fs-7.fw-bold.text-muted {
        margin: -2px 0 6px;
        color: #A1A5B7 !important;
        font-size: 11.5px !important;
        font-weight: 400 !important;
    }

    /* native date field: softer calendar icon, normal text weight like the other fields */
    .fv-row input[type="date"].form-control {
        font-weight: 400;
    }

    .fv-row input[type="date"]::-webkit-calendar-picker-indicator {
        opacity: 0.5;
    }

    /* image upload: a full-width dashed tile with a soft blue icon badge (same tile, same tap-to-add); its container's
       mt-4 left a bigger label gap than the other fields */
    .fv-row > .d-flex.flex-wrap.gap-3.mt-4 {
        margin-top: 0 !important;
    }

    /* the (still empty) preview list took its own line in the wrapping row, adding a 10px gap above the tile */
    .fv-row > .d-flex.flex-wrap.gap-3 > .d-flex.flex-wrap.gap-3:empty {
        display: none !important;
    }

    .fv-row label.image-input.image-input-empty {
        width: 100% !important;
        height: 96px !important;
        justify-content: center;
        border: 1.5px dashed #C9CED9 !important;
        border-radius: 12px !important;
        background-color: #fff !important;
    }

    .fv-row label.image-input.image-input-empty > i {
        width: 38px;
        height: 38px;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 10px;
        background-color: #E8F6FF;
        color: #009EF7 !important;
        font-size: 18px !important;
    }

    /* signature pad: dashed like the other upload areas (its size is left alone - stretching a canvas with CSS would
       offset the strokes from the finger) */
    .fv-row canvas.signature-pad {
        border: 1.5px dashed #C9CED9 !important;
        border-radius: 12px !important;
        /* block: the clear / save buttons sit together on one row under the pad instead of the clear button hanging
           off the pad's bottom-right corner and save wrapping onto its own line (inline canvas + inline-flex buttons) */
        display: block;
        margin-bottom: 4px;
    }
    /* view mode (e.g. Equipment Details), option A: small grey label above a dark value across the full width, a thin
       divider between fields. form-builder lays each view field out as label | value with an inline display:flex (only
       view mode does - edit fields have none, so they're untouched) and gives every label an inline
       "margin-bottom: min(16px,5%) !important" (except fields with hint text) - in the flex row that margin set the row's
       height, so fields were 26 / 36 / 39 / 55px tall. as a block the label is inline text, where a vertical margin has
       no effect, so every field gets the same 9px above and below. hidden fields keep their inline display:none */
    /* font-size / line-height here too: the label is inline text, so its line takes its height from the field - inside
       sections (whose body is fs-6) fields came out 2px taller than the ones above them */
    .fv-row[style*="display: flex"] {
        display: block !important;
        margin: 0 !important;
        padding: 9px 0;
        border-bottom: 1px solid #EFF2F5;
        font-size: 11.5px;
        line-height: 1.5;
    }

    /* label: also the one yes/no fields wrap in an extra div */
    .fv-row[style*="display: flex"] > label.fs-6,
    .fv-row[style*="display: flex"] > div > label.fs-6 {
        display: inline;
        color: #7E8299;
        font-size: 11.5px !important;
        font-weight: 500 !important;
    }

    .fv-row[style*="display: flex"] > label.fs-5 {
        display: block;
        margin: 2px 0 0 !important;
        color: #181C32 !important;
        font-size: 14px !important;
        font-weight: 500;
        line-height: 1.35;
    }

    /* empty value: a dash instead of a blank gap - also image / file / signature fields, whose view value is a
       div.mb-2 that stays empty when nothing is uploaded */
    .fv-row[style*="display: flex"] > label.fs-5:empty::before,
    .fv-row[style*="display: flex"] > div.mb-2:empty::before {
        content: '\2014';
        color: #B5B5C3;
        font-weight: 400;
    }

    /* form-builder groups view fields two per "row g-8 mb-1" (most sections) or "row g-8 mb-2" (sections it regroups,
       fixViewModeAlignmentSafe) - only view mode creates these rows. no extra gap after a group (the fields' own padding
       and dividers space them evenly), and no negative side margin: Bootstrap's -13px gutter margin pulled the fields
       out to 4px from a section card's edge, while the section title sits 16px in - now both line up */
    .row.g-8.mb-1,
    .row.g-8.mb-2 {
        margin-bottom: 0 !important;
        margin-left: 0;
        margin-right: 0;
    }

    /* empty sub-grids in view mode (option A): a grid with no rows shows only a short dashed "no rows" box - no column
       headers, empty scrollbar or grid-view button. grids with rows are unchanged. keyed on ag-Grid's own no-rows message
       (its text, e.g. "No child assets", is kept), so the :has() rules re-check only when that message appears / goes.
       scoped to the view-only rows above, so edit forms (where empty grids are for adding rows) are untouched */
    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material:has(> .ag-root-wrapper .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper) .ag-header,
    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material:has(> .ag-root-wrapper .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper) .ag-body-horizontal-scroll,
    :is(.row.g-8.mb-1, .row.g-8.mb-2) .mb-2:has(> .ag-theme-material .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper) > button {
        display: none !important;
    }

    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material:has(> .ag-root-wrapper .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper) > .ag-root-wrapper {
        border: 1px dashed #DDE1E9 !important;
        border-radius: 10px;
        background-color: #fff;
    }

    /* Child Assets draws its own border box around the grid */
    :is(.row.g-8.mb-1, .row.g-8.mb-2) .rounded.border:has(> .ag-theme-material .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper) {
        border: 0 !important;
    }

    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material:has(> .ag-root-wrapper .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper) .ag-center-cols-clipper {
        min-height: 44px !important;
    }

    /* the (empty) rows area is still as wide as all the columns, which drew a native scrollbar - nothing to scroll */
    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material:has(> .ag-root-wrapper .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper) .ag-center-cols-viewport {
        overflow-x: hidden !important;
    }

    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material .ag-overlay:not(.ag-hidden) .ag-overlay-no-rows-wrapper {
        justify-content: flex-start;
        padding: 0 12px;
    }

    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material .ag-overlay-no-rows-wrapper > * {
        display: flex;
        align-items: center;
        gap: 9px;
        color: #A1A5B7;
        font-size: 12.5px;
    }

    :is(.row.g-8.mb-1, .row.g-8.mb-2) .ag-theme-material .ag-overlay-no-rows-wrapper > *::before {
        content: "\f03a"; /* list */
        display: flex;
        align-items: center;
        justify-content: center;
        width: 26px;
        height: 26px;
        border-radius: 8px;
        background-color: #F5F8FA;
        color: #B5B5C3;
        font-family: "Font Awesome 5 Free";
        font-weight: 900;
        font-size: 11px;
    }
    /* ===== forms option 1 - END ===== */

    /* sub-form tables (e.g. PO Line Items) scroll sideways inside their own box, but Select2 keeps each original <select>
       hidden as a 1-2px position:absolute element at its column's position; div.sub-form isn't positioned, so those
       escape its scroll clip and stretched the whole page to the table's width (2542px on Purchase Order). anchoring the
       hidden selects at the left keeps them inside the screen - they are invisible and aria-hidden, Select2 only keeps
       the value in them. (making .sub-form position:relative instead would clip its tree-view pickers) */
    div.sub-form select.select2-hidden-accessible {
        left: 0 !important;
    }

    /* ===== sub-form rows as cards (option A) - START: a grid-type sub-form (Reservation Setting, PO Line Items ...) is a
       table of fixed 200px columns that had to be scrolled sideways. on mobile each row is a card instead: row number and
       the same + / ✕ buttons on top, then the fields stacked full width, each with its column name above it (data-label,
       set where the cell is built in form-builder.js - the header row is hidden). card-type sub-form rows have no .row
       class and are not touched. inline styles from form-builder.js need !important ===== */
    div.sub-form {
        display: block !important;
        overflow-x: visible !important;
    }

    div.sub-form > .row:not(.sub-form-row) {
        display: none !important;
    }

    div.sub-form > .all-rows-div {
        margin-top: 0 !important;
    }

    div.sub-form .sub-form-row.row {
        display: grid !important;
        grid-template-columns: minmax(0, 1fr);
        gap: 12px;
        --bs-gutter-x: 0;
        margin: 0 0 10px !important;
        padding: 0 12px 14px;
        border: 1px solid #EEF0F4;
        border-radius: 12px;
        background: #FAFBFC;
    }

    div.sub-form .sub-form-row.row > div:first-child {
        display: flex !important;
        align-items: center;
        gap: 6px;
        width: auto !important;
        min-height: 46px;
        margin: 0 -12px;
        padding: 7px 8px 7px 12px !important;
        border-bottom: 1px solid #F1F1F4;
    }

    div.sub-form .sub-form-row.row > div:first-child > .row-counter {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 26px;
        height: 26px;
        margin: 0 auto 0 0 !important;
        padding: 0 8px;
        border-radius: 13px;
        background: #F1F4F8;
        color: #5E6278;
        font-size: 12px;
        font-weight: 700;
    }


    div.sub-form .sub-form-row.row > div:first-child > .btn.btn-icon {
        width: 32px !important;
        height: 32px !important;
        margin: 0 !important;
        border-radius: 8px;
    }

    /* a cell: its column name (+ red * when required) on the first line, the field full width under it */
    div.sub-form .sub-form-row.row > .sub-form-row-cell {
        display: flex;
        flex-wrap: wrap;
        justify-content: flex-start !important;
        width: auto !important;
        min-width: 0 !important;
        padding: 0;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell::before {
        content: attr(data-label);
        order: -2;
        margin-bottom: 6px;
        color: #3F4254;
        font-size: 13px;
        font-weight: 600;
        line-height: 1.4;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell[data-required]::after {
        content: '*';
        order: -1;
        margin: 0 0 6px 3px;
        color: #F1416C;
        font-size: 13px;
        font-weight: 600;
        line-height: 1.4;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell > * {
        flex: 0 0 100%;
        max-width: 100%;
        min-width: 0;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell > .select2-container {
        width: 100% !important;
    }

    /* switch cell (d-flex, already a switch row card): column name on the left, switch on the right, one line */
    div.sub-form .sub-form-row.row > .sub-form-row-cell.d-flex {
        flex-wrap: nowrap;
        align-items: center;
        padding: 4px 12px;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell.d-flex::before,
    div.sub-form .sub-form-row.row > .sub-form-row-cell.d-flex[data-required]::after {
        margin-bottom: 0;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell.d-flex > label.form-check {
        flex: none;
        margin-left: auto;
    }

    /* tree-view cell: its wrapper has an inline 250px min-width; the open list sits in the flow under the field (like
       the other popup/page fields) instead of floating, and fills the card's width */
    div.sub-form .sub-form-row.row > .sub-form-row-cell > div:not(.invalid-feedback) {
        display: flex !important;
        flex-wrap: wrap;
        position: relative;
        min-width: 0 !important;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell > div > .treeview.ag-theme-material {
        position: relative !important;
        flex: 0 0 100%;
        width: 100% !important;
        min-width: 0 !important;
        margin-top: 6px !important;
    }

    div.sub-form .sub-form-row.row > .sub-form-row-cell .treeview .ag-root-wrapper {
        width: 100%;
    }

    /* its select-all ✓ / clear ✕ sit inside the field's right end, left of the ▾ (same as the other tree-view fields) */
    div.sub-form .sub-form-row-cell > div > textarea.treeview-input ~ a.btn.btn-icon {
        position: absolute;
        top: 6px;
        right: 34px;
        z-index: 3;
        width: 34px !important;
        height: 34px !important;
        margin: 0 !important;
        border-radius: 8px;
        background-color: transparent !important;
    }

    div.sub-form .sub-form-row-cell > div > textarea.treeview-input ~ a.btn.btn-icon i {
        font-size: 15px !important;
    }

    div.sub-form .sub-form-row-cell > div > textarea.treeview-input ~ a.btn.btn-icon:not([style*="none"]) ~ a.btn.btn-icon:not([style*="none"]) {
        right: 68px;
    }
    /* ===== sub-form rows as cards - END ===== */

    /* ===== checklist two-column tiles (Checklist Master) - START: ChecklistMaster.js (mobile only) puts each checklist item's
       fields in one .checklist-item card inside a one-column .checklist-items list instead of the wide table. in the card
       the fields are tiles two per line; file upload / text area / Remarks (.checklist-tile-wide) take the full line. each
       tile shows its column name (data-column) in place of the header row; items are numbered with a CSS counter.
       save / validation / edit values find fields by their own attributes, not by layout ===== */
    .checklist-items {
        gap: 12px;
        counter-reset: checklist-item;
    }

    .checklist-item {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 8px;
        min-width: 0;
        counter-increment: checklist-item;
    }

    .checklist-item + .checklist-item {
        padding-top: 12px;
        border-top: 1px solid #EEF0F4;
    }

    .checklist-item::before {
        content: counter(checklist-item);
        grid-column: 1 / -1;
        justify-self: start;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 24px;
        height: 24px;
        padding: 0 7px;
        border-radius: 12px;
        background: #009EF7;
        color: #fff;
        font-size: 11px;
        font-weight: 700;
    }

    .checklist-tile {
        min-width: 0;
        padding: 10px;
        border: 1px solid #EEF0F4;
        border-radius: 10px;
        background: #fff;
    }

    .checklist-tile-wide {
        grid-column: 1 / -1;
    }

    /* the column name as a small caption over the field's own label */
    .checklist-tile[data-column]::before {
        content: attr(data-column);
        display: block;
        margin-bottom: 2px;
        color: #A1A5B7;
        font-size: 10.5px;
        font-weight: 700;
        letter-spacing: .3px;
        text-transform: uppercase;
        overflow-wrap: anywhere;
    }

    .checklist-tile .fv-row.mt-5 {
        margin-top: 0 !important;
    }

    .checklist-tile .fv-row > label {
        margin-bottom: 6px !important;
        font-size: 13px !important;
        overflow-wrap: anywhere;
    }

    /* group title: ChecklistMaster.js gives it an inline dark underline with 20px padding - lighter over the tiles */
    [data-is-from-checklist-master] > .mb-5 > label {
        padding-bottom: 8px !important;
        border-bottom-color: #EEF0F4 !important;
    }
    /* ===== checklist two-column tiles - END ===== */

    /* mobile card view is recognised by its hidden column header: every card view path sets headerHeight 0, which
       ag-Grid renders as an inline "height: 1px" on .ag-header (the tree-view dropdown grid also hides its header,
       hence :not(.treeview)). plain sibling selectors instead of .ag-root-wrapper:has(.mobileTicketCell) - that :has()
       was re-checked on every card row ag-Grid adds or removes while scrolling (measured ~20% slower per row) */

    /* mobile card view: the card always fits the visible width, so the grid never needs a horizontal scrollbar */
    .ag-theme-material:not(.treeview) .ag-header[style*="height: 1px"] ~ .ag-body-horizontal-scroll {
        display: none !important;
    }

    /* mobile card view: the cards' own scroll box shows no scrollbar - ag-Grid draws the visible one in its separate
       vertical-scroll column, which keeps the thin thumb from the scrollbar rules at the top (2px) */
    .ag-theme-material:not(.treeview) .ag-header[style*="height: 1px"] ~ .ag-body .ag-body-viewport {
        scrollbar-width: none;
    }

    .ag-theme-material:not(.treeview) .ag-header[style*="height: 1px"] ~ .ag-body .ag-body-viewport::-webkit-scrollbar {
        display: none;
    }

    .card-body.grid-component {
        padding-top: 8px !important;
        padding-bottom: 8px !important;
    }


    .mobile-header-row {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        row-gap: 4px;
        width: 100%;
    }

    /* a long page title wraps onto a second line next to the toolbar instead of being cut off (the ellipsis never
       showed: the text is in the inner .card-label, not in this flex box) */
    .mobile-header-row > .card-title {
        min-width: 0;
        flex: 1 1 0; /* 0 basis: the title wraps beside the toolbar instead of pushing it onto its own line */
    }

    /* custom.css caps card titles at 140px (".card .card-header .card-title.flex-column") - here the title takes the
       space the toolbar leaves */
    .card .card-header .mobile-header-row > .card-title.flex-column {
        max-width: none;
    }

    .mobile-header-row > .card-title > .card-label {
        white-space: normal;
        overflow-wrap: anywhere;
        line-height: 1.3;
    }

    .mobile-grid-toolbar {
        display: flex;
        flex-wrap: nowrap;
        align-items: center;
        flex-shrink: 0;
        margin-left: auto;
    }

    .mobile-grid-toolbar > .btn {
        flex-shrink: 0;
    }

    /* ===== list toolbar buttons option A - START: the solid black / blue / yellow / red squares become soft tints - each
       keeps its colour as a light background with a coloured icon (like Cancel/Clear on forms). same buttons, icons, order
       and taps. Metronic re-applies the solid colour on hover / focus / press with !important, so every state is covered.
       delete to "option A - END" to revert ===== */
    .mobile-grid-toolbar > .btn.btn-icon { --tb-bg: #F3F5F9; --tb-bg-press: #E4E6EF; --tb-fg: #5E6278; }
    .mobile-grid-toolbar > .btn.btn-icon.btn-dark { --tb-bg: #EEF0F8; --tb-bg-press: #E1E4F0; --tb-fg: #3F4254; }
    .mobile-grid-toolbar > .btn.btn-icon.btn-primary { --tb-bg: #E8F6FF; --tb-bg-press: #D3EEFF; --tb-fg: #009EF7; }
    .mobile-grid-toolbar > .btn.btn-icon.btn-warning { --tb-bg: #FFF8DD; --tb-bg-press: #FFF0BF; --tb-fg: #E0A800; }
    .mobile-grid-toolbar > .btn.btn-icon.btn-danger { --tb-bg: #FFF5F8; --tb-bg-press: #FFE2EA; --tb-fg: #F1416C; }
    .mobile-grid-toolbar > .btn.btn-icon.btn-success { --tb-bg: #E8FFF3; --tb-bg-press: #D2F7E4; --tb-fg: #1BC5BD; }
    .mobile-grid-toolbar > .btn.btn-icon.btn-info { --tb-bg: #F8F5FF; --tb-bg-press: #EEE5FF; --tb-fg: #7239EA; }

    .mobile-grid-toolbar > .btn.btn-icon,
    .mobile-grid-toolbar > .btn.btn-icon:hover:not(.btn-active),
    .mobile-grid-toolbar > .btn.btn-icon:focus:not(.btn-active),
    .mobile-grid-toolbar > .btn.btn-icon.active {
        border-color: var(--tb-bg) !important;
        border-radius: 9px;
        background-color: var(--tb-bg) !important;
        box-shadow: none !important;
    }

    .mobile-grid-toolbar > .btn.btn-icon:active:not(.btn-active) {
        border-color: var(--tb-bg-press) !important;
        background-color: var(--tb-bg-press) !important;
    }

    /* the filter / view icons get an inline white colour from grid-builder.js / DynamicPage.js */
    .mobile-grid-toolbar > .btn.btn-icon i {
        color: var(--tb-fg) !important;
    }

    /* filter row open (grid-builder.js sets data-show-filter): light blue with a dot */
    .mobile-grid-toolbar > .btn.btn-icon[data-show-filter="true"] {
        --tb-bg: #E8F6FF;
        --tb-bg-press: #D3EEFF;
        --tb-fg: #009EF7;
        position: relative;
    }

    .mobile-grid-toolbar > .btn.btn-icon[data-show-filter="true"]::after {
        content: '';
        position: absolute;
        top: 5px;
        right: 5px;
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background-color: #009EF7;
        box-shadow: 0 0 0 2px #fff;
    }

    /* ===== list toolbar buttons option A - END ===== */

    /* ag-Grid gives this cell asymmetric left/right padding by default */
    .ag-cell.mobileTicketCell {
        padding-left: 8px !important;
        padding-right: 8px !important;
        box-sizing: border-box !important;
    }

    .ag-theme-material:not(.treeview) .ag-header[style*="height: 1px"] ~ .ag-body .ag-body-viewport,
    .ag-theme-material:not(.treeview) .ag-header[style*="height: 1px"] ~ .ag-body .ag-center-cols-viewport {
        overflow-x: hidden !important;
        touch-action: pan-y !important;
    }

    .commonSearchContainer {
        padding-left: 16px;
        padding-right: 16px;
        box-sizing: border-box;
    }

    .card.fb-grid .card-header {
        padding-bottom: 6px !important;
    }

    /* list pages: the grid container sits empty for ~1-2s while the saved views are fetched and ag-Grid is set up
       (search box and view picker already showing above it) - show a spinner there from the first moment instead of a
       blank area. :empty stops matching as soon as ag-Grid inserts the grid, whose own loading row takes over.
       cheap: :empty is only re-checked when this element gets its first child, and the spin is a GPU transform */
    .card.fb-grid > .card-body > .ag-theme-material:empty {
        position: relative;
    }

    /* ag-Grid's own loading row (its icon-font spinner + "Loading") shows the same ring, so the two loading steps look
       like one: hidden icon/text, ring centred in the row. on the first load (a single loading row) the ring sits where
       the set-up spinner below was, so the hand-over doesn't jump */
    .card.fb-grid .ag-row-loading .ag-loading-icon,
    .card.fb-grid .ag-row-loading .ag-loading-text {
        display: none;
    }

    .card.fb-grid .ag-row-loading.ag-row-first.ag-row-last {
        overflow: visible;
    }

    .card.fb-grid .ag-row-loading .ag-loading::before,
    .card.fb-grid > .card-body > .ag-theme-material:empty::before {
        content: '';
        position: absolute;
        top: 56px;
        left: 50%;
        width: 28px;
        height: 28px;
        margin-left: -14px;
        border: 3px solid #E4E6EF;
        border-top-color: #009EF7;
        border-radius: 50%;
        animation: mobileGridSetupSpin .8s linear infinite;
    }

    .card.fb-grid .ag-row-loading .ag-loading::before {
        top: 50%;
        margin-top: -14px;
    }

    .card.fb-grid .ag-row-loading.ag-row-first.ag-row-last .ag-loading::before {
        top: 55px;
        margin-top: 0;
    }

    .commonSearchInput {
        width: 100% !important;
        max-width: none !important;
        margin-left: 0 !important;
    }

    /* grid search + custom view picker as a matched pair, styled like the form fields (option F):
       white, outlined, 44px, a leading icon - CSS only, same elements and behaviour */
    .commonSearchInput.form-control.form-control-solid {
        height: 44px;
        padding: 0 14px 0 40px;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
        background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23A1A5B7' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E") no-repeat 14px center / 16px;
        color: #181C32;
        font-size: 16px; /* 16px or more stops iPhones zooming in when the field is tapped */
        font-weight: 500;
    }

    .commonSearchInput.form-control.form-control-solid::placeholder {
        color: #A1A5B7;
        font-weight: 400;
    }

    .commonSearchInput.form-control.form-control-solid:focus {
        border-color: #009EF7;
        background-color: #fff;
        box-shadow: 0 0 0 3px rgba(0, 158, 247, 0.12);
    }

    .cdBtn {
        align-items: center;
        gap: 10px;
        height: 44px;
        margin: 4px 0 8px;
        padding: 0 12px 0 14px;
        border: 1px solid #DDE1E9;
        border-radius: 10px;
        background-color: #fff;
        box-shadow: none;
        color: #181C32;
        font-size: 14px;
    }

    /* leading "views" icon */
    .cdBtn::before {
        content: "\f0ca"; /* list-ul */
        flex: none;
        color: #A1A5B7;
        font-family: "Font Awesome 5 Free";
        font-weight: 900;
        font-size: 14px;
    }

    .cdBtn > .cdLabel {
        font-weight: 600;
        line-height: 1.2;
    }

    .cdBtn > .cdIcon {
        padding-top: 0 !important;
    }

    .cdBtn > .cdIcon > i {
        padding: 0 !important;
        color: #7E8299 !important;
        font-size: 14px;
    }

    .cdBtn:active {
        background-color: #F5F8FA;
    }


    .modal.fade .modal-dialog {
        transform: translateY(100%);
        transition: transform .3s ease-out;
        margin: 0 !important;
        max-width: 100% !important;
        width: 100% !important;
    }

    .modal.show .modal-dialog {
        transform: translateY(0);
    }

    .modal-content {
        display: flex;
        flex-direction: column;
        max-height: calc(100vh - 50px);
        max-height: calc(100dvh - 50px);
        border-radius: 0;
    }

    /* full-width modal: square corners (Metronic also rounds the header's top corners) */
    .modal-content > .modal-header {
        border-radius: 0;
    }

    .modal-content::before {
        content: '';
        display: block;
        width: 40px;
        height: 4px;
        background: #dee2e6;
        border-radius: 2px;
        margin: 8px auto;
    }

    .modal-content .modal-header,
    .modal-content .modal-footer {
        flex: 0 0 auto;
    }

    .modal-content .modal-body {
        flex: 1 1 auto;
        overflow-y: auto;
    }

    /* Select2 opens its list inside the form (form-builder's dropdownParent) but positions it against the nearest
       positioned box - the popup body, which now scrolls itself, and Select2 doesn't add that scroll: the further the
       form was scrolled, the further from its field a list opened. positioned against the form, it moves with it */
    .modal-content .modal-body form.form {
        position: relative;
    }

}


/* mobile card view (MobileCellRenderer): "spec sheet" card - shaded label column + value column, row colour as a thin top stripe */
.mobile-spec-card {
    background: #fff;
    border: 1px solid #E4E6EF;
    border-top: 3px solid var(--row-color, #C9CBD6);
    border-radius: 10px;
    margin: 6px 2px 10px; /* small gap between cards comes from the top margin; the 10px bottom margin only cancels the -10px the renderer puts on the cell (less cuts the card off) */
    overflow: hidden;
    font-family: Poppins, helvetica, sans-serif;
    -webkit-font-smoothing: subpixel-antialiased;
    color: #333;
    box-shadow: 0 1px 3px rgba(24, 28, 50, 0.04);
}

/* light-grey background behind the cards so the white cards stand apart (the grid's own background is white).
   .mobile-spec-grid is added by MobileCellRenderer; the hidden-header check (card view sets headerHeight 0, rendered as
   "height: 1px") turns it off again when the same grid is switched to table view. no :has() - see the card view rules above */
.mobile-spec-grid .ag-header[style*="height: 1px"] ~ .ag-body .ag-body-viewport,
.mobile-spec-grid .ag-header[style*="height: 1px"] ~ .ag-body .ag-center-cols-viewport,
.mobile-spec-grid .ag-header[style*="height: 1px"] ~ .ag-body .ag-row {
    background-color: #F5F8FA !important;
}

/* selected card: light blue across the whole card (custom.css made it light green, which only showed on the value side
   because the labels have their own background), with a blue outline; the top border keeps the row's status colour.
   the row around it stays on the grey page background - the card itself shows the selection. static styles only */
@media (max-width: 991.98px) {
    .ag-row-selected .mobile-ticket-card {
        background-color: #F1FAFF !important;
        border-color: #009EF7 !important;
    }

    /* the second pixel of the blue outline is drawn just inside the card edge (an outline with a negative offset - painted
       over the label/value backgrounds, adds no size): the earlier outer ring/glow was clipped at the bottom by the cell */
    .ag-row-selected .mobile-spec-card {
        border-top-color: var(--row-color, #009EF7) !important;
        outline: 1px solid #009EF7;
        outline-offset: -2px;
    }

    .ag-row-selected .mobile-spec-card .mobile-card-label {
        background-color: #E3F2FF;
        border-right-color: #D6ECFF;
        color: #0079BF;
    }

    .ag-row-selected .mobile-spec-card .mobile-card-row {
        border-bottom-color: #D6ECFF;
    }
}

.mobile-card-row {
    display: grid;
    grid-template-columns: 42% 1fr;
    border-bottom: 1px solid #EFF2F5;
}

.mobile-card-label {
    background: #F9FAFC;
    border-right: 1px solid #EFF2F5;
    padding: 8px 12px;
    color: #7E8299;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.45;
    white-space: normal; /* ag-Grid cells are nowrap, which kept long labels on one line running into the value column */
    overflow-wrap: anywhere;
}

.mobile-card-value {
    min-width: 0;
    padding: 8px 12px;
    color: #3F4254;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.45;
    text-align: left;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.mobile-card-more {
    padding: 6px;
    border-top: 1px solid #EFF2F5;
}

.mobile-card-more a {
    font-size: 12.5px !important;
}

/* ===== dashboard D2 - START. Dashboards page only (#dashboard-content), CSS only; delete everything down to
   "dashboard D2 - END" to revert. Swipeable pill tabs, widgets as white cards on the grey page, the default number
   tile restyled like the home counters, charts with only the refresh icon, and grid widgets as the list pages' cards
   with a card / table switch - same widgets, data, refresh, drill-down and tabs behaviour ===== */
@media (max-width: 991.98px) {
    #dashboard-content {
        background-color: transparent;
        box-shadow: none;
    }

    /* header: tabs on the left (one swipeable row), the add / edit / delete icons on the right */
    #dashboard-content > .card-header {
        flex-wrap: nowrap;
        align-items: center;
        gap: 8px;
        min-height: 0;
        padding: 10px 12px 6px !important;
    }

    #dashboard-content > .card-header > .fv-row {
        flex: 1;
        min-width: 0;
    }

    #dashboardSelectWrapper .nav-line-tabs {
        flex-wrap: nowrap;
        gap: 8px;
        overflow-x: auto;
        margin-bottom: 0 !important;
        padding: 2px 0 4px;
        border-bottom: 0;
        scrollbar-width: none;
        /* fade the last 28px, so a tab cut off at the edge fades out (hinting "swipe for more") instead of
           leaving a sliver of its rounded border next to the + button */
        -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
        mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    }

    #dashboardSelectWrapper .nav-line-tabs::-webkit-scrollbar {
        display: none;
    }

    #dashboardSelectWrapper .nav-line-tabs .nav-item {
        flex: none;
        margin: 0;
    }

    #dashboardSelectWrapper .nav-line-tabs .nav-item .nav-link {
        display: flex;
        align-items: center;
        height: 34px;
        margin: 0 !important;
        padding: 0 14px !important;
        border: 1px solid #E4E6EF;
        border-radius: 17px;
        background-color: #fff;
        color: #5E6278;
        font-size: 13px;
        font-weight: 600;
        white-space: nowrap;
    }

    #dashboardSelectWrapper .nav-line-tabs .nav-item .nav-link.active {
        border-color: #009EF7;
        background-color: #009EF7;
        color: #fff;
    }

    #dashboard-content > .card-header > .card-toolbar {
        flex: none;
        flex-wrap: nowrap;
        gap: 6px;
        margin: 0;
    }

    #dashboard-content > .card-header > .card-toolbar > .btn.btn-icon {
        width: 32px;
        height: 32px;
        border: 1px solid #E4E6EF;
        border-radius: 9px;
        background-color: #fff;
    }

    #dashboard-content > #widgetBody {
        padding: 0 12px 12px !important;
    }

    /* widgets: white rounded cards instead of the dashed edit-mode border (layout editing is web-only) */
    #dashboards-body .grid-stack-item {
        border: 0;
    }

    #dashboards-body .grid-stack-item > .grid-stack-item-content,
    #dashboards-body > .widget {
        border: 1px solid #EEF0F4;
        border-radius: 12px;
        background-color: #fff;
        box-shadow: 0 1px 3px rgba(24, 28, 50, 0.06);
    }

    /* default number tile (the built-in dark template; widgets with their own HTML keep their look):
       blue icon badge on the left, large number and grey label on the right */
    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark {
        border-radius: 12px;
        background-color: #fff !important;
        box-shadow: none;
    }

    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark > .card-body {
        display: grid;
        grid-template-columns: 46px 1fr;
        grid-template-rows: auto auto;
        align-content: center;
        column-gap: 14px;
        height: 100%;
        padding: 0 16px !important;
    }

    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark .svg-icon {
        grid-row: 1 / span 2;
        align-self: center;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 46px;
        height: 46px;
        border-radius: 13px;
        background-color: #E8F6FF;
    }

    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark .svg-icon svg {
        width: 22px !important;
        height: 22px !important;
    }

    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark .svg-icon svg [fill]:not(g) {
        fill: #009EF7 !important;
    }

    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark .card-title {
        align-self: end;
        margin: 0 !important;
        color: #181C32 !important;
        font-size: 22px !important;
        line-height: 1.15;
    }

    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark .card-body > .fw-bold {
        align-self: start;
        color: #7E8299 !important;
        font-size: 12px !important;
        font-weight: 600 !important;
    }

    /* the refresh icon on that tile was white (for the dark background) - grey on the white tile */
    #dashboards-body .grid-stack-item-content > .card.card-custom.bg-dark ~ .widget-refresh-button-plain {
        color: #A1A5B7;
    }

    /* Taxonomy widget: its Location / Health Status filters have fixed min-widths (250px / 300px inline) that
       pushed the widget wider than small phones - let them shrink to the card */
    #dashboards-body #healthStatus,
    #dashboards-body .grid-stack-item-content div[style*="min-width: 250px"]:not(.treeview) {
        min-width: 0 !important;
    }

    #dashboards-body #locationTaxonomyTvEl {
        max-width: calc(100vw - 48px);
    }

    /* grid widgets: the title becomes a left-aligned card header, and the grid (650px inline) is fitted to the card,
       so it scrolls inside itself with the column headers always visible instead of the whole card scrolling.
       no :has() here on purpose - ag-Grid changes its DOM constantly and a :has() on the widget made every change
       re-check the selector (measured ~5x slower). grid and number widget wrappers have no inline style (chart,
       split-view and taxonomy widgets get one from Dashboards.js), and the grid title row is the un-classed div
       Dashboards.js gives an inline justify-content */
    #dashboards-body .grid-stack-item-content:not([style]) {
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    #dashboards-body .grid-stack-item-content > div:not([class])[style*="justify-content"] {
        flex: none;
        justify-content: flex-start !important;
        margin: 0 !important;
        padding: 12px 44px 10px 14px;
        border-bottom: 1px solid #EFF2F5;
    }

    #dashboards-body .grid-stack-item-content > div:not([class])[style*="justify-content"] h3.card-label {
        margin: 0 !important;
        color: #181C32;
        font-size: 14px !important;
        font-weight: 700 !important;
    }

    #dashboards-body .grid-stack-item-content:not([style]) > .ag-theme-material {
        flex: 1 1 0;
        min-height: 0;
        height: auto !important;
    }

    /* lighter grid: soft header row, muted header text, faint lines */
    #dashboards-body .ag-theme-material .ag-header,
    #dashboards-body .ag-theme-material .ag-header-cell,
    #dashboards-body .ag-theme-material .ag-header-group-cell {
        background-color: #F9FAFC;
        border-color: #EFF2F5;
    }

    #dashboards-body .ag-theme-material .ag-header-cell-text {
        color: #7E8299;
        font-size: 12px;
        font-weight: 600;
    }

    /* headers line up with their values (custom.css centres every grid header with !important, while the values are
       left-aligned); columns ag-Grid marks as right-aligned (numeric) keep their header on the right */
    #dashboards-body .ag-theme-material .ag-header-cell-label {
        justify-content: flex-start !important;
    }

    #dashboards-body .ag-theme-material .ag-right-aligned-header .ag-header-cell-label {
        justify-content: flex-end !important;
    }

    #dashboards-body .ag-theme-material .ag-row {
        border-bottom-color: #F1F1F4;
    }

    #dashboards-body .ag-theme-material .ag-cell {
        border-right-color: #F1F1F4;
        color: #3F4254;
    }

    /* charts: only the refresh icon - ag-Grid's chart menu (settings / download / the side "‹" tab) is hidden on mobile,
       so the refresh icon takes the corner it had to leave free */
    #dashboards-body .ag-chart-menu,
    #dashboards-body .ag-chart-menu-close {
        display: none !important;
    }

    #dashboards-body .widget-refresh-button-chart {
        right: 6px;
    }

    /* grid widgets: card / table switch, left of the refresh icon (Dashboards.js addGridViewToggleButton) */
    #dashboards-body .widget-view-toggle-button {
        position: absolute;
        top: 6px;
        right: 36px;
        z-index: 10;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 26px;
        height: 26px;
        padding: 0;
        border: 0;
        border-radius: 50%;
        background-color: rgba(255, 255, 255, 0.85);
        color: #3F4254;
    }

    /* grouped / pivoted grid widgets: their group cards (Dashboards.js addGridGroupCards, same card classes as the
       list pages) cover the grid, on the same grey background as card view, scrolling inside the widget */
    #dashboards-body .widget-group-cards-host {
        position: relative;
    }

    #dashboards-body .widget-group-cards {
        position: absolute;
        inset: 0;
        z-index: 3;
        overflow-y: auto;
        padding: 2px 8px 6px;
        background-color: #F5F8FA;
        scrollbar-width: none;
    }

    #dashboards-body .widget-group-cards::-webkit-scrollbar {
        display: none;
    }

    #dashboards-body .widget-group-cards .mobile-spec-card {
        margin: 6px 0;
    }
}
/* ===== dashboard D2 - END ===== */

/* animations used by the mobile rules above - kept outside the @media block (same effect, keyframes are global)
   because the server's CSS minifier can't parse @keyframes nested in @media and would serve the whole file unminified */
@keyframes mobileHomeSkeleton {
    from { opacity: 1; }
    to { opacity: .45; }
}

@keyframes mobileToastIn {
    from { transform: translateY(-16px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}

/* animates "translate" (not "transform", which is pinned to none on the sheets to cancel the dropdown's own positioning) */
@keyframes mobileSheetUp {
    from { translate: 0 100%; }
    to { translate: 0 0; }
}

@keyframes mobileGridSetupSpin {
    to { transform: rotate(360deg); }
}

/* start-up skeleton: blue bar along the top, light band sweeping across the outline, fade pulse for "reduce motion" */
@keyframes startupBar {
    from { transform: translateX(-110%); }
    to { transform: translateX(300%); }
}

@keyframes startupShimmer {
    from { transform: translateX(-100%); }
    to { transform: translateX(100%); }
}

@keyframes startupPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

/* activity calendar drag & drop (web and mobile, so outside the mobile block): the day under the dragged activity turns
   light blue. moved here from custom.css - custom.css is bundled by bundleconfig.json, whose minifier fails the build on
   :has(); this file already has :has() selectors, so it costs nothing here */
.activity-calendar.fc .fc-daygrid-day:has(.fc-event-mirror) {
    background-color: #F1FAFF;
}
