/* Hand-written overrides for things the Bootstrap/Metronic bundle doesn't
   define out of the box. Loaded after style.bundle.css so these declarations
   win the cascade without needing !important. */

/* Metronic's own header-avatar demo markup assumes a pre-cropped square
   source image; our uploaded avatars aren't guaranteed square, and a plain
   <img> has no intrinsic size limit inside the 40px topbar icon button
   without this. */
.user-avatar {
    width: 30px;
    height: 30px;
    object-fit: cover;
}

/* Background picker swatches on the Customization tab (see
   inc/account/settings.php) — ported dimensions from _v2's own
   .bg-selector (inc/panels/user-preferences-sidebar.php's picker). */
.bg-selector {
    width: 100px;
    height: 100px;
    border-radius: 4px;
    object-fit: cover;
}

/* Highlights the currently-selected background swatch. Uses outline (not
   border) so it doesn't shift the 100x100 box size or the grid layout. */
.bg-selector-active {
    outline: 3px solid #8950FC;
    outline-offset: 2px;
}

/* The fixed header (#kt_header) has no explicit z-index anywhere in the
   vendor bundle, so its own dropdown menus (z-index: 96 in style.bundle.css)
   don't reliably paint above later-in-DOM page content that establishes its
   own stacking context via position:sticky -- e.g. the Asset Management
   sidebar (amRenderSidebar(), Bootstrap's .sticky-top utility carries
   z-index: 1020) and the Search page's title card (inc/dashboard/search.php,
   z-index: 10) both ended up rendering the header's user-profile dropdown
   underneath themselves. 1030 matches Bootstrap's own .fixed-top convention
   (deliberately above .sticky-top's 1020), so the header and everything it
   contains -- including its dropdowns -- now always wins against any
   sticky/positioned page content using the values already in use here.

   The primary nav bar (#kt_header_menu_wrapper, "Dashboard/Clients/Jobs/..."
   with its own .menu-submenu dropdowns, z-index: 98) is a SEPARATE sibling
   of #kt_header in the markup, not a descendant of it -- elevating #kt_header
   alone doesn't help this one. At desktop widths .header-menu-wrapper is
   `position: relative` with no z-index of its own (only a mobile media
   query gives it one), so it needs the same treatment independently. */
#kt_header,
#kt_header_menu_wrapper {
    z-index: 1030;
}

/* Pagination "jump to page" input (renderPagination() in
   inc/core/functions.php). A plain type="number" <input> doesn't inherit
   .page-link's box model as cleanly as an <a> does (browsers apply their own
   default sizing/line-height to form controls), so it needs its own explicit
   width/height/padding to actually match the surrounding numbered buttons
   instead of rendering visibly wider and taller. Also hides the up/down
   spinner arrows, which look out of place here regardless of size. */
input[data-page-jump] {
    -moz-appearance: textfield;
    width: 2.75rem;
    height: calc(1.25em + 1rem + 2px);
    padding: 0.5rem 0.25rem;
    line-height: 1.25;
    box-sizing: border-box;
}
input[data-page-jump]::-webkit-outer-spin-button,
input[data-page-jump]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Metronic's own .checkbox component (see Manage Groups) renders its box
   (the <span>) flush against the label text with no gap at all — the
   compiled bundle never gives it a margin, even in the theme's own
   reference examples. */
.checkbox > span {
    margin-right: 0.75rem;
}

/* Hides the up/down spin buttons on a type="number" input, for the cases
   where a plain, compact numeric text box reads better than a stepper (e.g.
   the Password Generator's length field). See also the pagination
   "jump to page" input above, which needs the same treatment but is scoped
   to its own [data-page-jump] selector rather than this general-purpose one. */
.no-spinner {
    -moz-appearance: textfield;
}
.no-spinner::-webkit-outer-spin-button,
.no-spinner::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Opt-in sticky table header for long reports/listings — add
   data-sticky-header to the .table-responsive WRAPPER (not the <table>
   itself). Bootstrap's .table-responsive sets overflow-x: auto, which per
   spec forces overflow-y to compute as auto too (never "visible") even
   though nothing overflows it vertically — that silently makes the wrapper
   itself the nearest scrolling ancestor for position: sticky, so a sticky
   thead inside it sticks relative to that div's own box (which never
   scrolls) instead of the page, and just gets covered as the page scrolls.
   Horizontal scrolling is really only needed on narrow/mobile viewports
   where the table is likely to overflow the screen width anyway, so above
   that breakpoint the overflow wrapping is switched off entirely — no
   scrolling ancestor is created, so the header sticks to the real page as
   it scrolls, and there's no extra scroll region to confuse the mouse or
   fight with print/full-screen. Below that breakpoint the table falls back
   to normal (non-sticky) horizontal-scroll behaviour, which is the more
   familiar pattern on small screens anyway. */
[data-sticky-header] thead th {
    position: sticky;
    top: 70px; /* #kt_header's actual rendered height on this page — style.bundle.css's generic .header height (100px) didn't match in practice */
    z-index: 2;
    background-color: #fff;
    /* .table thead th's own border-bottom (style.bundle.css) reads fine
       sitting in normal flow, but loses its visual separation once this row
       is floating detached above the scrolled body — reapplying the exact
       same border here so the stuck header still has a clean edge. */
    border-bottom: 2px solid #EBEDF3;
}
/* Bootstrap tables use border-collapse: collapse, which resolves borders
   against the table's logical grid position — not where a position: sticky
   cell is visually displaced to while floating, so the border-bottom above
   never actually shows up once stuck. Separate border model so each cell
   paints its own border independently of the (moot, once separate)
   collapse resolution; border-spacing: 0 keeps cells visually flush like
   they were before, just without the collapsed-border side effect. */
.table-responsive[data-sticky-header] table {
    border-collapse: separate;
    border-spacing: 0;
}

/* Opt-in slightly-larger base font size for data-dense report tables (add
   data-report-table to the <table>). Everything else in these cells that
   sizes off the parent (Bootstrap's <small>, .text-muted, etc.) is
   percentage-based, not a fixed px/rem value, so it scales up right along
   with this rather than needing its own override. */
[data-report-table] {
    font-size: 1.05rem;
}

/* Full-width "band" section for a report's totals — a sibling of .card-body
   (not nested inside it), so it's already flush with the card's own edges
   and just needs its own padding to match card-body's (2.25rem, see
   .card-body in style.bundle.css) for the content to line up visually.
   Needs its own bottom corner radius since .card doesn't clip its children
   to its rounded corners (no overflow: hidden) and this is meant to be the
   last element in the card. Ported from _v2's invoice-style reports, which
   set their totals section apart from the main table the same way. */
.card-body-band {
    padding: 1.75rem 2.25rem;
    background-color: #F3F6F9;
    border-bottom-left-radius: 0.42rem;
    border-bottom-right-radius: 0.42rem;
}

/* Same overflow-clipping problem as [data-sticky-header] above, but for
   plain hover tooltips (e.g. .status-dot-wrap on the Network Devices list)
   that pop up outside the row's own box -- .table-responsive's overflow-x:
   auto forces overflow-y to compute as auto too, silently clipping/hiding
   anything that rises above the row instead of letting it float on top.
   Opt in with data-allow-tooltip-overflow on the wrapper; same >=992px
   breakpoint (horizontal scroll is only actually needed on narrow/mobile
   viewports anyway). Also applies to .am-switch-panel (Port Layout, same
   file) -- with overflow-x:auto in effect Bootstrap's tooltip "flip"
   modifier treats the panel itself as the nearest scrollable boundary, and
   with barely 16px of padding above/below the port rows it decides there's
   never enough room in the requested direction and flips every tooltip onto
   the OPPOSITE port row instead of respecting data-placement="top"/"bottom" —
   letting the panel's own overflow go visible removes that boundary so the
   requested placement is honored. */
@media (min-width: 992px) {
    .table-responsive[data-allow-tooltip-overflow],
    .am-switch-panel {
        overflow-x: visible;
        overflow-y: visible;
    }
}

@media (min-width: 992px) {
    .table-responsive[data-sticky-header] {
        overflow-x: visible;
        overflow-y: visible;
    }
}

/* Feature badges (Edge/DHCP/Cellular/PoE pills on the Asset Management
   Network View device table) — ported verbatim from _v2's style.custom.css.
   The tooltip is pure CSS (a nested .tooltip span shown on :hover), not a
   JS library, so it works with zero extra init. */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 11px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 500;
    vertical-align: middle;
    cursor: default;
    position: relative;
    transition: filter 0.15s, transform 0.15s;
    user-select: none;
    white-space: nowrap;
}
.pill:hover {
    filter: brightness(0.93);
    transform: translateY(-1px);
}
.pill-icon {
    font-size: 13px;
    display: inline-flex;
    align-items: center;
}
.pill .tooltip {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    background: #2C2C2A;
    color: #FFFFFF;
    font-size: 12px;
    font-weight: 400;
    padding: 6px 10px;
    border-radius: 6px;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s, transform 0.15s;
    transform: translateX(-50%) translateY(4px);
    z-index: 10;
}
.pill .tooltip::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: #2C2C2A;
}
.pill:hover .tooltip {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}
.pill-blue { background: #185FA5; color: #E6F1FB; }
.pill-blue .pill-icon { color: #85B7EB; }
.pill-teal { background: #0F6E56; color: #E1F5EE; }
.pill-teal .pill-icon { color: #5DCAA5; }
.pill-purple { background: #534AB7; color: #EEEDFE; }
.pill-purple .pill-icon { color: #AFA9EC; }
.pill-amber { background: #854F0B; color: #FAEEDA; }
.pill-amber .pill-icon { color: #EF9F27; }

/* Colored status dot + dark tooltip for a tracked Meraki device's live
   Online/Offline/Alerting state (Network Devices list, left of the device
   Label). Same pure-CSS hover-tooltip mechanism as .pill above, just without
   the pill's background/padding since a dot has no label text of its own. */
.status-dot-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
    cursor: default;
    margin-right: 6px;
    vertical-align: middle;
}
.status-dot-wrap .tooltip {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    background: #2C2C2A;
    color: #FFFFFF;
    font-size: 12px;
    font-weight: 400;
    padding: 6px 10px;
    border-radius: 6px;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s, transform 0.15s;
    z-index: 1000;
}
.status-dot-wrap .tooltip::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: #2C2C2A;
}
.status-dot-wrap:hover .tooltip {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}
.status-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
}
.status-dot-success { background: #1dc9b7; }
.status-dot-warning { background: #ffb822; }
.status-dot-danger { background: #fd397a; }
.status-dot-info { background: #6993FF; }
.status-dot-dark { background: #B5B5C3; }

/* Asset Management Network Devices' "Port Layout" visual (below the Ports &
   Connections table) -- a physical switch faceplate look rather than a
   generic grid: ports stacked in vertical pairs (matching how every real
   switch is silkscreened, 1-over-2, 3-over-4, ...) on a dark faceplate
   background, each with its own small link-status LED distinct from the
   port's own fill color, same as real hardware. */
.am-switch-panel {
    background: linear-gradient(180deg, #333840, #1E2126);
    border-radius: 8px;
    padding: 16px 18px;
    display: flex;
    gap: 5px;
    overflow-x: auto;
}
.am-switch-port-pair {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: none;
}
/* Small switches (<=16 ports, see network-devices.php) read better as one
   row than two mostly-empty stacked ones -- ports are direct flex children
   here instead of being wrapped in .am-switch-port-pair columns, and get a
   little more breathing room since there's no second row competing for
   height. */
.am-switch-panel-single-row .am-switch-port {
    width: 38px;
    height: 34px;
}
.am-switch-port {
    position: relative;
    width: 34px;
    height: 26px;
    border-radius: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 600;
    text-decoration: none;
    cursor: default;
}
.am-switch-port-free {
    background: #3D434C;
    border: 1px solid #4E5560;
    color: #9BA2AC;
}
.am-switch-port-empty {
    background: transparent;
    border: 1px dashed #3A3F47;
}
.am-switch-port-used {
    background: #185FA5;
    border: 1px solid #2B78C8;
    color: #E6F1FB;
    cursor: pointer;
    transition: filter 0.15s;
}
.am-switch-port-used:hover {
    filter: brightness(1.2);
    color: #E6F1FB;
}
.am-switch-port-led {
    position: absolute;
    top: 2px;
    right: 3px;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: #545B66;
}
.am-switch-port-led-on {
    background: #3DD598;
    box-shadow: 0 0 3px #3DD598;
}
.am-switch-legend-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 3px;
    vertical-align: middle;
}

/* Ported verbatim from _v2's style.bundle.css .kt-link — a plain inline text
   link (not a button/dropdown-item), colored rather than the browser
   default, with an underline that animates in on hover instead of always
   being visible. Metronic v7 (this app's theme) never carried this class
   forward, so it's reproduced here rather than left behind — same colors,
   same 0.3s width transition, same modifiers. Use plain .kt-link for the
   default color; add a --success/--danger/etc modifier only when the link
   needs to carry that specific meaning (e.g. a destructive action). */
.kt-link {
    text-decoration: none;
    position: relative;
    display: inline-block;
    color: #5d78ff;
}
.kt-link::after {
    display: block;
    content: '';
    position: absolute;
    bottom: 0;
    top: 1rem;
    left: 0;
    width: 0%;
    transition: width 0.3s ease;
}
.kt-link:hover {
    text-decoration: none !important;
    color: #3e5fff;
}
.kt-link:hover::after {
    width: 100%;
    border-bottom: 1px solid #3e5fff;
    opacity: 0.3;
}
.kt-link.kt-link--brand { color: #716aca; }
.kt-link.kt-link--brand:hover { color: #4d44bd; }
.kt-link.kt-link--brand:hover::after { border-bottom-color: #4d44bd; }
.kt-link.kt-link--dark { color: #282a3c; }
.kt-link.kt-link--dark:hover { color: #14151d; }
.kt-link.kt-link--dark:hover::after { border-bottom-color: #14151d; }
.kt-link.kt-link--primary { color: #5867dd; }
.kt-link.kt-link--primary:hover { color: #2e40d4; }
.kt-link.kt-link--primary:hover::after { border-bottom-color: #2e40d4; }
.kt-link.kt-link--success { color: #1dc9b7; }
.kt-link.kt-link--success:hover { color: #179c8e; }
.kt-link.kt-link--success:hover::after { border-bottom-color: #179c8e; }
.kt-link.kt-link--info { color: #5578eb; }
.kt-link.kt-link--info:hover { color: #2754e6; }
.kt-link.kt-link--info:hover::after { border-bottom-color: #2754e6; }
.kt-link.kt-link--warning { color: #ffb822; }
.kt-link.kt-link--warning:hover { color: #eea200; }
.kt-link.kt-link--warning:hover::after { border-bottom-color: #eea200; }
.kt-link.kt-link--danger { color: #fd397a; }
.kt-link.kt-link--danger:hover { color: #fc0758; }
.kt-link.kt-link--danger:hover::after { border-bottom-color: #fc0758; }

/* .kt-link modifier for text that reveals more info via a tooltip (e.g. an
   IP address annotated with knownIP(), inc/core/functions.php) rather than
   a real navigable link — a permanent dashed underline instead of .kt-link's
   default hover-only animated one, plus a "help" cursor, signal "hover for
   more info" without implying "click to navigate". */
.kt-link.kt-link--dashed {
    border-bottom: 1px dashed currentColor;
    cursor: help;
}
.kt-link.kt-link--dashed::after {
    display: none;
}

/* Clearable search inputs (Credentials/Users tabs' live search boxes, and
   any future one) — a small "x" that only appears once the input has a
   value, clears it and re-fires filtering on click. Purely declarative:
   wrap the <input id="foo"> in .search-clearable alongside a sibling
   [data-search-clear-for="foo"] element, and the delegated listeners in
   inc/core/shell-foot.php handle the rest — no per-page JS needed, so this
   works even for lazy-loaded tab content whose own <script> tags never run. */
.search-clearable {
    position: relative;
}
.search-clearable input {
    padding-right: 2rem;
}
.search-clear {
    position: absolute;
    top: 50%;
    right: 0.75rem;
    transform: translateY(-50%);
    cursor: pointer;
    color: #B5B5C3;
    font-size: 1.1rem;
    line-height: 1;
    user-select: none;
}
.search-clear:hover {
    color: #7E8299;
}
/* .form-control-sm (e.g. the Users tab's search box) is shorter than the
   default .form-control — scale the icon and its offset down to match,
   rather than the fixed size above overhanging the smaller input's edges. */
.search-clearable input.form-control-sm {
    padding-right: 1.75rem;
}
.search-clearable input.form-control-sm ~ .search-clear {
    right: 0.5rem;
    font-size: 0.95rem;
}

/* Client Viewer's tab nav (inc/clients/client-viewer.php) — with a full
   permission set, this can run to 14 tabs, which wraps onto a second row on
   narrower viewports. Rather than guess a fixed breakpoint/tab-count that'd
   go stale the moment a label changes or a tab gets added, the page's own
   JS measures whether the nav has actually wrapped (comparing each tab's
   offsetTop) and toggles this class — hide the icon, keep the text, so the
   same number of tabs takes less horizontal room and (usually) fits back
   onto one row. */
.client-viewer-tabs.tabs-compact .nav-icon {
    display: none;
}

/* Shopify Draggable's mirror element (Boarding Steps modal + Processes tab,
   see initProcessSortable()/wireBody()) is appended straight to <body> with
   no z-index of its own (the library's Mirror plugin has no zIndex option —
   confirmed via grep of draggable.bundle.js). style.bundle.css's own
   .draggable-mirror rule (needed for the dashed-border drag visual and to
   hide the original/placeholder — see .draggable--original and
   .draggable-source--is-dragging.draggable--over there) doesn't set one
   either, so with z-index left at auto the mirror painted underneath
   Bootstrap's modal (z-index 1050) and its backdrop (1040) whenever dragging
   inside the Boarding Steps modal. */
.draggable.draggable-mirror {
    z-index: 1060;
}

/* Checklist item description wrapper (inc/clients/boarding.php's User
   Boarding checklists). Needs its own single flex item, and it can't be a
   bare `<span>` — that's `.checkbox > span`'s toggle-glyph selector, which
   clips its content to an 18x18px box (the earlier "garbled checklist" bug).
   .checkbox is `display: flex`, so without this wrapper every inline child
   of the label (the bolded item title, the surrounding description text,
   any inline links) becomes its OWN separate flex item — flex items don't
   preserve the plain-text spacing between them the way normal inline flow
   does, and a long description spread across several such items wraps
   oddly instead of flowing as one paragraph. Wrapping it all in one non-span
   box restores normal inline layout inside that box. */
.checklist-item-text {
    flex: 1 1 auto;
    min-width: 0;
}

/* Processes tab (inc/clients/tabs/processes.php, process-display.php,
   process-manage.php) — the list of Process Lists, the read-only checklist,
   and the Manage & Reorder modal all read a little small at the theme's
   default 1rem for something meant to be followed step-by-step at a glance,
   per explicit feedback. .form-control (style.bundle.css) sets its own
   font-size, so it needs its own override here rather than relying on
   inheritance from the wrapping element alone. */
.process-text-lg,
.process-text-lg .form-control {
    font-size: 1.15rem;
}

/* select2 fields' red-invalid state (see shell-foot.php's global .is-invalid
   validation-styling script) — select2 hides the real <select> (opacity: 0,
   style.bundle.css) and inserts its own widget as the very next sibling, so
   .is-invalid landing on the hidden original never shows visually. The
   theme's own attempt at this (style.bundle.css's
   ".form.form-state.is-invalid .select2-container...") ships with
   `border-color: danger;`, which isn't a real CSS color and so never
   actually applies — this replaces it with a working rule scoped to
   whichever select2 is a sibling of the specific select currently marked
   invalid, matching .form-control.is-invalid's own red (#F64E60). */
select.is-invalid + .select2 .select2-selection {
    border-color: #F64E60 !important;
}

/* Required checkboxes' red-invalid state (same global script) — the
   .checkbox component's real <input> is invisible by design (opacity: 0,
   position: absolute — style.bundle.css) and the glyph the user actually
   sees is its sibling <span>, so .is-invalid landing on the input itself has
   nothing to visibly style. This paints that sibling <span> instead,
   matching .form-control.is-invalid's own red (#F64E60), whenever a
   required checkbox (e.g. every User Boarding checklist item) fails
   validation. */
.checkbox > input.is-invalid + span {
    border-color: #F64E60 !important;
    box-shadow: 0 0 0 1px #F64E60;
}

/* M365 Dashboard (inc/clients/msdashboard.php) "Not Configured"-style
   badges — the theme's own .label-light-danger (#F64E60 text on a #FFE2E5
   background) and .label-light-secondary (#E4E6EF text on a #EBEDF3
   background — nearly the same color as its own background, used for the
   inert Onboarding placeholder) are both too low-contrast to read
   comfortably at a small badge's font weight/size. Darkens just the text
   color on this page's badges specifically, keeping the same light
   background so the "needs attention" visual language stays consistent
   with every other light-danger badge elsewhere in the app. */
.msd-badge.label-light-danger {
    color: #C4183A;
}
.msd-badge.label-light-secondary {
    color: #7E8299;
}

/* Topbar (inc/core/shell-head.php) quick-search box -- the vendor bundle
   left this at a fixed inline width: 320px regardless of screen size, while
   its own wrapper (.topbar-item) carries Bootstrap's w-100 w-lg-auto (full
   width below 992px, auto at 992px+) with no matching width rule on the
   input-group itself. Below 992px this meant the wrapper claimed 100% width
   while the search box inside it stayed pinned at 320px, and since .topbar
   has no flex-wrap of its own (see below), the remaining topbar-item icon
   buttons (SMS/Margin Calculator/Alerts/User) had nowhere to go but overlap
   the fixed-width search box -- reported as "the burger button conflicts
   with the other header buttons" on tablet widths, since tapping the
   topbar-toggle (inc/core/shell-head.php's #kt_header_mobile_topbar_toggle)
   is what actually reveals this squeezed row.

   The real bottleneck (confirmed live) turned out to be one level up:
   #kt_quick_search_inline itself carries Bootstrap's .w-auto (width: auto
   !important), which caps the whole chain at its own shrink-to-fit content
   width regardless of what's set on .quick-search-input-group inside it --
   .w-auto's !important means the override below needs one too. */
.quick-search-input-group {
    width: 320px;
}
@media (max-width: 991.98px) {
    #kt_quick_search_inline.w-auto {
        width: 100% !important;
    }
    .quick-search-input-group {
        width: 100%;
    }
    /* Its wrapper's mr-3 (Bootstrap, margin-right: 1rem !important) is
       needed at desktop widths to space the search box from the icon
       buttons sitting next to it on the same row -- once full-width and
       wrapped onto its own row here, that same margin just pushes the
       right edge in, misaligning it against the icon row below. */
    .topbar-item.mr-3:has(#kt_quick_search_inline) {
        margin-right: 0 !important;
    }
    /* Lets the now full-width search box (above) push the icon buttons onto
       their own row instead of squeezing into the same one -- .topbar
       (style.bundle.css) is display: flex with no wrap of its own. */
    .topbar {
        flex-wrap: wrap;
        row-gap: 8px;
    }
    /* Wrapping (above) makes .topbar's own content taller than the vendor's
       hardcoded height: 60px whenever it's NOT actively revealed -- before
       topbar-mobile-on is toggled, .topbar is only hidden via opacity: 0 +
       margin-top: -60px (style.bundle.css), which shifts its 60px box up
       out of view but does nothing about content taller than that box, or
       about hit-testing: opacity: 0 doesn't affect pointer-events, and
       negative margins let a child visually escape its parent's own box
       without the parent (#kt_header's own .container-fluid -- an ordinary
       div, default pointer-events: auto, overflow: visible) ever knowing --
       confirmed live: that .container-fluid's own rendered box exactly
       coincides with #kt_header_mobile's burger/topbar-toggle row, and
       being an ordinary block box it captures clicks across its full area
       regardless of what its (invisible) content actually looks like. A
       tap that looked like it was hitting the mobile burger was really
       landing on this invisible ancestor. Disabling pointer-events on all
       of #kt_header (rather than chasing .topbar/.container-fluid
       individually) is the one rule that actually covers every element in
       that hidden subtree, restored only on .topbar once it's genuinely
       revealed. */
    #kt_header {
        pointer-events: none;
    }
    /* The vendor's own mobile .topbar rule (style.bundle.css) hardcodes
       height: 60px -- fine for a single row, but once flex-wrap (above)
       splits the content into two rows, the box itself stays clamped to
       60px while the second (icon) row keeps rendering past it, landing
       on top of/underneath whatever comes next in the page (confirmed
       live: the icon row underlapped the top of the main content). Only
       overridden for the actually-revealed state -- left alone while
       hidden, where the fixed 60px still correctly cancels out against
       margin-top: -60px with no extra layout space reserved. */
    .topbar-mobile-on .topbar {
        height: auto;
        pointer-events: auto;
    }
    /* Breathing room between the revealed icon row and the page content
       directly below it -- per instruction. */
    .topbar-mobile-on .topbar.topbar-minimize {
        padding-bottom: 15px;
    }
}
