/* ── tokens ─────────────────────────────────────────────────────────── */
:root{
  --ink:#0F1B2D; --ink-2:#48576F; --ink-3:#6B7A93;
  --page:#F4F6FA; --card:#FFFFFF; --card-2:#FAFBFD; --rail:#111C2E; --rail-ink:#AFBDD2;
  --line:#D7DEEA; --line-2:#E7ECF4;
  --primary:#2563EB; --primary-ink:#FFFFFF; --primary-soft:#E4EDFD; --primary-deep:#1D4ED8;
  --attn:#EA580C; --attn-soft:#FDEBDF; --attn-ink:#9A3412;
  --ok:#047857; --ok-soft:#DDF3EA; --crit:#DC2626; --crit-soft:#FBE4E4;
  --shadow:0 1px 2px rgba(15,27,45,.06);
  --r:6px;
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px;
  --ui:"Fira Sans",-apple-system,"Segoe UI",system-ui,sans-serif;
  --mono:"Fira Code",ui-monospace,"Cascadia Mono",Consolas,monospace;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ink:#E6ECF5; --ink-2:#9DABC2; --ink-3:#7A89A2;
    --page:#0B1220; --card:#131C2C; --card-2:#182234; --rail:#080E19; --rail-ink:#8E9DB5;
    --line:#26334A; --line-2:#1E2A3D;
    --primary:#5B8DEF; --primary-ink:#08111F; --primary-soft:#17263F; --primary-deep:#7FA9F5;
    --attn:#FB8B3C; --attn-soft:#33210F; --attn-ink:#FDBA8C;
    --ok:#34D399; --ok-soft:#0E2B23; --crit:#F87171; --crit-soft:#331616;
    --shadow:0 1px 2px rgba(0,0,0,.4);
  }
}
:root[data-theme="dark"]{
  --ink:#E6ECF5; --ink-2:#9DABC2; --ink-3:#7A89A2;
  --page:#0B1220; --card:#131C2C; --card-2:#182234; --rail:#080E19; --rail-ink:#8E9DB5;
  --line:#26334A; --line-2:#1E2A3D;
  --primary:#5B8DEF; --primary-ink:#08111F; --primary-soft:#17263F; --primary-deep:#7FA9F5;
  --attn:#FB8B3C; --attn-soft:#33210F; --attn-ink:#FDBA8C;
  --ok:#34D399; --ok-soft:#0E2B23; --crit:#F87171; --crit-soft:#331616;
  --shadow:0 1px 2px rgba(0,0,0,.4);
}
/* ── base ───────────────────────────────────────────────────────────── */
*{box-sizing:border-box}
body{margin:0;background:var(--page);color:var(--ink);font-family:var(--ui);
     font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}
h1,h2,h3{margin:0;text-wrap:balance;font-weight:600;letter-spacing:-.01em}
h1{font-size:20px} h2{font-size:15px} h3{font-size:13px}
p{margin:0}
code,.mono,.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
button,input,select,textarea{font:inherit;color:inherit}
a{color:var(--primary)}
:focus-visible{outline:2px solid var(--primary);outline-offset:2px;border-radius:3px}
.lbl{font-size:10.5px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.muted{color:var(--ink-2)}
.tiny{font-size:12px}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

/* ── shell ──────────────────────────────────────────────────────────── */
.app{display:grid;grid-template-columns:232px 1fr;min-height:100vh}
.rail{background:var(--rail);color:var(--rail-ink);display:flex;flex-direction:column;
      position:sticky;top:0;height:100vh;overflow-y:auto}
.brand{padding:var(--s5) var(--s4) var(--s4);border-bottom:1px solid rgba(255,255,255,.07)}
.brand b{color:#fff;font-size:15px;letter-spacing:-.01em;display:block}
.brand span{font-size:11px;color:#6D7E96;font-family:var(--mono)}
.nav{padding:var(--s3) var(--s2);display:flex;flex-direction:column;gap:2px;flex:1}
.nav button{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;
  background:none;border:0;border-radius:var(--r);color:var(--rail-ink);cursor:pointer;
  text-align:left;font-size:13.5px;transition:background .18s,color .18s}
.nav button:hover{background:rgba(255,255,255,.06);color:#fff}
.nav button[aria-current="page"]{background:var(--primary);color:#fff;font-weight:500}
.nav svg{width:16px;height:16px;flex-shrink:0}
.nav .count{margin-left:auto;font-family:var(--mono);font-size:11px;padding:1px 6px;
  border-radius:20px;background:var(--attn);color:#fff;font-weight:600}
.nav button[aria-current="page"] .count{background:rgba(0,0,0,.28)}
.rail-foot{padding:var(--s3) var(--s4);border-top:1px solid rgba(255,255,255,.07);
  font-size:11px;color:#5E6E86;font-family:var(--mono)}

.main{min-width:0;display:flex;flex-direction:column}
.topbar{position:sticky;top:0;z-index:20;background:var(--card);border-bottom:1px solid var(--line);
  padding:0 var(--s5);display:flex;align-items:center;gap:var(--s5);min-height:56px;flex-wrap:wrap}
.topbar .sep{width:1px;height:26px;background:var(--line)}
.heartbeat{display:flex;align-items:flex-end;gap:2px;height:20px}
.heartbeat i{width:3px;border-radius:1px;background:var(--ok);display:block}
.heartbeat i.f{background:var(--crit)}
.push{margin-left:auto}
.content{padding:var(--s5);display:flex;flex-direction:column;gap:var(--s5);max-width:1520px;width:100%}
.screen{display:none;flex-direction:column;gap:var(--s5)}
.screen.on{display:flex}
.page-head{display:flex;align-items:flex-end;gap:var(--s4);flex-wrap:wrap}
.page-head .actions{margin-left:auto;display:flex;gap:var(--s2);flex-wrap:wrap}

/* ── panel ──────────────────────────────────────────────────────────── */
.panel{background:var(--card);border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow)}
.panel-hd{display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s4);
  border-bottom:1px solid var(--line-2);flex-wrap:wrap}
.panel-bd{padding:var(--s4)}
.panel-bd.flush{padding:0}
.grid{display:grid;gap:var(--s4)}
.g4{grid-template-columns:repeat(4,minmax(0,1fr))}
.g3{grid-template-columns:repeat(3,minmax(0,1fr))}
.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.g-2-1{grid-template-columns:minmax(0,2fr) minmax(0,1fr)}
@media(max-width:1180px){.g4{grid-template-columns:repeat(2,minmax(0,1fr))}
  .g3,.g-2-1{grid-template-columns:minmax(0,1fr)}}
@media(max-width:820px){.app{grid-template-columns:1fr}.rail{position:static;height:auto}
  .g4,.g2{grid-template-columns:minmax(0,1fr)}.nav{flex-direction:row;flex-wrap:wrap}}

/* ── stat ───────────────────────────────────────────────────────────── */
.stat{padding:var(--s4);display:flex;flex-direction:column;gap:var(--s1)}
.stat .v{font-family:var(--mono);font-size:26px;font-weight:600;letter-spacing:-.02em;line-height:1.1}
.stat .sub{font-size:12px;color:var(--ink-2)}
.stat.attn{border-left:3px solid var(--attn)}
.stat.attn .v{color:var(--attn)}
.stat.ok{border-left:3px solid var(--ok)}
/* O thong ke dung lam bo loc: phai nhin ra bam duoc, va o dang chon phai
   khac han — mot bo loc dang bat ma trong giong o thuong la cach chac chan
   de nguoi dung tuong he thong mat du lieu. */
.stat.as-filter,.stat.as-filter:hover,.stat.as-filter *{text-decoration:none}
.stat.as-filter{color:inherit;cursor:pointer;
  transition:border-color .12s,background .12s}
.stat.as-filter:hover{background:var(--bg-2);border-color:var(--line-1)}
.stat.as-filter:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.stat.as-filter.dang-chon{border-color:var(--acc);background:var(--bg-2)}
.stat.as-filter.dang-chon .lbl{color:var(--acc)}

/* ── pills ──────────────────────────────────────────────────────────── */
.pill{display:inline-flex;align-items:center;gap:5px;padding:2px 8px;border-radius:20px;
  font-size:11.5px;font-weight:500;white-space:nowrap;border:1px solid transparent}
.pill.ok{background:var(--ok-soft);color:var(--ok);border-color:color-mix(in srgb,var(--ok) 30%,transparent)}
.pill.warn{background:var(--attn-soft);color:var(--attn-ink);border-color:color-mix(in srgb,var(--attn) 34%,transparent)}
.pill.crit{background:var(--crit-soft);color:var(--crit);border-color:color-mix(in srgb,var(--crit) 30%,transparent)}
.pill.info{background:var(--primary-soft);color:var(--primary-deep);border-color:color-mix(in srgb,var(--primary) 30%,transparent)}
.pill.mute{background:var(--card-2);color:var(--ink-2);border-color:var(--line)}
.pill .dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex-shrink:0}
.pill .dot.live{animation:pulse 2.4s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
.code-pill{font-family:var(--mono);font-size:11px;padding:1px 6px;border-radius:3px;
  background:var(--card-2);border:1px solid var(--line);color:var(--ink-2);white-space:nowrap}

/* ── buttons ────────────────────────────────────────────────────────── */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:7px 13px;
  min-height:34px;border-radius:var(--r);border:1px solid var(--line);background:var(--card);
  color:var(--ink);cursor:pointer;font-size:13px;font-weight:500;
  transition:background .18s,border-color .18s,color .18s}
.btn:hover{background:var(--card-2);border-color:var(--ink-3)}
.btn svg{width:15px;height:15px}
.btn.pri{background:var(--primary);border-color:var(--primary);color:var(--primary-ink)}
.btn.pri:hover{background:var(--primary-deep);border-color:var(--primary-deep)}
.btn.attn{background:var(--attn);border-color:var(--attn);color:#fff}
.btn.attn:hover{filter:brightness(.93)}
.btn.ghost{background:none;border-color:transparent}
.btn.ghost:hover{background:var(--card-2);border-color:var(--line)}
.btn.sm{min-height:28px;padding:4px 9px;font-size:12px}
.btn:disabled{opacity:.45;cursor:not-allowed}
/* Nút quay lại đứng trên tiêu đề trang: cần khoảng thở phía dưới, và chữ mờ
   hơn để không tranh chú ý với chính tiêu đề nó đang dẫn ra khỏi. */
.btn.back{margin-bottom:var(--s2);color:var(--ink-2)}
.btn.back:hover{color:var(--ink)}
.btn.danger{color:var(--crit);border-color:color-mix(in srgb,var(--crit) 34%,transparent)}
.btn.danger:hover{background:var(--crit-soft)}

/* ── table ──────────────────────────────────────────────────────────── */
.tw{overflow-x:auto;max-width:100%}
table{border-collapse:collapse;width:100%;font-size:13px}
thead th{position:sticky;top:0;background:var(--card-2);z-index:2;text-align:left;
  padding:8px var(--s3);border-bottom:1px solid var(--line);white-space:nowrap;
  font-size:10.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
tbody td{padding:8px var(--s3);border-bottom:1px solid var(--line-2);vertical-align:top}
tbody tr:hover{background:var(--card-2)}
tbody tr.sel{background:var(--primary-soft)}
td.n,th.n{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums}
td .mono{font-size:12.5px}
.trunc{max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}
.sev{border-left:3px solid transparent}
.sev.w{border-left-color:var(--attn)} .sev.c{border-left-color:var(--crit)} .sev.i{border-left-color:var(--primary)}
.rowbtn{opacity:0;transition:opacity .15s}
tr:hover .rowbtn,tr:focus-within .rowbtn{opacity:1}

/* ── form ───────────────────────────────────────────────────────────── */
.field{display:flex;flex-direction:column;gap:5px}
.field label{font-size:12.5px;font-weight:500}
.field .hint{font-size:11.5px;color:var(--ink-3)}
.input,select.input{width:100%;padding:7px 10px;min-height:34px;border:1px solid var(--line);
  border-radius:var(--r);background:var(--card);transition:border-color .18s}
.input:hover{border-color:var(--ink-3)}
.input.mono{font-family:var(--mono);font-size:12.5px}
input[type="date"].input{color-scheme:light}
input[type="date"].input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.75}
input[type="date"].input::-webkit-calendar-picker-indicator:hover{opacity:1}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) input[type="date"].input{color-scheme:dark}
}
:root[data-theme="dark"] input[type="date"].input{color-scheme:dark}
.filebox{display:flex;align-items:center;gap:var(--s2);padding:7px 10px;border:1px dashed var(--line);
  border-radius:var(--r);background:var(--card-2)}
.filebox .name{font-family:var(--mono);font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.check{display:flex;gap:9px;align-items:flex-start;padding:8px 10px;border:1px solid var(--line);
  border-radius:var(--r);cursor:pointer;transition:border-color .18s,background .18s}
.check:hover{background:var(--card-2)}
.check input{margin-top:2px;accent-color:var(--primary);width:15px;height:15px;cursor:pointer}
.check b{font-weight:500;display:block;font-size:13px}
.switch{position:relative;width:38px;height:21px;flex-shrink:0}
.switch input{opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.switch span{position:absolute;inset:0;background:var(--line);border-radius:20px;pointer-events:none;transition:background .2s}
.switch span::after{content:"";position:absolute;top:2px;left:2px;width:17px;height:17px;border-radius:50%;
  background:#fff;transition:transform .2s;box-shadow:0 1px 2px rgba(0,0,0,.25)}
.switch input:checked+span{background:var(--primary)}
.switch input:checked+span::after{transform:translateX(17px)}
.switch input:focus-visible+span{outline:2px solid var(--primary);outline-offset:2px}
.row{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap}
.divider{height:1px;background:var(--line-2);margin:var(--s3) 0}

/* ── stage rail (Stage 0→6, the project's own pipeline) ─────────────── */
.stages{display:flex;flex-direction:column;gap:0}
.stage{display:grid;grid-template-columns:26px 1fr auto;gap:var(--s3);align-items:start;
  padding:10px 0;position:relative}
.stage+.stage{border-top:1px solid var(--line-2)}
.stage .n{width:24px;height:24px;border-radius:50%;border:1px solid var(--line);
  display:grid;place-items:center;font-family:var(--mono);font-size:11px;color:var(--ink-3);background:var(--card)}
.stage.done .n{background:var(--ok);border-color:var(--ok);color:#fff}
.stage.run .n{background:var(--primary);border-color:var(--primary);color:#fff}
.stage.run{background:var(--primary-soft);margin:0 -8px;padding-left:8px;padding-right:8px;border-radius:var(--r)}
.stage .t{font-size:13px;font-weight:500}
.stage .d{font-size:11.5px;color:var(--ink-2)}
.stage .m{font-family:var(--mono);font-size:11.5px;color:var(--ink-2);white-space:nowrap}

/* ── flag bars ──────────────────────────────────────────────────────── */
.bars{display:flex;flex-direction:column;gap:9px}
.bar{display:grid;grid-template-columns:1fr 46px;gap:var(--s3);align-items:center;
  background:none;border:0;padding:2px 0;cursor:pointer;text-align:left;width:100%}
.bar:hover .bar-t{color:var(--primary)}
/* Nhãn cảnh báo là câu tiếng Việt, không phải mã — để font mono thì chữ có dấu
   nhìn như log máy in ra. */
.bar-t{font-size:12.5px;color:var(--ink);display:block;margin-bottom:3px}
/* ``display:block`` là bắt buộc: cả hai là <span>, mà chiều cao không có tác
   dụng lên phần tử inline — thiếu nó thì thanh tỉ lệ cao 0px, tức là biểu đồ
   chỉ còn chữ với số. */
.bar-track{display:block;height:5px;background:var(--line-2);border-radius:3px;
  overflow:hidden}
.bar-fill{display:block;height:100%;border-radius:3px;background:var(--attn)}
.bar-fill.i{background:var(--primary)} .bar-fill.c{background:var(--crit)}
.bar-n{font-family:var(--mono);font-size:13px;text-align:right;font-variant-numeric:tabular-nums}

/* ── tabs ───────────────────────────────────────────────────────────── */
.tabs{display:flex;gap:var(--s1);border-bottom:1px solid var(--line);padding:0 var(--s4);flex-wrap:wrap}
.tabs button{background:none;border:0;border-bottom:2px solid transparent;padding:10px 12px;
  cursor:pointer;font-size:13px;color:var(--ink-2);margin-bottom:-1px;transition:color .18s,border-color .18s}
.tabs button:hover{color:var(--ink)}
.tabs button[aria-selected="true"]{color:var(--primary);border-bottom-color:var(--primary);font-weight:500}
.tabpane{display:none} .tabpane.on{display:block}

/* ── chips editor ───────────────────────────────────────────────────── */
.chips{display:flex;flex-wrap:wrap;gap:var(--s2)}
.chip{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border-radius:var(--r);
  border:1px solid var(--line);background:var(--card-2);font-size:12.5px;min-width:0}
.chip .x{background:none;border:0;cursor:pointer;color:var(--ink-3);padding:0;
  display:grid;place-items:center;width:16px;height:16px;flex-shrink:0;border-radius:3px}
.chip .x:hover{color:var(--crit);background:var(--crit-soft)}
.chip.custom{border-color:var(--primary);background:var(--primary-soft)}
.chip span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}

/* ── drawer ─────────────────────────────────────────────────────────── */
.scrim{position:fixed;inset:0;background:rgba(11,18,32,.45);z-index:40;display:none}
.scrim.on{display:block}
.drawer{position:fixed;top:0;right:0;bottom:0;width:min(520px,100%);background:var(--card);
  border-left:1px solid var(--line);z-index:41;display:none;flex-direction:column;
  box-shadow:-8px 0 24px rgba(11,18,32,.14)}
.drawer.on{display:flex;animation:slidein .22s ease-out}
@keyframes slidein{from{transform:translateX(16px);opacity:.4}to{transform:none;opacity:1}}
.drawer-hd{padding:var(--s4);border-bottom:1px solid var(--line);display:flex;
  align-items:flex-start;gap:var(--s3)}
.drawer-bd{padding:var(--s4);overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:var(--s4)}
.drawer-ft{padding:var(--s3) var(--s4);border-top:1px solid var(--line);display:flex;gap:var(--s2);flex-wrap:wrap}
.kv{display:grid;grid-template-columns:130px 1fr;gap:6px var(--s3);font-size:12.5px;align-items:baseline}
.kv dt{color:var(--ink-3)} .kv dd{margin:0;font-family:var(--mono);word-break:break-word}
.flagcard{border:1px solid var(--line);border-left:3px solid var(--attn);border-radius:var(--r);
  padding:10px var(--s3);display:flex;flex-direction:column;gap:4px}
.flagcard.c{border-left-color:var(--crit)} .flagcard.i{border-left-color:var(--primary)}

/* ── log ────────────────────────────────────────────────────────────── */
.log{font-family:var(--mono);font-size:12px;line-height:1.65;background:var(--card-2);
  border-radius:var(--r);padding:var(--s3);max-height:290px;overflow:auto}
.log div{display:flex;gap:var(--s2);padding:1px 0}
.log .lv{flex-shrink:0;width:62px;font-weight:600}
.log .W{color:var(--attn-ink)} .log .E{color:var(--crit)} .log .I{color:var(--ok)} .log .D{color:var(--ink-3)}
.log .ts{color:var(--ink-3);flex-shrink:0}

/* ── misc ───────────────────────────────────────────────────────────── */
.note{display:flex;gap:10px;padding:10px var(--s3);border-radius:var(--r);font-size:12.5px;
  border:1px solid var(--line);background:var(--card-2);align-items:flex-start}
.note svg{width:16px;height:16px;flex-shrink:0;margin-top:1px}
.note.warn{background:var(--attn-soft);border-color:color-mix(in srgb,var(--attn) 30%,transparent);color:var(--attn-ink)}
.note.info{background:var(--primary-soft);border-color:color-mix(in srgb,var(--primary) 26%,transparent);color:var(--primary-deep)}
.note.ok{background:var(--ok-soft);border-color:color-mix(in srgb,var(--ok) 30%,transparent);color:var(--ok)}
.empty{padding:var(--s6) var(--s4);text-align:center;display:flex;flex-direction:column;
  align-items:center;gap:var(--s2);color:var(--ink-2)}
.empty svg{width:28px;height:28px;color:var(--ink-3)}
.bulkbar{display:flex;align-items:center;gap:var(--s3);padding:8px var(--s4);
  background:var(--primary-soft);border-bottom:1px solid var(--line);flex-wrap:wrap}
.toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(8px);
  background:var(--rail);color:#fff;padding:10px 16px;border-radius:var(--r);font-size:13px;
  z-index:60;opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;
  display:flex;align-items:center;gap:9px;box-shadow:0 6px 20px rgba(11,18,32,.3)}
.toast.on{opacity:1;transform:translateX(-50%) translateY(0)}
.toast svg{width:16px;height:16px;color:#4ADE80}
.filebar{display:flex;align-items:center;gap:var(--s3);padding:10px var(--s3);
  border:1px solid var(--line);border-radius:var(--r)}
.filebar .ic{width:32px;height:32px;border-radius:var(--r);display:grid;place-items:center;
  background:var(--ok-soft);color:var(--ok);flex-shrink:0}
.filebar .ic svg{width:16px;height:16px}

/* ── phần riêng của app web (ngoài prototype) ───────────────────── */
.nav a{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;
  border-radius:var(--r);color:var(--rail-ink);text-decoration:none;
  font-size:13.5px;transition:background .18s,color .18s}
.nav a:hover{background:rgba(255,255,255,.06);color:#fff}
.nav a[aria-current="page"]{background:var(--primary);color:#fff;font-weight:500}
.nav a[aria-current="page"] .count{background:rgba(0,0,0,.28)}
.nav a svg{width:16px;height:16px;flex-shrink:0}

a.btn{text-decoration:none}
.tabs a{background:none;border:0;border-bottom:2px solid transparent;padding:10px 12px;
  font-size:13px;color:var(--ink-2);margin-bottom:-1px;text-decoration:none;
  transition:color .18s,border-color .18s}
.tabs a:hover{color:var(--ink)}
.tabs a[aria-selected="true"]{color:var(--primary);border-bottom-color:var(--primary);font-weight:500}

/* thông báo sau khi POST */
.flash{display:flex;align-items:flex-start;gap:10px;padding:10px var(--s4);
  border-radius:var(--r);font-size:13px;border:1px solid transparent}
.flash svg{width:16px;height:16px;flex-shrink:0;margin-top:2px}
.flash.ok{background:var(--ok-soft);color:var(--ok);
  border-color:color-mix(in srgb,var(--ok) 30%,transparent)}
.flash.err{background:var(--crit-soft);color:var(--crit);
  border-color:color-mix(in srgb,var(--crit) 30%,transparent)}

/* trang đăng nhập */
.login-page{min-height:100vh;display:grid;place-items:center;padding:var(--s5);
  background:var(--page)}
.login-card{width:100%;max-width:380px;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r);box-shadow:0 4px 20px rgba(15,27,45,.08);overflow:hidden}
.login-mark{background:var(--rail);color:#fff;padding:var(--s5) var(--s5) var(--s4)}
.login-mark b{display:block;font-size:18px;letter-spacing:-.01em}
.login-mark span{font-family:var(--mono);font-size:11.5px;color:#7A8BA5}
.login-body{padding:var(--s5);display:flex;flex-direction:column;gap:var(--s4)}
/* Nút mắt cạnh ô mật khẩu — cùng khuôn với ô mật khẩu mail của CUS. */
.mk-o{position:relative;display:block}
.mk-o .input{padding-right:36px}
.mk-mat{position:absolute;top:0;right:0;width:34px;height:34px;display:grid;place-items:center;
  padding:0;border:0;border-radius:0 var(--r) var(--r) 0;background:none;color:var(--ink-2);cursor:pointer}
.mk-mat:hover{color:var(--ink)}
.mk-mat:focus-visible{outline:2px solid var(--primary);outline-offset:-2px}
.mk-mat .gach{display:none}
.mk-mat[aria-pressed="true"] .gach{display:inline}

form.inline{display:contents}
.formgrid{display:grid;gap:var(--s3)}
.stack{display:flex;flex-direction:column;gap:var(--s3)}
.stack-lg{display:flex;flex-direction:column;gap:var(--s4)}
.wrap{display:flex;gap:var(--s2);flex-wrap:wrap}
.nowrap{white-space:nowrap}

/* dòng đã xử lý ở hàng đợi — vẫn đọc được, chỉ lùi khỏi tầm chú ý */
tbody tr.done td:not(:first-child){opacity:.62}
tbody tr.done:hover td{opacity:1}

/* ── nhóm điều hướng gập được (Cài đặt) ─────────────────────────── */
/* Dùng <details> chứ không phải JS: trạng thái mở/đóng phải đúng ngay ở lần
   vẽ đầu tiên. Nếu để script bật sau khi tải, đang ở trang con mà menu vẫn
   đóng một nhịp rồi mới bung ra. */
.nav .navgroup{display:flex;flex-direction:column}
.nav .navgroup summary{display:flex;align-items:center;gap:10px;padding:9px 10px;
  border-radius:var(--r);color:var(--rail-ink);cursor:pointer;list-style:none;
  font-size:13.5px;transition:background .18s,color .18s}
.nav .navgroup summary::-webkit-details-marker{display:none}
.nav .navgroup summary:hover{background:rgba(255,255,255,.06);color:#fff}
.nav .navgroup summary.on{color:#fff;font-weight:500}
.nav .navgroup summary:focus-visible{outline:2px solid var(--primary);outline-offset:1px}
.nav .navgroup .chev{margin-left:auto;width:14px;height:14px;transition:transform .18s}
.nav .navgroup[open] .chev{transform:rotate(180deg)}
/* Thụt vào và có vạch dọc để thấy rõ đây là mục con của "Cài đặt". */
.nav .subnav{display:flex;flex-direction:column;gap:2px;margin:2px 0 2px 19px;
  padding-left:10px;border-left:1px solid rgba(255,255,255,.1)}
.nav .subnav a{padding:7px 10px;font-size:13px}
/* Rail nằm ngang trên màn hình hẹp: nhóm chiếm trọn một hàng để mục con
   không bị kẹp giữa các mục cấp một. */
@media(max-width:820px){.nav .navgroup{flex-basis:100%}
  .nav .subnav{flex-direction:row;flex-wrap:wrap}}

/* ── sửa tại chỗ ở màn hình Danh mục tham chiếu ─────────────────── */
.input.sm{min-height:28px;padding:4px 8px;font-size:12px}
/* Dòng đang sửa phải nổi lên: bảng nước dài vài chục dòng, cuộn xuống rồi
   quay lại mà không biết đang sửa dòng nào là sửa nhầm dòng. */
tr.editing{background:var(--primary-soft)}
tr.editing td{vertical-align:middle}
tr.editing .btn{white-space:nowrap}
/* Nút của dòng đang sửa luôn hiện — .rowbtn chỉ hiện khi hover, mà form sửa
   thì không được phép biến mất lúc con trỏ rời đi. */
.editbar{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center;
  padding:10px var(--s3);border:1px solid var(--primary);
  background:var(--primary-soft);border-radius:var(--r)}
.chip a.x{display:inline-flex;align-items:center;justify-content:center;
  color:var(--ink-3);text-decoration:none;border-radius:3px;padding:0}
.chip a.x:hover{color:var(--primary);background:var(--primary-soft)}

/* ── thư mục hàng đợi (một lượt chạy = một hàng đợi) ─────────────── */
/* Khung ngoài cầm viền + nền, link và nút xoá nằm trong. Nút xoá là <form> nên
   không thể nằm trong <a>; tách ra thế này cũng để bấm nút xoá không kích hoạt
   luôn link mở hàng đợi. */
.queuerow{display:flex;align-items:center;gap:var(--s2);padding-right:var(--s3);
  border:1px solid var(--line);border-radius:var(--r);
  transition:border-color .18s,background .18s}
.queuerow:hover{background:var(--card-2);border-color:var(--ink-3)}
/* Vạch màu bên trái: quét danh sách là thấy ngay lượt nào còn việc, không phải
   đọc từng con số. */
.queuerow.attn{border-left:3px solid var(--attn)}
.queuedel{display:inline-flex;margin:0;flex-shrink:0}
/* Nút xoá chỉ hiện khi trỏ vào hàng thì người dùng không tìm ra nó; giữ mờ để
   không tranh chú ý với con số "còn phải xử lý", rõ hẳn khi rê chuột. */
.queuedel .btn{opacity:.55;transition:opacity .18s}
.queuerow:hover .queuedel .btn,.queuedel .btn:focus-visible{opacity:1}
.queuecard{display:flex;align-items:center;gap:var(--s3);padding:12px var(--s3);
  text-decoration:none;color:inherit;flex:1;min-width:0}
.queuerow.attn .ic{background:var(--attn-soft);color:var(--attn)}
.queuecard .ic{width:32px;height:32px;border-radius:var(--r);display:grid;
  place-items:center;background:var(--ok-soft);color:var(--ok);flex-shrink:0}
.queuecard .ic svg{width:16px;height:16px}
.queuecard .body{display:flex;flex-direction:column;min-width:0;flex:1}
.queuecard .t{font-size:14px;font-weight:500}
.queuecard .right{display:flex;flex-direction:column;align-items:flex-end;
  gap:2px;text-align:right;flex-shrink:0}
.queuecard .right .n{font-family:var(--mono);font-size:20px;font-weight:600;
  line-height:1.1;color:var(--attn)}
.queuecard .chev{width:16px;height:16px;color:var(--ink-3);flex-shrink:0}
.queuerow:hover .chev{color:var(--primary)}
@media(max-width:720px){.queuecard{flex-wrap:wrap}
  .queuecard .right{align-items:flex-start;text-align:left}
  /* Hẹp thì nút xoá luôn hiện: không có chuột thì không có :hover để gọi nó ra. */
  .queuedel .btn{opacity:1}}

/* ── xem trước dữ liệu file đã sinh ─────────────────────────────── */
/* Bảng cuộn trong khung của nó chứ không kéo dài cả trang: báo cáo có tới 40
   cột và 200 dòng xem trước, để tràn ra thì mất luôn phần dưới màn hình.
   Header dính nhờ thead th{position:sticky} đã có sẵn. */
.tw.tall{max-height:520px;overflow:auto}
/* Mỗi ô một dòng: nội dung dài đã bị cắt ở tầng dữ liệu, cho xuống dòng nữa
   thì các dòng cao thấp khác nhau, dò theo hàng ngang không nổi. */
table.preview td{white-space:nowrap}
table.preview th{vertical-align:bottom}
/* Chữ cái cột Excel — để đối chiếu nhanh với file mở bằng Excel. */
table.preview th .let{font-family:var(--mono);font-size:9.5px;color:var(--ink-3);
  border:1px solid var(--line);border-radius:3px;padding:0 3px;margin-left:3px;
  letter-spacing:0}

/* Tên báo cáo trong bảng lịch sử: 1 lượt chạy thường có cả 3 nên phải gọn,
   tên đầy đủ nằm ở tooltip. */
.rtags{display:inline-flex;gap:4px;white-space:nowrap}
.rtag{padding:1px 6px;border-radius:var(--r);border:1px solid var(--line-2);
      background:var(--card-2);color:var(--ink-2);font-size:11px;white-space:nowrap}
/* Hai nút trên cùng một dòng bảng — form nút Xoá là block nên phải xếp ngang. */
.rowacts{display:inline-flex;align-items:center;gap:var(--s1);justify-content:flex-end;
         white-space:nowrap}
/* Kỳ báo cáo là một khoảng ngày — ngắt giữa hai mốc thì đọc thành 2 kỳ. */
td.period{white-space:nowrap}
.rowacts form{display:inline-flex;margin:0}
.rowacts .btn[disabled]{opacity:.4;cursor:not-allowed}
/* Thêm cột "Báo cáo" làm bảng lịch sử rộng hơn khung ở màn hình hẹp. Ghim cột
   thao tác vào mép phải: nút Xoá phải kéo ngang mới thấy thì coi như không có.
   Nền phải đục, nếu không chữ của cột bên dưới lộ qua khi kéo. */
table.runs td.acts,table.runs th.acts{position:sticky;right:0;background:var(--card);
  box-shadow:-8px 0 8px -8px rgba(0,0,0,.35);text-align:right}
tr:hover table.runs td.acts{background:var(--card)}

/* Bảng tài khoản mail rộng hơn khung (9 cột), nên cột thao tác cũng phải ghim
   vào mép phải — cùng lý do với bảng lịch sử: nút Xoá phải kéo ngang mới thấy
   thì coi như không có. Dòng đang sửa giữ nền riêng, nếu không thì ô ghim đục
   sẽ che mất dấu hiệu "đang sửa dòng này". */
table.accs td.acts,table.accs th.acts{position:sticky;right:0;background:var(--card);
  box-shadow:-8px 0 8px -8px rgba(0,0,0,.35);text-align:right}
table.accs tr.editing td.acts{background:var(--primary-soft)}


/* ── hộp thoại chọn file khoá ──────────────────────────── */
/* Ô đường dẫn + nút duyệt đi liền một hàng; cài ở khung hẹp thì nút
   xuống dòng thay vì bóp ô đường dẫn còn hai chữ. */
.pathpick{display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap}
.pathpick .input{flex:1 1 220px;min-width:0}
.pathpick .btn{flex:0 0 auto;white-space:nowrap}

.picker{position:fixed;z-index:41;inset:auto;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(880px,calc(100vw - 32px));height:min(620px,calc(100vh - 48px));
  background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:0 18px 48px rgba(11,18,32,.28);display:none;flex-direction:column;overflow:hidden}
.picker.on{display:flex;animation:popin .16s ease-out}
@keyframes popin{from{transform:translate(-50%,-48%);opacity:.5}to{transform:translate(-50%,-50%);opacity:1}}
.picker-hd{padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);
  display:flex;align-items:flex-start;gap:var(--s3)}
.picker-hd h2{font-size:15px}
.picker-hd .btn{margin-left:auto;flex:0 0 auto}
.picker-bar{padding:var(--s3) var(--s4) var(--s2);display:flex;gap:var(--s2);align-items:center}
.picker-bar #pickPath{flex:1 1 auto;min-width:0}
.picker-bar #pickFilter{flex:0 0 180px}
.picker-crumbs{padding:0 var(--s4) var(--s2);display:flex;flex-wrap:wrap;align-items:center;
  gap:2px;font-size:11.5px;color:var(--ink-3)}
.picker-crumbs button{background:none;border:0;padding:2px 5px;border-radius:3px;cursor:pointer;
  color:var(--ink-2);font:inherit;font-family:var(--mono)}
.picker-crumbs button:hover{background:var(--card-2);color:var(--ink)}
.picker-bd{flex:1;min-height:0;display:grid;grid-template-columns:180px 1fr;
  border-top:1px solid var(--line-2)}
.picker-side{border-right:1px solid var(--line-2);overflow-y:auto;padding:var(--s2);
  display:flex;flex-direction:column;gap:2px;background:var(--card-2)}
.picker-side button{text-align:left;background:none;border:0;font:inherit;cursor:pointer;
  padding:6px 8px;border-radius:var(--r);color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.picker-side button:hover{background:var(--card);color:var(--ink)}
.picker-list{overflow-y:auto;padding:var(--s2)}
.picker-row{display:flex;align-items:center;gap:var(--s2);width:100%;text-align:left;
  background:none;border:0;font:inherit;cursor:pointer;padding:6px 8px;border-radius:var(--r);
  color:var(--ink)}
.picker-row:hover{background:var(--card-2)}
.picker-row.on{background:var(--primary-soft);color:var(--ink)}
.picker-row svg{width:15px;height:15px;flex:0 0 auto;color:var(--ink-3)}
.picker-row.dir svg{color:var(--primary)}
.picker-row .nm{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-family:var(--mono);font-size:12.5px}
/* Ghi chú nhận dạng khoá — thứ duy nhất phân biệt được file khoá thật với
   mấy file .json khác cùng thư mục, nên cho nó chỗ riêng thay vì tooltip. */
.picker-row .note{flex:0 1 auto;font-size:11px;color:var(--ok);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:46%}
/* File không phải .json vẫn hiện (mờ, không bấm được): thư mục trống trơn
   khiến người dùng tưởng vào nhầm chỗ. */
.picker-row.other{cursor:default;color:var(--ink-3);opacity:.72}
.picker-row.other:hover{background:none}
.picker-empty{padding:var(--s4);color:var(--ink-3);font-size:12.5px}
.picker-ft{padding:var(--s3) var(--s4);border-top:1px solid var(--line);
  display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap}
.picker-ft #pickNote{flex:1 1 240px;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.grow{flex:1 1 auto}
@media (max-width:720px){
  .picker-bd{grid-template-columns:1fr}
  .picker-side{display:none}
  .picker-bar #pickFilter{flex:1 1 120px}}

/* ── xem chi tiết một chứng từ ───────────────────────────────────────
   Hai cột: bản PDF gốc bên trái, dữ liệu đã trích xuất bên phải. Chiều cao
   khoá theo màn hình để khung PDF cuộn *bên trong* nó — cuộn cả trang thì
   người đối chiếu mất bảng dữ liệu khỏi tầm mắt đúng lúc đang cần so. */
.doc-review{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);
  gap:var(--s4);align-items:start}
/* Chiều cao *cố định* theo màn hình, không phải max-height: để nội dung tự
   quyết thì khung PDF co lại còn đúng min-height và người đối chiếu phải phóng
   to mới đọc được, trong khi nửa dưới màn hình bỏ trống. */
.doc-pdf{position:sticky;top:var(--s4);display:flex;flex-direction:column;
  height:calc(100vh - 132px);min-height:520px;overflow:hidden}
.doc-frame{flex:1 1 auto;width:100%;min-height:420px;border:0;
  border-top:1px solid var(--line-2);background:var(--card-2)}
.doc-data{display:flex;flex-direction:column;gap:var(--s4);min-width:0}

/* Một ô dữ liệu. Nhãn - giá trị - nút sửa, cùng một hàng cho cả tầng header
   lẫn tầng dòng hàng: với người đối chiếu đó là cùng một việc. */
.fr{display:grid;grid-template-columns:150px minmax(0,1fr) auto;gap:var(--s3);
  align-items:baseline;padding:8px var(--s4);border-bottom:1px solid var(--line-2)}
.fr:last-child{border-bottom:0}
.fr.da-sua{background:var(--primary-soft)}
.fr-lb{color:var(--ink-3);font-size:12.5px}
.fr-v{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap;min-width:0;
  font-size:13px;word-break:break-word}
/* "chưa có" thay cho ô trắng tinh: một ô trống không phân biệt được với một ô
   chưa render, còn con số 0 thì đọc như một sự thật. */
.fr-trong{color:var(--ink-3);font-style:italic;font-size:12px}
.fr-act{display:flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:flex-end}

/* Ô nhập gấp lại bằng <details>: không cần JavaScript, và mở nhiều ô cùng lúc
   vẫn được — sửa xong một tờ hoá đơn thường là sửa vài ô liền nhau. */
.fx{position:relative}
.fx>summary{list-style:none;cursor:pointer}
.fx>summary::-webkit-details-marker{display:none}
.fx-form{display:flex;gap:6px;align-items:center;margin-top:6px}
.fx-form .input{min-width:150px}
.fx-hint{margin-top:4px;max-width:320px}

.line-card{border-bottom:1px solid var(--line-2)}
.line-card:last-child{border-bottom:0}
.line-card>summary{display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  padding:8px var(--s4);cursor:pointer;list-style:none}
.line-card>summary::-webkit-details-marker{display:none}
.line-card>summary:hover{background:var(--card-2)}
.line-card[open]>summary{border-bottom:1px solid var(--line-2)}
.line-bd{padding:4px 0 8px;background:var(--card-2)}
.line-bd .fr{background:none}
.line-bd .fr.da-sua{background:var(--primary-soft)}

@media (max-width:1100px){
  .doc-review{grid-template-columns:1fr}
  .doc-pdf{position:static;max-height:none}
  .doc-frame{height:70vh}
  .fr{grid-template-columns:1fr;gap:4px}
  .fr-act{justify-content:flex-start}}

/* ── cột "Cảnh báo" của bảng Chứng từ ────────────────────────────────
   Nhãn cờ ở đây là **câu diễn giải** của ``FLAG_DESCRIPTIONS``, không phải
   nhãn ngắn: 43/46 mã tầng chứng từ không có nhãn ngắn trong ``FLAG_INFO``,
   nên đổi sang ``flag_label()`` là in mã trần ra màn hình cho gần như mọi
   dòng — đúng thứ quy ước "No raw flag codes on screen" cấm.

   Nên chỗ phải sửa là bề ngang. ``.pill`` mặc định ``white-space:nowrap`` +
   ``inline-flex``, tức một hộp **không thể co**: ``max-width`` đặt trên ô
   không ràng buộc được nó, thẻ tràn ra ngoài và **vẽ đè lên cột bên cạnh**
   (người dùng báo 28/08/2026 — cờ ``bkhd_currency_not_usd`` dài 99 ký tự,
   cần ~545px trong một ô 260px, đè lên cả *File gốc* lẫn *Trạng thái*).

   ``.pill.wrap`` cho xuống dòng trong thẻ; ``td.flags`` là chỗ ``max-width``
   thật sự bám vào. Cần **cả hai**: chỉ nới ô thì thẻ vẫn không co, chỉ cho
   thẻ xuống dòng thì ô vẫn giãn theo bảng.
   ------------------------------------------------------------------ */
.pill.wrap{display:inline-block;white-space:normal;text-align:left;
  line-height:1.35;max-width:100%;overflow-wrap:anywhere}
td.flags{max-width:280px;min-width:170px;white-space:normal}
/* Nhiều thẻ trên một ô: xếp dọc, mỗi thẻ một dòng. Để chúng chảy ngang thì
   hai câu dài ghép lại đọc thành một câu, và mắt không thấy đâu là ranh
   giới giữa hai cảnh báo khác nhau. */
td.flags .pill.wrap{display:block;width:fit-content}
td.flags .pill.wrap + .pill.wrap{margin-top:4px}

/* ── hộp thoại (<dialog>) ───────────────────────────────────────────── */
/* Dùng thẻ <dialog> thật nên nền mờ, khoá tiêu điểm bàn phím và phím Esc là
   của trình duyệt; phần dưới đây chỉ lo hình thức cho khớp panel. */
.modal{border:1px solid var(--line);border-radius:var(--r2);padding:0;
  width:min(560px,calc(100vw - 32px));background:var(--card);color:var(--ink);
  box-shadow:0 12px 32px rgba(11,18,32,.18)}
.modal::backdrop{background:rgba(11,18,32,.45)}
.modal-hd{padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3)}
.modal-hd h2{margin:0;font-size:15px}
.modal-bd{padding:var(--s4);display:flex;flex-direction:column;gap:var(--s3)}
.modal-ft{padding:var(--s3) var(--s4);border-top:1px solid var(--line);
  display:flex;gap:var(--s2);flex-wrap:wrap}
