@font-face{font-family:Inter;src:url('/fonts/Inter-400-latin.woff2?v=2') format('woff2');font-weight:100 900;font-display:swap}@font-face{font-family:'Space Grotesk';src:url('/fonts/SpaceGrotesk-700-latin.woff2?v=2') format('woff2');font-weight:300 700;font-display:swap}
:root{color-scheme:dark;--bg:#111318;--surface:#1a1d23;--accent:#8ab4f8;--cyan:#7bcfff;--mint:#81c995;--danger:#f28b82;--text:#c2c7cf;--muted:#8c9199;--border:rgba(255,255,255,.08);--ease:cubic-bezier(.16,1,.3,1);font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI','Microsoft YaHei',sans-serif;color:#fff;background:var(--bg)}*{box-sizing:border-box}/* 页面滚动条不画出来（客户明确要求"别让我看见那条线"）：滚轮 / 触控板 /
   键盘 / 触屏都照常滚，只是不再绘制浏览器那条原生滚动条 —— 顺带也彻底消掉
   "进记录页时滚动条闪现一下"。只处理 html（页面本身）；抽屉、控制台弹窗这些
   自己的滚动容器仍旧用原生滚动条，别一刀切。 */
html{scrollbar-width:none}html::-webkit-scrollbar{width:0;height:0}
/* scrollbar-width 是**可继承**属性：上面把 html 设成 none 后，抽屉、控制台弹窗、
   文本域这些自己的滚动容器会跟着一起没有滚动条。这里显式还回 auto ——
   只隐藏"页面"那一条，组件内部该有的滚动条留着（否则 Firefox 里记事本/剪贴板
   这类文本框会看不出还能滚）。 */
.drawer,.console-popover,textarea,.table-wrap,pre,code{scrollbar-width:auto}
body{margin:0;line-height:1.65}h1,h2,h3,p{margin-top:0}h1,h2,h3,.brand{font-family:'Space Grotesk',Inter,sans-serif}button,input,textarea,select{font:inherit}button,a,input,textarea,select{touch-action:manipulation}button{cursor:pointer}button,a{transition:background .2s var(--ease),border-color .2s var(--ease),transform .2s var(--ease)}button{color:inherit;background:none;border:0}button:disabled{opacity:.45;cursor:not-allowed}a{color:inherit;text-decoration:none}button:focus-visible,a:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid var(--accent);outline-offset:4px}[hidden]{display:none!important}.skip{position:absolute;top:-80px;z-index:9999;background:var(--accent);color:#002e69;padding:12px}.skip:focus{top:0}::selection{background:var(--accent);color:#002e69}/* The nav and the account button slide in from the right instead of toggling the
   hidden attribute: display:none cannot be animated, which is what made them pop.
   They keep their place in the row while hidden, so nothing shifts on login. */
.site-header #nav,.site-header #menu{
  transition:opacity .28s var(--ease),transform .28s var(--ease),visibility .28s var(--ease);
}
.site-header #nav.is-off,.site-header #menu.is-off{
  opacity:0;visibility:hidden;pointer-events:none;transform:translateX(18px);
}
.site-header{height:72px;display:flex;align-items:center;gap:16px;padding:0 max(24px,calc((100vw - 1312px)/2));position:sticky;top:0;z-index:10;background:rgba(17,19,24,.9);backdrop-filter:blur(16px);border-bottom:1px solid var(--border)}.brand{font-weight:700;font-size:18px;letter-spacing:-.02em;margin-right:auto}nav{display:flex;padding:4px;border-radius:999px;background:var(--surface);border:1px solid var(--border)}nav button{padding:6px 18px;border-radius:999px;color:var(--text);font-size:13px}nav .active{background:var(--accent);color:#002e69;font-weight:700}.icon{width:36px;height:36px;display:inline-grid;place-items:center;background:rgba(255,255,255,.05);border:1px solid transparent;border-radius:50%;font-size:18px;line-height:1;flex-shrink:0;transition:background .2s var(--ease)}.icon:hover{background:rgba(255,255,255,.1)}.primary,.secondary,.danger{display:inline-flex;justify-content:center;align-items:center;gap:8px;min-height:44px;border-radius:999px;padding:10px 20px;font-size:14px;font-weight:600;border:1px solid rgba(255,255,255,.18)}.primary{background:var(--accent);color:#002e69;border-color:transparent}.primary:hover{background:#c2d7ff}.secondary{color:var(--text)}.secondary:hover{background:#2f3540;border-color:var(--accent)}.danger{color:#ffb4ab;border-color:#85534e}.danger:hover{background:#482927}.surface{background:var(--surface);border:1px solid var(--border);border-radius:24px;box-shadow:0 4px 12px #0004}.muted,.fine{color:var(--muted)}.fine{font-size:12px}dialog .actions button,dialog form>button{min-height:40px;min-width:88px;padding:0 18px;font-size:13.5px;justify-content:center}dialog form>textarea{margin-top:8px}dialog .actions{margin-top:20px;justify-content:flex-end}dialog p{color:var(--text);font-size:14px;line-height:1.6}dialog .error{font-size:13.5px}#drawer .secondary{min-height:40px;font-size:13.5px}.eyebrow{font-size:12px;letter-spacing:.12em;color:var(--accent)}.login-shell{display:grid;grid-template-columns:1.1fr .9fr;align-items:center;gap:clamp(32px,6vw,112px);min-height:calc(100vh - 72px);max-width:1600px;margin:auto;padding:60px clamp(24px,5vw,88px)}.hero h1{font-size:clamp(3.4rem,6.5vw,7.5rem);line-height:.94;letter-spacing:-.05em;margin-bottom:32px}.hero p{font-size:clamp(18px,1.7vw,24px);color:var(--text);margin-bottom:40px}.login-card{max-width:480px;width:100%;padding:40px;justify-self:end}.login-card h2{font-size:30px;font-weight:600;letter-spacing:-.02em;margin:10px 0}.login-card .primary{width:100%}label{display:block;font-size:13.5px;color:var(--text);margin:18px 0}input,textarea,select{background:var(--bg);color:white;border:1px solid rgba(255,255,255,.16);border-radius:10px;padding:11px 13px;max-width:100%}input,textarea{display:block;width:100%;margin-top:8px}.check{display:flex;align-items:center;gap:10px}.check input{width:auto;margin:0}.content{max-width:1360px;margin:auto;padding:56px 24px 96px}.page-head,.row,.dialog-head{display:flex;align-items:center;justify-content:space-between;gap:20px}.page-head{align-items:end;margin-bottom:32px}.page-head h1{font-size:clamp(38px,5vw,72px);line-height:1.08;letter-spacing:-.05em;margin:14px 0 18px}.page-head p{margin:0}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,370px),1fr));gap:24px}.vm-card{padding:28px}.vm-card h2{font-size:32px;margin:16px 0 4px;overflow-wrap:anywhere}.badge{border-radius:999px;background:#8ab4f81a;border:1px solid #8ab4f83d;color:var(--accent);padding:5px 11px;font-size:12px;white-space:nowrap}.badge.running{color:#8fe0b2;background:#8fe0b212;border-color:#8fe0b240}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px;margin:28px 0}.metrics small{display:block;color:var(--muted);font-size:12px}.metrics strong{font:600 24px 'Space Grotesk',sans-serif;overflow-wrap:anywhere}.actions{display:flex;flex-wrap:wrap;gap:8px}.vm-card .actions{margin-top:24px}.task-state{display:inline-block;padding:3px 10px;border-radius:999px;background:rgba(255,255,255,.07);color:var(--text);font-size:12px}.task-state.is-ok{background:rgba(129,221,177,.14);color:#8fe0b2}.task-state.is-error{background:rgba(242,139,130,.14);color:#f28b82}.error{color:#ffb4ab;white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px}.error:empty{display:none}.empty{padding:44px;text-align:center;color:var(--muted)}.section{padding:24px;margin-top:20px}.section h2{font-size:23px;margin-bottom:16px}.charts{display:grid;grid-template-columns:1fr 1fr;gap:24px}.chart{min-width:0}.chart svg{display:block;width:100%;height:auto;overflow:visible}.chart .axis{fill:var(--muted);font-size:10px}.chart .grid{stroke:#ffffff12}.legend{font-size:12px;color:var(--text);display:flex;gap:16px}.legend span:first-child{color:var(--accent)}.legend span:nth-child(2){color:var(--cyan)}.table-wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:13.5px}th,td{text-align:left;padding:12px 14px;border-bottom:1px solid var(--border);vertical-align:middle}th{font-weight:500;color:var(--muted);font-size:12px;letter-spacing:.03em;white-space:nowrap}td{overflow-wrap:anywhere}tbody tr:hover{background:rgba(255,255,255,.03)}td .actions{min-width:0}td button{min-height:36px;padding:5px 12px}.task-log{white-space:pre-wrap;overflow-wrap:anywhere;max-height:240px;overflow:auto;font-size:12px;color:var(--text)}dialog{color:#fff;background:var(--surface);border:1px solid rgba(255,255,255,.09);border-radius:28px;padding:24px;width:min(520px,calc(100% - 32px));max-height:90dvh;overflow:auto;box-shadow:0 24px 56px #000a}dialog::backdrop{background:#000a;backdrop-filter:blur(8px)}.dialog-head h2{margin:0;font-size:22px;font-weight:600;letter-spacing:-.02em;line-height:1.25}.dialog-head{margin-bottom:16px}.console-dialog{width:96vw;height:92dvh;max-width:1600px}.console-dialog .dialog-head{margin-bottom:8px}#console-screen{height:calc(100% - 120px);min-height:220px;background:#080a0d;overflow:hidden;border-radius:8px}#console-status{font-size:13px;color:var(--text)}#notice{position:fixed;inset:auto;left:50%;bottom:26px;transform:translateX(-50%);margin:0;max-width:min(560px,calc(100vw - 32px));padding:10px 18px;border-radius:999px;background:rgba(33,38,46,.96);border:1px solid rgba(255,255,255,.1);color:#e6eaf2;font-size:12.5px;line-height:1.5;text-align:center;white-space:pre-wrap;box-shadow:0 16px 38px #000a;z-index:1000;pointer-events:none;backdrop-filter:blur(10px)}#notice:popover-open{display:block}#notice:empty{display:none}/* 位置只走 transform（写 --cursor-x/--cursor-y），所以移动不触发布局；
   基础态**不给 transform 过渡** —— 空白处必须严格跟手。吸附/松手两档过渡见下面两条，
   力度是照 Hylab 站点那份实现搬的：0.14s cubic-bezier(0,.1,.1,1)。 */
#cursor{position:fixed;inset:auto;top:0;left:0;--cursor-x:-200px;--cursor-y:-200px;margin:0;padding:0;overflow:visible;width:24px;height:24px;border:1px solid #fff;border-radius:420px;background:#fff;pointer-events:none;transform:translate3d(var(--cursor-x),var(--cursor-y),0) translate(-50%,-50%);mix-blend-mode:exclusion;z-index:99999;opacity:0;will-change:transform,width,height,border-radius,opacity;transition:width .15s ease-in,height .15s ease-in,border-radius .15s ease-in,opacity .1s ease-in}
/* 吸附在控件上时，光点把"反相"这件事**交给控件自己做**（.hy-cursor-invert），
   自己则淡出成透明 —— 只保留"形状跟控件一致"的尺寸/圆角过渡，所以看上去是
   "圆圈长成按钮的形状、然后把反相交给按钮"。这样按钮边缘干干净净：
   任何"白形状盖在按钮上"的做法都会在按钮边缘留下一条白边（见下面注释）。 */
#cursor.-gliding{transition:transform .14s cubic-bezier(0,.1,.1,1),width .15s cubic-bezier(0,.1,.1,1),height .15s cubic-bezier(0,.1,.1,1),border-radius .15s cubic-bezier(0,.1,.1,1),background-color .14s ease-out,border-color .14s ease-out,opacity .1s ease-in;mix-blend-mode:normal;background:transparent;border-color:transparent}
/* 离开控件：**位置立刻回到指针**（transform 不参与过渡，否则指针下面会空一段，
   看起来像圆圈消失又回来），只有尺寸做一次 ease-out —— 站点那份就是这个做法。 */
#cursor.-leaving{transition:width .2s ease-out,height .2s ease-out,border-radius .2s ease-out,background-color .18s ease-out,border-color .18s ease-out}
/* 吸附到按钮/链接上时，**由控件自己反相**，而不是让光点盖住它：光点那种
   "白形状 + exclusion"的边界永远不可能和控件的抗锯齿边缘对齐，对不齐的那零点几
   像素会把控件自己的 hover 底色（浅蓝）留在外面 —— 在深色背景上就是一条白边
   （用户报过，DPR 125%/150% 更明显）。控件自己反相时，边缘就是它自己的抗锯齿
   边缘与深色背景的平滑过渡，没有边。⚠️ 这里不要写 transition，否则会覆盖掉
   控件自己的 hover 过渡。 */
.hy-cursor-invert{filter:invert(1)}
/* The magnetic cursor lives in the browser top layer (popover="manual") so it
   also paints above modal <dialog> windows such as the console. */
#cursor:popover-open{display:block}
/* The magnetic cursor replaces the native pointer wherever it is active, so the
   white circle always sits exactly on the pointer. Text entry, tables and the
   VNC console keep their own system cursor. */
/* The native pointer is hidden only while html.hy-cursor is set, and that class is
   added by script *after* it has verified the replacement can actually render.
   Hiding it on a media query alone left some browsers with no cursor at all. */
html.hy-cursor body{cursor:none}
html.hy-cursor body *:not(input):not(textarea):not(select):not([contenteditable]):not(.table-wrap):not(.table-wrap *){cursor:none!important}
html.hy-cursor input,html.hy-cursor textarea,html.hy-cursor [contenteditable]{cursor:text}
html.hy-cursor select,html.hy-cursor .table-wrap,html.hy-cursor .table-wrap *{cursor:auto}
@media(max-width:768px){#cursor{display:none!important}}@media(max-width:850px){.login-shell{grid-template-columns:1fr;min-height:auto;padding-top:48px}.hero{text-align:center}.hero h1{font-size:clamp(52px,12vw,85px)}.login-card{justify-self:center}.hero .secondary{display:none}.charts{grid-template-columns:1fr}.page-head{align-items:start}.site-header{gap:8px;padding:0 16px}.brand{font-size:15px}nav button{padding:6px 12px}.section,.vm-card{padding:22px}.console-dialog .dialog-head{align-items:start}.console-dialog .dialog-head .actions{justify-content:flex-end}.console-dialog .secondary{font-size:11px;padding:7px 10px}}@media(max-width:480px){.login-card{padding:28px}.content{padding:36px 16px 72px}.page-head h1{font-size:40px}.metrics strong{font-size:21px}.page-head{gap:10px}.page-head .secondary{padding:8px 13px}.dialog-head{gap:10px}.console-dialog{padding:16px}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}#cursor,#cursor.-gliding,#cursor.-leaving{transition:none!important}}

/* The VM number is what a customer quotes in support and types to confirm a risky
   operation, so it is the one part of these labels that is coloured. */
.vm-no{color:var(--danger);font-weight:700;letter-spacing:0}

/* 加载占位也要占住"一屏表格"的高度。否则进入记录页的那一瞬间页面比视口矮，
   右侧滚动条先消失、数据回来又出现 —— 看起来就是一条闪过的滑动指示线（用户报过）。
   顺带也让内容不会在加载完成时往上跳。 */
.empty.is-loading{min-height:min(62vh,620px);display:flex;align-items:center;justify-content:center}
