/* ============================================================
   Nexus — retro-pixel design system
   Dark is primary (the CRT). Light is the beige workstation.
   Base unit is 4px; nothing is rounded; nothing is anti-aliased.
   ============================================================ */

:root{
  --void:#CFC9DE; --panel:#E4E0EE; --panel-hi:#F2EFF8; --sunk:#C4BDD6;
  --bevel-lt:#FFFFFF; --bevel-dk:#8B82A6; --rule:#B3A9C9;
  --text:#1C1528; --text-2:#4A4063; --text-3:#7A6F96;
  --accent:#0F6E85; --accent-2:#7B3FB8; --on-accent:#FFFFFF;
  --ok:#1F7A45; --warn:#9A6200; --crit:#B32334;
  --scan:rgba(28,21,40,.025); --scan-blend:multiply;
  --u:4px;
  --pixel:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
  --screen:"Chakra Petch","IBM Plex Sans",system-ui,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,Consolas,monospace;
  --bitmap:"Silkscreen","Courier New",monospace;
  color-scheme:light;
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
  --void:#14101F; --panel:#221B33; --panel-hi:#2E2545; --sunk:#191327;
  --bevel-lt:#4A3D6B; --bevel-dk:#0D0916; --rule:#3A2F55;
  --text:#E8E2F5; --text-2:#A99CC4; --text-3:#6E6291;
  --accent:#4EE1E8; --accent-2:#C77DFF; --on-accent:#14101F;
  --ok:#5CE68A; --warn:#FFC145; --crit:#FF4D5E;
  --scan:rgba(0,0,0,.10); --scan-blend:normal;
  color-scheme:dark;
}}
:root[data-theme="dark"]{
  --void:#14101F; --panel:#221B33; --panel-hi:#2E2545; --sunk:#191327;
  --bevel-lt:#4A3D6B; --bevel-dk:#0D0916; --rule:#3A2F55;
  --text:#E8E2F5; --text-2:#A99CC4; --text-3:#6E6291;
  --accent:#4EE1E8; --accent-2:#C77DFF; --on-accent:#14101F;
  --ok:#5CE68A; --warn:#FFC145; --crit:#FF4D5E;
  --scan:rgba(0,0,0,.10); --scan-blend:normal;
  color-scheme:dark;
}

*{box-sizing:border-box}
img{image-rendering:pixelated;display:block}
html,body{height:100%;margin:0}
body{
  background:var(--void);color:var(--text);
  font-family:var(--pixel);font-size:15px;line-height:1.5;
  overflow:hidden;
}
button,input,select{font:inherit;color:inherit}
button{background:none;border:none;padding:0;cursor:pointer}
a{color:var(--accent)}
.spacer{flex:1}
[hidden]{display:none!important}

/* Off unless explicitly enabled in Settings. */
#scan{display:none}
:root[data-crt="on"] #scan{
  display:block;
  position:fixed;inset:0;z-index:9999;pointer-events:none;
  background:repeating-linear-gradient(var(--scan) 0 1px,transparent 1px 4px);
  mix-blend-mode:var(--scan-blend);
}

.raised{border-top:var(--u) solid var(--bevel-lt);border-left:var(--u) solid var(--bevel-lt);
        border-right:var(--u) solid var(--bevel-dk);border-bottom:var(--u) solid var(--bevel-dk)}
.inset{border-top:var(--u) solid var(--bevel-dk);border-left:var(--u) solid var(--bevel-dk);
       border-right:var(--u) solid var(--bevel-lt);border-bottom:var(--u) solid var(--bevel-lt)}

/* ---------------- buttons + controls ---------------- */
.btn{
  font-family:var(--screen);font-size:10px;letter-spacing:.07em;
  padding:calc(var(--u)*1.5) calc(var(--u)*2.5);background:var(--panel);
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
  white-space:nowrap;
}
.btn:hover{background:var(--panel-hi)}
.btn:active{border-top-color:var(--bevel-dk);border-left-color:var(--bevel-dk);
            border-right-color:var(--bevel-lt);border-bottom-color:var(--bevel-lt)}
.btn.primary{background:var(--accent);color:var(--on-accent)}
.btn.danger{color:var(--crit)}
.btn[disabled]{opacity:.45;cursor:not-allowed}
.btn.sm{font-size:9px;padding:calc(var(--u)) calc(var(--u)*1.5)}

input[type=text],input[type=password],input[type=search]{
  font-family:var(--mono);font-size:13px;padding:calc(var(--u)*1.5) calc(var(--u)*2);
  background:var(--sunk);color:var(--text);width:100%;
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);
}
label{font-family:var(--screen);font-size:9px;letter-spacing:.1em;color:var(--text-3);display:block;margin-bottom:var(--u)}

.pill{display:inline-flex;align-items:center;gap:4px;font-family:var(--screen);font-size:9px;
      letter-spacing:.08em;padding:2px 5px;border:2px solid currentColor;flex:none}
.pill.ok{color:var(--ok)} .pill.warn{color:var(--warn)} .pill.crit{color:var(--crit)} .pill.idle{color:var(--text-3)}
.dot{width:var(--u);height:var(--u);background:currentColor;flex:none}
.dot.live{animation:blip 1.6s steps(2,end) infinite}
@keyframes blip{0%,60%{opacity:1}61%,100%{opacity:.25}}

/* ---------------- login / setup ---------------- */
#gate{position:fixed;inset:0;z-index:800;display:grid;place-items:center;background:var(--void);padding:calc(var(--u)*4)}
.gatebox{width:min(400px,100%);background:var(--panel)}
.gatehead{display:flex;align-items:center;gap:calc(var(--u)*2);background:var(--accent-2);
          padding:calc(var(--u)*2) calc(var(--u)*3);font-family:var(--screen);font-size:12px;letter-spacing:.08em;color:#fff}
:root[data-theme="dark"] .gatehead,
:root:not([data-theme="light"]) .gatehead{color:#14101F}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .gatehead{color:#fff}}
:root[data-theme="light"] .gatehead{color:#fff}
.gatebody{padding:calc(var(--u)*5);display:flex;flex-direction:column;gap:calc(var(--u)*4)}
.gatebody img.crt{width:64px;height:64px;margin:0 auto}
.gatemsg{font-size:13px;color:var(--text-2);text-align:center}
.gateerr{font-family:var(--screen);font-size:9px;letter-spacing:.06em;color:var(--crit);text-align:center;min-height:14px}

/* ---------------- shell ---------------- */
#app{display:grid;grid-template-rows:auto minmax(0,1fr);height:100vh;height:100dvh}

#topbar{display:flex;align-items:center;gap:calc(var(--u)*3);padding:calc(var(--u)*2) calc(var(--u)*3);
        background:var(--panel);border-bottom:var(--u) solid var(--bevel-dk);flex-wrap:wrap}
#logo{width:32px;height:32px;flex:none}
.brand{font-family:var(--bitmap);font-size:14px;letter-spacing:.1em}
.brand .v{color:var(--text-3);font-size:10px;margin-left:6px}
.stat{display:flex;align-items:center;gap:calc(var(--u)*1.5);font-family:var(--screen);font-size:10px;
      letter-spacing:.06em;padding:calc(var(--u)) calc(var(--u)*2);background:var(--sunk);color:var(--text-2);
      border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
      border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);white-space:nowrap}
.stat b{color:var(--text);font-weight:400;font-variant-numeric:tabular-nums}
#clock{font-family:var(--screen);font-size:13px;font-variant-numeric:tabular-nums;letter-spacing:.04em}

#body{display:grid;grid-template-columns:auto minmax(0,1fr);min-height:0}
/* Width-driven: one variable controls the layout, the icon size and whether
   labels show. The grip sets it by drag; the value is persisted per browser. */
#rail{display:flex;flex-direction:column;gap:calc(var(--u)*2);padding:calc(var(--u)*3) calc(var(--u)*2);
      background:var(--panel);border-right:var(--u) solid var(--bevel-dk);
      width:var(--rail-w,72px);flex:none;position:relative}
.nav{width:100%;min-height:calc(var(--rail-icon,26px) + var(--u)*5);
     display:flex;align-items:center;gap:calc(var(--u)*2);
     padding:calc(var(--u)*1.5);position:relative;
     background:var(--panel);
     border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
     border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk)}
.nav img{width:var(--rail-icon,26px);height:var(--rail-icon,26px);flex:none;margin:0 auto}
#rail.wide .nav img{margin:0}
.nlabel{display:none;font-family:var(--screen);font-weight:600;font-size:12px;letter-spacing:.05em;
        color:var(--text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#rail.wide .nlabel{display:block}
#rail.wide .nav .tip{display:none!important}
.nav.on .nlabel{color:var(--on-accent)}
#rail-toggle{width:100%;display:flex;align-items:center;justify-content:center;gap:6px;
             font-family:var(--screen);font-weight:600;font-size:11px;letter-spacing:.06em;
             padding:calc(var(--u)*1.5);color:var(--text-3);
             border-bottom:2px solid var(--rule);margin-bottom:var(--u)}
#rail-toggle:hover{color:var(--accent)}
#rail-grip{position:absolute;top:0;right:-4px;width:8px;height:100%;cursor:ew-resize;z-index:20;
           touch-action:none}
#rail-grip:hover{background:var(--accent);opacity:.35}
.nav:hover{background:var(--panel-hi)}
.nav.on{background:var(--accent-2)}
.nav.on::after{content:"";position:absolute;right:-6px;top:50%;transform:translateY(-50%);
               width:var(--u);height:calc(var(--u)*4);background:var(--accent)}
.nav .tip{position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%);
          font-family:var(--screen);font-size:9px;letter-spacing:.08em;white-space:nowrap;
          background:var(--sunk);padding:3px 6px;border:2px solid var(--bevel-dk);
          opacity:0;pointer-events:none;transition:opacity .1s;z-index:50}
.nav:hover .tip{opacity:1}

/* Extra room at the bottom so the floating selection bar never sits on top of
   the last row of a list or the last widget in the grid. */
#main{overflow:auto;padding:calc(var(--u)*4) calc(var(--u)*4) calc(var(--u)*16);min-width:0;-webkit-overflow-scrolling:touch}
#toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:calc(var(--u)*2);margin-bottom:calc(var(--u)*4)}
#toolbar h1{font-family:var(--screen);font-weight:700;font-size:20px;letter-spacing:.06em;margin:0}
.hint{font-size:12px;color:var(--text-3)}

.page{display:none}
.page.on{display:block}

/* ---------------- widget grid ---------------- */
#grid{position:relative;min-height:120px}
.w{position:absolute;background:var(--wbg,var(--panel));overflow:hidden;display:flex;flex-direction:column;
   border-top:var(--u) solid var(--bevel-lt);border-left:var(--u) solid var(--bevel-lt);
   border-right:var(--u) solid var(--bevel-dk);border-bottom:var(--u) solid var(--bevel-dk);
   transition:left .12s steps(3,end),top .12s steps(3,end),width .12s steps(3,end),height .12s steps(3,end)}
.w.dragging,.w.resizing{transition:none;z-index:100}
.w.dragging{filter:brightness(1.08)}
.w.spawn{animation:spawn .18s steps(3,end)}
@keyframes spawn{from{transform:scale(.82)}to{transform:scale(1)}}
.w-head{display:flex;align-items:center;gap:calc(var(--u)*2);padding:calc(var(--u)*1.5) calc(var(--u)*2);
        background:var(--sunk);cursor:grab;flex:none;border-bottom:2px solid var(--bevel-dk);touch-action:none}
.w.dragging .w-head{cursor:grabbing}
.w-head img{width:calc(20px * var(--ws,1));height:calc(20px * var(--ws,1));flex:none}
.w-head .t{font-family:var(--screen);font-weight:600;font-size:calc(12px * var(--ws,1));
           letter-spacing:.09em;color:var(--text-2);
           flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.w-x{font-family:var(--screen);font-size:10px;color:var(--text-3);width:18px;height:18px;display:grid;place-items:center;flex:none}
.w-x:hover{color:var(--crit);background:var(--panel)}
.w-body{padding:calc(var(--u)*3);flex:1;min-height:0;display:flex;flex-direction:column;gap:calc(var(--u)*2);overflow:hidden}
.w-rs{position:absolute;right:0;bottom:0;width:18px;height:18px;cursor:nwse-resize;touch-action:none;
      background:repeating-linear-gradient(135deg,transparent 0 2px,var(--bevel-dk) 2px 4px)}

.big{font-weight:600;font-size:calc(34px * var(--ws,1));line-height:1;font-variant-numeric:tabular-nums;
     display:flex;align-items:baseline;gap:4px;color:var(--wc,var(--text))}
.big .u{font-size:calc(14px * var(--ws,1));color:var(--text-3);font-weight:400}
.sub{font-family:var(--mono);font-size:calc(12px * var(--ws,1));color:var(--text-3)}
.chart{width:100%;flex:1;min-height:0;display:block}
.meter{display:flex;gap:2px;height:calc(var(--u)*4);padding:2px;flex:none}
.meter i{flex:1;background:var(--rule)}
.meter i.on{background:var(--wc,var(--accent))} .meter i.warn{background:var(--warn)} .meter i.crit{background:var(--crit)}
.klist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:calc(var(--u)*1.5);overflow:auto;min-height:0}
.klist li{display:flex;align-items:center;justify-content:space-between;gap:calc(var(--u)*2);
          font-size:calc(14px * var(--ws,1))}
.klist .k{color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.klist .v{font-family:var(--mono);font-size:calc(12px * var(--ws,1));font-variant-numeric:tabular-nums;flex:none}

.term{background:#0D0916;padding:calc(var(--u)*2);flex:1;min-height:0;overflow:auto}
.term pre{margin:0;font-family:var(--mono);font-size:12px;line-height:1.55;color:#C9C2DE;white-space:pre-wrap;word-break:break-word}
.cur{display:inline-block;width:7px;height:13px;background:#5CE68A;vertical-align:-2px;animation:cb 1s steps(2,end) infinite}
@keyframes cb{0%,50%{opacity:1}51%,100%{opacity:0}}

.clockbig{font-family:var(--bitmap);font-size:calc(clamp(20px,7cqw,38px) * var(--ws,1));
          letter-spacing:.04em;font-variant-numeric:tabular-nums;text-align:center}
/* ---------------- tables (containers, files, audit) ---------------- */
.tw{overflow-x:auto;background:var(--panel);padding:calc(var(--u)*2)}
table{border-collapse:collapse;width:100%;min-width:520px;font-size:13px}
th{text-align:left;font-family:var(--screen);font-size:9px;letter-spacing:.1em;color:var(--text-3);
   padding:calc(var(--u)) calc(var(--u)*2);border-bottom:2px solid var(--rule);white-space:nowrap}
td{padding:calc(var(--u)*1.5) calc(var(--u)*2);border-bottom:1px solid var(--rule);color:var(--text-2);vertical-align:middle}
tr:last-child td{border-bottom:none}
tr:hover td{background:var(--panel-hi)}
td.mono{font-family:var(--mono);font-size:11px;font-variant-numeric:tabular-nums}
td.name{color:var(--text)}
.rowbtns{display:flex;gap:var(--u);flex-wrap:wrap}

.crumbs{display:flex;flex-wrap:wrap;gap:var(--u);align-items:center;font-family:var(--mono);font-size:12px;margin-bottom:calc(var(--u)*3)}
.crumbs button{color:var(--accent);text-decoration:underline}
.fname{display:flex;align-items:center;gap:calc(var(--u)*2)}
.fname .ic{width:16px;height:16px;flex:none;background:var(--accent-2)}
.fname .ic.file{background:var(--text-3)}

/* ---------------- terminal page ---------------- */
#termwrap{background:#0D0916;padding:calc(var(--u)*3);height:min(62vh,560px);overflow:auto}
#termout{margin:0;font-family:var(--mono);font-size:13px;line-height:1.5;color:#C9C2DE;white-space:pre-wrap;word-break:break-word}
#terminput{margin-top:calc(var(--u)*3);display:flex;gap:calc(var(--u)*2);align-items:center}
#terminput .ps{font-family:var(--mono);font-size:13px;color:var(--ok);flex:none}

/* ---------------- drawer ---------------- */
#drawer{position:fixed;inset:0;z-index:500;display:none;background:rgba(13,9,22,.6);padding:calc(var(--u)*6);overflow:auto}
#drawer.open{display:block}
.dw{max-width:760px;margin:0 auto;background:var(--panel)}
.dw-head{display:flex;align-items:center;gap:calc(var(--u)*2);background:var(--accent-2);
         padding:calc(var(--u)*2) calc(var(--u)*3);font-family:var(--screen);font-size:12px;letter-spacing:.08em;color:#fff}
:root[data-theme="dark"] .dw-head,
:root:not([data-theme="light"]) .dw-head{color:#14101F}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .dw-head{color:#fff}}
:root[data-theme="light"] .dw-head{color:#fff}
.dw-body{padding:calc(var(--u)*4);display:grid;grid-template-columns:repeat(auto-fill,minmax(184px,1fr));gap:calc(var(--u)*3)}
.card{display:flex;gap:calc(var(--u)*2);align-items:flex-start;padding:calc(var(--u)*3);background:var(--panel);text-align:left;
      border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
      border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk)}
.card:hover{background:var(--panel-hi)}
.card img{width:26px;height:26px;flex:none}
.card .cn{font-family:var(--screen);font-weight:600;font-size:13px;letter-spacing:.04em;display:block;margin-bottom:3px}
.card .cd{font-size:13px;color:var(--text-3);line-height:1.4}

.empty{padding:calc(var(--u)*10);text-align:center;color:var(--text-3);font-family:var(--screen);font-size:11px;
       letter-spacing:.08em;background:var(--sunk)}
.section{background:var(--panel);padding:calc(var(--u)*4);margin-bottom:calc(var(--u)*4)}
.section h2{font-family:var(--screen);font-size:11px;letter-spacing:.1em;color:var(--accent);margin:0 0 calc(var(--u)*3);
            padding-bottom:calc(var(--u)*2);border-bottom:2px solid var(--rule)}
.kv{display:grid;grid-template-columns:minmax(120px,auto) minmax(0,1fr);gap:calc(var(--u)*2) calc(var(--u)*4);font-size:13px}
.kv dt{font-family:var(--screen);font-size:9px;letter-spacing:.08em;color:var(--text-3);align-self:center}
.kv dd{margin:0;font-family:var(--mono);font-size:12px;color:var(--text);word-break:break-all}

#toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:1000;display:flex;flex-direction:column;gap:8px;align-items:center}
.toast{font-family:var(--screen);font-size:10px;letter-spacing:.06em;padding:calc(var(--u)*2) calc(var(--u)*4);
       background:var(--panel);color:var(--text);
       border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
       border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
       animation:tin .15s steps(3,end)}
.toast.err{color:var(--crit)} .toast.ok{color:var(--ok)}
@keyframes tin{from{transform:translateY(8px)}to{transform:translateY(0)}}

:focus-visible{outline:var(--u) solid var(--accent);outline-offset:2px}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* ---------------- responsive ---------------- */
/* Tablets keep the vertical rail. At 768px it costs 72px and leaves ~700px for
   the canvas, which the 12-column grid handles fine — collapsing it to a
   horizontal strip here (as this rule used to) threw away a working layout and
   hit the same width:100% bug the phone bar did, leaving one reachable icon. */
@media (max-width:860px){
  #topbar{gap:calc(var(--u)*2);padding:calc(var(--u)*2)}
  .brand .v{display:none}
  #main{padding:calc(var(--u)*3)}
}
@media (max-width:560px){
  body{font-size:14px}
  #clock{font-size:11px}
  .stat{font-size:9px}
  .big{font-size:28px}
  #toolbar h1{font-size:13px}
  .dw-body{grid-template-columns:1fr}
  #drawer{padding:calc(var(--u)*3)}
}

/* Stacked single-column canvas for phones. Drag/resize are meaningless here,
   so the resize grip is hidden and widgets flow normally. */
#grid.stack{position:static;height:auto!important;display:flex;flex-direction:column;gap:calc(var(--u)*2)}
#grid.stack .w{position:static;width:auto!important;left:auto!important;top:auto!important;transition:none}
#grid.stack .w-rs{display:none}
#grid.stack .w-head{cursor:default}

/* ============================================================
   APP STORE
   ============================================================ */
.storebar{display:flex;flex-wrap:wrap;gap:calc(var(--u)*2);align-items:center;margin-bottom:calc(var(--u)*4)}
.searchwrap{position:relative;flex:1 1 260px;min-width:0}
.searchwrap .si{position:absolute;left:calc(var(--u)*2);top:50%;transform:translateY(-50%);width:16px;height:16px;pointer-events:none}
.searchwrap input{padding-left:calc(var(--u)*8)}
.sel{font-family:var(--screen);font-size:10px;letter-spacing:.06em;padding:calc(var(--u)*1.5) calc(var(--u)*2);
     background:var(--sunk);color:var(--text);
     border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
     border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt)}

.appgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:calc(var(--u)*3)}
.appcard{display:flex;flex-direction:column;gap:calc(var(--u)*2);padding:calc(var(--u)*3);
         background:var(--panel);text-align:left;min-width:0;
         border-top:var(--u) solid var(--bevel-lt);border-left:var(--u) solid var(--bevel-lt);
         border-right:var(--u) solid var(--bevel-dk);border-bottom:var(--u) solid var(--bevel-dk)}
.appcard:hover{background:var(--panel-hi)}
.appcard:active{border-top-color:var(--bevel-dk);border-left-color:var(--bevel-dk);
                border-right-color:var(--bevel-lt);border-bottom-color:var(--bevel-lt)}
.appcard .top{display:flex;gap:calc(var(--u)*2);align-items:flex-start;min-width:0}
.appcard .ico{width:40px;height:40px;flex:none;background:var(--sunk);object-fit:contain;
              border:2px solid var(--bevel-dk);image-rendering:auto}
.appcard .nm{font-family:var(--screen);font-size:10px;letter-spacing:.06em;color:var(--text);
             display:block;margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.appcard .tg{font-size:12px;color:var(--text-3);line-height:1.35;display:-webkit-box;
             -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.appcard .meta{display:flex;gap:var(--u);align-items:center;flex-wrap:wrap;margin-top:auto;padding-top:calc(var(--u)*2)}
.loadmore{display:flex;justify-content:center;padding:calc(var(--u)*5) 0}

/* ============================================================
   MODAL
   ============================================================ */
#modal{position:fixed;inset:0;z-index:700;display:none;background:rgba(13,9,22,.72);
       padding:calc(var(--u)*5);overflow:auto}
#modal.open{display:block}
.mw{max-width:720px;margin:0 auto;background:var(--panel);display:flex;flex-direction:column;max-height:calc(100vh - var(--u)*10)}
.mw-head{display:flex;align-items:center;gap:calc(var(--u)*2);background:var(--accent-2);
         padding:calc(var(--u)*2) calc(var(--u)*3);font-family:var(--screen);font-size:11px;
         letter-spacing:.08em;color:#fff;flex:none}
:root[data-theme="dark"] .mw-head,
:root:not([data-theme="light"]) .mw-head{color:#14101F}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .mw-head{color:#fff}}
:root[data-theme="light"] .mw-head{color:#fff}
.mw-head img{width:20px;height:20px;flex:none}
.mw-head #mw-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mw-body{padding:calc(var(--u)*4);overflow:auto;flex:1;min-height:0;display:flex;flex-direction:column;gap:calc(var(--u)*4)}
.mw-foot{padding:calc(var(--u)*3) calc(var(--u)*4);border-top:2px solid var(--rule);
         display:flex;gap:calc(var(--u)*2);justify-content:flex-end;flex-wrap:wrap;flex:none}
.mw-body p{margin:0;font-size:14px;color:var(--text-2);line-height:1.55}
.mw-body h3{font-family:var(--screen);font-size:10px;letter-spacing:.1em;color:var(--text-3);margin:0 0 calc(var(--u)*2)}
.applead{display:flex;gap:calc(var(--u)*3);align-items:flex-start}
.applead img{width:64px;height:64px;flex:none;background:var(--sunk);border:2px solid var(--bevel-dk);
             object-fit:contain;image-rendering:auto}
.applead .t{font-family:var(--screen);font-size:14px;letter-spacing:.06em;color:var(--text);display:block;margin-bottom:6px}
.field{display:flex;flex-direction:column;gap:var(--u)}
.field .fh{font-size:12px;color:var(--text-3)}
textarea{font-family:var(--mono);font-size:12px;padding:calc(var(--u)*2);background:var(--sunk);color:var(--text);
         width:100%;min-height:180px;resize:vertical;
         border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
         border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt)}
.joblog{background:#0D0916;padding:calc(var(--u)*3);max-height:300px;overflow:auto}
.joblog pre{margin:0;font-family:var(--mono);font-size:11.5px;line-height:1.6;color:#C9C2DE;white-space:pre-wrap;word-break:break-word}
.joblog .okl{color:#5CE68A} .joblog .errl{color:#FF4D5E}
.warnbox{border-left:var(--u) solid var(--warn);padding:calc(var(--u)*2) calc(var(--u)*4);
         background:var(--panel);margin-bottom:calc(var(--u)*4);font-size:13px;color:var(--text-2)}
.warnbox.err{border-left-color:var(--crit)}
.libitem{display:flex;gap:calc(var(--u)*2);align-items:center;padding:calc(var(--u)*2);background:var(--sunk);flex-wrap:wrap}
.libitem .ln{font-family:var(--screen);font-size:10px;letter-spacing:.06em;flex:1 1 160px;min-width:0;
             overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.libitem .lu{font-family:var(--mono);font-size:10px;color:var(--text-3);width:100%;word-break:break-all}

/* ============================================================
   TERMINAL
   ============================================================ */
.termchrome{background:var(--panel);overflow:hidden}
.termbar{display:flex;align-items:center;gap:calc(var(--u)*2);padding:calc(var(--u)*1.5) calc(var(--u)*2);
         background:var(--sunk);border-bottom:2px solid var(--bevel-dk)}
.termbar img{width:16px;height:16px;flex:none}
.termbar .tt{font-family:var(--screen);font-size:10px;letter-spacing:.08em;color:var(--text-2);
             overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#xterm-host{height:min(64vh,620px);padding:calc(var(--u)*2);background:#0D0916}
#xterm-host .xterm{height:100%}
.xterm-viewport::-webkit-scrollbar{width:10px}
.xterm-viewport::-webkit-scrollbar-track{background:#0D0916}
.xterm-viewport::-webkit-scrollbar-thumb{background:#3A2F55}

@media (max-width:560px){
  .appgrid{grid-template-columns:1fr}
  #modal{padding:calc(var(--u)*2)}
  .mw{max-height:calc(100vh - var(--u)*4)}
  #xterm-host{height:56vh}
  .applead{flex-direction:column}
}

/* ============================================================
   CONTEXT MENU  (right-click a widget)
   ============================================================ */
#ctx{
  position:fixed;z-index:900;display:none;min-width:212px;background:var(--panel);
  border-top:var(--u) solid var(--bevel-lt);border-left:var(--u) solid var(--bevel-lt);
  border-right:var(--u) solid var(--bevel-dk);border-bottom:var(--u) solid var(--bevel-dk);
  padding:calc(var(--u)*2);
}
#ctx.open{display:block}
#ctx .ctxhead{
  font-family:var(--screen);font-weight:700;font-size:12px;letter-spacing:.06em;
  color:var(--text);padding:0 0 calc(var(--u)*2);margin-bottom:calc(var(--u)*2);
  border-bottom:2px solid var(--rule);display:flex;align-items:center;gap:calc(var(--u)*2);
}
#ctx .ctxhead img{width:18px;height:18px}
.ctxgroup{margin-bottom:calc(var(--u)*3)}
.ctxlabel{
  display:flex;align-items:center;gap:6px;
  font-family:var(--screen);font-weight:600;font-size:10px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--text-3);margin-bottom:calc(var(--u)*1.5);
}
.ctxlabel img{width:14px;height:14px;opacity:.85}
.ctxrow{display:flex;gap:calc(var(--u));flex-wrap:wrap}
.ctxopt{
  font-family:var(--screen);font-weight:600;font-size:11px;letter-spacing:.04em;
  padding:calc(var(--u)*1.25) calc(var(--u)*2);background:var(--panel);color:var(--text-2);
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
}
.ctxopt:hover{background:var(--panel-hi);color:var(--text)}
.ctxopt.sel{background:var(--accent);color:var(--on-accent);
  border-top-color:var(--bevel-dk);border-left-color:var(--bevel-dk);
  border-right-color:var(--bevel-lt);border-bottom-color:var(--bevel-lt)}

/* colour swatches — actual colour, not a name */
.swatch{
  width:26px;height:26px;padding:0;position:relative;
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
}
.swatch.sel::after{
  content:"";position:absolute;inset:3px;border:2px solid var(--void);
}
.ctxfoot{border-top:2px solid var(--rule);padding-top:calc(var(--u)*2);display:flex;gap:calc(var(--u))}

/* ============================================================
   SENSOR BARS  (right-click a Sensors widget -> visual)
   ============================================================ */
.sbars{display:flex;flex-direction:column;gap:calc(var(--u)*2);overflow:auto;min-height:0}
.sbar{display:flex;flex-direction:column;gap:3px}
.sbar .srow{display:flex;justify-content:space-between;align-items:baseline;gap:calc(var(--u)*2)}
.sbar .sname{font-size:calc(13px * var(--ws,1));color:var(--text-2);
             overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sbar .sval{font-family:var(--mono);font-size:calc(12px * var(--ws,1));
            font-variant-numeric:tabular-nums;flex:none}
.sbar .strack{height:calc(var(--u)*3);background:var(--sunk);position:relative;
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt)}
.sbar .sfill{position:absolute;left:0;top:0;bottom:0;background:var(--wc,var(--accent))}
.sbar .sfill.warn{background:var(--warn)}
.sbar .sfill.crit{background:var(--crit)}

/* ============================================================
   ANALOG CLOCK
   ============================================================ */
.analog{flex:1;display:grid;place-items:center;min-height:0}
.analog svg{width:100%;height:100%;max-width:calc(220px * var(--ws,1));max-height:calc(220px * var(--ws,1))}
.cdate{font-family:var(--screen);font-weight:600;font-size:calc(12px * var(--ws,1));
       letter-spacing:.05em;color:var(--text-3);text-align:center}

/* Clock face keeps the bitmap font — that flourish was working. */
.clockbig{color:var(--wc,var(--text))}

/* ============================================================
   WIDGET ACCENT PALETTE
   Separate tokens from the status colours on purpose: green/amber/red keep
   meaning "healthy / warning / failing", so a widget you tinted amber for
   taste must not read as a warning.
   ============================================================ */
:root{
  --wc-cyan:#0F6E85; --wc-orchid:#7B3FB8; --wc-amber:#9A6200;
  --wc-green:#1F7A45; --wc-coral:#B3382C; --wc-blue:#2D5AA8;
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
  --wc-cyan:#4EE1E8; --wc-orchid:#C77DFF; --wc-amber:#FFC145;
  --wc-green:#5CE68A; --wc-coral:#FF7A87; --wc-blue:#7DA2FF;
}}
:root[data-theme="dark"]{
  --wc-cyan:#4EE1E8; --wc-orchid:#C77DFF; --wc-amber:#FFC145;
  --wc-green:#5CE68A; --wc-coral:#FF7A87; --wc-blue:#7DA2FF;
}

/* ---------------- topbar clock: time + day ----------------
   Fixed width, centred, tabular figures.

   The clock sits between two buttons in a flex row, so anything that changes
   its measured width shoves them sideways. Three separate things were doing
   that: the seconds digit (now gone), the hour crossing 9 -> 10, and the day
   line going from "MON 7 SEP" to "WED 27 SEP". A min-width wide enough for the
   longest of those pins the whole block, and centring keeps both lines looking
   deliberate inside it rather than ragged. */
#clock{display:flex;flex-direction:column;line-height:1.15;align-items:center;
       min-width:104px;text-align:center}
#clock .ctime{font-family:var(--screen);font-weight:600;font-size:15px;letter-spacing:.03em;
              font-variant-numeric:tabular-nums;color:var(--text);white-space:nowrap}
#clock .cday{font-family:var(--screen);font-weight:600;font-size:10px;letter-spacing:.09em;
             color:var(--text-3);white-space:nowrap}
@media (max-width:820px){ #clock{min-width:88px} }

/* ---------------- network widget: both directions, equal billing ---------- */
.netrow{display:flex;align-items:baseline;gap:calc(var(--u)*4);flex-wrap:wrap;flex:none}
.netstat{display:flex;flex-direction:column;gap:1px}
.netstat .nlbl{font-family:var(--screen);font-weight:600;font-size:calc(10px * var(--ws,1));
               letter-spacing:.1em;color:var(--text-3)}
.netstat .nval{font-family:var(--mono);font-size:calc(17px * var(--ws,1));
               font-variant-numeric:tabular-nums;color:var(--wc,var(--text))}
.netstat:nth-child(2) .nval{color:var(--text-2)}
.npeak{font-family:var(--mono);font-size:calc(11px * var(--ws,1));color:var(--text-3);white-space:nowrap}

/* ---------------- suggested libraries ---------------- */
.libsug{display:flex;flex-direction:column;gap:calc(var(--u)*2)}
.libsug .s{display:flex;gap:calc(var(--u)*2);align-items:center;padding:calc(var(--u)*2);
           background:var(--sunk);flex-wrap:wrap}
.libsug .sn{font-family:var(--screen);font-weight:600;font-size:12px;letter-spacing:.04em;flex:1 1 150px}
.libsug .sd{font-size:12px;color:var(--text-3);width:100%;line-height:1.4}


/* ============================================================
   WIDGET BACKGROUND TINTS
   Mixed against the panel colour rather than set flat, so a tint stays a tint
   in both themes instead of becoming a solid block in one of them.
   ============================================================ */
.bg-none   { --wbg: var(--panel); }
.bg-cyan   { --wbg: color-mix(in srgb, var(--wc-cyan)   14%, var(--panel)); }
.bg-orchid { --wbg: color-mix(in srgb, var(--wc-orchid) 14%, var(--panel)); }
.bg-amber  { --wbg: color-mix(in srgb, var(--wc-amber)  14%, var(--panel)); }
.bg-green  { --wbg: color-mix(in srgb, var(--wc-green)  14%, var(--panel)); }
.bg-coral  { --wbg: color-mix(in srgb, var(--wc-coral)  14%, var(--panel)); }
.bg-blue   { --wbg: color-mix(in srgb, var(--wc-blue)   14%, var(--panel)); }

/* ---------------- file manager ---------------- */
.crumbs button{font-family:var(--mono);font-size:12px;color:var(--accent);padding:2px 5px}
.crumbs button:hover{background:var(--panel-hi)}
.crumbs .csep{color:var(--text-3);font-family:var(--mono)}
#f-table tr{cursor:default}
#f-table tr.sel td{background:var(--accent-soft,var(--panel-hi));box-shadow:inset 2px 0 0 var(--accent)}
#f-table td.dim{color:var(--text-3)}
.fname{display:flex;align-items:center;gap:calc(var(--u)*2);min-width:0}
.fname .ftxt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fic{
  flex:none;width:34px;min-width:34px;text-align:center;
  font-family:var(--screen);font-weight:700;font-size:9px;letter-spacing:.04em;
  padding:3px 0;color:var(--void);background:var(--text-3);
}
.k-dir { background:var(--wc-amber);  }
.k-doc { background:var(--wc-cyan);   }
.k-cfg { background:var(--wc-orchid); }
.k-code{ background:var(--wc-green);  }
.k-img { background:var(--wc-coral);  }
.k-vid { background:var(--wc-blue);   }
.k-snd { background:var(--wc-blue);   }
.k-arc { background:var(--text-3);    }
.k-log { background:var(--text-3);    }
.k-pdf { background:var(--wc-coral);  }
.k-bin { background:var(--rule);      }

/* ---------------- context menu: stacked action list ---------------- */
.ctxstack{display:flex;flex-direction:column;gap:2px;margin-bottom:0}
.ctxopt.wide{width:100%;text-align:left}
.ctxopt.danger{color:var(--crit)}

/* ---------------- text editor ---------------- */
.edmeta{display:flex;gap:calc(var(--u)*2);align-items:center;font-size:11px;color:var(--text-3);
        margin-bottom:calc(var(--u)*2);flex-wrap:wrap}
#ed-area{
  width:100%;min-height:min(56vh,460px);resize:vertical;
  font-family:var(--mono);font-size:13px;line-height:1.55;
  padding:calc(var(--u)*3);background:#0D0916;color:#C9C2DE;white-space:pre;overflow:auto;
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);
}
:root[data-theme="light"] #ed-area{background:var(--sunk);color:var(--text)}
.mw-foot .hint{margin-right:auto}

/* ---------------- containers widget ---------------- */
.clist{display:flex;flex-direction:column;gap:calc(var(--u)*1.5);overflow:auto;min-height:0}
.crow{display:flex;align-items:center;gap:calc(var(--u)*2);padding:calc(var(--u)*1.5);
      background:var(--sunk);min-width:0}
.crow .cdot{width:8px;height:8px;flex:none}
.crow .cdot.up{background:var(--ok)} .crow .cdot.down{background:var(--crit)}
.crow .cname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
             font-size:calc(13px * var(--ws,1))}
.crow .cport{font-family:var(--mono);font-size:calc(11px * var(--ws,1));color:var(--text-3);flex:none}
.crow .cact{font-family:var(--screen);font-weight:600;font-size:calc(10px * var(--ws,1));
            letter-spacing:.05em;padding:2px 6px;flex:none;
            border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
            border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk)}
.crow .cact:hover{background:var(--panel-hi)}
.crow .cact[disabled]{opacity:.4}
.csummary{display:flex;gap:calc(var(--u)*2);align-items:baseline;flex:none;flex-wrap:wrap}


/* ---------------- file manager: root switcher ---------------- */
/* Cards, not buttons. A path alone does not tell you what a drive is for, and
   "how full is it" is the question you are usually holding when you look. */
.rootbar{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
  gap:calc(var(--u)*2);align-items:stretch;margin-bottom:calc(var(--u)*3);
}
.rootcard{
  display:flex;flex-direction:column;gap:calc(var(--u));min-width:0;
  padding:calc(var(--u)*2) calc(var(--u)*2.5);background:var(--panel);
  text-align:left;cursor:pointer;
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
}
.rootcard:hover{background:var(--panel-hi)}
.rootcard.on{
  background:var(--panel-hi);
  border-left:var(--u) solid var(--accent);
  border-top-color:var(--bevel-dk);
}
.rootcard.missing{opacity:.5;cursor:not-allowed}
.rootcard.dragging{opacity:.45}
.rootcard.dropbefore{box-shadow:inset 3px 0 0 var(--accent-2)}

.rootcard .rhead{display:flex;align-items:baseline;gap:calc(var(--u)*2);min-width:0}
.rootcard .rn{
  font-family:var(--screen);font-weight:700;font-size:12px;letter-spacing:.07em;
  color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.rootcard .rpct{
  font-family:var(--mono);font-size:11px;font-variant-numeric:tabular-nums;
  color:var(--text-2);flex:none;
}
.rootcard .rpct.warn{color:var(--warn)}
.rootcard .rpct.crit{color:var(--crit)}
.rootcard .rp{
  font-size:10.5px;color:var(--text-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* The bar is the point of the redesign: a number you have to read is not the
   same as a shape you can judge at a glance. */
.rootcard .rbar{
  display:block;height:calc(var(--u)*2);background:var(--sunk);margin-top:2px;
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);
}
.rootcard .rbar i{display:block;height:100%;background:var(--accent);transition:width .3s linear}
.rootcard .rbar.warn i{background:var(--warn)}
.rootcard .rbar.crit i{background:var(--crit)}

.rootcard .rfig{
  font-size:11px;color:var(--text-2);font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.rootcard .rfig b{color:var(--text-3);font-weight:400}
.rootcard .rfig .rfree{color:var(--text-3)}
.rootcard .rfig.dim{color:var(--text-3)}

.rootcard .rnote{
  font-size:11px;line-height:1.35;color:var(--text-2);
  padding-top:calc(var(--u));border-top:2px solid var(--rule);margin-top:auto;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.rootcard .rnote.empty{color:var(--text-3);opacity:.6;font-style:italic}

.rootshint{grid-column:1/-1;min-width:0}
.rootshint code{font-size:.9em}

/* ---------------- file manager: the clipboard ---------------- */
.clipbar{
  display:flex;align-items:center;gap:calc(var(--u)*3);flex-wrap:wrap;
  padding:calc(var(--u)*2) calc(var(--u)*3);margin-bottom:calc(var(--u)*3);
  background:var(--sunk);border-left:var(--u) solid var(--accent-2);
}
.clipbar .clipinfo{
  font-family:var(--screen);font-weight:700;font-size:11px;letter-spacing:.07em;
  color:var(--accent-2);
}
.clipbar .hint{flex:1 1 200px;font-size:11px}

/* Cut rows stay visible but greyed, so it is clear what is about to move
   rather than it vanishing from under you when the paste lands. */
#f-table tr.cut td{opacity:.45;font-style:italic}

/* ---------------- file manager: drag and drop ----------------
   While a drag is in progress every place you could drop is outlined, so the
   answer to "where can this go?" is on screen rather than found by trial. The
   one under the pointer is filled in as well. */
#f-table tr[data-path]{cursor:default}
#page-files.dragging-rows{cursor:grabbing}
.droptarget{outline:2px dashed color-mix(in srgb, var(--accent) 55%, transparent);outline-offset:-2px}
#f-table tr.droptarget td{background:color-mix(in srgb, var(--accent) 6%, transparent)}
.dropinto{outline:2px solid var(--accent)!important;outline-offset:-2px}
#f-table tr.dropinto td{background:color-mix(in srgb, var(--accent) 20%, transparent)}
#f-roots .rootcard.dropinto{background:color-mix(in srgb, var(--accent) 16%, var(--panel))}
#f-crumbs [data-go].dropinto{background:color-mix(in srgb, var(--accent) 22%, transparent)}


/* ============================================================
   MULTI-SELECT  (widgets and files)
   ============================================================ */

/* The bevel is already doing work on every edge, so selection is marked with an
   inset outline rather than another border — it reads as "picked up" without
   changing the widget's box or nudging the grid. */
.w.selected{outline:3px solid var(--accent);outline-offset:-3px}
.w.selected .w-head{background:color-mix(in srgb, var(--accent) 26%, var(--sunk))}
.w.selected .w-head .t{color:var(--text)}
.w.selected.dragging{outline-color:var(--accent-2)}

/* The marquee, drawn on the canvas itself. */
.wband{
  position:absolute;z-index:150;pointer-events:none;
  border:2px solid var(--accent);
  background:color-mix(in srgb, var(--accent) 14%, transparent);
}

/* Floats over the content rather than sitting in the flow.
   As an in-flow bar it appeared on the first click and pushed everything below
   it down by its own height — so the second ctrl-click of a multi-select landed
   on the row above the one you were pointing at. Nothing that appears *because*
   you clicked may move what you are clicking on. */
.selbar{
  position:fixed;z-index:200;
  left:calc(var(--rail-w,72px) + var(--u)*8);
  right:calc(var(--u)*8);
  bottom:calc(var(--u)*4);
  /* It floats over the list, so it must not swallow clicks meant for the rows
     underneath it. There is nothing interactive in here — it only reports. */
  pointer-events:none;
  display:flex;align-items:center;gap:calc(var(--u)*3);flex-wrap:wrap;
  padding:calc(var(--u)*2) calc(var(--u)*3);
  background:var(--panel);
  border-top:var(--u) solid var(--bevel-lt);border-left:var(--u) solid var(--accent);
  border-right:var(--u) solid var(--bevel-dk);border-bottom:var(--u) solid var(--bevel-dk);
}
.selbar .selc{font-family:var(--screen);font-weight:700;font-size:11px;letter-spacing:.08em;
              color:var(--accent);white-space:nowrap}
.selbar .hint{font-size:11px}
@media (max-width:640px){ .selbar .hint{display:none} }

/* Rubber band. Positioned inside #page-files, which is why that gets a
   containing block below. */
.page{position:relative}
#f-band{
  position:absolute;z-index:60;pointer-events:none;
  border:2px solid var(--accent);
  background:color-mix(in srgb, var(--accent) 16%, transparent);
}
#page-files{user-select:none}

/* ============================================================
   CONTEXT MENU: tick lists and notes
   ============================================================ */

/* A host with a dozen sensors makes this menu taller than the viewport, so it
   scrolls internally instead of running off the bottom of the screen. */
#ctx{max-height:min(78vh,660px);overflow:auto;overscroll-behavior:contain}

.ctxnote{margin:0;font-size:11px;line-height:1.45;color:var(--text-3)}
.ctxchecks{display:flex;flex-direction:column;gap:2px;max-height:238px;overflow:auto;
           background:var(--sunk);padding:2px}
.ctxcheck{
  display:flex;align-items:center;gap:calc(var(--u)*2);width:100%;text-align:left;
  padding:calc(var(--u)*1.25) calc(var(--u)*1.5);background:var(--panel);color:var(--text-2);
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
}
.ctxcheck:hover{background:var(--panel-hi);color:var(--text)}
.ctxcheck .tick{
  flex:none;width:16px;height:16px;display:grid;place-items:center;
  font-family:var(--screen);font-size:11px;line-height:1;
  background:var(--sunk);color:var(--on-accent);
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);
}
.ctxcheck.on .tick{background:var(--accent)}
.ctxcheck .ct{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
.ctxcheck .cl{font-family:var(--mono);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ctxcheck .cs{font-size:10px;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ctxcheck.on .cl{color:var(--text)}
.ctxallrow{margin-top:calc(var(--u)*1.5)}

/* ============================================================
   CONTROL PANEL
   ============================================================ */
.cpform{display:flex;flex-direction:column;gap:calc(var(--u)*4)}
.cpform h3{font-family:var(--screen);font-size:10px;letter-spacing:.1em;color:var(--accent);
           margin:0;padding-bottom:calc(var(--u));border-bottom:2px solid var(--rule)}
.frow{display:flex;gap:calc(var(--u)*4);flex-wrap:wrap}
.frow > .field{flex:1 1 210px;min-width:0}
.checkrow{display:flex;flex-direction:column;gap:2px}

/* --- rules and scheduled tasks share a row shape --- */
.rulelist{display:flex;flex-direction:column;gap:calc(var(--u)*2)}
.rule{
  display:flex;align-items:center;gap:calc(var(--u)*3);flex-wrap:wrap;
  padding:calc(var(--u)*3);background:var(--sunk);
  border-left:var(--u) solid var(--accent);
}
.rule.off{border-left-color:var(--rule);opacity:.62}
.rule .rtxt{display:flex;flex-direction:column;gap:2px;flex:1 1 260px;min-width:0}
.rule .rn{font-family:var(--screen);font-weight:700;font-size:12px;letter-spacing:.05em;color:var(--text)}
.rule .rd{font-size:12px;color:var(--text-2);line-height:1.45}
.rule .rd.dim{color:var(--text-3);font-size:11px}
.rule .rd b{font-family:var(--mono);font-weight:400;color:var(--text)}

/* A switch, not a checkbox: these turn real automations on and off, and the
   state needs to read across the room. */
.toggle{
  flex:none;width:42px;height:22px;padding:2px;position:relative;background:var(--void);
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);
}
.toggle i{display:block;width:16px;height:14px;background:var(--text-3);
          transition:transform .1s steps(2,end),background .1s}
.toggle.on{background:color-mix(in srgb, var(--ok) 30%, var(--void))}
.toggle.on i{transform:translateX(20px);background:var(--ok)}

/* --- day picker --- */
.daypick{display:flex;gap:var(--u);flex-wrap:wrap}
.dayb{
  width:30px;height:30px;flex:none;
  font-family:var(--screen);font-weight:700;font-size:11px;color:var(--text-3);background:var(--panel);
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
}
.dayb:hover{background:var(--panel-hi);color:var(--text)}
.dayb.on{background:var(--accent);color:var(--on-accent);
  border-top-color:var(--bevel-dk);border-left-color:var(--bevel-dk);
  border-right-color:var(--bevel-lt);border-bottom-color:var(--bevel-lt)}

/* --- alerts --- */
.alertlist{display:flex;flex-direction:column;gap:calc(var(--u)*1.5);max-height:340px;overflow:auto}
.alert{
  display:flex;align-items:baseline;gap:calc(var(--u)*3);flex-wrap:wrap;
  padding:calc(var(--u)*2) calc(var(--u)*3);background:var(--sunk);
  border-left:var(--u) solid var(--text-3);
}
.alert.info{border-left-color:var(--ok)}
.alert.warn{border-left-color:var(--warn)}
.alert.crit{border-left-color:var(--crit)}
.alert .apill{
  flex:none;font-family:var(--screen);font-weight:700;font-size:9px;letter-spacing:.08em;
  min-width:44px;text-align:center;padding:2px 4px;color:var(--void);background:var(--text-3);
}
.alert.info .apill{background:var(--ok)}
.alert.warn .apill{background:var(--warn)}
.alert.crit .apill{background:var(--crit)}
.alert .atxt{display:flex;flex-direction:column;gap:2px;flex:1 1 260px;min-width:0}
.alert .at{font-family:var(--screen);font-weight:600;font-size:12px;letter-spacing:.04em;color:var(--text)}
.alert .am{font-size:12px;color:var(--text-2);line-height:1.45}
.alert .awhen{font-size:10.5px;color:var(--text-3);flex:none}

@media (max-width:640px){
  .rule{gap:calc(var(--u)*2)}
  .frow{gap:calc(var(--u)*3)}
  .alert .awhen{width:100%}
}

/* The file list panel fills the page rather than shrink-wrapping its rows.
   Two reasons: a four-item folder no longer leaves a stub of a panel floating
   in the middle of the page, and there is always blank panel below the last row
   to start a rubber-band drag from. Without this, a short listing has no empty
   space inside #page-files at all and the marquee has nowhere to begin. */
#page-files .tw{min-height:56vh}

/* ============================================================
   TOOLTIPS
   One floating element, driven by [data-tip]. Native title= could not be
   styled to match anything here, and its delay is the browser's preference
   rather than ours.
   ============================================================ */
#tip{
  position:fixed;z-index:1200;max-width:290px;
  padding:calc(var(--u)*1.5) calc(var(--u)*2.5);
  background:var(--panel-hi);color:var(--text);
  font-size:12px;line-height:1.45;
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .09s steps(2,end);
}
#tip.on{opacity:1;visibility:visible}

/* ============================================================
   CHART CROSSHAIR
   One floating overlay, moved over whichever chart is being hovered. It cannot
   live inside the <svg>: every widget rewrites its chart once a second and
   would erase it mid-hover.

   Everything here is structure and the classic look. The workstation theme
   rounds the dot and the card in workstation.css; the geometry is shared.
   ============================================================ */
#probe{
  position:fixed;z-index:1150;pointer-events:none;
  opacity:0;visibility:hidden;
  transition:opacity .1s steps(2,end);
  /* Derived from the palette's own ink, so the line is legible on parchment and
     on a dark panel without either theme having to name a colour for it. */
  --probe-line:var(--text-3);
  --probe-ring:var(--pbg,var(--panel));
}
@supports (color:color-mix(in srgb,red,blue)){
  #probe{--probe-line:color-mix(in srgb,var(--text) 55%,transparent)}
}
#probe.on{opacity:1;visibility:visible}

/* Snapped to a sample, so the short glide is between two real readings rather
   than a line chasing the cursor through the gaps. */
.pb-line{
  position:absolute;top:0;bottom:0;left:0;width:1px;
  background:var(--probe-line);
  transition:transform .07s linear;
}
.pb-dot{
  position:absolute;top:0;left:0;width:7px;height:7px;margin:-4px 0 0 -4px;
  background:var(--pc,var(--accent));
  box-shadow:0 0 0 2px var(--probe-ring);
  transition:transform .07s linear;
}
.pb-dot[hidden],.pb-row[hidden]{display:none}

.pb-card{
  position:absolute;top:0;left:0;
  display:flex;flex-direction:column;gap:var(--u);
  padding:calc(var(--u)*1.5) calc(var(--u)*2.5);
  background:var(--panel-hi);color:var(--text);white-space:nowrap;
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
  transition:transform .07s linear;
}
.pb-when{font-family:var(--mono);font-size:10px;letter-spacing:.03em;color:var(--text-3)}
.pb-rows{display:flex;flex-direction:column;gap:var(--u)}
.pb-row{display:flex;align-items:center;gap:calc(var(--u)*3)}
.pb-key{width:7px;height:7px;flex:none}
.pb-lbl{font-family:var(--screen);font-size:9px;letter-spacing:.1em;color:var(--text-2);flex:1}
.pb-val{font-family:var(--mono);font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}

/* ============================================================
   WHERE A WIDGET IS PICKED UP
   The title bar, or anywhere on a widget that is already selected. The body
   used to be a handle everywhere, which put the grab cursor over the whole
   widget — and a widget that is entirely something to move reads as having
   nothing in it worth touching. Each cursor here names exactly one thing the
   spot under it will do.
   ============================================================ */
.w{cursor:default;user-select:none}
.w button,.w a,.w input,.w select,.w textarea{cursor:pointer}
.w .w-rs{cursor:nwse-resize}
/* Selected widgets move from anywhere, so they wear the handle cursor. */
.w.selected{cursor:grab}
/* A chart is read, not dragged, and the cursor is the only place that can say
   so before you try it. Last, so it holds on a selected widget too. */
.w .chart,.w.selected .chart{cursor:crosshair}
/* Mid-drag nothing under the pointer is anything but the drag. */
.w.dragging,.w.dragging .chart,.w.dragging .w-head{cursor:grabbing}
#grid.stack .w{cursor:default}

/* ============================================================
   INSTALL PROGRESS RING
   The number is parsed from Docker's own per-layer pull output, so an idle
   ring means an idle download. Nothing here is on a timer.
   ============================================================ */
.jobwrap{
  display:flex;align-items:center;gap:calc(var(--u)*5);flex-wrap:wrap;
  padding:calc(var(--u)*3);margin-bottom:calc(var(--u)*3);background:var(--sunk);
}
.donut{position:relative;width:128px;height:128px;flex:none}
.donut svg{width:100%;height:100%;transform:rotate(-90deg)}
.donut circle{fill:none;stroke-width:12}
.donut .track{stroke:var(--rule);opacity:.5}
.donut .fill{
  stroke:var(--accent);stroke-linecap:butt;
  transition:stroke-dashoffset .25s linear,stroke .2s;
}
.jobwrap.ok  .fill{stroke:var(--ok)}
.jobwrap.err .fill{stroke:var(--crit)}
.dnum{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:1px;
  font-family:var(--screen);font-weight:700;font-size:26px;
  font-variant-numeric:tabular-nums;color:var(--text);
}
.dnum i{font-style:normal;font-size:13px;color:var(--text-3)}
.jobmeta{display:flex;flex-direction:column;gap:calc(var(--u));flex:1 1 200px;min-width:0}
.jphase{
  font-family:var(--screen);font-weight:600;font-size:12px;letter-spacing:.06em;
  color:var(--accent);text-transform:uppercase;
}
.jobwrap.ok  .jphase{color:var(--ok)}
.jobwrap.err .jphase{color:var(--crit)}

@media (max-width:520px){
  .donut{width:96px;height:96px}
  .dnum{font-size:20px}
}

/* ============================================================
   UPLOADS
   ============================================================ */

/* Drop target. Covers the file page only — dropping anywhere else in the
   window is swallowed so the browser does not navigate away from Nexus. */
#f-drop{
  position:absolute;inset:0;z-index:150;
  display:grid;place-items:center;
  background:color-mix(in srgb, var(--void) 78%, transparent);
  pointer-events:none;                     /* the drop is handled by the page */
}
.dropcard{
  display:flex;flex-direction:column;align-items:center;gap:calc(var(--u)*2);
  padding:calc(var(--u)*8) calc(var(--u)*12);text-align:center;
  background:var(--panel);
  border:var(--u) dashed var(--accent);
}
.dropcard img{width:48px;height:48px}
.dropcard .dt{font-family:var(--screen);font-weight:700;font-size:15px;letter-spacing:.1em;color:var(--accent)}
.dropcard .dp{font-family:var(--mono);font-size:12px;color:var(--text-2);
              max-width:60ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Progress panel. Fixed for the same reason the selection bar is: it appears
   because of something you did, and must not move what you are looking at. */
#uploader{
  position:fixed;z-index:220;
  right:calc(var(--u)*4);bottom:calc(var(--u)*4);
  width:min(420px,calc(100vw - var(--u)*8));
  display:flex;align-items:center;gap:calc(var(--u)*4);
  padding:calc(var(--u)*3);background:var(--panel);
  border-top:var(--u) solid var(--bevel-lt);border-left:var(--u) solid var(--accent);
  border-right:var(--u) solid var(--bevel-dk);border-bottom:var(--u) solid var(--bevel-dk);
}
#uploader.ok{border-left-color:var(--ok)}
#uploader.err{border-left-color:var(--crit)}
#uploader.ok  .donut .fill{stroke:var(--ok)}
#uploader.err .donut .fill{stroke:var(--crit)}

.donut.sm{width:64px;height:64px}
.donut.sm .dnum{font-size:15px}
.donut.sm .dnum i{font-size:9px}
.donut.sm circle{stroke-width:14}

.upmeta{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.uptitle{font-family:var(--screen);font-weight:700;font-size:11px;letter-spacing:.08em;color:var(--accent)}
#uploader.ok  .uptitle{color:var(--ok)}
#uploader.err .uptitle{color:var(--crit)}
.upname{font-size:11.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#uploader .hint{font-size:11px}

@media (max-width:640px){
  #uploader{left:calc(var(--u)*4);right:calc(var(--u)*4);width:auto}
  .dropcard{padding:calc(var(--u)*6)}
}

/* ============================================================
   DEVICES
   Three axes, kept separate on purpose:
     - width      decides layout (how many columns, where the nav goes)
     - pointer    decides hit-target size (a 1024px iPad is not a desktop)
     - viewport   decides reading size on a TV across a room
   Mixing them is why "mobile styles" usually break on tablets.
   ============================================================ */

/* iOS: keep content clear of the notch, the home indicator and rounded corners. */
#topbar{padding-left:max(calc(var(--u)*3),env(safe-area-inset-left));
        padding-right:max(calc(var(--u)*3),env(safe-area-inset-right))}
#main{padding-left:max(calc(var(--u)*4),env(safe-area-inset-left));
      padding-right:max(calc(var(--u)*4),env(safe-area-inset-right))}

/* ---------------- widget header controls ---------------- */
.w-cfg{
  font-size:13px;line-height:1;color:var(--text-3);flex:none;
  width:22px;height:22px;display:grid;place-items:center;
}
.w-cfg:hover{color:var(--accent);background:var(--panel)}
.w-x{font-size:15px;line-height:1}

/* Picked up by a long press: lifted, so it is obvious which one is in hand. */
.w.held{
  outline:3px solid var(--accent-2);outline-offset:-3px;
  filter:brightness(1.12);z-index:200;
}
#grid.stack .w.dragging{opacity:.9;outline:3px solid var(--accent);outline-offset:-3px}

/* ============================================================
   TOUCH  (any device driven by a finger, whatever its width)
   Apple and Google both land on ~44px as the smallest reliable target.
   ============================================================ */
@media (pointer:coarse){
  .btn{min-height:40px;padding:calc(var(--u)*2) calc(var(--u)*3);font-size:11px}
  .btn.sm{min-height:34px;font-size:10px}
  .nav{min-height:52px}
  .ctxopt{min-height:38px;padding:calc(var(--u)*2) calc(var(--u)*3)}
  .ctxcheck{min-height:44px}
  .swatch{width:36px;height:36px}
  .dayb{width:40px;height:40px}
  .toggle{width:52px;height:28px}
  .toggle i{width:20px;height:18px}
  .toggle.on i{transform:translateX(24px)}
  .w-cfg,.w-x{width:34px;height:34px;font-size:16px}
  .w-head{padding:calc(var(--u)*2)}
  /* A resize corner you can actually hit with a thumb. */
  .w-rs{width:30px;height:30px}
  #f-table td,#c-table td,.tw td{padding-top:calc(var(--u)*2.5);padding-bottom:calc(var(--u)*2.5)}
  .crumbs button{padding:calc(var(--u)*1.5) calc(var(--u)*2)}
  .rootcard{min-height:46px}
  input[type=text],input[type=password],input[type=search],select,.sel{min-height:42px}
  /* Hover states are noise on touch — they latch on after a tap. */
  .btn:hover,.nav:hover,.ctxopt:hover,.rootcard:hover{background:inherit}
}

/* ============================================================
   PHONES
   ============================================================ */
@media (max-width:640px){
  /* The nav becomes a bottom bar: the top of a phone screen is the hardest
     place to reach, and this is the control you press most. */
  #body{grid-template-columns:1fr;grid-template-rows:minmax(0,1fr) auto}
  #main{grid-row:1;padding:calc(var(--u)*3);padding-bottom:calc(var(--u)*10);
        padding-left:max(calc(var(--u)*3),env(safe-area-inset-left));
        padding-right:max(calc(var(--u)*3),env(safe-area-inset-right))}
  #rail{
    grid-row:2;flex-direction:row;width:auto;gap:0;
    padding:calc(var(--u)) calc(var(--u)) max(calc(var(--u)),env(safe-area-inset-bottom));
    border-right:none;border-top:var(--u) solid var(--bevel-dk);
    overflow-x:auto;overscroll-behavior-x:contain;
  }
  #rail .spacer,#rail-toggle,#rail-grip{display:none}
  /* The base .nav is width:100% for the vertical rail. Left in place here it
     makes every item as wide as the whole bar, so only the first one is on
     screen and the rest are a horizontal scroll away. */
  .nav{width:auto;flex:1 1 0;min-width:52px;flex-direction:column;gap:2px;padding:calc(var(--u)) 2px}
  .nav img{width:24px;height:24px;margin:0 auto}
  .nlabel{display:block!important;font-size:9px;letter-spacing:0;text-align:center;
          max-width:100%;line-height:1.1}
  .nav .tip{display:none!important}
  .nav.on::after{display:none}

  #topbar{gap:calc(var(--u)*1.5);padding:calc(var(--u)*1.5) calc(var(--u)*2)}
  #logo{width:24px;height:24px}
  .brand{font-size:11px}
  .stat.opt{display:none}
  #toolbar{gap:calc(var(--u))}
  #toolbar h1{font-size:12px}

  /* Toolbar actions scroll sideways rather than wrapping into three ragged
     rows and pushing the content off the bottom of the screen. */
  #toolbar > div{display:flex;gap:var(--u);overflow-x:auto;max-width:100%;
                 padding-bottom:2px;scrollbar-width:none}
  #toolbar > div::-webkit-scrollbar{display:none}

  .section{padding:calc(var(--u)*3)}
  .kv{grid-template-columns:1fr;gap:calc(var(--u))}
  .kv dt{margin-top:calc(var(--u)*2)}
  #f-drop .dropcard{padding:calc(var(--u)*5) calc(var(--u)*4)}
  .dropcard .dp{max-width:80vw}
  /* Clear of the bottom nav. Both of these are position:fixed, so without the
     offset they land on top of the navigation — which is the one control you
     need while a selection or an upload is in progress. */
  .selbar,#uploader{
    left:calc(var(--u)*2);right:calc(var(--u)*2);width:auto;
    bottom:calc(76px + env(safe-area-inset-bottom));
  }
  #page-files .tw{min-height:auto}
}

/* Full-height sheets beat tiny centred boxes on a phone. */
@media (max-width:560px){
  #modal{padding:0}
  .mw{max-width:none;max-height:100dvh;height:100dvh}
  .mw-body{padding:calc(var(--u)*3)}
  .mw-foot{padding:calc(var(--u)*3);padding-bottom:max(calc(var(--u)*3),env(safe-area-inset-bottom))}
  .mw-foot .btn{flex:1 1 auto}

  #ctx.sheet{
    left:0;right:0;bottom:0;top:auto;
    width:100%;max-width:none;min-width:0;max-height:76dvh;
    padding-bottom:max(calc(var(--u)*2),env(safe-area-inset-bottom));
    border-left-width:0;border-right-width:0;border-bottom-width:0;
    animation:sheetin .16s steps(3,end);
  }
  #ctx.sheet .ctxchecks{max-height:34dvh}
  #ctx.sheet .ctxfoot{position:sticky;bottom:0;background:var(--panel);padding-bottom:calc(var(--u)*2)}
  @keyframes sheetin{from{transform:translateY(24px)}to{transform:translateY(0)}}

  #drawer{padding:0}
  .dw{max-height:100dvh;height:100dvh;max-width:none}
  .appgrid{grid-template-columns:1fr}
  .jobwrap{gap:calc(var(--u)*3)}
}

/* ============================================================
   TABLETS  (iPad portrait sits right here)
   The 12-column canvas still works at this width; it just needs the chrome
   to stop eating a third of it.
   ============================================================ */
@media (min-width:641px) and (max-width:1024px){
  #main{padding:calc(var(--u)*3)}
  .appgrid{grid-template-columns:repeat(auto-fill,minmax(180px,1fr))}
  .frow > .field{flex:1 1 100%}
}

/* ============================================================
   TV AND LARGE DISPLAYS
   Read from three metres away, driven by a D-pad rather than a pointer.
   ============================================================ */
@media (min-width:1800px){
  body{font-size:17px}
  #topbar{padding:calc(var(--u)*3) calc(var(--u)*6)}
  #logo{width:40px;height:40px}
  .brand{font-size:18px}
  .stat{font-size:12px;padding:calc(var(--u)*1.5) calc(var(--u)*3)}
  #clock{min-width:140px}
  #clock .ctime{font-size:20px}
  #clock .cday{font-size:12px}
  .btn{font-size:12px;padding:calc(var(--u)*2) calc(var(--u)*4)}
  #toolbar h1{font-size:26px}
  /* Keep the overscan margin a TV panel may be cropping into. */
  #main{padding:calc(var(--u)*6);
        padding-left:max(calc(var(--u)*6),env(safe-area-inset-left));
        padding-right:max(calc(var(--u)*6),env(safe-area-inset-right))}
  .section h2{font-size:14px}
  .nlabel{font-size:15px}
  #tip{font-size:15px;max-width:380px}
}

/* A TV is driven by arrow keys, so focus has to be unmissable — the default
   2px offset disappears against a bevel at viewing distance. */
@media (min-width:1800px), (hover:none) and (pointer:coarse){
  :focus-visible{outline:calc(var(--u)*1.5) solid var(--accent);outline-offset:3px}
}

/* Landscape phones are short, not narrow. Give the content the height back. */
@media (max-height:460px) and (orientation:landscape){
  #topbar{padding:var(--u) calc(var(--u)*2)}
  #logo{width:22px;height:22px}
  .brand,.stat.opt{display:none}
  #toolbar{margin-bottom:calc(var(--u)*2)}
  #ctx{max-height:88dvh}
}

/* ============================================================
   CPU CORES
   A grid of columns that fill from the bottom. Status colours are reused
   deliberately here — on a core, "busy" is the meaning, not decoration.
   ============================================================ */
.coregrid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(26px,1fr));
  gap:calc(var(--u));flex:1;min-height:0;align-content:stretch;
}
.coregrid.asbars{grid-template-columns:1fr;gap:2px}
.core{
  position:relative;overflow:hidden;background:var(--sunk);min-height:18px;
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);
}
.core .cfill{
  position:absolute;left:0;right:0;bottom:0;height:var(--p,0%);
  background:var(--wc,var(--accent));opacity:.85;transition:height .25s linear;
}
.core.warn .cfill{background:var(--warn)}
.core.crit .cfill{background:var(--crit)}

/* Bars mode: same fill, turned on its side. */
.coregrid.asbars .core{min-height:0}
.coregrid.asbars .cfill{
  top:0;bottom:0;left:0;right:auto;height:auto;width:var(--p,0%);
  transition:width .25s linear;
}

/* Both labels sit ON the fill, and at 100% the fill is a solid block of colour.
   The shadow is what keeps them readable at every value — without it the core
   index vanishes into a red cell exactly when the cell matters most. */
.core .cn,.core .cv{
  position:absolute;pointer-events:none;
  text-shadow:0 0 4px var(--sunk),0 0 2px var(--sunk);
}
.core .cn{
  top:2px;left:0;right:0;text-align:center;
  font-family:var(--screen);font-size:calc(9px * var(--ws,1));color:var(--text-2);
}
.core .cv{
  bottom:2px;left:0;right:0;text-align:center;
  font-family:var(--mono);font-size:calc(10px * var(--ws,1));
  font-variant-numeric:tabular-nums;color:var(--text);
}
.coregrid.asbars .cn{top:50%;transform:translateY(-50%);left:calc(var(--u)*1.5);right:auto;text-align:left}
.coregrid.asbars .cv{top:50%;bottom:auto;transform:translateY(-50%);right:calc(var(--u)*1.5);left:auto;text-align:right}
.coreempty{font-size:12px;color:var(--text-3)}

/* ============================================================
   TOP PROCESSES
   Name and value on one line, a thin meter under it. The old version washed
   the whole row with a translucent bar, which muddied the text and — because
   it was scaled against 100 — was invisible on an idle box where every
   process is under 2%.
   ============================================================ */
.proclist{display:flex;flex-direction:column;gap:calc(var(--u)*1.5);flex:1;min-height:0;overflow:auto}
.prow{display:flex;flex-direction:column;gap:calc(var(--u));min-width:0;flex:none}
.prow .ptop{display:flex;align-items:baseline;gap:calc(var(--u)*2);min-width:0}
.prow .pname{
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:calc(12.5px * var(--ws,1));color:var(--text-2);
}
.prow .pval{
  flex:none;font-family:var(--mono);font-size:calc(12px * var(--ws,1));
  font-variant-numeric:tabular-nums;color:var(--text);
}
.prow .ptrack{
  display:block;height:calc(var(--u)*1.5);background:var(--sunk);position:relative;overflow:hidden;
  border-top:1px solid var(--bevel-dk);border-left:1px solid var(--bevel-dk);
}
.prow .pfill{
  position:absolute;left:0;top:0;bottom:0;background:var(--wc,var(--accent));
  transition:width .35s cubic-bezier(.22,.61,.36,1);
}
.prow .pfill.warn{background:var(--warn)}
.prow .pfill.crit{background:var(--crit)}
.prow:hover .pname{color:var(--text)}

/* ============================================================
   SECURITY WATCH
   ============================================================ */
.secwrap{display:flex;flex-direction:column;gap:calc(var(--u)*2);flex:1;min-height:0}
.sectiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(64px,1fr));gap:calc(var(--u));flex:none}
.stile{
  display:flex;flex-direction:column;align-items:center;gap:1px;
  padding:calc(var(--u)*1.5) calc(var(--u));background:var(--sunk);text-align:center;
  border-top:2px solid var(--bevel-dk);border-left:2px solid var(--bevel-dk);
  border-right:2px solid var(--bevel-lt);border-bottom:2px solid var(--bevel-lt);
}
.stile .sv{
  font-family:var(--screen);font-weight:700;font-size:calc(19px * var(--ws,1));
  font-variant-numeric:tabular-nums;color:var(--text);line-height:1.1;
}
.stile .sl{
  font-family:var(--screen);font-size:calc(8px * var(--ws,1));letter-spacing:.06em;
  color:var(--text-3);white-space:nowrap;
}
.stile.bad .sv{color:var(--crit)}
.stile.bad{border-left-color:var(--crit)}

.secfind{display:flex;flex-direction:column;gap:calc(var(--u));overflow:auto;min-height:0}
.sfind{
  display:flex;flex-direction:column;gap:1px;
  padding:calc(var(--u)*1.5) calc(var(--u)*2);background:var(--sunk);
  border-left:var(--u) solid var(--text-3);
}
.sfind.ok{border-left-color:var(--ok)}
.sfind.info{border-left-color:var(--accent)}
.sfind.warn{border-left-color:var(--warn)}
.sfind.crit{border-left-color:var(--crit)}
.sfind .st{font-family:var(--screen);font-weight:600;font-size:calc(11.5px * var(--ws,1));
           letter-spacing:.03em;color:var(--text)}
.sfind .sd{font-size:calc(11.5px * var(--ws,1));color:var(--text-3);line-height:1.45}

/* The header carries the worst finding's colour, so a glance at the title bar
   is enough to know whether the widget is worth opening. */
.w:has(.secwrap.lvl-warn) .w-head{box-shadow:inset 0 -2px 0 var(--warn)}
.w:has(.secwrap.lvl-crit) .w-head{box-shadow:inset 0 -2px 0 var(--crit)}

/* ============================================================
   WIDGET LIBRARY: what is already on the dashboard
   Each widget can be added once, so the library has to say which ones are
   spent. Muted rather than nearly invisible — you still need to read the name
   to understand why it cannot be added, and an unreadable card is just a hole
   in the grid.
   ============================================================ */
.dw-head .dcount{
  font-family:var(--screen);font-size:10px;letter-spacing:.08em;opacity:.75;
}
.card{position:relative}
.card .ct{flex:1;min-width:0}
/* The description is inline by default, so the badge flowed onto the end of its
   last line and sat on top of the text. */
.card .cd{display:block}

.card.used{
  background:var(--sunk);cursor:not-allowed;
  /* Pressed-in bevel: the same language the toggles and selected options use
     for "this one is already chosen". */
  border-top-color:var(--bevel-dk);border-left-color:var(--bevel-dk);
  border-right-color:var(--bevel-lt);border-bottom-color:var(--bevel-lt);
}
.card.used:hover{background:var(--sunk)}
.card.used img{opacity:.45;filter:grayscale(1)}
.card.used .cn{color:var(--text-3)}
.card.used .cd{color:var(--text-3);opacity:.7}

.card .cused{
  display:inline-flex;align-items:center;gap:5px;margin-top:calc(var(--u)*1.5);
  font-family:var(--screen);font-weight:600;font-size:9px;letter-spacing:.08em;
  padding:2px 6px;color:var(--ok);border:2px solid currentColor;
}
.card .cused i{font-style:normal;font-size:10px;line-height:1}

/* ============================================================
   SCROLLBARS
   The browser default is a rounded grey pill from a different design language
   entirely. These are square, bevelled and use the panel palette, so a
   scrolling list looks like part of the machine rather than a web page.
   ============================================================ */
*{scrollbar-width:thin;scrollbar-color:var(--bevel-lt) var(--sunk)}

::-webkit-scrollbar{width:12px;height:12px}
::-webkit-scrollbar-track{background:var(--sunk)}
::-webkit-scrollbar-corner{background:var(--sunk)}
::-webkit-scrollbar-thumb{
  background:var(--panel-hi);
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
}
::-webkit-scrollbar-thumb:hover{background:var(--accent)}
::-webkit-scrollbar-thumb:active{
  background:var(--accent);
  border-top-color:var(--bevel-dk);border-left-color:var(--bevel-dk);
  border-right-color:var(--bevel-lt);border-bottom-color:var(--bevel-lt);
}

/* Inside a widget the bar has to be slimmer or it eats the content. */
.w-body ::-webkit-scrollbar,.w-body::-webkit-scrollbar{width:8px;height:8px}
.w-body *{scrollbar-width:thin}

/* The terminal keeps xterm's own scrollbar sizing; overriding it there fights
   the canvas renderer's own measurements. */
#xterm-host ::-webkit-scrollbar{width:12px}

/* ============================================================
   HOST
   The old version used the generic key/value list, which right-aligns the
   value on one line — so a long distro string or a CPU model was clipped or
   collided with its own label. Labels are now a fixed narrow column and values
   wrap underneath themselves.
   ============================================================ */
.hostkv{
  display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:calc(var(--u)*2) calc(var(--u)*3);
  margin:0;align-content:start;overflow:auto;min-height:0;
}
.hostkv.dense{gap:calc(var(--u)) calc(var(--u)*2)}
.hostkv dt{
  font-family:var(--screen);font-weight:600;font-size:calc(10px * var(--ws,1));
  letter-spacing:.08em;text-transform:uppercase;color:var(--text-3);
  white-space:nowrap;padding-top:2px;
}
.hostkv dd{
  margin:0;min-width:0;
  font-family:var(--mono);font-size:calc(12.5px * var(--ws,1));
  color:var(--text);line-height:1.45;overflow-wrap:anywhere;
}
.hostkv dd a{color:var(--accent);text-decoration:none;border-bottom:1px solid currentColor}
.hostkv dd a:hover{background:var(--panel-hi)}
.hostkv .hdim{color:var(--text-3);font-size:.88em}

/* ============================================================
   CONTAINER PORTS
   Clickable, because the number alone just makes you retype it.
   ============================================================ */
.cports{display:inline-flex;gap:calc(var(--u));flex-wrap:wrap;align-items:center;min-width:0}
.cport{
  font-family:var(--mono);font-size:calc(11px * var(--ws,1));
  padding:1px calc(var(--u));color:var(--accent);text-decoration:none;flex:none;
  border:2px solid color-mix(in srgb, var(--accent) 45%, transparent);
}
.cport:hover{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
.crow .cports{flex:none}
#c-table .cports{gap:calc(var(--u)*1.5)}
#c-table .dim{color:var(--text-3)}

/* ============================================================
   DROP PREVIEW
   The outline of what you are carrying, drawn where it would land.
   Accent = the space is free and nothing else moves. Amber = the drop will
   push other widgets aside. Knowing which before you let go is the point.
   ============================================================ */
.wghost{
  position:absolute;z-index:90;pointer-events:none;
  border:3px dashed var(--accent);
  background:color-mix(in srgb, var(--accent) 10%, transparent);
  transition:left .07s linear,top .07s linear,width .07s linear,height .07s linear;
}
.wghost.bump{
  border-color:var(--warn);
  background:color-mix(in srgb, var(--warn) 10%, transparent);
}
#grid.stack .wghost{display:none}

/* ============================================================
   SERVER CAT
   Four moods, each a two-frame sprite animation. The frames are swapped in
   JavaScript on a mood-dependent beat rather than driven by a CSS keyframe,
   because the tempo is the information: a fast tail means a busy machine.
   ============================================================ */
.catwrap{
  display:flex;flex-direction:column;align-items:center;justify-content:flex-end;
  gap:calc(var(--u)*2);flex:1;min-height:0;position:relative;
  cursor:pointer;user-select:none;
}
.catwrap:focus-visible{outline:var(--u) solid var(--accent);outline-offset:2px}

.catstage{
  position:relative;flex:1;min-height:0;width:100%;
  display:flex;align-items:flex-end;justify-content:center;
  /* Room at the top for the speech bubble to float in. The bubble lives inside
     the stage and is absolutely positioned, so it can appear and disappear
     without ever changing how much room the cat has — which is what used to
     make the sprite visibly grow whenever she stopped talking. */
  padding-top:46px;
}
.catimg{
  width:100%;height:100%;max-height:160px;object-fit:contain;object-position:bottom center;
  image-rendering:pixelated;
  /* The sprite swap does the animation. This is only the slow breath under it,
     and a whole-body bob would fight the frames rather than add to them. */
  animation:catbreath 3.4s ease-in-out infinite;
  transform-origin:50% 100%;
}
@keyframes catbreath{
  0%,100%{transform:scaleY(1) scaleX(1)}
  50%{transform:scaleY(1.025) scaleX(.99)}
}
.mood-alert  .catimg{animation-duration:1.9s}
.mood-grumpy .catimg{animation-duration:1.1s}
.mood-sleepy .catimg{animation-duration:4.6s}
/* Mid-transition the pose is already changing shape; breathing on top of it
   reads as a wobble. */
.catwrap.rising .catimg{animation:none}

/* A press makes the cat squash, so a click feels like it landed on something. */
.catwrap.pet .catimg{animation:catpet .32s ease-out}
@keyframes catpet{
  0%{transform:scaleY(1) scaleX(1)}
  35%{transform:scaleY(.9) scaleX(1.07)}
  100%{transform:scaleY(1) scaleX(1)}
}
.catwrap:hover .catimg{filter:brightness(1.12) drop-shadow(0 0 6px color-mix(in srgb,var(--accent) 45%,transparent))}

/* Sleep Zs, drifting up and fading. */
.catz{
  position:absolute;right:14%;top:8%;pointer-events:none;
  font-family:var(--screen);font-weight:700;color:var(--text-3);
}
.catz i,.catz b{font-style:normal;font-weight:700;position:absolute}
.catz   {font-size:13px;animation:zdrift 3.4s linear infinite}
.catz i {font-size:10px;left:10px;top:-11px;animation:zdrift 3.4s linear infinite .9s}
.catz b {font-size:8px;left:18px;top:-20px;animation:zdrift 3.4s linear infinite 1.8s}
@keyframes zdrift{
  0%{opacity:0;transform:translateY(4px)}
  25%{opacity:.9}
  100%{opacity:0;transform:translateY(-16px)}
}

/* ---- speech bubble ----
   Drawn in CSS rather than 9-sliced from a sprite. border-image would not
   stretch the generated panel's middle to fill the box — it stayed a fixed
   width while the text grew past it, which is exactly the overlapping this was
   meant to fix. Square corners also put it back in step with the rest of the
   design system, which has no border radius anywhere.

   Absolutely positioned inside the stage: showing and hiding it must never
   resize the cat. */
.catbubble{
  position:absolute;top:0;left:50%;transform:translateX(-50%);z-index:5;
  max-width:96%;width:max-content;
  padding:calc(var(--u)*2) calc(var(--u)*3);
  background:#FDFDFD;border:2px solid #171320;
  font-family:var(--screen);font-weight:600;
  font-size:calc(11px * var(--ws,1));line-height:1.35;letter-spacing:.01em;
  color:#171320;text-align:center;
}
.catbubble[hidden]{display:none}
.catbubble .cbt{display:block;max-width:24ch;overflow-wrap:break-word}
/* One tail, in two layers so it carries the same outline as the box. The
   generated sprite had a tail of its own; having both is how the bubble ended
   up with two of them. */
.catbubble::before,.catbubble::after{
  content:"";position:absolute;left:50%;width:0;height:0;
  border-left:7px solid transparent;border-right:7px solid transparent;
}
.catbubble::before{margin-left:-7px;bottom:-12px;border-top:12px solid #171320}
.catbubble::after{margin-left:-7px;bottom:-9px;border-top:10px solid #FDFDFD}

/* The centring translate has to be repeated in the keyframes or the bubble
   jumps sideways for the length of the animation. */
.catbubble.pop{animation:bubblepop .18s steps(3,end)}
@keyframes bubblepop{
  from{opacity:0;transform:translateX(-50%) translateY(5px) scale(.92)}
  to{opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
}

/* ---- name and mood ---- */
.catfoot{
  display:flex;align-items:center;gap:calc(var(--u)*2);flex-wrap:wrap;
  justify-content:center;flex:none;width:100%;
}
.catname{
  font-family:var(--screen);font-weight:700;font-size:calc(12px * var(--ws,1));
  letter-spacing:.08em;color:var(--text);
}
.catmood{
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--screen);font-size:calc(9px * var(--ws,1));letter-spacing:.08em;
  padding:2px 6px;color:var(--text-3);border:2px solid currentColor;
}
.catmood .mdot{width:var(--u);height:var(--u);background:currentColor;flex:none}
.mood-sleepy  .catmood{color:var(--text-3)}
.mood-content .catmood{color:var(--ok)}
.mood-alert   .catmood{color:var(--warn)}
.mood-grumpy  .catmood{color:var(--crit)}
.mood-grumpy  .catmood .mdot{animation:blip 1s steps(2,end) infinite}

/* ---- carrying the cat around her own box ----
   The mover holds position, the image holds the breathing animation. Both are
   transforms, so they cannot live on the same element without one clobbering
   the other — which is why this is a wrapper and not a class on the sprite. */
.catmover{
  position:absolute;left:50%;top:100%;
  transform:translate(-50%,-100%);
  width:100%;height:100%;
  display:flex;align-items:flex-end;justify-content:center;
  cursor:grab;touch-action:none;
  transition:left .18s ease-out,top .18s ease-out;
}
.catwrap.carrying .catmover{
  cursor:grabbing;z-index:6;
  /* No transition while she is in your hand, or she lags behind the pointer. */
  transition:none;
  filter:drop-shadow(0 6px 4px rgba(0,0,0,.45));
}
.catwrap.carrying .catimg{animation:none;transform:rotate(-4deg) scale(1.04)}
.catwrap.landed .catimg{animation:catland .34s ease-out}
@keyframes catland{
  0%{transform:translateY(-6px) scaleY(1.06) scaleX(.95)}
  45%{transform:translateY(0) scaleY(.9) scaleX(1.08)}
  100%{transform:translateY(0) scaleY(1) scaleX(1)}
}

/* ============================================================
   NEXUS EXPERT — the dock, the panel, the robot
   Structure and the classic look. The workstation theme rounds and softens the
   same geometry in workstation.css; nothing here is theme-specific except the
   square corners, which belong to the pixel theme.
   ============================================================ */

/* ---- the robot ---- */
.hbot-art{display:inline-block;line-height:0;flex:none;width:34px}
.hbot-art.sm{width:22px}
.hbot-art.lg{width:64px}
.hbot{width:100%;height:auto;display:block;overflow:visible}
.hb-head{fill:var(--panel-hi);stroke:var(--rule);stroke-width:.5}
.hb-ear,.hb-stalk{fill:var(--rule)}
.hb-visor{fill:var(--void)}
.hb-eye{fill:var(--accent)}
.hb-bulb{fill:var(--crit)}
.hb-tooth{fill:var(--rule)}
.hb-scan{fill:var(--accent);opacity:0}

/* Three things move and nothing else: the bulb blinks, the eyes look around,
   and on hover a scan line sweeps the visor. Everything is a transform or an
   opacity, so none of it costs a layout. */
@keyframes hb-blink{0%,92%,100%{opacity:1}95%{opacity:.25}}
@keyframes hb-look{0%,38%{transform:translateX(0)}46%,60%{transform:translateX(.7px)}68%,86%{transform:translateX(-.7px)}94%,100%{transform:translateX(0)}}
@keyframes hb-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-.5px)}}
@keyframes hb-sweep{0%{opacity:0;transform:translateY(0)}20%{opacity:.5}100%{opacity:0;transform:translateY(3.1px)}}
.hb-bulb{animation:hb-blink 3.2s ease-in-out infinite}
.hb-eyes{animation:hb-look 7s ease-in-out infinite}
.hb-body{animation:hb-bob 4.4s ease-in-out infinite}
#hermes-open:hover .hb-scan{animation:hb-sweep 1.15s linear infinite}
#hermes-open:hover .hb-bulb{animation-duration:1s}
#hermes-open:hover .hb-eyes{animation-duration:2.2s}

/* ---- the dock button ---- */
#hermes-open{
  position:fixed;right:calc(var(--u)*5);bottom:calc(var(--u)*5);z-index:1100;
  display:flex;align-items:center;gap:calc(var(--u)*2);
  padding:calc(var(--u)*2) calc(var(--u)*3);
  background:var(--panel);color:var(--text);cursor:pointer;
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
  font-family:var(--screen);font-size:10px;letter-spacing:.1em;font-weight:600;
  transition:transform .14s ease-out,background-color .14s ease-out;
}
#hermes-open:hover{background:var(--panel-hi);transform:translateY(-2px)}
#hermes-open:active{transform:translateY(0)}
#hermes-open.tucked{transform:translateY(140%);pointer-events:none;opacity:0}
/* No key yet: the bulb goes amber rather than the button shouting. */
#hermes-open.unconfigured .hb-bulb{fill:var(--warn)}
.hbot-label{white-space:nowrap}

/* ---- the panel ---- */
#hermes{
  position:fixed;right:calc(var(--u)*5);bottom:calc(var(--u)*5);z-index:1100;
  width:min(410px,calc(100vw - var(--u)*10));
  height:min(620px,calc(100vh - var(--u)*24));
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--panel);
  border-top:2px solid var(--bevel-lt);border-left:2px solid var(--bevel-lt);
  border-right:2px solid var(--bevel-dk);border-bottom:2px solid var(--bevel-dk);
}
#hermes[hidden]{display:none}
.hx-head{display:flex;align-items:center;gap:calc(var(--u)*2);flex:none;
         padding:calc(var(--u)*2) calc(var(--u)*3);background:var(--sunk);
         border-bottom:2px solid var(--bevel-dk)}
.hx-who{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.hx-who b{font-family:var(--screen);font-size:11px;letter-spacing:.12em}
.hx-model{font-family:var(--mono);font-size:10px;color:var(--text-3);
          overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hx-ico{width:24px;height:24px;flex:none;display:grid;place-items:center;
        background:none;color:var(--text-3);font-size:14px;cursor:pointer}
.hx-ico:hover{background:var(--panel);color:var(--text)}

.hx-body{flex:1;min-height:0;overflow:auto;padding:calc(var(--u)*3);
         display:flex;flex-direction:column;gap:calc(var(--u)*2.5)}
.hx-hello{margin:auto;text-align:center;display:flex;flex-direction:column;
          align-items:center;gap:calc(var(--u)*2);padding:calc(var(--u)*4)}
.hx-hi{margin:0;font-family:var(--screen);font-size:15px;letter-spacing:.04em}
.hx-sub{margin:0;font-size:12px;color:var(--text-3);line-height:1.5;max-width:30ch}

.hx-row{display:flex}
.hx-row.me{justify-content:flex-end}
.hx-bub{max-width:88%;padding:calc(var(--u)*2) calc(var(--u)*2.5);
        font-size:13px;line-height:1.5;background:var(--sunk);word-break:break-word}
.hx-row.me .hx-bub{background:var(--selection,var(--panel-hi))}
.hx-bub p{margin:0 0 .6em}
.hx-bub p:last-child{margin:0}
.hx-bub code{font-family:var(--mono);font-size:12px;background:var(--void);padding:0 3px}
.hx-code{font-family:var(--mono);font-size:11.5px;background:var(--void);
         padding:calc(var(--u)*1.5);overflow:auto;margin:.5em 0;white-space:pre-wrap;word-break:break-word}
.hx-err{font-size:12px;color:var(--crit);background:var(--sunk);
        padding:calc(var(--u)*1.5) calc(var(--u)*2);width:100%}

/* A tool call is a fact about the machine, so it reads as a record: what it
   did, whether it worked, detail one click away. */
.hx-tool{font-size:12px;background:var(--sunk);border-left:3px solid var(--rule)}
.hx-tool summary{display:flex;align-items:center;gap:calc(var(--u)*2);
                 padding:calc(var(--u)*1.5) calc(var(--u)*2);cursor:pointer;list-style:none}
.hx-tool summary::-webkit-details-marker{display:none}
.hx-tool summary em{margin-left:auto;font-style:normal;font-family:var(--screen);
                    font-size:9px;letter-spacing:.1em;color:var(--text-3)}
.hx-tick{width:6px;height:6px;flex:none;background:var(--ok)}
.hx-tool.failed{border-left-color:var(--crit)} .hx-tool.failed .hx-tick{background:var(--crit)}
.hx-tool.denied{border-left-color:var(--warn)} .hx-tool.denied .hx-tick{background:var(--warn)}
.hx-tool pre{margin:0;padding:0 calc(var(--u)*2) calc(var(--u)*2);font-family:var(--mono);
             font-size:11px;white-space:pre-wrap;word-break:break-word;color:var(--text-2);
             max-height:260px;overflow:auto}

@keyframes hx-pip{0%,80%,100%{opacity:.25}40%{opacity:1}}
.hx-think{display:flex;gap:4px;padding:calc(var(--u)*2)}
.hx-think i{width:5px;height:5px;background:var(--text-3);animation:hx-pip 1.1s infinite}
.hx-think i:nth-child(2){animation-delay:.15s}
.hx-think i:nth-child(3){animation-delay:.3s}

/* ---- the approval gate ---- */
.hx-ask{flex:none;padding:calc(var(--u)*3);background:var(--sunk);
        border-top:2px solid var(--warn)}
.hx-ask[hidden]{display:none}
.hx-askhead{display:flex;align-items:center;gap:calc(var(--u)*2);font-size:12px;margin-bottom:calc(var(--u)*2)}
.hx-risk{font-family:var(--screen);font-size:9px;letter-spacing:.1em;padding:2px 5px;
         background:var(--warn);color:var(--void);flex:none}
.hx-risk.exec{background:var(--crit);color:var(--panel-hi)}
.hx-preview{margin:0 0 calc(var(--u)*2);font-family:var(--mono);font-size:11px;
            background:var(--void);padding:calc(var(--u)*2);max-height:160px;overflow:auto;
            white-space:pre-wrap;word-break:break-word}
.hx-askrow{display:flex;gap:calc(var(--u)*2);justify-content:flex-end}

/* ---- composer + footer ---- */
.hx-compose{flex:none;display:flex;gap:calc(var(--u)*2);align-items:flex-end;
            padding:calc(var(--u)*2.5);border-top:2px solid var(--rule)}
.hx-compose textarea{flex:1;min-height:36px;max-height:120px;resize:none;
                     font-family:var(--pixel);font-size:13px;line-height:1.45}
.hx-foot{flex:none;display:flex;align-items:center;gap:calc(var(--u)*2);
         padding:calc(var(--u)*1.5) calc(var(--u)*3);background:var(--sunk);
         font-family:var(--mono);font-size:10px;color:var(--text-3);
         border-top:1px solid var(--rule)}
.hx-link{background:none;color:var(--text-3);cursor:pointer;font-family:var(--screen);
         font-size:9px;letter-spacing:.1em}
.hx-link:hover{color:var(--accent)}

/* On a phone the panel is the page. A 410px card floating over a 390px screen
   is a card with no screen left around it. */
@media(max-width:560px){
  #hermes{right:0;bottom:0;width:100vw;height:min(88vh,100vh - 40px);max-height:none}
  #hermes-open{right:calc(var(--u)*3);bottom:calc(var(--u)*3)}
  .hbot-label{display:none}
}
@media(max-width:640px){
  /* The rail is a bottom bar below 640px — sit above it, not on it. */
  #hermes-open{bottom:calc(var(--u)*3 + 68px)}
}

/* ============================================================
   SETTINGS → NEXUS EXPERT
   ============================================================ */
.ag-block{padding:calc(var(--u)*4) 0;border-top:1px solid var(--rule)}
.ag-block:first-child{border-top:0;padding-top:calc(var(--u)*2)}
.ag-block h3{margin:0 0 calc(var(--u)*3);font-family:var(--screen);font-size:11px;
             letter-spacing:.1em;color:var(--text-2)}
.ag-block h3 small{display:block;margin-top:4px;font-family:var(--mono);font-size:10px;
                   letter-spacing:0;color:var(--text-3);font-weight:400}

.ag-provs{display:flex;flex-wrap:wrap;gap:calc(var(--u)*2)}
.ag-prov{display:flex;flex-direction:column;align-items:flex-start;gap:3px;
         padding:calc(var(--u)*2) calc(var(--u)*3);background:var(--panel);cursor:pointer;
         border:2px solid var(--rule);text-align:left}
.ag-prov b{font-size:13px}
.ag-prov span{font-family:var(--mono);font-size:10px;color:var(--text-3)}
.ag-prov.on{border-color:var(--accent);background:var(--panel-hi)}

.ag-models{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:calc(var(--u)*2)}
.ag-model{display:flex;flex-direction:column;align-items:flex-start;gap:4px;text-align:left;
          padding:calc(var(--u)*3);background:var(--panel);border:2px solid var(--rule);cursor:pointer}
.ag-model.on{border-color:var(--accent);background:var(--panel-hi)}
.ag-tier{font-family:var(--screen);font-size:9px;letter-spacing:.1em;color:var(--accent)}
.ag-model b{font-size:14px}
.ag-price{font-family:var(--mono);font-size:11px;color:var(--text-2)}
.ag-price small{color:var(--text-3)}
.ag-price.none{color:var(--text-3);font-style:italic}
.ag-note{font-size:11.5px;color:var(--text-3);line-height:1.4}

.ag-row{display:flex;align-items:center;gap:calc(var(--u)*2);flex-wrap:wrap;margin-top:calc(var(--u)*3)}
.ag-row label{margin:0;flex:none;min-width:120px}
.ag-row input[type=text],.ag-row input[type=password]{flex:1;min-width:180px}
.ag-row input[type=number]{width:80px}

.ag-caps{display:flex;flex-direction:column;gap:calc(var(--u)*2)}
.ag-cap{display:flex;gap:calc(var(--u)*3);align-items:flex-start;padding:calc(var(--u)*2.5);
        background:var(--panel);border:2px solid var(--rule);cursor:pointer}
.ag-cap input{margin-top:3px;flex:none}
/* Tick boxes in the palette's own accent, not the browser's blue — the only
   place in this section that was still wearing somebody else's colour. */
#agent-panel input[type=checkbox]{accent-color:var(--accent);width:15px;height:15px}
.ag-capbody{display:flex;flex-direction:column;gap:3px}
.ag-capbody b{font-size:13px;font-weight:600}
.ag-capbody small{font-size:11.5px;color:var(--text-3);line-height:1.45}
.ag-cap.danger{border-color:var(--crit)}
.ag-cap.danger .ag-capbody b{color:var(--crit)}
.ag-cap.off{opacity:.55;cursor:not-allowed}

.ag-roots{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:calc(var(--u)*2)}
.ag-check{display:flex;gap:calc(var(--u)*2);align-items:flex-start;padding:calc(var(--u)*2);
          background:var(--panel);border:2px solid var(--rule);cursor:pointer}
.ag-check span{display:flex;flex-direction:column;min-width:0}
.ag-check small{font-family:var(--mono);font-size:10px;color:var(--text-3);
                overflow:hidden;text-overflow:ellipsis}

.ag-modes{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:calc(var(--u)*2)}
.ag-mode{display:flex;flex-direction:column;align-items:flex-start;gap:4px;text-align:left;
         padding:calc(var(--u)*3);background:var(--panel);border:2px solid var(--rule);cursor:pointer}
.ag-mode.on{border-color:var(--accent);background:var(--panel-hi)}
.ag-mode span{font-size:11.5px;color:var(--text-3);line-height:1.4}
.warnline{border-left:3px solid var(--warn);padding-left:calc(var(--u)*2);margin-top:calc(var(--u)*3)}
.ag-usage dd{font-family:var(--mono)}
.ag-usage small{color:var(--text-3)}

/* ---- key test result ---- */
.ag-test{margin:calc(var(--u)*2) 0 0;font-family:var(--mono);font-size:11.5px;min-height:1.2em;color:var(--text-3)}
.ag-test.ok{color:var(--ok)} .ag-test.bad{color:var(--crit)}
.ag-test.working{color:var(--text-3)}

/* ============================================================
   SKILL LIBRARY
   ============================================================ */
.sk-sum{display:flex;flex-wrap:wrap;gap:calc(var(--u)*4);align-items:center;
        padding:calc(var(--u)*3);background:var(--sunk);margin-bottom:calc(var(--u)*3)}
.sk-sumrow{display:flex;align-items:baseline;gap:calc(var(--u)*2)}
.sk-sumrow b{font-family:var(--mono);font-size:19px;font-variant-numeric:tabular-nums}
.sk-sumrow span{font-size:11.5px;color:var(--text-3)}
.sk-budget{flex:1 1 220px;min-width:180px;display:flex;flex-direction:column;gap:4px}
.sk-budget span{font-size:10.5px;color:var(--text-3);font-family:var(--mono)}
.sk-track{height:6px;background:var(--void);overflow:hidden}
.sk-track i{display:block;height:100%;background:var(--accent)}
.sk-budget.over .sk-track i{background:var(--warn)}
.sk-budget.over span{color:var(--warn)}

.sk-drop{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
         padding:calc(var(--u)*6) calc(var(--u)*3);margin-bottom:calc(var(--u)*3);text-align:center;
         border:2px dashed var(--rule);background:var(--sunk);color:var(--text-3);font-size:12px;
         transition:background-color .12s ease-out,border-color .12s ease-out}
.sk-drop b{color:var(--text);font-family:var(--screen);font-size:11px;letter-spacing:.09em}
.sk-drop.over{border-color:var(--accent);background:var(--panel-hi);color:var(--text)}

.sk-list{display:flex;flex-direction:column;gap:calc(var(--u)*2)}
.sk-item{display:flex;gap:calc(var(--u)*3);align-items:flex-start;padding:calc(var(--u)*3);
         background:var(--panel);border:2px solid var(--rule)}
.sk-item.off{opacity:.5}
.sk-on{flex:none;margin:2px 0 0}
.sk-on input{width:16px;height:16px;accent-color:var(--accent)}
.sk-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.sk-name{display:flex;align-items:center;gap:calc(var(--u)*2);flex-wrap:wrap;font-size:14px;font-weight:600}
.sk-mode{font-family:var(--screen);font-size:9px;letter-spacing:.1em;padding:2px 5px;flex:none;
         background:var(--sunk);color:var(--text-2)}
.sk-mode.always{background:var(--accent);color:var(--on-accent)}
.sk-seed{font-family:var(--screen);font-size:9px;letter-spacing:.1em;padding:2px 5px;
         color:var(--text-3);border:1px solid var(--rule)}
.sk-desc{font-size:12px;color:var(--text-2);line-height:1.45}
.sk-meta{font-family:var(--mono);font-size:10px;color:var(--text-3)}
/* A row, not a column: stacked they made every entry three buttons tall and
   the library scrolled for no reason. */
.sk-acts{display:flex;flex-wrap:wrap;gap:calc(var(--u)*1.5);flex:none;align-items:flex-start}
.sk-loading{padding:calc(var(--u)*8);text-align:center;color:var(--text-3);font-size:12px}
.sk-foothint{margin-right:auto}

.sk-edit{display:flex;flex-direction:column;gap:calc(var(--u)*2)}
.sk-modes{flex:1;min-width:240px}
.sk-body{width:100%;min-height:340px;resize:vertical;font-family:var(--mono);font-size:12px;
         line-height:1.6;background:var(--sunk);color:var(--text);padding:calc(var(--u)*3);
         border:2px solid var(--rule)}

@media(max-width:820px){
  .sk-item{flex-wrap:wrap}
  .sk-acts{width:100%}
}

/* The status pill's colour follows the real state of the link — it used to be
   pinned green by the theme, which meant it read LIVE while nothing was. */
#st-state[data-link="live"]{color:var(--ok)}
#st-state[data-link="polling"]{color:var(--warn)}
#st-state[data-link="offline"]{color:var(--crit)}
#st-state[data-link="connecting"]{color:var(--text-3)}

/* ============================================================
   THE LINK BANNER
   Shown when the metrics WebSocket cannot be opened. It names the cause and
   the fix, because "everything reads zero" with no explanation is how a
   reverse-proxy setup eats an afternoon.
   ============================================================ */
.wsbanner{
  display:flex;align-items:flex-start;gap:calc(var(--u)*3);
  padding:calc(var(--u)*3) calc(var(--u)*4);margin-bottom:calc(var(--u)*4);
  background:var(--panel);border-left:4px solid var(--warn);
  font-size:13px;line-height:1.55;
}
.wsbanner.crit{border-left-color:var(--crit)}
.wsb-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:calc(var(--u)*1.5)}
.wsb-body b{font-family:var(--screen);font-size:11px;letter-spacing:.08em;color:var(--warn)}
.wsbanner.crit .wsb-body b{color:var(--crit)}
.wsb-body code{font-family:var(--mono);font-size:12px;background:var(--sunk);padding:1px 5px}
.wsb-body pre{margin:calc(var(--u)*1.5) 0 0;padding:calc(var(--u)*2.5);background:var(--sunk);
              font-family:var(--mono);font-size:11.5px;overflow:auto;white-space:pre}
.wsb-x{flex:none;width:22px;height:22px;display:grid;place-items:center;background:none;
       color:var(--text-3);font-size:15px;cursor:pointer}
.wsb-x:hover{color:var(--text);background:var(--sunk)}
