
/* Bố cục: thanh trên cố định + 3 màn (Nhập tuyến / Tra cứu ODF / Danh mục), một cột trên điện thoại, hai cột trên màn rộng */
:root{
  --bg:#0f141b; --surface:#161d26; --surface2:#1d2631; --line:#2a3644;
  --fg:#e8edf3; --muted:#8d9aab; --accent:#e0b04a; --accent-soft:rgba(224,176,74,.14); --on-accent:#1a1405;
  --cable:#4c8dff; --jumper:#f59e0b; --odf:#ef5350; --danger:#f87171; --ok:#4ade80; --warn:#fbbf24;
  --odf-text:#ff8a80; --err-text:#fca5a5; --warn-text:#fcd34d; --info-text:#9ec0ff; --chip-text:#ffb4ac;
  --err-bg:rgba(248,113,113,.14); --warn-bg:rgba(251,191,36,.14); --info-bg:rgba(76,141,255,.16);
  --shadow:0 1px 2px rgba(0,0,0,.3);
  --font:"Be Vietnam Pro","Segoe UI",Roboto,Arial,sans-serif;
  --mono:"Roboto Mono",Consolas,ui-monospace,monospace;
  color-scheme:dark;
}
:root[data-theme="light"]{
  --bg:#f3f5f8; --surface:#ffffff; --surface2:#f1f4f8; --line:#d9e0e8;
  --fg:#16202b; --muted:#5b6878; --accent:#b7791f; --accent-soft:rgba(183,121,31,.12); --on-accent:#ffffff;
  --cable:#1f6feb; --jumper:#d97706; --odf:#d93025; --danger:#c62828; --ok:#15803d; --warn:#a16207;
  --odf-text:#c62828; --err-text:#b91c1c; --warn-text:#92400e; --info-text:#1d4ed8; --chip-text:#b42318;
  --err-bg:rgba(220,38,38,.10); --warn-bg:rgba(217,119,6,.12); --info-bg:rgba(31,111,235,.10);
  --shadow:0 1px 3px rgba(16,24,40,.08);
  color-scheme:light;
}
*{box-sizing:border-box}
body{background:var(--bg);color:var(--fg);font-family:var(--font);font-size:15px;line-height:1.5}
button,input,select{font:inherit;color:inherit}
.top{position:sticky;top:env(safe-area-inset-top,0px);z-index:5;background:var(--surface);border-bottom:1px solid var(--line);
  padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px));padding-block:10px 0}
.brand{display:flex;align-items:center;justify-content:space-between;gap:12px}
.brand h1{font-size:17px;font-weight:700;margin:0;letter-spacing:.2px}
.brand h1 span{color:var(--accent)}
.save{font-size:12px;color:var(--muted)}
.tabs{display:flex;gap:4px;margin-top:8px;overflow-x:auto}
.tab{background:none;border:0;border-bottom:3px solid transparent;padding:10px 12px;color:var(--muted);font-weight:600;white-space:nowrap;cursor:pointer;min-height:44px}
.tab[aria-selected="true"]{color:var(--fg);border-color:var(--accent)}
main{padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px));padding-block:16px 40px;max-width:1280px;margin:0 auto}
.grid{display:grid;gap:16px}
.grid>*{min-width:0}
.hop-head select{flex:1 1 0;width:0;min-width:0}
.hop-row>*{min-width:0;width:100%}
.tools .btn.icon{width:34px}
@media(min-width:1000px){.grid.two{grid-template-columns:minmax(380px,460px) minmax(0,1fr);align-items:start}}
.panel{background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:14px;min-width:0}
.panel h2{font-size:14px;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);margin:0 0 10px;font-weight:600}
.row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
select,input{background:var(--surface2);border:1px solid var(--line);border-radius:8px;padding:10px;min-height:44px;min-width:0}
select:focus,input:focus,button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.btn{background:var(--surface2);border:1px solid var(--line);border-radius:8px;padding:10px 14px;min-height:44px;cursor:pointer;font-weight:600}
.btn.primary{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
.btn.ghost{background:none}
.btn.danger{color:var(--danger)}
.btn.sm{min-height:36px;padding:6px 10px;font-size:13px}
.icon{width:40px;min-height:40px;padding:0;display:inline-grid;place-items:center}
.mono{font-family:var(--mono)}
.muted{color:var(--muted)}
.field{display:grid;gap:4px;flex:1 1 160px}
.field label{font-size:12px;color:var(--muted)}
.route-meta{display:grid;gap:8px;grid-template-columns:1fr 1fr;margin-top:10px}
@media(max-width:480px){.route-meta{grid-template-columns:1fr}}
.status{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:600;padding:4px 10px;border-radius:999px;background:var(--surface2)}
.status.ok{color:var(--ok)} .status.err{color:var(--danger)}
/* Hop cards */
.hops{display:grid;gap:0;margin-top:12px}
.hop{background:var(--surface2);border:1px solid var(--line);border-radius:10px;padding:10px;display:grid;gap:8px}
.hop.has-err{border-color:rgba(248,113,113,.6)}
.hop-head{display:flex;gap:8px;align-items:center}
.idx{flex:none;width:30px;height:30px;border-radius:50%;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;font-weight:700;font-size:14px}
.hop-head select{flex:1}
.tools{display:flex;gap:2px}
.tools .btn{background:none;border-color:transparent;color:var(--muted)}
.hop-row{display:grid;grid-template-columns:40px minmax(0,1fr) minmax(0,1fr);gap:8px;align-items:center}
.hop-row .lbl{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase}
.msgs{display:grid;gap:4px;font-size:13px}
.msgs:empty{display:none}
.msg.err{color:var(--danger)} .msg.warn{color:var(--warn)}
.link{display:flex;align-items:center;gap:10px;padding:6px 0 6px 14px;font-size:13px;color:var(--muted)}
.link::before{content:"";width:3px;align-self:stretch;min-height:22px;background:var(--cable);border-radius:2px}
.link.bad::before{background:var(--danger)}
.add-row{margin-top:12px;width:100%}
.hint{font-size:12px;color:var(--muted);margin-top:8px}
/* Diagram */
.diag-head{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.seg button{background:none;border:0;padding:6px 12px;min-height:36px;color:var(--muted);cursor:pointer;font-weight:600}
.seg button[aria-pressed="true"]{background:var(--accent-soft);color:var(--accent)}
.diag{overflow-x:auto;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:8px}
.diag svg{display:block;margin:0 auto}
.diag.v svg{max-width:100%;height:auto}
.legend{display:flex;gap:14px;flex-wrap:wrap;font-size:12px;color:var(--muted);margin-top:8px}
.legend i{display:inline-block;width:22px;height:0;border-top:3px solid var(--cable);vertical-align:middle;margin-right:6px}
.legend i.j{border-top:3px dashed var(--jumper)}
.legend i.o{width:14px;height:10px;border:2px dashed var(--odf)}
/* Table */
.tbl-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:8px}
table{border-collapse:collapse;width:100%;font-size:13px;min-width:600px}
th,td{padding:8px 10px;border-bottom:1px solid var(--line);text-align:left;white-space:nowrap}
th{color:var(--muted);font-weight:600;background:var(--surface2);font-size:12px}
td.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
td .odf{color:var(--odf-text);font-family:var(--mono)}
.pill{font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px}
.pill.c{background:var(--info-bg);color:var(--info-text)} .pill.j{background:var(--warn-bg);color:var(--warn-text)}
/* Lookup */
.results{display:grid;gap:6px;margin-top:10px}
.res{display:flex;justify-content:space-between;gap:10px;align-items:center;background:var(--surface2);border:1px solid var(--line);border-radius:8px;padding:10px 12px;cursor:pointer;text-align:left;width:100%;min-height:48px}
.res[aria-current="true"]{border-color:var(--accent)}
.ports{display:grid;grid-template-columns:repeat(auto-fill,minmax(38px,1fr));gap:4px;margin-top:10px}
.port{font-family:var(--mono);font-size:12px;text-align:center;padding:6px 0;border-radius:6px;background:var(--surface2);color:var(--muted);border:1px solid var(--line)}
.port.used{background:var(--info-bg);color:var(--info-text);border-color:var(--cable)}
.port.multi{background:var(--err-bg);color:var(--err-text);border-color:var(--danger)}
.kv{display:flex;gap:18px;flex-wrap:wrap;font-size:13px}
.kv b{font-size:20px;display:block;font-variant-numeric:tabular-nums}
.route-card{border-top:1px solid var(--line);padding-top:12px;margin-top:12px}
/* Catalog */
.st-card{background:var(--surface2);border:1px solid var(--line);border-radius:10px;padding:12px;display:grid;gap:8px}
.chips{display:flex;gap:6px;flex-wrap:wrap}
.chip{font-family:var(--mono);font-size:12px;border:1px dashed var(--odf);color:var(--chip-text);border-radius:6px;padding:4px 8px;display:inline-flex;gap:6px;align-items:center}
.chip button{background:none;border:0;color:var(--muted);cursor:pointer;padding:0 2px;font-size:14px}
.cat-grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}
.note{font-size:13px;color:var(--muted);border-left:3px solid var(--accent);padding:6px 10px;background:var(--accent-soft);border-radius:0 6px 6px 0}
.toast{position:fixed;left:50%;bottom:calc(20px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);background:var(--fg);color:var(--bg);padding:10px 16px;border-radius:8px;font-weight:600;font-size:14px;z-index:20}
@media (prefers-reduced-motion:no-preference){.hop{transition:border-color .2s}}

/* ---- bổ sung cho bản chạy server ---- */
.user{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}
.user input{min-height:32px;padding:4px 8px;width:130px;font-size:13px}
.ports-cell{display:flex;gap:6px;min-width:0}
.ports-cell input{flex:1;min-width:0;width:100%}
.qr-btn{flex:none;width:44px;min-height:44px;padding:0;display:grid;place-items:center;background:var(--surface2);border:1px solid var(--line);border-radius:8px;cursor:pointer;color:var(--accent)}
.qr-btn svg{width:22px;height:22px}
.save-bar{display:flex;gap:8px;margin-top:12px}
.save-bar .btn{flex:1}
.errbox{margin-top:10px;border:1px solid var(--danger);background:var(--err-bg);border-radius:8px;padding:10px;font-size:13px;color:var(--err-text)}
.errbox:empty{display:none}
.dirty{color:var(--warn);font-size:12px;font-weight:600}
.modal{position:fixed;inset:0;background:rgba(0,0,0,.75);z-index:30;display:grid;place-items:center;padding:16px}
.modal-box{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:14px;width:min(440px,100%);display:grid;gap:10px}
#qrReader{width:100%;border-radius:8px;overflow:hidden;background:#000;min-height:240px}
.offline{background:var(--err-bg);color:var(--err-text);text-align:center;font-size:13px;padding:6px}
.brand h1{white-space:nowrap}
@media(max-width:480px){.user span{display:none}.user input{width:110px}}

/* ---- báo cáo ---- */
.kpis{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
@media(min-width:700px){.kpis{grid-template-columns:repeat(4,minmax(0,1fr))}}
#rpOdf{min-width:0}
.kpi{background:var(--surface2);border:1px solid var(--line);border-radius:10px;padding:12px}
.kpi b{display:block;font-size:24px;font-variant-numeric:tabular-nums;line-height:1.2}
.kpi span{font-size:12px;color:var(--muted)}
.kpi.err b{color:var(--danger)} .kpi.warn b{color:var(--warn)} .kpi.ok b{color:var(--ok)}
.meter{height:6px;background:var(--line);border-radius:3px;margin-top:8px;overflow:hidden}
.meter i{display:block;height:100%;background:var(--cable)}
@media(min-width:1000px){.grid.two-eq{grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);align-items:start}}
.rtype{display:grid;grid-template-columns:76px minmax(0,1fr) 48px;gap:8px;align-items:center;width:100%;background:none;border:0;border-bottom:1px solid var(--line);padding:8px 4px;text-align:left;cursor:pointer;min-height:44px}
.rtype:hover,.rtype[aria-current="true"]{background:var(--surface2)}
.rtype .bar{height:6px;border-radius:3px;background:var(--line);margin-top:4px;overflow:hidden}
.rtype .bar i{display:block;height:100%}
.rtype .n{text-align:right;font-variant-numeric:tabular-nums;font-weight:700}
.rtype.zero{opacity:.45}
.sev{font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;text-align:center}
.sev.e{background:var(--err-bg);color:var(--err-text)} .sev.w{background:var(--warn-bg);color:var(--warn-text)} .sev.i{background:var(--info-bg);color:var(--info-text)}
.act{font-size:13px;padding:6px 0;border-bottom:1px solid var(--line)} .act:last-child{border-bottom:0}
#rpList td{white-space:normal;min-width:80px} #rpList td.wide{min-width:240px}

/* ---- giao diện sáng/tối, chữ ---- */
body{-webkit-font-smoothing:antialiased;font-feature-settings:"tnum" 0;letter-spacing:.005em}
h1,h2,.tab,.btn{font-family:var(--font)}
.panel{box-shadow:var(--shadow)}
.theme-btn{background:var(--surface2);border:1px solid var(--line);border-radius:999px;min-height:36px;padding:0 12px;display:inline-flex;align-items:center;gap:6px;cursor:pointer;color:var(--fg);font-size:13px;font-weight:600}
.theme-btn svg{width:16px;height:16px}
.brand-right{display:flex;align-items:center;gap:10px}

/* ---- tất cả tuyến ---- */
.filters{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr) 180px;gap:8px}
@media(max-width:700px){.filters{grid-template-columns:1fr}}
#allTbl td{white-space:normal} #allTbl td.path{min-width:260px;font-size:12px;color:var(--muted)}
#allTbl td.path b{color:var(--fg);font-weight:600}
.st-pill{font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;white-space:nowrap}
.st-pill.ok{background:rgba(74,222,128,.14);color:var(--ok)} .st-pill.err{background:var(--err-bg);color:var(--err-text)} .st-pill.note{background:var(--warn-bg);color:var(--warn-text)}
.map-wrap{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:12px;margin-top:6px}
@media(max-width:900px){.map-wrap{grid-template-columns:1fr}}
#cy{height:68vh;min-height:420px;border:1px solid var(--line);border-radius:8px;background:var(--bg)}
.map-info{background:var(--surface2);border:1px solid var(--line);border-radius:8px;padding:12px;max-height:68vh;overflow:auto;font-size:13px}
.map-info h3{margin:0 0 4px;font-size:15px}
.map-info .rlist button{display:block;width:100%;text-align:left;background:none;border:0;border-bottom:1px solid var(--line);padding:8px 2px;cursor:pointer;color:var(--fg);font:inherit}
.map-info .rlist button:hover{color:var(--accent)}

/* ---- đăng nhập, người dùng, phân quyền ---- */
body:not(.role-admin) [data-min="admin"]{display:none!important}
body.role-viewer [data-min="editor"]{display:none!important}
body.role-viewer #hops, body.role-viewer #routeCode, body.role-viewer #routeDesc{pointer-events:none}
body.role-viewer #hops .qr-btn, body.role-viewer #hops .tools{display:none}
.auth{position:fixed;inset:0;z-index:40;display:grid;place-items:center;background:var(--bg);
  padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px));padding-block:16px}
.auth-box{width:min(400px,100%);background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:24px;display:grid;gap:10px;box-shadow:var(--shadow)}
.auth-box h1{margin:0;font-size:24px;font-weight:700}
.auth-box h1 span{color:var(--accent)}
.auth-logo{width:44px;height:44px;border-radius:12px;background:linear-gradient(135deg,var(--accent),var(--cable))}
.user-menu{position:relative}
.user-chip{background:var(--surface2);border:1px solid var(--line);border-radius:999px;min-height:36px;padding:0 12px;color:var(--fg);font:inherit;font-size:13px;font-weight:600;cursor:pointer;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.user-pop{position:absolute;right:0;top:calc(100% + 6px);background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:6px;min-width:200px;z-index:20;box-shadow:0 8px 24px rgba(0,0,0,.25)}
.user-pop button{display:block;width:100%;text-align:left;background:none;border:0;padding:10px 8px;border-radius:6px;color:var(--fg);font:inherit;cursor:pointer}
.user-pop button:hover{background:var(--surface2)}
textarea{width:100%;background:var(--surface2);border:1px solid var(--line);border-radius:8px;padding:10px;color:var(--fg);font-family:var(--mono);font-size:13px;resize:vertical}
#usersTbl td{white-space:normal} #usersTbl select{min-height:36px;padding:4px 8px}
@media(max-width:600px){.theme-btn span{display:none}}

/* ---- tối ưu điện thoại: chữ to, ô nhập 16px (không bị tự phóng to trên iPhone), thao tác một tay ---- */
@media (max-width: 640px){
  body{font-size:16px}
  input, select, textarea, .btn{font-size:16px}
  .btn.sm{font-size:14px;min-height:40px}
  .hint{font-size:13px}
  main{padding-block:12px 24px}
  .panel{padding:12px;border-radius:12px}
  .panel h2{font-size:13px}
  .brand h1{font-size:16px}
  .tab{padding:10px 10px;font-size:15px}
  /* thẻ trạm: ODF chiếm cả hàng, ô port + nút QR xuống hàng dưới */
  .hop{padding:12px;gap:10px}
  .hop-head{flex-wrap:wrap}
  .hop-head select{font-size:16px;font-weight:600;flex:1 1 0;width:0;min-width:0;max-width:calc(100% - 40px)}
  .hop-head .tools{width:100%;justify-content:flex-end;margin-top:-4px}
  .hop-row{grid-template-columns:44px minmax(0,1fr);grid-template-areas:"lbl odf" ". ports";row-gap:6px}
  .hop-row .lbl{grid-area:lbl;font-size:13px}
  .hop-row > select{grid-area:odf}
  .hop-row > .ports-cell{grid-area:ports}
  .ports-cell input{font-size:18px;letter-spacing:.5px}
  .qr-btn{width:52px;min-height:48px}
  .tools .btn.icon{width:40px;min-height:40px;font-size:18px}
  .link{font-size:14px}
  .msgs{font-size:14px}
  /* nút Lưu luôn nằm dưới cùng màn hình */
  .save-bar{position:sticky;bottom:0;z-index:4;background:var(--surface);margin:12px -12px -12px;padding:10px 12px calc(10px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--line);border-radius:0 0 12px 12px}
  .save-bar .btn{min-height:50px;font-size:17px}
  .route-meta{grid-template-columns:1fr}
  .kpi b{font-size:22px}
  #cy{height:60vh;min-height:360px}
  .user-chip{max-width:130px}
  .theme-btn{padding:0 10px}
  /* bảng thành thẻ */
  table.cards{min-width:0}
  table.cards tr:first-child{display:none}
  table.cards tr{display:block;border-bottom:1px solid var(--line);padding:8px 0}
  table.cards td{display:block;position:relative;border:0;padding:5px 12px 5px 120px;white-space:normal;min-width:0!important;font-size:15px;min-height:28px}
  table.cards td::before{content:attr(data-label);position:absolute;left:12px;top:7px;width:100px;color:var(--muted);font-size:12px;font-weight:600}
  table.cards td[data-label=""]{padding-left:12px}
  table.cards td[data-label=""]::before{content:none}
  .tbl-wrap:has(table.cards){border-radius:10px}
}
