:root{
  --bg:#0b1220; --bg-2:#0f172a; --panel:#111c30; --panel-2:#1e293b;
  --border:#243049; --border-2:#334155;
  --text:#e2e8f0; --text-dim:#94a3b8; --text-faint:#64748b;
  --accent:#3b82f6; --accent-hi:#60a5fa; --accent-press:#2563eb;
  --green:#22c55e; --green-soft:#4ade80; --amber:#f59e0b; --red:#ef4444; --red-soft:#f87171;
  --radius:10px;
}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
#app{height:100%;display:flex;flex-direction:column}
body{
  background:var(--bg); color:var(--text);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  font-size:14px; line-height:1.45;
}
a{color:var(--accent-hi);text-decoration:none}
button{font-family:inherit}

/* ---------- Top bar ---------- */
.topbar{
  position:relative;
  height:56px; flex:none; display:flex; align-items:center; justify-content:space-between;
  padding:0 22px; border-bottom:1px solid var(--border); background:var(--bg-2);
}
/* Centered Bifrost wordmark, absolutely positioned so it stays centered in the
   window regardless of the left brand / right profile widths. */
.topbar-logo{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  height:30px; width:auto; opacity:.85; pointer-events:none;
}
.login-logo{height:40px;width:auto;opacity:.9}
.brand{display:flex;align-items:center;gap:10px;font-weight:700;letter-spacing:.5px}
.brand .logo{width:26px;height:26px;border:2px solid var(--accent-hi);border-radius:6px;
  display:flex;align-items:center;justify-content:center;transform:rotate(45deg)}
.brand .logo i{width:8px;height:8px;background:var(--accent-hi);border-radius:2px}
.brand .sub{font-weight:400;color:var(--text-faint);font-size:12px;letter-spacing:2px;text-transform:uppercase}
.who{display:flex;align-items:center;gap:12px;font-size:13px;color:var(--text-dim)}
.profile-btn{display:flex;align-items:center;gap:10px;background:none;border:none;cursor:pointer;color:var(--text-dim);padding:4px 6px;border-radius:8px}
.profile-btn:hover{background:var(--panel)}
.profile-name{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}
.profile-menu{position:fixed;background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:6px;min-width:180px;box-shadow:0 10px 30px rgba(0,0,0,.4);z-index:60;display:flex;flex-direction:column;gap:2px}
.profile-menu-item{text-align:left;background:none;border:none;color:var(--text);padding:9px 12px;border-radius:7px;cursor:pointer;font-size:13px}
.profile-menu-item:hover{background:var(--panel-2)}
.profile-menu-item.danger{color:var(--red-soft)}
.profile-menu-item{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%}
.pm-badge{background:var(--accent,#3b82f6);color:#fff;font-size:11px;font-weight:600;min-width:18px;height:18px;line-height:18px;text-align:center;border-radius:9px;padding:0 5px}
.acc-group{padding:10px 0;border-top:1px solid var(--border)}
.acc-group:first-child{border-top:none}
.acc-org{color:var(--muted);font-weight:400;font-size:12px;margin-left:4px}
.acc-res{list-style:none;margin:6px 0 0;padding:0}
.acc-res li{display:flex;align-items:center;justify-content:space-between;padding:4px 0;font-size:13px;color:var(--text)}
.acc-kind{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.03em}
.side-org{padding:10px 12px;margin-bottom:8px;border:1px solid var(--border);border-radius:8px;background:var(--bg-2)}
.side-org-name{font-weight:600;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side-org-exit{margin-top:8px;width:100%;text-align:left;background:none;border:none;color:var(--accent-hi);cursor:pointer;font-size:12px;padding:0}
.side-org-exit:hover{text-decoration:underline}
.searchbar{margin-bottom:14px}
.searchbar > .input:only-child{max-width:420px}
.bulkbar{display:flex;align-items:center;justify-content:space-between;gap:12px;
  background:var(--panel-2);border:1px solid var(--border-2);border-radius:10px;
  padding:10px 14px;margin-bottom:12px}
.bulk-count{font-weight:600;font-size:13px}
.bulk-actions{display:flex;gap:8px;flex-wrap:wrap}
.bulk-actions .btn{width:auto}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}
.th-sort{cursor:pointer;user-select:none;white-space:nowrap}
.th-sort:hover{color:var(--text)}
.th-sort.active{color:var(--accent-hi)}
.avatar{width:30px;height:30px;border-radius:50%;background:var(--green);color:#06281a;
  display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px}

/* ---------- Layout ---------- */
.shell{display:flex;flex:1;min-height:0}
.side{width:230px;border-right:1px solid var(--border);background:var(--bg-2);padding:16px 12px;display:flex;flex-direction:column;gap:4px}
.side .navitem{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:8px;color:var(--text-dim);cursor:pointer}
.side .navitem:hover{background:var(--panel);color:var(--text)}
.side .navitem.active{background:var(--panel-2);color:#fff}
.side .navitem .nav-badge{margin-left:auto;background:#ef4444;color:#fff;font-size:11px;font-weight:700;min-width:18px;height:18px;line-height:18px;text-align:center;border-radius:999px;padding:0 5px}
.side .navitem .ic{width:18px;height:18px;opacity:.8}
/* External link in the sidebar (the connection gateway). Styled as a nav item but
   never gets the .active state, it leaves the app, so it's never "where you are".
   text-decoration:none because it's a real <a>, unlike the view items. */
.side a.navitem{text-decoration:none}
.side .navitem-ext .navitem-ext-icon{margin-left:auto;font-size:12px;opacity:.55}
.side .navitem-ext:hover .navitem-ext-icon{opacity:.9}
.side .spacer{flex:1}
.main{flex:1;overflow:auto;padding:28px 34px}

/* ---------- Headings ---------- */
.page-title{font-size:22px;font-weight:700;margin:0 0 4px}
.page-sub{color:var(--text-dim);margin:0 0 20px;font-size:13px}

/* ---------- Toolbar ---------- */
.toolbar{display:flex;align-items:center;gap:12px;margin-bottom:16px}
.search{flex:1;max-width:520px}
.input{
  width:100%;padding:10px 12px;background:var(--panel);border:1px solid var(--border);
  border-radius:8px;color:var(--text);font-size:14px;outline:none;
}
.input:focus{border-color:var(--accent)}
.icon-btn{background:var(--panel-2);border:1px solid var(--border);color:var(--text-dim);
  border-radius:8px;padding:9px 11px;cursor:pointer}
.icon-btn:hover{color:var(--text)}

/* ---------- Table ---------- */
.card{background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}
table{width:100%;border-collapse:collapse}
th{ text-align:left;font-size:12px;color:var(--text-dim);font-weight:600;
   padding:14px 16px;border-bottom:1px solid var(--border);white-space:nowrap}
td{padding:14px 16px;border-bottom:1px solid var(--border);font-size:13px}
tr:last-child td{border-bottom:none}
tbody tr:hover{background:rgba(255,255,255,0.02)}
.dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:8px;vertical-align:middle}
.dot.on{background:var(--green)} .dot.off{background:var(--red)}
.pill{display:inline-block;padding:2px 10px;border-radius:999px;font-size:11px;font-weight:600}
.pill.kind{background:rgba(59,130,246,.15);color:var(--accent-hi)}
.pill.role{background:rgba(148,163,184,.15);color:var(--text-dim)}
.pill.green{background:rgba(34,197,94,.12);color:var(--green-soft)}
.pill.red{background:rgba(239,68,68,.15);color:#f08a8a}
.pill.amber{background:rgba(234,179,8,.15);color:#d9a520}
/* Device keys: legacy-claim queue and device alerts */
.da-banner{background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.4);color:#f3a0a0;border-radius:8px;padding:10px 14px;margin:0 0 14px;font-weight:600}
.da-alerts{margin:6px 0 0;padding:8px 10px;background:rgba(239,68,68,.07);border:1px solid rgba(239,68,68,.25);border-radius:6px;font-size:12px}
.da-alert{display:flex;gap:8px;align-items:flex-start;justify-content:space-between;padding:4px 0}
.da-alert + .da-alert{border-top:1px solid rgba(239,68,68,.15)}
.da-alert .k{color:#f08a8a;font-weight:700;white-space:nowrap}
.da-alert.acked{opacity:.55}
.da-alert.acked .k{color:var(--text-dim)}
.da-alert-btn{cursor:pointer;border:none}

/* ---------- Buttons ---------- */
.btn{padding:10px 16px;border-radius:8px;border:1px solid transparent;cursor:pointer;font-weight:600;font-size:13px}
.btn.primary{background:var(--accent);color:#fff}
.btn.primary:hover{background:var(--accent-press)}
.btn.ghost{background:transparent;border-color:var(--border-2);color:var(--text-dim)}
.btn.ghost:hover{color:var(--text)}
/* ---------- Unit configuration card ---------- */
.uc-card{padding:20px 22px;margin-bottom:18px}
.uc-toggle{display:flex;align-items:center;gap:10px;width:100%;background:none;border:none;
  cursor:pointer;padding:0;color:var(--text);text-align:left;font:inherit}
.uc-title-txt{font-weight:700;font-size:15px}
.uc-toggle .uc-ico{width:26px;height:26px;border-radius:7px;display:flex;align-items:center;justify-content:center;
  background:rgba(59,130,246,.14);color:var(--accent-hi);font-size:14px;flex:none}
.uc-chevron{margin-left:auto;color:var(--text-faint);font-size:13px}
.uc-body{margin-top:14px}
.uc-body[hidden]{display:none}
.uc-intro{color:var(--text-faint);font-size:12px;margin-bottom:18px;max-width:560px}
.uc-sec{border:1px solid var(--border);border-radius:10px;padding:16px;margin-bottom:14px;background:var(--bg-2)}
.uc-sec:last-child{margin-bottom:0}
.uc-sec-h{display:flex;align-items:center;gap:8px;font-weight:600;font-size:13px;margin-bottom:4px}
.uc-sec-h .uc-badge{font-size:11px;font-weight:500;color:var(--text-faint);background:var(--panel-2);
  border:1px solid var(--border);border-radius:6px;padding:1px 7px;margin-left:auto}
.uc-sec-sub{color:var(--text-faint);font-size:11px;margin-bottom:12px}
.uc-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.uc-grid .full{grid-column:1 / -1}
.uc-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:12px}
.uc-result{font-size:12px;font-weight:600}
.uc-result.pending{color:var(--text-dim)}
.uc-result.ok{color:var(--green-soft)}
.uc-result.err{color:var(--red-soft)}
.uc-note{color:var(--text-faint);font-size:11px;margin-top:8px}
.uc-chips{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 10px}
/* Interface priority editor. The rows are a preference ORDER, so they're numbered and
   reorderable; an interface bypassed by a lock is dimmed rather than hidden, so the
   operator can still see the order a lock is overriding. */
.uc-prio{display:flex;flex-direction:column;gap:6px;margin-top:8px}
.uc-prio-item{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:8px;
  background:var(--bg-2);border:1px solid var(--border-2)}
.uc-prio-item.inert{opacity:.45}
.uc-prio-n{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;
  border-radius:999px;background:var(--panel-2);color:var(--text-dim);font-size:11px;font-weight:600;flex:none}
.uc-prio-name{flex:1;font-size:13px}
.uc-prio-up{padding:2px 9px;line-height:1.2}
.uc-prio-up:disabled{opacity:.3;cursor:default}
.uc-warn{margin-top:10px;padding:9px 11px;border-radius:8px;font-size:12px;line-height:1.5;
  background:rgba(234,179,8,.1);border:1px solid rgba(234,179,8,.3);color:#d9a520}
.uc-chip{background:var(--panel-2);border:1px solid var(--border-2);color:var(--text);
  border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;display:flex;align-items:center;gap:7px}
.uc-chip:hover{border-color:var(--accent)}
.uc-chip .rssi{color:var(--text-faint);font-size:11px}
.uc-chip.active{border-color:var(--green);color:var(--green-soft)}
.uc-offline{background:rgba(245,158,11,.1);border:1px solid rgba(245,158,11,.3);color:var(--amber);
  border-radius:8px;padding:9px 12px;font-size:12px;margin-bottom:16px}
@media(max-width:640px){.uc-grid{grid-template-columns:1fr}}
.btn.danger{background:transparent;border-color:rgba(239,68,68,.4);color:var(--red-soft)}
.btn.kvm{background:#14532d;border-color:#166534;color:#bbf7d0}
.btn.kvm:hover{background:#166534}
.btn.kvm:disabled{opacity:.5;cursor:default}
/* Terminal shortcut. Deliberately a different hue from the green KVM button: the two
   sit side by side and open different things, so they must not read as one control. */
.btn.term{background:#1e3a5f;border-color:#2d5a8c;color:#cfe0f5}
.btn.term:hover{background:#2d5a8c}
.btn.term:disabled{opacity:.5;cursor:default}
.btn.link{background:none;border:none;color:var(--accent-hi);cursor:pointer;font-weight:600;padding:0}
.row-actions{display:flex;gap:8px;justify-content:flex-end}
.addbar{display:flex;justify-content:flex-end;margin-top:14px}

/* ---------- Login ---------- */
.login-wrap{height:100%;display:flex;align-items:center;justify-content:center;background:
  radial-gradient(1200px 600px at 50% -10%, rgba(59,130,246,.12), transparent), var(--bg)}
.login{width:380px;background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:30px}
.login h1{font-size:20px;margin:0 0 4px;text-align:center}
.login .tag{color:var(--text-dim);text-align:center;margin:0 0 22px;font-size:13px}
/* Heading with no tagline beneath it (the sign-in screen). The tagline used to supply
   the 22px gap above the first field; without it the h1's 4px would crowd the form. */
.login h1.no-tag{margin-bottom:22px}
.field{margin-bottom:14px}
.field label{display:block;font-size:12px;color:var(--text-dim);margin-bottom:6px}
.login .btn{width:100%;margin-top:6px}
.err{color:var(--red-soft);font-size:13px;margin:8px 0;min-height:18px}
.err.notice{color:var(--muted)}
.login-banner{white-space:pre-wrap;font-size:12px;line-height:1.5;color:var(--muted);background:var(--panel2,#0d1117);border:1px solid var(--border,#222);border-radius:6px;padding:10px 12px;margin:0 0 14px;text-align:left;max-height:160px;overflow:auto}
.muted{color:var(--text-faint);font-size:12px}

/* ---------- Modal ---------- */
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.55);display:flex;align-items:center;justify-content:center;z-index:50;
  padding:24px;overflow:auto}
/* A modal must never grow past the viewport: it's centred, so overflow spills off BOTH
   ends and the action buttons become unreachable with no way to scroll. Column layout
   with a scrollable body keeps the title and the buttons pinned. */
.modal{width:440px;max-width:100%;max-height:calc(100vh - 48px);
  background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:24px;
  display:flex;flex-direction:column}
.modal h3{margin:0 0 16px;font-size:17px;flex:none}
.modal .field{margin-bottom:12px}
/* Everything between the title and the actions scrolls. The negative margin + matching
   padding keeps focus rings from being clipped at the scroll edge. */
.modal-body{flex:1 1 auto;overflow-y:auto;min-height:0;margin:0 -4px;padding:0 4px}
.modal-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:18px;flex:none}
/* Long forms get more room to breathe than a confirm dialog needs. */
.modal.wide{width:560px}
.modal.wide .field{margin-bottom:16px}
.modal.wide .field > label{display:block;margin-bottom:6px;font-size:12px;color:var(--text-dim)}
.modal.wide .set-help{margin-top:6px}
/* Section headings inside the rule editor: the form covers four separate decisions
   (trigger, scope, action, safety) and running them together is what made it read as a
   wall of inputs. */
.ar-sec{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--text-faint);margin:18px 0 10px;padding-top:14px;border-top:1px solid var(--border)}
.ar-sec:first-of-type{margin-top:10px}
/* Explanations are worth having but not worth the vertical space by default. */
.ar-note{margin-top:6px;font-size:12px;color:var(--text-dim);line-height:1.55}
.ar-note summary{cursor:pointer;color:var(--accent-hi);font-size:12px;user-select:none}
.ar-note[open] summary{margin-bottom:6px}
.ar-note code{background:var(--bg-2);padding:1px 4px;border-radius:4px;font-size:11px}
.confirm-msg{color:var(--text);font-size:14px;line-height:1.5}
.confirm-msg div{margin:2px 0}
select.input{appearance:none}
.token-box{background:var(--bg-2);border:1px dashed var(--border-2);border-radius:8px;padding:12px;
  font-family:ui-monospace,Menlo,Consolas,monospace;font-size:13px;word-break:break-all;color:var(--green-soft)}
.hidden{display:none}
.center{text-align:center}
.qr{display:block;margin:12px auto;background:#fff;padding:10px;border-radius:8px}
.empty{padding:40px;text-align:center;color:var(--text-faint)}

/* ---------- Settings ---------- */
.set-card{padding:18px 20px;margin-bottom:16px}
.set-h{font-weight:600;font-size:14px;margin-bottom:12px}
.set-row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 0;border-top:1px solid var(--border)}
.set-row:first-of-type{border-top:none}
/* Two-column body for a card with a lot of short rows (the unit's Status card). Each
   column is its own element, so .set-row:first-of-type still drops the top border on the
   first row of EACH column rather than only the first of the card. Collapses back to one
   column when there is not room for two, at which point the divider has to go as well,
   or it draws a line down the middle of a single column. */
.set-cols{display:grid;grid-template-columns:1fr 1fr;gap:0 28px}
.set-cols>div+div{padding-left:28px;border-left:1px solid var(--border)}
@media (max-width:1000px){
  .set-cols{grid-template-columns:1fr;gap:0}
  .set-cols>div+div{padding-left:0;border-left:none}
  /* Joined back into one list: the second column's first row needs its border again,
     or there is a gap in the rules where the columns used to meet. */
  .set-cols>div+div>.set-row:first-of-type{border-top:1px solid var(--border)}
}
.set-label{font-size:13px;color:var(--text)}
.set-help{font-size:12px;color:var(--text-faint);margin-top:2px;max-width:420px}
.bs-pc{list-style:none;margin:6px 0 0;padding:0;font-size:12px;line-height:1.5;color:var(--text-dim);max-width:560px}
.bs-pc li{position:relative;padding-left:16px}
.bs-pc li:before{position:absolute;left:0;font-weight:600}
.bs-pc li.pro:before{content:"+";color:var(--green-soft)}
.bs-pc li.con:before{content:"\2212";color:var(--red-soft)}
.bs-pc li.cost:before{content:"\25B8";color:var(--amber)}
.bs-note{font-size:12px;color:var(--text-faint);margin-top:10px;max-width:560px;line-height:1.5}
.set-toggle{position:relative;display:inline-block;width:42px;height:24px;flex:0 0 auto}
.set-toggle input{opacity:0;width:0;height:0;position:absolute}
.set-toggle span{position:absolute;inset:0;background:var(--panel-2);border:1px solid var(--border-2);border-radius:999px;transition:.15s;cursor:pointer}
.set-toggle span:before{content:"";position:absolute;width:18px;height:18px;left:2px;top:2px;background:var(--text-dim);border-radius:50%;transition:.15s}
.set-toggle input:checked + span{background:var(--accent);border-color:var(--accent)}
.set-toggle input:checked + span:before{transform:translateX(18px);background:#fff}

/* ---------- Responsive: phones / portrait tablets ---------- */
@media (max-width: 720px){
  /* Topbar: tighter, truncate the long email+role text so the Sign out button fits. */
  .topbar{padding:0 12px;gap:8px}
  .topbar-logo{display:none}
  .who{gap:8px;min-width:0;font-size:12px}
  .profile-name{max-width:38vw}
  .brand{font-size:13px}

  /* Shell stacks vertically; the sidebar becomes a horizontal scrolling tab bar. */
  .shell{flex-direction:column;flex:1;min-height:0}
  .side{
    width:100%;flex-direction:row;overflow-x:auto;overflow-y:hidden;gap:6px;
    padding:8px 10px;border-right:none;border-bottom:1px solid var(--border);
    -webkit-overflow-scrolling:touch;
  }
  .side .navitem{white-space:nowrap;padding:8px 12px}
  .side .navitem .nav-badge{margin-left:6px}
  .side .spacer{display:none}
  .main{flex:1;padding:18px 14px}

  /* Tables: let each card scroll sideways so columns stay legible (no reflow needed). */
  .card{overflow-x:auto;-webkit-overflow-scrolling:touch}
  th,td{padding:11px 12px}
  table{min-width:520px}

  /* Action buttons wrap instead of overflowing the row. */
  .row-actions{flex-wrap:wrap;gap:6px}
  .btn{padding:9px 13px}

  /* Login + modal go (almost) full width. */
  .login{width:100%;max-width:420px;padding:24px}
  .login-wrap{padding:16px}
  .modal{width:calc(100vw - 28px);max-width:440px;max-height:88vh;overflow-y:auto;padding:20px}

  /* Page headings a touch smaller. */
  .page-title{font-size:18px}
}

/* Very small phones: drop table min-width a bit and shrink avatar. */
@media (max-width: 400px){
  .profile-name{max-width:30vw}
  .avatar{width:26px;height:26px}
  table{min-width:460px}
}

/* ---------- Update server additions ---------- */
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}
.hash{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--text-faint)}
.row-actions{text-align:right;white-space:nowrap}
.row-actions .btn{padding:6px 10px;font-size:12px;margin-left:6px}
.pill.draft{background:rgba(148,163,184,.15);color:var(--text-dim)}
.pill.dev{background:rgba(234,179,8,.15);color:#d9a520}
.pill.std{background:rgba(59,130,246,.15);color:var(--accent-hi)}
.kv{display:grid;grid-template-columns:170px 1fr;gap:8px 16px;padding:16px 18px;font-size:13px}
.kv div:nth-child(odd){color:var(--text-dim)}
.drop{border:1.5px dashed var(--border-2);border-radius:10px;padding:22px;text-align:center;color:var(--text-dim);cursor:pointer}
.drop.has{border-color:var(--accent);color:var(--text)}
.progress{height:6px;background:var(--panel-2);border-radius:99px;overflow:hidden;margin-top:12px}
.progress > i{display:block;height:100%;width:0;background:var(--accent);transition:width .2s}
.signin{max-width:420px;margin:12vh auto;text-align:center}
.signin .card{padding:30px}
.signin p{color:var(--text-dim)}
.filters{display:flex;gap:8px;margin-bottom:14px}
.filters .btn{padding:7px 12px;font-size:12px}
.filters .btn.on{background:var(--panel-2);color:#fff;border-color:var(--border-2)}
.shell{flex:1;display:flex;min-height:0}
[hidden]{display:none !important}

.pill.test { background: rgba(168, 85, 247, .16); color: #c084fc; }
