/*
 * Thư viện thành phần giao diện: thẻ, nút, badge, bảng, form, phân trang.
 * Mọi màu và khoảng cách lấy từ tokens.css - không hard-code giá trị ở đây.
 */

/* ── Thẻ ───────────────────────────────────────────────────── */

.card {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-xs);
}

.card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--border);
}

.card__title {
    font-size: var(--text-md);
    font-weight: 600;
}

.card__subtitle {
    margin-top: 2px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.card__body {
    padding: var(--space-5);
}

.card__body--flush {
    padding: 0;
}

.card__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--border);
    background: var(--bg-raised);
    border-radius: 0 0 var(--radius-md) var(--radius-md);
}

/* ── Thẻ chỉ số ────────────────────────────────────────────── */

.stat {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4) var(--space-5);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-xs);
    /*
      Khai thẻ này là một khung tham chiếu để cỡ chữ của con số bên trong đo theo
      bề ngang THẺ, chứ không theo bề ngang màn hình. Cần đúng như vậy vì số thẻ
      trên một hàng thay đổi theo màn: bảy thẻ ở khổ 1920 hẹp hơn năm thẻ ở khổ
      1366, nên một ngưỡng theo khổ màn hình sẽ đoán sai chiều.
    */
    container-type: inline-size;
}

.stat__label {
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--text-muted);
}

/*
  Con số của thẻ chỉ số co lại khi thẻ hẹp.

  Số tiền là chỗ duy nhất trong cả giao diện không thể xuống dòng: "1.200.022.231 ₫"
  là một khối liền, và ở cỡ chữ 28px nó cần 227px trong ô rộng 178px - thò hẳn ra
  ngoài viền thẻ. Cắt bớt chữ số thì thành con số SAI chứ không phải con số xấu,
  nên phải co cỡ chữ.

  10cqw nghĩa là một phần mười bề ngang thẻ. Cùng với cận trên var(--text-2xl),
  quy tắc này đọc là: giữ nguyên cỡ chữ thiết kế khi thẻ đủ rộng, và chỉ co lại
  đúng bằng mức cần thiết khi không đủ. Cận dưới var(--text-md) để một hàng nhiều
  thẻ trên màn hẹp không biến con số thành chữ thường.
*/
.stat__value {
    font-size: clamp(var(--text-md), 10cqw, var(--text-2xl));
    font-weight: 600;
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
}

.stat__hint {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

/*
  Thẻ chỉ số BẤM ĐƯỢC, nhảy sang đúng tab hoặc bộ lọc tương ứng (chuẩn 08, mục 3).

  Phải khai lại màu chữ: thẻ là một <a>, mà quy tắc nền cho mọi liên kết màu nhấn -
  không có dòng này thì cả nhãn lẫn con số chuyển sang xanh và thẻ bấm được trông
  khác hẳn thẻ bên cạnh, dù chúng là cùng một loại thông tin.
*/
.stat--link {
    color: inherit;
    cursor: pointer;
    transition: border-color var(--transition), box-shadow var(--transition);
}

@media (hover: hover) {
    .stat--link:hover {
        border-color: var(--border-strong);
        box-shadow: var(--shadow-sm);
    }
}

.stat--accent .stat__value { color: var(--accent); }
.stat--success .stat__value { color: var(--success); }
.stat--warning .stat__value { color: var(--warning); }
.stat--danger .stat__value { color: var(--danger); }
/* 'info' được vài màn truyền vào từ lâu mà chưa từng có luật - thẻ chỉ số hiện
   màu mặc định trong im lặng (vòng phản biện 12/08 chiều). */
.stat--info .stat__value { color: var(--info); }

/* ── Nút ───────────────────────────────────────────────────── */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: 0 var(--space-4);
    height: 34px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition),
                color var(--transition), transform var(--transition);
}

.btn:hover {
    text-decoration: none;
}

/* Phản hồi xúc giác khi bấm giữ */
.btn:active {
    transform: translateY(1px);
}

.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: 0.55;
    cursor: not-allowed;
    transform: none;
}

.btn--primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-inverse);
}

.btn--primary:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
    color: var(--text-inverse);
}

.btn--secondary {
    background: var(--bg-surface);
    border-color: var(--border-strong);
    color: var(--text-primary);
}

.btn--secondary:hover {
    background: var(--neutral-50);
    border-color: var(--neutral-400);
    color: var(--text-primary);
}

.btn--danger {
    background: var(--bg-surface);
    border-color: var(--danger-border);
    color: var(--danger);
}

.btn--danger:hover {
    background: var(--danger-soft);
    border-color: var(--danger);
    color: var(--danger);
}

.btn--ghost {
    background: transparent;
    border-color: transparent;
    color: var(--text-secondary);
}

.btn--ghost:hover {
    background: var(--neutral-100);
    color: var(--text-primary);
}

.btn--sm {
    height: 28px;
    padding: 0 var(--space-3);
    font-size: var(--text-sm);
}

.btn--icon {
    width: 34px;
    padding: 0;
}

.btn__icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* ── Bảng nạp dữ liệu (dropdown trong thanh hành động) ─────── */

.import-panel {
    position: relative;
}

.import-panel__trigger::-webkit-details-marker {
    display: none;
}

.import-panel__trigger {
    list-style: none;
}

/* Mở panel thì nút giữ trạng thái nhấn để người dùng biết nguồn của bảng */
.import-panel[open] > .import-panel__trigger {
    background: var(--neutral-100);
    border-color: var(--neutral-400);
}

.import-panel__menu {
    position: absolute;
    right: 0;
    top: calc(100% + var(--space-2));
    z-index: 30;
    width: min(420px, calc(100vw - var(--space-8)));
    padding: var(--space-4);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    text-align: left;
}

.import-panel__hint {
    margin-bottom: var(--space-4);
    font-size: var(--text-xs);
    color: var(--text-secondary);
    line-height: 1.6;
}

.import-panel__form + .import-panel__form {
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border);
}

.import-panel__form-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
}

.import-panel__form-title {
    font-size: var(--text-base);
    font-weight: 600;
}

.import-panel__template {
    font-size: var(--text-xs);
    white-space: nowrap;
}

.import-panel__form-desc {
    margin: var(--space-1) 0 var(--space-3);
    font-size: var(--text-xs);
    color: var(--text-muted);
    line-height: 1.5;
}

.import-panel__form-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.import-panel__file {
    flex: 1;
    min-width: 0;
    height: auto;
    padding: var(--space-2);
    font-size: var(--text-xs);
}

/* Khối chọn cột của chế độ Cập nhật (yêu cầu 11/08). Nằm dưới cùng khối nạp,
   đóng sẵn: người chỉ muốn thêm mới không phải nhìn thấy nó. */
.import-panel__update {
    margin: var(--space-2) 0 var(--space-3);
    border-top: 1px dashed var(--border);
    padding-top: var(--space-2);
}

.import-panel__update > summary {
    cursor: pointer;
    list-style: none;
}

.import-panel__update > summary::-webkit-details-marker {
    display: none;
}

.import-panel__cot {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-top: var(--space-2);
}

.import-panel__cot-mot {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-xs);
    color: var(--text-secondary);
}

/* Ô tích bị khoá vẫn phải ĐỌC ĐƯỢC là nó đang bật, không phải mờ tới mức trông
   như đang tắt - nó là cột bắt buộc, không phải cột bị cấm. */
.import-panel__cot-mot input:disabled {
    opacity: 0.8;
}

.import-panel__cot > .btn {
    align-self: flex-start;
    margin-top: var(--space-2);
}

/* ── Nhóm ô nhập nhỏ nằm chung một cột bảng ────────────────── */

/*
  Ba ô kích thước và cặp đơn vị/quy cách phải vừa một ô bảng mà vẫn bấm được.
  Dùng grid thay vì flex để ba ô luôn bằng nhau, không co theo nội dung.
*/
.input-trio {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-1);
}

.input-pair {
    display: grid;
    grid-template-columns: 1fr 80px;
    gap: var(--space-1);
}

.input-trio .input,
.input-pair .input,
.input-pair .select {
    min-width: 0;
    padding-left: var(--space-2);
    padding-right: var(--space-2);
    font-size: var(--text-xs);
}

/* Ô tích trong bảng: bỏ khoảng đệm rộng của biểu mẫu để không đội cao dòng */
.checkbox--compact {
    gap: var(--space-2);
    font-size: var(--text-xs);
    white-space: nowrap;
}

/* ── Nút bánh răng cài đặt (dropdown cuối hàng tab) ────────── */

/*
  Hàng tab và nút cài đặt nằm chung một dải: nút phải ở ngay trên thanh lọc để
  người dùng thấy được vì sao bảng đang xếp loại như vậy, nhưng không được
  chiếm chỗ của các tab lọc nhanh vốn dùng thường xuyên hơn nhiều.
*/
.tabs-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    border-bottom: 1px solid var(--border);
}

.tabs-row > .tabs {
    flex: 1;
    min-width: 0;
    border-bottom: none;
}

.gear-panel {
    position: relative;
    flex-shrink: 0;
    margin-right: var(--space-4);
}

.gear-panel__trigger::-webkit-details-marker {
    display: none;
}

.gear-panel__trigger {
    list-style: none;
    cursor: pointer;
}

.gear-panel[open] > .gear-panel__trigger {
    background: var(--neutral-100);
    color: var(--text-primary);
}

.gear-panel__menu {
    position: absolute;
    right: 0;
    top: calc(100% + var(--space-2));
    z-index: 30;
    width: min(340px, calc(100vw - var(--space-8)));
    padding: var(--space-4);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    text-align: left;
}

.gear-panel__hint {
    margin-bottom: var(--space-4);
    font-size: var(--text-xs);
    color: var(--text-secondary);
    line-height: 1.6;
}

.gear-panel__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-2);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
}

/* Nhắc lại ngưỡng đang chạy để biết mình có đang sửa thật hay không */
.gear-panel__current {
    font-size: var(--text-xs);
    color: var(--text-muted);
    line-height: 1.4;
}

/* ── Badge trạng thái ──────────────────────────────────────── */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
}

.badge--neutral {
    background: var(--neutral-100);
    border-color: var(--neutral-200);
    color: var(--neutral-700);
}

.badge--info {
    background: var(--info-soft);
    border-color: var(--info-border);
    color: var(--info);
}

.badge--success {
    background: var(--success-soft);
    border-color: var(--success-border);
    color: var(--success);
}

.badge--warning {
    background: var(--warning-soft);
    border-color: var(--warning-border);
    color: var(--warning);
}

.badge--danger {
    background: var(--danger-soft);
    border-color: var(--danger-border);
    color: var(--danger);
}

/* ── Bảng dữ liệu ──────────────────────────────────────────── */

/*
 * Quy tắc UI-12: KHÔNG cuộn ngang.
 *
 * Bản trước để .table-wrap { overflow-x: auto } và .table { width: max-content },
 * nghĩa là bảng cứ giãn ra theo nội dung rồi đẩy người dùng đi kéo ngang - đúng
 * thứ quy tắc cấm, và cột thao tác là cột đầu tiên bị đẩy ra ngoài khung nhìn.
 *
 * Giờ bảng bị buộc vừa khổ; chỗ để cắt bớt cột là nút chọn cột ở cuối hàng tiêu
 * đề. Cuộn ngang chỉ còn là lối thoát cuối cùng trên màn hình hẹp (khai ở cuối
 * tệp), vì trên điện thoại thì thà kéo ngang còn hơn chữ bị bóp thành một ký tự
 * mỗi dòng.
 *
 * Bỏ overflow ở đây còn mở ra một thứ trước đây không làm được: hàng tiêu đề
 * dính khi cuộn dọc (quy tắc UI-19). Hộp có overflow là một khung cuộn riêng,
 * và position:sticky bên trong nó thì dính vào khung đó - tức là không dính gì
 * cả, vì khung cao đúng bằng nội dung.
 */
.table-wrap {
    position: relative;
}

/*
  Ngoại lệ của UI-12: bảng NHẬP LIỆU của chứng từ được phép cuộn ngang.

  Chính lời biện hộ của UI-12 dẫn tới ngoại lệ này - "thà kéo ngang còn hơn chữ
  bị bóp thành một ký tự mỗi dòng". Với bảng TRA CỨU thì bóp chữ chỉ khó đọc, và
  lối thoát đã có sẵn là nút chọn cột. Với bảng NHẬP LIỆU thì không có nút chọn
  cột nào cả - không giấu được một ô bắt buộc - và ô nhập khai width:100% nên nó
  co tới sát 0 mà trình duyệt không coi là lỗi.

  Đo trên bản đang chạy trước khi sửa: ô Đơn giá của phiếu nhập 27px, ô Giá vốn
  của sản phẩm 25px, ba ô Dài × Rộng × Cao mỗi ô 18px. Gõ 1.250.000 vào một ô
  25px thì không đọc lại được chữ số nào - người dùng phải bôi đen rồi kéo con
  trỏ để xem mình vừa gõ gì. Đó là hỏng chức năng, không phải hơi chật.

  min-width khai theo tổng bề ngang các cột đã định, đặt ngay tại chỗ dùng bằng
  biến --bang-rong-toi-thieu: mỗi biểu mẫu một số khác nhau, và để số ấy cạnh
  cái bảng nó mô tả thì sửa cột là thấy ngay phải sửa số.

  Đánh đổi đã biết: hộp có overflow là một khung cuộn riêng nên hàng tiêu đề
  dính (UI-19) mất tác dụng trong bảng này. Chấp nhận được - chứng từ nhập tay
  hiếm khi dài quá chục dòng, còn ô nhập thì dòng nào cũng phải đọc được.
*/
.table-wrap--entry {
    overflow-x: auto;
}

.table-wrap--entry > .table {
    min-width: var(--bang-rong-toi-thieu, 1100px);
}

.table {
    width: 100%;
    table-layout: auto;
    font-size: var(--text-sm);
}

/*
  Bảng dòng chứng từ: bề ngang cột do HÀNG TIÊU ĐỀ quyết định, không do nội dung.

  Với table-layout mặc định (auto), một thẻ <select> lấy bề ngang tối thiểu bằng
  lựa chọn DÀI NHẤT của nó. Bảng nhập hàng có ô chọn mã hàng, mà tên hàng ở đây
  dài tới bảy mươi ký tự ("Bánh Steamed buns biscuits vị trứng (Máy Lọc Nước
  Rabbit Cute Hồng) 50g") - nên chỉ cần chọn một mã là cột Sản phẩm nở ra và bóp
  hết phần còn lại: đo trên bản đang chạy, ô Đơn giá còn 30px và chỉ hiện được
  một ký tự. Người dùng gõ 1.250.000 vào rồi không đọc lại được chữ số nào.

  Khoá bề ngang theo tiêu đề thì cột nào cũng giữ đúng số px đã khai, còn tên
  hàng quá dài thì bị cắt bớt phần đuôi - vẫn đọc đủ để nhận ra mã, và bản đầy
  đủ nằm trong danh sách xổ xuống. Đây là cách giữ được quy tắc UI-12 (không
  cuộn ngang) mà không phải bóp ô nhập.
*/
/*
  ÉP CỘT: bề ngang khai ở tiêu đề là bề ngang THẬT.

  Không có lớp này thì `style="width: 130px"` trên <th> chỉ là một lời đề nghị.
  table-layout mặc định là auto, tức trình duyệt đo nội dung trước rồi mới chia
  chỗ, và nó bỏ qua con số đã khai bất cứ khi nào nội dung đòi rộng hơn. Hai hệ
  quả, cả hai đều thấy được bằng mắt:

   * cột nhảy mỗi lần dữ liệu đổi. Cùng một bảng, đơn có tên hàng dài thì cột
     Đơn giá hẹp lại, đơn khác thì rộng ra - người dùng mở mười đơn là nhìn mười
     bố cục khác nhau, và không quét dọc cột số được vì cột không đứng yên;
   * ô nhập bị bóp tới mức không đọc lại được chữ mình vừa gõ. Đo trên bản đang
     chạy trước khi ép: ô Đơn giá phiếu nhập 27px, ô Giá vốn sản phẩm 25px, ba ô
     Dài × Rộng × Cao mỗi ô 18px.

  table-layout: fixed chia chỗ theo đúng hàng tiêu đề, không thèm đọc nội dung.
  Cột nào không khai bề ngang thì chia đều phần còn lại.
*/
.table--co-dinh {
    table-layout: fixed;
}

.table--co-dinh .select,
.table--co-dinh .input {
    max-width: 100%;
}

/*
  Ép cột thì chuỗi không có dấu cách - mã SKU, mã vạch - không còn chỗ để đẩy
  cột rộng ra nữa, nên nó tràn ra đè lên cột bên cạnh. Cho ngắt ở bất kỳ đâu.
*/
.table--co-dinh td,
.table--co-dinh th {
    overflow-wrap: anywhere;
}

/*
  Dòng chú thích dưới một con số vẫn phải xuống dòng được.

  .num và .money khai white-space: nowrap để con số không bị bẻ giữa chừng -
  đúng, một con số cắt làm hai dòng thì đọc ra số khác. Nhưng cái nowrap ấy đổ
  xuống cả những dòng chữ nhỏ nằm trong cùng ô: "/ Thùng", "1 Thùng = 24 Hộp",
  "mặt hàng chưa khai thuế". Chúng là CHỮ, và giữ nowrap ở đó là ép cột rộng
  bằng cả câu.

  Đo trên màn giỏ đặt hàng trước khi sửa: ô Đơn giá rộng 126px phải chứa 176px
  nội dung, ô Thuế rộng 88px phải chứa 163px - cả hai tràn ra đè cột bên cạnh
  và đẩy bảng vượt khung.
*/
.num .table__meta,
.money .table__meta {
    white-space: normal;
}

/* ── Bề ngang cột theo LOẠI dữ liệu ────────────────────────────────────────
  Khai theo loại chứ không theo từng bảng, và đây là phần đáng giá nhất.

  Cột "Đơn giá" rộng bằng nhau ở phiếu nhập, đơn đặt hàng, phiếu trả và báo giá
  thì mắt người dùng học một lần dùng cho mọi màn: nhìn quen chỗ nào là tiền,
  chỗ nào là số lượng, không phải đọc lại tiêu đề ở từng bảng. Rải con số rời
  vào từng tệp thì mỗi lần thêm cột lại lệch một ít, và không ai phát hiện ra vì
  hai bảng ấy không bao giờ nằm cạnh nhau trên cùng một màn.

  Con số chọn theo nội dung DÀI NHẤT thật sự phải chứa, cộng 2 × 16px đệm ô:
    tiền  1.250.000 ₫ ~ 94px chữ  → 126px, ô nhập còn 94px
    tiền nhập  ô gõ được 1.250.000 cần ~110px → 142px
    lượng  99.999 ~ 52px, cộng nút tăng giảm của ô số → 100px
    suất   100% → 88px
    ngày   31/12/2027 trong ô date của Chrome cần 130px → 150px
*/
/*
  Bảng NHIỀU CỘT: thu đệm ngang lại.

  Đệm 16px mỗi bên là đúng cho bảng bảy tám cột, nhưng bảng danh sách sản phẩm có
  mười bốn cột đang hiện - tức 448px chỉ để làm khoảng cách. Đo được: bảng rộng
  tối thiểu 1.331px trong khung 1.326px, thò ra khỏi thẻ đúng 5px, và không co
  thêm được vì đó đã là bề ngang tối thiểu của nội dung.

  Thu về 10px lấy lại 168px - đủ để bảng vừa khung mà chữ vẫn không dính vào nhau.
  Chỉ dùng cho bảng tra cứu nhiều cột; bảng nhập liệu giữ đệm rộng vì ô nhập cần
  chỗ thở quanh viền.
*/
.table--gon {
    --table-pad-x: var(--space-2);
}

.col-stt        { width: 44px; }
.col-luong      { width: 100px; }
.col-suat       { width: 88px; }
.col-tien       { width: 126px; }
.col-tien-nhap  { width: 142px; }
.col-ngay       { width: 150px; }
.col-dvt        { width: 118px; }
.col-sku        { width: 150px; }
.col-nut        { width: 44px; }

/*
  Cột tên dài được phép xuống dòng nhưng vẫn giữ bề ngang tối thiểu dễ đọc.

  90px chứ không phải 120px như trước: con số này là SÀN CỨNG cộng thẳng vào bề
  ngang tối thiểu của cả bảng, và bảng nào cũng có cột tên. Ở khổ 1366 thì 30px
  đó là phần đủ để một bảng mười cột vừa khổ hay không.
*/
.table__primary {
    display: inline-block;
    min-width: 90px;
}

.table th {
    padding: var(--table-pad-y) var(--table-pad-x);
    text-align: left;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--text-secondary);
    background: var(--bg-raised);
    border-bottom: 1px solid var(--border);
    /*
      Tiêu đề ĐƯỢC PHÉP xuống dòng. Đây là thay đổi một dòng có sức nặng lớn
      nhất trong cả tệp này.

      white-space: nowrap ở tiêu đề ép mỗi cột rộng tối thiểu bằng cả nhãn viết
      trên một dòng, nên "Ngày tạo phiếu" hay "Nhà cung cấp chính" tự đặt sàn
      140-170px cho một cột chỉ chứa con số ba chữ số. Cộng mười bốn cột như thế
      là bảng rộng 1721px trong khung 1252px - nó thò ra ngoài thẻ, đẩy cả trang
      cuộn ngang, và cột thao tác ở mép phải là thứ bị đẩy ra khỏi khung nhìn
      đầu tiên.

      Cho xuống dòng thì nhãn cao thêm một dòng chữ 12px - trả một lần ở hàng
      tiêu đề - còn bề ngang thì mỗi cột chỉ còn cần bằng TỪ DÀI NHẤT.
    */
    white-space: normal;
    overflow-wrap: break-word;
}

/*
 * Hàng tiêu đề dính khi cuộn dọc (quy tắc UI-19): bảng 6.600 dòng cuộn được vài
 * vòng là người dùng mất hết mốc, và một con số không có tên cột thì không đọc
 * được.
 *
 * Dính ở ĐỈNH khung nhìn, không phải dưới một khoảng 56px như trước: khoảng đó
 * là chiều cao thanh ngang trên cùng, mà thanh ấy đã bị bỏ khi khối người dùng
 * chuyển xuống cuối thanh menu trái. Giữ nguyên số cũ thì tiêu đề bảng treo lơ
 * lửng cách mép trên 56px và các dòng dữ liệu chui lên nằm bên trên nó.
 *
 * z-index lớn hơn 1 của ô dữ liệu là đủ; không còn thanh nào ở trên để tránh.
 */
.table thead th {
    position: sticky;
    top: 0;
    z-index: 5;
}

.table td {
    padding: var(--table-pad-y) var(--table-pad-x);
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
    /* Bảng đã vừa khổ nên chuỗi dài phải tự ngắt, không được đội bề ngang bảng */
    overflow-wrap: break-word;
}

.table tbody tr:last-child td {
    border-bottom: none;
}

.table tbody tr:hover {
    background: var(--neutral-25);
}

/*
 * Ô chứa badge căn giữa cả tiêu đề lẫn dữ liệu (quy tắc UI-01). Đặt cùng một
 * lớp lên <th> và <td> là cách duy nhất bảo đảm hai thứ không lệch nhau khi ai
 * đó chèn thêm cột ở giữa.
 */
.table th.center,
.table td.center {
    text-align: center;
}

.table__code {
    font-family: var(--font-mono);
    font-weight: 500;
}

.table__primary {
    font-weight: 500;
    color: var(--text-primary);
}

.table__meta {
    display: block;
    margin-top: 1px;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.table tfoot td {
    padding: var(--table-pad-y) var(--table-pad-x);
    background: var(--bg-raised);
    border-top: 1px solid var(--border);
    font-weight: 600;
}

/*
  Đệm ô thu dần theo khổ màn hình.

  Ngưỡng đặt theo khổ máy thật chứ không theo số tròn: 1600 là màn 1080p có thanh
  menu mở, 1400 là laptop 1440, 1280 là laptop 1366 - ba khổ chiếm gần hết máy
  đang dùng. Mỗi bậc thu 4px mỗi bên, tức mười bốn cột được trả lại 112px.
*/
@media (max-width: 1600px) {
    :root {
        --table-pad-x: var(--space-3);
    }
}

@media (max-width: 1400px) {
    :root {
        --table-pad-x: 10px;
    }
}

@media (max-width: 1280px) {
    :root {
        --table-pad-x: var(--space-2);
        --table-pad-y: 10px;
    }
}

/* ── Form ──────────────────────────────────────────────────── */

.field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.field__label {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--text-primary);
}

.field__required {
    color: var(--danger);
}

.field__hint {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.field__error {
    font-size: var(--text-xs);
    color: var(--danger);
}

.input,
.select,
.textarea {
    width: 100%;
    padding: var(--space-2) var(--space-3);
    height: 34px;
    background: var(--bg-surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    transition: border-color var(--transition), box-shadow var(--transition);
}

.textarea {
    height: auto;
    min-height: 76px;
    resize: vertical;
}

.input::placeholder,
.textarea::placeholder {
    /* Đủ tương phản để đọc được nhưng vẫn nhạt hơn giá trị đã nhập */
    color: var(--neutral-500);
}

.input:hover,
.select:hover,
.textarea:hover {
    border-color: var(--neutral-400);
}

.input:focus,
.select:focus,
.textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.input--error,
.select--error {
    border-color: var(--danger);
}

.input--error:focus,
.select--error:focus {
    box-shadow: 0 0 0 3px var(--danger-soft);
}

.select {
    appearance: none;
    padding-right: var(--space-8);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%236b7688' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--space-3) center;
}

.checkbox {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    cursor: pointer;
}

.checkbox input {
    width: 15px;
    height: 15px;
    accent-color: var(--accent);
    cursor: pointer;
}

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-4);
}

.form-grid--full > .field {
    grid-column: 1 / -1;
}

/* Ô nhập trong bảng dòng hàng - gọn hơn để bảng không phình */
.table .input,
.table .select {
    height: 30px;
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-sm);
}

/* ── Tab lọc nhanh theo trạng thái ─────────────────────────── */

.tabs {
    display: flex;
    gap: var(--space-5);
    padding: 0 var(--space-5);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    /* Chặn thanh cuộn dọc 1px do gạch chân của tab đang hoạt động gây ra */
    overflow-y: hidden;
}

.tab {
    position: relative;
    padding: var(--space-3) 0 var(--space-2);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    font-weight: 500;
    white-space: nowrap;
    transition: color var(--transition);
}

.tab::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    background: transparent;
    transition: background var(--transition);
}

.tab:hover {
    color: var(--text-primary);
    text-decoration: none;
}

.tab--active {
    color: var(--accent);
}

.tab--active::after {
    background: var(--accent);
}

.tab__count {
    margin-left: var(--space-2);
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--neutral-100);
    font-size: var(--text-xs);
    color: var(--text-secondary);
}

.tab--active .tab__count {
    background: var(--accent-soft);
    color: var(--accent);
}

/* ── Thanh công cụ lọc ─────────────────────────────────────── */

.toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    padding: var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--border);
}

/* Trang không có ô tìm kiếm vẫn cần đẩy nhóm nút lọc về bên phải */
.toolbar__spacer {
    flex: 1;
}

/* Ô tìm kiếm chiếm phần còn lại, các nút lọc dồn về bên phải */
.toolbar__search {
    position: relative;
    flex: 1 1 280px;
    min-width: 220px;
}

.toolbar__search-icon {
    position: absolute;
    left: var(--space-2);
    top: 50%;
    transform: translateY(-50%);
    width: 14px;
    height: 14px;
    color: var(--neutral-500);
    pointer-events: none;
}

/* Thanh lọc dùng cỡ điều khiển gọn hơn form nhập liệu */
.toolbar__search .input {
    height: 30px;
    padding-left: calc(var(--space-2) * 2 + 14px);
    font-size: var(--text-sm);
}

.toolbar__filters {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Nút trong thanh lọc thấp bằng ô tìm kiếm */
.toolbar .btn {
    height: 30px;
    padding: 0 var(--space-3);
    font-size: var(--text-sm);
}

/* ── Nút lọc dạng dropdown ─────────────────────────────────── */

.filter-drop {
    position: relative;
}

.filter-drop__trigger::-webkit-details-marker {
    display: none;
}

.filter-drop__trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    height: 30px;
    padding: 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--bg-surface);
    color: var(--text-primary);
    font-size: var(--text-sm);
    white-space: nowrap;
    list-style: none;
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}

/* Giá trị đã chọn dài thì cắt bớt để nút không kéo dãn cả thanh lọc */
.filter-drop__trigger > span:first-of-type {
    max-width: 168px;
    overflow: hidden;
    text-overflow: ellipsis;
}

.filter-drop__trigger:hover {
    background: var(--neutral-50);
    border-color: var(--neutral-400);
}

.filter-drop[open] > .filter-drop__trigger {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Bộ lọc đang có giá trị được tô nổi để người dùng biết bảng đang bị lọc */
.filter-drop--active > .filter-drop__trigger {
    background: var(--accent-soft);
    border-color: var(--accent-border);
    color: var(--accent);
    font-weight: 500;
}

.filter-drop__chevron {
    width: 10px;
    height: 10px;
    flex-shrink: 0;
    color: var(--text-muted);
    transition: transform var(--transition);
}

.filter-drop[open] .filter-drop__chevron {
    transform: rotate(180deg);
}

.filter-drop__icon {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}

.filter-drop__menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 30;
    width: max-content;
    min-width: 200px;
    max-width: min(320px, calc(100vw - var(--space-8)));
    padding: var(--space-1);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    text-align: left;
}

.filter-drop__scroll {
    max-height: 260px;
    overflow-y: auto;
}

.filter-drop__option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: background var(--transition);
}

.filter-drop__option:hover {
    background: var(--neutral-50);
}

.filter-drop__option input {
    accent-color: var(--accent);
    cursor: pointer;
}

.filter-drop__option-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filter-drop__option-detail {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.filter-drop__section {
    padding: var(--space-2) var(--space-3) var(--space-1);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--text-muted);
}

.filter-drop__body {
    padding: var(--space-2);
    display: grid;
    gap: var(--space-3);
}

.filter-drop__body .input,
.filter-drop__body .select {
    height: 30px;
    font-size: var(--text-sm);
}

.filter-drop__footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-1);
    padding: var(--space-2) var(--space-2) var(--space-1);
    border-top: 1px solid var(--border);
}

.filter-drop__footer .btn {
    height: 28px;
}

@media (max-width: 720px) {
    .toolbar__filters {
        width: 100%;
    }

    .filter-drop__menu {
        left: 0;
        right: auto;
    }
}

/* ── Phân trang ────────────────────────────────────────────── */

.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-5);
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
}

.pagination__summary {
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.pagination__pages {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.pagination__link {
    display: grid;
    place-items: center;
    min-width: 30px;
    height: 30px;
    padding: 0 var(--space-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.pagination__link:hover {
    background: var(--neutral-50);
    border-color: var(--border-strong);
    color: var(--text-primary);
    text-decoration: none;
}

.pagination__link--active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-inverse);
}

.pagination__link--active:hover {
    background: var(--accent-hover);
    color: var(--text-inverse);
}

.pagination__link--disabled {
    opacity: 0.45;
    pointer-events: none;
}

/* ── Thông báo ─────────────────────────────────────────────── */

.alert {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
}

.alert__icon {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    margin-top: 1px;
}

.alert--success {
    background: var(--success-soft);
    border-color: var(--success-border);
    color: var(--success);
}

.alert--danger {
    background: var(--danger-soft);
    border-color: var(--danger-border);
    color: var(--danger);
}

.alert--warning {
    background: var(--warning-soft);
    border-color: var(--warning-border);
    color: var(--warning);
}

.alert--info {
    background: var(--info-soft);
    border-color: var(--info-border);
    color: var(--info);
}

/* ── Trạng thái rỗng ───────────────────────────────────────── */

.empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-12) var(--space-6);
    text-align: center;
}

.empty__icon {
    width: 44px;
    height: 44px;
    color: var(--neutral-400);
}

.empty__title {
    font-size: var(--text-md);
    font-weight: 600;
}

.empty__message {
    max-width: 46ch;
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

/* ── Danh sách mô tả (trang chi tiết) ──────────────────────── */

.definition-list {
    display: grid;
    gap: var(--space-3);
}

.definition-list__row {
    display: grid;
    grid-template-columns: 132px minmax(0, 1fr);
    gap: var(--space-3);
    align-items: baseline;
}

/*
  Danh sách thuộc tính TRẢI RỘNG - dùng khi thẻ chiếm hết bề ngang trang.

  Một dòng nhãn 132px cộng giá trị trên khổ 1.140px để trống gần chín trăm pixel
  bên phải, và sáu dòng như thế đẩy thẻ cao gấp mấy lần lượng chữ nó chứa. Đo
  trên màn đơn đặt hàng: đúng sáu dòng ấy chiếm 391px.

  auto-fit gấp chúng thành hai hoặc ba cột tuỳ khổ màn và tự về một cột dưới
  300px, nên không cần media query riêng. Khoảng cách ngang rộng hơn dọc để mắt
  còn tách được cột này với cột kia.
*/
.definition-list--rong {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--space-3) var(--space-6);
}

.definition-list__term {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.definition-list__value {
    font-size: var(--text-sm);
    color: var(--text-primary);
    word-break: break-word;
}

/* Bảng tổng tiền cuối chứng từ */
.totals {
    display: grid;
    gap: var(--space-2);
    margin-left: auto;
    width: min(340px, 100%);
}

.totals__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    font-size: var(--text-sm);
}

.totals__row--grand {
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
    font-size: var(--text-md);
    font-weight: 600;
}

.totals__label {
    color: var(--text-secondary);
}

.totals__value {
    font-variant-numeric: tabular-nums;
}

/* ── Chọn nhiều dòng ───────────────────────────────────────── */

/* Cột checkbox chỉ rộng vừa đủ và không co giãn theo nội dung bảng */
.col-check {
    width: 36px;
    padding-right: 0 !important;
}

.col-check input {
    width: 15px;
    height: 15px;
    accent-color: var(--accent);
    cursor: pointer;
    vertical-align: middle;
}

/*
 * Thanh thao tác thay chỗ hàng tiêu đề khi có dòng được chọn, nên người dùng
 * thấy ngay thao tác áp dụng cho phần vừa tích mà bảng không bị nhảy chỗ.
 */
.bulk-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    padding: var(--space-2) var(--space-5);
    background: var(--accent-soft);
    border-bottom: 1px solid var(--accent-border);
}

.bulk-bar[hidden] {
    display: none;
}

.bulk-bar__count {
    font-size: var(--text-sm);
    color: var(--accent);
}

.bulk-bar__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Cột thao tác cuối dòng: các nút chỉ hiện rõ khi rê vào dòng */
.row-actions {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    justify-content: flex-end;
    white-space: nowrap;
}

.row-actions .btn--ghost {
    opacity: 0.75;
}

.table tbody tr:hover .row-actions .btn--ghost {
    opacity: 1;
}

/* Nút bị chặn vì nghiệp vụ: vẫn thấy được nhưng không bấm được */
.row-actions__disabled {
    display: inline-flex;
    align-items: center;
    height: 28px;
    padding: 0 var(--space-3);
    font-size: var(--text-sm);
    color: var(--neutral-400);
    cursor: not-allowed;
}

/* ── Ảnh đại diện chữ cái ──────────────────────────────────── */

.avatar {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--accent);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
}

/* Ô ảnh sản phẩm; hiện khung rỗng khi sản phẩm chưa có ảnh */
.thumb {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    overflow: hidden;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--neutral-400);
}

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

.thumb svg {
    width: 18px;
    height: 18px;
}

/* Con số bấm được, trông như liên kết chứ không như nút bấm nặng nề */
.link-button {
    padding: 0;
    font: inherit;
    color: var(--accent);
    background: none;
    border: 0;
    border-bottom: 1px dashed currentColor;
    cursor: pointer;
}

.link-button:hover,
.link-button.is-open {
    color: var(--accent-hover);
    border-bottom-style: solid;
}

.link-button.is-loading {
    opacity: 0.5;
    cursor: progress;
}

.link-button.is-error {
    color: var(--danger);
}

/* Dòng sản phẩm bung ra trong cây danh mục: nền chìm hơn để không lẫn với danh mục */
.tree-row--product > td {
    background: var(--bg-raised);
}

/* Ảnh cỡ lớn cho trang chi tiết: đủ to để nhìn ra mặt hàng, vẫn không lấn tiêu đề */
.thumb--lg {
    width: 72px;
    height: 72px;
    border-radius: var(--radius-md);
}

.thumb--lg svg {
    width: 28px;
    height: 28px;
}

/* Ô đầu dòng gồm ảnh/avatar đứng cạnh phần chữ */
.media {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.media__body {
    min-width: 0;
}

/* ── Cây danh mục ──────────────────────────────────────────── */

/*
 * Thụt lề theo cấp bằng padding trên chính ô đầu tiên, nên vẫn là một bảng
 * phẳng - sắp xếp và phân trang không bị vỡ như khi lồng bảng vào nhau.
 */
.tree-cell {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.tree-cell__indent {
    flex-shrink: 0;
    align-self: stretch;
}

/* Nút thu/mở nhánh; ô giữ chỗ cùng kích thước để tên các dòng thẳng hàng */
.tree-toggle {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}

.tree-toggle:hover {
    background: var(--neutral-100);
    color: var(--text-primary);
}

.tree-toggle--leaf {
    cursor: default;
}

.tree-toggle__chevron {
    width: 12px;
    height: 12px;
    /* Mở: mũi tên chỉ xuống. Thu gọn: quay về chỉ sang phải. */
    transform: rotate(90deg);
    transition: transform var(--transition);
}

.tree-toggle--collapsed .tree-toggle__chevron {
    transform: rotate(0deg);
}

/* Tên danh mục và badge "Cấp gán sản phẩm" đứng cùng dòng, xuống dòng khi cột hẹp */
.tree-cell__body {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    min-width: 0;
}

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

/* Đoạn trùng từ khoá tìm kiếm: tô nền chứ không đổi cỡ chữ để dòng không nhảy */
.tree-cell__match {
    padding: 0 2px;
    border-radius: var(--radius-sm);
    background: var(--warning-soft);
    color: inherit;
    font-weight: 600;
}

/*
 * Dòng giữ lại chỉ để làm ngữ cảnh cho con cháu khớp bộ lọc: làm mờ để phân biệt
 * với dòng thực sự khớp, nhưng vẫn đọc được vì nó chính là đường dẫn của nhánh.
 */
.tree-row--context > td {
    opacity: 0.65;
}

/* Danh mục gốc đậm hơn để nhìn ra ngay các nhánh chính */
.tree-row--root .tree-cell__name {
    font-weight: 600;
}

.tree-row--inactive .tree-cell__name {
    color: var(--text-muted);
}

.level-chip {
    display: inline-flex;
    align-items: center;
    padding: 1px var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--neutral-100);
    color: var(--text-secondary);
    font-size: var(--text-xs);
    font-weight: 500;
    white-space: nowrap;
}

/* ── Dòng thời gian trạng thái ─────────────────────────────── */

.status-track {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.status-track__item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.status-track__dot {
    width: 8px;
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--neutral-300);
}

.status-track__item--done .status-track__dot {
    background: var(--success);
}

.status-track__item--done {
    color: var(--text-primary);
}

.status-track__arrow {
    color: var(--neutral-300);
}

/* ── Thanh mức lấp đầy ─────────────────────────────────────── */

.fill-bar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 116px;
}

.fill-bar__track {
    flex: 1;
    height: 6px;
    border-radius: var(--radius-pill);
    background: var(--neutral-200);
    overflow: hidden;
}

.fill-bar__value {
    display: block;
    height: 100%;
    border-radius: var(--radius-pill);
    background: var(--accent);
}

.fill-bar__label {
    font-size: var(--text-xs);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* ── Sơ đồ ô của một giàn ──────────────────────────────────── */

.shelf-map {
    display: grid;
    gap: var(--space-4);
}

/* Mỗi tầng là một dải ngang, nhãn tầng nằm bên trái như nhìn giàn thật */
.shelf-map__rack {
    display: grid;
    grid-template-columns: 84px minmax(0, 1fr);
    gap: var(--space-3);
    align-items: start;
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-app);
}

.shelf-map__rack-label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text-secondary);
    padding-top: var(--space-2);
}

.shelf-map__cells {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: var(--space-2);
}

/* Nút khoá nằm đè lên góc ô nên mỗi ô là một form định vị tương đối */
.cell-tile-form {
    position: relative;
}

.cell-tile {
    display: grid;
    gap: 2px;
    padding: var(--space-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-surface);
    text-align: center;
    transition: var(--transition);
}

.cell-tile:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-sm);
}

.cell-tile__code {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-primary);
}

.cell-tile__count {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.cell-tile--empty {
    border-style: dashed;
}

.cell-tile--filled {
    background: var(--success-soft);
    border-color: var(--success-border);
}

.cell-tile--filled .cell-tile__count {
    color: var(--success);
    font-weight: 600;
}

.cell-tile--blocked {
    background: var(--neutral-100);
    border-color: var(--border-strong);
    opacity: 0.75;
}

.cell-tile__lock {
    position: absolute;
    top: 2px;
    right: 2px;
    padding: 0 2px;
    border: 0;
    background: transparent;
    font-size: 11px;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: var(--transition);
}

.cell-tile-form:hover .cell-tile__lock,
.cell-tile__lock:focus-visible {
    opacity: 1;
}

.cell-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.cell-legend__item {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

.cell-chip {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    border: 1px solid var(--border);
}

.cell-chip--empty {
    border-style: dashed;
}

.cell-chip--filled {
    background: var(--success-soft);
    border-color: var(--success-border);
}

.cell-chip--blocked {
    background: var(--neutral-100);
    border-color: var(--border-strong);
}

/* ── Màn quét mã vào ô ─────────────────────────────────────── */

.scan-form {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Ô nhập to và rõ: nhân viên cầm máy quét, nhìn từ xa vẫn phải thấy mình gõ gì */
.scan-form__input {
    flex: 1 1 260px;
    font-family: var(--font-mono);
    font-size: var(--text-md);
    letter-spacing: 0.03em;
}

/* Hộp xác nhận viền dày để không lẫn với các thẻ nội dung khác trên trang */
.scan-confirm {
    border-width: 2px;
}

.scan-confirm--ready {
    border-color: var(--accent);
}

.scan-confirm--blocked {
    border-color: var(--danger-border);
    background: var(--danger-soft);
}

.scan-confirm .card__footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* Ô chọn phiếu nhập nằm ngang với nút sinh kiện */
.receipt-pick {
    display: flex;
    align-items: flex-end;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.receipt-pick .field {
    flex: 1 1 320px;
}

/* ══════════════════════════════════════════════════════════════
   Bộ khung dùng chung cho màn danh sách (mục 0 của bản kế hoạch)
   ══════════════════════════════════════════════════════════════ */

/* ── Dải chỉ số thu gọn được ───────────────────────────────── */

.scorecard {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/*
  Thanh nút nằm TRÊN dải chỉ số và canh phải: nút thu gọn là thao tác hiếm nên
  không được chiếm chỗ đọc, nhưng vẫn phải ở nơi mắt tìm tới khi cần.
  Thanh do app.js chèn vào nên khi chưa có JavaScript thì chỉ số hiện bình thường.
*/
.scorecard__bar {
    display: flex;
    justify-content: flex-end;
}

.scorecard__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px var(--space-2);
    font: inherit;
    font-size: var(--text-xs);
    color: var(--text-muted);
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.scorecard__toggle:hover {
    color: var(--text-primary);
    background: var(--neutral-100);
}

.scorecard__chevron {
    width: 10px;
    height: 7px;
    transition: transform 0.15s ease;
}

/* Mũi tên quay lên khi dải đang mở - hướng mũi tên là hướng sẽ đi khi bấm */
.scorecard.is-collapsed .scorecard__chevron {
    transform: rotate(-90deg);
}

.scorecard.is-collapsed .scorecard__body {
    display: none;
}

/* Con số bấm được: gạch chân chỉ khi trỏ vào, để dải không trông như một rừng liên kết */
.stat__value--link {
    color: inherit;
    text-decoration: none;
}

.stat__value--link:hover {
    text-decoration: underline;
}

/* ── Chip cho điều kiện lọc đang bật ───────────────────────── */

.chip-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border);
}

.chip-bar__label {
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--text-muted);
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 3px var(--space-2);
    font-size: var(--text-xs);
    color: var(--accent);
    text-decoration: none;
    background: var(--accent-soft);
    border: 1px solid var(--accent-border);
    border-radius: 999px;
}

.chip:hover {
    color: var(--danger);
    background: var(--danger-soft);
    border-color: var(--danger-border);
}

.chip__remove {
    width: 11px;
    height: 11px;
    flex-shrink: 0;
    opacity: 0.6;
}

.chip:hover .chip__remove {
    opacity: 1;
}

.chip-bar__clear {
    margin-left: auto;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

/* ── Bộ lọc bật/tắt ───────────────────────────────────────── */

.toggle-filter {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 var(--space-3);
    height: 32px;
    font-size: var(--text-sm);
    color: var(--text-secondary);
    background: var(--bg-surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    cursor: pointer;
    white-space: nowrap;
}

.toggle-filter:hover {
    border-color: var(--neutral-400);
}

.toggle-filter--active {
    color: var(--accent);
    background: var(--accent-soft);
    border-color: var(--accent-border);
}

/* ── Menu thao tác của một dòng ────────────────────────────── */

/*
  Cột thao tác luôn hẹp bằng nhau vì chỉ chứa một nút. Không dùng overflow:hidden
  ở ô hay ở hàng: menu bung ra ngoài biên ô và sẽ bị cắt mất.
*/
.col-actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

/*
  Cột thao tác trong bảng KHUNG CỐ ĐỊNH phải khai bề ngang thật.

  `width: 1%` ở trên là mẹo của table-layout: auto - trình duyệt hiểu là "co lại
  vừa nội dung". Dưới table-layout: fixed thì 1% đúng nghĩa một phần trăm: cột
  rộng 13px trong khi nút bên trong cần 61px, và cả bảng cuộn ngang 48px.

  Triệu chứng đánh lừa: bảng cuộn ngang trông y như bảng quá nhiều cột, nên người
  sửa đi thu hẹp các cột khác - mà thu bao nhiêu cũng vẫn thừa đúng 48px ấy.
*/
.table--co-dinh .col-actions {
    width: 70px;
}

.row-menu {
    position: relative;
    display: inline-block;
}

.row-menu__trigger::-webkit-details-marker {
    display: none;
}

.row-menu__trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    list-style: none;
    color: var(--text-muted);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.row-menu__trigger:hover,
.row-menu[open] > .row-menu__trigger {
    color: var(--text-primary);
    background: var(--neutral-100);
}

.row-menu__trigger svg {
    width: 15px;
    height: 15px;
}

.row-menu__list {
    position: absolute;
    right: 0;
    top: calc(100% + 2px);
    z-index: 40;
    min-width: 216px;
    padding: var(--space-1);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    text-align: left;
}

/*
  Dòng sát đáy màn hình: app.js gắn lớp này để menu mở LÊN TRÊN. Không có nó thì
  menu của dòng cuối bảng bị khung nhìn cắt mất và người dùng phải cuộn mù.
*/
.row-menu.is-flipped .row-menu__list {
    top: auto;
    bottom: calc(100% + 2px);
}

.row-menu__item {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    font: inherit;
    font-size: var(--text-sm);
    color: var(--text-primary);
    text-align: left;
    text-decoration: none;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.row-menu__item:hover {
    background: var(--neutral-100);
}

.row-menu__item--danger {
    color: var(--danger);
}

.row-menu__item--danger:hover {
    background: var(--danger-soft);
}

/*
  Hành động bị chặn: mờ và không bấm được, nhưng VẪN HIỆN kèm lý do. Nút mất tích
  khiến người dùng tưởng hệ thống thiếu tính năng; nút mờ có lý do thì trả lời
  luôn câu "vì sao tôi không xoá được".
*/
.row-menu__item--blocked {
    color: var(--text-muted);
    cursor: not-allowed;
}

.row-menu__item--blocked:hover {
    background: none;
}

/* Lý do in thành dòng nhỏ vì trỏ chuột không dùng được trên điện thoại */
.row-menu__reason {
    display: block;
    margin-top: 1px;
    font-size: var(--text-xs);
    line-height: 1.4;
    color: var(--text-muted);
    white-space: normal;
}

/* ── Chọn cột hiển thị ─────────────────────────────────────── */

.gear-panel__menu--right {
    width: min(260px, calc(100vw - var(--space-8)));
}

.column-picker__list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 320px;
    overflow-y: auto;
}

.column-picker__option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-sm);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.column-picker__option:hover {
    background: var(--neutral-100);
}

/*
  Ẩn cột làm ở tầng CSS: app.js gắn một lớp vào bảng, không phải sửa từng ô. Nhờ
  vậy bật/tắt là tức thời và không phải tải lại trang - tải lại thì mất vị trí
  cuộn và mất cả từ khoá đang gõ dở.
*/
.table--picked [data-col].is-hidden {
    display: none;
}

/* ── Nút sao chép mã ──────────────────────────────────────── */

.copy-code {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 0;
    font: inherit;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-muted);
    background: none;
    border: none;
    cursor: pointer;
}

.copy-code:hover {
    color: var(--accent);
}

.copy-code svg {
    width: 11px;
    height: 11px;
    opacity: 0;
    transition: opacity 0.12s ease;
}

.copy-code:hover svg {
    opacity: 0.7;
}

/* Đổi hẳn nhãn thành "Đã sao chép" thì mất mã đang xem, nên chỉ nháy màu xác nhận */
.copy-code.is-copied {
    color: var(--success);
}

/*
  Biến thể chỉ có biểu tượng, dùng khi bản thân cái mã đã là liên kết vào chi
  tiết - HTML không cho lồng <button> vào trong <a>, nên nút sao chép phải đứng
  riêng bên cạnh.

  Ở biến thể này biểu tượng luôn hiện mờ chứ không ẩn hẳn như biến thể có chữ:
  một nút vô hình rộng 11px thì không ai nhắm được vào để rê chuột lên.
*/
.copy-code--bare {
    margin-left: var(--space-1);
    vertical-align: middle;
}

.copy-code--bare svg {
    width: 12px;
    height: 12px;
    opacity: 0.4;
}

.copy-code--bare.is-copied svg {
    opacity: 1;
}

/* Vạch ngăn hai chiều lọc nhanh trên cùng một dãy tab */
.tabs__divider {
    width: 1px;
    height: 18px;
    margin: 0 var(--space-2);
    background: var(--border-strong);
    flex-shrink: 0;
    align-self: center;
}

/*
  Ô tích phụ nằm ngay dưới một trường khác trong bảng nhập liệu.

  Dùng cho hai lựa chọn "nhập theo thùng / bán theo thùng": chúng chỉ có nghĩa khi
  ô quy cách phía trên đã khai, nên phải nằm cùng một ô của bảng thay vì mở thêm
  hai cột luôn trống với đa số mặt hàng.
*/
.check-inline {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-top: 3px;
    font-size: var(--text-xs);
    color: var(--text-secondary);
    cursor: pointer;
}

/*
  Ô quét mã vạch trên form nhập hàng (chốt chặn thứ tư của quy tắc SP-10).

  Đứng cạnh nút "Thêm dòng" vì nó là cách thêm dòng nhanh hơn: người nhập kho quét
  tem thùng, hệ thống tự chọn mã hàng và quy cách, họ chỉ còn gõ số lượng.
*/
.scan-box {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1 1 260px;
    min-width: 0;
}

.scan-box .input {
    flex: 0 1 240px;
}

.scan-box__hint {
    font-size: var(--text-xs);
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.scan-box__hint.is-ok {
    color: var(--success);
}

.scan-box__hint.is-error {
    color: var(--danger);
}

/* ══════════════════════════════════════════════════════════════
   Bộ quy tắc hiển thị chung (mục "Quy tắc giao diện hiển thị")
   ══════════════════════════════════════════════════════════════ */

/*
  Ô trống (quy tắc UI-05).

  Ba cách viết sai đều đã từng có trong dự án này: để trắng (người dùng không
  biết là chưa khai hay bảng lỗi), ghi số 0 (nói "đã hết" thay vì "chưa có"), và
  in chữ "null" ra màn hình. Một dấu gạch nhạt trả lời đúng câu "chỗ này chưa có
  gì" mà không giả vờ là một giá trị.
*/
.blank {
    color: var(--neutral-400);
    user-select: none;
}

/*
  Ô trống nhưng BẤM ĐƯỢC.

  Khác .blank ở chỗ nó không chỉ nói "chưa có gì" mà còn dẫn tới việc phải làm:
  ô "Chưa gán" ở cột Nhà cung cấp mở ra đúng tập mặt hàng cùng cảnh để khai một
  lượt. Vẫn giữ màu nhạt để không tranh chỗ với dữ liệu thật, chỉ thêm gạch chân
  nét đứt - dấu hiệu duy nhất phân biệt nó với một ô trống thường.
*/
.link-muted {
    color: var(--neutral-400);
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}

.link-muted:hover {
    color: var(--accent);
    text-decoration: underline;
}

/*
  Tiền tệ (quy tắc UI-02): số căn phải, chữ số đều bề ngang, ký hiệu ₫ đứng SAU
  số và nhạt hơn số.

  Ký hiệu để nhạt vì nó lặp lại ở mọi dòng: cái mắt cần so sánh là các con số,
  không phải cái ký hiệu.
*/
.money {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.money__unit {
    margin-left: 2px;
    color: var(--text-muted);
    font-weight: 400;
}

/*
  Số tăng / giảm trong lịch sử biến động (quy tắc UI-10).

  Dấu + và − in ra thành chữ chứ không chỉ tô màu: người mù màu vẫn phải đọc
  được chiều của một bút toán, và bảng in ra giấy trắng đen cũng vậy.
*/
.delta {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
}

.delta--up {
    color: var(--success);
}

.delta--down {
    color: var(--danger);
}

/*
  Chênh lệch GIÁ MUA, nơi cao hơn là xấu.

  Không dùng lại .delta--up được: hai lớp trên mã hoá CHIỀU (tăng / giảm) và được
  tô theo nghĩa của tồn kho, nơi tăng là tin tốt nên nó màu xanh. Đem nguyên sang
  cột "đắt hơn nơi rẻ nhất bao nhiêu phần trăm" thì +15% hiện ra màu xanh lá -
  tô đúng luật cũ nhưng nói ngược hoàn toàn ý nghĩa.

  Lớp này mã hoá ĐÁNH GIÁ chứ không phải chiều, nên nó không bao giờ bị đem dùng
  nhầm chỗ như vậy nữa.
*/
.delta--worse {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
    color: var(--danger);
}

/*
  Khối tóm tắt đứng trước một bảng dài.

  Có mặt vì bảng đối chiếu giá trả lời được câu hỏi nhưng không trả lời NGAY:
  người mua hàng phải đọc sáu cột rồi tự trừ trong đầu. Hai câu ở đây nói thẳng
  kết luận, còn bảng bên dưới là chứng cứ cho ai muốn kiểm lại.
*/
.callout {
    display: grid;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--info-border);
    border-left: 3px solid var(--info);
    border-radius: var(--radius-sm);
    background: var(--info-soft);
    font-size: var(--text-sm);
}

.callout__note {
    color: var(--text-secondary);
}

/*
  ── Khối hướng dẫn đầu màn ───────────────────────────────────

  Dành cho những màn mà cái bảng không tự giải thích được: nhãn trạng thái do hệ
  thống đặt, cột số cần biết nguồn, hoặc thao tác có hệ quả mà nhìn vào không
  đoán ra. Người dùng mới cần đọc trước khi bấm; người dùng quen bấm một lần cho
  gọn lại.

  Dùng <details> thay vì tự viết đóng/mở bằng JS: trình duyệt lo phần trạng thái,
  bàn phím và trình đọc màn hình, và khối vẫn đọc được nguyên vẹn khi JS hỏng -
  đúng lúc người dùng cần lời hướng dẫn nhất.
*/
.help-panel > .card__body {
    display: grid;
    gap: var(--space-4);
    padding-top: 0;
}

.help-panel__summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-5);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
}

.help-panel__summary:hover {
    color: var(--text-primary);
}

.help-panel__label {
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
}

/*
  Ba bước, mỗi bước một cột trên màn rộng và xếp dọc trên màn hẹp.

  Đánh số bằng bộ đếm CSS chứ không gõ "1." "2." vào nội dung: chèn thêm một
  bước ở giữa thì số tự chạy lại, còn số gõ tay sẽ đứng im và bước 2 sẽ có hai
  cái cùng mang số 2.
*/
.howto {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--space-3);
    counter-reset: howto;
    list-style: none;
    margin: 0;
    padding: 0;
}

.howto > li {
    display: grid;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-raised);
    font-size: var(--text-sm);
    counter-increment: howto;
}

.howto > li > strong::before {
    content: counter(howto);
    display: inline-grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    margin-right: var(--space-2);
    border-radius: 50%;
    background: var(--accent);
    color: var(--text-inverse);
    font-size: var(--text-xs);
    font-weight: 700;
}

.howto > li > span {
    color: var(--text-secondary);
}

/*
  Bảng chú giải: nhãn bên trái, nghĩa bên phải.

  Nhãn dùng ĐÚNG thẻ badge như trong bảng, không phải một chấm màu mô phỏng lại.
  Người dùng đối chiếu bằng cách nhìn, nên hai chỗ phải giống nhau tuyệt đối -
  lệch một sắc độ là họ phải dừng lại tự hỏi có phải cùng một thứ không.
*/
.legend {
    display: grid;
    gap: var(--space-2);
    margin: 0;
}

.legend__item {
    display: grid;
    grid-template-columns: minmax(11rem, auto) 1fr;
    align-items: baseline;
    gap: var(--space-2) var(--space-4);
    font-size: var(--text-sm);
}

.legend__item > dt {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    flex-wrap: wrap;
}

.legend__item > dd {
    margin: 0;
    color: var(--text-secondary);
}

@media (max-width: 40rem) {
    .legend__item {
        grid-template-columns: 1fr;
        gap: var(--space-1);
    }
}

/*
  Dòng phụ dưới tên cột, nói nguồn của con số.

  Ba cột tiền đứng cạnh nhau thì tên cột không đủ tách chúng ra, mà viết tên dài
  cho đủ nghĩa lại làm vỡ bề rộng cột. Dòng phụ nhỏ hơn giải quyết cả hai.
*/
.th__hint {
    display: block;
    font-size: var(--text-xs);
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--text-secondary);
}

/*
  ── Danh sách nhóm, mỗi nhóm mở ra một bảng ──────────────────

  Dùng cho màn đề xuất báo giá: 76 nhà cung cấp, mỗi người một bảng mặt hàng dài
  tới trăm dòng. Bày phẳng thì trang thành 981 dòng và người dùng phải cuộn qua
  trọn bảng của người thứ nhất mới thấy người thứ hai; gấp lại thì cả danh sách
  đọc được trong một tầm mắt.

  Mỗi dòng phải quét được bằng MẮT chứ không phải đọc: tên bên trái, nhãn đếm bên
  phải, và không có câu văn nào - bảy mươi sáu câu xếp chồng lên nhau là một bức
  tường chữ, không phải một danh sách.
*/
.ncc-fold {
    border-bottom: 1px solid var(--border);
}

.ncc-fold:last-child {
    border-bottom: 0;
}

.ncc-fold__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5);
    cursor: pointer;
    list-style: none;
}

.ncc-fold__row::-webkit-details-marker {
    display: none;
}

.ncc-fold__row:hover {
    background: var(--bg-raised);
}

.ncc-fold[open] > .ncc-fold__row {
    background: var(--bg-raised);
    border-bottom: 1px solid var(--border);
}

.ncc-fold__chevron {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
    color: var(--text-muted);
    transition: transform var(--transition);
}

.ncc-fold[open] > .ncc-fold__row .ncc-fold__chevron {
    transform: rotate(90deg);
}

.ncc-fold__name {
    flex: 1;
    min-width: 0;
    display: grid;
}

/*
  Nhãn đếm không xuống dòng và không co lại: chúng là phần người dùng quét dọc để
  chọn chỗ làm trước, nên phải thẳng hàng nhau ở mọi dòng. Tên nhà cung cấp mới là
  thứ được phép co - nó đã có sẵn dấu ba chấm.
*/
.ncc-fold__tags {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-1);
    flex-shrink: 0;
}

.ncc-fold__panel {
    border-top: 1px solid var(--border);
}

.ncc-fold__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    padding: var(--space-2) var(--space-5);
    background: var(--accent-soft);
    border-bottom: 1px solid var(--accent-border);
}

.ncc-fold__loading {
    margin: 0;
    padding: var(--space-4) var(--space-5);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

/*
  ── Khối dòng hàng gấp lại được ──────────────────────────────

  Dùng cho những màn liệt kê nhiều nhóm, mỗi nhóm một bảng dài: gấp lại thì cả
  trang thành một danh sách nhóm đọc được trong một tầm mắt.

  Thanh bật/tắt trông như một hàng của bảng chứ không như một cái nút, vì nó nằm
  ngay trên bảng và nếu nổi hơn tiêu đề thẻ thì mắt sẽ đọc nó trước.
*/
.lines-fold__toggle {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    border-top: 1px solid var(--border);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--accent);
    cursor: pointer;
    user-select: none;
}

.lines-fold__toggle:hover {
    background: var(--bg-raised);
}

.lines-fold__meta {
    color: var(--text-secondary);
    font-weight: 400;
}

.lines-fold__meta::before {
    content: "·";
    margin-right: var(--space-2);
}

/*
  ── Chọn nhiều mục từ danh sách dài ──────────────────────────

  Chiều cao CỐ ĐỊNH rồi cuộn bên trong, chứ không giãn theo số mục: 108 thương
  hiệu trải hết ra sẽ đẩy nút Lưu xuống dưới hai màn hình, và người dùng không
  còn thấy mình đang ở trong một biểu mẫu nữa.
*/
.checklist {
    display: grid;
    gap: var(--space-2);
}

.checklist__items {
    max-height: 16rem;
    overflow-y: auto;
    display: grid;
    gap: 1px;
    padding: var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--bg-surface);
}

.checklist__item {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    cursor: pointer;
}

.checklist__item:hover {
    background: var(--bg-raised);
}

.checklist__item[hidden] {
    display: none;
}

.checklist__label {
    flex: 1;
}

.checklist__meta {
    color: var(--text-muted);
    font-size: var(--text-xs);
}

.checklist__empty,
.checklist__count {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-secondary);
}

/*
  Bản gọn của khối chọn nhiều: phần ĐANG GẮN đứng trên, kho chọn giấu sau nút.

  Danh sách đang gắn KHÔNG giới hạn chiều cao như `.checklist__items`: nó là câu
  trả lời chính của khối, cuộn nó đi là giấu mất thứ người ta vào đây để xem. Kho
  chọn thì vẫn chặn chiều cao vì nó dài cả trăm dòng.
*/
.picker {
    display: grid;
    gap: var(--space-2);
}

.picker__thanh {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.picker__count {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-secondary);
}

.picker__da-chon {
    display: grid;
    gap: 1px;
}

.picker__da-chon:empty {
    display: none;
}

.picker__kho {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--bg-surface);
}

.picker__items {
    max-height: 16rem;
    overflow-y: auto;
    display: grid;
    gap: 1px;
}

.picker__item {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    cursor: pointer;
}

.picker__item:hover {
    background: var(--bg-raised);
}

.picker__item[hidden] {
    display: none;
}

/* Dòng đang gắn có nền riêng để tách hẳn khỏi dòng trong kho chọn. */
.picker__da-chon .picker__item {
    background: var(--bg-raised);
}

.picker__label {
    flex: 1;
}

.picker__meta {
    color: var(--text-muted);
    font-size: var(--text-xs);
}

.picker__trong {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-secondary);
}

/* Cụm số đếm + nút ở góc phải tiêu đề thẻ. */
.card__header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-shrink: 0;
}

/*
  Dải trạng thái của ô sửa tại chỗ.

  Ba trạng thái, ba màu, và trạng thái LỖI phải khác hẳn hai cái kia: khi màn hình
  không có nút Lưu thì dòng này là thứ duy nhất trả lời "vừa rồi có ăn không". Để
  nó cùng màu với dòng gợi ý bên cạnh thì một lần lưu hỏng sẽ trôi qua không ai
  thấy, và người dùng rời trang tin rằng đã lưu.

  min-height giữ chỗ sẵn: không có nó thì mỗi lần dải hiện ra là cả khối bên dưới
  bị đẩy xuống một nhịp, ngay lúc người dùng đang định bấm ô kế tiếp.
*/
.inline-status {
    display: block;
    min-height: 1.25em;
    margin-top: var(--space-1);
    font-size: var(--text-xs);
    line-height: 1.25;
}

.inline-status--saving {
    color: var(--text-muted);
}

.inline-status--saved {
    color: var(--success);
}

.inline-status--error {
    color: var(--danger);
    font-weight: 600;
}

/*
  Nút hoàn tác nằm ngay trong dải, không phải một hộp thoại.

  Hộp thoại đòi người dùng dừng lại và trả lời; cái họ cần ở đây là một lối lùi
  nhỏ, bấm thì bấm, không bấm thì nó tự biến mất sau năm giây.
*/
.inline-status__undo {
    padding: 0;
    border: none;
    background: none;
    color: var(--accent);
    font: inherit;
    font-weight: 600;
    text-decoration: underline;
    cursor: pointer;
}

.inline-status__undo:hover {
    color: var(--accent-hover);
}

/*
  Ô sửa tại chỗ nằm trong danh sách nhãn - giá trị, nên nó phải chiếm hết bề ngang
  cột giá trị thay vì co theo nội dung.
*/
.definition-list__row .input,
.definition-list__row .select {
    width: 100%;
}

/* Giá so sánh gạch ngang - nó là giá cũ, không phải giá đang bán. */
.price-compare {
    text-decoration: line-through;
}

/*
  Nhóm badge trong một ô (quy tắc UI-11): tối đa hai cái, phần dư gộp thành
  "+N" và xem đủ ở màn chi tiết.

  Ba badge trở lên trên một dòng bảng thì mắt phải đọc cả ba mới biết dòng đó có
  vấn đề gì - mà bảng là để nhìn lướt.
*/
.badge-group {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex-wrap: nowrap;
}

.badge--more {
    background: var(--neutral-100);
    color: var(--text-secondary);
    border-color: var(--border);
    cursor: help;
}

/*
  Căn cứ đi kèm badge cảnh báo (quy tắc UI-09).

  Dùng chung .table__meta nhưng thêm biến thể này cho phần căn cứ của badge, để
  một badge cảnh báo không có căn cứ trở thành thứ nhìn ra ngay khi soát lại
  template: không có dòng nhỏ nào bên dưới là thiếu.
*/
.badge-basis {
    display: block;
    margin-top: 2px;
    font-size: var(--text-xs);
    color: var(--text-muted);
    white-space: nowrap;
}

/*
  Trường bị khoá (quy tắc UI-27).

  Vô hiệu hoá im lặng là cách tệ nhất: người dùng bấm vào ô, không gõ được, và
  không biết là hệ thống hỏng hay là cố ý. Ô khoá phải trông khác hẳn ô nhập và
  phải kèm câu nói vì sao.
*/
.input--locked {
    background: var(--neutral-50);
    color: var(--text-secondary);
    border-color: var(--border);
    cursor: not-allowed;
}

.field__locked {
    display: flex;
    align-items: flex-start;
    gap: var(--space-1);
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.field__locked svg {
    flex: none;
    width: 12px;
    height: 12px;
    margin-top: 2px;
}

/*
  Kết quả suy diễn hiện ngay dưới ô nhập (quy tắc UI-28).

  Nền nhấn nhạt để phân biệt với câu hướng dẫn: đây không phải lời khuyên mà là
  hệ quả của con số người dùng vừa gõ, và họ cần thấy nó TRƯỚC khi lưu.
*/
.field__derived {
    display: block;
    padding: var(--space-2) var(--space-3);
    border-left: 2px solid var(--accent);
    background: var(--accent-soft);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-size: var(--text-xs);
    color: var(--text-primary);
}

.field__derived--muted {
    border-left-color: var(--border-strong);
    background: var(--neutral-50);
    color: var(--text-secondary);
}

/*
  Bảng kiểm chặn lưu, đặt ở cột phải cạnh biểu mẫu (quy tắc UI-30).

  Cố ý KHÔNG dùng hộp thoại bật lên: hộp thoại hiện ra rồi mất, còn người nhập
  kho cần con số ngưỡng nằm trước mắt trong lúc họ sửa lại dòng hàng.
*/
.gate {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.gate__row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--bg-raised);
    font-size: var(--text-xs);
    line-height: 1.45;
}

.gate__row--ok {
    border-color: var(--success-border);
    background: var(--success-soft);
    color: var(--success);
}

.gate__row--blocked {
    border-color: var(--danger-border);
    background: var(--danger-soft);
    color: var(--danger);
}

.gate__mark {
    flex: none;
    font-weight: 700;
}

.gate__body strong {
    display: block;
}

.gate__empty {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

/*
  Thanh menu bên trái: đổi bề ngang bằng MỘT CÚ BẤM, không tự bung theo chuột.

  Bản trước làm hai việc bằng cách rê chuột: dải biểu tượng tự nở ra đè lên vùng
  nội dung, và danh sách con nổi thành một tấm bay sang phải. Cả hai đều là
  chuyển động không ai yêu cầu - con trỏ đi ngang qua trên đường tới chỗ khác là
  menu bung ra che mất bảng đang đọc, và nó xảy ra nhiều lần trong một phiên làm
  việc. Giờ mọi thay đổi bố cục đều có một cú bấm đứng sau nó.

  Danh sách con quay về đúng bản chất thẻ <details>: xổ xuống ngay dưới tên phân
  hệ, nằm trong lòng thanh menu. Nó có đẩy các nhóm bên dưới xuống một nhịp khi
  mở - đúng cái mà bản tấm nổi muốn tránh - nhưng nhịp đó do chính người dùng
  bấm ra nên không ai bị bất ngờ, khác hẳn với việc thanh menu tự nhảy khi con
  trỏ vô tình lướt qua.

  Không còn khai @media (hover: hover) nữa: cách mở giờ giống hệt nhau ở chuột
  và cảm ứng, nên không cần hai lối cư xử để đồng bộ.
*/
@media (min-width: 901px) {
    /*
      Giữ nguyên height: 100dvh. Từng đổi thành "height: auto; min-height: 100dvh"
      và làm hỏng đúng thứ người dùng báo: thanh bên là một ô của lưới, mà ô lưới
      thì mặc định GIÃN kín hàng (align-self: stretch). height:auto trên một ô
      đang giãn nghĩa là cao bằng cả hàng - tức cao bằng toàn bộ trang.
      position:sticky khi đó vô nghĩa: phần tử đã trải hết chiều cuộn, nên menu
      nằm ở đầu cái hộp khổng lồ đó và trôi mất khi cuộn xuống.

      align-self: start để ô không giãn nữa; cùng height: 100dvh cố định thì
      sticky bám đúng: menu đứng yên, chỉ nội dung bên phải cuộn.
    */
    .sidebar {
        height: 100dvh;
        align-self: start;
        transition: width var(--transition);
    }

    /* ── Dạng thu về dải biểu tượng ───────────────────────── */

    /*
      Ở dạng thu, CỘT LƯỚI hẹp lại còn 64px và thanh menu hẹp đúng bằng cột.

      Cột lưới giữ nguyên 64px KỂ CẢ lúc bung ra theo chuột - phần bung là một
      lớp đè lên nội dung, không đẩy nó. Đây chính là điều kiện để lối bung theo
      chuột dùng được: bản trước của tính năng này đẩy bảng bên phải sang ngang
      172px mỗi lần con trỏ đi lạc vào mép trái, và dòng người dùng đang đọc thì
      trượt đi mất. Đè thì nội dung đứng yên tuyệt đối, phần bị che là dải bên
      trái - nơi không có gì để đọc.
    */
    .app--nav-rail {
        grid-template-columns: var(--sidebar-rail) 1fr;
    }

    /* ── Tự ẩn: rê chuột vào mới bung ─────────────────────── */

    /*
      Lớp is-nav-peek do app.js gắn, không dùng thẳng :hover của CSS.

      Vì cần ĐỘ TRỄ. Mép trái màn hình là đường con trỏ đi qua liên tục - để với
      tới cột đầu của bảng, để bấm nút lùi của trình duyệt - và bung ngay lập tức
      biến mọi lần đi ngang thành một cú nhấp nháy. app.js chờ 140ms trước khi mở
      và 220ms trước khi đóng: đủ để lọc hết những lần chỉ lướt qua, mà người thật
      sự muốn mở thì không kịp thấy mình đang chờ.

      Hai mốc thời gian lệch nhau có chủ ý: mở khó hơn đóng. Bung nhầm thì che mất
      nội dung, còn đóng chậm một nhịp thì chẳng mất gì.
    */
    .app--nav-rail.is-nav-peek .sidebar {
        width: var(--sidebar-width);
        /* Trên mọi thứ có thể nổi lên từ nội dung: bảng chọn 60, menu dòng 40. */
        z-index: 80;
        box-shadow: var(--shadow-lg);
    }

    /* Bung ra thì trả lại đủ chữ - đúng những gì dạng thu vừa giấu đi. */
    .app--nav-rail.is-nav-peek .sidebar__store,
    .app--nav-rail.is-nav-peek .nav-item__label,
    .app--nav-rail.is-nav-peek .nav-group__label,
    .app--nav-rail.is-nav-peek .nav-group__chevron,
    .app--nav-rail.is-nav-peek .sidebar__user-text,
    .app--nav-rail.is-nav-peek .sidebar__user-chevron,
    .app--nav-rail.is-nav-peek .nav-pin__label {
        opacity: 1;
        visibility: visible;
    }

    /*
      Danh sách con và menu người dùng chỉ hiện lại khi thẻ <details> ĐANG MỞ.
      Thiếu vế [open] thì quy tắc này thắng cả quy tắc mặc định của trình duyệt và
      mọi nhóm đều xổ ra cùng lúc, kể cả nhóm người dùng đã tự gập.
    */
    .app--nav-rail.is-nav-peek .nav-group[open] > .nav-group__items {
        display: block;
    }

    .app--nav-rail.is-nav-peek .sidebar__user[open] .sidebar__user-menu {
        display: grid;
    }

    .app--nav-rail.is-nav-peek .sidebar__user-trigger {
        justify-content: flex-start;
        padding-left: var(--space-4);
        padding-right: var(--space-4);
    }

    .app--nav-rail.is-nav-peek .sidebar__logo {
        max-width: none;
    }

    .app--nav-rail .sidebar {
        width: var(--sidebar-rail);
    }

    /*
      Chữ tắt đi bằng visibility chứ không phải opacity.

      opacity: 0 vẫn để lại một khối chữ trong suốt tràn ra ngoài dải, và khối đó
      VẪN NHẬN chuột: người dùng bấm vào một ô của bảng nhưng trúng cái nhãn menu
      vô hình đang nằm đè lên. visibility: hidden bỏ hẳn phần tử khỏi phép thử
      chuột mà vẫn chuyển động mượt được cùng opacity.
    */
    .app--nav-rail .sidebar__store,
    .app--nav-rail .nav-item__label,
    .app--nav-rail .nav-group__label,
    .app--nav-rail .nav-group__chevron,
    .app--nav-rail .sidebar__user-text,
    .app--nav-rail .sidebar__user-chevron,
    .app--nav-rail .nav-pin__label {
        opacity: 0;
        visibility: hidden;
        transition: opacity var(--transition), visibility var(--transition);
    }

    /*
      Ở dạng thu, khối người dùng còn đúng cái avatar và menu của nó gập hẳn:
      cùng lý do với danh sách con bên dưới - dải chỉ rộng bằng một biểu tượng
      thì không có chỗ nào để xổ ra cả.
    */
    .app--nav-rail .sidebar__user-trigger {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
    }

    .app--nav-rail .sidebar__user-menu {
        display: none;
    }

    /*
      Ở dạng thu thì danh sách con không có chỗ để xổ, nên ẩn hẳn kể cả khi thẻ
      <details> đang mở sẵn theo trang hiện tại. display:none chứ không phải
      visibility: một danh sách vô hình mà vẫn chiếm chỗ sẽ đội dải biểu tượng
      cao lên hàng trăm điểm ảnh và đẩy nút thu gọn ra khỏi khung nhìn.

      Bấm vào nhóm lúc này thì app.js mở rộng thanh menu ra rồi mới xổ nhóm đó.
    */
    .app--nav-rail .nav-group__items {
        display: none;
    }

    /*
      Ảnh hiệu phải chịu bó theo bề ngang dải, không chỉ theo chiều cao. Logo hiện
      tại là ảnh nằm ngang: khai mỗi height rồi để width tự do thì nó rộng hơn
      64px và thò ra ngoài nền thanh menu khi đã thu.
    */
    .app--nav-rail .sidebar__logo {
        max-width: calc(var(--sidebar-rail) - 2 * var(--space-4));
    }

    /* ── Nút thu gọn / mở rộng ────────────────────────────── */

    .nav-pin {
        display: flex;
        align-items: center;
        gap: var(--space-3);
        width: 100%;
        margin-top: auto;
        padding: var(--space-3) var(--space-4);
        border: none;
        border-top: 1px solid rgba(255, 255, 255, 0.08);
        background: none;
        color: var(--text-sidebar);
        font: inherit;
        font-size: var(--text-sm);
        text-align: left;
        cursor: pointer;
        transition: background var(--transition), color var(--transition);
    }

    .nav-pin:hover {
        background: var(--bg-sidebar-hover);
        color: var(--text-inverse);
    }

    .nav-pin__icon {
        flex-shrink: 0;
        width: 18px;
        height: 18px;
        transition: transform var(--transition);
    }

    /* Mũi tên chỉ HƯỚNG SẼ ĐI: sang phải để mở rộng, sang trái để thu lại. */
    .app--nav-rail .nav-pin__icon {
        transform: rotate(180deg);
    }

    .nav-pin__label {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
}

/*
  Nút thu gọn chỉ có nghĩa ở nơi thanh menu là một cột dọc.

  Dưới 900px nó nằm ngang ở đầu trang, không thu vào đâu cả, nên nút ở đó là một
  cái nút không làm gì - tệ hơn cả việc không có nút.

  Bỏ vế "(hover: none)" của bản trước: hồi đó việc thu gọn phụ thuộc vào rê chuột
  nên trên cảm ứng nút thật sự vô dụng. Giờ nó là một cú bấm, mà bấm thì màn hình
  cảm ứng làm được - máy tính bảng dựng đứng ở khổ 912 là nơi cần thu gọn nhất
  chứ không phải nơi nên giấu nút đi.
*/
@media (max-width: 900px) {
    .nav-pin {
        display: none;
    }
}

/*
  Lối thoát khi bảng vẫn không vừa: cuộn TRONG LÒNG THẺ, không đẩy cả trang.

  Bật bằng một lớp do app.js gắn sau khi ĐO, chứ không bằng ngưỡng khổ màn hình.
  Ngưỡng cứng luôn sai ở một trong hai đầu: đặt 900px thì máy tính bảng dựng đứng
  912px rơi vào vùng không có lối thoát nào - đo được 26 trang cuộn ngang ở khổ
  đó, nhiều nhất trong mọi khổ; mà nâng lên 1200px thì laptop 1366 có bảng chín
  cột vừa thừa chỗ vẫn bị gắn thanh cuộn vô cớ. Bề ngang thật của một bảng phụ
  thuộc vào SỐ CỘT VÀ NỘI DUNG của chính nó, không phụ thuộc khổ màn hình, nên
  câu trả lời đúng chỉ có bằng cách đo từng bảng một.

  Đo cũng là cách giữ lại hàng tiêu đề dính. Hộp có overflow là một khung cuộn
  riêng, và position:sticky bên trong nó thì bám vào khung đó - tức không bám gì
  cả, vì khung cao đúng bằng nội dung. Chỉ những bảng thật sự phải cuộn mới mất
  tiêu đề dính; bảng vừa khổ - phần lớn màn hình, và là toàn bộ khổ 1920 - vẫn
  giữ nguyên.

  Cột thao tác dán vào mép phải để nó vẫn luôn nhìn thấy đúng như quy tắc UI-12
  đòi: đó là cột đầu tiên bị đẩy khuất khi bảng rộng hơn khung.
*/
.table-wrap--scroll {
    overflow-x: auto;
    /*
      Chặn cuộn lây sang thẻ cha. Không có dòng này thì trên máy tính bảng, vuốt
      trong lòng bảng tới mép rồi bị khung cuộn trả cử chỉ về cho trang, và trang
      nhảy ngang một nhịp giữa lúc đang đọc.
    */
    overscroll-behavior-x: contain;
}

.table-wrap--scroll .table thead th {
    position: static;
}

.table-wrap--scroll .table th.col-actions,
.table-wrap--scroll .table td.col-actions {
    position: sticky;
    right: 0;
    background: var(--bg-surface);
    box-shadow: -6px 0 8px -6px rgba(20, 26, 35, 0.18);
}

.table-wrap--scroll .table thead th.col-actions {
    background: var(--bg-raised);
}

.table-wrap--scroll .table tbody tr:hover td.col-actions {
    background: var(--neutral-25);
}

/*
  Lưới an toàn khi không có JavaScript.

  Dưới 900px thì bảng nhiều cột chắc chắn không vừa, nên bật cuộn sẵn thay vì đợi
  phép đo. Trên khổ rộng hơn mà JavaScript hỏng thì bảng lại tràn như cũ - chấp
  nhận được, vì cả ứng dụng này đã cần JavaScript cho ô chọn hàng và lưu tại chỗ.
*/
@media (max-width: 900px) {
    .table-wrap {
        overflow-x: auto;
    }

    .table thead th {
        position: static;
    }
}

/* ══════════════════════════════════════════════════════════════
   Ô chọn có tìm kiếm (gõ là lọc ngay, kiểu YouTube)
   ══════════════════════════════════════════════════════════════ */

/*
  Thay cho thẻ <select> của trình duyệt ở mọi chỗ phải chọn từ một danh sách.

  Lý do đổi: <select> chỉ nhảy tới mục theo chữ cái ĐẦU. Với 95 nhà cung cấp hay
  6.684 mã hàng thì cách đó vô dụng - người dùng phải biết trước tên bắt đầu bằng
  gì, mà tên thật lại là "Công ty TNHH TM DV & XNK Chi Bảo (mămmy, a2)". Gõ "chi
  bảo" phải ra được, và đó là thứ <select> không làm.

  Thẻ <select> gốc VẪN NẰM TRONG TRANG (chỉ bị ẩn) và vẫn là nơi giữ giá trị:
  nhờ vậy th:field, ràng buộc phía máy chủ, và mọi trình nghe sự kiện change đang
  có đều không phải sửa gì. Không có JavaScript thì <select> hiện lại như cũ và
  trang vẫn dùng được.
*/
.combo {
    position: relative;
    display: block;
    min-width: 0;
}

/* Ẩn <select> gốc nhưng KHÔNG dùng display:none - vẫn cần nó gửi giá trị lên */
.combo > select {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
}

.combo__trigger {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    min-height: 34px;
    padding: var(--space-2) var(--space-3);
    font: inherit;
    font-size: var(--text-sm);
    color: var(--text-primary);
    text-align: left;
    background: var(--bg-surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.combo__trigger:hover {
    border-color: var(--neutral-400);
}

.combo__trigger:focus-visible,
.combo.is-open > .combo__trigger {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
    outline: none;
}

/* Chưa chọn gì thì nhãn để nhạt như placeholder của ô nhập */
.combo__value {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.combo__value--empty {
    color: var(--text-muted);
}

.combo__chevron {
    flex: none;
    width: 11px;
    height: 11px;
    color: var(--text-muted);
    transition: transform var(--transition);
}

.combo.is-open .combo__chevron {
    transform: rotate(180deg);
}

.combo__trigger[aria-disabled="true"] {
    background: var(--neutral-50);
    color: var(--text-muted);
    cursor: not-allowed;
}

/* Ô chọn báo lỗi ràng buộc: viền đỏ giống .select--error của biểu mẫu */
.combo.is-invalid > .combo__trigger {
    border-color: var(--danger);
}

.combo__panel {
    position: absolute;
    z-index: 60;
    top: calc(100% + 3px);
    left: 0;
    /*
      Rộng hơn ô kích hoạt vì tên nhà cung cấp và tên hàng đều dài; nhưng không
      được vượt khung nhìn trên máy hẹp.
    */
    min-width: 100%;
    width: max-content;
    max-width: min(460px, calc(100vw - var(--space-8)));
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

/* Ô ở gần đáy khung nhìn thì bảng chọn mở LÊN TRÊN, giống menu thao tác của bảng */
.combo.is-flipped .combo__panel {
    top: auto;
    bottom: calc(100% + 3px);
}

.combo__search {
    position: relative;
    padding: var(--space-2);
    border-bottom: 1px solid var(--border);
}

.combo__search svg {
    position: absolute;
    top: 50%;
    left: calc(var(--space-2) + var(--space-2));
    width: 13px;
    height: 13px;
    color: var(--text-muted);
    transform: translateY(-50%);
    pointer-events: none;
}

.combo__input {
    width: 100%;
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-6);
    font: inherit;
    font-size: var(--text-sm);
    color: var(--text-primary);
    background: var(--bg-app);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
}

.combo__input:focus {
    background: var(--bg-surface);
    border-color: var(--accent-border);
    outline: none;
}

.combo__list {
    max-height: 288px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--space-1);
}

.combo__group {
    padding: var(--space-2) var(--space-2) var(--space-1);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.combo__option {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    cursor: pointer;
}

/*
  Mục đang được bàn phím trỏ tới và mục đang rê chuột dùng CÙNG một kiểu nền: hai
  kiểu khác nhau thì lúc vừa gõ vừa rê chuột sẽ thấy hai mục cùng sáng và không
  biết Enter sẽ chọn cái nào.
*/
.combo__option.is-active,
.combo__option:hover {
    background: var(--neutral-100);
}

.combo__option.is-selected {
    color: var(--accent);
    font-weight: 500;
}

.combo__option.is-selected::after {
    content: "✓";
    margin-left: auto;
    font-weight: 700;
}

.combo__label {
    flex: 1;
    min-width: 0;
}

/* Phần khớp với chữ đang gõ được tô đậm để mắt bắt được ngay vì sao dòng này hiện */
.combo__label mark {
    padding: 0;
    color: inherit;
    font-weight: 700;
    background: var(--warning-soft);
    border-radius: 2px;
}

.combo__hint {
    flex: none;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.combo__note {
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-xs);
    color: var(--text-muted);
    border-top: 1px solid var(--border);
    background: var(--bg-raised);
}

/* Ô tìm trong menu lọc của trang danh sách, dùng lại kiểu của ô tìm trong combo */
.filter-drop__search {
    position: relative;
    padding: var(--space-2);
    border-bottom: 1px solid var(--border);
}

.filter-drop__search svg {
    position: absolute;
    top: 50%;
    left: calc(var(--space-2) + var(--space-2));
    width: 13px;
    height: 13px;
    color: var(--text-muted);
    transform: translateY(-50%);
    pointer-events: none;
}

.filter-drop__search input {
    width: 100%;
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-6);
    font: inherit;
    font-size: var(--text-sm);
    background: var(--bg-app);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
}

.filter-drop__search input:focus {
    background: var(--bg-surface);
    border-color: var(--accent-border);
    outline: none;
}

.filter-drop__empty {
    padding: var(--space-3);
    font-size: var(--text-xs);
    color: var(--text-muted);
}

/* ==========================================================================
   Giỏ đặt hàng dính chân màn (quy tắc NCC-14)

   Dính ở đáy khung nhìn chứ không nằm cuối trang: bảng mặt hàng dài vài chục
   dòng, và một tổng tiền chỉ thấy được sau khi cuộn hết bảng thì không ai
   nhìn nó trước lúc bấm Tạo đơn.
   ========================================================================== */

.sticky-cart {
    position: sticky;
    bottom: 0;
    z-index: 20;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: flex-end;
    justify-content: space-between;
    margin-top: var(--space-4);
    /* Chừa chỗ bên phải cho nút Trợ lý (position:fixed, right:--space-5).
       Nó nổi cao hơn thanh này nên nút cuối cùng bị che mất chữ - đo trên
       trình duyệt thật thấy "Tạo 4 đơn đ…". Chỉ chừa từ khổ rộng trở lên;
       màn hẹp thì thanh tự xuống dòng nên không đụng nhau. */
    padding: var(--space-3) var(--space-4);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

@media (min-width: 900px) {
    .sticky-cart {
        padding-right: 190px;
    }
}

.sticky-cart__summary {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    font-size: var(--text-sm);
}

.sticky-cart__total {
    font-size: var(--text-lg);
    color: var(--text-primary);
}

.sticky-cart__fields {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* Nhãn nằm trên ô, cụm gọn lại vừa một hàng của thanh giỏ. */
.field--inline {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0;
}

.field--inline .field__label {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.input--sm,
.select--sm {
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-sm);
}

/*
  ══════════════════════════════════════════════════════════════
  Trợ lý hướng dẫn
  ══════════════════════════════════════════════════════════════

  Nơi DUY NHẤT còn chứa hướng dẫn sử dụng. Các màn hình đã gỡ hết đoạn chữ dạy
  việc; chúng chỉ còn nhãn, chữ mờ trong ô và thông báo lỗi.

  Góc dưới bên PHẢI vì mép trái đã là thanh menu, và đó là chỗ quy ước của loại
  nút này nên người dùng tìm thấy mà không cần ai chỉ.
*/
.tro-ly {
    position: fixed;
    right: var(--space-5);
    bottom: var(--space-5);
    z-index: 90;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-2);
}

.tro-ly__nut {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border: 0;
    border-radius: 999px;
    background: var(--accent);
    color: var(--text-inverse);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    box-shadow: var(--shadow-lg);
    cursor: pointer;
}

.tro-ly__nut:hover {
    filter: brightness(1.08);
}

.tro-ly__nut svg {
    width: 20px;
    height: 20px;
}

/* Mở rồi thì nút thu về đúng biểu tượng, nhường chỗ cho khung hội thoại. */
.tro-ly.is-open .tro-ly__nut-chu {
    display: none;
}

/*
  Khung cao tối đa 70% khung nhìn rồi cuộn bên trong.

  Không để nó cao theo nội dung: một nhánh sáu bước kèm ba lưu ý sẽ dài hơn màn
  hình và đẩy chính ô gõ câu hỏi ra ngoài tầm nhìn - tức là hỏi được một câu rồi
  mất luôn đường hỏi câu thứ hai.
*/
.tro-ly__khung {
    display: flex;
    flex-direction: column;
    width: min(400px, calc(100vw - 2 * var(--space-5)));
    max-height: min(70vh, 640px);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.tro-ly__khung[hidden] {
    display: none;
}

.tro-ly__dau {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border);
    background: var(--bg-raised);
}

.tro-ly__ten {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 600;
}

.tro-ly__phu {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-secondary);
}

.tro-ly__dong {
    padding: var(--space-1);
    border: 0;
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
}

.tro-ly__dong svg {
    width: 16px;
    height: 16px;
    display: block;
}

.tro-ly__than {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--space-4);
    display: grid;
    gap: var(--space-3);
    align-content: start;
    font-size: var(--text-sm);
}

.tro-ly__loi {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--bg-raised);
}

/* Câu của người dùng lệch sang phải và đổi màu: hai bên phải phân biệt được
   bằng liếc mắt, không phải bằng cách đọc. */
.tro-ly__loi--toi {
    justify-self: end;
    max-width: 85%;
    background: var(--accent-soft);
    border: 1px solid var(--accent-border);
}

.tro-ly__loi p {
    margin: 0;
}

.tro-ly__tieu-de {
    font-weight: 600;
    margin-bottom: var(--space-1) !important;
}

.tro-ly__tom-tat {
    color: var(--text-secondary);
    margin-bottom: var(--space-2) !important;
}

.tro-ly__buoc {
    margin: 0 0 var(--space-2);
    padding-left: var(--space-5);
    display: grid;
    gap: var(--space-1);
}

/*
  Lưu ý dùng dấu chấm tròn và màu cảnh báo, tách hẳn khỏi danh sách bước đánh số.
  Gộp chung một kiểu thì người dùng đọc lưu ý như một bước phải làm.
*/
.tro-ly__luu-y {
    margin: 0 0 var(--space-2);
    padding-left: var(--space-5);
    display: grid;
    gap: var(--space-1);
    color: var(--warning);
}

.tro-ly__nhan {
    margin-top: var(--space-2) !important;
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
}

.tro-ly__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-top: var(--space-1);
}

.tro-ly__chip {
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--bg-surface);
    color: var(--text-primary);
    font: inherit;
    font-size: var(--text-xs);
    text-align: left;
    cursor: pointer;
}

.tro-ly__chip:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.tro-ly__di {
    margin-top: var(--space-2);
}

.tro-ly__cho {
    color: var(--text-muted);
}

/*
  Nói rõ câu trả lời tìm ra bằng cách nào.

  Có mặt vì hai lối đoán ý định cho chất lượng khác nhau rõ rệt, và khi trợ lý
  dẫn sai đường thì đây là manh mối đầu tiên: "Khớp từ khoá" nghĩa là không gọi
  được mô hình, tức có gì đó hỏng ở mạng hay ở khoá chứ không phải ở nội dung.
*/
.tro-ly__nguon {
    margin-top: var(--space-2) !important;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.tro-ly__gui {
    display: flex;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border);
    background: var(--bg-raised);
}

.tro-ly__gui .input {
    flex: 1;
}

/* Trên điện thoại thì khung chiếm gần trọn màn, nút chỉ còn biểu tượng. */
@media (max-width: 560px) {
    .tro-ly {
        right: var(--space-3);
        bottom: var(--space-3);
    }

    .tro-ly__nut-chu {
        display: none;
    }

    .tro-ly__khung {
        max-height: 80vh;
    }
}

/*
  ══════════════════════════════════════════════════════════════
  Sáu lớp bị dùng mà chưa từng được khai
  ══════════════════════════════════════════════════════════════

  Tìm ra bằng một lượt đối chiếu mọi lớp trong tệp giao diện với mọi lớp trong
  tệp CSS. Chúng không gây lỗi ở đâu cả - trình duyệt bỏ qua lớp không có định
  nghĩa - nên phần tử mang chúng cứ hiện ở dạng mặc định và không ai để ý: một
  cái nút xám của trình duyệt, một dòng chữ đúng cỡ chữ gốc.

  Bài học đã ghi vào chú thích của màn Đề xuất bổ sung: trước khi gõ một tên lớp,
  grep xem nó có thật không.
*/

/* Cỡ chữ, đứng cạnh các tiện ích .text-* sẵn có. */
.text-sm {
    font-size: var(--text-sm);
}

.text-xs {
    font-size: var(--text-xs);
}

/*
  Nút sao chép mã.

  Chữ "Sao chép" chỉ hiện cho trình đọc màn hình; trên màn hình nó là một biểu
  tượng nhỏ mờ, rõ lên khi rê vào. Bày chữ ra thì mỗi dòng bảng cõng thêm tám ký
  tự lặp lại y hệt nhau, và mắt phải lọc chúng để đọc được cái mã.
*/
.copy-code__btn {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text-muted);
    font-size: 0;
    cursor: pointer;
    opacity: 0.45;
    transition: opacity var(--transition), color var(--transition);
}

.copy-code__btn::before {
    content: "⧉";
    font-size: 13px;
    line-height: 16px;
}

.copy-code__btn:hover,
.copy-code:hover .copy-code__btn {
    opacity: 1;
    color: var(--accent);
}

/* Nháy xanh khi đã chép - app.js gắn lớp này rồi gỡ sau 1,2 giây. */
.copy-code__btn.is-copied {
    opacity: 1;
    color: var(--success);
}

.sticky-cart__count {
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

/*
  Hàng nút cuối biểu mẫu, dùng cho biểu mẫu KHÔNG nằm trong thẻ card - nơi đã có
  .card__footer làm việc này.
*/
.form-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding-top: var(--space-2);
}

/* ==========================================================================
   Hành động bị chặn (quy ước §6)

   Vẫn hiện nhưng mờ và kèm câu nêu lý do ở thuộc tính title, thay vì ẩn đi.
   Ẩn thì người dùng tưởng hệ thống thiếu chức năng và đi tìm ở chỗ khác.
   ========================================================================== */

.is-disabled {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: auto;
}

/* ==========================================================================
   Khối khuyến mại trên dòng báo giá

   Hai nhánh loại trừ nhau, và dòng phụ hàng tặng thụt vào một bậc để đọc ra
   ngay là nó phụ thuộc dòng trên chứ không phải một mục ngang hàng.
   ========================================================================== */

.promo-branch {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px dashed var(--border);
}

.promo-branch[hidden] {
    display: none;
}

.promo-branch__row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.promo-branch__row .input {
    max-width: 4.5rem;
}

.promo-branch__label {
    font-size: var(--text-xs);
    color: var(--text-muted);
    white-space: nowrap;
}

.promo-branch__hint {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

/* Dòng phụ: thụt đầu dòng, có vạch dẫn để mắt nối được về dòng cha. */
.promo-subline {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-2);
    padding-left: var(--space-2);
    border-left: 2px solid var(--accent-border, var(--border-strong));
}

.promo-subline[hidden] {
    display: none;
}

.promo-subline__marker {
    color: var(--text-muted);
    line-height: 1.4;
}

.promo-subline__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    flex: 1;
}

/* Dòng phụ ở bảng chỉ đọc: cùng cách đọc, khác chỗ dùng. */
.table__subrow > td {
    padding-top: 0;
    border-top: 0;
    background: var(--bg-app);
}

.table__subrow .table__subrow-body {
    display: flex;
    gap: var(--space-2);
    padding-left: var(--space-5);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

/* ── Hộp khai nhanh ─────────────────────────────────────────────
   Dùng cho việc khai một thứ nhỏ mà KHÔNG rời trang đang mở: quy cách
   đóng gói khai giữa lúc lập báo giá hoặc phiếu nhập. Rời trang ở hai
   biểu mẫu đó là mất trắng mọi dòng đang gõ, vì chúng chưa có lưu nháp.

   Không phải hộp thoại xác nhận: hộp xác nhận hỏi một câu rồi biến mất,
   còn cái này nhận dữ liệu và phải hiện được lỗi tại chỗ để người dùng
   sửa mà không mất gì. */
.modal {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    background: rgba(15, 23, 42, 0.45);
}

.modal__panel {
    width: min(420px, 100%);
    max-height: 90vh;
    overflow-y: auto;
    padding: var(--space-5);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.modal__title {
    margin: 0 0 var(--space-2);
    font-size: var(--text-lg);
    font-weight: 600;
}

.modal__hint {
    margin: 0 0 var(--space-4);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.modal__actions {
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
    margin-top: var(--space-4);
}

/* ── Thanh tiến độ trong ô bảng ─────────────────────────────────
   Đi KÈM con số, không thay nó: một thanh màu cho biết "gần xong"
   nhưng không cho biết còn thiếu bao nhiêu hộp, mà đó mới là con số
   người mua hàng cầm đi gọi nhà cung cấp. */
.tien-do {
    display: block;
    width: 100%;
    height: 4px;
    margin-top: var(--space-1);
    background: var(--border);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.tien-do__thanh {
    display: block;
    height: 100%;
    background: var(--success-border);
    border-radius: var(--radius-pill);
}

/* ── Chip tag sản phẩm (đặc tả 12 §A2) ──────────────────────────
   Bảng màu CỐ ĐỊNH, tám sắc, ánh xạ 1-1 với enum TagColor.

   Không dùng thuộc tính style nội tuyến mang mã màu từ cơ sở dữ liệu:
   như thế là để một trường người dùng sửa được chảy thẳng vào CSS, và
   cũng khiến chế độ tương phản cao sau này không có chỗ can thiệp. */
.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    max-width: 100%;
    padding: 2px var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tag-chip--xanh-duong {
    background: #e8f1fe;
    border-color: #bcd8fb;
    color: #1149a3;
}

.tag-chip--xanh-la {
    background: #e6f5ed;
    border-color: #b3e0c8;
    color: #106036;
}

.tag-chip--do {
    background: #fdecec;
    border-color: #f6c4c4;
    color: #a01f1f;
}

.tag-chip--cam {
    background: #fdf1e6;
    border-color: #f7cfa8;
    color: #9a5109;
}

.tag-chip--vang {
    background: #fdf8e2;
    border-color: #f0e2a4;
    color: #7d6205;
}

.tag-chip--tim {
    background: #f1ecfd;
    border-color: #d6c8f7;
    color: #56319e;
}

.tag-chip--hong {
    background: #fdecf4;
    border-color: #f6c4dc;
    color: #9e2a63;
}

.tag-chip--xam {
    background: var(--neutral-100);
    border-color: var(--neutral-200);
    color: var(--neutral-700);
}

/* Tên tag bấm được (dẫn về danh sách lọc theo tag) nhưng phải giữ màu chữ của
   chip - màu liên kết chung sẽ phá tông màu từng nhóm tag. Gạch chân khi rê
   chuột là đủ dấu hiệu bấm được. */
.tag-chip a {
    color: inherit;
    text-decoration: none;
}

.tag-chip a:hover {
    text-decoration: underline;
}

/* Nút × trên chip: là nút gửi biểu mẫu thật, không phải trang trí. */
.tag-chip__remove {
    display: inline-flex;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font-size: var(--text-sm);
    line-height: 1;
    cursor: pointer;
    opacity: 0.55;
}

.tag-chip__remove:hover {
    opacity: 1;
}

.tag-tray {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* ── Bộ ảnh sản phẩm (đặc tả 12 §A2) ────────────────────────────
   Lưới ô vuông tự xuống dòng. Ô cuối là ô "+ thêm ảnh" và biến mất
   khi đã chạm trần mười tấm - chỗ trống biến mất chính là câu trả
   lời cho "vì sao tôi không thêm được nữa", trước cả khi bấm. */
.anh-bo {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: var(--space-3);
}

.anh-o {
    position: relative;
    aspect-ratio: 1 / 1;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-raised);
    overflow: hidden;
}

.anh-o img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Viền nhấn cho ảnh đại diện: nhìn cả lưới là thấy ngay tấm nào đang
   đại diện, không phải rà từng dấu sao. */
.anh-o--dai-dien {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
}

.anh-o__sao {
    position: absolute;
    top: var(--space-1);
    left: var(--space-1);
    padding: 1px var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: var(--text-inverse);
    font-size: var(--text-xs);
    line-height: 1.5;
}

/* Thanh thao tác nằm đè đáy ô, hiện thường trực chứ không đợi trỏ
   chuột: trên điện thoại không có thao tác trỏ chuột nào cả. */
.anh-o__thao-tac {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: center;
    gap: var(--space-1);
    padding: 2px;
    background: rgba(20, 26, 35, 0.62);
}

.anh-o__nut {
    padding: 1px var(--space-2);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text-inverse);
    font-size: var(--text-xs);
    line-height: 1.5;
    cursor: pointer;
}

.anh-o__nut:hover {
    background: rgba(255, 255, 255, 0.22);
}

.anh-o--them {
    display: flex;
    align-items: center;
    justify-content: center;
    border-style: dashed;
    color: var(--text-muted);
    cursor: pointer;
}

.anh-o--them:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* Ô đang được kéo: mờ đi để người dùng thấy mình đang cầm tấm nào. */
.anh-o--dang-keo {
    opacity: 0.4;
}

/* ── Sơ đồ kho (đặc tả 12 §6) ───────────────────────────────────
   Lưới ô vuông, KHÔNG phải bảng. Một bảng bắt mắt đọc theo dòng
   rồi mới ra vị trí; sơ đồ thì vị trí trên màn chính là vị trí
   trong kho, nên người vận hành nhìn một cái là biết đi tới đâu. */
.so-do {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    overflow-x: auto;
}

.so-do__hang {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.so-do__tang {
    flex: 0 0 auto;
    width: 2.2rem;
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: 600;
    text-align: right;
}

.so-do__o {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 46px;
    height: 46px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--neutral-50);
    color: var(--text-muted);
    font-size: var(--text-xs);
    text-decoration: none;
}

.so-do__o:hover {
    border-color: var(--accent);
}

/* Bậc màu theo mức lấp đầy. Dùng nền đặc dần chứ không đổi tông
   màu: đổi tông thì người đọc phải nhớ bảng tra, còn đặc dần thì
   "đậm hơn = đầy hơn" tự hiểu. */
.so-do__o--trong {
    background: var(--neutral-0);
}

.so-do__o--it {
    background: #e8f1fe;
    border-color: #bcd8fb;
    color: #1149a3;
}

.so-do__o--vua {
    background: #bcd8fb;
    border-color: #8fbdf6;
    color: #0d3c86;
}

.so-do__o--gan-day {
    background: #fdf1e6;
    border-color: #f7cfa8;
    color: #9a5109;
}

.so-do__o--day {
    background: #fdecec;
    border-color: #f6c4c4;
    color: #a01f1f;
}

/* Ô chưa khai: mờ và KHÔNG bấm được. Bỏ hẳn ô đi thì cả hàng lệch
   một cột và sơ đồ không còn khớp với kệ thật. */
.so-do__o--khuyet {
    border-style: dashed;
    background: transparent;
    opacity: 0.45;
}

/* Ô có mức riêng - ngoại lệ, phải nhìn thấy được mà không cần trỏ
   chuột vào. Dấu chấm ở góc chứ không đổi màu nền: màu nền đã
   dùng để nói mức lấp đầy rồi. */
.so-do__o--rieng::after {
    content: "";
    position: absolute;
    top: 3px;
    right: 3px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--warning);
}

.so-do__o--sang {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.so-do__chu-giai {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    color: var(--text-muted);
    font-size: var(--text-xs);
}

.so-do__mau {
    display: inline-block;
    width: 14px;
    height: 14px;
    margin-left: var(--space-3);
    border: 1px solid var(--border);
    border-radius: 3px;
    vertical-align: middle;
}

.so-do__mau--trong { background: var(--neutral-0); }
.so-do__mau--it { background: #e8f1fe; border-color: #bcd8fb; }
.so-do__mau--vua { background: #bcd8fb; border-color: #8fbdf6; }
.so-do__mau--gan-day { background: #fdf1e6; border-color: #f7cfa8; }
.so-do__mau--day { background: #fdecec; border-color: #f6c4c4; }
.so-do__mau--rieng { background: var(--warning); border-color: var(--warning); }

/* ── Khay nội dung ô ───────────────────────────────────────────
   Trượt vào từ phải, đè lên trang. Không đẩy nội dung sang trái:
   đẩy thì cả lưới sơ đồ nhảy vị trí đúng lúc người dùng vừa chỉ
   tay vào một ô, và họ mất chỗ đang nhìn. */
.khay-o {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    width: min(420px, 100vw);
    background: var(--bg-surface);
    border-left: 1px solid var(--border);
    box-shadow: -8px 0 24px rgba(20, 26, 35, 0.12);
    overflow-y: auto;
}

.khay-o[hidden] {
    display: none;
}

.khay-o__dau {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4);
    border-bottom: 1px solid var(--border);
}

.khay-o__noi-dung {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-4);
}

.khay-o__kien {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-3);
}

.khay-o__kien-dau {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

/* Nút "Gỡ khỏi ô" của cả kiện: đẩy về mép phải của dòng đầu, tách hẳn khỏi
   cụm mã kiện · trạng thái · hạn để không ai bấm nhầm nó khi định đọc. */
.khay-o__go {
    margin-left: auto;
}

/* Nút × gỡ một dòng hàng lẻ. Nhỏ hơn nút của màn cầm tay (44px là ngưỡng ngón
   tay đeo găng) vì đây là chuột trên máy bàn, nhưng vẫn phải to hơn chữ trong
   bảng - một dấu × cỡ chữ 12px thì trỏ chuột run một cái là trượt. */
.khay-o__go-dong {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--text-sm);
    height: 24px;
    line-height: 1;
    width: 24px;
}

.khay-o__go-dong:hover {
    border-color: var(--danger);
    color: var(--danger);
}

/* Bảng gọn dùng trong khay và trong màn cất hàng: cùng .table
   nhưng thưa hơn một bậc, vì chúng nằm trong khung hẹp. */
.table--compact th,
.table--compact td {
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-xs);
}

/* ── Bảng dòng hàng hiện TÊN ĐẦY ĐỦ ─────────────────────────────
   Ô chọn mặt hàng bình thường cắt tên bằng dấu ba chấm
   (.combo__value { text-overflow: ellipsis }). Trên bảng soạn báo
   giá thì không được: tên hàng mẹ và bé dài, và phần phân biệt hai
   mã lại nằm ở ĐUÔI - "Cháo Tươi Sài Gòn Baby Cá Hồi Và Cải Bó Xôi"
   với "…Cua Bắp Cà Rốt" chỉ khác nhau ở chỗ dấu ba chấm vừa cắt đi.
   Người mua hàng chọn nhầm mã mà không có cách nào nhận ra.

   Nút chọn khai min-height chứ không phải height nên nó tự cao lên
   theo số dòng chữ, không đè lên hàng dưới. */
.table--ten-day-du .combo__value {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    line-height: 1.35;
}

/* Nút chọn thôi căn giữa theo chiều dọc khi nhãn xuống hai dòng -
   căn giữa làm mũi tên xổ xuống trôi ra giữa khối chữ. */
.table--ten-day-du .combo__trigger {
    align-items: flex-start;
}

.table--ten-day-du .combo__chevron {
    margin-top: 3px;
}


/* ══ Thanh mức lấp đầy (màn Cấu hình kho, thiết kế 04/08) ══════════════════════

   Con số % một mình không đọc nhanh bằng một vệt dài ngắn khi bảng có chục dòng
   - mắt so vệt, không so số. Vệt + số đi cùng nhau: vệt để so, số để nói chính
   xác. */
.muc-day {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    white-space: nowrap;
}

.muc-day__ray {
    width: 90px;
    height: 6px;
    flex: none;
    border-radius: 3px;
    background: var(--border);
    overflow: hidden;
}

/*
  display:block là BẮT BUỘC, không phải trang trí.

  Phần tử này là <span> nằm trong .muc-day__ray - mà .muc-day__ray tuy có
  width/height nhưng KHÔNG phải flex container, nên con của nó giữ nguyên
  display:inline. Phần tử inline bỏ qua cả width lẫn height: thanh vẽ ra rộng
  0px, cao 0px, ở mọi mức phần trăm. Đo được 12/08 trên ba màn đang dùng khối
  này: getBoundingClientRect().width = 0 dù style ghi "width: 63%" và màu nền
  đúng là xanh. Người dùng chỉ thấy cái rãnh xám và tưởng thanh hỏng.
*/
.muc-day__nen {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: var(--accent);
    /* Đổi mượt khi số liệu đổi sau một lần nạp - mắt bắt được cái gì vừa nhúc nhích. */
    transition: width 240ms ease;
}

/*
  Bậc màu theo mức đầy, cùng ngưỡng với sơ đồ kho (>90 đầy · ≥70 gần đầy).

  Tên lớp KHÔNG có hai gạch dưới, dù khối này theo lối BEM ở mọi chỗ khác.
  Lý do nằm ở Thymeleaf: nó đọc `__...__` trong một biểu thức là cú pháp TIỀN XỬ
  LÝ, nên "muc-day__nen--day" viết trong th:classappend làm cả trang nổ với câu
  "Could not parse as expression" - đã trả giá đúng hai lần trong ngày 12/08.
  Chọn tên không dấu gạch đôi là cách duy nhất giữ được logic bậc màu trong
  template mà không phải chẻ ra ba nhánh th:if.
*/
.muc-day__nen.muc-day--gan-day {
    background: var(--warning);
}

.muc-day__nen.muc-day--day {
    background: var(--danger);
}


/* ══ Khu trong kho: mỗi khu một khối đóng/mở (màn chi tiết kho, 05/08) ═════════

   Giàn kệ nằm TRONG từng khu, nên mỗi khu là một <details> có summary trông như
   một hàng bảng (cùng khuôn .ncc-fold): mã + tên bên trái, dòng hàng và con số ở
   giữa, trạng thái + menu thao tác bên phải. Menu (⋯) nằm ngay trong summary để
   khỏi mở khu ra mới sửa được khu — app.js (initKhuGianKe) chặn click của vùng
   đó khỏi lật panel. */
.khu-panel {
    border-top: 1px solid var(--border);
}

.khu-panel__summary {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-5);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.khu-panel__summary::-webkit-details-marker {
    display: none;
}

.khu-panel__summary:hover {
    background: var(--bg-raised);
}

.khu-panel[open] > .khu-panel__summary {
    background: var(--bg-raised);
    border-bottom: 1px solid var(--border);
}

.khu-panel__chevron {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
    color: var(--text-muted);
    transition: transform var(--transition);
}

.khu-panel[open] > .khu-panel__summary .khu-panel__chevron {
    transform: rotate(90deg);
}

/* Mỗi "ô" của summary xếp dòng chính trên dòng phụ, như một ô của bảng. */
.khu-panel__cell {
    display: grid;
    min-width: 0;
}

.khu-panel__cell--chinh {
    flex: 1 1 10rem;
}

/* Menu thao tác không co: nó là lối vào Sửa/Ngừng dùng của khu. */
.khu-panel__actions {
    flex-shrink: 0;
}

/* Màn hẹp: giữ mã, trạng thái và menu — hai cột chữ giữa nhường chỗ trước,
   thông tin của chúng vẫn đọc được sau khi mở khu ra. */
@media (max-width: 720px) {
    .khu-panel__cell--phu {
        display: none;
    }
}

.khu-panel__ghi-chu {
    margin: 0;
    padding: var(--space-2) var(--space-5) 0;
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.khu-panel__trong {
    margin: 0;
    padding: var(--space-3) var(--space-5);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.khu-panel__chan {
    padding: var(--space-2) var(--space-5) var(--space-3);
}

/* ── Xem ảnh phóng to ────────────────────────────────────────
   Ô hình nhỏ 36px không đủ để nhìn con thú bông có đúng màu không, mà đường
   dẫn ảnh tải lên lại nằm ngoài thư mục tĩnh (chỉ mở được sau khi đăng nhập)
   nên mở tab mới là rơi ra khỏi ngữ cảnh đang làm. Khối này phóng ảnh ngay
   tại chỗ, đóng lại là về đúng dòng vừa đứng.

   z-index 300: cao hơn .modal (200) vì người dùng mở được ảnh từ TRONG một
   hộp thoại; thấp hơn thì ảnh chui xuống dưới lớp phủ của hộp thoại đó. */
.xem-anh {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    background: rgba(15, 23, 42, 0.72);
}

/* Khung BÓ THEO ẢNH thay vì cố định bề ngang: ảnh dọc 240px mà khung 860px
   thì hai bên là hai mảng trắng lớn hơn cả tấm ảnh, và mắt không biết nhìn
   vào đâu. Chặn dưới 360px để tiêu đề với nút đóng còn chỗ đứng. */
.xem-anh__khung {
    display: flex;
    flex-direction: column;
    width: auto;
    min-width: min(360px, 100%);
    max-width: min(920px, 100%);
    max-height: 92vh;
    overflow: hidden;
    background: var(--bg-surface);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.xem-anh__dau {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border);
}

.xem-anh__ten {
    flex: 1;
    min-width: 0;
    margin: 0;
    overflow: hidden;
    font-size: var(--text-base);
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.xem-anh__dem {
    flex-shrink: 0;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.xem-anh__dong {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    font-size: 20px;
    line-height: 1;
    color: var(--text-secondary);
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.xem-anh__dong:hover {
    background: var(--bg-raised);
}

/* Thân co giãn còn ảnh giới hạn theo cả hai chiều: ảnh dọc cao 4000px cũng
   không đẩy nút đóng ra khỏi màn hình. */
.xem-anh__than {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
    padding: var(--space-4);
    background: var(--bg-raised);
}

/* Chỉ bộ nhiều ảnh mới chừa lề hai bên cho nút lật. Chừa sẵn cho cả ảnh lẻ
   là mỗi lần xem một tấm đều thấy hai khoảng trống không ai dùng tới. */
.xem-anh--nhieu .xem-anh__than {
    padding-left: 56px;
    padding-right: 56px;
}

.xem-anh__anh {
    max-width: 100%;
    max-height: calc(92vh - 120px);
    object-fit: contain;
}

/* Nút lật đè lên ảnh, chỉ hiện khi bộ có từ hai tấm trở lên. */
.xem-anh__nut {
    position: absolute;
    top: 50%;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    font-size: 22px;
    line-height: 1;
    color: var(--text-primary);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-sm);
    transform: translateY(-50%);
    cursor: pointer;
}

.xem-anh__nut--truoc {
    left: var(--space-3);
}

.xem-anh__nut--sau {
    right: var(--space-3);
}

.xem-anh__nut:hover {
    background: var(--bg-raised);
}

/* Ô hình nhỏ bấm được: con trỏ và viền nhấn nói rằng đây không phải hình trang trí. */
.thumb--bam,
.anh-o__mo {
    cursor: zoom-in;
}

.thumb--bam:hover {
    border-color: var(--accent);
}

@media (max-width: 720px) {
    .xem-anh {
        padding: var(--space-2);
    }

    .xem-anh__nut {
        width: 34px;
        height: 34px;
    }
}

/* ==========================================================================
   Chốt nhà cung cấp theo lối GIỎ HÀNG (đợt 22)

   Nhà cung cấp là nhóm, mặt hàng là dòng con. Đổi từ bảng phẳng sang lối này
   vì câu người mua hỏi trước khi bấm không phải "mã này mua của ai" mà là
   "tôi sắp gửi mấy đơn, mỗi đơn cho ai, gồm gì, hết bao nhiêu" - câu ấy bảng
   theo mã không trả lời được, phải cuộn xuống khối xem trước mới thấy.

   KHÔNG dùng <table>: một dòng hàng ở đây là một khối hai tầng (tên + mã ở
   trên, lượng/giá/nút ở dưới) và các nút thao tác nằm trong đó. Nhồi vào ô
   bảng thì hoặc bảng tràn ngang, hoặc chữ bị ép xuống thành cột chỉ vài ký tự.
   ========================================================================== */

.ncc-nhom {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    margin-bottom: var(--space-3);
    overflow: hidden;
}

/* Nhóm chưa biết mua của ai: viền cảnh báo, vì nó là việc còn dang dở chứ
   không phải một nhà cung cấp thật. */
.ncc-nhom--chua-chon {
    border-color: var(--warning-border);
    background: var(--warning-soft);
}

.ncc-nhom__dau {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
    justify-content: space-between;
    padding: var(--space-3) var(--space-4);
    background: var(--bg-app);
    border-bottom: 1px solid var(--border);
}

.ncc-nhom__ten {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 600;
}

.ncc-nhom__tong {
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.ncc-dong {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: flex-start;
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border);
}

.ncc-nhom__dau + .ncc-dong,
.ncc-nhom__lich + .ncc-dong {
    border-top: 0;
}

/* Dải lịch + quà của một nhóm: nằm dưới tiêu đề nhóm, trên các dòng hàng.
   Nền nhạt hơn đầu nhóm để đọc ra là "cài đặt của cả nhóm này", không phải một
   dòng hàng nữa. */
.ncc-nhom__lich {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
    padding: var(--space-2) var(--space-4);
    border-bottom: 1px solid var(--border);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.ncc-nhom__lich-nhan {
    display: flex;
    gap: var(--space-2);
    align-items: center;
}

/* Dòng bỏ tích vẫn đọc được nhưng lùi hẳn ra sau: nó không nằm trong đơn sắp
   gửi, và để nó cùng độ đậm với dòng sẽ đặt là mời người ta đếm nhầm.

   Làm mờ TỪNG PHẦN chứ không cả dòng: cụm nút vẫn phải đọc như nút bấm được,
   vì "Chuyển NCC" chính là đường thoát của một dòng chưa có nơi mua. Đặt
   opacity lên cả dòng thì con không sáng lại được - opacity mở một tầng xếp
   riêng, luật ở con không đè ngược lên cha. */
.ncc-dong--tat .ncc-dong__than > *:not(.row-actions),
.ncc-dong--tat .ncc-dong__so {
    opacity: 0.55;
}

.ncc-dong__than {
    flex: 1 1 260px;
    min-width: 0;
}

.ncc-dong__so {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
}

.ncc-dong__thanh-tien {
    min-width: 110px;
    text-align: right;
    font-weight: 600;
}

/* --------------------------------------------------------------------------
   Nút tăng/giảm lượng.

   min-width chứ không width cố định cho ô số: lượng ở đây đo bằng đơn vị cơ
   sở nên năm chữ số là chuyện thường (2400 lon), và một ô hẹp cắt mất chữ số
   đầu là người dùng gõ đúng mà đọc ra sai.
   -------------------------------------------------------------------------- */

.stepper {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.stepper__nut {
    width: 30px;
    border: 0;
    background: var(--bg-app);
    color: var(--text-primary);
    font-size: var(--text-md);
    line-height: 1;
    cursor: pointer;
}

.stepper__nut:hover:not(:disabled) {
    background: var(--accent-soft);
}

.stepper__nut:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

.stepper__o {
    width: 72px;
    min-width: 72px;
    border: 0;
    border-left: 1px solid var(--border);
    border-right: 1px solid var(--border);
    padding: var(--space-2);
    text-align: right;
    background: var(--bg-surface);
    color: var(--text-primary);
}

/* Ẩn mũi tên mặc định của input[type=number]: đã có hai nút riêng, để cả hai
   là hai cách làm cùng một việc nằm cạnh nhau trong 30px. */
.stepper__o::-webkit-outer-spin-button,
.stepper__o::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.stepper__o[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* --------------------------------------------------------------------------
   Hộp chuyển nhà cung cấp: mỗi lựa chọn kèm GIÁ nơi đó đã từng giao mã này.
   Đó là lý do hộp này tồn tại thay vì một ô chọn - ô chọn chỉ bày được tên.
   -------------------------------------------------------------------------- */

.ncc-chon__muc {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-2);
    background: var(--bg-surface);
    text-align: left;
    cursor: pointer;
}

.ncc-chon__muc:hover {
    border-color: var(--accent);
}

.ncc-chon__muc[aria-current="true"] {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.ncc-chon__ten {
    flex: 1 1 auto;
    font-weight: 600;
}

.ncc-chon__gia {
    font-variant-numeric: tabular-nums;
}

.ncc-chon__tim {
    margin-bottom: var(--space-2);
}

/* Hộp chứa cả một BẢNG nhập liệu (danh sách quà khuyến mãi): 420px của hộp
   thường nhét bảy cột vào là mỗi ô rộng năm ký tự. Vẫn giới hạn theo bề ngang
   khung nhìn để trên điện thoại hộp không tràn ra ngoài màn. */
.modal__panel--rong {
    width: min(940px, 100%);
}

/* Ô chọn hàng bị tắt vì dòng quà đang khai bằng tên gõ tay. Thẻ select gốc đã
   disabled; lớp này làm mờ cả ô tìm kiếm bọc ngoài, nếu không nó vẫn trông như
   gõ được. */
.combo--tat {
    opacity: 0.5;
    pointer-events: none;
}

/* Danh sách đầy đủ cuộn TRONG hộp, không kéo dài cái hộp: hơn một trăm nhà
   cung cấp mà để hộp tự cao thì nút "Khai nhà cung cấp mới" ở chân hộp bị đẩy
   khỏi khung nhìn, và người dùng kết luận là không có nút ấy. */
.ncc-chon__ds {
    max-height: 40vh;
    overflow-y: auto;
    padding-right: var(--space-1);
}

/* Nhóm vừa thêm mà chưa có mã nào: một lời mời, không phải một lỗi. */
.ncc-nhom__trong {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-sm);
    color: var(--text-secondary);
}
