:root {
    /* Chiều cao thanh nav fixed-top; .content phải chừa đúng khoảng này, và mọi mốc sticky bên dưới
       cũng neo theo nó. */
    --navbar-height: 50px;
}

/*
 * ĐÃ THỬ rồi bỏ (14/10/2026): `html { overflow-y: scroll }` để rãnh cuộn luôn có, cho đỡ giật ~15px
 * mỗi lần đi từ trang dài sang trang ngắn (rõ nhất khi đổi tab ở Cài đặt).
 *
 * Bỏ vì nó phá phần bù thanh cuộn của Bootstrap: lúc mở modal, Bootstrap đặt overflow: hidden lên
 * BODY chứ không phải html (xem addClass(container, 'modal-open') trong BootstrapModalManager), nên
 * rãnh ở html KHÔNG mất — mà nó vẫn đo ra 15px và vẫn cộng padding-right vào body. Kết quả: mở modal
 * là nội dung phía sau dịch sang 15px. `scrollbar-gutter: stable` cũng hỏng y vậy, và còn phá
 * holdNavbarWidth() ở resources/js/lib/lightbox.ts.
 *
 * Vá được bằng `html:has(> body.modal-open) { overflow-y: hidden }`, nhưng chủ repo chọn bỏ cả cặp:
 * đổi một chỗ giật lấy hai luật CSS ràng buộc nhau thì không đáng.
 */

body {
    font-size: 0.875rem;
    background-color: #f6f8fb;
}

a {
    color: #206bc4;
    text-decoration: none;
}

b, strong, .form-label, .col-form-label, .nav-underline .nav-link.active {
    font-weight: 600;
}

.form-label {
    margin-bottom: .375rem;
}

/* Form hẹp (login / OAuth / đổi mật khẩu): 25rem = 400px trên desktop, full width trên mobile. */
.form-narrow {
    width: 100%;
    max-width: 25rem;
    margin-inline: auto;
}

.main_wrapper {
    display: flex;
    max-width: 1920px;
    margin: 0 auto;
}

.sidebar {
    width: 240px;
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
}

.main_wrap {
    display: flex;
    flex-direction: column;
    flex: 1;
    /* Như .wrapper: cho co dưới min-content của bảng bên trong, nếu không nó tràn khỏi .list_layout
       thay vì để .main_panel (overflow: auto) cuộn ngang. */
    min-width: 0;
}

.main_wrap .main_panel {
    overflow: auto;
}

.main_wrap .heading_search, .main_wrap .search_group, .sidebar .heading {
    flex: 0 0 auto;
}

.main_wrap div.section + div.section {
    margin-top: 1rem;
}

.content {
    padding-top: var(--navbar-height);
}

.wrapper {
    flex: 1;
    /* Không có dòng này thì item flex không co dưới min-content của bảng bên trong, phần dư bị
       đẩy sang .side. Cho phép co để .main_panel (overflow: auto) tự lo phần cuộn ngang. */
    min-width: 0;
}

.side {
    /* Một chỗ khai, dùng lại ở .side_wide .nav để nới thanh cuộn ra sát mép. */
    --side-padding: .25rem;
    width: 5rem;
    /* Giống .sidebar: cột cố định, không được co khi nội dung bên phải quá rộng. */
    flex: 0 0 auto;
    background-color: rgba(32, 107, 196, .1);
    /* KHÔNG padding ở đây — nó nằm ở .side .nav, để thanh cuộn của rail chạm sát viền trong thay vì
       hở ra một khoảng. Cùng khuôn với .channel-list / .channel-list-scroll. */
    padding: 0;
    /*
     * KHÔNG bo góc ở đây. Rail hẹp của các trang tab chạm hẳn mép trái và mép dưới màn hình (.content
     * không có padding ngang, dải nền cao ít nhất hết phần dưới thanh nav — xem min-height), nên bo chỉ
     * ăn ở hai góc phải — nhìn như bo hụt. Chỉ .side_wide mới bo, vì trang settings canh giữa nên cả
     * bốn góc đều hở.
     */

    /*
     * Dải nền cao ít nhất một màn hình.
     *
     * Trước 12/10/2026 không cần: hàng cao đúng calc(100vh - navbar) nên .side là flex item stretch đã
     * tự cao hết màn. Từ khi trang cuộn theo body, hàng cao TỰ NHIÊN — trang ngắn (Chấm công lúc chưa
     * mở khóa, Quảng cáo facebook) thì dải nền cụt ngang chỗ nội dung hết, nhìn như rail bị cắt khúc.
     *
     * min-height chứ không height: trang dài hơn một màn thì rail vẫn phải kéo xuống hết nội dung, nếu
     * không cuộn xuống là hở một mảng trắng bên trái.
     */
    min-height: calc(100vh - var(--navbar-height));
}

/* .nav là flex nên gap lo khoảng cách giữa các mục. Nếu để margin trên .nav-link thì hai mục liền
   nhau cộng margin của cả hai — flex không gộp margin như block — thành ra cách nhau gấp đôi
   khoảng cách từ mục ra lề, đúng lỗi nhìn thấy trên rail của trang Đơn hàng. */
.side .nav {
    gap: .25rem;
    /* Mục bo nhỏ hơn khung ngoài. Đổi biến của .nav-pills thay vì đè border-radius của .nav-link:
       Bootstrap đọc chính biến này, sửa ở đây là mọi trạng thái (active, hover) theo cùng. */
    --bs-nav-pills-border-radius: var(--bs-border-radius-sm);
    /* Lề quanh danh sách nằm ở đây chứ không ở .side: để ngoài thì thanh cuộn bị đẩy vào trong, hở một
       khoảng với viền; để trong thì thanh cuộn dính sát viền, còn mục vẫn thụt vào như cũ.
       Cũng không đặt ở margin của .nav-link: mục đầu và mục cuối chỉ có margin của riêng nó nên mép
       trên dưới sẽ hụt một nửa so với trái phải. */
    padding: var(--side-padding);
}

.side .nav-link {
    padding: .25rem;
}

.side .nav-link.active {
    background-color: rgba(32, 107, 196, .15);
    color: #206bc4;
}

/*
 * Trang settings bó CẢ rail lẫn nội dung vào giữa màn hình — ở đây là chữ mô tả và form ngắn, trải hết
 * 1920px thì mắt quét quá xa và dòng chữ dài lê thê. Các trang tab khác là bảng danh sách nhiều cột,
 * bó lại là mất chỗ, nên chỉ settings mang class này.
 *
 * Bề rộng KHÔNG khai ở đây: hàng được BỌC trong .container-xl (xem Layouts/TabsLayout) nên lấy thẳng thang
 * bề rộng của Bootstrap — 1140px ở 1200–1399px và 1320px từ 1400px trở lên, dưới 1200px thì rộng hết
 * màn. Trước 14/10/2026 chỗ này ghim cứng max-width: 1200px, một con số không ăn nhập với breakpoint nào.
 *
 * Chọn .container-xl chứ KHÔNG phải .container-xxl: xxl chỉ có max-width từ 1400px trở lên, dưới đó
 * rộng 100% — màn 1200–1399 sẽ trải hết thay vì bó lại như xưa nay.
 *
 * ĐÃ BỎ cùng lúc: margin-left: max(0px, round(down, calc((100% - 1200px) / 2), 1px)). Nó chốt lề trái về
 * pixel nguyên để chữa lỗi lệch 1px của rail trên Firefox. Không giữ lại được vì công thức phải biết
 * max-width, mà giờ con số đó đổi theo breakpoint của Bootstrap — viết lại là ghim cứng cả thang, đúng
 * thứ vừa bỏ. Lỗi 1px nay chữa bằng cách khác: rail thôi sticky, xem chú thích .side_wide trong khối
 * @media (min-width: 992px).
 */
.main_wrapper.narrow_layout {
    /* Hở đáy cân với lề trên của hàng (xem .main_wrapper.narrow_layout trong khối @media ở dưới). Khai
       ở đây chứ không trong media query vì không có luật nào tranh chấp, nên màn hẹp cũng được hưởng. */
    padding-bottom: 1rem;
}

/*
 * Phần tử cuối của một tab settings bỏ lề dưới của chính nó: đã có padding của hàng ở trên, để cả hai
 * là hở gấp đôi. Đây là NGOẠI LỆ của trang settings, đừng nới ra cho trang danh sách — ở đó không có
 * padding nào ở hàng nên lề của thanh phân trang / card chính là khoảng hở, xem chú thích .scroll_body.
 *
 * Hai selector vì mỗi tab một kiểu: tab có form thì con cuối của form là cụm nút Lưu, tab không form
 * thì con cuối của hàng (.main_wrapper) là cái card hoặc bảng dưới cùng.
 *
 * PHẢI !important: lề đến từ utility mb-2/mb-3 của Bootstrap, mà utility nào cũng khai !important nên
 * selector thường dù đặc hiệu tới đâu cũng thua.
 */
.narrow_layout form > :last-child,
.narrow_layout .main_wrapper > :last-child {
    margin-bottom: 0 !important;
}

/* Biến thể chỉ dùng cho rail của trang settings — 20 tab, gấp nhiều lần 15 trang .side còn lại,
   nên rail hẹp căn giữa kiểu cũ vừa cao gấp đôi vì nhãn xuống dòng, vừa bị cắt cụt do không cuộn.
   Đừng gộp vào .side: 15 trang kia chỉ vài tab, nới rộng chúng là lấy không chỗ của bảng. */
.side_wide {
    /* Nới từ 12rem khi nhãn về cỡ chữ thường (0.875rem) — "Thanh điều hướng", "Cấu hình hóa đơn"
       là hai nhãn dài nhất, 12rem là chúng chạm mép. */
    width: 13.5rem;
    /* Cùng bán kính với khung .channel-list (hộp chọn kênh ở hộp thoại Livestream) cho hai chỗ nhìn
       giống nhau; mục bên trong hạ xuống -sm ở .side .nav. */
    border-radius: var(--bs-border-radius);
    /* ĐÃ BỎ `overflow: hidden`. Nó có để cắt thanh cuộn của .side_wide .nav theo góc bo — thanh cuộn nằm
       trong chính khối cuộn nên border-radius của khối đó không cắt được nó, hai góc phải thành vuông.
       Từ 14/10/2026 .nav thôi cuộn nên không còn thanh cuộn nào để cắt; tiện thể bớt một thuộc tính khiến
       Firefox tách lớp vẽ riêng, thứ dính tới lỗi lệch 1px. Dựng lại cuộn cho rail thì khai lại dòng này. */
    /* Gỡ min-height thừa kế từ .side: rail hẹp cần dải nền cao hết màn vì nó chạm mép trái và mép dưới,
       còn rail này là một khối bo góc canh giữa — cao tự nhiên theo số tab mới đúng dáng. */
    min-height: auto;
}

/*
 * ĐÃ BỎ 14/10/2026 cả cụm `max-height: calc(100vh - navbar) + overflow-y: auto + flex-wrap: nowrap`.
 *
 * Nó có từ thời khung trang cao đúng một màn hình: rail dài hơn màn sẽ kéo cả hàng flex cao theo mà
 * KHÔNG AI CUỘN được, mục cuối nằm ngoài tầm với — nên phải cho chính rail tự cuộn. (flex-wrap: nowrap
 * đi kèm vì .nav của Bootstrap mặc định wrap: hướng cột mà bị chặn chiều cao thì mục không vừa nhảy sang
 * CỘT THỨ HAI chứ không tràn dọc.)
 *
 * Nay body cuộn nên rail dài chỉ làm trang dài thêm, cuộn trang là tới — khối cuộn đó hết việc. Bỏ nó
 * đồng thời gỡ một khung cuộn khỏi cột, thứ mà Firefox cũng đẩy lên lớp vẽ riêng và snap về pixel nguyên
 * y như phần tử sticky — tức ứng viên còn lại của lỗi lệch 1px.
 *
 * Đánh đổi: màn hình THẤP hơn chiều cao rail (20 tab ≈ 700px, laptop 1366×768 còn ~600px khung nhìn) thì
 * rail dính ngay từ lượt cuộn đầu và mấy tab cuối chỉ lộ ra khi cuộn tới đáy trang — đúng bản chất sticky
 * đã gặp ở cột lọc, xem .sidebar_scroll.
 *
 * Nếu phải dựng lại cuộn cho rail: đặt ở .nav chứ đừng ở .side — thanh cuộn của .side bám mép ngoài khối
 * nên cắt ngang góc bo, còn ở .nav thì nó nằm trong padding .25rem, lọt hẳn vào trong khối bo.
 */

.side_wide .nav-link {
    /*
     * Nhãn nằm cạnh icon chứ không ở dưới nên cần thở ngang. Dọc nới hơn .side (.25rem) vì mục ở đây
     * chỉ cao một dòng chữ, để .25rem thì nhìn dẹp.
     *
     * Đừng nới quá .375rem: 20 mục × (21px dòng chữ + padding + 4px gap) phải lọt một màn hình laptop
     * (768px trừ thanh nav), không thì rail phải cuộn ngay cả khi màn còn chỗ.
     */
    padding: .375rem .5rem;
    /* Nhãn một dòng, nếu không 20 mục cao gấp đôi và rail buộc phải cuộn. */
    white-space: nowrap;
}

/*
 * ĐÃ BỎ cả cụm "chừa đáy cho khung cuộn" (padding-bottom cho bốn lớp khung cuộn, pagination-wrapper
 * margin-bottom: 0, và hai luật :last-child { margin-bottom: 0 !important }).
 *
 * Chúng tồn tại vì margin dưới của con cuối trong một khối overflow KHÔNG được tính vào vùng cuộn —
 * cuộn tới đáy là card hay thanh phân trang vẫn dính mép dù đã mang mb-3. Nay trang cuộn theo body,
 * không còn khối overflow nào nuốt margin nên mb-3 sẵn có tự tạo ra khoảng hở, và thêm padding nữa là
 * hở gấp đôi. Đừng dựng lại.
 */

.wrapper .main_wrapper {
    padding-top: 1rem;
}

.daterangepicker, .wb-title {
    font-family: inherit;
}

.daterangepicker .ranges {
    margin: 8px 0;
}

.form-control, .form-select, .input-group-text {
    font-size: 0.875rem;
}

.btn {
    --bs-btn-font-size: 0.875rem;
}

.dropdown-menu {
    box-shadow: var(--bs-box-shadow);
    --bs-dropdown-font-size: .875rem;
    --bs-dropdown-padding-x: .25rem;
    --bs-dropdown-padding-y: .25rem;
}

.dropdown-item {
    border-radius: .25rem;
}

.dropdown-divider {
    --bs-dropdown-divider-margin-y: .25rem;
}

.page-link {
    --bs-pagination-font-size: 0.875rem;
}

.btn-minimize {
    --bs-btn-font-size: 0.75rem;
    --bs-btn-padding-y: 0.25rem;
    --bs-btn-padding-x: 0.5rem;
}

.btn-minimize:active {
    border-color: transparent !important;
}

.form-control:focus, .form-select:focus, .btn.focus, .btn:focus, .page-link:focus, .btn-close:focus, .form-check-input:focus,
.focus .ts-control, .plugin-dropdown_input.focus .ts-dropdown .dropdown-input {
    box-shadow: none !important;
}

.form-check-label {
    font-weight: 400;
}

.table {
    --bs-table-bg: transparent;
}

.text-info {
    color: #17a2b8 !important;
}

.card {
    --bs-card-border-color: var(--bs-border-color);
}

/* Mặc định Bootstrap là translate(0, -50px) — trông như rơi từ trên xuống.
   Đổi sang scale để modal phóng ra từ giữa. Bắt buộc có :not(.show) — rule này nằm sau
   bootstrap.css nên nếu không loại trừ sẽ đè luôn .modal.show .modal-dialog { transform: none }
   và modal kẹt ở scale(.9) sau khi mở. */
.modal.fade:not(.show) .modal-dialog {
    transform: scale(.95);
}

/* Rút ngắn từ .3s của Bootstrap. Để rời ra chứ không gộp vào rule trên: transition phải có mặt
   ở cả hai trạng thái, còn rule trên chỉ áp khi chưa .show. */
.modal.fade .modal-dialog {
    transition-duration: .15s;
}

.modal-lg {
    max-width: 900px;
}

.modal-xl {
    max-width: 1200px;
}

.badge-shopee {
    background-color: #f63;
    color: #fff;
}

.badge-lazada {
    background-color: rgb(233, 41, 145);
    color: #fff;
}

.text-bg-muted {
    background-color: #ccc !important;
    color: #333 !important;
}

.text-bg-info {
    background-color: #17a2b8 !important;
    color: #fff !important;
}

.badge-pending {
    color: #fff;
    background-color: #9d6bff;
}

a.badge-pending:focus, a.badge-pending:hover {
    color: #fff;
    background-image: linear-gradient(rgba(0, 0, 0, .1), rgba(0, 0, 0, .1));
}

a.badge-primary.focus, a.badge-primary:focus, a.badge-secondary.focus, a.badge-secondary:focus, a.badge-success.focus, a.badge-success:focus, a.badge-danger.focus, a.badge-danger:focus, a.badge-warning.focus, a.badge-warning:focus, a.badge-info.focus, a.badge-info:focus, a.badge-light.focus, a.badge-light:focus, a.badge-dark.focus, a.badge-dark:focus {
    box-shadow: none;
}

/*
 * Thanh nav GIỮ position: fixed (class .fixed-top của Bootstrap). Đã thử đổi sang sticky trong luồng
 * 13/10/2026 rồi hoàn nguyên ngay — đừng thử lại:
 *
 * Mấy thư viện khoá cuộn bằng cách đặt overflow: hidden lên <html>. Khung cuộn biến mất thì sticky hết
 * chỗ bám, thanh nav rơi về vị trí TĨNH của nó ở đầu tài liệu. Đang cuộn giữa trang mà mở lightbox là
 * nó biến mất khỏi khung nhìn. Fixed neo theo khung nhìn nên không dính chuyện đó.
 *
 * Đổi lại fixed nằm ngoài body: thư viện nào bù bề rộng thanh cuộn bằng padding/margin phải TRÊN BODY
 * thì thanh nav không ăn theo, thanh cuộn mất là nó nở thêm ~15px và các mục căn phải nhảy. Bootstrap
 * tự vá chỗ này (SELECTOR_FIXED_CONTENT có .fixed-top), GLightbox thì không — nên phần bù cho lightbox
 * nằm ở resources/js/lib/lightbox.ts.
 */
.navbar {
    padding-top: 0;
    padding-bottom: 0;
    background-color: #066fd1;
}

.navbar > .container-fluid {
    max-width: 1920px;
}

.navbar-main {
    display: flex;
    flex-grow: 1;
    /* Không có dòng này thì .navbar-main không co dưới min-content, menu đẩy .navbar-right
       (chuông, avatar) ra khỏi màn hình. */
    min-width: 0;
}

.navbar-brand {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 1.125rem;
    padding-top: 0;
    padding-bottom: 0;
    line-height: var(--navbar-height);
}

.navbar-main .navbar-nav {
    flex-wrap: wrap;
}

/* Mắt cuối của chuỗi co: thiếu nó thì .navbar-collapse nở theo nội dung, .nav_overflow đo được
   chỗ trống bằng đúng chiều dài menu và kết luận không có gì tràn. */
.navbar-main .navbar-collapse {
    min-width: 0;
}

/* Menu không được xuống dòng: navOverflow đo bề rộng từng mục trên MỘT hàng ngang.
   overflow: hidden để phần dư không lòi ra trong lúc chờ fit() chạy. */
.nav_overflow > .navbar-nav {
    flex-wrap: nowrap;
    overflow: hidden;
}

/* Mục KHÔNG được co. Để mặc định flex-shrink: 1 thì chúng tự thu nhỏ cho vừa chỗ trống —
   không bao giờ "tràn" nên không có gì để dồn, mà bề rộng đo lúc khởi tạo cũng là số đã bị bóp. */
.nav_overflow > .navbar-nav > li {
    flex-shrink: 0;
}

.navbar-dark .navbar-nav .nav-link {
    padding-left: .75rem;
    padding-right: .75rem;
    color: #fff;
    border-radius: var(--bs-border-radius);
}

.navbar-dark .navbar-nav .nav-link.active {
    background-color: rgba(255, 255, 255, 0.15);
}

/* Bám theo .notif-panel chứ không theo li.ajax-dropdown: chuông do React dựng (NotificationBell). */
.dropdown-menu.notif-panel {
    overflow: hidden;
    padding: 0;
}

.dropdown-menu.notif-panel .dropdown-content {
    overflow-y: auto;
    max-height: 90vh;
    max-height: 90dvh;
    width: 22.5rem;
}

/* .notif-panel: dùng chung cho dropdown (desktop) và bottom sheet (mobile) */

.notif-panel .lead {
    font-size: 18px;
}

.notif-panel .dropdown-item {
    white-space: normal;
}

.notif-panel .dropdown-item a {
    color: unset;
}

.notif-panel .dropdown-item a:not(.action) {
    display: block;
    margin-right: 1.5rem;
}

.notif-sheet {
    --bs-offcanvas-height: 85dvh;
    /* Nội dung nạp về là .dropdown-item nhưng sheet không phải .dropdown-menu,
       nên phải tự khai các biến Bootstrap chỉ định nghĩa trên .dropdown-menu */
    --bs-dropdown-item-padding-x: 1rem;
    --bs-dropdown-item-padding-y: .25rem;
    --bs-dropdown-link-color: var(--bs-body-color);
    --bs-dropdown-link-hover-color: var(--bs-body-color);
    --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg);
    --bs-dropdown-divider-bg: var(--bs-border-color-translucent);
    border-top-left-radius: var(--bs-border-radius-lg);
    border-top-right-radius: var(--bs-border-radius-lg);
}

.notif-sheet .offcanvas-header {
    border-bottom: 1px solid var(--bs-border-color);
}

/*
 * Bottom sheet của menu "…" trong bảng (RowMenu trên màn hẹp). Khác .notif-sheet ở chiều cao:
 * panel thông báo là danh sách dài nên cố định 85dvh, còn đây chỉ vài mục nên để cao theo nội
 * dung — mặc định 30vh của Bootstrap sẽ chừa một mảng trống dưới các mục.
 */
.row-menu-sheet {
    --bs-offcanvas-height: auto;
    border-top-left-radius: var(--bs-border-radius-lg);
    border-top-right-radius: var(--bs-border-radius-lg);
    /* Thanh gạt home của iPhone đè lên mục cuối nếu không chừa. */
    padding-bottom: env(safe-area-inset-bottom);
}

.row-menu-sheet .list-group-item {
    border: 0;
    border-radius: var(--bs-border-radius);
    padding: .75rem 1rem;
}

/* Cùng ý với Dropdown.Divider: tách thao tác nguy hiểm khỏi nhóm mục thường. */
.row-menu-sheet .list-group-item.divider {
    border-top: 1px solid var(--bs-border-color);
    border-radius: 0;
}

.navbar-right li.nav-item span.count {
    position: absolute;
    bottom: 50%;
    left: 50%;
}

.nav-link {
    color: #206bc4;
}

.section_username .nav-link {
    padding-top: .25rem !important;
    padding-bottom: .25rem !important;
}

.dropdown .avatar {
    width: 29px;
    height: 29px;
}

hr {
    color: rgba(0, 0, 0, .4);
}

.notification-content {
    padding: .5rem;
}

.notification-content .notif-icon {
    width: 3.5rem;
    height: 3.5rem;
    flex-shrink: 0;
    border-radius: 50%;
    background-color: #e9ecef;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    color: #6c757d;
}

.notification-content .notif-icon i {
    font-size: 1.25rem;
    line-height: 1;
}

.notification-content .notif-icon img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.notif-panel .dropdown-item {
    padding-inline: .5rem;
    border-radius: .375rem;
}

.notif-panel .dropdown-item a.action {
    opacity: 0;
    background-color: #ddd;
    width: 1.5rem;
    height: 1.5rem;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    float: right;
}

.notif-panel .dropdown-item:hover a.action {
    opacity: 1;
    color: #6c757d;
}

.notif-panel .dropdown-item:hover a.action:hover {
    color: #212529;
}

.notif-panel .dropdown-item:hover, .notif-panel .dropdown-item:active {
    background-color: rgba(0, 0, 0, 0.05);
    color: unset;
}

.notif-panel .dropdown-divider {
    margin: 0;
}

/* nav_overflow */

.nav_overflow {
    display: flex;
    position: relative;
    /* flex-basis PHẢI là 0, không phải auto: auto lấy bề rộng cơ sở theo nội dung nên wrapper
       phình bằng trọn chiều dài menu, fit() đo được chỗ trống bằng đúng nội dung và kết luận
       không có gì tràn. Basis 0 thì nó chỉ nhận phần còn lại sau brand và .navbar-right. */
    flex: 1 1 0;
    min-width: 0;
}

.nav_overflow__menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: var(--bs-dropdown-spacer);
}

.nav_overflow--open .nav_overflow__menu {
    display: block;
}

.nav_overflow__menu li a {
    color: var(--bs-body-color);
    padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
    border-radius: .25rem;
}

.nav_overflow__menu li a:hover {
    color: var(--bs-dropdown-link-hover-color);
    background-color: var(--bs-dropdown-link-hover-bg);
}

.nav_overflow__menu li a.active {
    color: var(--bs-dropdown-link-active-color);
    background-color: var(--bs-dropdown-link-active-bg);
}

/* Nút nằm ngoài .navbar-nav nên không thừa hưởng biến padding của .nav — khai lại như .add_tab. */
.nav_overflow__toggle {
    flex-shrink: 0;
    --bs-nav-link-padding-x: .75rem;
    --bs-nav-link-padding-y: .5rem;
    color: #fff;
    background: none;
    border: 0;
}

.nav_overflow__toggle:hover {
    color: rgba(255, 255, 255, .75);
}

/* nav_overflow */

#menu.offcanvas {
    --bs-offcanvas-width: clamp(200px, 80%, 440px);
    --bs-offcanvas-padding-y: .875rem;
    --bs-offcanvas-border-width: 0;
    --bs-offcanvas-bg: #066fd1;
    --bs-offcanvas-color: #fff;
}

#menu .offcanvas-header {
    height: var(--navbar-height);
    flex-shrink: 0;
    background-color: rgba(0, 0, 0, .1);
}

#menu .offcanvas-body {
    /* Chừa đáy để mục cuối không đụng góc bo tròn / thanh home của máy đời mới */
    padding: 0 0 var(--navbar-height);
}

#menu .nav-link {
    color: rgba(255, 255, 255, .85);
    padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);
    border-bottom: 1px solid rgba(255, 255, 255, .15);
}

#menu .nav-link:hover, #menu .nav-link.active {
    color: #fff;
    background-color: rgba(255, 255, 255, .1);
}

#menu .nav-link i {
    width: 1.25rem;
    margin-right: .5rem;
    text-align: center;
}

.heading .search_section {
    max-width: 400px;
    flex: 1;
    margin-right: 15px;
}

.heading .date {
    width: 200px;
}

.heading .date input {
    background-color: #fff;
    cursor: pointer;
}

.heading .date input:focus {
    border-color: #ccc;
}

.heading .date span {
    background-color: #fff;
}

.heading_search #datefilter {
    width: 220px;
}

.heading .lead {
    line-height: calc(1.35em + .375rem + 2px);
}

.customer_section, .order_section, .shipping_section {
    position: relative;
    margin-top: .5rem;
}

.bg-complete {
    background-color: #dff0d8 !important;
}

.bg-info {
    background-color: #d9edf7 !important;
}

.bg-warning {
    background-color: #fcf8e3 !important;
}

.bg-danger {
    background-color: #f2dede !important;
}

.bg-muted {
    background-color: #eee !important;
}

.main_panel {
    margin-bottom: calc(var(--bs-gutter-x) * .5);
}

.main_panel .inner {
    display: flex;
    flex-direction: column;
    height: 100%;
}

/*
 * ĐÃ BỎ min/max-width: 225px.
 *
 * Con số đó là 240 (bề rộng .sidebar) trừ đi một thanh cuộn, ghim cứng để ô lọc khỏi co giãn mỗi lượt
 * rê chuột — hồi .side-sheet còn bật/tắt overflow theo :hover. Nay overflow bật suốt và chỉ thanh cuộn
 * đổi màu, bố cục không nhúc nhích nữa nên nó hết việc.
 *
 * Bỏ được là tốt: 225 chỉ "vừa" trên Chrome/Windows (thanh cuộn 15px). Firefox dùng 17px nên hụt 2px và
 * bị overflow-x: hidden cắt im, còn macOS để thanh cuộn phủ rộng 0px thì thừa nguyên 15px. Giờ trình
 * duyệt tự chừa đúng bề rộng thật của nó.
 */
.filter_content {
    /* Nửa gutter của container (BS5), thay cho 15px cứng thời BS3/4. Khai qua biến để lỡ đổi
       --bs-gutter-x thì cột lọc giãn theo. */
    padding-right: calc(var(--bs-gutter-x) * .5);
    /* Khoảng hở dưới ô lọc cuối, thay cho margin-bottom đã bỏ ở .side-sheet. Là PADDING của khối bên
       trong nên ở cột .sidebar_scroll nó nằm trong vùng cuộn, không bị nuốt như margin của phần tử cuối. */
    padding-bottom: 1rem;
}

/* Cột trái của .sidebar khi nó đổi vai theo bề rộng: từ 992px là cột tĩnh, dưới đó Bootstrap
   biến chính nó thành sheet trượt từ dưới lên (xem .offcanvas-lg trong các view dùng class này).
   Dùng cho cả menu (announcements, channels) lẫn bộ lọc (orders...).

   Trước đây cột này mang id #filter_panel cùng class .main_panel, và bộ luật bám vào chúng
   (height, margin-bottom, :hover overflow) làm animation trượt lệch pha với backdrop —
   backdrop tối trước, sheet mới bò theo sau. Đã bisect từng luật một mà không quy được về
   luật nào, nhưng bỏ cả cụm thì hết. Nên đừng gộp ngược .side-sheet vào một selector dùng
   chung với thứ khác: mỗi chế độ chỉ khai đúng thứ nó cần. */
@media (min-width: 992px) {
    /*
     * CHỈ cột lọc mang .sidebar_scroll — tức cột dài hơn một màn hình, xem chú thích ở .sidebar_scroll
     * phía dưới. Cột lọc thường cao tự nhiên và dính bằng sticky, không có overflow nào nên dropdown ô
     * lọc không bị cắt; thêm overflow vào đây là cắt lại toàn bộ.
     *
     * Cuộn bật SUỐT, thanh cuộn chỉ TÀNG HÌNH cho tới khi rê chuột.
     *
     * Trước đây để `overflow: hidden` rồi :hover mới đổi sang auto. Nhìn thì giống hệt (không rê chuột
     * thì không thấy thanh cuộn) nhưng nó đổi BỐ CỤC: thanh cuộn sinh ra chiếm 15–17px, nội dung co lại
     * nhường chỗ, rời chuột thì nở ra — ô lọc giật mỗi lượt rê chuột ra vào. Đó cũng là lý do
     * .filter_content từng phải ghim cứng 225px để né, nay đã bỏ.
     *
     * Cách này đổi MÀU thumb chứ không đổi overflow, nên bố cục đứng yên tuyệt đối. Tiện thể lăn chuột
     * cuộn được cả khi thumb đang tàng hình, điều mà overflow: hidden không cho.
     *
     * Màu khai hai lần vì hai dòng trình duyệt: scrollbar-color là chuẩn (Firefox, Chrome 121+), còn
     * ::-webkit-scrollbar-thumb cho Chrome/Safari cũ. Không đụng nhau — Chrome mới thấy scrollbar-color
     * thì bỏ qua hẳn nhóm pseudo-element kia.
     *
     * Trên macOS, hễ tạo kiểu cho thanh cuộn là trình duyệt bỏ loại "phủ" (rộng 0px) chuyển sang loại
     * chiếm chỗ — nhờ vậy ba nền tảng về cùng một hành vi thay vì mỗi nơi một bề rộng.
     *
     * KHÔNG margin-bottom: khối cuộn chạy sát viền dưới như cột nội dung bên phải, để thanh cuộn của nó
     * cũng dài hết cột. Khoảng hở cho ô lọc cuối nằm ở padding-bottom của .filter_content — margin ở đây
     * đẩy cả khối cuộn lên, còn padding nằm TRONG vùng cuộn nên vừa giữ hở vừa không cắt ngắn thanh cuộn.
     *
     * Giữ overflow-x: hidden như cũ: khai mỗi overflow-y thì trục x tự về auto (một trục khác visible là
     * trục kia thành auto), nhãn dài sẽ đẻ thêm thanh cuộn ngang.
     */
    .sidebar_scroll .side-sheet {
        height: 100%;
        overflow-x: hidden;
        overflow-y: auto;
        /*
         * Chừa chỗ thanh cuộn THƯỜNG TRỰC, kể cả khi danh sách lọc ngắn và không có gì để cuộn.
         *
         * Thiếu dòng này thì khoảng hở sang bảng bên phải khác nhau theo trang: trang nhiều ô lọc hở
         * 12px (padding-right) cộng dải thanh cuộn, trang ít ô lọc chỉ còn 12px nên ô lọc dính sát bảng.
         * `stable` cho mọi trang cùng một khoảng hở, mà vẫn do trình duyệt tự tính đúng bề rộng của nó.
         *
         * Safari dưới 18.2 chưa hỗ trợ: ở đó rơi về `auto`, tức đúng tình trạng không đều nói trên —
         * xấu chứ không hỏng.
         */
        scrollbar-gutter: stable;
        scrollbar-color: transparent transparent;
    }

    .sidebar_scroll .side-sheet:hover {
        scrollbar-color: rgba(0, 0, 0, .25) transparent;
    }

    .sidebar_scroll .side-sheet::-webkit-scrollbar-thumb {
        background-color: transparent;
    }

    .sidebar_scroll .side-sheet:hover::-webkit-scrollbar-thumb {
        background-color: rgba(0, 0, 0, .25);
    }
}

/* Khối dưới đây còn thay vai trò của .offcanvas-body: không gắn class đó lên .filter_content
   được, vì từ 992px Bootstrap đặt cho .offcanvas-body display:flex và padding:0 với độ ưu
   tiên cao hơn, nuốt mất padding-right mà cột tĩnh cần. */
@media (max-width: 991.98px) {
    .side-sheet .filter_content {
        /* Lấp đầy sheet. Không phải gỡ min/max-width nữa — bản desktop đã bỏ hẳn phần ghim bề rộng. */
        flex-grow: 1;
        padding: 1rem;
        overflow-y: auto;
    }

    /* Cao cố định 85dvh giống .notif-sheet — để mặc định 30vh thì menu vài mục ra cái sheet
       lùn tịt. Khai qua biến đúng như .notif-sheet, đừng ghi thẳng height. */
    .side-sheet {
        --bs-offcanvas-height: 85dvh;
        border-top-left-radius: var(--bs-border-radius-lg);
        border-top-right-radius: var(--bs-border-radius-lg);
    }

    /* Ngăn tiêu đề sheet với danh sách, giống .notif-sheet của panel thông báo. */
    .side-sheet .offcanvas-header {
        border-bottom: 1px solid var(--bs-border-color);
    }

    /* Tiêu đề dùng .lead fw-semibold như sheet thông báo, nhưng .lead mặc định là 1.25rem
       còn bên đó bị ".notif-panel .lead" kéo về 18px — khai lại đúng con số để hai sheet
       cùng cỡ chữ. Chỉ cần ở mobile: từ 992px Bootstrap ẩn hẳn .offcanvas-header. */
    .side-sheet .offcanvas-title {
        font-size: 18px;
    }
}

/* Hàng lọc nằm ngang của các tab settings, thay cho sidebar 240px cũ: mỗi tab ở đó chỉ có một
   nhóm radio 2-4 lựa chọn nên không đáng một cột riêng. Tách class thay vì dùng utility để ghi đè
   margin-right của .heading .search_section — gộp hai thứ lại thì khoảng cách thành 15px + gap. */
.filter_bar {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
}

.filter_bar .search_section {
    margin-right: 0;
}

/* Select rộng theo nội dung để nhiều bộ lọc vẫn nằm vừa một hàng (settings/user có 3 cái). */
.filter_bar .form-select {
    width: auto;
}

.sale_panel ul li a span.close_tab, ul.cart-tab li a span.close_tab {
    color: #ccc;
    margin-left: .5rem;
}

.sale_panel ul li a span.close_tab:hover, ul.cart-tab li a span.close_tab:hover {
    color: #333;
    cursor: pointer;
}

.sale_panel ul li a span.tab_name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav-underline-wrapper {
    box-shadow: inset 0 -1px 0 #ddd;
    /* Cùng lý do với .nav-wrapper bên dưới: đặt trong .card (flex-column) mà .main_panel có
       overflow:auto nên card bị khống chế chiều cao, tab là flex item sẽ bị co. Thêm nữa
       overflow-x:auto khiến overflow-y tự thành auto, phần bị co bị CẮT chứ không tràn —
       mất nửa dưới chữ lẫn gạch chân. Thấy ở reports?tab=request và ?tab=payment. */
    flex-shrink: 0;
    /* Tab tràn thì cuộn ngang (vuốt trên mobile) thay vì dồn vào dropdown.
       Giấu thanh cuộn cho gọn — tab đang chọn vẫn được kéo vào tầm nhìn bằng JS. */
    overflow-x: auto;
    scrollbar-width: none;
    /* scrollIntoView tôn trọng scroll-padding: nó coi khung nhìn hẹp hơn 2rem mỗi bên nên khi
       kéo tab đang chọn vào, mép vẫn hở một phần tab kế bên — đủ để biết còn tab phía đó. */
    scroll-padding-inline: 2rem;
}

.nav-underline-wrapper::-webkit-scrollbar {
    display: none;
}

/* .nav mặc định là flex-wrap: wrap — để nguyên thì tab xuống dòng chứ không cuộn. */
.nav-underline-wrapper .nav-underline {
    flex-wrap: nowrap;
}

.nav-underline {
    --bs-nav-underline-gap: unset;
}

.nav-underline .nav-link {
    padding-left: 1rem;
    padding-right: 1rem;
    text-align: center;
    white-space: nowrap;
}

/*
 * Dải tab CÓ icon (icon trên, nhãn dưới) thì mỗi tab đã cao và rộng hơn — bóp padding để tab thứ tư,
 * thứ năm không bị đẩy khỏi màn hình 390px. Dải toàn chữ (Báo cáo) giữ 1rem, dáng không đổi.
 *
 * Dọc bằng nửa ngang (.375rem / .75rem) — cùng tỉ lệ với rail dọc ở desktop. Đừng cộng thêm khoảng
 * hở dưới: đáy tab đã có sẵn 2px border-bottom (đường ray của gạch chân) và ~1.4px half-leading của
 * dòng chữ, nên số đo dưới luôn dôi hơn số khai.
 *
 * Phải đặt SAU luật trên và ghi thẳng padding: luật kia dùng padding cứng chứ không qua
 * --bs-nav-link-padding-x, nên khai biến ở đây không ăn.
 */
.nav-underline-icon .nav-link {
    padding: .375rem .75rem;
}

/* Tab đang chọn giữ MÀU XANH như tab thường (mặc định Bootstrap tô đen). Không bó trong "> li > a":
   tab React dựng bằng <button class="nav-link"> (react-bootstrap Nav.Link as="button"). */
.nav-underline .nav-link.active {
    color: #206bc4;
}

/*
 * Bootstrap tô gạch chân khi :hover/:focus (bootstrap.css:3897). iOS giữ hai trạng thái này trên
 * phần tử vừa chạm cho tới khi chạm chỗ khác, nên tab cũ vẫn còn gạch sau khi đã chuyển tab.
 * Bỏ hẳn: chỉ tab đang chọn mới có gạch chân.
 */
.nav-underline .nav-link:not(.active):hover,
.nav-underline .nav-link:not(.active):focus {
    border-bottom-color: transparent;
}

/* .nav-wrapper là flex item trong #cartHome (flex-column), mà phần giỏ hàng bên dưới có h-100
   nên nó bị co chiều cao — khoá lại, không thì tab bị cắt mất nửa dưới. */
.nav-wrapper {
    flex-shrink: 0;
}

/* Chỉ dải tab cuộn, nút + nằm ngoài nên luôn thấy. */
.nav-wrapper .tab_scroller {
    overflow-x: auto;
    scrollbar-width: none;
    /* Cho co để nhường chỗ cho nút +; mặc định min-width: auto sẽ chặn không cho co. */
    min-width: 0;
    /* Hở một phần tab kế bên khi scrollIntoView kéo tab đang chọn vào — xem .nav-underline-wrapper. */
    scroll-padding-inline: 2rem;
}

.nav-wrapper .tab_scroller::-webkit-scrollbar {
    display: none;
}

.nav-wrapper .order_tab {
    flex-wrap: nowrap;
}

.order_tab > .nav-item {
    flex: 0 0 auto;
}

.nav-wrapper .add_tab {
    flex-shrink: 0;
    margin-left: .25rem;
    /* Nút này nằm ngoài .nav và .nav-pills nên không thừa hưởng được padding lẫn bo góc của tab —
       khai lại đúng hai biến Bootstrap dùng, để đổi theme thì vẫn khớp. */
    --bs-nav-link-padding-x: 1rem;
    --bs-nav-link-padding-y: .5rem;
    border-radius: var(--bs-border-radius);
    background-color: #e9ecef;
    color: #6c757d;
}

@media (hover: hover) {
    .nav-wrapper .add_tab:hover {
        color: #333;
    }
}

.order_tab > .nav-item > .nav-link {
    background-color: #e9ecef;
}

.order_tab > .nav-item > .nav-link.active {
    background-color: #0d6efd;
}

.order_tab .nav-link.active span {
    color: #fff;
}

.order_tab > .nav-item > .nav-link {
    width: 140px;
    display: flex;
    justify-content: space-between;
}

.order_tab > .nav-item:not(:first-child) {
    margin-left: .25rem;
}

.nav .open > a, .nav .open > a:focus, .nav .open > a:hover {
    background-color: unset;
    border-color: transparent;
}

.filter .col-auto {
    width: 250px;
}

.product-channel-select {
    min-width: 220px;
}

.product-channel-select .ts-wrapper {
    width: 100%;
}

.table-bordered > thead > tr > td, .table-bordered > thead > tr > th {
    border-bottom-width: 1px;
}

.table > thead > tr > th, .table > tbody > tr > th {
    font-weight: 600;
}

.table > thead:first-child > tr:first-child > th, .table > tbody:first-child > tr:first-child > td {
    border-top: 0;
}

.table > tfoot > tr > td {
    border-top: none;
}

.table > :not(caption) > * > * {
    border-bottom-width: 0px;
    border-top-width: 1px;
}

/* .table-responsive có overflow nên cắt mất bo góc của card bọc ngoài — bo lại đúng bằng bán kính của card. */
.card > .table-responsive:only-child {
    border-radius: inherit;
}

.search_result, .customer_result {
    width: 100%;
    margin-top: .125rem;
}

.search_result .search_content, .customer_result .search_content {
    overflow-y: scroll;
    max-height: 350px;
}

span.phone_result, span.description_result {
    font-weight: 600;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

.pagination-wrapper {
    margin-bottom: 1rem;
}

.pagination-wrapper .dropdown-menu {
    min-width: 100%;
    text-align: center;
}

.pagination {
    margin-bottom: 0;
    gap: .25rem;
}

.pagination > li > a, .pagination > li > span {
    border-color: transparent;
    border-radius: .375rem;
}

.not_featured i {
    color: #ccc;
}

.note_list ul li a {
    opacity: 0;
}

.note_list ul li:hover a {
    opacity: 1;
}

.list-group-item {
    word-wrap: break-word;
}

.list-group.nested-sortable > .list-group-item > .list-group > .list-group-item:first-child {
    margin-top: .5rem;
}

.shipment {
    background-color: #eee;
}

.express {
    background-color: #f2dede;
}

.gotoshop {
    background-color: #faebcc;
}

.pending {
    background-color: #d9edf7;
}

.bg_purple {
    background-color: #ebe0ff !important;
}

.bg_pink {
    background-color: #ffe0e6;
}

.quote {
    border-left: 2px solid #3085d6;
}

tr.canceled .text, tr.canceled .link a, tr.canceled .amount span, li.canceled, li.canceled .order_product_wrap a {
    text-decoration: line-through;
    color: #a94442;
}

.card.canceled .text, .card.canceled .link a {
    text-decoration: line-through;
    color: #a94442;
}

.panel_customer li span.action, .panel_order li span.action {
    opacity: 0;
}

.panel_customer li:hover span.action, .panel_order li:hover span.action {
    opacity: 100;
}

.main_data .dropdown > a:hover {
    color: unset;
}

/* ClipboardJS gắn vào .clipboard toàn cục (main.js) nên con trỏ cũng phải toàn cục,
   không bó theo panel — nếu không thì các chỗ ngoài panel bấm được mà nhìn như chữ thường. */
.clipboard {
    cursor: pointer;
}

.main_data hr {
    margin-top: .5rem;
    margin-bottom: .5rem;
}

/* Nhạt hơn #eee của .shipment một nấc để dòng đã giao còn nổi, vẫn đủ đậm để card trắng tách khỏi nền.
   .modal-panel là bản React (Components/Dialog contentClassName) — modal đơn / khách dùng chung nền này. */
#mainModal .modal-content, #customerModal .modal-content, .modal-content.modal-panel {
    background-color: #f2f2f2;
}

/* Dòng "… đang xem đơn này" ở header modal đơn: Bootstrap cho .btn-close sẵn margin-left auto, nên khi có
   thêm một phần tử cũng ms-auto thì khoảng trống bị CHIA ĐÔI và dòng chữ rơi ra giữa header.
   Bỏ hẳn auto của nút đóng: bản PHP gói .node và nút đóng trong CÙNG một div co theo nội dung nên auto ở đó
   bằng 0, khoảng cách giữa hai cái chỉ do me-3 của .node — thêm margin nữa là rộng hơn bản gốc. */
.modal-header .node ~ .btn-close {
    margin-left: 0;
}

.order_product_item {
    display: flex;
    flex-wrap: wrap;
}

.order_product_item .item {
    width: 50%;
    padding: 6px 0;
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid #eee;
}

.order_product_item .item:nth-child(even) {
    border-left: 1px solid #eee;
    padding-left: 6px;
}

.order_product_item .item:nth-child(odd) {
    padding-right: 6px;
}

.order_product_item .line {
    width: 100%;
    padding: 6px 0;
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid #eee;
}

/*
 * Mỗi sản phẩm một DÒNG, kèm tên sản phẩm chữ nhỏ bên dưới mã. Bật bằng option `order_item_stacked`
 * ở Thiết lập chung, đọc qua Teamapp (xem resources/js/lib/legacy.ts).
 *
 * Y hệt bộ luật mà màn hẹp vẫn dùng — ở đó xếp dọc là bắt buộc, còn từ 768px trở lên thì tuỳ option.
 * Vì vậy dưới 768px class này thừa nhưng vô hại: hai bên khai cùng giá trị.
 */
.order_product_item.stacked .item {
    width: 100%;
    padding-right: unset;
}

.order_product_item.stacked .item:nth-child(2n) {
    border-left: unset;
    padding-left: 0;
}

/*
 * Tên CẮT MỘT DÒNG. Đơn 6 dòng mà tên nào cũng xuống hai ba dòng thì khối tổng tiền và hàng trạng thái
 * bị đẩy khỏi tầm nhìn — chủ repo chốt cắt thay vì cho xuống dòng.
 *
 * min-width: 0 ở cột trái, nếu không text-overflow không bao giờ ăn: .item là flex, mà flex item mặc
 * định không co dưới min-content của chữ bên trong nên chẳng có gì để cắt.
 */
.order_product_item.stacked .item > div:first-child {
    min-width: 0;
}

.order_product_item.stacked .product_name {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.order_product_wrap .bottom {
    display: flex;
    justify-content: space-between;
    /* Hàng chật thì cả Ô xuống dòng, chứ không cắt đôi chữ trong nhãn — xem .item .left bên dưới.
       Thiếu wrap thì các ô nowrap không co được nữa và cả hàng lòi ra ngoài khung. */
    flex-wrap: wrap;
    gap: .25rem 1rem;
}

/* Nhãn không được ngắt giữa chừng, kiểu "Chênh lệch" rớt thành "Chênh" / "lệch". */
.order_product_wrap .bottom .item .left {
    white-space: nowrap;
}

.mini_pagination {
    display: flex;
    justify-content: flex-end;
    list-style-type: none;
    padding: 8px;
    margin: 0;
}

.mini_pagination li a {
    padding: 8px;
}

.mini_pagination li.active a {
    color: #ccc;
}

.customer_orders .table .order_product {
    text-overflow: ellipsis;
    overflow: hidden;
    max-width: 250px;
    white-space: nowrap;
}

.search_section .input-group-text {
    background-color: #fff;
}

.search_section .input-group > .form-control {
    border-left: none;
    padding-left: 0;
}

.search_group .input-group > .form-control {
    border-left: none;
    border-right: none;
    padding-left: 0;
    padding-right: 0;
}

.search_group .search_section {
    max-width: 400px;
    flex: 1;
}

.search_group .search_section .dropdown-menu {
    padding: 0.75rem;
}

.search_group .search_section .dropdown-menu .form-control {
    border-top: none;
    border-left: none;
    border-right: none;
    padding-left: 0;
    padding-right: 0;
    border-radius: 0;
}

.search_group .search_section .dropdown-menu div:not(:last-child) {
    margin-bottom: 0.75rem;
}

.search_section .input-group > .form-control:focus {
    border-color: #ced4da;
}

.search_group .search_section .input-group > .input-group-text:last-child {
    padding: 2px;
}

.search_group .search_section .input-group > .input-group-text:last-child > span {
    padding: .25rem .375rem;
    border-radius: 50%;
}

.search_group .search_section .input-group > .input-group-text:last-child > span:hover {
    background-color: #dee2e6;
    cursor: pointer;
}

.main_data .notes, .customer_orders {
    position: relative;
}

.notes .pinned {
    background-color: #f2dede;
    border-color: #ebccd1;
}

/* RowMenu dựng .dropdown bằng <div>, bản PHP cũ là <span> — nhắm cả hai. */
.notes td .dropdown, .main_data .payment_section li span.dropdown {
    opacity: 0;
}

.notes td:hover .dropdown, .main_data .payment_section li:hover span.dropdown {
    opacity: 1;
}

.notes .table td {
    padding-left: 1rem;
    padding-right: 1rem;
}

.overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1000;
    background-color: rgba(255, 255, 255, 0.5);
}

/*
 * htmx gắn class này lên phần tử trỏ bởi hx-indicator, hoặc lên chính phần tử gây request
 * khi không khai. Chặn thao tác và phủ mờ trong lúc gửi, thay cho div.overlay bật tắt tay.
 *
 * Mặc định gắn indicator vào khối NẰM TRONG modal (form, card): trỏ vào .modal-content là
 * phủ cả tấm modal, kể cả header và nút Đóng. Ngoại lệ hợp lệ là khi chính .modal-content
 * bị swap — products/product_info đổi tab thay sạch nội dung modal, phủ hết mới đúng.
 *
 * (Cảnh báo cũ "dùng opacity thì nhìn xuyên xuống trang nền" không còn áp dụng: lớp phủ là
 * pseudo-element đục, không phải opacity trên chính khối.)
 */
form.htmx-request,
.card.htmx-request,
.modal-content.htmx-request,
.is-loading {
    position: relative;
    pointer-events: none;
}

/* Lớp phủ riêng chứ không đặt opacity lên chính khối — opacity làm nó trong suốt thật. */
form.htmx-request::after,
.card.htmx-request::after,
.modal-content.htmx-request::after,
.is-loading::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1000;
    background-color: rgba(255, 255, 255, .5);
    /* border-radius không kế thừa: thiếu dòng này lớp phủ vẽ vuông góc đè lên góc bo của .card. */
    border-radius: inherit;
}

.overlay > div {
    position: relative;
    top: 50%;
    transform: translateY(-50%);
}

#datetimepicker_timer {
    word-break: normal;
}

.action > a:not(.color) {
    color: #ccc;
}

.action > a:not(.color):hover {
    color: #000;
}

.birth_month .picker-switch {
    display: none !important;
}

.birth_month .prev {
    display: none !important;
}

.birth_month .next {
    display: none !important;
}

.notes_list li {
    background-color: #eee;
}

.more_wrap {
    border-top: 1px solid #ddd;
    padding: 8px;
}

.item_thumb {
    width: 42px;
    height: 42px;
    position: relative;
}

.item_title {
    padding-left: 8px;
}

/* Tên sản phẩm ở ô Sản phẩm trang Kênh: một dòng, dài quá thì "...", tên đầy đủ nằm ở tooltip.
   Cắt bằng CSS theo bề rộng thật thay cho truncate(25) ở server — số ký tự cố định thì mobile
   vẫn xuống 4 dòng mà PC lại cụt. Phải có max-width: ô bảng co giãn theo nội dung, nowrap
   không kèm giới hạn thì cột nở theo tên chứ không cắt. */
.product_name_line {
    /* Đặt trên chính span mang tooltip, không phải div bọc ngoài: span inline thì hộp của nó dài
       bằng cả tên đầy đủ dù bị div che bớt, tippy căn mũi tên giữa hộp đó nên lệch hẳn sang phải.
       inline-block để hộp chỉ rộng bằng phần chữ thấy được (tên ngắn thì ngắn theo). */
    display: inline-block;
    /* Co giãn theo màn hình: không bám được thẳng theo bảng vì ở layout auto, chính độ dài tên
       quyết định cột rộng bao nhiêu. Trang Kênh chỉ có thanh menu trái cố định nên bề rộng bảng
       gần như tỉ lệ với màn hình. Mobile giữ sàn 10rem, PC rộng thì lấy thêm chỗ trống,
       trần 28rem cho khỏi kéo cột quá dài. */
    max-width: clamp(10rem, 22vw, 28rem);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* inline-block có overflow khác visible lấy mép dưới làm baseline, thiếu dòng này thì dư
       vài px bên dưới dòng tên. */
    vertical-align: top;
}

/* Bảng tổng hợp ở channels/index.php có một cột cho mỗi kênh, cột Sản phẩm phải hẹp nên giữ cố
   định 10rem thay cho clamp theo màn hình ở trên (vốn dành cho trang từng kênh, ít cột). */
.product_name_line.product_name_compact {
    max-width: 10rem;
}

.item_thumb .attachment {
    height: 100%;
}

.item_thumb .attachment img {
    object-fit: contain;
    width: 100%;
    height: 100%;
}

.item_thumb .attachment svg {
    color: #cecece;
    fill: currentColor;
    width: 16px;
    height: 16px;
    position: absolute;
    margin: auto;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

.images_wrap {
    list-style-type: none;
    padding-left: 0;
    overflow: hidden;
    margin-bottom: 0;
}

.images_wrap li {
    float: left;
    width: 25%;
    padding: .25rem;
}

.product_list {
    margin: 0 -8px;
    overflow: hidden;
    display: flex;
    flex-wrap: wrap;
}

.product_list .item {
    width: 12.5%;
    padding: .5rem;
    margin-bottom: .5rem;
}

.product_cart {
    position: relative;
    margin-top: .5rem;
}

.product_cart:not(.store_cart) .code {
    width: 60%;
}

.cart_footer .item {
    padding: .25rem .5rem;
    display: flex;
    justify-content: space-between;
}

.cart_footer .item .item_label {
    width: 80%;
    display: inline-block;
    text-align: right;
}

.panel_cart .alert {
    margin-top: 8px;
    margin-bottom: 0;
}

.panel_cart .product_cart .item .title, #cartHome .product_cart .item .title, .cart_input .product_cart .item .title {
    width: 60%;
}

.product_cart .item .quantity {
    width: 75px;
}

/*
 * Sàn chiều rộng cho bảng giỏ hàng (SalesCart, ReturnCart).
 *
 * Chỉ bọc .table-responsive là chưa đủ: bảng vẫn cố co vừa khung cuộn, nên trong modal hẹp
 * cột mã/tên bị bóp tới mức xuống dòng từng chữ. Có sàn thì bảng tràn ra và khung cuộn mới
 * thực sự hoạt động, các cột giữ được chiều rộng đọc được.
 */
.cart-table {
    min-width: 30rem;
}

.mobile .card-body, .card.user_salary .card-body {
    padding: .5rem .75rem;
}

.mobile hr {
    margin-top: .5rem;
    margin-bottom: .5rem;
}

.suggested_price, .suggested_fee {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    grid-gap: .5rem;
}

.result_count {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
}

.description p {
    margin-bottom: 0;
}

/* attachment */

.attachment {
    border: 1px solid #ddd;
    background-color: #f5f5f5;
}

.attachment.marked {
    border-color: rgb(var(--bs-danger-rgb));
}

.attachment:hover .bottom {
    opacity: 1;
}

.attachment_preview {
    position: relative;
    padding-bottom: 100%;
}

.thumb {
    overflow: hidden;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

.centered {
    width: 100%;
    height: 100%;
    transform: translate(50%, 50%);
}

.attachment .thumb img, .attachment .thumb svg {
    max-width: 100%;
    max-height: 100%;
    transform: translate(-50%, -50%);
}

.attachment .thumb svg {
    width: 48px;
    height: 48px;
}

.attachment .bottom {
    position: absolute;
    bottom: 0;
    width: 100%;
    text-align: center;
    background: none repeat scroll 0 0 rgba(0, 0, 0, 0.5);
    opacity: 0;
    padding: 3px;
}

.attachment .bottom a {
    color: #fff;
}

/*
 * Hộp thoại Hình ảnh sản phẩm: thao tác nằm dưới khung ảnh cho luôn thấy, thay lớp phủ .bottom chỉ hiện khi
 * trỏ chuột. flex-wrap để badge "Chưa duyệt" xuống dòng khi ô ảnh hẹp thay vì đè lên hai icon.
 */
.photo_actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .25rem;
    padding-top: .25rem;
}

/*
 * Dãy ảnh trong hộp thoại này thưa hơn mặc định .25rem: dưới mỗi ô còn checkbox, badge và hai icon, sát nhau
 * thì không thấy hàng thao tác nào thuộc ô nào. Khung chứa biến thể lấy cùng số cho đều mắt.
 */
.photo-dialog ul.d-grid, .photo-dialog .d-grid.variant {
    gap: .5rem;
}

/*
 * Ba cỡ ảnh: 8 ô một hàng trên máy tính, 4 ô từ 991.98px trở xuống, 2 ô trên điện thoại (khai trong khối
 * max-width 575px bên dưới). 991.98px là mốc Bootstrap hạ .modal-xl xuống 500px, chật nhất trong các cỡ.
 *
 * Đặt TRƯỚC .variant ul.d-grid để dãy ảnh trong một biến thể vẫn một cột — hai selector cùng độ đặc hiệu,
 * cái khai sau thắng.
 */
@media (max-width: 991.98px) {
    .photo-dialog ul.d-grid, .photo-dialog .d-grid.variant {
        grid-template-columns: repeat(4, 1fr);
    }
}

/* Checkbox chọn và badge trạng thái là một cụm, tách khỏi cụm đánh dấu/xóa ở mép phải. */
.photo_actions .state {
    display: flex;
    align-items: center;
    gap: .25rem;
}

.photo_actions .action {
    display: flex;
    gap: .5rem;
}

/*
 * Cùng quy ước span.action của .panel_customer và .notif-panel: mờ khi thường, sáng khi trỏ vào ô ảnh.
 * Khác một điểm — hai chỗ kia dùng opacity 0 (ẩn hẳn), ở đây để .65 cho còn thấy là có thao tác.
 */
.photo_actions .action a {
    opacity: .65;
    transition: opacity .15s ease-in-out;
}

li:hover > .photo_actions .action a {
    opacity: 1;
}

/* end attachment */

ul.d-grid {
    grid-template-columns: repeat(8, 1fr);
    gap: .25rem;
    list-style-type: none;
    padding-left: 0;
}

.d-grid.variant {
    grid-template-columns: repeat(8, 1fr);
    gap: .25rem;
}

.variant ul.d-grid {
    grid-template-columns: repeat(1, 1fr);
}

.collection_product_list .product_item {
    padding: .5rem;
}

.collection_product_list .product_item:hover {
    background-color: #f5f5f5;
}

.collection_product_select {
    position: relative;
}

.collection_product_search .product_item {
    padding: 8px;
    border-bottom: 1px solid #ddd;
}

.collection_product_search .product_item:hover {
    background-color: #f5f5f5;
}

.nav.ln_tab {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    grid-gap: .125rem;
    /* Ô rộng bằng nhau nên chữ số canh giữa mới cân (mặc định .nav-link canh trái). */
    text-align: center;
}

.nav.ln_tab .nav-link:not(.active) {
    color: #383d41;
    background-color: #e2e3e5;
}

.create_tab {
    display: flex;
    flex-wrap: wrap;
}

.create_tab > span {
    width: unset;
    margin-bottom: .25rem;
}

.create_tab a {
    margin-bottom: .25rem;
}

.create_tab > span:not(:first-child), .create_tab a:not(:first-child) {
    margin-left: .25rem;
}

#channel_group_select a.active {
    font-weight: 600;
    color: #a94442;
}

.channel_group img {
    max-width: 1.5rem;
    border-radius: .25rem;
    border: 1px solid #dee2e6;
}

.channel_group > .nav-link.active {
    background-color: rgba(32, 107, 196, .15);
    color: #206bc4;
}

.section_tab {
    display: flex;
    flex-direction: column;
    padding-left: calc(1rem + 1.5rem + .5rem);
    padding-top: .25rem;
    padding-bottom: .25rem;
}

.section_tab a {
    padding: .25rem 0;
    display: block;
}

.section_tab a.active {
    font-weight: 600;
}

.list_open {
    position: fixed;
    display: flex;
    flex-direction: row-reverse;
    bottom: 15px;
    right: 15px;
}

.header_content {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

#select_tab .table .checkbox {
    margin-top: 0;
    margin-bottom: 0;
}

.user_salary .salary_main {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
}

.user_salary .salary_sub .item {
    display: flex;
    margin-bottom: 15px;
}

.user_salary .salary_sub .item:last-child {
    margin-bottom: 0;
}

.user_salary .salary_sub .item label {
    width: 10%;
}

.avatar-group {
    display: flex;
    flex-wrap: wrap;
}

.avatar {
    width: 2.25rem;
    height: 2.25rem;
    object-fit: cover;
}

.avatar.small {
    width: 1.25rem;
    height: 1.25rem;
}

.avatar .avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.avatar .avatar-text {
    width: 100%;
    height: 100%;
    background-color: #e9ecef;
    display: flex;
    justify-content: center;
    align-items: center;
}

.avatar-group.avatar-group-overlapped .avatar {
    margin-right: -.75rem;
}

.avatar-group .avatar .avatar-img, .avatar-group .avatar .avatar-text {
    border: 2px solid #fff;
}

.profile_photo {
    width: 96px;
    height: 96px;
    background-color: #e9ecef;
    border-radius: 50%;
    cursor: pointer;
}

.profile_photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.profile_photo span {
    font-size: 1.75rem;
    color: #495057;
    font-weight: 300;
}

.file {
    position: relative;
    padding-top: 100%;
}

.file-preview {
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: #f5f5f5;
    color: unset;
}

.file-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.file-preview i {
    font-size: 1.25rem;
}

.file-content {
    position: absolute;
    bottom: 0;
    width: 100%;
    background: rgba(0, 0, 0, 0.5);
    text-align: center;
    display: none;
}

.file-content a {
    color: #fff;
}

.file:hover .file-content {
    display: block;
}

.static .dropzone {
    border: 1px dashed #ccc;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    color: var(--bs-tertiary-color);
    min-height: unset;
    border-radius: unset;
}

.static .dropzone:hover {
    border-color: var(--bs-body-color-rgb);
    color: var(--bs-body-color-rgb);
}

/* Ô thả tệp đứng riêng (hộp thoại tải lên ở trang Media) — dropzone.css mặc định là
   viền đen đặc, đổi cho khớp kiểu gạch đứt của .static .dropzone. */
.upload-dropzone {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 120px;
    padding: 1rem;
    border: 1px dashed var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    color: var(--bs-tertiary-color);
    cursor: pointer;
}

.upload-dropzone:hover {
    border-color: var(--bs-secondary-color);
}

.channel_logo {
    vertical-align: sub;
    height: 16px;
    margin-right: .25rem;
}

.toastify {
    padding: .5rem;
    border-radius: .25rem;
}

.min-w-0 {
    min-width: 0px;
}

.sortable-list a.sortable_handle, .nested-sortable a.sortable_handle {
    cursor: move;
}

.sorting-indicator {
    display: none;
}

.sorting-indicator::before {
    content: "▲";
    font-size: .625rem;
    color: #3c434a;
}

th.sorted .sorting-indicator {
    display: unset;
}

th.sorted.desc .sorting-indicator::before {
    content: "▼";
}

span.readonly {
    background-color: #eee;
}

ul.tag {
    list-style-type: none;
    padding-left: 0;
    margin-bottom: 0;
    display: flex;
    flex-wrap: wrap;
}

ul.tag li {
    margin-right: .25rem;
}

ul.tag li .label {
    display: block;
    font-size: unset;
    line-height: unset;
    font-weight: unset;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.all_tags a {
    margin-bottom: 4px;
    display: inline-block;
}

/* Mặc định SweetAlert đứng ở 1060, thấp hơn hộp thoại React mở CHỒNG (Components/Dialog stacked tính từ 1075
   trở lên) — hộp xác nhận mở từ trong những hộp thoại đó sẽ nằm dưới, bấm không tới. */
/* !important vì file sweetalert2.min.css nạp SAU main.css, cùng mức đặc hiệu thì luật của nó thắng. */
div:where(.swal2-container) {
    z-index: 2000 !important;
}

div:where(.swal2-container) div:where(.swal2-popup),
div:where(.swal2-container) div:where(.swal2-html-container),
div:where(.swal2-container) .swal2-radio label, div:where(.swal2-container) .swal2-checkbox label,
div:where(.swal2-container) .swal2-file {
    font-size: unset !important;
}

div:where(.swal2-container) .swal2-radio, div:where(.swal2-container) .swal2-checkbox {
    flex-wrap: wrap;
}

div[data-notify="container"] {
    max-width: 150px;
    padding: .5rem;
    font-size: 85%;
}

/*
 * Ô TomSelect chọn một mục: nhãn dài thì cắt bằng "..." thay vì xuống dòng, cho ô cao đúng một dòng
 * như mọi ô khác trong form (vd. phụ kiện đi kèm ở hộp thoại Sản phẩm — nhãn là "MÃ — Tên đầy đủ").
 *
 * Đặt class này lên CỘT chứa ô (vd. <div class="col truncate">), không phải lên chính ô: min-width
 * phải rơi đúng vào flex item của hàng .row thì nó mới co được.
 *
 * min-width: 0 xuất hiện ba lần và lần nào cũng cần, vì flex item mặc định không co nhỏ hơn nội dung
 * của nó, mà ở đây có hai tầng flex lồng nhau:
 * - cột trong .row: không co thì cả hàng rớt xuống dòng (ô số lượng và nút xóa bị đẩy xuống);
 * - .item trong .ts-control: không co thì chữ không bao giờ tràn nên ellipsis không hiện;
 * - ô gõ cũng trong .ts-control: xem chú thích riêng ở dưới.
 *
 * Bản cũ của rule này đặt display: inline, thứ khiến text-overflow mất tác dụng hoàn toàn (ellipsis
 * cần hộp block). Lúc đó chưa chỗ nào dùng tới nên không ai thấy.
 */
.truncate {
    min-width: 0;
}

.truncate .ts-control {
    flex-wrap: nowrap;
}

.truncate .ts-control > .item {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * Ô gõ tìm nằm cạnh nhãn và thư viện dành cứng cho nó `min-width: 7rem` — tức 112px bị giữ chỗ dù
 * chưa gõ gì, nên nhãn bị cắt sớm hơn hẳn bề rộng thật của ô.
 *
 * Trước đây không thấy vì .ts-control còn flex-wrap: wrap, ô gõ rớt xuống dòng dưới — chính là cái
 * làm ô cao hai dòng. Nay đã nowrap thì phải cho nó co về 0: chưa gõ thì nhãn chiếm hết, gõ tới đâu
 * ô nở tới đó (flex-basis auto, nở theo nội dung) và nhãn lùi lại.
 */
.truncate .ts-control > input {
    min-width: 0;
}

.thumb_link .thumb {
    position: relative;
    width: 2.5rem;
    height: 2.5rem;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-right: .25rem;
    background-size: cover;
    background-position: 50%;
    background-repeat: no-repeat;
    border: 1px solid #ddd;
}

.post_list .card + .card {
    margin-top: .5rem;
}

.badge__red {
    background-color: rgb(254, 215, 215) !important;
    color: rgb(155, 44, 44);
}

.badge__gray {
    background-color: rgb(237, 242, 247) !important;
    color: rgb(45, 55, 72);
}

.badge__teal {
    background-color: rgb(178, 245, 234) !important;
    color: rgb(40, 94, 97);
}

.badge__blue {
    background-color: rgb(190, 227, 248) !important;
    color: rgb(44, 82, 130);
}

.badge__yellow {
    background-color: rgb(254, 252, 191) !important;
    color: rgb(151, 90, 22);
}

.badge__green {
    background-color: rgb(198, 246, 213) !important;
    color: rgb(39, 103, 73);
}

.badge__orange {
    background-color: rgb(254, 235, 200) !important;
    color: rgb(156, 66, 33);
}

.badge__purple {
    background-color: rgb(233, 216, 253) !important;
    color: rgb(85, 60, 154);
}

.badge__pink {
    background-color: rgb(254, 215, 226) !important;
    color: rgb(151, 38, 109);
}

.palette input[type="radio"] {
    display: none;
}

.palette input[type="radio"]:checked + label span {
    border: 1px solid #333;
}

.palette label {
    display: inline-block;
    width: 2.5rem;
    height: 2.5rem;
    margin-right: .5rem;
    margin-bottom: .5rem;
    cursor: pointer;
}

.palette label span {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
}

.cs_readmore-btn {
    background: unset;
    border: unset;
    color: #206bc4;
    padding: unset;
}


@media (max-width: 575px) {
    .modal-dialog {
        --bs-modal-margin: .25rem;
    }

    ul.d-grid {
        grid-template-columns: repeat(4, 1fr);
    }

    .d-grid.variant {
        grid-template-columns: repeat(4, 1fr);
    }

    /*
     * Hộp thoại Hình ảnh sản phẩm riêng thì 2 ô một hàng: dưới ô ảnh còn checkbox, badge và hai icon,
     * 4 ô một hàng như các chỗ khác thì chật. Khai sau hai rule trên để thắng cùng độ đặc hiệu.
     */
    .photo-dialog ul.d-grid, .photo-dialog .d-grid.variant {
        grid-template-columns: repeat(2, 1fr);
    }

    /* Mỗi biến thể vẫn chỉ một ảnh, không bị rule trên kéo thành 2 cột. */
    .photo-dialog .variant ul.d-grid {
        grid-template-columns: repeat(1, 1fr);
    }

    .product_list .item {
        width: 50%;
    }
}

@media (max-width: 767px) {
    .navbar .container-fluid {
        padding-left: 0;
        padding-right: 0;
    }

    .navbar-main {
        flex-direction: row-reverse;
        justify-content: flex-end;
    }

    .navbar-toggler {
        --bs-navbar-toggler-focus-width: 0;
        /* Icon rộng 1.5em theo cỡ chữ này: mặc định 1.25rem ra ~30px, trông to so với chuông và
           avatar bên phải; 1rem còn ~24px. Padding của nút tính bằng rem nên không co theo. */
        --bs-navbar-toggler-font-size: 1rem;
        border: none;
    }

    /* Chuông và đồng hồ to hơn chữ 14px của body, chỉ ở mobile: trên PC chúng đứng cạnh icon
       các mục nav nên giữ cùng cỡ. Nhắm riêng thẻ <i>, không đặt cỡ chữ cho cả .nav-link, để
       badge đếm số chưa đọc nằm cùng thẻ a giữ nguyên cỡ. */
    .navbar-right li.ajax-dropdown > .nav-link > i {
        font-size: 1rem;
    }

    .navbar-right > li {
        position: unset;
    }

    .navbar-right {
        flex-direction: row;
    }

    .navbar-dark .navbar-right .nav-link {
        font-size: .875rem;
        position: relative;
    }

    .navbar-nav > li > .dropdown-menu {
        position: absolute;
        margin-right: var(--bs-dropdown-spacer);
    }

    .nav-link span.username {
        display: none;
    }

    .heading_search, .search_group {
        flex-direction: column;
    }

    .heading_search #datefilter, .search_group .datefilter {
        width: unset;
    }

    .search_section #search_form, .search_group .search_section, .heading .search_section {
        margin-right: 0;
    }

    .form-group .total_hour {
        margin-bottom: 15px;
    }

    #profile_form .col-sm-4:not(:last-child) {
        margin-bottom: 15px;
    }

    .user_salary .salary_main .item {
        display: flex;
        width: 100%;
        justify-content: space-between;
    }

    .user_salary .salary_sub .item {
        display: block;
    }

    .user_salary .salary_sub .item label {
        width: unset;
    }

    .card.user_salary hr {
        margin-top: .5rem;
        margin-bottom: .5rem;
    }

    /*
     * Dùng chung bộ luật với .stacked (xem dưới .order_product_item): màn hẹp thì LUÔN xếp dọc, không
     * cần option. Gộp selector chứ không chép lại để hai chế độ không trôi khỏi nhau.
     */
    .main_data .order_product_item .item {
        width: 100%;
        padding-right: unset;
    }

    .main_data .order_product_item .item:nth-child(2n) {
        border-left: unset;
        padding-left: 0;
    }

    /*
     * Xếp dọc thì mỗi .item là một DÒNG, nên khoảng cách phải đặt ở đây mới đều:
     * gap-1 của khung ngoài chỉ cách được các khối .bottom với nhau. Cùng .25rem
     * để dòng trong khối và dòng giữa hai khối nhìn như một danh sách liền mạch.
     */
    .main_data .order_product_wrap .bottom {
        flex-direction: column;
        gap: .25rem;
    }

    .main_data .order_product_wrap .bottom .item {
        margin-left: unset;
        display: flex;
        justify-content: space-between;
    }
}

@media (max-width: 991px) {
    .main_wrapper {
        display: inherit;
    }

    .wrapper .main_wrapper {
        height: unset;
    }

    .sidebar {
        width: 100%;
    }
}

@media (min-width: 768px) {
    /* Bề rộng cơ sở của .navbar-collapse là trọn chiều dài menu (các li không co được), nên
       flexbox bóp brand lại và cắt bằng ellipsis. Khoá brand để phần menu gánh — nó co được
       nhờ min-width: 0 và overflow: hidden. Chỉ từ md trở lên vì dưới đó .navbar-collapse ẩn,
       không ai tranh chỗ, và vẫn cần ellipsis phòng tên tổ chức quá dài. */
    .navbar-brand {
        flex-shrink: 0;
    }

    .navbar-right .dropdown-item-text.username {
        display: none;
    }

    .master_product td.select {
        width: 40px;
    }

    .master_product td.ordinal {
        width: 55px;
    }

    .master_product td.code {
        width: 200px;
    }

    .master_product td.cost {
        width: 96px;
    }

    .master_product td.price {
        width: 110px;
    }

    .master_product td.stock {
        width: 100px;
    }

    .master_product td.action {
        width: 75px;
    }

    .modal-sm {
        max-width: 300px;
    }
}

@media (min-width: 992px) {
    /*
     * ĐÃ BỎ `body { overflow: hidden }` cùng cả hai khuôn cuộn cũ (.scroll_section, .scroll_page,
     * .scroll_layout, .settings_scroll) ngày 12/10/2026 — xem khuôn .scroll_body ở cuối khối này.
     *
     * Lối cũ là: tài liệu không tự cuộn, mọi hàng cao đúng calc(100vh - navbar) và việc cuộn đẩy xuống
     * các div bên trong. Lối đó đẻ ra hai khuôn song song chỉ vì phải đưa thanh cuộn ra sát mép màn
     * hình, kèm margin âm bù padding container và cả cụm chừa đáy cho khung cuộn.
     *
     * Từng thử rồi hoàn nguyên 8/10/2026 vì cảm giác cả trang trôi; chủ repo mở lại 12/10 sau khi xem
     * hai trang mẫu và chốt áp cho cả app. Hai mốc này ghi lại để đừng ai lật lại bằng trí nhớ.
     */

    .list_layout {
        display: flex;
    }


    /*
     * ===== KHUÔN CHUNG CỦA MỌI TRANG DANH SÁCH: BODY làm vùng cuộn =====
     *
     * Trước 12/10/2026 có hai khuôn song song (.scroll_page và .scroll_section) và chúng tồn tại vì đúng
     * MỘT lý do: đưa thanh cuộn ra sát mép màn hình. .scroll_section cuộn cột phải nên phải bù margin
     * âm, mà cách đó bất lực với .container-xl vì nửa phần canh giữa CSS không biết trước — nên trang
     * hẹp buộc phải dùng .scroll_page. Để body cuộn thì thanh cuộn là thanh cuộn cửa sổ, sát mép sẵn,
     * bề rộng container thôi liên quan, và hai khuôn gộp về một.
     *
     * Nhờ vậy đã xoá được: chuỗi height calc(100vh - navbar), margin âm bù padding container, cụm chừa
     * đáy cho khung cuộn (padding-bottom + :last-child margin-bottom: 0), .main_panel overflow riêng, và
     * .side-sheet cuộn riêng ở mọi trang. Cột lọc không còn khối overflow nên dropdown ô lọc thôi bị cắt.
     *
     * Đánh đổi đã biết và đã chấp nhận: heading trang trôi mất khi cuộn sâu (thanh tìm sticky đỡ được
     * phần nào), bảng dài thì phải cuộn về đầu mới thấy lại cột lọc.
     *
     * Class .scroll_body gắn ở HÀNG hai cột của trang. Giữ class thay vì khai trần cho mọi .main_wrapper
     * vì còn mấy trang không phải danh sách (POS, chat) không theo khuôn này.
     */

    /*
     * Hàng hai cột cao TỰ NHIÊN. Vẫn phải khai dù .wrapper .main_wrapper đã bỏ height, vì padding-top
     * của nó phải về 0: lề trên dời vào từng cột, do sticky neo theo mép padding mà padding thì sticky
     * không che được — để nguyên là nội dung luồn qua đúng khe đó.
     *
     * Class gắn THẲNG LÊN HÀNG, chỉ một cách duy nhất. Trước 14/10/2026 còn cách thứ hai — một thẻ div
     * bọc NGOÀI container chỉ để mang class — là di sản của .scroll_page, hồi đó khung cuộn buộc phải
     * nằm ngoài container thì thanh cuộn mới ra tới mép màn hình. Nay body cuộn nên thẻ đó vô nghĩa, đã
     * gỡ ở 34 trang. Đừng dựng lại: thêm một tầng div là thêm một chỗ để luật bám nhầm độ sâu.
     */
    .wrapper .main_wrapper.scroll_body {
        padding-top: 0;
    }

    /* CỐ Ý không có padding-bottom: hở đáy do margin-bottom sẵn có của .pagination-wrapper (1rem) và
       .main_panel (12px) lo, vì không còn khối overflow nào nuốt margin của con cuối. Thêm padding nữa
       là hở gấp đôi. */

    /*
     * Cột lọc dính dưới thanh nav. align-self để nó cao tự nhiên: .sidebar là flex item, mặc định
     * stretch là cao bằng cả hàng nên sticky chẳng có chỗ nào mà dính.
     *
     * Cao TỰ NHIÊN, không max-height — nhờ vậy trong cột không có khối overflow nào và dropdown ô lọc
     * thôi bị cắt. Chỉ đúng với cột lọc THẤP HƠN viewport; cột cao hơn phải thêm .sidebar_scroll, xem
     * rule dưới.
     */
    .scroll_body .sidebar {
        position: sticky;
        top: var(--navbar-height);
        align-self: flex-start;
        padding-top: 1rem;
    }

    /*
     * Cột lọc CAO HƠN viewport thì sticky thuần không dùng được, và đây là bản chất của sticky chứ không
     * phải lỗi: phần tử sticky bị chặn trong containing block của nó. Cột cao hơn màn hình thì nó dính
     * ngay từ lượt cuộn đầu và không nhúc nhích nữa, nên mấy ô lọc dưới cùng chỉ lộ ra khi mép dưới của
     * containing block (hàng hai cột) đẩy nó lên — tức khi đã cuộn tới ĐÁY danh sách. Trang Sản phẩm có
     * 14 khối lọc, dải "Chữ số cuối của mã hàng" nằm cuối nên bị đúng cảnh đó.
     *
     * Cách chữa là chặn chiều cao rồi trả việc cuộn về .side-sheet như khuôn cũ. Đổi lại dropdown trong
     * cột bị cắt trở lại — bằng đúng hiện trạng các trang .scroll_section hôm nay, không tệ hơn.
     *
     * Đã cân phương án không cần overflow: sticky neo `bottom: 0` thay cho `top`, cột trôi lên tới khi
     * mép dưới chạm đáy màn hình rồi mới dính, nên cuộn một đoạn ngắn là thấy hết ô lọc. Không chọn vì
     * đầu cột (Chi nhánh, Loại sản phẩm) trôi mất hẳn, mà cột lọc thấp thì nó lại dính lơ lửng ở đáy
     * màn hình — vẫn phải phân biệt cao/thấp, tức vẫn cần class riêng mà đổi lấy hành vi lạ hơn.
     *
     * PHẢI gắn thủ công theo trang: CSS không đo được cột cao hơn viewport hay không. Gắn cho cột lọc
     * nào dài quá một màn hình laptop (Sản phẩm, Đơn hàng), bỏ cho cột ngắn (Sai sót) — ở cột ngắn mà
     * gắn vào là mất không cái lợi dropdown không bị cắt, vì overflow: auto cắt kể cả khi chưa có gì để
     * cuộn.
     */
    .scroll_body .sidebar_scroll {
        max-height: calc(100vh - var(--navbar-height));
    }

    /* Thanh tìm + nút tác vụ dính dưới thanh nav — `top: 0` như hai khuôn cũ là nó chui sau navbar, vì
       giờ neo theo viewport chứ không theo mép khung cuộn. */
    .scroll_body .main_wrap > .search_group,
    .scroll_body .main_wrap > .heading_search {
        position: sticky;
        top: var(--navbar-height);
        z-index: 2;
        /* Nền đục màu body vì nội dung cuộn luồn BÊN DƯỚI, trong suốt là chữ chồng chữ. */
        background-color: #f6f8fb;
        padding-top: 1rem;
    }

    /*
     * Cột nội dung KHÔNG mở đầu bằng thanh tìm thì tự chừa lề trên.
     *
     * Lề trên của hàng đã về 0 (rule .main_wrapper.scroll_body ở trên) và giao cho từng cột tự lo, nhưng
     * ở cột nội dung thì lề đó nằm trong rule sticky ngay trên — tab nào không có .search_group hay
     * .heading_search (Kênh bán > Cấu hình, Lịch sử đồng bộ…) là card dính sát thanh nav.
     *
     * Không đặt padding-top thẳng cho mọi .main_wrap rồi thôi: cột CÓ thanh tìm thì thanh đó phải chạm
     * đỉnh cột, chừa thêm là hở một khe cho nội dung cuộn luồn qua phía trên nó.
     */
    .scroll_body .main_wrap:not(:has(> .search_group, > .heading_search)) {
        padding-top: 1rem;
    }

    /*
     * Rail tab dính theo, nếu không cuộn xuống là mất đường đổi tab. Khai cho MỌI rail chứ không bó
     * trong .scroll_body: rail nằm ở Layouts/TabsLayout, ngoài hàng của trang.
     *
     * RAIL HẸP: sticky đặt ở .nav chứ không ở .side, vì .side mang dải nền phải cao hết màn (stretch +
     * min-height) mà stretch thì sticky không có chỗ dính.
     */
    .side:not(.side_wide) > .nav {
        position: sticky;
        top: var(--navbar-height);
    }

    /*
     * RAIL RỘNG (Cài đặt) dính CẢ khối bo góc, không phải chỉ .nav bên trong như rail hẹp: nền của nó
     * chính là cái khối đó, dính mỗi .nav thì viền bo trôi mất còn mục thì ở lại, nhìn như hai mảnh rời.
     *
     * Cộng 1rem vào top cho khớp khoảng hở trên của hàng, nếu không lúc dính nó chui sát thanh nav.
     * align-self: mặc định stretch là cao bằng cả hàng, sticky không có chỗ dính.
     *
     * LỊCH SỬ lỗi lệch 1px trên Firefox, để đừng ai dò lại từ đầu: trang Cài đặt canh giữa nên mép trái
     * container hay rơi vào NỬA pixel; Firefox đẩy cả phần tử sticky LẪN khung cuộn lên lớp vẽ riêng rồi
     * snap lớp đó về lưới pixel nguyên, trong khi container bọc vẫn ở nửa pixel — lệch 1px, nhìn như lề
     * phải của rail mỏng hơn lề trái (Chrome không snap nên không lộ). Đã thử lần lượt: chốt lề bằng
     * margin-left: round(down, ...) — bỏ vì chủ repo không thích và công thức phải biết trước max-width;
     * bỏ sticky — chạy được nhưng mất tính năng. Cuối cùng gỡ KHUNG CUỘN của .side_wide .nav (nó đã hết
     * việc từ khi body cuộn — xem chú thích ở đó), giữ lại sticky.
     */
    .side_wide {
        position: sticky;
        top: calc(var(--navbar-height) + 1rem);
        align-self: flex-start;
    }

    /*
     * Lề trên của trang Cài đặt nằm ở HÀNG chứ không ở cột, khác mọi trang danh sách: hai cột ở đây là
     * rail bo góc và khối nội dung, cả hai cùng cần hở một khoảng với thanh nav. Trước 12/10/2026 việc
     * này do .settings_scroll > .main_wrapper lo.
     *
     * Kèm theo phải gỡ lề trên của hàng BÊN TRONG (SettingsPanel dựng một .main_wrapper nữa), không thì
     * nội dung tụt thêm 1rem so với rail.
     */
    .main_wrapper.narrow_layout {
        padding-top: 1rem;
    }

    .narrow_layout .wrapper .main_wrapper {
        padding-top: 0;
    }

    /* Thôi cuộn riêng: cuộn trong một khối đang cuộn thì kéo chuột không biết cái nào chạy. Cuộn NGANG
       của bảng rộng vẫn còn vì nằm ở .table-responsive bên trong card. */
    .scroll_body .main_panel {
        overflow: visible;
    }

    /* Bù khoảng hở sang bảng: cột lọc KHÔNG cuộn thì không có dải thanh cuộn, còn mỗi 12px padding-right
       nên ô lọc dính sát bảng. Cộng 1rem cho xấp xỉ một thanh cuộn, để hai nhóm trang nhìn như nhau. Cột
       .sidebar_scroll có thanh cuộn thật nên không bù, nếu không là hở gấp đôi. Cố ý KHÔNG viết 15px: bề
       rộng thanh cuộn mỗi trình duyệt một khác (Chrome 15, Firefox 17). */
    .scroll_body .sidebar:not(.sidebar_scroll) .filter_content {
        padding-right: calc(var(--bs-gutter-x) * .5 + 1rem);
    }

}


@media (min-width: 1200px) {
    .main_data > .row > .col-md-3 {
        max-width: 300px;
    }

    .main_data > .row > .col-md-6 {
        max-width: 100%;
        flex: 1;
    }

    .modal-xl {
        max-width: 1260px;
    }
}

/* Livestream */
.livestream-statistic .table .bg-muted {
    width: 280px;
}

.livestream-page .sections {
    list-style: none;
    padding: 0;
}

/*
 * Bảng ca live rộng tối thiểu 510px, nên cho CẢ danh sách cuộn ngang chung một thanh thay vì mỗi bảng
 * một thanh riêng. Trước đây mượn .table-responsive cho tiện, nhưng đây là <ul> chứ không phải thẻ bọc
 * bảng — tên sai làm người đọc tưởng bên trong có table.
 *
 */
.livestream-page .sections_scroll {
    overflow-x: auto;
    flex-shrink: 0;
}

.livestream-page .sections h5 {
    font-size: 1.125rem;
}

.livestream-page .sections h5 .badge {
    font-weight: 600;
}

.livestream-page .section .table {
    min-width: 510px;
}

.livestream-page .section::before {
    content: '';
    width: 15px;
    height: 15px;
    background: #ccc;
    border: 2px solid #f6f8fb;
    border-radius: 50%;
    position: absolute;
    left: -8px;
    top: 7px;
}

.livestream-page .section:not(:first-child)::before {
    top: 15px;
}

.livestream-page .section .item {
    width: 200px;
}

.livestream-page .section .card-body {
    padding: .5rem .75rem;
}

.livestream-page .table td.shift {
    min-width: 230px;
}

@media (max-width: 767px) {
    .livestream-page .heading .date {
        width: 100%;
    }

    .livestream-page .section .item {
        width: 100%;
    }
}

/*
 * Dải xem nhanh ở trang Đơn hàng: các ô chia đều, ngăn nhau bằng một đường kẻ dọc.
 *
 * Dùng grid chứ không flex: mọi ô rộng bằng nhau bất kể số dài ngắn, nên số không nhảy chỗ khi dữ liệu đổi.
 */
.stat_strip {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    /*
     * Lề dưới chỉ cần trên màn hẹp: từ 992px thanh tìm ngay dưới đã có padding-top riêng (nó dính đỉnh
     * cột nên cần mảng nền đó), giữ thêm margin ở đây là hở gấp đôi.
     */
    margin-bottom: 1rem;
}

@media (min-width: 992px) {
    .stat_strip {
        /* Khung cuộn bỏ padding trên để sticky dính sát nav, nên lề trên nằm ở chính dải này. */
        margin-top: 1rem;
        margin-bottom: 0;
    }
}

.stat_strip > div + div {
    border-left: 1px solid var(--bs-border-color);
}

/*
 * Điện thoại: 2 ô một hàng. Bốn ô cạnh nhau thì mỗi ô chỉ còn chừng 90px, số tiền xuống dòng ngay.
 * Ô lẻ mở hàng mới nên bỏ viền trái; từ ô thứ 3 thêm viền trên để hai hàng vẫn tách nhau.
 */
@media (max-width: 767.98px) {
    .stat_strip {
        grid-template-columns: repeat(2, 1fr);
    }

    .stat_strip > div:nth-child(odd) {
        border-left: 0;
    }

    .stat_strip > div:nth-child(n+3) {
        border-top: 1px solid var(--bs-border-color);
    }
}

/* Trang tệp đính kèm */
/*
 * Danh sách kênh luôn hiện trong hộp thoại ca live, nhìn như một dropdown đang mở sẵn. Chặn chiều cao
 * để nó không đẩy dài cả hộp thoại, tự vẽ viền — không có khung thì danh sách cuộn trông như trôi
 * lơ lửng giữa form.
 *
 * Mục bên trong là .dropdown-item nhưng khối này không phải .dropdown-menu, nên phải tự khai các biến
 * Bootstrap chỉ định nghĩa trên .dropdown-menu — cùng lý do với .notif-sheet ở trên. Màu mục đang chọn
 * lấy bộ nhạt như .filter-chip.active thay vì xanh đặc mặc định của Bootstrap.
 */
.channel-list {
    /* overflow: hidden ở lớp ngoài mới cắt được thanh cuộn của lớp trong theo góc bo — thanh cuộn
       nằm trong chính khối cuộn nên border-radius của khối đó không cắt nó, hai góc phải thành vuông. */
    overflow: hidden;
    /* Không padding ở đây — nó nằm trong .channel-list-scroll để thanh cuộn chạm sát viền ngoài. */
    padding: 0;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    --bs-dropdown-item-padding-x: 1rem;
    --bs-dropdown-item-padding-y: .375rem;
    --bs-dropdown-link-color: var(--bs-body-color);
    --bs-dropdown-link-hover-color: var(--bs-body-color);
    --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg);
    --bs-dropdown-link-active-color: var(--bs-primary-text-emphasis);
    --bs-dropdown-link-active-bg: var(--bs-primary-bg-subtle);
}

/*
 * Padding đặt ở ĐÂY chứ không ở .channel-list: để ngoài thì thanh cuộn bị đẩy vào trong, hở một
 * khoảng với viền; để trong thì thanh cuộn dính sát viền, còn mục vẫn thụt vào như .dropdown-menu.
 *
 * Cao vừa đúng 6 mục: mỗi mục = một dòng chữ (line-height 1.5) + padding trên dưới của .dropdown-item,
 * cộng padding của chính khung này (box-sizing: border-box nên max-height tính cả padding). Tính bằng
 * calc theo biến thay vì gõ số cứng, đổi padding là chiều cao tự theo.
 *
 * Mục có tên dài xuống hai dòng thì thấy ít hơn 6 — chấp nhận, chặn cứng mới là cắt ngang chữ.
 */
.channel-list-scroll {
    --channel-list-padding: .25rem;
    padding: var(--channel-list-padding);
    max-height: calc(6 * (1.5em + 2 * var(--bs-dropdown-item-padding-y)) + 2 * var(--channel-list-padding));
    overflow-y: auto;
}

/* .dropdown-item mặc định nowrap — tên kênh dài sẽ đẻ ra thanh cuộn ngang thay vì xuống dòng. */
.channel-list .dropdown-item {
    white-space: normal;
}

/* Quản lý file: ô xem trước trong bảng, cố định vuông để các dòng cao bằng nhau. */
.file-table img.file_thumb {
    width: 2.5rem;
    height: 2.5rem;
    object-fit: cover;
    border: 1px solid #ddd;
    background-color: #f5f5f5;
}

/* WordPress dialog */
.wordpress-dialog .d-grid {
    grid-template-columns: repeat(8, 1fr);
    gap: .375rem;
}

/* POS */
.pos-page .sale_panel {
    height: calc(100vh - var(--navbar-height));
    display: flex;
    overflow: hidden;
}

.pos-page .cart-payment {
    height: calc(100vh - var(--navbar-height));
    background-color: #fff;
    padding: 1rem 15px;
    border-left: 1px solid #dee2e6;
}

.pos-page .sale_panel .product_cart {
    overflow: auto;
    width: 100%;
    margin-top: unset;
}

/* .btn chứ không chỉ a: bản React dùng <button>, bản PHP dùng <a>. */
.pos-page .action_right .btn {
    color: #777;
}

@media (max-width: 767px) {
    .pos-page .sale_panel {
        display: unset;
    }

    .pos-page .cart-payment {
        border-left: none;
    }
}

/*
 * Bỏ ảnh nền, thay bằng dải chuyển sắc xanh nhạt lấy từ màu chủ đạo #206bc4.
 *
 * Ảnh cũ dùng background-size: cover nên khi trang ngắn lại (xem media query bên dưới) nó
 * tính theo hộp <body> chứ không theo khung nhìn, ảnh bị cắt ngang form. Gradient ở đây né
 * hẳn chuyện đó bằng hai điểm:
 *
 *   background-size cố định 22rem — dải màu luôn cao đúng chừng đó, không co theo nội dung
 *   no-repeat + background-color trùng ĐIỂM CUỐI của gradient — phần dưới liền màu, không
 *   lặp thành sọc như khi để repeat mặc định
 *
 * Nhờ vậy trang cao hay thấp, mobile hay desktop, đều liền một mảng.
 */
.login-page {
    background-color: #f6f8fb;
    background-image: linear-gradient(180deg, #8fb8e8 0%, #c3daf3 40%, #e2ebf8 72%, #f6f8fb 100%);
    background-repeat: no-repeat;
    background-size: 100% 28rem;
}

.login-page .content {
    padding-top: unset;
    min-height: 100vh;
    min-height: 100dvh;
}

.login-page .login {
    display: flex;
    min-height: 90vh;
    align-items: center;
}

/*
 * Điện thoại: KHÔNG căn giữa theo chiều dọc.
 *
 * Căn giữa thì ô nhập nằm giữa trang, bàn phím bật lên là trình duyệt phải cuộn xuống để lộ
 * ô đang gõ. Toast báo lỗi là position:fixed neo theo LAYOUT viewport (bàn phím chỉ thu nhỏ
 * VISUAL viewport, không thu nhỏ layout), nên nó bị đẩy lên trên vùng nhìn thấy — người dùng
 * phải cuộn ngược lên mới đọc được thông báo sai mật khẩu.
 *
 * Để thẻ sát đầu trang thì lúc focus gần như không phải cuộn, toast luôn trong tầm mắt.
 *
 * Bỏ luôn min-height ở .content: giữ 100dvh thì trang vẫn cao bằng màn hình trong khi bàn
 * phím đã ăn mất nửa dưới, nên vẫn còn thanh cuộn. Để chiều cao tự nhiên thì trang chỉ cao
 * bằng cái thẻ, không còn gì để cuộn.
 *
 * Ảnh nền KHÔNG mất khi trang ngắn lại: nền đặt trên <body>, mà <html> không khai nền nào
 * nên trình duyệt lan nền của body ra toàn khung nhìn (background propagation). Đừng thêm
 * min-height lại để "sửa" nền — nền vẫn phủ kín.
 */
@media (max-width: 575.98px) {
    .login-page .content {
        min-height: auto;
    }

    .login-page .login {
        min-height: auto;
        align-items: flex-start;
        /* Đủ để thẻ không dính mép trên, nhưng vẫn nằm trong vùng thấy khi bàn phím bật —
           đẩy thêm nữa là quay lại cảnh phải cuộn mới thấy toast. */
        padding-top: 4rem;
    }
}

/* Nền mờ của hộp thoại React mở chồng lên modal PHP (Components/Dialog stacked, LegacyBridge) — nằm trên modal PHP
   (1055) và dưới hộp thoại React (1075). */
.modal-backdrop.modal-backdrop-stacked {
    z-index: 1070;
}

/*
 * Chip lọc trên bảng — dải trạng thái ở trang Đơn hàng (Pages/Orders/Order.tsx).
 *
 * KHÔNG dùng .btn: .btn-light mang sẵn nền xám lúc rê chuột, đè lên màu của chip đang chọn. Màu chip
 * đang chọn lấy đúng bộ biến của .list-group-item-primary để giống mục đã tích ở OrderItemPicker.
 */
.filter-chip {
    /* Chip ở báo cáo là thẻ <a> (link đổi URL), không có dòng này là Bootstrap gạch chân chữ. */
    text-decoration: none;
    padding: .25rem .75rem;
    font-size: .875rem;
    line-height: 1.5;
    border: 1px solid var(--bs-border-color);
    border-radius: 50rem;
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
}

.filter-chip:hover {
    background-color: var(--bs-secondary-bg);
}

.filter-chip.active {
    background-color: var(--bs-primary-bg-subtle);
    border-color: var(--bs-primary-border-subtle);
    color: var(--bs-primary-text-emphasis);
}

.filter-chip.active:hover {
    background-color: var(--bs-primary-border-subtle);
}
