/* CRM.1: :where(:not(.kt-in-crm *)) keeps the broad !important rules of this file off the precon screens drawn inside the CRM
   layout (_PreconInCrm), which keep the type scale, colours and spacing they have on the precon site — a span forced to 15px
   turns its 30px figure and its 12px caption into the same size. :where() adds no specificity: on every other page each rule
   wins exactly where it won before. */
p:where(:not(.kt-in-crm *)) {
	font-size: 15px !important;	
}
label:where(:not(.kt-in-crm *)) {
	font-size: 15px !important;	
}
input:where(:not(.kt-in-crm *)) {
	font-size: 15px !important;	
}
select:where(:not(.kt-in-crm *)) {
	font-size: 15px !important;	
}
span:where(:not(.kt-in-crm *)) {
	font-size: 15px !important;	
}
a:where(:not(.kt-in-crm *)) {
	font-size: 15px !important;	
}
button:where(:not(.kt-in-crm *)) {
	font-size: 15px !important;	
}

table th:where(:not(.kt-in-crm *)) {
    background-color: #d5f3ff !important;
}

/* Uniform header cells on every list table — DataTables and server-rendered alike. */
.dataTable th,
.table thead th:where(:not(.kt-in-crm *)) {
    /* 12px sides, raised from 4px on request 2026-09-22 so a header sits clear of its cell edge
       and of the column beside it. The VERTICAL padding is left alone deliberately: the comment
       below records that 20px of text plus 8+8 is what makes a 36px header row, and changing it
       would move every list's header height. */
    padding-left: 12px !important;
    padding-right: 12px !important;
    padding-top: 8px !important;
    padding-bottom: 8px !important;
    line-height: 20px !important; /* 20px text + 8px/8px padding = 36px header row */
}

/* Table footers: give the record info and the paging controls room to breathe on both
   DataTables pages and the server-rendered CRM lists (bootstrap .pagination). */
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate {
    margin-top: .75rem;
    margin-bottom: 1.25rem;
}

/* …and put the two on ONE line: count on the left, pager on the right, instead of stacked.

   Floats rather than flex, and here rather than in the page scripts. Around thirty list scripts
   initialise DataTables with dom: 'lrtip', so it emits .dataTables_info and .dataTables_paginate as
   bare siblings of the table inside .dataTables_wrapper — none of the row/col wrappers its bootstrap
   integration normally adds. There is no element around just those two to make a flex row of, and
   giving the wrapper display:flex would take the table with it. Floating the pair is what
   DataTables' own default stylesheet does, and it fixes every listing page at once rather than
   asking thirty inits to agree.

   The clearfix is NOT optional, and not for the usual reason. .dataTables_wrapper does set
   overflow-x: auto further down this file, which would contain the floats by itself — but
   admin-sticky-head.js adds .crm-fits to every table narrow enough to need no horizontal scroll,
   and that class sets overflow: visible so the sticky header can escape. So containment would
   depend on whether a given table happened to fit its container: measured on /admin/customer, the
   wrapper computed overflow-x: visible and the floats hung out the bottom of it.

   >= 768px only. Below that crm-mobile.css restacks these deliberately, and a 332-page pager will
   not share a line with the count on a phone. */
@media (min-width: 768px) {
    .dataTables_wrapper > .dataTables_info {
        float: left;
        width: auto;
    }

    .dataTables_wrapper > .dataTables_paginate {
        float: right;
        width: auto;
    }

    .dataTables_wrapper::after {
        content: "";
        display: block;
        clear: both;
    }
}

.nk-content .pagination {
    margin-top: .75rem;
    margin-bottom: 1.25rem;
}

/* DataTables length ("Hiển thị N dữ liệu"): modern pill select + space before the table.
   Selectors mirror dashlite's (div-qualified) so these win the cascade, and the explicit
   margins neutralize any inherited negative offsets. */
div.dataTables_wrapper div.dataTables_length {
    /* Negative top eats into the banner's 1rem bottom margin for a snug fit. */
    margin-top: -6px !important;
    margin-bottom: 7px !important;
    padding-top: 6px;
}

div.dataTables_wrapper div.dataTables_length label {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: #526484;
    font-weight: 500;
    margin: 0;
}

div.dataTables_wrapper div.dataTables_length select {
    appearance: none;
    -webkit-appearance: none;
    min-width: 88px;
    height: auto;
    padding: 7px 36px 7px 18px !important;
    border-radius: 999px !important;
    border: 1.5px solid #b6ddef !important;
    background: #fff url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='12'%20height='8'%20viewBox='0%200%2012%208'%3E%3Cpath%20d='M1%201l5%205%205-5'%20fill='none'%20stroke='%233f74d6'%20stroke-width='2'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 14px center / 12px 8px !important;
    font-weight: 600;
    color: #16315f;
    box-shadow: 0 2px 8px rgba(63, 116, 214, .10);
    transition: border-color .15s ease, box-shadow .15s ease;
    cursor: pointer;
}

div.dataTables_wrapper div.dataTables_length select:hover {
    border-color: var(--brand-1) !important;
}

div.dataTables_wrapper div.dataTables_length select:focus {
    outline: none;
    border-color: var(--brand-1) !important;
    box-shadow: 0 0 0 3px rgba(63, 116, 214, .18);
}
/* Body cells match the 12px the headers now carry, so a value lines up under its caption
   instead of starting 8px to its left. Sides only — row height stays where it was. */
.crmodd td {
    padding-left: 12px !important;
    padding-right: 12px !important;
    background-color: #f4fff9;
}
.crmeven td {
    padding-left: 12px !important;
    padding-right: 12px !important;
    background-color: #e3e7fc;
}
.redrow td {
    background-color: #ffc6cf !important;
}

/* ============================================================
   Responsive / mobile refinements (admin CRM + logistics)
   ============================================================ */

/* Data tables get horizontal scroll on small screens (auto-wrapped by JS). */
.table-responsive {
    -webkit-overflow-scrolling: touch;
}

/* Card headers used across the CRM pages stack cleanly on phones. */
@media (max-width: 767.98px) {
    .card-header h3 {
        font-size: 1.15rem;
        line-height: 1.3;
    }

    .card-header .text-md-end {
        text-align: left !important;
        margin-top: .5rem;
    }

    /* Keep rows on one line so the horizontal scroll (not wrapping) governs width. */
    .table.table-sm td,
    .table.table-sm th {
        white-space: nowrap;
    }

    /* Action buttons inside table cells breathe a little when they wrap. */
    td .btn,
    td form.d-inline {
        margin-bottom: 2px;
    }

    /* Button toolbars (AI hub panels, etc.) wrap instead of overflowing. */
    .card-inner .btn-group {
        flex-wrap: wrap;
    }

    /* Side "result" panels are pinned on desktop; let them flow on mobile. */
    .card[style*="sticky"] {
        position: static !important;
    }
}

/* Very small screens: filter/search bars stack their controls full width. */
@media (max-width: 575.98px) {
    form.row.g-2 > [class*="col-"] {
        flex: 0 0 100%;
        max-width: 100%;
    }

    /* Input groups (lead picker, enrol/adjust forms) stack instead of overflowing. */
    .input-group {
        flex-wrap: wrap;
    }

    .input-group > .form-control,
    .input-group > .form-select {
        flex: 1 1 100%;
        width: 100%;
        margin-bottom: 4px;
        border-radius: .25rem !important;
    }

    .input-group > .btn {
        flex: 1 1 100%;
        width: 100%;
        border-radius: .25rem !important;
    }

    .input-group > .input-group-text {
        border-radius: .25rem 0 0 .25rem;
    }
}

/* ------------------------------------------------------------
   Stacked "card" layout for key list tables on phones.
   Add class="... cards-sm" to the <table> and data-label="..."
   to each <td>. Below 768px each row becomes a labelled card.
   ------------------------------------------------------------ */
@media (max-width: 767.98px) {
    table.cards-sm thead {
        display: none;
    }

    table.cards-sm,
    table.cards-sm tbody,
    table.cards-sm tr,
    table.cards-sm td {
        display: block;
        width: 100%;
    }

    table.cards-sm tr {
        margin-bottom: .75rem;
        border: 1px solid #dbdfea;
        border-radius: .5rem;
        padding: .25rem .75rem;
        background: #fff;
        box-shadow: 0 1px 3px rgba(43, 55, 72, .08);
    }

    table.cards-sm td {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 1rem;
        border: none;
        border-bottom: 1px solid #f0f2f7;
        padding: .5rem .1rem;
        text-align: right;
        white-space: normal !important;
    }

    table.cards-sm td:last-child {
        border-bottom: none;
    }

    table.cards-sm td::before {
        content: attr(data-label);
        font-weight: 600;
        color: #526484;
        text-align: left;
        flex: 0 0 40%;
        white-space: nowrap;
    }

    /* Cells with no label (or hidden empties) don't render a label column. */
    table.cards-sm td:not([data-label])::before,
    table.cards-sm td[data-label=""]::before {
        content: none;
    }

    /* Action cells: full-width, left-aligned, buttons wrap. */
    table.cards-sm td.cell-actions {
        justify-content: flex-start;
        flex-wrap: wrap;
        gap: .25rem;
        padding-top: .6rem;
    }

    table.cards-sm td.cell-actions::before {
        content: none;
    }

    /* Footer total rows (e.g. quote total) stay a tidy single line as a card. */
    table.cards-sm tfoot tr {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        border: 1px solid #dbdfea;
        border-radius: .5rem;
        padding: .5rem .75rem;
        background: #f5f6fa;
        font-weight: 600;
    }

    table.cards-sm tfoot th {
        border: none;
        padding: 0;
    }

    table.cards-sm tfoot th:empty {
        display: none;
    }
}

/* ============================================================
   Modern theme layer — sidebar, background, header, cards.
   Applied globally (this stylesheet loads on every page).
   ============================================================ */
:root {
    --brand-1: #3f74d6;
    --brand-2: #4f86e6;
    --brand-3: #34c9d6;
    --brand-cyan: #3fd9c4;
}

/* ---- App background: soft, airy, hint of color ---- */
.nk-body {
    background:
        radial-gradient(1100px 560px at 100% -8%, rgba(63,116,214,.12), transparent 60%),
        radial-gradient(880px 500px at -8% 112%, rgba(63,217,196,.10), transparent 55%),
        linear-gradient(160deg, #f1f6ff 0%, #e8f0fb 100%) !important;
    background-attachment: fixed !important;
}

/* ---- Sidebar (left panel menu) ---- */
.nk-sidebar {
    background: linear-gradient(185deg, #16315f 0%, #102a4e 45%, #0d2240 100%) !important;
    border-right: none !important;
    box-shadow: 10px 0 34px rgba(19, 14, 46, .22);
}

/* Desktop sidebar only. Below 768px the same element is the app-launcher tile
   grid in crm-mobile.css, which needs its own padding AND a bottom safe-area
   inset; this file loads after that one and at equal specificity was silently
   winning, so an unscoped rule here deletes the inset and drops the last row of
   tiles under the home indicator. */
@media (min-width: 768px) {
    /* No horizontal padding: the links carry their own 14px and the rounded
       hover state is meant to reach the sidebar edge. The vertical 10px stays. */
    .nk-sidebar .nk-menu {
        padding: 10px 0;
    }
}

.nk-sidebar .nk-menu-item {
    margin: 2px 0;
}

.nk-sidebar .nk-menu-link {
    border-radius: 11px;
    padding: 10px 14px;
    color: #aebbd9 !important;
    transition: background .18s ease, color .18s ease, transform .18s ease, box-shadow .18s ease;
}

.nk-sidebar .nk-menu-link .nk-menu-icon {
    color: #8093bd;
    transition: color .18s ease, transform .18s ease;
}

.nk-sidebar .nk-menu-link .nk-menu-text {
    font-weight: 500;
    letter-spacing: .1px;
}

.nk-sidebar .nk-menu-link:hover {
    color: #fff !important;
    background: rgba(63, 116, 214, .15);
    transform: translateX(3px);
}

.nk-sidebar .nk-menu-link:hover .nk-menu-icon {
    color: #5fe0cf;
    transform: scale(1.06);
}

/* Active item: gradient pill + glow */
.nk-sidebar .nk-menu-item.active > .nk-menu-link,
.nk-sidebar .nk-menu-link.active,
.nk-sidebar .nk-menu-item.current-menu > .nk-menu-link {
    color: #fff !important;
    background: linear-gradient(135deg, var(--brand-1) 0%, var(--brand-2) 100%);
    box-shadow: 0 8px 22px rgba(63, 116, 214, .42);
}

.nk-sidebar .nk-menu-item.active > .nk-menu-link .nk-menu-icon,
.nk-sidebar .nk-menu-link.active .nk-menu-icon {
    color: #fff;
}

/* Sub-menu: guide line + softer active */
.nk-sidebar .nk-menu-sub {
    margin: 3px 0 5px 12px;
    padding-left: 8px;
    border-left: 1px dashed rgba(150, 168, 210, .28);
}

.nk-sidebar .nk-menu-sub .nk-menu-link {
    padding: 8px 12px;
    font-size: 13px;
    color: #9aa9cb !important;
}

/* Dashlite indents sub-menu links 60px; matches its specificity to pull them in (60 → 32px). */
.nk-sidebar .nk-menu > li .nk-menu-sub .nk-menu-link {
    padding-left: 32px;
}

/* A sub-menu inside a sub-menu («Thiết Kế» › Cài đặt, 2026-10-30). Dashlite indents it another 60px, which left its labels
   about 160px and clipped «Duyệt mẫu theo họ bản vẽ» mid-word; this keeps it one step in, under its parent's label, and
   lets a label that still does not fit wrap onto a second line rather than lose its end. */
.nk-sidebar .nk-menu > li .nk-menu-sub .nk-menu-sub {
    margin: 2px 0 4px 32px;
    padding-left: 4px;
}

.nk-sidebar .nk-menu > li .nk-menu-sub .nk-menu-sub .nk-menu-link {
    padding-left: 12px;
}

.nk-sidebar .nk-menu-sub .nk-menu-sub .nk-menu-text {
    white-space: normal;
    line-height: 1.35;
}

.nk-sidebar .nk-menu-sub .nk-menu-link:hover {
    color: #fff !important;
    background: rgba(63, 116, 214, .13);
}

/* Sidebar labels 3px larger (owner, 2026-10-31: "change the font-size bigger than current 3px"): 18px at every level. What
   showed was 15px at both levels, top and sub-menu: the `span` and `a` rules at the top of this file force 15px !important,
   which beat Dashlite's 15px/14px and the sub-menu's 13px above. So this is !important too, with the specificity to outrank
   them. A stored Appearance font size (Theme.FontSizePx) moves it with the text: _Layout's theme block sets that size + 3px.
   Desktop only: below 768px the same element is the phone's tile grid, sized in crm-mobile.css. At 18px the longest labels
   («Báo Cáo & Thống Kê», «Duyệt mẫu theo họ bản vẽ») no longer fit the sidebar's width, and Dashlite keeps .nk-menu-text on
   one line, which cut them off; they wrap onto a second line instead. */
@media (min-width: 768px) {
    .nk-sidebar .nk-menu-link .nk-menu-text {
        font-size: 18px !important;
        white-space: normal;
        line-height: 1.3;
    }
}

.nk-sidebar .nk-menu-sub .nk-menu-item.active > .nk-menu-link,
.nk-sidebar .nk-menu-sub .nk-menu-link.active {
    color: #fff !important;
    background: rgba(63, 116, 214, .22);
    box-shadow: none;
}

.nk-sidebar .nk-menu-badge {
    background: linear-gradient(135deg, var(--brand-2), var(--brand-3)) !important;
    color: #fff !important;
    border: none;
}

.nk-sidebar .simplebar-scrollbar:before {
    background: rgba(150, 168, 210, .38);
}

/* ---- Header (top bar): light glass ---- */
.nk-header.is-light {
    background: rgba(255, 255, 255, .78) !important;
    backdrop-filter: saturate(150%) blur(9px);
    -webkit-backdrop-filter: saturate(150%) blur(9px);
    box-shadow: 0 2px 18px rgba(43, 55, 90, .06);
    border-bottom: 1px solid rgba(120, 139, 255, .10);
}

/* ---- Cards: soft, elevated, rounded ---- */
.card,
.card-bordered {
    border: 1px solid rgba(120, 139, 255, .10) !important;
    border-radius: 14px !important;
    box-shadow: 0 6px 22px rgba(43, 55, 90, .06);
    transition: transform .18s ease, box-shadow .18s ease;
}

.card-bordered:hover {
    box-shadow: 0 12px 32px rgba(43, 55, 90, .11);
}

/* Content area sits flush — dashlite's 32px/22px .nk-content padding removed. */
.nk-content {
    padding: 0 !important;
}

/* Tight card body (was 12px 20px). */
/* 12px on all four sides, raised from 6px on request 2026-09-22.

   This one rule is why forms across the admin sat flush against their card edge — the leave-request
   form in the screenshot, and every other .card-body with it. 6px is not a gutter, it is a hairline,
   and at that size a full-width <select> or <textarea> reads as touching the border.

   Global on purpose: the ask was "apply to other similar forms where the padding is under 12px",
   and this rule IS the padding for every one of them. Note it carries !important and sits after
   Bootstrap, so it already outranks a p-0 / p-1 / p-2 utility written on a .card-body — those were
   getting 6px before this change, not the 0/4/8px their class name suggests. A view that genuinely
   needs a flush card body has to say so with its own more specific selector. */
.card-body:where(:not(.kt-in-crm *)) {
    padding: 12px !important;
}

/* The sign-in card wants breathing room instead: it is a narrow, standalone card rather than one
   panel in a dense list. !important is required only to beat the global rule above. */
.card-body.auth-card-body {
    padding-left: 30px !important;
    padding-right: 30px !important;
}

/* Page banners / search panels match the table header colour so the page reads as one
   palette. One rule set styles every list/search panel (Customer, Leads, Tasks, Settings, ...). */
.card-header.bg-info.bg-gradient,
.card-header.bg-info {
    background: #d5f3ff !important; /* same as table th */
    border: none !important;
    border-radius: 14px !important;
    box-shadow: 0 4px 14px rgba(63, 116, 214, .10);
    padding: 1.25rem 1.5rem !important;
    margin-bottom: 2px !important; /* beats per-view mb-2/mb-3 utilities for a uniform gap */
}

/* Even vertical rhythm: title row → filter rows → action row. */
.card-header.bg-info .row + .row {
    margin-top: .875rem;
    margin-bottom: 0 !important;
}

/* Filter fields breathe evenly, also when they wrap on smaller screens. */
.card-header.bg-info .row > [class*="col-"] {
    margin-bottom: .5rem;
}

.card-header.bg-info label {
    margin-bottom: .375rem;
    font-weight: 500;
    font-size: .8125rem;
    letter-spacing: .01em;
}

/* Inputs/selects on the banner: consistent soft edge on the light-blue panel. */
.card-header.bg-info .form-control {
    border: 1px solid #b6ddef;
    box-shadow: 0 1px 3px rgba(22, 49, 95, .08);
}

/* Searchable dropdowns (Select2 via crm-select.js): hide dashlite's wrapper arrow
   behind the Select2 container so selects don't show a double chevron, and give the
   Select2 box the same soft edge as other banner inputs. */
.form-control-select:has(.select2-container)::after {
    display: none;
}

.card-header.bg-info .select2-container--default .select2-selection--single {
    border: 1px solid #b6ddef;
    box-shadow: 0 1px 3px rgba(22, 49, 95, .08);
}

/* Search/reset action row: right-aligned flex with a uniform gap. Flex neutralizes the
   per-view float:right hacks, and primary action naturally sits rightmost. */
.card-header.bg-info .col-12.mt-4 {
    display: flex;
    justify-content: flex-end;
    gap: .75rem;
    margin-top: .5rem !important;
    margin-bottom: 0;
}

.card-header.bg-info .col-12.mt-4 .btn {
    float: none;
}

/* ---- Buttons: subtle gradient on primary ---- */
.btn-primary:where(:not(.kt-in-crm *)) {
    background: linear-gradient(135deg, var(--brand-1) 0%, var(--brand-2) 100%) !important;
    border: none !important;
    box-shadow: 0 4px 14px rgba(63, 116, 214, .30);
    transition: filter .15s ease, box-shadow .15s ease;
}

.btn-primary:hover {
    filter: brightness(1.06);
    box-shadow: 0 7px 20px rgba(63, 116, 214, .42);
}

.badge {
    border-radius: 6px;
    letter-spacing: .2px;
}

/* Softer, modern scrollbars app-wide */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(120, 139, 255, .35) transparent;
}
*::-webkit-scrollbar {
    width: 9px;
    height: 9px;
}
*::-webkit-scrollbar-thumb {
    background: rgba(120, 139, 255, .35);
    border-radius: 8px;
}
*::-webkit-scrollbar-thumb:hover {
    background: rgba(120, 139, 255, .55);
}

/* Labels/titles on the light-blue banner use the navy heading colour for contrast
   (overrides the text-white utility the views still carry from the gradient era). */
.card-header.bg-info label,
.card-header.bg-info .form-label,
.card-header.bg-info .control-label,
.card-header.bg-info legend,
.card-header.bg-info p,
.card-header.bg-info h1,
.card-header.bg-info h2,
.card-header.bg-info h3,
.card-header.bg-info h4,
.card-header.bg-info h5,
.card-header.bg-info h6 {
    color: #16315f !important;
}

/* Wider sidebar: +28px (242 → 270) so the VVB Solutions brand fits and the menu
   breathes. Scoped with :not(.is-compact) so the 74px compact-collapse still works. */
.nk-sidebar:not(.is-compact) {
    width: 270px;
}

@media (min-width: 1200px) {
    .nk-sidebar:not(.is-compact) {
        width: 270px;
    }

    .nk-sidebar:not(.is-compact) + .nk-wrap,
    .nk-sidebar-overlay + .nk-wrap {
        padding-left: 270px;
    }

    .nk-sidebar:not(.is-compact) + .nk-wrap > .nk-header-fixed,
    .nk-sidebar-overlay + .nk-wrap > .nk-header-fixed {
        left: 270px;
    }
}

/* A compact sidebar expanded by hover falls back to DashLite's base 242px, because the 270px rule
   above is scoped :not(.is-compact) and stops applying the moment it collapses. Match it, or hovering
   the rail opens a panel 28px narrower than the sidebar it stands in for and every label wraps
   somewhere different than it does when the sidebar is pinned open. */
@media (min-width: 1200px) {
    .nk-sidebar.is-compact.has-hover {
        width: 270px;
    }

    /* Centre the glyphs in the collapsed rail.
       The row is built for "icon then label": .nk-menu-link is a flex row padded 10px 14px, and
       .nk-menu-icon is a fixed 36px box left-aligned inside it. That is right at full width and
       wrong at 74px — the label is only transparent, not gone, so the icon stays pinned left and
       the glyph lands 11px left of the rail's centre with dead space to its right, which is what
       you see as a column of icons that are not quite in the middle.

       Dropping the link's side padding and letting the icon fill the row centres it without
       touching .nk-menu-text, whose opacity fade is what animates the expand. The label keeps its
       place in the flex flow, is pushed past the right edge, and is clipped by the sidebar's own
       overflow: hidden. Scoped :not(.has-hover) so the hovered panel keeps the normal layout. */
    .nk-sidebar.is-compact:not(.has-hover) .nk-menu-link {
        padding-left: 0;
        padding-right: 0;
    }

    .nk-sidebar.is-compact:not(.has-hover) .nk-menu-icon {
        width: 100%;
        text-align: center;
    }

    /* Centre the hamburger over the column of icons below it.
       Nothing here is about the button itself. .nk-sidebar-head is a 242px flex row with 24px side
       padding — sized for the expanded sidebar and merely clipped by the 74px rail — and the brand
       still occupies ~155px of it because DashLite only fades the logo to opacity 0. So there was
       nothing to centre against: the button sat at the head's padding-left and measured 6px off.
       Taking the brand out of flow and dropping the head's padding gives the trigger the full rail
       to centre in. All :not(.has-hover), so the expanded panel keeps its normal header. */
    .nk-sidebar.is-compact:not(.has-hover) .nk-sidebar-head {
        width: 100%;
        padding-left: 0;
        padding-right: 0;
        justify-content: center;
    }

    .nk-sidebar.is-compact:not(.has-hover) .nk-sidebar-brand {
        display: none;
    }

    .nk-sidebar.is-compact:not(.has-hover) .nk-menu-trigger {
        margin: 0;
    }

    .nk-sidebar.is-compact:not(.has-hover) .nk-nav-compact {
        padding-right: 0;
    }
}

@media (max-width: 991.98px) {
    .nk-sidebar-mobile {
        width: 270px !important;
    }
}


/* ---- Dashboard: hero KPI cards, attention list, action lists ---- */
.dash-hero {
    border: none;
    border-radius: 14px;
    color: #fff;
    overflow: hidden;
}

.dash-hero .card-inner { padding: 18px 20px; }
.dash-hero .hero-label { font-size: .8125rem; opacity: .88; letter-spacing: .01em; }
.dash-hero .hero-value { font-size: 1.65rem; font-weight: 700; line-height: 1.25; margin: 2px 0; color: #fff; }
.dash-hero .hero-sub { font-size: .78rem; opacity: .92; }
.dash-hero .hero-sub strong { color: #fff; }
.dash-hero .hero-sub.up em { color: #b6ffdc; }
.dash-hero .hero-sub.down em { color: #ffd2cc; }

.dash-hero .hero-icon {
    width: 44px;
    height: 44px;
    border-radius: 12px;
    background: rgba(255, 255, 255, .18);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    color: #fff;
    flex-shrink: 0;
}

.hero-a { background: linear-gradient(135deg, #3f74d6, #2a4f9e); box-shadow: 0 8px 20px rgba(63, 116, 214, .28); }
.hero-b { background: linear-gradient(135deg, #12b3a2, #0b7d72); box-shadow: 0 8px 20px rgba(18, 179, 162, .28); }
.hero-c { background: linear-gradient(135deg, #8b5cf6, #5f36c9); box-shadow: 0 8px 20px rgba(139, 92, 246, .28); }
.hero-d { background: linear-gradient(135deg, #f4a10e, #cf7e00); box-shadow: 0 8px 20px rgba(244, 161, 14, .28); }

.dash-attention a {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 6px;
    border-radius: 8px;
    color: #526484;
}

.dash-attention a:hover { background: #f4f8ff; color: #16315f; }
.dash-attention .num { margin-left: auto; font-weight: 700; }

.dash-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 9px 4px;
    border-bottom: 1px dashed #e6ecf5;
}

.dash-list li:last-child { border-bottom: none; }
.dash-list a { min-width: 0; }
.dash-list .t { display: block; font-weight: 600; color: #16315f; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 240px; }
.dash-list .s { display: block; font-size: .75rem; color: #8094ae; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 240px; }
.dash-list .r { text-align: right; flex-shrink: 0; }
.dash-list .v { display: block; font-weight: 600; font-size: .8125rem; color: #364a63; }

/* ============================================================
   Global mobile polish (phones & small tablets).
   Additive layer applied on every page (this stylesheet loads
   on both the app layout and the auth layout). No per-view
   markup changes required — it hardens the shared patterns the
   CRM uses everywhere: page/card headers, action toolbars, tab
   strips, modals, media, code blocks and container padding.
   ============================================================ */

/* ------------------------------------------------------------
   THE PAGE IS NEVER WIDER THAN THE DEVICE — at EVERY width.
   ------------------------------------------------------------
   These guards used to live only inside `@media (max-width: 991.98px)`,
   so a desktop browser had no protection at all: on 2026-09-16
   /admin/mobileaccount measured 1113px against a 1024px viewport and
   cut the "Xóa" button and the actions column off at the right edge.
   A laptop at 1024-1280px is a device like any other, so the rules
   apply unconditionally now.

   The principle, in order of preference:
     1. content that can wrap, wraps (see .crm-filter-actions);
     2. content that genuinely needs width scrolls inside ITS OWN box;
     3. the clip below is only a net for sub-pixel gutter bleed.
   Reaching for 3 to hide a real overflow makes content unreachable,
   which is worse than a scrollbar. ------------------------------- */
html,
body {
    max-width: 100%;
}

/* Media never forces the page wider than the screen. */
img,
video,
canvas,
svg.responsive-svg {
    max-width: 100%;
}

/* Pre-formatted / code blocks scroll internally instead of
   stretching the layout (JSON viewers, audit payloads, etc.). */
pre,
code,
.font-monospace {
    max-width: 100%;
    overflow-x: auto;
    word-break: break-word;
}

/* ---- Tablets and below: ≤ 991.98px ---- */
@media (max-width: 991.98px) {
    /* Page/card header rows (title on the left, action buttons on
       the right) wrap gracefully instead of colliding. Covers both
       the DashLite block header and the Bootstrap card header the
       CRM listing/detail pages use. */
    .nk-block-between,
    .nk-block-head-content + .nk-block-head-content {
        flex-wrap: wrap;
        gap: .5rem;
    }

    /* Tab strips scroll sideways rather than wrapping into a tall,
       broken stack (opportunity/detail pages have many tabs). */
    .nav-tabs,
    .nk-nav.nav-tabs,
    .nav.nav-tabs-s2 {
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
    }

    .nav-tabs .nav-item {
        flex: 0 0 auto;
    }
}

/* ---- Phones: ≤ 767.98px ---- */
@media (max-width: 767.98px) {
    /* Reclaim the horizontal space desktop padding eats up so
       forms and tables get the full narrow viewport. */
    .nk-content {
        padding: 18px 0;
    }

    .card-inner,
    .card-inner-lg {
        padding: 1rem;
    }

    /* Listing headers built as a Bootstrap .row inside .card-header:
       the right-aligned "add / actions" column becomes left-aligned
       full width under the title instead of a cramped right sliver. */
    .card-header .row > [style*="text-align: right"],
    .card-header .row > [style*="text-align:right"] {
        text-align: left !important;
        margin-top: .5rem;
    }

    /* Action buttons in page/card headers go full width and stack so
       they are easy tap targets and never overflow. */
    .card-header .row > [style*="text-align: right"] .btn,
    .card-header .row > [style*="text-align:right"] .btn,
    .nk-block-tools .btn {
        margin-bottom: .25rem;
    }

    /* Every other action row: buttons wrap onto their own line on a phone and would otherwise sit
       edge to edge. Most headers align their actions with the Bootstrap utility classes rather than
       the inline style matched above — `text-end` and `text-md-end` are two different class names,
       so both need listing. */
    [class*="text-end"] > .btn,
    [class*="text-md-end"] > .btn,
    [class*="text-right"] > .btn,
    .card-tools .btn,
    .btn-toolbar > .btn,
    .crm-filter-actions .btn,
    .dash-toolbar-actions .btn,
    .modal-footer > .btn {
        margin-top: .25rem;
        margin-bottom: .25rem;
    }

    /* Buttons that are meant to touch: a segmented group, an input add-on or a pager reads as one
       control, so the spacing above must not break them apart. */
    .btn-group > .btn,
    .btn-group-sm > .btn,
    .input-group > .btn,
    .pagination .btn,
    .dash-tools .btn {
        margin-top: 0;
        margin-bottom: 0;
    }

    /* Button toolbars / tool groups wrap instead of overflowing. */
    .btn-toolbar,
    .card-tools,
    .nk-block-tools,
    .btn-group {
        flex-wrap: wrap;
    }

    /* Headings scale down so long Vietnamese titles don't wrap into
       three lines and push content off-screen. */
    h1, .h1 { font-size: 1.5rem; }
    h2, .h2 { font-size: 1.35rem; }
    h3, .h3, .card-header h3 { font-size: 1.2rem; line-height: 1.3; }

    /* Modals use nearly the full width and scroll their body so tall
       CRM forms (quotes, tickets) are usable on a phone. */
    .modal-dialog {
        margin: .5rem;
        max-width: calc(100% - 1rem);
    }

    .modal-body {
        max-height: 75vh;
        overflow-y: auto;
    }

    /* Bootstrap grid form filters (label + control per column) each
       take the full row so nothing is squeezed. Complements the
       .row.g-2 rule already defined above for search bars. */
    .card-header .row > [class*="col-"] {
        margin-bottom: .25rem;
    }
}

/* ---- Small phones: ≤ 575.98px ---- */
@media (max-width: 575.98px) {
    /* Header action buttons span the full width on the smallest
       screens for comfortable one-thumb tapping. */
    .card-header .row > [style*="text-align: right"] .btn,
    .card-header .row > [style*="text-align:right"] .btn {
        display: block;
        width: 100%;
    }

    /* Pagination stays on one scrollable line instead of wrapping. */
    .pagination {
        flex-wrap: wrap;
    }
}

/* ------------------------------------------------------------
   Basic / advanced search on filtered listings (wired in crm-select.js).

   The toggle gates the advanced filters at EVERY width, not just on
   phones. It used to be desktop-hidden, with the collapse rule scoped
   to a media query so the panel could never stay shut on a wide screen
   — which meant a listing opened with a ten-field filter form
   permanently occupying the top of the page, and no way to put it away.

   The panel is NOT hidden wholesale. Every DataTables grid in this app
   is built with dom: 'lrtip', i.e. with the library's own search box
   deliberately dropped, and no GetAll endpoint reads search[value] — so
   this filter form IS the page's only search. Hiding all of it would
   leave those listings with no way to search at all. The first text
   field and the Tìm / Xóa lọc buttons are therefore marked
   .crm-search-always and stay put as the basic search; everything else
   collapses behind the button.

   Collapsed is the DEFAULT but not the memory: crm-select.js records
   each page's last state, so a page someone works filtered in stays
   open for them.
   ------------------------------------------------------------ */
.crm-search-toggle {
    display: inline-flex;
    width: auto;
    align-items: center;
    gap: 8px;
    margin-bottom: .75rem;
    padding: .5rem .85rem;
    font-size: .9375rem;
    font-weight: 600;
    color: #364a63;
    background: #f5f6fa;
    border: 1px solid #dbdfea;
    border-radius: .5rem;
}

/* The toggle lives in the search shell's header (see filter-panel.css), where the
   standalone bottom margin would push it out of line with the caption beside it. */
.crm-search-shell .crm-search-toggle {
    margin-bottom: 0;
    white-space: nowrap;
}

.crm-search-toggle .crm-search-caret {
    margin-left: auto;
    transition: transform .2s ease;
}

.crm-search-toggle.is-open .crm-search-caret {
    transform: rotate(180deg);
}

/* Child-level, not panel-level: the basic field and its buttons survive. */
.crm-search-panel.is-collapsed > :not(.crm-search-always) {
    display: none !important;
}

/* Collapsed, the two or three survivors keep their col-md-2 / col-md-3 widths and wrap into a
   ragged block with most of the row empty. Size them to their content instead — only while
   collapsed, so the expanded grid is exactly the layout each page already had. */
@media (min-width: 576px) {
    .crm-search-panel.is-collapsed > .crm-search-always {
        flex: 0 0 auto;
        width: auto;
        max-width: none;
    }

    .crm-search-panel.is-collapsed > .crm-search-always .form-control,
    .crm-search-panel.is-collapsed > .crm-search-always .form-select {
        min-width: 16rem;
    }

    /* crm-filter-actions wraps its buttons to fit a narrow column; with the column now sized to
       content there is nothing to fit to, and wrapping just stacks two buttons for no reason. */
    .crm-search-panel.is-collapsed > .crm-search-always.crm-filter-actions {
        flex-wrap: nowrap;
    }
}

@media (max-width: 575.98px) {
    /* Full width on a phone, where a floated pill next to nothing looks stranded. */
    .crm-search-toggle {
        width: 100%;
    }
}

/* ------------------------------------------------------------
   Listing tables & page-level overflow guards (phones/tablets).
   ------------------------------------------------------------ */
/* DataTables build their own .dataTables_wrapper, so the global table
   auto-wrap (in _Layout) intentionally skips them. Give that wrapper the
   horizontal scroll instead, so wide grids (customers, users, tasks, …)
   never push the page wider than the screen — on a laptop as much as on
   a phone, which is why this is not inside a media query. */
.dataTables_wrapper {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* DataTables lays its length/search/pager controls in Bootstrap rows whose
   negative gutter would otherwise bleed a few px past the viewport. */
.dataTables_wrapper > .row {
    margin-left: 0;
    margin-right: 0;
}

@media (max-width: 991.98px) {

    /* Safety net: clip any small horizontal bleed at the content edge
       (e.g. a top-level .row's negative gutter on pages that render a
       bare .card-header). `clip` establishes no scroll container, so
       inner scrollers (.table-responsive, .dataTables_wrapper) keep
       their own horizontal scroll and sticky/fixed chrome is unaffected. */
    .nk-content-body {
        overflow-x: clip;
    }

    /* ...which trims anything that cannot wrap: "0% hoàn tất" on the project form and the URL pills on
       Quản lý menu lost up to 84px at 390px (layout audit 2026-10-07). Let them wrap instead. */
    .nk-content-body .badge,
    .nk-content-body .url-pill {
        white-space: normal;
        max-width: 100%;
        overflow-wrap: anywhere;
    }

    /* A stage accordion's header is one flex line: a long stage chip pushed its "0% hoàn tất" badge
       past the edge. Let the header wrap so the badge drops below the chip. */
    .nk-content-body .accordion-button {
        flex-wrap: wrap;
        row-gap: 4px;
    }
    .nk-content-body .accordion-button .chip {
        white-space: normal;
        max-width: 100%;
    }
}

/* ------------------------------------------------------------
   Projects upsert form (#frmProject): the desktop layout places
   tiny label columns (col-2) beside inputs (col-6) with empty
   spacer columns. On phones those fixed widths crush the labels,
   so stack every field full-width (label above input) and drop
   the now-pointless empty spacer/filler columns.
   ------------------------------------------------------------ */
@media (max-width: 767.98px) {
    #frmProject > [class*="col-"],
    #frmProject .row > [class*="col-"] {
        flex: 0 0 100%;
        max-width: 100%;
        margin-top: .25rem;
    }

    /* Columns that hold no actual control (spacers / layout fillers,
       which contain only an HTML comment) collapse away. :has keeps
       this robust; where unsupported the rule is simply ignored and
       the fields still stack (just with a little extra spacing). */
    #frmProject > [class*="col-"]:not(:has(*)),
    #frmProject .row > [class*="col-"]:not(:has(*)) {
        display: none;
    }

    #frmProject label.control-label {
        margin-top: .35rem;
        margin-bottom: .1rem;
        font-weight: 600;
    }

    /* Save / Back / Delete become full-width, easy-to-tap, stacked. */
    #frmProject > .mt-4 .btn {
        width: 100%;
        margin: 0 0 .5rem 0 !important;
        float: none !important;
    }
}

/* ============================================================
   Project Type / Stage admin: icon + colour pickers, and the
   coloured badges / process timeline used across projects.
   ============================================================ */

/* Icon picker grid */
#iconPickerGrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
    gap: 8px;
    max-height: 220px;
    overflow-y: auto;
    padding: 6px;
    border: 1.5px solid #dbe4f0;
    border-radius: 12px;
    background: #fbfdff;
}
.icon-pick {
    height: 44px;
    border: 1.5px solid #e2e8f2;
    border-radius: 10px;
    background: #fff;
    color: #526484;
    cursor: pointer;
    transition: all .14s ease;
}
.icon-pick .icon { font-size: 20px; }
.icon-pick:hover { border-color: var(--brand-1); color: var(--brand-1); transform: translateY(-1px); }
.icon-pick.selected {
    border-color: var(--brand-1);
    background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
    color: #fff;
    box-shadow: 0 4px 12px rgba(63,116,214,.35);
}

/* Colour swatches */
#colorSwatches { display: flex; flex-wrap: wrap; gap: 8px; }
.swatch {
    width: 30px; height: 30px;
    border-radius: 8px;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1.5px #dbe4f0;
    cursor: pointer;
    padding: 0;
    transition: transform .12s ease, box-shadow .12s ease;
}
.swatch:hover { transform: scale(1.08); }
.swatch.selected { box-shadow: 0 0 0 2.5px var(--brand-1); transform: scale(1.08); }

.lookup-preview {
    display: inline-flex; align-items: center; gap: 10px;
    padding: 10px 16px; border-radius: 12px;
    background: #f4f8ff; border: 1.5px dashed #cddaf0;
    font-weight: 600;
}
.lookup-preview .icon { font-size: 24px; }

/* Coloured pill badge for a type / stage / status */
.chip {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px 10px; border-radius: 999px;
    font-size: 12.5px; font-weight: 600; line-height: 1.4;
    color: #fff; white-space: nowrap;
}
.chip .icon { font-size: 13px; }
.chip.chip-soft { background: transparent; }

/* Swatch cell shown in the admin list tables */
.chip-dot {
    display: inline-block; width: 14px; height: 14px;
    border-radius: 4px; vertical-align: middle; margin-right: 6px;
}

/* -------- Project list (/admin/projects) -------- */
/* One line per record. Only the body: a header that wraps costs one row of height, whereas a
   wrapped customer name made every record three to six lines tall. Not !important, so the phone
   card layout's white-space:normal !important in crm-mobile.css still lets a card wrap. The
   table is then wider than a narrow desktop, and .pj-scroll (projects.js's dom option) scrolls it. */
table[data-page-key="projects"] tbody td { white-space: nowrap; }
.pj-scroll { overflow-x: auto; }
table[data-page-key="projects"] .pj-pct {
    margin-left: 6px; font-size: 12.5px; font-weight: 600; color: #526484;
}
/* The assigned names are the one cell that can run to several hundred pixels on a single line,
   so they stop at 260px with an ellipsis and the full list is in the title. Desktop only: a phone
   card has room to wrap and shows the names in full. */
@media (min-width: 768px) {
    table[data-page-key="projects"] .pj-names {
        display: inline-block; max-width: 260px; vertical-align: middle;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
}

/* -------- Every listing table: one line per record (asked for on 2026-09-25) -------- */
/* The projects rule above, for every catalogued list (a table with data-page-key). A wrapped name,
   source or owner made a customer row three lines tall, and the action buttons stacked because the
   1%-wide actions column wrapped them. The table grows wider instead and its wrapper scrolls
   (.dataTables_wrapper / .table-responsive already overflow-x: auto). Desktop and tablet only: below
   768px the lists become cards (crm-mobile.css), which are meant to wrap. */
@media (min-width: 768px) {
    table[data-page-key] tbody td { white-space: nowrap; }
    /* Free text (a care note, a remark) can run to paragraphs: one line, cut with an ellipsis. Its
       <p>/<li>/<div> are flowed inline so the paragraphs join the line instead of stacking. The
       record's own page shows it in full. */
    table[data-page-key] .cell-clip {
        display: block; max-width: 320px;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    table[data-page-key] .cell-clip p,
    table[data-page-key] .cell-clip li,
    table[data-page-key] .cell-clip div { display: inline; margin: 0; }
    table[data-page-key] .cell-clip p + p::before,
    table[data-page-key] .cell-clip li + li::before { content: " · "; }
}

/* -------- Project detail: process timeline -------- */
.proc-track {
    display: flex; flex-wrap: wrap; gap: 6px 0; align-items: flex-start;
    counter-reset: step;
}
.proc-step { flex: 1 1 0; min-width: 90px; text-align: center; position: relative; }
.proc-step::before {
    content: ""; position: absolute; top: 21px; left: -50%; width: 100%; height: 3px;
    background: #e2e8f2; z-index: 0;
}
.proc-step:first-child::before { display: none; }
.proc-step.done::before { background: #22c55e; }
.proc-node {
    position: relative; z-index: 1;
    width: 44px; height: 44px; margin: 0 auto 6px;
    border-radius: 50%; display: flex; align-items: center; justify-content: center;
    background: #fff; border: 2.5px solid #e2e8f2; color: #8094ae;
    font-size: 19px;
}
.proc-step.done .proc-node,
.proc-step.current .proc-node { border-color: currentColor; color: #fff; }
.proc-step.current .proc-node { box-shadow: 0 0 0 5px rgba(63,116,214,.15); }
.proc-label { font-size: 12.5px; font-weight: 600; color: #364a63; }
.proc-step.pending .proc-label { color: #8094ae; }

/* -------- Project detail: vertical stage history -------- */
.tl { list-style: none; margin: 0; padding: 0; position: relative; }
.tl::before { content: ""; position: absolute; left: 19px; top: 6px; bottom: 6px; width: 2px; background: #e2e8f2; }
.tl-item { position: relative; padding: 0 0 18px 52px; }
.tl-dot {
    position: absolute; left: 8px; top: 2px;
    width: 24px; height: 24px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: 12px; box-shadow: 0 0 0 3px #fff;
}
.tl-card { background: #fff; border: 1px solid #e5e9f2; border-radius: 12px; padding: 10px 14px; box-shadow: 0 1px 3px rgba(43,55,72,.06); }
.tl-time { font-size: 12px; color: #8094ae; }

/* ============================================================
   Stage checklist editor + per-project activity tracking.
   ============================================================ */
.btn-xs { padding: 2px 7px; font-size: 12px; line-height: 1.3; border-radius: 7px; }
.badge.bg-danger-dim { background: #fde8e8 !important; color: #d92d20 !important; }
.badge.bg-warning-dim { background: #fef3e2 !important; color: #b25e09 !important; }

.chk-tree, .chk-subs { list-style: none; margin: 0; padding: 0; }
.chk-task { border: 1px solid #e5e9f2; border-radius: 12px; margin-bottom: 10px; background: #fff; box-shadow: 0 1px 3px rgba(43,55,72,.05); }
.chk-row { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; }
.chk-ic { font-size: 18px; color: var(--brand-1); margin-top: 2px; }
.chk-bullet { width: 8px; height: 8px; border-radius: 50%; background: #b6c2d9; margin: 8px 5px 0; flex: 0 0 auto; }
.chk-main { flex: 1 1 auto; min-width: 0; }
.chk-name { font-weight: 600; color: #253858; }
.chk-desc { font-size: 12.5px; color: #8094ae; margin-top: 2px; }
.chk-actions { display: flex; gap: 4px; flex: 0 0 auto; }
.chk-subs { border-top: 1px dashed #e9edf5; padding: 4px 12px 8px 22px; }
.chk-sub { padding: 6px 0; border-bottom: 1px dashed #f0f2f7; }
.chk-subs .chk-sub:last-child { border-bottom: none; }

/* Per-project checklist (project detail) */
.pchk { list-style: none; margin: 0; padding: 0; }
.pchk-task > .pchk-row { background: #f7f9fd; border-radius: 8px; }
.pchk-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 10px; border-bottom: 1px solid #f0f2f7; }
.pchk-row.done .pchk-name { text-decoration: line-through; color: #8094ae; }
.pchk-sub { padding-left: 26px; }
.pchk-main { flex: 1 1 auto; min-width: 0; }
.pchk-name { font-weight: 600; color: #253858; }
.pchk-ctl { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.pchk-ctl select, .pchk-ctl input { font-size: 12.5px; }
.risk-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
.acc-head { cursor: pointer; }

/* ============================================================
   Modern project detail form (Projects/Upsert) — sectioned,
   label-on-top, responsive; professional on desktop + mobile.
   ============================================================ */
.project-card .card-header h3 { color: #16315f; }
.hdr-chips .chip { box-shadow: 0 2px 6px rgba(43,55,72,.12); }

.project-form .form-section { margin-bottom: 22px; }
.project-form .form-section-title {
    display: flex; align-items: center; gap: 8px;
    font-size: 13px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: #16315f; margin: 4px 0 0;
}
.project-form .form-section-title .icon { font-size: 18px; color: var(--brand-1); }
.project-form .sec-divider {
    height: 1px; margin: 8px 0 16px;
    background: linear-gradient(90deg, #dbe6f5 0%, rgba(219,230,245,0) 100%);
}
.project-form .form-label { font-weight: 600; color: #3a4a63; margin-bottom: .35rem; }
.project-form .form-control { border-radius: 10px; padding: .55rem .8rem; }
.project-form input[type="file"].form-control { padding: .4rem .6rem; }
.project-form .form-control:focus {
    border-color: var(--brand-1);
    box-shadow: 0 0 0 3px rgba(63,116,214,.15);
}

/* Uploaded-file chips */
.file-list { list-style: none; padding: 0; margin: .55rem 0 0; display: flex; flex-direction: column; gap: 6px; }
.file-list li {
    display: flex; align-items: center; gap: 8px;
    background: #f7f9fd; border: 1px solid #e9eef7; border-radius: 9px;
    padding: 6px 10px; font-size: 13px;
}
.file-list li img { width: 22px; height: 22px; flex: 0 0 auto; }
.file-list li a { flex: 1 1 auto; min-width: 0; color: #2456b3; }
.file-list li .delete-file { cursor: pointer; flex: 0 0 auto; }
.file-empty { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: #8094ae; margin-top: .5rem; }

.img-list { list-style: none; padding: 0; margin: .6rem 0 0; display: flex; flex-wrap: wrap; gap: 10px; }
.img-list li { position: relative; }
.img-list li img { width: 150px; height: 110px; object-fit: cover; border: 1px solid #e5e9f2; border-radius: 10px; }
.img-list li .delete-file {
    position: absolute; top: 6px; right: 6px; cursor: pointer;
    background: #fff; border-radius: 50%; padding: 3px; box-shadow: 0 1px 4px rgba(0,0,0,.2);
}

/* Sticky-ish action bar */
.project-form .form-actions {
    display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
    border-top: 1px solid #eef1f7; padding-top: 18px; margin-top: 6px;
}
@media (max-width: 575.98px) {
    .project-form .form-actions { flex-direction: column-reverse; align-items: stretch; }
    .project-form .form-actions .btn { width: 100%; justify-content: center; }
    .project-form .form-actions .ms-auto { width: 100%; }
    .project-form .form-actions .ms-auto .btn { flex: 1; }
}

/* ============================================================
   Project form polish: left inset, colour-separated section
   panels, and equal-size action buttons.
   ============================================================ */
.project-card > .card-header,
.project-card > .card-body { padding-left: 20px; padding-right: 20px; padding-bottom: 20px; }

/* Each info group becomes a tinted panel with a coloured left accent */
.project-form .form-section {
    background: #fbfdff;
    border: 1px solid #eaf0f9;
    border-left: 4px solid var(--brand-1);
    border-radius: 12px;
    padding: 14px 18px 18px;
    margin-bottom: 18px;
    box-shadow: 0 1px 2px rgba(43,55,72,.04);
}
.project-form .sec-blue   { border-left-color:#3f74d6; background:#f4f8ff; }
.project-form .sec-blue   .form-section-title .icon { color:#3f74d6; }
.project-form .sec-purple { border-left-color:#8b5cf6; background:#f8f6ff; }
.project-form .sec-purple .form-section-title .icon { color:#8b5cf6; }
.project-form .sec-teal   { border-left-color:#14b8a6; background:#f1fcfa; }
.project-form .sec-teal   .form-section-title .icon { color:#14b8a6; }
.project-form .sec-amber  { border-left-color:#f4a10e; background:#fffaf0; }
.project-form .sec-amber  .form-section-title .icon { color:#f4a10e; }
.project-form .form-section .sec-divider {
    background: linear-gradient(90deg, rgba(22,49,95,.16) 0%, rgba(22,49,95,0) 100%);
}
.project-form .form-section .form-control { background: #fff; }

/* Equal-size buttons (uniform W x H) — fixed height so gradient/shadow
   variants don't render taller than the plain one, keeping them on one line. */
.project-form .form-actions { align-items: center; }
.project-form .form-actions .ms-auto { display: inline-flex; align-items: center; gap: 10px; }
.project-form .form-actions .btn {
    width: 150px;
    height: 46px;
    padding-top: 0;
    padding-bottom: 0;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-weight: 600;
    border-radius: 10px;
    vertical-align: middle;
}
@media (max-width: 575.98px) {
    .project-form .form-actions .btn { width: 100%; }
}

/*
 * Dải hướng dẫn + nút hành động ở đầu trang danh sách (Khu vực dự án).
 *
 * Cố ý KHÔNG dùng các class .gap-* của Bootstrap: theme Dashlite định nghĩa
 * .gap-2 là spacer với height: .75rem, nên đặt nó lên một flex container sẽ ép
 * chiều cao khối, nội dung tràn khỏi viền và đè lên panel tìm kiếm bên dưới.
 * Khoảng cách ở đây đặt bằng thuộc tính `gap` thật.
 */
.area-hint {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px 16px;
    padding: 14px 16px;
    margin-bottom: 16px;
    border: 1px solid #e5e9f2;
    border-radius: 8px;
    background: #f8f9fc;
}

/* Chữ co giãn và tự xuống dòng; dưới 320px thì nút rơi xuống hàng dưới thay vì chèn nhau. */
.area-hint-text {
    flex: 1 1 320px;
    min-width: 0;
    font-size: .8125rem;
    line-height: 1.5;
    color: #526484;
}

.area-hint-text p { margin: 0; }
.area-hint-text strong { display: block; margin-top: 4px; color: #364a63; }

.area-hint-action { flex: 0 0 auto; margin: 0; }
.area-hint-action .btn { white-space: nowrap; }

/*
 * Header trang Dự án: tiêu đề + chip trạng thái bên trái, cụm nút hành động bám phải.
 *
 * Khoảng cách đặt bằng thuộc tính `gap` thật thay vì class .gap-* của Bootstrap —
 * theme Dashlite định nghĩa .gap-1/.gap-2/.gap-3 là spacer có height cố định
 * (6/12/16px), nên đặt lên flex container sẽ ép chiều cao khối và làm nội dung
 * tràn ra ngoài, đè lên nhau.
 */
.project-hdr {
    gap: 10px 14px;
    row-gap: 10px;
}

.project-hdr > h3 { margin-bottom: 0; }

.hdr-chips { gap: 6px; }

/* margin-left:auto đẩy cả cụm về mép phải, và giữ 3 nút dính nhau thành một nhóm
   thay vì bị justify-content:space-between rải đều ra khắp hàng. */
.hdr-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

.hdr-actions .btn { white-space: nowrap; }

@media (max-width: 767.98px) {
    /* Hàng đã xếp dọc (crm-mobile.css), nên bỏ đẩy phải và cho cụm nút chiếm trọn bề ngang. */
    .hdr-actions {
        margin-left: 0;
        width: 100%;
    }
}

/* ------------------------------------------------------------------------- *
 * Gỡ va chạm tên class: .gap-* của Dashlite vs utility flex-gap của Bootstrap
 * ------------------------------------------------------------------------- *
 *
 * dashlite.css khai .gap-* HAI lần, ở hai chỗ khác nhau trong cùng file:
 *
 *   dòng ~8662   .gap-2 { gap: .75rem !important }   ← khoảng cách flex (Bootstrap)
 *   dòng ~31831  .gap-2 { height: .75rem }           ← size modifier cho component
 *                                                       spacer dọc `.gap`
 *
 * Rule thứ hai nằm sau nên nó thắng về `height`. Hệ quả: mọi
 * `<div class="d-flex gap-2">` viết theo phản xạ Bootstrap đều bị ép chiều cao
 * xuống đúng bằng giá trị gap (6/12/16px...), nội dung tràn ra ngoài khối và đè
 * lên phần tử bên dưới. Đã gặp ở ít nhất 3 màn hình trong repo, và đo được 5 khối
 * đang tràn chỉ riêng trên /admin/settings/customerstatuses.
 *
 * `height` chỉ có nghĩa khi đi kèm class `.gap` (spacer thật: display:block,
 * width:100%). Nên `:not(.gap)` gỡ chiều cao cho mọi cách dùng kiểu Bootstrap mà
 * KHÔNG đụng tới component spacer của theme — nếu sau này có ai dùng
 * `<div class="gap gap-2"></div>` thì nó vẫn cao 12px như thiết kế.
 *
 * Sửa ở một chỗ thay vì bỏ class ở từng view: các chỗ đang sai được vá hết, và
 * người viết view sau này gõ `d-flex gap-2` theo thói quen Bootstrap cũng không
 * dính bẫy nữa.
 */
.gap-0:not(.gap),
.gap-1:not(.gap),
.gap-2:not(.gap),
.gap-3:not(.gap),
.gap-4:not(.gap),
.gap-5:not(.gap),
.gap-gs:not(.gap),
.gap-sm:not(.gap),
.gap-md:not(.gap),
.gap-lg:not(.gap),
.gap-xl:not(.gap),
.gap-10px:not(.gap) {
    height: auto;
}

/* ------------------------------------------------------------
   Listing tables: the column headers stay put while you scroll.

   WHY: these lists run to 100 rows a page and the headers scrolled away with
   them, so halfway down a customer list the columns are unlabelled and you are
   counting cells to work out which one is the phone number.

   WHY IT IS GATED ON .crm-fits RATHER THAN JUST APPLIED. position:sticky resolves
   against the nearest scrolling ancestor, and these tables sit inside
   .table-responsive (and DataTables' own wrapper), both overflow:auto. That makes
   the WRAPPER the sticky container, and since it is as tall as its content the
   header never actually sticks — applying the rule alone does nothing at all,
   which is exactly what the first attempt at this did.

   Clearing the overflow fixes it, but cannot be done unconditionally: Projects is
   12 columns and 1169px inside a 1000px container, and without the container's own
   scrollbar the whole PAGE scrolls sideways instead, dragging the sidebar and
   header with it. CSS cannot express "scroll horizontally but not vertically" —
   overflow-x:auto forces overflow-y to compute as auto too — so it is one or the
   other per table. admin-sticky-head.js measures each table and adds .crm-fits only
   where the table fits, so a wide grid keeps its horizontal scroll and simply does
   not get a sticky header.

   Desktop only: below 768px cards-sm has already hidden the header and captioned
   each cell instead, so there is nothing to stick.
   ------------------------------------------------------------ */
@media (min-width: 768px) {
    .table-responsive.crm-fits,
    .dataTables_wrapper.crm-fits {
        overflow: visible;
    }

    /* Opaque background is not optional: the default header is transparent and rows
       would scroll through it. z-index 2 clears the striped row backgrounds without
       reaching the dropdown and sidebar layers above. */
    .crm-fits table > thead > tr > th {
        position: sticky;
        top: 0;
        z-index: 2;
        background-color: #fff;
    }

    /* The per-column filter row admin-table-sort-filter adds sits under the captions
       rather than on top of them. */
    .crm-fits table > thead > tr:nth-child(2) > th {
        top: 42px;
        z-index: 1;
    }

    /* Tài khoản ứng dụng opts out, by request.

       Its header is the theme's .table-tranx, whose tint is painted on the ROW, not the cells. The
       sticky rule sets background-color on the th, so the cells keep their own opaque white only
       where a cell background exists — on this table the tinted row scrolled away from under the
       stuck cells and the body rows showed through the gap, with the row actions drawn across the
       caption. Static position puts the header back in the flow. */
    .crm-fits table#mobileAccountTable > thead > tr > th {
        position: static;
        z-index: auto;
    }
}

/* --- jQuery UI datepicker ------------------------------------------------------------------
   THE STYLESHEET THAT NORMALLY DRAWS THIS WIDGET IS NOT IN THE PROJECT. wwwroot/lib/jquery-ui/
   holds jquery-ui.min.js and i18n/ and nothing else — /lib/jquery-ui/jquery-ui.min.css answers
   404. Nine views load the script, and each carries its own <style> block that colours the
   header and pads the cells, but none of them supplies the LAYOUT the widget assumes.

   Without it the calendar does not fail, it sprawls: prev/next are position:static, so
   "Tháng trước" and "Tháng sau" flow as two text links across the top instead of sitting in the
   corners as arrows; the title is left-aligned because nothing centres it; and the panel has no
   width, so the 1.2em font and 10px cell padding those nine blocks set push one month to roughly
   750px tall.

   Only the layout rules are restored here. Colour stays with the nine blocks, which is what the
   site actually wants the thing to look like. Selectors carry the calendar's own class
   (table.ui-datepicker-calendar) so they outrank those blocks' bare .ui-datepicker table / td on
   specificity — no !important, and a view that wants a different size can still say so.

   The real cleanup is deleting the nine duplicated blocks and keeping one copy here. That edits
   nine .cshtml files and needs a build, so it is not this change. */
.ui-datepicker {
    width: 18em;
    padding: .2em .2em 0;
}

.ui-datepicker .ui-datepicker-header {
    position: relative;
    padding: .2em 0;
}

.ui-datepicker .ui-datepicker-prev,
.ui-datepicker .ui-datepicker-next {
    position: absolute;
    top: .2em;
    width: auto;
    padding: .2em .5em;
    font-size: .8125rem;
    line-height: 1.6;
}

.ui-datepicker .ui-datepicker-prev {
    left: .2em;
}

.ui-datepicker .ui-datepicker-next {
    right: .2em;
}

.ui-datepicker .ui-datepicker-title {
    margin: 0 5.5em;
    line-height: 1.8;
    text-align: center;
}

.ui-datepicker table.ui-datepicker-calendar {
    width: 100%;
    margin: 0 0 .4em;
    font-size: max(12px, .9em);
    border-collapse: collapse;
}

.ui-datepicker table.ui-datepicker-calendar th {
    padding: .4em .2em;
    font-weight: 600;
    text-align: center;
    border: 0;
}

.ui-datepicker table.ui-datepicker-calendar td {
    padding: 1px;
    border: 0;
}

.ui-datepicker table.ui-datepicker-calendar td a,
.ui-datepicker table.ui-datepicker-calendar td span {
    display: block;
    padding: .35em .45em;
    text-align: center;
    text-decoration: none;
}

/* The jQuery UI datepicker has to clear the sticky header above.

   jQuery UI appends the panel to <body> and gives it an inline z-index derived from the input's
   stacking context, which on these pages lands at 1 — under the sticky th's 2. The symptom is not
   a hidden calendar but a sliced one: on the customer list the header band painted straight
   across the panel and swallowed a whole week row, so September jumped from 19 to 27.

   !important because the value is inline. 1060 puts it over Bootstrap's modal (1055), since three
   of the nine screens open a datepicker inside one, and under the tooltip layer (1080).

   Here rather than in the nine views that call .datepicker(): all nine carry a copy of the same
   <style> block, and a rule added to one of them fixes one page. */
.ui-datepicker {
    z-index: 1060 !important;
}

/* ------------------------------------------------------------------------- */
/* Status pipeline strip (_StatusPipeline.cshtml)                            */
/*                                                                           */
/* Replaces a row of summary cards on listing pages whose records move        */
/* through a workflow. It is narrower than the cards it replaces and it is    */
/* also the status filter, so the dropdown stops being the only way to ask    */
/* "show me the ones waiting on approval".                                    */
/* ------------------------------------------------------------------------- */
.crm-pipeline {
    border: 1px solid #e5e9f2;
    border-radius: 6px;
    background: #fff;
}

.crm-pipeline-track {
    display: flex;
    align-items: stretch;
    overflow-x: auto;
}

.crm-stage {
    position: relative;
    flex: 1 1 0;
    min-width: 104px;
    padding: 0.6rem 0.65rem 0.45rem;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    text-decoration: none;
    color: inherit;
    border-left: 1px solid #f0f3f9;
}

.crm-stage:first-child {
    border-left: 0;
}

.crm-stage:hover {
    background: #f7f9fc;
    color: inherit;
}

/* The stage's own colour, matching the badge the rows at that stage carry in the
   table underneath. Muted until the stage is selected or is the one holding the
   queue up, so five colours do not compete for attention at once. */
.crm-stage::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--crm-stage-color, #8094ae);
    opacity: 0.25;
}

.crm-stage.is-active::after,
.crm-stage.is-bottleneck::after {
    opacity: 1;
}

/* The hand-over between stages. Drawn rather than an icon so it inherits the
   divider colour and costs no font. */
.crm-stage + .crm-stage::before {
    content: "";
    position: absolute;
    left: -6px;
    top: 50%;
    width: 9px;
    height: 9px;
    transform: translateY(-50%) rotate(45deg);
    border-top: 1px solid #dbe0ea;
    border-right: 1px solid #dbe0ea;
    background: #fff;
}

.crm-stage-head {
    display: flex;
    align-items: flex-start;
    gap: 0.3rem;
    min-width: 0;
}

/* Wraps rather than truncating. The label is the one part of a stage that has to
   be readable — "Chờ GĐD…" tells nobody which queue they are looking at — and at
   a narrow window several of these are too long for one line. */
.crm-stage-label {
    font-size: 12px;
    color: #526484;
    line-height: 1.2;
    overflow-wrap: anywhere;
}

.crm-stage-dot {
    margin-top: 0.28rem;
}

/* Pushed to the bottom so the counts line up across stages whether or not a label
   wrapped, without reserving a second line on pages where none of them do. */
.crm-stage-count {
    font-size: 1.3rem;
    font-weight: 600;
    line-height: 1.1;
    margin-top: auto;
}

.crm-stage-value {
    font-size: 12px;
    color: #8094ae;
    min-height: 1em;
}

/* Filled = ABK is holding it; hollow = a customer or supplier is. The footer says
   the same thing in words, so the dot reinforces rather than carries it alone. */
.crm-stage-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.crm-pipeline-foot .crm-stage-dot {
    margin-top: 0;
}

.crm-wait-ours {
    background: var(--crm-stage-color, #8094ae);
}

.crm-wait-theirs {
    border: 1.5px solid var(--crm-stage-color, #8094ae);
}

.crm-wait-none {
    border: 1.5px solid #dbe0ea;
}

.crm-stage.is-active {
    background: #f5f8ff;
}

.crm-stage.is-bottleneck {
    background: #fffaf3;
}

.crm-stage.is-bottleneck .crm-stage-count {
    color: #b3690a;
}

.crm-pipeline-foot {
    border-top: 1px solid #f0f3f9;
    padding: 0.4rem 0.65rem;
    font-size: 0.78rem;
    color: #526484;
}

.crm-pipeline-key {
    background: #e8a33d;
    margin-right: 0.15rem;
    vertical-align: middle;
}

.crm-pipeline-sep {
    margin: 0 0.3rem;
    color: #c4cefe;
}

/* ==========================================================================================
   Customer detail — page head, read-before-edit, care-note timeline, assign strip.
   Everything here is desktop-shaped. crm-mobile.css already restyles .crm-carenote and strips
   table borders under 768px, which is why these are measured at >= 1200px.
   ========================================================================================== */

/* --- the head that answers "who is this, how do I reach them, where has it got to" ---------

   NO hardcoded colour here, and that is the whole point. This strip first shipped with
   color: #fff, on the assumption that .card-header.bg-info is dark. It is not necessarily:
   _Layout writes an admin-configurable theme, and `h1..h6,.title { color: <Theme.HeadingColor>
   !important }` is why the h3 above this strip renders dark navy even though its markup says
   .text-white. Measured on a banner of rgb(213,243,255): the h3 sat at 11.04:1 while every
   element in this strip sat at 1.16:1 — white on near-white, effectively invisible.

   Inheriting from .card-header follows whatever the theme decides. On that same banner it
   resolves to rgb(82,100,132) at 5.15:1, and on a dark banner it follows the theme the other
   way. The phone link stays distinguishable by its underline rather than by a colour that
   cannot know what it is sitting on. */
.crm-cust-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 14px;
    margin-top: 6px;
    color: inherit;
}

.crm-cust-name {
    font-size: 1.05rem;
    font-weight: 700;
}

.crm-cust-tel {
    color: inherit;
    text-decoration: underline;
}

.crm-cust-tel:hover {
    color: inherit;
    opacity: .85;
}

.crm-cust-meta {
    font-size: .85rem;
    opacity: .92;
}

/* --- read first, edit on request ----------------------------------------------------------
   The inputs are still the real inputs, with their names and bindings, so the save posts what it
   always posted. Only the presentation changes. pointer-events is half the job — the JS sets
   readonly so Tab cannot walk in either; neither alone is enough. */
.crm-field-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-bottom: 6px;
}

.is-reading .form-control,
.is-reading select.form-control,
.is-reading textarea.form-control {
    border: 0 !important;
    background: transparent !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    pointer-events: none;
    box-shadow: none !important;
}

/* The rich-text editors are divs, not inputs, so they need saying separately. Blocking pointer
   events here also means the lazy editor is never constructed while reading, which is the point. */
.is-reading #divQuillRemark,
.is-reading #divQuillRemarkToSupplier {
    border: 0 !important;
    background: transparent !important;
    pointer-events: none;
    height: auto !important;
    min-height: 0;
}

.is-reading .form-control-select::after {
    display: none;
}

/* A separator per field while reading. Reading mode strips every input border (above), which
   leaves twelve label/value pairs running together as one block of text with nothing to say
   where one field ends and the next begins.

   Both columns of a pair get the border rather than a wrapper, because there is no wrapper:
   the label col and the value col are siblings in one flat .row, and a pair always fills the
   full twelve columns, so two half-borders meet as one continuous line. The last pair drops
   it so the list does not end on a rule just above the buttons.

   Reading only. In edit mode every field already draws its own input box and a second line
   under it reads as a mistake. */
#customerFields.is-reading > [class*="col-"] {
    border-bottom: 1px solid #e5e9f2;
    padding-bottom: 6px;
}

#customerFields.is-reading > [class*="col-"]:nth-last-child(-n+2) {
    border-bottom: 0;
}

/* The two selects on this page are Select2 widgets: crm-select.js hides the real <select> and
   renders .select2-container beside it. Styling the <select> therefore did nothing visible, and —
   worse — the widget kept pointer-events, so the status dropdown still OPENED while the panel read
   as locked. Measured before this: elementFromPoint over the control returned
   .select2-selection__rendered and a mousedown opened the list. */
.is-reading .select2-container {
    pointer-events: none;
}

.is-reading .select2-selection {
    border: 0 !important;
    background: transparent !important;
}

/* Specificity matters here: Select2's own rule is
   .select2-container--default .select2-selection--single .select2-selection__arrow (0,3,0), so a
   plain .is-reading .select2-selection__arrow (0,2,0) loses and the chevron stays. Measured: the
   arrow still computed display:flex while reading. */
.is-reading .select2-container--default .select2-selection--single .select2-selection__arrow {
    display: none;
}

/* --- care notes as a timeline ------------------------------------------------------------- */
.crm-carenote-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
}

.crm-carenote-filters {
    display: flex;
    gap: 8px;
}

.crm-carenote-filters .form-control {
    width: auto;
    min-width: 150px;
}

.crm-timeline {
    padding: 4px 0 0 0;
}

.crm-tl-item {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 10px 4px;
    border-bottom: 1px solid #e5e9f2;
}

/* Fixed width, not a column that sizes to content: every note's text has to start at the same x
   or the eye has to re-find the left edge on every row. */
.crm-tl-when {
    flex: 0 0 96px;
    width: 96px;
    text-align: right;
    line-height: 1.25;
}

.crm-tl-date {
    display: block;
    font-weight: 600;
    font-size: .82rem;
    white-space: nowrap;
}

.crm-tl-time {
    display: block;
    font-size: .75rem;
    opacity: .6;
}

/* The note gets the width. */
.crm-tl-body {
    flex: 1 1 auto;
    min-width: 0;
}

.crm-tl-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 3px;
}

.crm-tl-who {
    font-weight: 600;
    font-size: .85rem;
}

.crm-tl-link {
    font-size: .8rem;
}

.crm-tl-actions {
    margin-left: auto;
    white-space: nowrap;
}

.crm-tl-note {
    word-break: break-word;
}

.crm-tl-note p:last-child {
    margin-bottom: 0;
}

.crm-timeline-more {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 4px;
}

.crm-tl-count {
    font-size: .82rem;
    opacity: .7;
}

/* --- assign-task strip on one line --------------------------------------------------------- */
.crm-assign-strip {
    padding: 10px 12px;
}

.crm-assign-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

.crm-assign-title {
    white-space: nowrap;
}

.crm-assign-label {
    white-space: nowrap;
}

.crm-assign-msg:empty {
    display: none;
}

/* ---------------------------------------------------------------------------
   Pagination sits 20px clear of the table it pages.

   DataTables emits .dataTables_paginate with no top margin, so the control
   butted straight up against the last row; the Bootstrap .pagination the
   server-rendered listings use has the same gap. Both are named so a page gets
   the spacing whichever pager it happens to use.

   margin-top on the control, not padding on the wrapper: the info line
   ("Hiển thị 1 đến 20 của 6.631") shares that row on most grids and should move
   with it rather than being pushed out on its own.
   --------------------------------------------------------------------------- */
/* div-qualified to match dashlite's own `div.dataTables_wrapper div.dataTables_paginate`
   (specificity 0,2,2). The plain class form was 0,2,0 and LOST — measured on /admin/mobileaccount,
   where the computed margin-top stayed 0px. Several rules above use the same div-qualified shape
   for the same reason; the theme writes all of its DataTables rules that way. */
div.dataTables_wrapper div.dataTables_paginate,
div.dataTables_wrapper div.dataTables_info,
.pagination {
    margin-top: 20px;
}

/* Body cells on a DataTables grid.

   The .crmodd / .crmeven rules above only reach the legacy grids whose rowCallback adds those
   classes. A DataTables row carries its own `odd`/`even`, so those grids were still taking the
   theme's `.table > :not(caption) > * > *` (8px) and `.table td:first-child` (20px) — a first
   column indented twice as far as every other. !important because that first-child rule outranks
   any reasonable selector here, and because the .crmodd rules beside this already use it. */
.dataTable > tbody > tr > td {
    padding-left: 12px !important;
    padding-right: 12px !important;
}

/* Group headers on the Dự án grid ("Nhóm theo vai trò phụ trách"). Drawn by projects.js rather
   than by the DataTables RowGroup extension, which is not loaded — see the note there.
   div-qualified because dashlite writes `div.dataTables_wrapper div.X` (0,2,2) and a plain class
   selector loses to it; the striping rules that would otherwise repaint this row are the same. */
div.dataTables_wrapper table.dataTable > tbody > tr.crm-group-row > td {
    background: #f5f7fb !important;
    font-weight: 600;
    color: #364a63;
    border-top: 2px solid #dbe2ec !important;
    padding: 8px 12px !important;
}

/* Status tag row on /admin/customer — Getfly-style clickable status filter.
   Tags, not pills (restyled 2026-09-25): the old 3px coloured left border on a fully rounded pill
   read as a shadow. Each tag is tinted with its own status colour (--pill, admin-set, validated
   #rrggbb in the view): a soft background and hairline border of that hue, a dot, and the count in
   a white badge. The selected tag fills solid. The tints use color-mix, with a plain neutral first
   so a browser without it still shows a clean tag. */
.crm-status-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.crm-status-pills .crm-pill {
    --pill: #8094ae;
    display: inline-flex; align-items: center; gap: 7px;
    border: 1px solid #dfe5ee;
    border: 1px solid color-mix(in srgb, var(--pill) 30%, #fff);
    background: #f6f8fb;
    background: color-mix(in srgb, var(--pill) 8%, #fff);
    color: #2b3a4d;
    border-radius: 6px; padding: 4px 6px 4px 10px;
    font-size: 12.5px; font-weight: 500; line-height: 1.5;
    cursor: pointer; white-space: nowrap;
    transition: background-color .15s, border-color .15s, color .15s;
}
.crm-status-pills .crm-pill::before {
    content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%;
    background: var(--pill);
}
.crm-status-pills .crm-pill:hover {
    background: #eef2f7;
    background: color-mix(in srgb, var(--pill) 15%, #fff);
    border-color: color-mix(in srgb, var(--pill) 45%, #fff);
}
.crm-status-pills .crm-pill:focus-visible {
    outline: 2px solid color-mix(in srgb, var(--pill) 55%, #fff); outline-offset: 1px;
}
.crm-status-pills .crm-pill .crm-pill-n {
    min-width: 22px; text-align: center;
    font-weight: 600; font-size: 12px; font-variant-numeric: tabular-nums;
    background: #fff; border-radius: 4px; padding: 0 6px;
    color: #526484;
    color: color-mix(in srgb, var(--pill) 70%, #1c2b46);
}
.crm-status-pills .crm-pill.is-active {
    background: var(--pill); border-color: var(--pill); color: #fff;
}
.crm-status-pills .crm-pill.is-active::before { background: #fff; }
.crm-status-pills .crm-pill.is-active .crm-pill-n { background: rgba(255,255,255,.22); color: #fff; }
.crm-status-pills .crm-pill.crm-pill-muted { --pill: #b6c2d0; color: #8094ae; }

/* Getfly deep-link icon after a customer name on /admin/customer — shown only when the customer
   carries a GetflyAccountId. Muted until hover so it does not compete with the name link. */
.dataTable a.getfly-link { color: #8094ae; font-size: 12px; text-decoration: none; }
.dataTable a.getfly-link:hover { color: #12507b; }

/* ==========================================================================================
   PAGE HEADER BLOCK — the top bar and the page banner read as one strip
   ==========================================================================================

   A listing page used to open with two floating panels stacked on each other: the 65px top bar
   (announcement + account menu), then a 91px rounded, drop-shadowed banner carrying a 32px
   title. 158px of chrome before the first row of data, on a page whose whole job is the table
   underneath. Asked for on 2026-09-23 — "merge these sections more closely, save more space",
   for every listing page.

   It lives here rather than in the views because that banner is hand-written in 211 .cshtml
   files; one rule set moves all of them. A CSS-only change also needs no rebuild, which matters
   locally where every running instance shares one binary.

   Three moves, worth ~50px (32%):
     - the top bar gives back 13px of padding — the 36px avatar still sets its height;
     - the banner loses its shadow and its top rounding, so it stops being a second panel
       floating above the page and becomes the top OF the page;
     - the title goes 32px -> 22px, which is a page heading rather than a hero.

   THE BANNER HAS TWO SHAPES AND THEY NEED DIFFERENT RULES. Measured over all 100 pages the
   sidebar links to: 24 wrap it as a card header (`.card > .card-header.bg-info`, Customer and
   Supplier among them) and 69 emit it bare into the content body with `py-3 mb-2` utilities
   (Cashbook, Call center, SMS, Document library, Quotes...). The remaining 7 use a different
   header pattern altogether and are untouched. A rule written for the card shape alone — the
   obvious one to write, since Customer has that shape — reaches barely a quarter of the admin.
   ========================================================================================== */

/* DashLite pads this row 14px top and bottom around a 36px avatar; 8px still clears it. */
.nk-header-tools,
.nk-header-brand {
    padding-top: 8px;
    padding-bottom: 8px;
}

/* Compaction is the same whichever shape the banner is. bg-secondary is in the list because
   the two Super-Admin screens (Nhật ký & Quyền) build the identical strip in grey — it is the
   same banner wearing a different colour, and leaving it out would make those two pages the
   only ones still opening with a 32px title. bg-light and bg-warning are deliberately NOT here:
   the three views using those are inner card headers, not page banners. */
.card-header.bg-info.bg-gradient,
.card-header.bg-secondary.bg-gradient,
.card-header.bg-info,
.card-header.bg-secondary {
    padding: .625rem 1.25rem !important;
    box-shadow: none !important;
}

/* ---- shape one: the banner heads a card ------------------------------------------------ */

/* Square where it meets the body and no gap: that 2px margin is what made the banner and the
   table under it read as two separate objects instead of one panel. */
.card > .card-header.bg-info {
    border-radius: 14px 14px 0 0 !important;
    margin-bottom: 0 !important;
}

/* Nothing below it to join, so it stays a rounded panel — otherwise its square bottom corners
   spill outside the card's rounded ones. */
.card > .card-header.bg-info:last-child {
    border-radius: 14px !important;
}

/* The page's first card butts straight against the top bar, so its top corners square off and
   the two become one continuous strip. :first-of-type rather than :first-child because most of
   these views emit a bare <script> ahead of the card. */
.nk-content-body > .card:first-of-type:has(> .card-header.bg-info) {
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
    /* Several views space this card off the header with their own mt-3/mt-4 (Supplier is one).
       That margin is what the flush join is removing, so it has to go with the corners — square
       corners 24px below the bar look like a mistake rather than a join. */
    margin-top: 0 !important;
}

.nk-content-body > .card:first-of-type > .card-header.bg-info {
    border-radius: 0 !important;
}

.nk-content-body > .card:first-of-type > .card-header.bg-info:last-child {
    border-radius: 0 0 14px 14px !important;
}

/* ---- shape two: a bare banner strip in the content body -------------------------------- */

/* This one has no card body to flow into, so it keeps its bottom rounding and a small gap
   below. .5rem restores the mb-2 the views themselves ask for; the old rule forced 2px on it. */
.nk-content-body > .card-header.bg-info,
.nk-content-body > .card-header.bg-secondary {
    border-radius: 14px !important;
    margin-bottom: .5rem !important;
}

/* Where that strip opens the page it squares off against the top bar, exactly as the card
   shape does. Where it does not — /admin/statistic puts a card above it — it stays a rounded
   panel, because there it is not the top of the page. */
.nk-content-body > .card-header.bg-info:first-of-type,
.nk-content-body > .card-header.bg-secondary:first-of-type {
    border-radius: 0 0 14px 14px !important;
    margin-top: 0 !important;
}

/* ---- the title ------------------------------------------------------------------------- */

/* A page title, not a hero. 32px was DashLite's h3 default and nothing had overridden it. */
.card-header.bg-info h1,
.card-header.bg-info h2,
.card-header.bg-info h3,
.card-header.bg-info h4,
.card-header.bg-secondary h1,
.card-header.bg-secondary h2,
.card-header.bg-secondary h3,
.card-header.bg-secondary h4 {
    font-size: 1.375rem;
    line-height: 1.35;
    margin-bottom: 0;
}

/* A banner holding a single row is a title + actions strip, so the .5rem gutter that exists for
   wrapped filter fields is dead space under it. From md up only: below that the title and the
   button group stack, and there the gap is doing real work. */
@media (min-width: 768px) {
    .card-header.bg-info .row:only-child > [class*="col-"],
    .card-header.bg-secondary .row:only-child > [class*="col-"] {
        margin-bottom: 0;
    }
}

/* Restores the phone size the generic `.card-header h3` rule used to give, which the more
   specific selectors above now outrank. */
@media (max-width: 767.98px) {
    .card-header.bg-info h1,
    .card-header.bg-info h2,
    .card-header.bg-info h3,
    .card-header.bg-info h4,
    .card-header.bg-secondary h1,
    .card-header.bg-secondary h2,
    .card-header.bg-secondary h3,
    .card-header.bg-secondary h4 {
        font-size: 1.15rem;
    }
}

/* Five admin views head the page with DashLite's .nk-block-head instead of a coloured banner
   (MobileAccount Index/Create, Projects Access/Assignments, ProjectAcceptance). They get no
   banner rule, and DashLite gives them 40px of bottom padding — so after the banners were
   compacted these were the pages left with the widest gap before their content. There is no
   strip to merge here, just space to give back. */
.nk-content-body > .nk-block-head,
.nk-content-inner > .nk-block-head,
/* DashLite spends its own 0,3,0 rule on exactly this case —
   `@media (min-width: 768px) { .nk-content-body > .nk-block-head:first-child { padding-bottom: 2.5rem } }` —
   so the two-class selector above loses to it on every desktop screen and the 40px stays. Matching
   the selector is what wins: same specificity, and crm-style.css is loaded after dashlite.css. */
.nk-content-body > .nk-block-head:first-child,
.nk-content-inner > .nk-block-head:first-child {
    padding-top: 0;
    padding-bottom: 1rem;
}

/* ==========================================================================================
   DATATABLES FOOTER ROW — page length, info and pager on one line under the grid
   ==========================================================================================
   Built by admin-table-footer.js, which moves the three controls into it after init. DataTables
   ships the length control above the grid and the other two below, so every listing carried a
   lone "Hiển thị 20 dữ liệu" between the search panel and the first row. Asked for 2026-09-23.

   All three arrive carrying layout for where they used to be — the length control a negative top
   margin that tucked it under the page banner, info and pager the floats DataTables' Bootstrap
   integration gives them — and every one of those fights the flex row. Note the selectors below
   have to out-specify `div.dataTables_wrapper div.dataTables_length` (0,2,2) to undo it, which is
   why they are written out long-hand rather than as a bare class.
   ========================================================================================== */
.crm-dt-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 18px;
    padding-top: 12px;
}

/* Pushed right by an auto margin rather than by a spacer, so a narrow screen wraps the pager onto
   its own line under the other two instead of stranding it mid-row. */
div.dataTables_wrapper div.crm-dt-foot > div.dataTables_paginate {
    margin-left: auto !important;
}

div.dataTables_wrapper div.crm-dt-foot > div.dataTables_length,
div.dataTables_wrapper div.crm-dt-foot > div.dataTables_info,
div.dataTables_wrapper div.crm-dt-foot > div.dataTables_paginate {
    float: none !important;
    margin-top: 0 !important;
    margin-bottom: 0 !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
}

/* .nk-content .pagination carries .75rem/1.25rem of its own for the standalone pagers on the
   server-rendered listings; in here it just knocks the pager off the shared baseline. */
.crm-dt-foot .pagination {
    margin-top: 0;
    margin-bottom: 0;
}

/* The row is the grid's own bottom edge, so it keeps a little air under it. */
.crm-dt-foot {
    padding-bottom: 4px;
}

/* Hành trình khách hàng (customer journey board) — /admin/customerjourney.
   Phases as fixed-width columns read left→right; the board scrolls rather than squashing. */
.crm-journey { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 8px; align-items: flex-start; }
.crm-journey-col { flex: 0 0 240px; background: #f5f7fb; border-radius: 8px; padding: 10px; }
.crm-journey-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; padding: 0 2px; }
.crm-journey-phase { font-weight: 600; color: #364a63; font-size: 13px; }
.crm-journey-phase-n { color: #8094ae; font-size: 12px; white-space: nowrap; }
.crm-journey-list { list-style: none; margin: 0; padding: 0; min-height: 40px; }
.crm-journey-card {
    background: #fff; border: 1px solid #e5e9f2; border-left: 3px solid var(--accent, #8094ae);
    border-radius: 6px; padding: 8px 10px; margin-bottom: 8px;
}
/* Only when the board is editable (has draggable cards) do we hint it. */
#journeyBoard[data-can-edit="1"] .crm-journey-card { cursor: grab; }
#journeyBoard[data-can-edit="1"] .crm-journey-card:active { cursor: grabbing; }
.crm-journey-placeholder {
    border: 1px dashed #b6c2d0; border-radius: 6px; background: #eef2f7; margin-bottom: 8px; height: 58px;
}
/* Folder edit controls (reorder / rename / delete), shown on an editable board. */
.crm-journey-tools { display: flex; gap: 2px; margin-bottom: 8px; }
.crm-journey-tools .btn-xs { padding: 1px 5px; font-size: 12px; line-height: 1.4; border-color: #e5e9f2; }
.crm-journey-tools .btn-xs .icon { font-size: 12px; }
.crm-journey-stage { font-size: 12px; color: #364a63; margin-bottom: 4px; }
.crm-journey-metrics { display: flex; justify-content: space-between; align-items: baseline; }
.crm-journey-count { font-size: 18px; font-weight: 700; color: #1b2a3d; }
.crm-journey-pct { font-size: 12px; color: #8094ae; }
.crm-journey-bar { height: 4px; background: #eef2f7; border-radius: 2px; margin-top: 6px; overflow: hidden; }
.crm-journey-bar > span { display: block; height: 100%; background: var(--accent, #8094ae); }

/* Tỷ lệ chuyển đổi (conversion report) — /admin/conversionrate. Inline share bars. */
.crm-conv-bar { height: 8px; background: #eef2f7; border-radius: 4px; overflow: hidden; }
.crm-conv-bar > span { display: block; height: 100%; background: #2263b3; border-radius: 4px; }

/* ==========================================================================================
   /admin/customer — Getfly's account list (anbaokhanggroup.getflycrm.com/#/crm/accounts)
   ==========================================================================================
   Laid out as Getfly lays it out, on the owner's instruction of 2026-09-27, because the sales team
   works in both and the two screens should read alike: a white header with the module's tabs, one
   search line, solid status chips carrying their counts on an orange badge, a grid whose rows wear
   their relationship colour down the left edge, and the pager, page size and column picker along
   the bottom. Everything is scoped to .gf-accounts so no other listing moves. The page does not use
   the teal card-header banner the other listings share: Getfly's header is white, and that is what
   was asked for. */
.gf-accounts {
    --gf-accent: #17508a;
    --gf-accent-soft: #e8f0f9;
    --gf-line: #e3e8ef;
    --gf-ink: #1c2b3d;
    --gf-muted: #6b7a90;
    --gf-badge: #f26c1f;
    --gf-head-bg: #f1f4f8;
    background: #fff;
    border-radius: 14px;
}

.gf-accounts .gf-body { padding: 14px 16px 16px; }

/* ---- toolbar ------------------------------------------------------------------------------ */
.gf-accounts .gf-toolbar { display: flex; flex-wrap: wrap; align-items: stretch; gap: 10px; margin-bottom: 12px; }
.gf-accounts .gf-search {
    display: flex; align-items: center; flex: 1 1 360px; min-width: 260px; max-width: 480px;
    border: 1px solid var(--gf-line); border-radius: 8px; background: #fff; height: 40px;
}
.gf-accounts .gf-search > .icon { padding: 0 8px 0 12px; color: var(--gf-muted); font-size: 16px; }
.gf-accounts .gf-search input {
    flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
    font-size: 14px; color: var(--gf-ink); height: 100%;
}
.gf-accounts .gf-search:focus-within { border-color: var(--gf-accent); box-shadow: 0 0 0 3px var(--gf-accent-soft); }
.gf-accounts .gf-filter-btn {
    display: inline-flex; align-items: center; gap: 6px; height: 100%;
    padding: 0 14px; border: 0; border-left: 1px solid var(--gf-line); background: transparent;
    color: var(--gf-ink); font-size: 14px; font-weight: 500; border-radius: 0 8px 8px 0;
}
.gf-accounts .gf-filter-btn:hover,
.gf-accounts .gf-filter-btn[aria-expanded="true"] { background: var(--gf-accent-soft); color: var(--gf-accent); }
.gf-accounts .gf-filter-n {
    min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
    background: var(--gf-badge); color: #fff; font-size: 12px; font-weight: 700; line-height: 18px; text-align: center;
}
.gf-accounts .gf-select { flex: 0 1 240px; min-width: 180px; }
.gf-accounts .gf-select-period { flex-basis: 190px; }
.gf-accounts .gf-select select { width: 100%; height: 40px; }
.gf-accounts .gf-select select.form-select {
    border: 1px solid var(--gf-line); border-radius: 8px; font-size: 14px; color: var(--gf-ink);
    padding: 0 32px 0 12px; box-shadow: none;
}
.gf-accounts .gf-toolbar .crm-saved-views { margin: 0 !important; }
.gf-accounts .gf-toolbar .crm-saved-views form { display: none !important; }
.gf-accounts .gf-toolbar .crm-saved-views .btn { height: 40px; border-color: var(--gf-line); color: var(--gf-ink); background: #fff; }

/* ---- Bộ lọc panel ------------------------------------------------------------------------- */
.gf-accounts .gf-advanced {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px 14px;
    padding: 14px; margin-bottom: 12px;
    background: #f8fafc; border: 1px solid var(--gf-line); border-radius: 10px;
}
.gf-accounts .gf-advanced[hidden] { display: none; }
.gf-accounts .gf-field { display: flex; flex-direction: column; gap: 4px; margin: 0; font-size: 12.5px; color: var(--gf-muted); }
.gf-accounts .gf-field select, .gf-accounts .gf-field input { width: 100%; }
.gf-accounts .gf-advanced-actions { display: flex; align-items: flex-end; gap: 8px; }
.gf-accounts .gf-advanced .crm-saved-views { grid-column: 1 / -1; margin: 4px 0 0 !important; }
.gf-accounts .gf-advanced .crm-saved-views > .dropdown { display: none; }

/* ---- chips -------------------------------------------------------------------------------- */
.gf-accounts .gf-chipbar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; padding-top: 8px; }
.gf-accounts .gf-chip-track {
    display: flex; gap: 8px; min-width: 0; flex: 0 1 auto;
    overflow-x: auto; scrollbar-width: none; padding-top: 9px; margin-top: -9px;
}
.gf-accounts .gf-chip-track::-webkit-scrollbar { display: none; }
.gf-accounts .gf-chip {
    --pill: #5b6b82;
    position: relative; flex: none;
    display: inline-flex; align-items: center; height: 38px; padding: 0 14px;
    border: 0; border-radius: 7px; background: var(--pill); color: #fff;
    font-size: 14px; font-weight: 500; white-space: nowrap; cursor: pointer;
    box-shadow: inset 0 0 0 2px transparent;
    transition: filter .15s, box-shadow .15s;
}
.gf-accounts .gf-chip:hover { filter: brightness(1.07); }
.gf-accounts .gf-chip:focus-visible { outline: 2px solid var(--gf-accent); outline-offset: 2px; }
/* The count rides the chip's top-right corner, as Getfly's does. */
.gf-accounts .gf-chip-n {
    position: absolute; top: -9px; right: -4px;
    min-width: 22px; height: 18px; padding: 0 6px; border-radius: 9px;
    background: var(--gf-badge); color: #fff; border: 2px solid #fff; box-sizing: content-box;
    font-size: 12px; font-weight: 700; line-height: 18px; text-align: center; font-variant-numeric: tabular-nums;
}
/* Selected: a ring inside the chip, so its own colour still says which status it is. */
.gf-accounts .gf-chip.is-active { box-shadow: inset 0 0 0 2px rgba(255,255,255,.9), 0 0 0 2px var(--pill); }
.gf-accounts .gf-chip-all { --pill: #0f3b63; }
.gf-accounts .gf-chip-muted { --pill: #9aa7b8; }
.gf-accounts .gf-chip-quick { --pill: #eef1f5; color: var(--gf-ink); }
.gf-accounts .gf-chip-quick.is-active { --pill: var(--gf-accent-soft); color: var(--gf-accent); box-shadow: 0 0 0 2px var(--gf-accent); }
.gf-accounts .gf-chip-scroll {
    flex: none; width: 34px; height: 34px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--gf-line); background: #fff; color: var(--gf-ink);
}
.gf-accounts .gf-chip-scroll[hidden] { display: none; }
.gf-accounts .gf-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none; }
.gf-accounts .gf-actions .btn { height: 38px; display: inline-flex; align-items: center; }
.gf-accounts .gf-actions .btn-light { background: var(--gf-accent); border-color: var(--gf-accent); color: #fff; }
.gf-accounts .gf-add { background: var(--gf-accent); border-color: var(--gf-accent); font-weight: 600; padding: 0 18px; }

/* ---- grid --------------------------------------------------------------------------------- */
.gf-accounts table.gf-grid { border-collapse: separate; border-spacing: 0; margin: 0 !important; }
/* background-color with !important: DataTables' sorting classes set the `background` shorthand for their
   arrows, which cleared the colour on every sortable header — and under the sticky header the rows then
   showed through all but the three unsortable ones. */
.gf-accounts table.gf-grid thead th {
    background-color: var(--gf-head-bg) !important; color: var(--gf-ink); font-weight: 700; font-size: 13.5px;
    border-bottom: 1px solid var(--gf-line) !important; border-top: 0; white-space: nowrap; vertical-align: middle;
}
.gf-accounts table.gf-grid tbody td {
    border-bottom: 1px solid var(--gf-line); border-top: 0; vertical-align: middle;
    font-size: 14px; color: var(--gf-ink); padding-top: 11px; padding-bottom: 11px;
    /* Wrapping, as Getfly's cells do: the admin-wide nowrap made a long campaign-name source push the
       grid to 1769px inside a 1398px card, with Tỉnh/thành phố off the edge. */
    white-space: normal;
}
.gf-accounts table.gf-grid tbody td.cell-actions,
.gf-accounts a.gf-phone { white-space: nowrap; }
.gf-accounts table.gf-grid tbody tr.gf-row > td:first-child { box-shadow: inset 4px 0 0 var(--gf-row, transparent); }
.gf-accounts table.gf-grid tbody tr:hover > td { background: #f7f9fc; }
.gf-accounts table.gf-grid tbody tr.highlight > td { background: #fff7e8; }
.gf-accounts .gf-num { text-align: center; color: var(--gf-muted); font-variant-numeric: tabular-nums; }
/* min-width: in a grid this wide the name is the one column the browser can wrap, so it gave it up
   first -- a name stood five lines tall in a 159px cell (layout audit 2026-10-07). */
.gf-accounts .gf-name { display: inline-flex; align-items: center; gap: 8px; min-width: 200px; }
.gf-accounts .gf-avatar {
    flex: none; width: 32px; height: 32px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    background: #dfe7f1; color: #52657e; font-size: 13px; font-weight: 700;
}
.gf-accounts .gf-name a.customer-link, .gf-accounts a.gf-phone { color: #1e6fd0; text-decoration: none; }
.gf-accounts .gf-name a.customer-link:hover, .gf-accounts a.gf-phone:hover { text-decoration: underline; }
.gf-accounts .gf-rel-none { color: var(--gf-muted); }
.gf-accounts .gf-date { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ---- ticked rows ------------------------------------------------------------------------- */
.gf-accounts .gf-bulk {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    padding: 8px 12px; margin-bottom: 10px;
    background: var(--gf-accent-soft); border: 1px solid color-mix(in srgb, var(--gf-accent) 25%, #fff); border-radius: 8px;
}
.gf-accounts .gf-bulk[hidden] { display: none; }
.gf-accounts .gf-bulk-n { color: var(--gf-accent); margin-right: 6px; }
.gf-accounts .gf-bulk .form-select { width: auto; min-width: 190px; }
.gf-accounts .gf-tick { display: inline-flex; align-items: center; gap: 8px; margin: 0; cursor: pointer; }
.gf-accounts .gf-check { margin: 0; cursor: pointer; }
.gf-accounts th .gf-check { margin-right: 6px; vertical-align: -2px; }
/* Getfly's orange count after a name — here, the customer's open tasks. */
.gf-accounts .gf-name-n {
    min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
    background: var(--gf-badge); color: #fff; font-size: 12px; font-weight: 700; line-height: 18px; text-align: center;
}

/* ---- footer: pager and info left, Tùy chỉnh and page size right --------------------------- */
.gf-accounts div.dataTables_wrapper div.crm-dt-foot { padding-top: 14px; border-top: 1px solid var(--gf-line); }
.gf-accounts div.dataTables_wrapper div.crm-dt-foot > div.dataTables_paginate { order: 1; margin-left: 0 !important; }
.gf-accounts div.dataTables_wrapper div.crm-dt-foot > div.dataTables_info { order: 2; color: var(--gf-ink); }
.gf-accounts div.dataTables_wrapper div.crm-dt-foot > .gf-foot-tools { order: 3; margin-left: auto; }
.gf-accounts div.dataTables_wrapper div.crm-dt-foot > div.dataTables_length { order: 4; }

/* This grid sorts, and DataTables gives every sortable header `position: relative` for its arrows,
   which out-specifies the admin-wide sticky rule above — so only the three unsortable headers stuck,
   and rows scrolled through the rest. Sticky is itself positioned, so the arrows still anchor to it. */
@media (min-width: 768px) {
    .gf-accounts .crm-fits table > thead > tr > th { position: sticky; top: 0; z-index: 2; }
}

@media (max-width: 767.98px) {
    .gf-accounts .gf-search { max-width: none; flex-basis: 100%; }
    .gf-accounts .gf-select { flex: 1 1 45%; }
    /* Tất cả and the scrolling status track share the first line, the three reminder chips the second,
       the buttons the third. Measured at 390px: without the track taking the rest of line one, Tất cả sat
       alone and Sinh nhật dropped to a fourth line. The row gap leaves room for the count badges, which
       ride 9px above each chip. */
    .gf-accounts .gf-chipbar { flex-wrap: wrap; row-gap: 16px; }
    /* A basis, not flex: 1 1 0 — with a zero basis the reminder chips joined line one and the track was
       squeezed to nothing. This leaves room for Tất cả and the two arrows and pushes the rest down. */
    .gf-accounts .gf-chip-track { flex: 1 1 calc(100% - 180px); }
    .gf-accounts .gf-chip-quick { padding: 0 11px; }
    .gf-accounts .gf-actions { margin-left: 0; width: 100%; justify-content: flex-end; }
    .gf-accounts table.gf-grid tbody tr.gf-row > td:first-child { box-shadow: none; }
    .gf-accounts table.gf-grid tbody tr.gf-row { box-shadow: inset 4px 0 0 var(--gf-row, transparent); }
}

/* ==========================================================================================
   THE PAGE TITLE, WHERE THE MODULE TAB ROW ALREADY NAMES THE PAGE (owner, 2026-10-02).
   page-banner.js marks what goes; these rules make it go. Only under .crm-titled-by-tab, which
   the layout sets when the lit tab IS this page, so a detail or edit page keeps its title.
   ========================================================================================== */
.crm-titled-by-tab .crm-banner-title-gone { display: none !important; }

/* The banner's first row lost its title column, so the buttons left in it take the whole row and
   sit at its right end, where the title's column used to push them. Used when there is no search
   row to join (step 3 of page-banner.js did not run). */
.crm-titled-by-tab .crm-banner-untitled > .row:first-child > [class*="col-"]:not(.crm-banner-title-gone) {
    flex: 1 1 auto;
    width: auto;
    max-width: none;
    display: flex;
    justify-content: flex-end;
}

/* ONE TOOLBAR: the search on the left, then "Tìm kiếm nâng cao", "Cột" and the page's own buttons
   at the right end, wrapping as one group when the row is too narrow for both.

   The "TÌM KIẾM" caption is dropped in this mode, and only in this mode. With the page's buttons on
   the same line it is what made the row too wide: measured on /admin/projects at a 1512px window,
   the caption, the first field and its two buttons took 590px and the right-hand group 866px,
   against 1384px of room. The search box and its "Tìm" button already say what the strip is. Saved
   views, which crm-select.js also puts in the head, stay. */
.crm-titled-by-tab .crm-shell-toolbar .crm-search-shell-title { display: none; }
.crm-titled-by-tab .crm-shell-toolbar > .crm-search-shell-head:not(:has(> :not(.crm-search-shell-title))) { display: none; }
/* The hidden head carried the row's left inset; the first panel takes it over. */
.crm-titled-by-tab .crm-shell-toolbar > .crm-search-shell-head + .crm-search-panel { padding-left: 14px; }
/* The fields take the room the buttons leave, from a 360px start. Not "auto": that is the panel's
   full width, so the group always went to a second line. Not shrink-to-content either: a panel's
   columns are percentages of it, so on Công nợ the box shrank to "Tìm t…" and Lọc was clipped. */
.crm-titled-by-tab .crm-search-shell.crm-shell-toolbar > .crm-search-panel { flex: 1 1 360px; width: auto; min-width: 0; }
/* The group is sized to its content by filter-panel.css (flex: 0 0 auto). With the page's buttons in
   it that is wider than a phone, and it ran off the right edge at 390px; allowed to shrink, it wraps
   inside the row instead. */
.crm-titled-by-tab .crm-shell-toolbar > .crm-search-shell-right { flex: 0 1 auto; min-width: 0; max-width: 100%; flex-wrap: wrap; justify-content: flex-end; }
/* Last in the group whichever script filled it first: the toggle and "Cột" belong to the search,
   the page's buttons act on the page. */
.crm-banner-actions { order: 99; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* Views float their button wrapper right and give it a margin; in a flex group both only misplace it. */
.crm-banner-actions > * { float: none !important; margin: 0 !important; }
/* The shell clips its contents (filter-panel.css) so a .row panel's negative gutters cannot bleed
   out; the column picker dodges that with a fixed-position menu. The page's own buttons bring
   ordinary dropdowns (Nhập opens "Tải tệp mẫu / Chọn tệp"), which the clip cut off at the row's
   bottom edge. The panel's side margins are zeroed inside the shell, so there is nothing to bleed. */
.crm-titled-by-tab .crm-search-shell.crm-shell-toolbar { overflow: visible; }
/* What stays of a banner once its title has gone: a page's buttons with no search row to join
   (Sổ quỹ's "Đối chiếu thu tiền / Thêm quỹ"), a description sentence, a month picker. The tint was
   the title's band; without the title a blue strip holding two buttons reads as something left
   behind, and the merged toolbars beside it carry no band either. The id is for weight:
   ".card-header.bg-info.bg-gradient" sets the tint with !important at three classes. */
#crm-page-body.crm-titled-by-tab .crm-banner-untitled {
    background: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
}

/* ---------------------------------------------------------------------------------------------
   Layout audit 2026-10-07 (every admin page at 1366px and 390px, measured in a real browser;
   ported from BMP df19b525f's chain).
   --------------------------------------------------------------------------------------------- */

/* .nk-content lost its padding above (flush content area), so the page container is 7px from the
   edge while a .row's negative margin is 14px: every row sitting straight in a card or a page body
   stuck out 7px and fourteen screens scrolled sideways on a desktop. clip, not hidden: it trims
   only the empty half-gutter and, unlike hidden, does not make .nk-content a scroll container, so
   sticky headers and toolbars inside it keep working. */
.nk-content {
    overflow-x: clip;
}

/* DataTables draws its sort arrows in the last ~21px of a sortable header and reserves 30px for
   them; the 12px side padding at the top of this file took that space back, so "Mối Quan Hệ",
   "Nguồn Khách Hàng", "Tỉnh/Thành Phố" ran under them. Higher specificity than ".dataTable th". */
.dataTable thead th.sorting,
.dataTable thead th.sorting_asc,
.dataTable thead th.sorting_desc,
.dataTable thead th.sorting_asc_disabled,
.dataTable thead th.sorting_desc_disabled {
    padding-right: 26px !important;
}

/* ---------------------------------------------------------------------------------------------
   Contrast on the theme's light accents (audit 2026-10-07). dashlite's warning #f4bd0e, success
   #1ee0ac and info #09c2de are light colours, and it draws white text on all three: 1.73, 1.71 and
   2.15:1 -- "Quay Lại" on 77 screens, every green status badge, every "Đang xử lý" cyan badge. The
   colours stay (they are the brand); what changes is the ink:
     - on a light FILL, the navy used for headings here: 7.40 / 7.51 / 5.97:1;
     - as TEXT or an OUTLINE on white, a darker shade of the same hue: #906f07 4.71, #128465 4.65,
       #068092 4.66:1 (WCAG AA is 4.5:1), which also carries white text when an outline fills on hover.
   .text-white is a !important utility, so it is beaten explicitly where views still carry it.
   --------------------------------------------------------------------------------------------- */
.btn-warning, .btn-success, .btn-info {
    --bs-btn-color: #16315f;
    --bs-btn-hover-color: #16315f;
    --bs-btn-active-color: #16315f;
    --bs-btn-disabled-color: #16315f;
    color: #16315f;
}
.btn.btn-warning.text-white, .btn.btn-success.text-white, .btn.btn-info.text-white,
.badge.bg-warning, .badge.bg-success, .badge.bg-info,
.badge.bg-warning.text-white, .badge.bg-success.text-white, .badge.bg-info.text-white,
.badge.badge-warning, .badge.badge-success, .badge.badge-info {
    color: #16315f !important;
}

.text-warning { color: #906f07 !important; }
.text-success { color: #128465 !important; }
.text-info    { color: #068092 !important; }

.btn-outline-warning {
    --bs-btn-color: #906f07; --bs-btn-border-color: #906f07;
    --bs-btn-hover-bg: #906f07; --bs-btn-hover-border-color: #906f07; --bs-btn-hover-color: #fff;
    --bs-btn-active-bg: #906f07; --bs-btn-active-border-color: #906f07; --bs-btn-active-color: #fff;
    color: #906f07; border-color: #906f07;
}
.btn-outline-success {
    --bs-btn-color: #128465; --bs-btn-border-color: #128465;
    --bs-btn-hover-bg: #128465; --bs-btn-hover-border-color: #128465; --bs-btn-hover-color: #fff;
    --bs-btn-active-bg: #128465; --bs-btn-active-border-color: #128465; --bs-btn-active-color: #fff;
    color: #128465; border-color: #128465;
}
.btn-outline-info {
    --bs-btn-color: #068092; --bs-btn-border-color: #068092;
    --bs-btn-hover-bg: #068092; --bs-btn-hover-border-color: #068092; --bs-btn-hover-color: #fff;
    --bs-btn-active-bg: #068092; --bs-btn-active-border-color: #068092; --bs-btn-active-color: #fff;
    color: #068092; border-color: #068092;
}
/* Text drawn on a light progress fill ("75%" inside an OKR bar) takes the same navy ink. */
.progress-bar.bg-warning, .progress-bar.bg-success, .progress-bar.bg-info {
    color: #16315f;
}

/* ABK only (same audit). A tone-dim badge -- bg-success-dim, bg-warning-dim ..., the tone picked by
   data such as an engineer's rating band -- is a pale tint that dashlite still letters in white:
   1.09:1. Each takes a darker ink of its own tone (4.5:1 or better on its tint). */
.badge.bg-primary-dim   { color: #3d4fd1 !important; }
.badge.bg-success-dim   { color: #0d6b52 !important; }
.badge.bg-warning-dim   { color: #765a05 !important; }
.badge.bg-info-dim      { color: #056a79 !important; }
.badge.bg-danger-dim    { color: #b8352a !important; }
.badge.bg-secondary-dim,
.badge.bg-gray-dim      { color: #3c4d62 !important; }

/* A banner's subtitle line written for the old teal band (<div class="text-white-50 small">,
   "Khách hàng chấm điểm trên ứng dụng") sat on the light #d5f3ff banner at 1.16:1. */
.card-header.bg-info small,
.card-header.bg-info .small,
.card-header.bg-info .text-white,
.card-header.bg-info .text-white-50 {
    color: #3d5a80 !important;
}

/* Small buttons in list rows were 22px tall on a phone ("Xem", "Chi Tiết", "Sao Kê", the project
   form's icon buttons at 18x31): under WCAG 2.2's 24px floor and hard to hit. 28px on phones and
   touch screens; desktop keeps the compact rows. */
@media (max-width: 767.98px), (pointer: coarse) {
    .nk-content .btn-sm,
    .nk-content .btn-xs {
        min-height: 28px;
        min-width: 28px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .nk-content .btn-link.p-0 {
        min-height: 24px;
    }
}

