/* Self-host Barlow/Barlow Semi Condensed/IBM Plex Mono (tải từ Google Fonts, giấy phép OFL) -
   thay vì phụ thuộc CDN hoặc font đã cài sẵn trên máy người dùng (trước đây --font-display/
   --font-body/--mono-font bên dưới chỉ khai TÊN font, không có gì đảm bảo hiển thị đúng nếu máy
   không cài sẵn - im lặng rơi về system-ui/Arial). Chỉ tải 3 subset cần cho nội dung Việt/Anh
   (latin/latin-ext/vietnamese - bỏ cyrillic/cyrillic-ext của IBM Plex Mono, không dùng tới) +
   ĐÚNG các weight thực tế đang dùng trong file này (400/500/600/700 - xem các dòng font-weight
   bên dưới). File .woff2 tại webapp/static/fonts/. */

@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/barlow-400-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/barlow-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/barlow-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/barlow-500-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/barlow-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/barlow-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/barlow-600-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/barlow-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/barlow-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-700-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-400-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-600-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-700-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow Semi Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-semi-condensed-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-400-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-700-vietnamese.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Bảng màu "Navy + Gold" lấy đúng từ lotusfoodsolution.com/css/styles.css (trang giới thiệu
   thật của công ty) - navy làm màu chữ/link/nút chính (--accent, đúng vai trò --navy-600 bên
   web thật), gold TÁCH RIÊNG thành --gold chỉ dùng cho viền nhấn/CTA phụ (không đủ tương phản
   để làm màu chữ thường). --success đổi thành xanh lá lấy ý từ logo Lotus theo yêu cầu người
   dùng (kết hợp cả logo + web thật), giá trị ước lượng bằng mắt - xem ghi chú cạnh biến. */
:root {
  --bg: #f7f9fb;
  --surface: #ffffff;
  --fg: #0b2049;
  --muted: #6e7a90;
  --border: #c5ccd8;
  --border-strong: #556074;
  --accent: #173a78;
  --accent-fg: #ffffff;
  --gold: #b07d2a;
  --gold-fg: #0b2049;
  --danger: #b23b3b;
  --success: #4c9a2a; /* xanh lá theo logo Lotus - ước lượng bằng mắt, chưa đo pixel chính xác */
  --review-bg: #f6ecd9;
  --unassigned-bg: #eef3fa;
  --mapped-bg: #e3f3db;
  --mono-font: "IBM Plex Mono", "SFMono-Regular", Consolas, "Cascadia Code", ui-monospace, monospace;
  --font-display: "Barlow Semi Condensed", "Arial Narrow", system-ui, sans-serif;
  --font-body: "Barlow", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --radius: 3px;
  --sidebar-w: 220px;
  --topbar-h: 48px;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0b0d11;
    --surface: #12151b;
    --fg: #dfe6f2;
    --muted: #9aa6bb;
    --border: #2c3542;
    --border-strong: #4a5568;
    --accent: #5b8def;
    --accent-fg: #071019;
    --gold: #d1a34f;
    --gold-fg: #0b0d11;
    --danger: #f0736e;
    --success: #7ad35a;
    --review-bg: #3d3319;
    --unassigned-bg: #0d2c47;
    --mapped-bg: #163a1f;
  }
}

* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--fg);
}
/* Màu link mặc định của trình duyệt (xanh dương chưa click/tím đã click) không đủ tương
   phản trên nền tối gần đen của theme này - đặt màu cố định theo --accent cho MỌI link chưa
   được style riêng (các class cụ thể hơn như .sidenav a/.cell-link vẫn thắng nhờ độ ưu tiên
   CSS cao hơn, không bị ảnh hưởng). */
a, a:visited { color: var(--accent); }

/* Bố cục toàn app: 1 thanh mỏng trên cùng (đổi ngôn ngữ + trạng thái đăng nhập, full-width) +
   sidebar dọc bên trái (brand + điều hướng) + nội dung bên phải. Cả .topbar và .sidebar dùng
   "position:sticky" (KHÔNG "fixed", KHÔNG bọc thêm 1 vùng overflow cuộn riêng) để window vẫn là
   scroll container DUY NHẤT - bảo toàn .data-table th{position:sticky} đang hoạt động đúng khắp
   app (nếu đổi sang 1 vùng cuộn lồng bên trong, mọi sticky-header bảng sẽ tính lại theo ancestor
   khác và có thể vỡ). */
.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  grid-template-rows: var(--topbar-h) 1fr;
  grid-template-areas: "topbar topbar" "sidebar main";
  min-height: 100vh;
}
.topbar {
  grid-area: topbar;
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 24px;
}
.sidebar {
  grid-area: sidebar;
  position: sticky;
  top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: 16px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.brand {
  text-decoration: none;
  display: block;
  border-bottom: 2px solid var(--success);
  padding-bottom: 10px;
  margin-bottom: 4px;
}
.brand-kicker {
  display: block;
  font-family: var(--font-display);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 2px;
}
.brand-name {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--fg);
  font-size: 1.05rem;
}
.sidenav { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
.sidenav a {
  color: var(--muted);
  text-decoration: none;
  font-size: .88rem;
  padding: 8px 10px;
  border-radius: var(--radius);
}
.sidenav a:hover { color: var(--fg); background: var(--bg); }
.sidenav a.active { color: var(--fg); background: var(--bg); font-weight: 600; border-left: 3px solid var(--gold); padding-left: 7px; }
.auth-status { margin-left: auto; display: flex; align-items: center; gap: 10px; font-size: .85rem; }
.auth-status a { color: var(--muted); text-decoration: none; }
.auth-status a:hover { color: var(--fg); text-decoration: underline; }
/* Nút chuyển ngôn ngữ VI/EN - xem base.html + webapp/i18n.py. */
.lang-switch { display: flex; gap: 6px; font-size: .8rem; }
.lang-switch a {
  color: var(--muted); text-decoration: none; padding: 2px 6px;
  border: 1px solid var(--border); border-radius: 4px;
}
.lang-switch a:hover { color: var(--fg); }
.lang-switch a.active { color: var(--accent-fg); background: var(--accent); border-color: var(--accent); font-weight: 600; }
button.linklike {
  background: none; border: none; padding: 0; color: var(--muted);
  font-size: .85rem; font-weight: 400; text-decoration: underline; cursor: pointer;
}
button.linklike:hover { color: var(--fg); }
/* Thanh tab con - dùng ở trang "Cấu hình" (/library/config) + 3 trang con của nó, xem
   library_config_subnav.html - tab đang active tô đậm + viền dưới theo --accent. */
.subnav {
  display: flex; gap: 4px; border-bottom: 1px solid var(--border);
  margin: 4px 0 20px;
}
.subnav a {
  color: var(--muted); text-decoration: none; font-size: .9rem;
  padding: 8px 14px; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.subnav a:hover { color: var(--fg); }
.subnav a.active { color: var(--fg); font-weight: 600; border-bottom-color: var(--accent); }
.badge {
  font-family: var(--mono-font);
  font-size: .7rem;
  font-weight: 700;
  color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 1px 8px;
  margin-left: 8px;
}
.container {
  grid-area: main;
  min-width: 0; /* grid item mặc định min-width:auto - bảng rộng có thể đẩy vỡ layout sidebar nếu thiếu dòng này */
  max-width: 1200px;
  margin: 0 auto;
  padding: 24px;
}
h1 { font-family: var(--font-display); font-size: 1.4rem; margin: 0 0 8px; }
.muted { color: var(--muted); }
.small { font-size: .85rem; }

/* Utility class ngắn, có chủ đích - thay cho các pattern style="..." lặp lại nhiều nhất trong
   template (spacing/flex-row/ẩn hiện/độ rộng cột). Xem .claude/skills/frontend-ui-standard/. */
.mt-1 { margin-top: 4px; }
.mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 16px; }
.mb-1 { margin-bottom: 4px; }
.mb-2 { margin-bottom: 8px; }
.mb-3 { margin-bottom: 16px; }
.m-0 { margin: 0; }
.hidden { display: none !important; }
.d-inline { display: inline; }
.overflow-x-auto { overflow-x: auto; }
.row-wrap { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.w-narrow { width: 70px; }
.w-sm { max-width: 140px; }
.w-md { max-width: 220px; }
.w-lg { max-width: 340px; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--gold);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin: 16px 0;
}
.add-import-forms > summary {
  cursor: pointer;
  font-weight: 500;
  list-style: none;
}
.add-import-forms > summary::-webkit-details-marker { display: none; }
.add-import-forms > summary::before { content: "▸ "; }
.add-import-forms[open] > summary::before { content: "▾ "; }
.add-import-forms[open] > summary { margin-bottom: 12px; }
.add-import-forms .card:first-of-type { margin-top: 0; }
.row { display: flex; gap: 8px; margin-top: 6px; }
input[type="text"], input[type="file"], input[type="email"], input[type="password"], select {
  flex: 1;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
}
label { font-weight: 500; font-size: .9rem; }
.error-box {
  background: var(--review-bg);
  border: 1px solid var(--border-strong);
  border-left: 4px solid var(--danger);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 14px;
  font-size: .9rem;
}
/* Thay cho pattern lặp lại nhiều lần style="background:rgba(37,99,235,.12); border-color:
   rgba(37,99,235,.4);" trên class="card" - dùng "class="card info-box"" thay cho style inline. */
.info-box {
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
  border: 1px solid var(--border-strong);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 14px;
  font-size: .88rem;
}
.auth-form { max-width: 380px; margin: 40px auto; }
.auth-form .card { display: flex; flex-direction: column; gap: 10px; }
.auth-divider { text-align: center; color: var(--muted); font-size: .85rem; margin: 8px 0; }

/* --- Layout tối giản cho 2 trang tra cứu mobile (/m/parts, /m/plant_components) - KHÔNG dùng
   .topbar/.sidebar rộng của base.html, chỉ 1 thanh tiêu đề nhỏ + nội dung 1 cột full-width. --- */
.mobile-page { max-width: 480px; margin: 0 auto; padding: 12px; }
.mobile-topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 0; border-bottom: 1px solid var(--border); margin-bottom: 10px;
}
.mobile-topbar .brand { font-weight: 600; font-size: .95rem; text-decoration: none; color: var(--fg); }
/* Dòng tài khoản/đăng xuất tái dùng ".mobile-topbar" (xem mobile_auth_status.html) - ".auth-status"
   (base.html) vốn giả định là phần tử CUỐI 1 hàng flex rộng hơn (margin-left:auto đẩy sang phải),
   ở đây nó là phần tử THỨ 2 duy nhất trong hàng riêng (justify-content:space-between của
   .mobile-topbar đã tự đẩy sang phải) nên chỉ cần bỏ margin-left, không cần viết lại từ đầu. */
.mobile-topbar .auth-status { margin-left: 0; }
.mobile-mode-toggle { display: flex; gap: 6px; margin-bottom: 10px; }
.mobile-mode-toggle button {
  flex: 1; background: var(--bg); color: var(--fg); border: 1px solid var(--border);
}
.mobile-mode-toggle button.active { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
.mobile-search-input { width: 100%; padding: 10px 12px; font-size: 1rem; margin-bottom: 10px; }
.mobile-tree-item, .mobile-tree-back {
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px;
  margin-bottom: 6px; cursor: pointer;
}
.mobile-tree-item:active, .mobile-tree-back:active { background: var(--border); }
.mobile-card {
  border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin-bottom: 8px;
}
.mobile-card strong { font-size: .95rem; }
.mobile-card .mobile-doc-link {
  display: inline-block; margin-left: 4px; font-size: .95rem; text-decoration: none;
  vertical-align: middle;
}
.mobile-card .muted { display: block; margin: 4px 0; font-size: .85rem; }
.mobile-card form { margin-top: 6px; }
.mobile-card button { width: 100%; padding: 6px; font-size: .85rem; }
.mobile-empty { color: var(--muted); text-align: center; padding: 20px 0; }

button {
  background: var(--accent);
  color: var(--accent-fg);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 7px 14px;
  font-weight: 600;
  font-size: .88rem;
  cursor: pointer;
}
button:hover { background: color-mix(in srgb, var(--accent) 85%, black); }
button:disabled { opacity: .5; cursor: not-allowed; }
button.danger { background: transparent; color: var(--danger); border: 1px solid var(--danger); padding: 4px 10px; }
button.danger:hover { background: color-mix(in srgb, var(--danger) 12%, transparent); }

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: .82rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
}
.data-table th, .data-table td {
  border: 1px solid var(--border);
  padding: 4px 8px;
  text-align: left;
  vertical-align: middle;
  line-height: 1.25;
}
.data-table th {
  font-family: var(--font-display);
  color: var(--fg);
  font-weight: 700;
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: .02em;
  background: var(--surface);
  border-bottom: 2px solid var(--border-strong);
  position: sticky;
  top: 0;
  /* z-index PHẢI khai báo tường minh (thấp hơn .filter-row th bên dưới): "position:sticky" LUÔN
     tạo stacking context MỚI kể cả khi z-index:auto - nên z-index của popup NẰM TRONG 1 ô sticky
     (vd .ms-filter-panel z-index:20) chỉ có tác dụng BÊN TRONG ô đó, không thể vượt ra ngoài để
     đè lên ô sticky khác. Vài trang (vd library_parts.html) có 2 BẢNG RIÊNG: 1 bảng chứa hàng lọc
     + 1 bảng dữ liệu; nếu cả 2 cùng z-index auto thì bảng ĐỨNG SAU trong DOM (bảng dữ liệu) luôn
     vẽ đè lên popup của bảng trước - đúng lỗi "dropdown bị nằm dưới tên cột". */
  z-index: 1;
}
/* Zebra rất nhạt để không lấn màu trạng thái (--review-bg/--unassigned-bg/--mapped-bg) - các
   rule màu trạng thái PHẢI đứng SAU trong file này để thắng zebra khi trùng specificity. */
.data-table tbody tr:nth-child(even) { background: color-mix(in srgb, var(--fg) 3%, var(--surface)); }
.data-table tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }
.data-table tr.needs-review { background: var(--review-bg); }
.data-table td.note { color: var(--muted); max-width: 320px; }
.data-table td.mono, .data-table td.tag, .mono { font-family: var(--mono-font); }
#select-all-header { accent-color: var(--accent); cursor: pointer; }
.data-table th.sortable { cursor: pointer; user-select: none; }
.data-table th.sortable:hover { color: var(--fg); }
.data-table th.sortable.sort-asc::after { content: " \25B2"; font-size: .7em; }
.data-table th.sortable.sort-desc::after { content: " \25BC"; font-size: .7em; }
/* Hàng lọc nằm NGAY DƯỚI hàng tiêu đề - cả 2 cùng "sticky" nên phải lệch "top" khác nhau,
   không thì đè lên nhau khi cuộn (hàng tiêu đề dính ở top:0, hàng lọc dính ngay bên dưới). */
.data-table tr.filter-row th {
  position: sticky;
  top: 30px; /* header thấp hơn trước (padding/font nhỏ lại) - đã ước lượng lại, cần soát bằng mắt trên browser thật */
  z-index: 2; /* > .data-table th (=1) - để popup lọc bên trong ô này đè được lên hàng tiêu đề,
                 kể cả hàng tiêu đề của 1 BẢNG KHÁC đứng sau trong DOM (xem chú thích ở .data-table th) */
  background: var(--surface);
  font-family: var(--font-body);
  color: var(--fg);
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  padding-top: 4px;
  padding-bottom: 4px;
}
.data-table tr.filter-row .col-filter {
  display: block;
  width: 100%;
  font-size: .78rem;
  padding: 3px 4px;
  margin-bottom: 2px;
}
/* Dropdown tick-chọn nhiều giá trị (thay cho <select> đơn) - nút nhỏ mở popup chứa checkbox,
   popup nổi lên trên nội dung khác (z-index) vì hàng lọc vốn đã sticky ở gần đỉnh trang. */
.ms-filter { position: relative; width: 100%; }
.ms-filter-btn {
  display: block;
  width: 100%;
  font-size: .78rem;
  padding: 3px 4px;
  text-align: left;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
}
.ms-filter-btn::after { content: " \25BE"; float: right; color: var(--muted); }
.ms-filter-btn.ms-active { border-color: var(--accent); color: var(--accent); }
/* .mech-type-target ("Loại cơ khí" suy ra)/.cat-picker-btn ("Phân loại", breadcrumb tới 10 Step)
   có thể rất dài - giới hạn 4 dòng + "..." thay vì tràn tự do (kéo cả bảng cao lên), max-width
   bắt buộc để chữ thật sự wrap trong 1 cột hẹp (table-layout auto mặc định sẽ giãn cột theo nội
   dung nếu không giới hạn). Đặt SAU .ms-filter-btn vì .cat-picker-btn có CẢ 2 class (cùng
   specificity) - rule đứng sau trong file mới thắng để ghi đè display:block của .ms-filter-btn.*/
.mech-type-target, .cat-picker-btn {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: 380px;
}
.ms-filter-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 20;
  min-width: 140px;
  max-height: 220px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, .3);
  padding: 4px;
}
.ms-filter.open .ms-filter-panel { display: block; }
.ms-filter-panel label {
  display: block;
  font-size: .78rem;
  font-weight: 400;
  padding: 2px 3px;
  white-space: nowrap;
  cursor: pointer;
}
.ms-filter-panel label:hover { background: var(--border); border-radius: 3px; }
.ms-filter-panel input[type="checkbox"] { accent-color: var(--accent); margin-right: 3px; }
.ms-filter-panel label.ms-empty-option {
  font-style: italic;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  margin-bottom: 3px;
  padding-bottom: 5px;
}

/* Combo chọn part có tìm kiếm - ô text để lọc + panel popup liệt kê part, CHỈ chọn được
   bằng click (không cho submit giá trị gõ tay tự do, xem input hidden .part-combo-value). */
.part-combo { position: relative; }
.part-combo-input { width: 100%; }
.part-combo-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 20;
  width: max-content;
  min-width: 220px;
  max-width: 340px;
  max-height: 260px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, .3);
  padding: 4px;
}
.part-combo-panel.open { display: block; }
.part-combo-item {
  padding: 3px 5px;
  font-size: .8rem;
  border-radius: 3px;
  cursor: pointer;
  white-space: normal;
}
.part-combo-item:hover { background: var(--border); }
.part-combo-suggest:empty { display: none; }
.part-combo-suggest {
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
  padding-bottom: 4px;
}
.part-combo-suggest-label { font-size: .72rem; color: var(--muted); padding: 2px 5px; }
.part-combo-suggest-item { background: var(--review-bg); }

.part-combo-toggle-mode {
  display: block;
  width: 100%;
  font-size: .72rem;
  padding: 2px 5px;
  margin-bottom: 4px;
  background: none;
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--accent);
  cursor: pointer;
}
.part-combo-tree-item {
  padding: 3px 5px;
  font-size: .8rem;
  border-radius: 3px;
  cursor: pointer;
}
.part-combo-tree-item:hover { background: var(--border); }
.part-combo-tree-back {
  padding: 3px 5px;
  font-size: .72rem;
  color: var(--muted);
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.part-combo-tree-back:hover { color: var(--accent); }

.type-suggest-hint { display: block; margin-top: 2px; }
.type-suggest-link { font-size: .72rem; color: var(--accent); text-decoration: none; }
.type-suggest-link:hover { text-decoration: underline; }

.part-editor { display: flex; flex-direction: column; gap: 4px; min-width: 170px; }
.part-chips { display: flex; flex-wrap: wrap; gap: 3px; }
.part-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  background: var(--border);
  border-radius: 10px;
  padding: 1px 4px 1px 7px;
  font-size: .78rem;
  white-space: nowrap;
}
.part-chip-remove {
  background: none;
  border: none;
  color: var(--danger);
  cursor: pointer;
  font-weight: 700;
  font-size: .85rem;
  line-height: 1;
  padding: 0 2px;
}
.part-chip-swap {
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  font-size: .82rem;
  line-height: 1;
  padding: 0 1px;
  max-width: 22px;
}
.part-add { display: flex; gap: 4px; align-items: center; }
.part-add-qty { width: 42px; font-size: .78rem; padding: 3px 4px; }
.part-add-btn { padding: 2px 8px; font-size: .8rem; }

.thumb-cell { width: 90px; }
img.thumb { width: 84px; height: 84px; object-fit: cover; border-radius: 4px; background: var(--border); }

.edit-toolbar { align-items: center; margin: 10px 0; }

.cell-input {
  padding: 4px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  font-size: .85rem;
}
/* Ô mô tả (Equipment Part list) - textarea 2 dòng thay vì input 1 dòng để chữ dài không bị ẩn,
   font nhỏ lại bằng cỡ nút "Phân loại" (.ms-filter-btn, .78rem) theo yêu cầu người dùng. */
.cell-textarea {
  padding: 4px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  font-size: .78rem;
  font-family: inherit;
  line-height: 1.3;
  resize: vertical;
}
.cell-input-narrow { width: 90px; }
.data-table td.center { text-align: center; }

/* Fragment autosave htmx trả về sau khi 1 field tự lưu lúc blur (xem
   webapp/templates/_partials/save_ok.html/save_error.html) - chữ nhỏ, không chiếm layout. */
.save-ok { color: var(--success); font-size: .8rem; }
.save-error { color: var(--danger); font-size: .8rem; }

.cell-link {
  display: inline-block;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  text-decoration: none;
  color: var(--fg);
  font-size: .85rem;
}
.cell-link.active { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }

/* Thanh menu hành động trong trang project - trước đây là các thẻ <a> mặc định (không có CSS
   riêng) nằm rời rạc cách nhau bằng "·", dựa hoàn toàn vào màu link mặc định của trình duyệt
   (tím cho link đã click) - rất khó đọc trên nền tối gần đen. Vẽ lại dạng dãy nút bo góc
   (tái dùng .cell-link, cùng kiểu nút đã dùng ở trang assign) để luôn đủ tương phản ở cả 2 theme. */
.project-menu { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 20px; }
.project-menu .cell-link:hover { border-color: var(--accent); color: var(--accent); }

.pagination { display: flex; align-items: center; gap: 12px; margin: 10px 0; }

.category-tree-root, .category-tree-children { list-style: none; margin: 0; padding-left: 0; }
.category-tree-children { padding-left: 20px; margin-top: 4px; }
.category-tree-root li, .category-tree-children li { margin: 3px 0; }
.tree-toggle {
  background: none;
  border: 1px solid var(--border);
  border-radius: 3px;
  box-sizing: border-box;
  color: var(--muted);
  cursor: pointer;
  font-size: .7rem;
  line-height: 1;
  width: 20px;
  text-align: center;
  padding: 1px 0;
  margin-right: 4px;
}
.tree-toggle:hover { color: var(--accent); border-color: var(--accent); }
/* Hàng "Kích cỡ:"/"Loại cơ khí:" (library_part_categories.html) - nhãn + checkbox "Step N" nằm
   chung 1 dòng, chiều rộng cố định + nowrap chỉ để hiển thị GỌN/ĐỀU CỘT (không cần khớp chính
   xác với hằng số JS nào - việc căn dấu +/- của node con vào tâm checkbox Step tương ứng giờ
   ĐO trực tiếp bằng getBoundingClientRect() sau khi cây gắn vào DOM, xem "pendingAlign" trong
   library_part_categories.html, không còn dựa vào các con số px này nữa). */
.step-row-wrap { margin: 2px 0 2px 20px; }
.step-row-caption { display: inline-block; width: 100px; white-space: nowrap; vertical-align: middle; }
.step-size-label { display: inline-block; width: 72px; white-space: nowrap; margin-right: 0; vertical-align: middle; }
.step-size-label input[type="checkbox"] { width: 13px; height: 13px; vertical-align: middle; margin: 0 4px 0 0; }

.mosaic-wrap {
  /* "Phá khung" ra khỏi giới hạn max-width:1200px của .container - trang có 2 mảng nền
     trống 2 bên trên màn hình rộng, tận dụng luôn cho mosaic thay vì bó hẹp như phần nội
     dung khác. Kỹ thuật: đẩy full-bleed bằng 50%/-50vw dựa theo tâm CỘT NỘI DUNG (main grid
     column, KHÔNG phải tâm viewport như trước khi chưa có sidebar) - chỉ bung tràn tới đúng
     mép cột nội dung bên phải sidebar, không đè lên/chạy dưới sidebar. Suy ra bằng đại số từ
     công thức full-bleed cũ (width:100vw; left:50%; margin-left:-50vw) sau khi trừ đi phần
     --sidebar-w chiếm bên trái viewport - khi --sidebar-w:0 công thức dưới trả về đúng công
     thức cũ. */
  width: calc(100vw - var(--sidebar-w));
  max-width: calc(100vw - var(--sidebar-w));
  position: relative;
  left: 50%;
  margin-left: calc(-50vw + var(--sidebar-w) / 2);
  max-height: 78vh;
  overflow-x: auto;
  overflow-y: auto;
  /* Ngăn cuộn dọc "tràn" ra ngoài cuộn trang khi đã cuộn hết mosaic - nếu không, một số
     trình duyệt sẽ chuyển sang cuộn trang ngoài ngay khi con trỏ đứng yên gần mép trên/dưới,
     khiến người dùng tưởng nhầm là mosaic không cuộn dọc được. */
  overscroll-behavior: contain;
  box-sizing: border-box;
  padding: 8px 24px;
}
.mosaic-grid {
  display: grid;
  /* Không để khoảng hở giữa các ô - mỗi ô ảnh vốn chỉ vẽ ĐÚNG phần bản vẽ trong ranh giới
     world-bbox của nó, nên 1 thiết bị nằm vắt ngang ranh giới 2 ô sẽ luôn bị chia đôi hình
     ảnh giữa 2 ô liền kề (giới hạn cố hữu của cách ghép mosaic từng ô vuông riêng) - gap 0
     ít nhất giúp 2 nửa đó nối liền sát nhau thay vì có khe hở càng làm rõ chỗ bị cắt. Nếu
     một thiết bị bị cắt ngay ranh giới, tăng "Kích thước ô lưới" ở trên (ít ranh giới hơn
     trên cùng 1 diện tích) thường né được vấn đề này.*/
  gap: 0;
  /* Cần để ".boundary-overlay" (tính năng "Kẻ biên" - hình chữ nhật có thể trải qua NHIỀU ô,
     khác mọi marker khác luôn nằm gọn trong 1 ô) định vị "absolute" theo ĐÚNG toàn bộ mosaic
     thay vì theo ancestor xa hơn (vd .mosaic-wrap, lệch vì có padding/scroll riêng). */
  position: relative;
}
/* Overlay PHỦ TRỌN ".mosaic-grid" (không phải từng ".mosaic-cell") - nơi vẽ/hiện hình chữ nhật
   "Kẻ biên". Mặc định "click xuyên qua" (pointer-events:none) để không cản trở tương tác bình
   thường với các marker/click-đặt-vị-trí bên dưới - CHỈ bật "auto" khi đang ở chế độ vẽ (JS
   toggle class "active", xem mục "Kẻ biên" trong assign.html). */
.boundary-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.boundary-overlay.active {
  pointer-events: auto;
  cursor: crosshair;
}
.boundary-rect {
  position: absolute;
  border: 2px dashed rgba(234, 88, 12, .9);
  background: rgba(234, 88, 12, .08);
  box-sizing: border-box;
  pointer-events: none;
}
/* Rect đang ở chế độ vẽ/sửa tương tác (chưa "Gán") - cho phép click vào chính nó (kéo để DI
   CHUYỂN cả khối, không chỉ resize từng góc) VÀ có 4 handle góc. */
.boundary-rect.editing {
  pointer-events: auto;
  cursor: move;
}
.boundary-handle {
  position: absolute;
  width: 12px;
  height: 12px;
  margin: -6px;
  background: rgba(234, 88, 12, .95);
  border: 1px solid #fff;
  border-radius: 2px;
  pointer-events: auto;
  touch-action: none;
}
.boundary-handle.nw { left: 0; top: 0; cursor: nwse-resize; }
.boundary-handle.ne { left: 100%; top: 0; cursor: nesw-resize; }
.boundary-handle.sw { left: 0; top: 100%; cursor: nesw-resize; }
.boundary-handle.se { left: 100%; top: 100%; cursor: nwse-resize; }
/* Label nhỏ ghi tên Unit + nút Sửa/Xoá, hiện cố định ở góc trên-trái của rect ĐÃ LƯU (không
   đang chỉnh sửa) - dùng để phân biệt nhiều rect chồng cạnh nhau trên mosaic. */
.boundary-rect-tag {
  position: absolute;
  left: 0;
  top: 0;
  transform: translateY(-100%);
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: .7rem;
  font-weight: 700;
  padding: 2px 5px;
  background: rgba(234, 88, 12, .9);
  color: #fff;
  border-radius: 2px 2px 0 0;
  white-space: nowrap;
  pointer-events: auto;
}
.boundary-rect-tag button {
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  padding: 0;
  font-size: .8rem;
}
/* Mini-toolbar nổi "Gán"/"Huỷ" khi đang vẽ/sửa 1 rect tương tác - định vị TUYỆT ĐỐI theo px
   chuột (JS set left/top trực tiếp), không nằm trong luồng layout bình thường. */
.boundary-toolbar {
  position: fixed;
  z-index: 60;
  display: none;
  gap: 6px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .25);
}
.mosaic-cell {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--border);
}
.mosaic-cell img {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--border);
}
.marker-group {
  position: absolute;
  z-index: 2;
  /* Tắt hẳn chế độ "forced/auto dark" của trình duyệt cho riêng cụm marker - nếu không,
     1 số trình duyệt (vd Chrome "Auto dark mode for web contents") có thể tự đảo màu nền/
     chữ đã khai báo tường minh bên dưới - marker cần giữ ĐÚNG màu đã chọn (nền tối, chữ
     trắng, cố định) vì nó đè lên ảnh CAD, không phải nội dung trang bình thường. */
  forced-color-adjust: none;
}
/* Dấu tick là ĐIỂM NEO đứng yên đúng vị trí thiết bị thật (px,py) - KHÔNG kéo được, không
   nằm chung khối di chuyển với ô nhập (".marker-box" bên dưới) để tránh mất dấu vị trí gốc
   khi người dùng kéo ô nhập né icon. */
.marker-group .marker-anchor {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
  width: 11px;
  height: 11px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
/* Ô nhập Unit/Tag - mặc định nằm ngay dưới dấu tick (translateY 9px), kéo dấu ⠿ để dịch
   chuyển RIÊNG khối này (xem JS) trong 1 bán kính giới hạn quanh dấu tick. */
.marker-box {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, 9px);
  display: flex;
  align-items: center;
  gap: 1px;
}
.marker-collapsible .marker-box { display: none; }
.marker-collapsible.marker-expanded .marker-box { display: flex; }
/* Ô chọn Type - ẩn mặc định, hiện khi block được tick chọn (mọi marker, kể cả đã khớp tag
   sẵn) để chọn/đổi Type luôn tại chỗ không cần cuộn xuống bảng. Nằm NGAY TRONG ".marker-box"
   (cùng khối với ô Unit/Tag) nên kéo tay cầm ⠿ sẽ di chuyển theo cùng, không tách rời. */
.marker-type-wrap { display: none; }
.marker-group.marker-expanded .marker-type-wrap { display: block; }
.marker-type-select {
  font-size: .55rem;
  line-height: 1;
  padding: 0 1px;
  height: 14px;
  border: 1px solid rgba(37, 99, 235, .8);
  background: rgba(15, 15, 15, .72);
  color: #fff;
  width: 64px;
}
.marker-type-select:focus { background: rgba(0, 0, 0, .92); border-color: var(--accent); }
.marker-drag {
  cursor: grab;
  font-size: .55rem;
  color: rgba(255, 255, 255, .85);
  background: rgba(0, 0, 0, .45);
  border-radius: 2px;
  padding: 0 1px;
  line-height: 1;
  touch-action: none;
  user-select: none;
  flex: none;
}
.marker-drag:active { cursor: grabbing; }
.marker-fields {
  display: flex;
  flex-direction: row;
  gap: 0;
  line-height: 1;
}
.marker-input {
  font-size: .56rem;
  line-height: 1;
  padding: 0 2px;
  height: 13px;
  border: 1px solid rgba(37, 99, 235, .8);
  background: rgba(15, 15, 15, .72);
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Giữ nguyên nền tối/chữ trắng khi focus để gõ - chỉ đậm màu hơn 1 chút cho dễ nhận biết
   đang gõ ở ô nào, KHÔNG đổi hẳn sang nền trắng như trước (đồng bộ với giao diện tối). */
.marker-input:focus {
  background: rgba(0, 0, 0, .92);
  border-color: var(--accent);
  width: 90px;
  position: relative;
  z-index: 3;
}
/* Unit xếp trước, Tag nối liền ngay sau (đọc như 1 Item ID liên tục) - bo góc ở 2 đầu
   ngoài cùng, cạnh chung giữa 2 ô không bo + không viền đôi để trông liền mạch. Unit rộng
   hơn hẳn (54px) vì tên vùng như "C1W1" bị cắt chữ ở bề rộng cũ. */
.marker-input-unit { border-radius: 2px 0 0 2px; border-right: none; width: 54px; }
.marker-input-tag { border-radius: 0 2px 2px 0; font-weight: 700; width: 56px; }
.marker-unmatched .marker-input { border-color: rgba(220, 38, 38, .8); }
/* Viền xanh nổi bật quanh khung khi block đang được tick chọn (đồng bộ 2 chiều với bảng
   danh sách/checkbox marker - xem JS phần "marker-selected"). */
.marker-group.marker-selected .marker-fields,
.marker-group.marker-selected .marker-anchor {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* Nhãn Unit đã đặt vị trí CAD (xem "+ Gán Unit Name") - màu CAM để phân biệt hẳn với marker
   thiết bị (xanh) trên cùng mosaic, không phải checkbox/ô nhập nên không cần marker-anchor.
   Cỡ chữ/padding TĂNG RÕ RỆT so với marker thiết bị (~.56rem) - theo yêu cầu người dùng "to
   lên 1 chút để giống với icon trong bản vẽ CAD" (không có cách quy đổi world-units chính xác
   tuyệt đối vì đây là overlay screen-space cố định, không co giãn theo world units - đây là
   giá trị khởi điểm hợp lý, tinh chỉnh thêm nếu chưa vừa mắt). */
.unit-label-marker {
  position: absolute;
  z-index: 2;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: .85rem;
  line-height: 1;
  font-weight: 700;
  padding: 3px 7px;
  border-radius: 3px;
  background: rgba(154, 52, 18, .85);
  border: 1px solid rgba(251, 146, 60, .9);
  color: #fff;
  white-space: nowrap;
  forced-color-adjust: none;
}
.unit-label-drag {
  cursor: grab;
  font-size: .8rem;
  line-height: 1;
  touch-action: none;
  user-select: none;
  flex: none;
}
.unit-label-drag:active { cursor: grabbing; }
.unit-label-edit,
.unit-label-delete {
  background: none;
  border: none;
  color: inherit;
  font-size: .9rem;
  line-height: 1;
  padding: 0 0 0 3px;
  cursor: pointer;
}

tr.row-highlight { outline: 2px solid var(--accent); background: var(--review-bg); }
.signal-select.duplicate-selected { outline: 2px solid var(--danger); }
.signal-combo-input.duplicate-selected { outline: 2px solid var(--danger); }
tr.io-duplicate-row { background: var(--review-bg); }
tr.signal-unassigned-row { background: var(--unassigned-bg); }
.device-signal-row.signal-mapped-row { background: var(--mapped-bg); border-radius: 4px; }
.collapse-toggle { background: transparent; border: 1px solid var(--border); border-radius: 4px;
  width: 24px; height: 24px; line-height: 1; cursor: pointer; color: var(--fg); }
.collapse-body.collapsed { display: none; }
.data-table th.check-col, .data-table td.check-col { text-align: center; width: 28px; }

/* Nút xoá icon "×" gọn (thay cho button.danger chữ "Xoá" đầy đủ) - dùng cho các dòng bảng lặp
   lại nhiều lần (hành động/part phụ Bảo trì) - xem library_maintenance_detail.html. */
.icon-delete-btn {
  background: none;
  border: none;
  color: var(--danger);
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
}
.icon-delete-btn:hover { color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); border-radius: var(--radius); }

/* Nút icon nhỏ trung tính (không phải hành động xoá) - vd nút camera tải ảnh lên trong
   _partials/photo_upload_widget.html, dùng ở IO Testing/Chạy thử/Gán tagname. */
.icon-btn {
  background: none;
  border: none;
  color: var(--fg);
  font-size: 1rem;
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
}
.icon-btn:hover { background: color-mix(in srgb, var(--fg) 10%, transparent); border-radius: var(--radius); }

/* Link "Bảo trì" ở bảng Equipment Part list - màu khác nhau theo trạng thái Bảo trì của part
   đó (xem library_parts_page truyền maintenance_status_by_code) để phân biệt NGAY trên bảng,
   không cần bấm vào mới biết đã cấu hình hay chưa. */
.maintenance-link.status-prev_maintenance { color: var(--success); font-weight: 600; }
.maintenance-link.status-no_maintenance { color: var(--muted); font-weight: 600; }
.maintenance-link.status-unconfigured { color: var(--accent); }

/* Kéo-thả + xem trước nhiều file (2026-08-20, xem webapp/static/multi_upload.js) - bọc quanh
   MỌI <input type="file" multiple data-multi-upload> qua JS, không cần sửa layout form nào
   thêm - input vẫn nằm nguyên vị trí cũ trong .row, chỉ được bọc thêm khung viền đứt nét. */
.dropzone {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 4px 6px;
}
.dropzone--active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.dropzone--compact { padding: 2px 4px; gap: 3px; }
.dropzone-hint { color: var(--muted); font-size: .8rem; }
.file-preview-strip { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.file-preview-thumb {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 2px 4px;
  background: var(--surface);
}
.file-preview-thumb img { width: 28px; height: 28px; object-fit: cover; border-radius: var(--radius); }
.file-preview-icon { font-size: 1rem; }
.file-preview-name { font-size: .78rem; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-preview-remove { font-size: .95rem; padding: 0 2px; }
