/* =====================================================================
 * BzEngage — Tabler theme custom layer
 * ---------------------------------------------------------------------
 * Section 1: App component styles (ported for the Tabler design system)
 * Section 2: BS4 / Pixel -> Bootstrap 5 / Tabler compatibility bridge
 *            (keeps not-yet-migrated views readable while the full 1:1
 *             Tabler rewrite is in progress)
 * Section 3: Polish / overrides
 * ===================================================================== */

/* ------------------------------------------------------------------ *
 * 0. Legacy design tokens
 *    The bundled third-party stylesheets (daterangepicker, select2, quill,
 *    cropper, pickr, …) were customised by AltumCode to reference the bzengage
 *    "Pixel" CSS variables (--white, --primary, --gray-*, --border-radius,
 *    --input-*). Tabler exposes everything under --tblr-*, so those vars are
 *    undefined here and the widgets render broken (e.g. transparent panels).
 *    Mapping the legacy tokens to Tabler equivalents repairs every such widget
 *    at once. These names are not used by Tabler/Bootstrap themselves.
 * ------------------------------------------------------------------ */
:root {
    --white: #ffffff;
    --black: #000000;
    --gray: var(--tblr-gray-600, #6c7a91);
    --gray-50: var(--tblr-gray-50, #f8fafc);
    --gray-100: var(--tblr-gray-100, #f1f5f9);
    --gray-200: var(--tblr-gray-200, #e6e7e9);
    --gray-300: var(--tblr-gray-300, #dadfe5);
    --gray-400: var(--tblr-gray-400, #a6b0c2);
    --gray-500: var(--tblr-gray-500, #7987a1);
    --gray-600: var(--tblr-gray-600, #6c7a91);
    --gray-700: var(--tblr-gray-700, #4b5468);
    --gray-800: var(--tblr-gray-800, #313b52);
    --gray-900: var(--tblr-gray-900, #1a2234);
    --primary: var(--tblr-primary, #206bc4);
    --primary-100: rgba(var(--tblr-primary-rgb, 32,107,196), .1);
    --primary-200: rgba(var(--tblr-primary-rgb, 32,107,196), .2);
    --primary-300: rgba(var(--tblr-primary-rgb, 32,107,196), .35);
    --primary-600: var(--tblr-primary, #206bc4);
    --primary-700: var(--tblr-primary, #206bc4);
    --primary-800: var(--tblr-primary, #206bc4);
    --primary-900: var(--tblr-primary, #206bc4);
    --dark: var(--tblr-dark, #1e293b);
    --body-bg: var(--tblr-body-bg, #f6f7f9);
    --border-radius: var(--tblr-border-radius, .25rem);
    --input-color: var(--tblr-body-color, #1a2234);
    --input-border-color: var(--tblr-border-color, #dadce0);
    --input-padding-x: .75rem;
    --input-padding-y: .4375rem;
}

/* ------------------------------------------------------------------ *
 * 1. App component styles
 * ------------------------------------------------------------------ */

/* Entrance animation used by the main content wrapper */
@keyframes bzengage-fade-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.bzengage-animate.bzengage-animate-fade-in {
    animation: bzengage-fade-in .35s ease-out both;
}
.bzengage-animate-fill-none { animation-fill-mode: both; }
@media (prefers-reduced-motion: reduce) {
    .bzengage-animate { animation: none !important; }
}

/* Brand logos */
.navbar-logo { max-height: 2.25rem; width: auto; }
.navbar-logo-mini { max-height: 1.5rem; width: auto; }
.footer-logo { max-height: 2rem; width: auto; }

/* Avatar shown in the top navbar user dropdown */
.navbar-avatar {
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    object-fit: cover;
}

/* Footer social icon chips */
.footer-social-wrapper {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--tblr-border-radius, .25rem);
}
.footer-social-wrapper a { line-height: 1; }

.footer-heading { text-decoration: none; }
.list-style-none { list-style: none; padding-left: 0; }

/* Small helper used across legacy views */
.font-size-little-small { font-size: .8125rem; }
.font-weight-450 { font-weight: 500; }
.rounded-2x { border-radius: calc(var(--tblr-border-radius, .25rem) * 2) !important; }

/* Notification badge dot in the navbar bell */
.internal-notification-icon {
    position: absolute;
    top: -.15rem;
    inset-inline-end: -.15rem;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--tblr-danger, #d63939);
}

/* Chart wrapper — Chart.js needs a bounded height on its container, otherwise
   the <canvas> collapses to 0 (or grows unbounded). Ported from the legacy
   custom.css that the Tabler theme does not load. Used by the dashboard and
   every statistics view. */
.chart-container {
    position: relative;
    width: 100%;
    height: 275px;
    margin: auto;
}
@media print {
    .chart-container canvas {
        min-height: 100%;
        max-width: 100%;
        max-height: 100%;
        height: auto !important;
        width: auto !important;
    }
}

/* Notification-type avatar chip in "latest notifications" tables. The
   background/text colours are set inline from the notification config; this
   only supplies the size and centering (shape comes from .rounded-circle). */
.notification-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1.05rem;
    background: var(--tblr-gray-100);
    color: var(--tblr-secondary);
}

/* Favicon glyphs shown next to campaign domains */
.icon-favicon {
    width: .95rem;
    height: auto;
    border-radius: 2px;
}
.icon-favicon-small {
    width: .85rem;
    height: auto;
    border-radius: 2px;
}

/* Dashboard stat cards — subtle hover lift for a more tactile feel. Keyed to
   the stable wrapper IDs so no view markup change is required. */
#total_campaigns_wrapper,
#total_notifications_wrapper,
#total_notifications_impressions_wrapper {
    transition: transform .15s ease, box-shadow .15s ease;
}
#total_campaigns_wrapper:hover,
#total_notifications_wrapper:hover,
#total_notifications_impressions_wrapper:hover {
    transform: translateY(-3px);
    box-shadow: var(--tblr-box-shadow-lg, 0 1rem 3rem rgba(0,0,0,.1));
}
@media (prefers-reduced-motion: reduce) {
    #total_campaigns_wrapper,
    #total_notifications_wrapper,
    #total_notifications_impressions_wrapper { transition: none; transform: none; }
}

/* ------------------------------------------------------------------ *
 * 2. Compatibility bridge  (BS4 / Pixel  ->  BS5 / Tabler)
 *    Aliases legacy utility class names to their Tabler equivalents so
 *    views that have not yet been converted keep a coherent layout.
 * ------------------------------------------------------------------ */

/* Directional spacing renamed in BS5: mr- / ml- became me- / ms- .
   (Do not write those names with a star and slash here — it closes this comment early and eats .mr-0.) */
.mr-0{margin-right:0!important}.mr-1{margin-right:.25rem!important}.mr-2{margin-right:.5rem!important}.mr-3{margin-right:1rem!important}.mr-4{margin-right:1.5rem!important}.mr-5{margin-right:3rem!important}
.ml-0{margin-left:0!important}.ml-1{margin-left:.25rem!important}.ml-2{margin-left:.5rem!important}.ml-3{margin-left:1rem!important}.ml-4{margin-left:1.5rem!important}.ml-5{margin-left:3rem!important}
.pr-0{padding-right:0!important}.pr-1{padding-right:.25rem!important}.pr-2{padding-right:.5rem!important}.pr-3{padding-right:1rem!important}.pr-4{padding-right:1.5rem!important}.pr-5{padding-right:3rem!important}
.pl-0{padding-left:0!important}.pl-1{padding-left:.25rem!important}.pl-2{padding-left:.5rem!important}.pl-3{padding-left:1rem!important}.pl-4{padding-left:1.5rem!important}.pl-5{padding-left:3rem!important}
.mr-lg-0,.mr-lg-3{}
@media (min-width:992px){
    .mr-lg-0{margin-right:0!important}.mr-lg-3{margin-right:1rem!important}
    .ml-lg-3{margin-left:1rem!important}.pr-lg-3{padding-right:1rem!important}.pl-lg-3{padding-left:1rem!important}
}

/* Font-weight helpers */
.font-weight-bold{font-weight:700!important}
.font-weight-normal{font-weight:400!important}
.font-weight-light{font-weight:300!important}
.font-weight-bolder{font-weight:600!important}

/* Text / display helpers */
.text-decoration-none{text-decoration:none!important}

/* Block buttons became .w-100 / .d-grid in BS5 */
.btn-block{display:block;width:100%}
.btn-block+.btn-block{margin-top:.5rem}

/* Legacy gray utility buttons (Pixel) mapped to Tabler surfaces */
.btn-gray-200{background-color:var(--tblr-gray-100);border-color:var(--tblr-border-color);color:var(--tblr-body-color)}
.btn-gray-200:hover{background-color:var(--tblr-gray-200)}

/* ------------------------------------------------------------------ *
 * Collapsible section headers -> accordion appearance
 * ---------------------------------------------------------------------
 * Every one of the 97 `.btn-gray-200` in the theme is a `data-bs-toggle="collapse"` section header
 * (notification editor + its 30 per-type setting files, account-preferences, admin settings). They are
 * already a hand-rolled accordion — `data-bs-parent` gives them the mutual-exclusion behaviour. What they
 * lacked was the chrome: `.btn` centres the label, and there was NO indicator that the section expands.
 *
 * Styled here rather than converted to Tabler's <div class="accordion"> component. Tabler's accordion
 * rotates a CHILD element (`.accordion-button-toggle`), not a CSS ::after, so adopting it literally means
 * restructuring all 97 toggles across 32 view files for an identical result — including the 30 per-type
 * files, where one bad edit breaks a notification type nobody has a row for. This reaches the same
 * appearance from one place and covers every toggle at once, and it reuses Tabler's own accordion tokens
 * so it tracks the theme.
 *
 * Keyed off [data-bs-toggle="collapse"] rather than .btn-gray-200 alone: verified that all 97 are collapse
 * toggles today, but this way a future plain grey button is not dragged into the accordion look.
 * ------------------------------------------------------------------ */
.btn-gray-200[data-bs-toggle="collapse"] {
    display: flex;
    align-items: center;
    gap: .75rem;
    text-align: left;
    padding: var(--tblr-accordion-btn-padding-y, 1rem) 1rem;
    font-weight: var(--tblr-accordion-btn-font-weight, 500);
    border: var(--tblr-border-width, 1px) solid var(--tblr-accordion-border-color, var(--tblr-border-color));
    border-radius: var(--tblr-accordion-border-radius, var(--tblr-border-radius));
}

/* The chevron the hand-rolled markup never had. Bootstrap keeps aria-expanded in sync on the toggle. */
.btn-gray-200[data-bs-toggle="collapse"]::after {
    flex-shrink: 0;
    width: var(--tblr-accordion-btn-toggle-width, 1.25rem);
    height: var(--tblr-accordion-btn-toggle-width, 1.25rem);
    margin-left: auto;
    content: "";
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%239ca3af' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 1rem;
    transition: transform .2s ease-in-out;
}

.btn-gray-200[data-bs-toggle="collapse"][aria-expanded="true"]::after {
    transform: rotate(-180deg);
}

@media (prefers-reduced-motion: reduce) {
    .btn-gray-200[data-bs-toggle="collapse"]::after { transition: none; }
}
.bg-gray-50{background-color:var(--tblr-gray-50)!important}
.bg-gray-100{background-color:var(--tblr-gray-100)!important}
.bg-gray-200{background-color:var(--tblr-gray-100)!important}
.text-gray-50{color:var(--tblr-gray-50)!important}
.text-primary-900{color:var(--tblr-primary)!important}

/* Legacy custom form controls (custom-switch / custom-control) -> Tabler form-check */
.custom-control.custom-switch{padding-left:2.5rem;position:relative;min-height:1.5rem}
.custom-control.custom-switch .custom-control-input{position:absolute;left:0;width:2rem;height:1.25rem;opacity:0;cursor:pointer}
.custom-control.custom-switch .custom-control-label{cursor:pointer}
.custom-control.custom-switch .custom-control-label::before{content:"";position:absolute;left:0;top:.1rem;width:2rem;height:1.25rem;background:var(--tblr-gray-300);border-radius:1rem;transition:background .15s}
.custom-control.custom-switch .custom-control-label::after{content:"";position:absolute;left:.15rem;top:.25rem;width:.95rem;height:.95rem;background:#fff;border-radius:50%;transition:transform .15s}
.custom-control.custom-switch .custom-control-input:checked~.custom-control-label::before{background:var(--tblr-primary)}
.custom-control.custom-switch .custom-control-input:checked~.custom-control-label::after{transform:translateX(.75rem)}

.custom-control.custom-radio,.custom-control.custom-checkbox{position:relative;padding-left:1.75rem;min-height:1.5rem}
.custom-control.custom-radio .custom-control-input,.custom-control.custom-checkbox .custom-control-input{position:absolute;left:0;top:.2rem;width:1.1rem;height:1.1rem;cursor:pointer}

.custom-select{display:block;width:100%;padding:.4375rem 2.25rem .4375rem .75rem;font-size:.875rem;line-height:1.4285714;color:var(--tblr-body-color);background-color:var(--tblr-bg-forms,#fff);border:1px solid var(--tblr-border-color);border-radius:var(--tblr-border-radius)}

/* form-group was dropped in BS5 (use margin utilities) */
.form-group{margin-bottom:1rem}

/* Legacy badge color modifiers -> BS5 background utilities */
.badge-primary{background-color:var(--tblr-primary)!important;color:#fff}
.badge-secondary{background-color:var(--tblr-secondary)!important;color:#fff}
.badge-success{background-color:var(--tblr-success)!important;color:#fff}
.badge-danger{background-color:var(--tblr-danger)!important;color:#fff}
.badge-warning{background-color:var(--tblr-warning)!important;color:#000}
.badge-info{background-color:var(--tblr-info)!important;color:#fff}
.badge-light{background-color:var(--tblr-gray-100)!important;color:#000}
.badge-dark{background-color:var(--tblr-dark)!important;color:#fff}
/* Badges must stay compact: Tabler's .badge is inline-flex, but when it is a
   direct child of a `.d-flex.flex-column` (used across views for stacked status
   badges) the default align-items:stretch blows it out to full width. Keep it
   sized to its content. */
.d-flex.flex-column > .badge,
.d-flex.flex-column > a.badge,
.d-flex.flex-column > div.badge { align-self: flex-start; max-width: max-content; }

/* Badge text contrast: Tabler defaults .badge text colour to --tblr-secondary
   (gray #6b7280), which is unreadable on a SOLID coloured background (green,
   blue, …). Force a contrasting colour. Light "-lt" badges keep their own
   coloured text, and explicit .text-* utilities still override these. */
.badge.bg-primary, .badge.bg-secondary, .badge.bg-success, .badge.bg-danger,
.badge.bg-info, .badge.bg-dark, .badge.bg-blue, .badge.bg-azure, .badge.bg-indigo,
.badge.bg-purple, .badge.bg-pink, .badge.bg-red, .badge.bg-green, .badge.bg-teal,
.badge.bg-cyan, .badge.bg-orange, .badge.bg-black,
a.badge.bg-primary, a.badge.bg-success, a.badge.bg-danger, a.badge.bg-info,
a.badge.bg-dark, a.badge.bg-secondary { color: #fff; }
.badge.bg-warning, .badge.bg-yellow, .badge.bg-lime, .badge.bg-light, .badge.bg-white { color: #182234; }

/* FontAwesome fixed-width helper still referenced by legacy markup */
.fa-fw{text-align:center;width:1.25em}
.fa-layers{position:relative;display:inline-block;height:1em;width:1.25em;text-align:center}
.fa-layers-counter{position:absolute;top:0;right:0;transform:scale(.6);transform-origin:top right}

/* ------------------------------------------------------------------ *
 * 2b. Pricing / plan cards (Pixel ".pricing-*" component -> Tabler)
 *     These bespoke classes live in the legacy pixel.css which the
 *     Tabler theme does not load, so they are ported here. Without this
 *     the decorative SVG (.pricing-deco-img) falls back to a black fill.
 * ------------------------------------------------------------------ */
.pricing-item {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--tblr-bg-surface, #fff);
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius-lg, .5rem);
    overflow: hidden;
    transition: transform .15s ease, box-shadow .15s ease;
}
.pricing-item.zoomer:hover { transform: translateY(-4px); box-shadow: var(--tblr-box-shadow-lg, 0 1rem 3rem rgba(0,0,0,.1)); }

.pricing-deco {
    position: relative;
    padding: 2rem 1.5rem 3.5rem;
    text-align: center;
    color: #fff;
    background-color: var(--tblr-primary);
}
.pricing-deco .pricing-title { color: #fff; margin: 0; font-size: 1.25rem; font-weight: 600; }

/* Decorative bottom wave: fill it with the card body colour so it reads as a wave,
   never the default black fill. */
.pricing-deco-img {
    position: absolute;
    left: 0;
    bottom: -1px;
    width: 100%;
    height: 60px;
    fill: var(--tblr-bg-surface, #fff);
}
.pricing-deco-img .deco-layer { fill: var(--tblr-bg-surface, #fff); }

.pricing-price { position: relative; z-index: 1; font-size: 2rem; font-weight: 700; color: #fff; }
.pricing-sub { position: relative; z-index: 1; color: rgba(255,255,255,.85); font-size: .875rem; }

.pricing-tag {
    position: absolute;
    top: 1rem;
    inset-inline-end: 1rem;
    z-index: 2;
    padding: .25rem .5rem;
    border-radius: var(--tblr-border-radius);
    background: var(--tblr-orange, #f76707);
    color: #fff;
    font-size: .75rem;
    font-weight: 600;
}

.pricing-feature-list { list-style: none; margin: 0; padding: 1.25rem 1.5rem; }
.pricing-feature { padding: .4rem 0; border-bottom: 1px solid var(--tblr-border-color-light, rgba(0,0,0,.04)); }
.pricing-feature:last-child { border-bottom: 0; }

/* ------------------------------------------------------------------ *
 * 2c. Marketing homepage (index) — ported from the legacy index-custom.css
 *     onto Tabler tokens, so the public landing page no longer needs
 *     pixel.css / index-custom.css / AOS (all removed from the view).
 * ------------------------------------------------------------------ */

/* Any leftover AOS scroll-reveal attributes must never hide content
   (AOS is no longer loaded). */
[data-aos] { opacity: 1 !important; transform: none !important; }

.index-fade {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(0deg, var(--tblr-bg-surface, #fff), transparent) !important;
}

/* The decorative hero "cover" is a cluster of live embed widgets that only
   render correctly with the full embed-widget CSS; on the Tabler landing page
   it is hidden in favour of the clean text hero + gradient backdrop. The
   notification-type grid further down showcases the widgets properly. */
.index-cover-container { display: none !important; }

.index-container { width: 100%; padding: 3rem 0 0; }

.index-header { font-size: 2.5rem; font-weight: 700; line-height: 1.1; }
.index-subheader { font-size: 1.2rem; }

.index-button {
    padding: 1rem 3rem;
    font-size: 1.15rem;
    font-weight: 500;
    border-radius: calc(2 * var(--tblr-border-radius, .25rem));
}

@media (min-width: 992px) {
    .index-header { font-size: 3.5rem; }
    .index-container { padding: 5rem 0; }

    .index-background-container {
        width: 100%; height: 100%;
        position: absolute; overflow: hidden; z-index: -2;
    }
    .index-background-image {
        position: absolute;
        border-radius: 2.5%;
        right: 0;
        transform: translate(58%, -18%) rotate(-20deg);
        height: 800px; width: 800px;
        background: radial-gradient(circle at 35% 30%, #8e46d4, #292cc4 70%);
        background-attachment: fixed;
        opacity: .9;
        animation: index-background 50s ease-in-out alternate infinite;
    }
    @keyframes index-background {
        0%   { background-size: 135%; filter: blur(0); }
        100% { background-size: 300%; filter: blur(1px); }
    }
    [dir="rtl"] .index-background-image { right: initial; left: -30%; }

    .index-cover { transform: translate(55%, 5%) scale(.7); }
    [dir="rtl"] .index-cover { transform: translate(-55%, 5%) scale(.7); }
}
@media (min-width: 1200px) {
    .index-background-image { transform: translate(27.5%, -18%) rotate(-20deg); }
    .index-cover { transform: translate(42%, 5%) scale(.7); }
    [dir="rtl"] .index-cover { transform: translate(-55%, 5%) scale(.7); }
}
@media (min-width: 1400px) {
    .index-background-image { transform: translate(20%, -18%) rotate(-20deg); height: 725px; width: 800px; }
}

.index-icon-container {
    background: rgba(var(--tblr-primary-rgb, 32,107,196), .12);
    color: var(--tblr-primary);
    width: 35px; height: 35px; min-width: 35px; min-height: 35px;
    font-size: .8rem;
    display: flex; align-items: center; justify-content: center;
    border-radius: calc(2 * var(--tblr-border-radius, .25rem));
    transition: all .3s ease-in-out;
}
.index-icon-container-steps { width: 60px; height: 60px; min-width: 60px; min-height: 60px; font-size: 1.5rem; }

.index-brand-icon-container {
    background: rgba(var(--tblr-primary-rgb, 32,107,196), .12);
    width: 75px; height: 75px; min-width: 75px; min-height: 75px;
    display: flex; align-items: center; justify-content: center;
    border-radius: calc(3 * var(--tblr-border-radius, .25rem));
    transform: rotate(45deg);
    transition: all .3s ease-in-out;
}
.index-brand-icon-container svg { width: 35px; height: auto; transform: rotate(-45deg); transition: all .3s ease-in-out; }
.index-brand-icon-container:hover { transform: rotate(0); }
.index-brand-icon-container:hover svg { transform: rotate(0); }

.index-register-container {
    background: var(--tblr-primary);
    color: #fff;
    padding: 4rem 0;
    border-radius: calc(2 * var(--tblr-border-radius, .25rem));
}
.index-register-container p { color: rgba(255,255,255,.85); }

.index-testimonial-avatar {
    width: 85px; height: 85px; border-radius: 50%;
    transform: translateY(-70%);
    border: 4px solid var(--tblr-bg-surface, #fff);
    background: var(--tblr-bg-surface, #fff);
    position: absolute; left: 50%; margin-left: -52px;
}
.index-testimonial-comment { margin-left: 18px; }
.index-faq svg { transition: all .15s; color: var(--tblr-primary); }

/* The live notification-type preview renders real embed widgets (which use
   fixed/absolute positioning to sit in page corners). Contain it to a tidy,
   centered box on the landing page so it cannot overlap the surrounding grid. */
#notification_preview {
    position: relative;
    overflow: hidden;
    max-width: 460px;
    min-height: 160px;
    margin: 0 auto 2.5rem;
    padding: 1rem;
    border-radius: var(--tblr-border-radius-lg, .5rem);
}
#notification_preview > * {
    position: relative !important;
    inset: auto !important;
    margin: 0 auto !important;
    max-width: 100% !important;
    transform: none !important;
}

.icon-zoom-animation:hover .index-icon-container,
.icon-zoom-animation:hover .index-icon-container-steps { transform: scale(1.08); }
.zoomer { transition: transform .15s ease, box-shadow .15s ease; }
.zoomer:hover { transform: translateY(-4px); box-shadow: var(--tblr-box-shadow-lg, 0 1rem 3rem rgba(0,0,0,.1)); }

/* ------------------------------------------------------------------ *
 * 2d. Vendor widgets — select2 + daterangepicker restyled to match
 *     Tabler form controls (Tabler ships styles for Tom Select, not these,
 *     and the legacy pixel/custom.css that styled them is not loaded).
 * ------------------------------------------------------------------ */

/* --- select2 (single) --- */
/*
 * Scope the width to .select2, NOT .select2-container. select2 renders its dropdown into a second
 * container appended to <body> and gives it a COPY of the real container's classes
 * (AttachBody.position: `$dropdown.attr('class', $container.attr('class'))`), minus `select2`. So a
 * rule on .select2-container also lands on that body-level clone and stretches it to the full page
 * width from wherever the field sits, overflowing to the right. select2's own stylesheet already does
 * `.select2 { width: 100% !important }` for the real container, which is the class the clone drops.
 */
.select2 { width: 100% !important; }

/*
 * Pin the body-level dropdown to the viewport. This is load-bearing — without it every select2 list
 * opens too high by exactly the current scroll offset.
 *
 * The scroll container is <body>, not the root (see "Scroll container: <body>, not the root" below).
 * That means the root never scrolls and `window.pageYOffset` is permanently 0. jQuery's .offset() is
 * `getBoundingClientRect().top + pageYOffset`, so with pageYOffset stuck at 0 it hands select2
 * VIEWPORT coordinates while select2 believes they are DOCUMENT coordinates. It then writes them as
 * `top:` on the clone it appends to <body>, whose containing block is body's padding box — which is
 * scrolled away by body.scrollTop. Net effect: the list lands body.scrollTop pixels too high, so it
 * looks fine at the top of a page and drifts further the more you scroll (Timezone looked correct
 * while Country and 2FA opened far up the page).
 *
 * Making the clone position:fixed puts it in the same coordinate space select2 actually measured — the
 * viewport — so the numbers line up. select2 scroll-locks its scrollable parents (body included) while
 * a dropdown is open, so it cannot drift while pinned.
 *
 * The clone is identified by NOT having the `select2` class: select2 copies the real container's class
 * list onto it and removes `select2` (AttachBody.position).
 */
body > .select2-container--open:not(.select2) { position: fixed !important; }

/*
 * Same root cause, same fix, for the other jQuery-offset widget in the bundle. daterangepicker's
 * move() also does `element.offset().top + outerHeight()` and appends to <body> (parentEl defaults to
 * "body" and no view overrides it), so its calendar drifted upward by body.scrollTop exactly like the
 * select2 lists. It is used on statistics, campaign statistics, notification data/statistics and
 * several admin pages.
 */
body > .daterangepicker { position: fixed !important; }
.select2-container--default .select2-selection--single {
    display: flex;
    align-items: center;
    min-height: calc(1.4285714em + .875rem + 2px);
    padding: .4375rem 2.25rem .4375rem .75rem;
    font-size: .875rem;
    color: var(--tblr-body-color);
    background-color: var(--tblr-bg-forms, #fff);
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    /* Tabler / Bootstrap 5 chevron */
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23616876' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right .75rem center;
    background-size: 16px 12px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
    padding: 0;
    line-height: 1.4285714;
    color: var(--tblr-body-color);
}
.select2-container--default .select2-selection--single .select2-selection__placeholder { color: var(--tblr-secondary); }
/* hide select2's own arrow triangle — we use the background chevron above */
.select2-container--default .select2-selection--single .select2-selection__arrow { display: none; }
.select2-container--default.select2-container--open .select2-selection--single,
.select2-container--default .select2-selection--single:focus {
    border-color: var(--tblr-primary);
    box-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb, 32,107,196), .25);
    outline: none;
}

/* --- select2 (multiple) --- */
.select2-container--default .select2-selection--multiple {
    min-height: calc(1.4285714em + .875rem + 2px);
    padding: .25rem .5rem;
    background-color: var(--tblr-bg-forms, #fff);
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
    display: inline-flex;
    align-items: center;
    margin: .125rem .25rem .125rem 0;
    padding: .125rem .5rem;
    color: #fff;
    background-color: var(--tblr-primary);
    border: 0;
    border-radius: var(--tblr-border-radius);
    font-size: .8125rem;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
    color: rgba(255,255,255,.85);
    margin-right: .25rem;
    border: 0;
    background: transparent;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { color: #fff; background: transparent; }

/* --- select2 dropdown panel --- */
.select2-dropdown {
    background-color: var(--tblr-bg-surface, #fff);
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    box-shadow: var(--tblr-box-shadow-dropdown, 0 1px 2px 0 rgba(0,0,0,.05));
    color: var(--tblr-body-color);
}
.select2-container--default .select2-search--dropdown .select2-search__field {
    padding: .375rem .5rem;
    color: var(--tblr-body-color);
    background-color: var(--tblr-bg-forms, #fff);
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
}
.select2-container--default .select2-results__option {
    padding: .375rem .75rem;
    font-size: .875rem;
}
.select2-container--default .select2-results__option--highlighted[aria-selected],
.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
    background-color: var(--tblr-primary);
    color: #fff;
}
.select2-container--default .select2-results__option[aria-selected="true"],
.select2-container--default .select2-results__option--selected {
    background-color: rgba(var(--tblr-primary-rgb, 32,107,196), .12);
    color: var(--tblr-body-color);
}

/* Dark mode surfaces for select2 */
[data-bs-theme="dark"] .select2-container--default .select2-selection--single,
[data-bs-theme="dark"] .select2-container--default .select2-selection--multiple,
[data-bs-theme="dark"] .select2-dropdown,
[data-bs-theme="dark"] .select2-container--default .select2-search--dropdown .select2-search__field {
    background-color: var(--tblr-bg-forms, #1a2234);
    border-color: var(--tblr-border-color);
    color: var(--tblr-body-color);
}

/* --- daterangepicker ---
   The picker is appended to <body>, outside the card scope where
   --tblr-bg-surface is defined, so concrete colors are used to guarantee an
   opaque panel (a var there resolves to transparent and page content bleeds
   through). Dark mode is handled by the [data-bs-theme="dark"] override below. */
.daterangepicker,
.daterangepicker .ranges,
.daterangepicker .ranges ul,
.daterangepicker .drp-calendar,
.daterangepicker .drp-buttons {
    background-color: #ffffff;
}
.daterangepicker {
    z-index: 3001;
    font-family: inherit;
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    box-shadow: 0 .5rem 1.5rem rgba(0,0,0,.15);
    color: var(--tblr-body-color);
}
.daterangepicker::before, .daterangepicker::after { display: none; }
.daterangepicker .calendar-table { background-color: transparent; border: 0; }
.daterangepicker td, .daterangepicker th { color: var(--tblr-body-color); }
.daterangepicker td.available:hover, .daterangepicker th.available:hover { background-color: var(--tblr-gray-100); border-radius: var(--tblr-border-radius); }
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date { color: var(--tblr-secondary); background-color: transparent; }
.daterangepicker td.in-range { background-color: rgba(var(--tblr-primary-rgb, 32,107,196), .12); color: var(--tblr-body-color); }
.daterangepicker td.active, .daterangepicker td.active:hover { background-color: var(--tblr-primary); color: #fff; border-radius: var(--tblr-border-radius); }
.daterangepicker .ranges li { color: var(--tblr-body-color); border-radius: var(--tblr-border-radius); }
.daterangepicker .ranges li:hover { background-color: var(--tblr-gray-100); }
.daterangepicker .ranges li.active { background-color: var(--tblr-primary); color: #fff; }
.daterangepicker .drp-buttons { border-top-color: var(--tblr-border-color); }
.daterangepicker .drp-buttons .btn { font-size: .875rem; }
.daterangepicker .drp-selected { color: var(--tblr-secondary); }
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect,
.daterangepicker .input-mini {
    color: var(--tblr-body-color);
    background-color: var(--tblr-bg-forms, #fff);
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
}
.daterangepicker .input-mini.active { border-color: var(--tblr-primary); }
[data-bs-theme="dark"] .daterangepicker,
[data-bs-theme="dark"] .daterangepicker .ranges,
[data-bs-theme="dark"] .daterangepicker .ranges ul,
[data-bs-theme="dark"] .daterangepicker .drp-calendar,
[data-bs-theme="dark"] .daterangepicker .drp-buttons { background-color: #182234; }
[data-bs-theme="dark"] .daterangepicker { border-color: var(--tblr-border-color); }
[data-bs-theme="dark"] .daterangepicker td.available:hover,
[data-bs-theme="dark"] .daterangepicker .ranges li:hover { background-color: rgba(255,255,255,.06); }

/* ------------------------------------------------------------------ *
 * 2e. App UI component classes ported from the legacy custom.css
 *     (which the Tabler theme does not load). Referenced across converted
 *     views: invoices, pricing (palden), custom radio-boxes, filters
 *     dropdown, breadcrumbs, blog posts, loading overlays, tables, etc.
 *     Uses the legacy tokens defined in section 0.
 * ------------------------------------------------------------------ */

/* FontAwesome size helpers still present on un-migrated fa icons */
.fa-sm { font-size: .875em; }
.fa-xs { font-size: .75em; }

/* Keyframe used by the announcement banner */
@keyframes ac-fade-in-down {
    from { opacity: 0; transform: translateY(-.5rem); }
    to   { opacity: 1; transform: translateY(0); }
}

.form-control-range {
	
	-webkit-appearance: none;
	appearance: none;

	
	overflow: hidden;

	
	accent-color: var(--primary);
	background: var(--gray-200);
	border-radius: var(--border-radius);
	height: .5rem;
	margin: 0.75rem 0;
}

.form-control-range::-webkit-slider-runnable-track, .form-control-range::-moz-range-track {
	background: var(--gray-200);
}

.form-control-range::-webkit-slider-thumb {
	
	-webkit-appearance: none;
	appearance: none;

	
	height: .75rem;
	width: .75rem;
	background-color: var(--primary);
	border-radius: 50%;
	border: 2px solid var(--white);

	
	box-shadow: -2007px 0 0 2000px var(--primary-300);
}

.form-control-range::-moz-range-thumb {
	
	-webkit-appearance: none;
	appearance: none;

	
	height: .75rem;
	width: .75rem;
	background-color: var(--primary);
	border-radius: 50%;
	border: 2px solid var(--white);

	
	box-shadow: -2007px 0 0 2000px var(--primary-300);
}

.filters-dropdown {
	width: 18rem;
	max-height: 30rem;
	overflow-y: auto;
}

.custom-breadcrumbs {
	list-style: none;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
}

.custom-breadcrumbs > li {
	margin-right: .5rem;
}

.custom-breadcrumbs > li > a {
	color: var(--gray);
}

.custom-breadcrumbs > li > svg {
	color: var(--gray-400);
	margin-left: .5rem;
}

.font-size-small {
	font-size: .9rem;
}

.font-weight-500 {
	font-weight: 500;
}

.nav-custom2 .nav-link {
	background: var(--gray-200);
	margin-bottom: 1rem;
	font-size: .9rem;
	font-weight: 500;
}

.notification-preview {
	height: min-content;
	position: sticky;
	top: 1rem;
}

.user-avatar {
	border-radius: 50%;
	max-width: 80px;
	max-height: 80px;
}

.container-disabled {
	pointer-events: none;
	opacity: .5;
}

.container-disabled-simple {
	pointer-events: none;
}

.table-custom-container {
	border: 1px solid var(--gray-200);
	border-radius: calc(2*var(--border-radius));
}

.table-custom {
	margin-bottom: 0;
}

.table-custom thead th {
	border-top: 0;
	border-bottom: 0;
	background: white;
}

[data-theme-style="dark"] .table-custom thead th {
	background: var(--white);
	color: var(--gray-800)
}

.table-custom th {
	padding: 1.25rem 1rem;
}

.table-custom td {
	padding: 1.75rem 1rem;
	background: var(--white);
	vertical-align: middle;
	border: 0;
}

.table-custom tbody tr td {
	transition: all .3s ease-in-out;
}

[data-theme-style="dark"] .table-custom tbody tr td {
	border-color: var(--gray-100)
}

.table-custom tbody tr:hover td {
	
}

.pre-custom {
	background: var(--gray-300);
	padding: 2rem;
	font-size: 0.75rem;
}

.no-focus:focus {
    outline: 0;
    box-shadow: none;
}

.appearance-none {
    appearance: none;
    -moz-appearance: none;
    -webkit-appearance: none;
}

.pricing {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-around;
	width: 100%;
	margin: 0 auto 3rem;
}

.pricing-palden .pricing-item {
	background: var(--gray-50);
	border-radius: calc(2*var(--border-radius));
	margin: 2rem 0;
    border: 1px solid var(--gray-200);
}

[data-theme-style="dark"] .pricing-palden .pricing-item {
	background: var(--gray-100);
}

.pricing-palden .pricing-deco {
	border-radius: calc(2*var(--border-radius)) calc(2*var(--border-radius)) 0 0;
	background: var(--primary);
	padding: 2rem 0 6rem;
	position: relative;
}

.pricing-palden .pricing-deco-img {
	position: absolute;
	bottom: 0;
	left: 0;
	width: 100%;
	height: 130px;
}

.pricing-palden .pricing-deco-img path {
	fill: var(--gray-50);
}

[data-theme-style="dark"] .pricing-palden .pricing-deco-img path {
	fill: var(--gray-100);
}

.pricing-palden .pricing-title {
	font-size: 0.8rem;
	text-transform: uppercase;
	letter-spacing: 1px;
	margin-bottom: 0;
	color: #fff;
}

.pricing-palden .deco-layer {
	transition: transform 0.5s;
}

.pricing-palden .pricing-item:hover .deco-layer--1 {
	transform: translate3d(15px, 0, 0);
}

.pricing-palden .pricing-item:hover .deco-layer--2 {
	transform: translate3d(-15px, 0, 0);
}

.pricing-palden .icon {
	font-size: 2.5rem;
}

.pricing-palden .pricing-price {
	font-size: 3rem;
	font-weight: bold;
	padding: 0;
	color: #fff;
	margin: 0 0 0.25rem 0;
	line-height: 0.75;
}

.pricing-palden .pricing-currency {
	font-size: .9rem;
	vertical-align: top;
}

.pricing-palden .pricing-sub {
	font-size: .9rem;
	margin: 2rem 0;
	color: #fff;
	padding: 0 1.5rem;
}

.pricing-palden .pricing__sentence {
	font-weight: bold;
	margin: 0 0 1em 0;
	padding: 0 0 0.5em;
}

.pricing-palden .pricing-feature-list {
	margin: 0;
	padding: 0 2rem;
	list-style: none;
    font-size: .95rem;
}

.pricing-palden .pricing-feature {
	padding: .75rem 0;
}

.custom-radio-box {
	cursor: pointer;
}

.custom-radio-box .custom-radio-box-main-text {
	font-size: 1.15rem;
	font-weight: bold;
}

.custom-radio-box .custom-radio-box-main-icon {
	font-size: 1.25rem;
}

.custom-radio-box input[type="radio"] + div, .custom-radio-box input[type="checkbox"] + div {
	transition: all .3s ease-in-out;
	border: 2px solid var(--gray-200);
	background: var(--white);
}

.custom-radio-box input[type="radio"]:checked + div, .custom-radio-box input[type="checkbox"]:checked + div {
	border: 2px solid var(--primary);
}

.custom-radio-box input[type="radio"]:hover + div, .custom-radio-box input[type="checkbox"]:hover + div {
	border: 2px solid var(--primary);
}

.cryptocurrency-icon {
    width: 40px;
    height: 40px;
}

.round-circle-md {
	width: 2.5rem;
	height: 2.5rem;
	display: flex;
	justify-content: center;
	align-items: center;
	border-radius: 50%;
}

.invoice-table th {
	border-top: 0 !important;
}

.invoice-table td {
	vertical-align: baseline !important;
}

.btn-custom {
	padding: .5rem 1.5rem;
	color: var(--gray-600);
	border-radius: var(--border-radius);
	font-size: .9rem;
	background: var(--white);

}

.btn-custom:hover {
	color: var(--gray-700);
}

.btn-custom.active {
	color: var(--white);
	background: var(--primary);
}

.blog-post-image {
	max-height: 20rem;
	object-fit: cover;
}

.blog-post-image-small {
	height: 13rem;
	max-height: 13rem;
	object-fit: cover;
}

.blog-post-content {
	line-height: 1.75;
	word-break: break-word;
}

.blog-post-content p {
	margin-bottom: 1.5rem;
}

.ql-content p {
	margin-bottom: 0 !important;
}

.ql-content li[data-list="bullet"] {
	list-style-type: disc;
}

.sticky {
	position: sticky !important;
	top: 1rem;
	height: min-content;
}

.zoom-animation-subtle {
	transition: transform .3s;
}

.zoom-animation-subtle:hover {
	transform: scale(1.025);
}

.zoom-animation-subtle:active {
	transform: scale(.98);
}

.up-animation {
	transition: transform .3s;
}

.up-animation:hover {
	transform: translateY(-10px);
}

.loading-overlay {
	padding: 1.25rem;
	border-radius: var(--border-radius);
	display: flex;
	justify-content: center;
	align-items: center;
	position: absolute;
	width: 100%;
	height: 100%;
	background: var(--white);
	z-index: 100;
	opacity: 0.8;
}

[data-theme-style="dark"] .loading-overlay {
	background: var(--gray-50);
}

.btn-group-custom {
	border: 1px solid var(--gray-100);
	padding: 0.5rem;
	border-radius: var(--border-radius);
	flex-wrap: wrap;
	display: inline-flex;
	gap: 0.5rem;
}

.btn-group-custom .btn {
	border-radius: var(--border-radius) !important;
	background: var(--gray-100);
}

.btn-group-custom .btn.active {
	background: var(--dark);
	color: var(--white);
    z-index: 0 !important;
}

.team-user-avatar {
	width: 45px;
	height: 45px;
}

.team-delegate-access-wrapper {
	margin: .75rem;
	border-radius: .5rem;
}

.announcement-wrapper {
	margin: .75rem;
	border-radius: .5rem;
	opacity: 0;
	transition: opacity .3s ease-in;
	animation: ac-fade-in-down .3s ease-in .6s forwards;
}

.pointer-events-all {
	pointer-events: all !important;
}

.rating-star {
	cursor: pointer;
}

.rating-star svg {
	color: #ffc107;
	transition: color .15s;
}

.rating-star:hover svg,
.rating-star:hover ~ .rating-star svg
{
	color: #ff8800 !important;
}

.rating-star-chosen svg,
.rating-star-chosen ~ .rating-star svg
{
	color: #dd6200;
}

.notification-radio-box:hover {
    cursor: pointer;
}

.notification-radio-box .notification-radio-box-main-icon {
    font-size: 1.25rem;
}

/*
 * Hide the radio itself — the card next to it IS the control, and the rules below draw its state.
 * The markup carried Bootstrap 4's `.form-check-input`, which back then was `position:absolute` with a
 * negative margin and so sat out of the way. Bootstrap 5 makes it a visible 1.25rem box in normal flow,
 * which put a stray radio dot above all 30 cards on the create-notification page. Same clip technique
 * BS5 itself uses for `.btn-check`, kept focusable so keyboard users still get a ring (below).
 */
.notification-radio-box input[type="radio"] {
    position: absolute;
    clip: rect(0, 0, 0, 0);
    pointer-events: none;
}

.notification-radio-box input[type="radio"]:focus-visible + div {
    outline: 2px solid var(--tblr-primary);
    outline-offset: 2px;
}

.notification-radio-box input[type="radio"] + div {
    transition: all .3s ease-in-out;
    border: 1px solid var(--gray-200);
    background: var(--white);
}

.notification-radio-box input[type="radio"]:checked + div {
    border: 1px solid var(--primary);
}

.notification-radio-box input[type="radio"]:hover + div {
    border: 1px solid var(--primary);
}

/*
 * No .notification-create-preview-* rules here on purpose. The create-notification preview used to float
 * over the page via those classes (bottom-right for a popup, full-width bottom for a bar), which the
 * bzengage custom.css defined and this theme never ported — so it silently rendered inline instead. That
 * floating behaviour was dropped rather than restored: it made the preview hard to work with. The preview
 * now lives in a `sticky` column beside the type list, and the widgets size themselves (bars are
 * width:100%, the rest carry fixed widths), so no placement CSS is needed and the view no longer toggles
 * those classes.
 */

.index-timeline {
    position: relative;
}

.index-timeline::before {
    content: '';
    position: absolute;
    border-left: 2px solid var(--gray-200);
    height: 100%;
    left: calc(57px / 2);
}

/* Remaining ported app classes (admin overlay, plugin/invoice, custom file input) */
.admin-overlay { position: fixed; width: 100%; height: 100%; background: var(--gray-200); z-index: 100; opacity: .75; }
.plugin-avatar { min-width: 60px; min-height: 60px; width: 60px; height: 60px; object-fit: cover; font-size: 1.25rem; }
.invoice-logo { max-height: 2.5rem; height: 2.5rem; }
/*
 * The file inputs now use Tabler's own .form-control styling (Bootstrap 5 styles ::file-selector-button
 * natively), so they match every other field on the page. The old .bzengage-file-input skin — a padded
 * grey box with a white chip — was a Bootstrap 4-era design carried over from the bzengage theme, and it
 * fought BS5: BS5's negative button margins are tuned to .form-control's padding, which that class
 * overrode, so the chip was dragged onto the container's border and off-centre, and BS5's
 * `color: var(--tblr-body-color)` went near-white against its hardcoded white chip in dark mode.
 * Rather than keep patching the skin, the class was dropped from the markup. Only the preview thumbnail
 * below is still used.
 */
.bzengage-file-input-preview { max-width: 100%; max-height: 68px; min-height: 68px; object-fit: cover; }

/* ------------------------------------------------------------------ *
 * 3. Polish
 * ------------------------------------------------------------------ */

/* Icon sizing: Tabler Icons default to 1em (text size), but the app was
   authored with FontAwesome's "fa-sm" (0.875em) on nearly every icon, so the
   converted "ti" icons ended up ~14% larger than the original design. Restore
   that weight. Explicit size utilities (.fs-*, .h1-.h6, .fa-lg, ...) still win
   because they carry higher specificity / !important. */
.ti { font-size: .875em; vertical-align: -.125em; }
/* Keep FontAwesome fallback icons visually consistent with the Tabler ones. */
.svg-inline--fa { vertical-align: -.125em; }
.navbar-brand img { max-height: 2.25rem; }
.page-body { margin-top: 1.5rem; }
.dropdown-menu { box-shadow: var(--tblr-box-shadow-dropdown, 0 1px 2px 0 rgba(0,0,0,.05)); }

/* Row-action dropdowns live inside .table-responsive, whose `overflow-x: auto`
   makes the browser compute `overflow-y: auto` on the other axis too — so an
   open menu is clipped and the table sprouts a scrollbar. While a menu is open,
   let the container overflow so the menu escapes cleanly. (:has is already used
   by the Tabler bundle, so support is assumed.) */
.table-responsive:has(.dropdown-menu.show) { overflow: visible; }
.caret{display:inline-block;width:0;height:0;border-top:.3em solid;border-right:.3em solid transparent;border-left:.3em solid transparent;vertical-align:middle}

/* ------------------------------------------------------------------ *
 * Scroll container: <body>, not the root
 * ---------------------------------------------------------------------
 * Goal: when a page is short (no scroll) it must look identical to when it is
 * long (scroll present) — otherwise the vertical scrollbar's ~15px appears and
 * disappears between pages and the content + top bar visibly jump.
 *
 * The clean CSS tool for this is `scrollbar-gutter: stable` (reserve the lane
 * always, but only paint a scrollbar when there is something to scroll). BUT on
 * the ROOT element (<html>) Chromium reserves that gutter on BOTH inline edges,
 * which insets the whole app and reads as the top bar being "cut off" left and
 * right. `overflow-y: scroll` on the root has the same root-only quirk (a
 * phantom mirrored margin) and additionally shows an empty track on short pages.
 *
 * Fix: move the scroll container down one level to <body> (a non-root element,
 * where scrollbar-gutter behaves per spec — reserves the right edge only, no
 * track when the page fits). Tabler already ships `body { height:100%;
 * min-height:100% }` and `.page { min-height:100% }` for exactly this; the only
 * missing piece was a height on <html> so the body percentage height resolves.
 * `scrollIntoView` (the only scroll JS in the app) targets the nearest scroll
 * container, so it keeps working. */
html { height: 100%; overflow: hidden; }
body { overflow-y: auto; scrollbar-gutter: stable; }

/* Classic (non-overlay) scrollbars always paint the track, so the reserved
   gutter shows as an empty groove on pages that do not scroll. Make ONLY the
   body scrollbar's track transparent so the reserved lane is invisible when
   empty; a subtle rounded thumb appears only when there is something to scroll
   (overflow-y:auto paints no thumb on short pages). This uses only the
   ::-webkit-scrollbar pseudo-elements — NOT the inheritable scrollbar-width /
   scrollbar-color, which would leak into inner scrollers (tables, dropdowns). */
body::-webkit-scrollbar { width: 14px; }
body::-webkit-scrollbar-track { background: transparent; }
body::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, .22);
    border-radius: 10px;
    border: 3px solid transparent;
    background-clip: padding-box;
}
body::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, .38); background-clip: padding-box; }
[data-bs-theme="dark"] body::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .2); background-clip: padding-box; }
[data-bs-theme="dark"] body::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, .34); background-clip: padding-box; }

/* With the gutter always reserved, locking the page scroll for a modal or
   offcanvas never frees up width — so Bootstrap's compensating padding-right
   (added inline to <body> and to fixed/sticky bars) would instead shove the
   whole layout left by a scrollbar width every time a dialog opens. Cancel it
   so opening a dialog leaves the page perfectly still. */
body.modal-open { padding-right: 0 !important; }
.modal-open .fixed-top,
.modal-open .sticky-top,
.modal-open .navbar.fixed-top { padding-right: 0 !important; }
