cicy-desktop 2.1.77 → 2.1.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +21 -18
  2. package/bin/cicy-desktop +7 -7
  3. package/build/icon.icns +0 -0
  4. package/build/icon.ico +0 -0
  5. package/build/icon.png +0 -0
  6. package/build/icon.svg +8 -18
  7. package/build/icons/icon-1024.png +0 -0
  8. package/build/icons/icon-128.png +0 -0
  9. package/build/icons/icon-16.png +0 -0
  10. package/build/icons/icon-24.png +0 -0
  11. package/build/icons/icon-256.png +0 -0
  12. package/build/icons/icon-32.png +0 -0
  13. package/build/icons/icon-48.png +0 -0
  14. package/build/icons/icon-512.png +0 -0
  15. package/build/icons/icon-64.png +0 -0
  16. package/build/icons/icon-96.png +0 -0
  17. package/build/icons/trayTemplate-16.png +0 -0
  18. package/build/icons/trayTemplate-16@2x.png +0 -0
  19. package/build/icons/trayTemplate-22.png +0 -0
  20. package/build/icons/trayTemplate-22@2x.png +0 -0
  21. package/build/icons/trayTemplate-32.png +0 -0
  22. package/build/icons/trayTemplate-32@2x.png +0 -0
  23. package/build/trayTemplate.png +0 -0
  24. package/build/trayTemplate.svg +11 -12
  25. package/build/trayTemplate@2x.png +0 -0
  26. package/package.json +6 -6
  27. package/src/backends/auth-loopback.js +17 -6
  28. package/src/backends/homepage-preload.js +22 -0
  29. package/src/backends/homepage-react/assets/index-CKpaMBKz.css +1 -0
  30. package/src/backends/homepage-react/assets/index-CSsNZgC5.js +365 -0
  31. package/src/backends/homepage-react/favicon-256.png +0 -0
  32. package/src/backends/homepage-react/favicon.svg +12 -0
  33. package/src/backends/homepage-react/index.html +4 -2
  34. package/src/backends/homepage-window.js +52 -7
  35. package/src/backends/ipc.js +57 -0
  36. package/src/backends/local-teams.js +114 -15
  37. package/src/backends/login-success.html +96 -0
  38. package/src/backends/sidecar-ipc.js +11 -0
  39. package/src/backends/webview-preload.js +5 -3
  40. package/src/backends/window-manager.js +13 -3
  41. package/src/chrome/chrome-launcher.js +5 -4
  42. package/src/chrome/debugger-port-resolver.js +1 -1
  43. package/src/cloud/cloud-client.js +435 -0
  44. package/src/cluster/types.js +0 -5
  45. package/src/extension/inject.js +1 -1
  46. package/src/main.js +329 -74
  47. package/src/master/chrome-config.js +2 -2
  48. package/src/preload-rpc.js +1 -1
  49. package/src/profiles/profile-store.js +321 -0
  50. package/src/profiles/trusted-origins-store.js +95 -0
  51. package/src/server/worker-observability-routes.js +0 -2
  52. package/src/sidecar/cicy-code.js +84 -23
  53. package/src/sidecar/localbin.js +20 -3
  54. package/src/sidecar/native.js +3 -3
  55. package/src/sidecar/version.js +45 -0
  56. package/src/tabbrowser/newtab-protocol.js +54 -0
  57. package/src/tabbrowser/tab-browser.html +151 -0
  58. package/src/tabbrowser/tab-shell-preload.js +28 -0
  59. package/src/tabbrowser/tab-shell.html +227 -0
  60. package/src/tools/account-tools.js +191 -25
  61. package/src/tools/chrome-tools.js +173 -37
  62. package/src/tools/device-tools.js +25 -0
  63. package/src/tools/index.js +2 -0
  64. package/src/tools/tab-browser-tools.js +453 -0
  65. package/src/tools/window-tools.js +64 -7
  66. package/src/utils/brand-host-electron.js +159 -0
  67. package/src/utils/context-menu-options.js +80 -0
  68. package/src/utils/cookie-logins.js +58 -0
  69. package/src/utils/ip-probe.js +50 -0
  70. package/src/utils/rpc-audit.js +53 -0
  71. package/src/utils/rpc-guard.js +189 -0
  72. package/src/utils/window-monitor.js +5 -15
  73. package/src/utils/window-registry.js +210 -0
  74. package/src/utils/window-thumbnails.js +126 -0
  75. package/src/utils/window-utils.js +192 -107
  76. package/workers/render/index.html +2 -0
  77. package/workers/render/package-lock.json +6 -6
  78. package/workers/render/public/favicon-256.png +0 -0
  79. package/workers/render/public/favicon.svg +12 -0
  80. package/workers/render/src/App.css +163 -33
  81. package/workers/render/src/App.jsx +756 -126
  82. package/src/backends/artifact-ipc.js +0 -142
  83. package/src/backends/homepage-react/assets/index-CPH-S8uU.css +0 -1
  84. package/src/backends/homepage-react/assets/index-DuWX0iug.js +0 -365
  85. package/src/cluster/artifact-registry.js +0 -61
package/README.md CHANGED
@@ -131,11 +131,11 @@ the Electron main process loads into the primary BrowserWindow.
131
131
 
132
132
  ### Where it's served from
133
133
 
134
- - **Windows**: remote `https://desktop.cicy-ai.com` (CF Worker hosts the
135
- prod bundle; UI changes ship without rebuilding cicy-desktop).
136
- - **Mac/Linux**: local `file:///.../src/backends/homepage-react/index.html`
137
- (a synced copy of `workers/render/dist/`, lets the homepage embed the
138
- HTTP team-assistant webview without mixed-content issues).
134
+ - **All platforms (Win/Mac/Linux)**: local
135
+ `file:///.../src/backends/homepage-react/index.html` (a synced copy of
136
+ `workers/render/dist/`). `pickHomepageURL()` returns this unconditionally —
137
+ the bundled `file://` SPA works offline, is fast, and lets the homepage
138
+ embed the HTTP team-assistant webview without mixed-content issues.
139
139
  - **Dev**: set `CICY_HOMEPAGE_URL=http://localhost:8173` to load the live
140
140
  vite dev server instead. See "Dev workflow" below.
141
141
 
@@ -146,8 +146,6 @@ the Electron main process loads into the primary BrowserWindow.
146
146
  cd workers/render && npm run build
147
147
  # copy into the file:// SPA folder cicy-desktop's main process loads from
148
148
  rsync -av --delete dist/ ../../src/backends/homepage-react/
149
- # deploy CF Worker (Windows users hit this URL)
150
- npx wrangler deploy
151
149
  ```
152
150
 
153
151
  ### Dev workflow
@@ -186,25 +184,30 @@ Vite HMR picks it up instantly. No Electron restart for React/CSS edits.
186
184
 
187
185
  `src/backends/homepage-window.js` URL priority:
188
186
  1. `CICY_HOMEPAGE_URL` env (dev override) → falls back to `file://` on load failure
189
- 2. Windows → `https://desktop.cicy-ai.com` (CF Worker `desktop-render`)
190
- 3. Mac/Linux → bundled `file://src/backends/homepage-react/index.html`
187
+ 2. All platforms bundled `file://src/backends/homepage-react/index.html`
191
188
 
192
- ### Shipping SPA changes to Windows
189
+ ### Shipping SPA changes to Windows (hotpatch)
193
190
 
194
- Win NSIS package main process loads `https://desktop.cicy-ai.com/`
195
- the `desktop-render` Cloudflare Worker. To ship a SPA change to Win
196
- users without rebuilding the desktop package, redeploy the Worker:
191
+ Windows loads the same local `file://` SPA as Mac/Linux the running
192
+ npm-global install reads `src/backends/homepage-react/`. So shipping a SPA
193
+ change = sync the files, **not** redeploy a Worker (there is no remote
194
+ homepage Worker for Windows anymore):
197
195
 
198
196
  ```bash
197
+ # build + mirror into the file:// folder
199
198
  cd workers/render && npm run build
200
- CLOUDFLARE_ACCOUNT_ID=$(jq -r .cf.prod.account_id ~/cicy-ai/global.json) \
201
- CLOUDFLARE_API_TOKEN=$(jq -r .cf.prod.api_token ~/cicy-ai/global.json) \
202
- npx wrangler deploy
203
- # Mirror dist/ into the file:// folder so Mac packaged builds match
204
199
  rsync -av --delete dist/ ../../src/backends/homepage-react/
200
+ # package backend + SPA and push to the Windows box
201
+ cd ../.. && tar czf /tmp/cicy-hotpatch.tgz src build bin package.json
202
+ scp /tmp/cicy-hotpatch.tgz win:'C:/Users/Administrator/cicy-hotpatch.tgz'
203
+ ssh win 'cd /d "C:\Users\Administrator\AppData\Roaming\npm\node_modules\cicy-desktop" && tar -xzf C:\Users\Administrator\cicy-hotpatch.tgz'
204
+ # relaunch in the *interactive* session (session 0 can't paint a GUI window)
205
+ ssh win 'schtasks /run /tn StartElectron'
205
206
  ```
206
207
 
207
- Win users see the new SPA on next cicy-desktop relaunch.
208
+ The `StartElectron` scheduled task is set to "interactive only" so the
209
+ relaunched window appears on the user's RDP desktop. `bin/cicy-desktop`
210
+ always opens `--remote-debugging-port=9221`, so verify the reload over CDP.
208
211
 
209
212
  ## Canonical config
210
213
 
package/bin/cicy-desktop CHANGED
@@ -323,7 +323,7 @@ function killAllElectronProcesses() {
323
323
  }
324
324
 
325
325
  function hardCleanupBeforeStart({ masterPort, workerPort }) {
326
- console.log(`🧹 Cleaning up ports ${masterPort}/${workerPort} and Electron processes...`);
326
+ console.log("🧹 Cleaning up previous instance…");
327
327
  killListenersByPort(masterPort);
328
328
  killListenersByPort(workerPort);
329
329
  killAllElectronProcesses();
@@ -738,8 +738,7 @@ async function startWorker({ workerPort, masterBaseUrl, token, workerUiUrl }) {
738
738
 
739
739
  async function startWorkerOnly({ workerPort }) {
740
740
  const tokenManager = new MasterTokenManager();
741
- const token = tokenManager.getToken();
742
- const workerUiUrl = `http://localhost:${workerPort}/console/chrome?token=${token}`;
741
+ tokenManager.getToken(); // ensure the token file exists before the worker reads it
743
742
 
744
743
  writeClusterState({
745
744
  masterPid: null,
@@ -750,8 +749,11 @@ async function startWorkerOnly({ workerPort }) {
750
749
  startedAt: new Date().toISOString(),
751
750
  });
752
751
 
753
- console.log(`🚀 Target worker http://localhost:${workerPort}`);
754
- console.log(`🪟 Worker UI ${workerUiUrl}`);
752
+ // The worker HTTP server on :PORT is skipped by default (see main.js —
753
+ // httpEnabled=automationEnabled), so don't advertise http://localhost:PORT or
754
+ // the /console/chrome UI: those URLs are dead unless CICY_DESKTOP_HTTP=1/--mcp.
755
+ // Renderer RPC goes through electronRPC injected in preload, not this port.
756
+ console.log("🚀 Starting CiCy Desktop…");
755
757
  console.log(`📄 Token file: ${tokenManager.getConfigPath()}`);
756
758
  console.log("🛑 Press Ctrl+C to stop processes started by this command");
757
759
 
@@ -827,8 +829,6 @@ async function startMasterCluster({ masterPort, workerPort }) {
827
829
  });
828
830
 
829
831
  console.log(`🚀 Target master ${masterBaseUrl}`);
830
- console.log(`🚀 Target worker http://localhost:${workerPort}`);
831
- console.log(`🪟 Worker UI ${workerUiUrl}`);
832
832
  console.log(`📄 Token file: ${tokenManager.getConfigPath()}`);
833
833
 
834
834
  const masterRunning = await checkMaster(masterBaseUrl);
package/build/icon.icns CHANGED
Binary file
package/build/icon.ico CHANGED
Binary file
package/build/icon.png CHANGED
Binary file
package/build/icon.svg CHANGED
@@ -1,22 +1,12 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" fill="none">
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" fill="none">
2
2
  <defs>
3
- <linearGradient id="bg" x1="0" y1="0" x2="1024" y2="1024" gradientUnits="userSpaceOnUse">
4
- <stop offset="0" stop-color="#FB923C"/>
5
- <stop offset="1" stop-color="#EA580C"/>
3
+ <linearGradient id="cicyMark" x1="16" y1="12" x2="80" y2="84" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#60A5FA"/>
5
+ <stop offset="0.55" stop-color="#2563EB"/>
6
+ <stop offset="1" stop-color="#1E3A8A"/>
6
7
  </linearGradient>
7
8
  </defs>
8
-
9
- <!-- Background: orange gradient rounded square (same palette as cicy.svg) -->
10
- <rect x="80" y="80" width="864" height="864" rx="200" fill="url(#bg)"/>
11
-
12
- <!-- Bold rounded "C" in soft cream/white (matches cicy.svg's #FFF7ED) -->
13
- <path d="M716 372
14
- C 670 320, 600 290, 520 290
15
- C 380 290, 280 392, 280 512
16
- C 280 632, 380 734, 520 734
17
- C 600 734, 670 704, 716 652"
18
- stroke="#FFF7ED"
19
- stroke-width="120"
20
- stroke-linecap="round"
21
- fill="none"/>
9
+ <path d="M48 11L39.5 33.3L16 29.5L31 48L16 66.5L39.5 62.7L48 85L56.5 62.7L80 66.5L65 48L80 29.5L56.5 33.3Z"
10
+ fill="url(#cicyMark)" stroke="url(#cicyMark)" stroke-width="8"
11
+ stroke-linejoin="round" stroke-linecap="round"/>
22
12
  </svg>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,13 +1,12 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" fill="none">
2
- <!-- Menu bar template: bold rounded "C", solid black on transparent.
3
- Matches the dock icon C shape for visual consistency. -->
4
- <path d="M16.5 7.2
5
- C 15.5 5.9, 13.9 5.2, 12 5.2
6
- C 8.7 5.2, 6.3 7.7, 6.3 11
7
- C 6.3 14.3, 8.7 16.8, 12 16.8
8
- C 13.9 16.8, 15.5 16.1, 16.5 14.8"
9
- stroke="#000"
10
- stroke-width="2.6"
11
- stroke-linecap="round"
12
- fill="none"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" fill="none">
2
+ <defs>
3
+ <linearGradient id="cicyMark" x1="16" y1="12" x2="80" y2="84" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#60A5FA"/>
5
+ <stop offset="0.55" stop-color="#2563EB"/>
6
+ <stop offset="1" stop-color="#1E3A8A"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <path d="M48 11L39.5 33.3L16 29.5L31 48L16 66.5L39.5 62.7L48 85L56.5 62.7L80 66.5L65 48L80 29.5L56.5 33.3Z"
10
+ fill="#000000" stroke="#000000" stroke-width="8"
11
+ stroke-linejoin="round" stroke-linecap="round"/>
13
12
  </svg>
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cicy-desktop",
3
- "version": "2.1.77",
3
+ "version": "2.1.79",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -133,11 +133,11 @@
133
133
  },
134
134
  "//optionalDependencies": "Runtime Bundle v1 (主人指令): platform binaries delivered by `npm i -g cicy-desktop` itself — npm installs only the current-platform subpackage (os/cpu pinned in each), so first start seeds the runtime store with ZERO network, ZERO npx. Windows packages are named *-windows-* (npm spam filter 403s new names containing win32). cicy-msys2 added once published.",
135
135
  "optionalDependencies": {
136
- "cicy-code-darwin-x64": "2.2.6",
137
- "cicy-code-darwin-arm64": "2.2.6",
138
- "cicy-code-linux-x64": "2.2.6",
139
- "cicy-code-linux-arm64": "2.2.6",
140
- "cicy-code-windows-x64": "2.2.6",
136
+ "cicy-code-darwin-x64": "2.3.0",
137
+ "cicy-code-darwin-arm64": "2.3.0",
138
+ "cicy-code-linux-x64": "2.3.0",
139
+ "cicy-code-linux-arm64": "2.3.0",
140
+ "cicy-code-windows-x64": "2.3.0",
141
141
  "cicy-mihomo-darwin-x64": "1.10.4",
142
142
  "cicy-mihomo-darwin-arm64": "1.10.4",
143
143
  "cicy-mihomo-linux-x64": "1.10.4",
@@ -18,10 +18,26 @@
18
18
  const http = require("http");
19
19
  const crypto = require("crypto");
20
20
  const os = require("os");
21
+ const fs = require("fs");
22
+ const path = require("path");
21
23
  const { shell } = require("electron");
22
24
  const log = require("electron-log");
23
25
 
24
26
  const LOGIN_BASE = "https://cicy-ai.com/login";
27
+
28
+ // Styled /cb success page (dark, CiCy-branded — matches the landing). Shipped
29
+ // alongside this module so it works in the packaged app; falls back to a plain
30
+ // inline page if the file is somehow missing.
31
+ const SUCCESS_FALLBACK = `<!doctype html><meta charset="utf-8">
32
+ <body style="font-family:-apple-system,sans-serif;text-align:center;padding:60px;color:#222;background:#f7f8fa">
33
+ <h1 style="color:#10b981;margin-bottom:8px">登录成功 ✓</h1>
34
+ <p style="opacity:.7">请回到 CiCy Desktop 继续使用。</p>
35
+ <p style="opacity:.4;font-size:13px;margin-top:32px">此页面可以安全关闭。</p>
36
+ </body>`;
37
+ const SUCCESS_HTML = (() => {
38
+ try { return fs.readFileSync(path.join(__dirname, "login-success.html"), "utf8"); }
39
+ catch (e) { log.warn(`[auth-loopback] success page load failed, using fallback: ${e.message}`); return SUCCESS_FALLBACK; }
40
+ })();
25
41
  // 120 s — per w-10032 cloud spec. Cloud-side fallback when /exchange fails
26
42
  // is to redirect to /team/dashboard, NOT back to our loopback, so the
27
43
  // listener never sees a result and must self-time-out instead of hanging
@@ -100,12 +116,7 @@ async function startLogin({ onResult } = {}) {
100
116
  }
101
117
 
102
118
  res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
103
- res.end(`<!doctype html><meta charset="utf-8">
104
- <body style="font-family:-apple-system,sans-serif;text-align:center;padding:60px;color:#222;background:#f7f8fa">
105
- <h1 style="color:#10b981;margin-bottom:8px">登录成功 ✓</h1>
106
- <p style="opacity:.7">请回到 CiCy Desktop 继续使用。</p>
107
- <p style="opacity:.4;font-size:13px;margin-top:32px">此页面可以关闭。</p>
108
- </body>`);
119
+ res.end(SUCCESS_HTML);
109
120
  try { onResult && onResult({ token, state, reused, accessToken, userId }); } catch {}
110
121
  setTimeout(() => shutdown("login complete"), 500);
111
122
  });
@@ -109,6 +109,7 @@ contextBridge.exposeInMainWorld("cicy", {
109
109
  // or Docker (Windows) — no in-app downloader. Only lifecycle + status remain.
110
110
  sidecar: {
111
111
  status: () => logInvoke("sidecar:status"),
112
+ versions: () => logInvoke("sidecar:versions"),
112
113
  start: () => logInvoke("sidecar:start"),
113
114
  stop: () => logInvoke("sidecar:stop"),
114
115
  restart: () => logInvoke("sidecar:restart"),
@@ -152,6 +153,26 @@ contextBridge.exposeInMainWorld("cicy", {
152
153
  clipboard: {
153
154
  write: (text) => logInvoke("clipboard:write", text),
154
155
  },
156
+ // Trusted-origins allowlist (Chrome-style site settings). Sites listed here may
157
+ // receive the electronRPC bridge in profile 0 = run commands on this machine,
158
+ // so the settings UI must warn loudly. list() returns [{host, builtin}], add/
159
+ // remove return { ok, origins?:[{host,builtin}], error? } and take effect live.
160
+ trustedOrigins: {
161
+ list: () => logInvoke("trustedOrigins:list"),
162
+ add: (host) => logInvoke("trustedOrigins:add", host),
163
+ remove: (host) => logInvoke("trustedOrigins:remove", host),
164
+ },
165
+ // Read-only RPC audit log viewer (~/cicy-ai/db/rpc-audit.log). tail(limit)
166
+ // returns { ok, entries:[{ts,kind,...}] newest-first, path }.
167
+ rpcAudit: {
168
+ tail: (limit) => logInvoke("rpcAudit:tail", { limit }),
169
+ },
170
+ // Open / reload a URL as a TAB in profile 0 (homepage cards open like the local
171
+ // card — current profile, not a new window / system browser).
172
+ tabs: {
173
+ open: (url, title) => logInvoke("tabs:open", { url, title }),
174
+ reload: (url, title) => logInvoke("tabs:reload", { url, title }),
175
+ },
155
176
 
156
177
  // ------- new bridges (last rebuild!) -------
157
178
  app: {
@@ -207,6 +228,7 @@ contextBridge.exposeInMainWorld("cicy", {
207
228
  remove: (id) => logInvoke("localTeams:remove", id),
208
229
  update: (id, patch) => logInvoke("localTeams:update", { id, patch }),
209
230
  upgrade: (id) => logInvoke("localTeams:upgrade", id),
231
+ syncCloud: () => logInvoke("localTeams:syncCloud"),
210
232
  // Subscribe to relay requests forwarded from a child <webview>
211
233
  // (the Team Helper webview, via webview-preload.js). Each fire
212
234
  // delivers {reqId, msg:{type, ...payload}}; the renderer is
@@ -0,0 +1 @@
1
+ :root{--accent: #5b8df7;--accent-soft: rgba(91,141,247,.16);--accent-line: rgba(91,141,247,.32);--accent-text: #a5c4ff;--ok: #4ade80;--danger: #f7a3a3;--text: #e5e7eb;--text-dim: #9da7b3;--text-mute: #6b7280;--line: rgba(255,255,255,.08)}*{box-sizing:border-box}html,body,#root{margin:0;height:100%}html,body{overflow:hidden}body{font-family:-apple-system,BlinkMacSystemFont,PingFang SC,Segoe UI,Roboto,Helvetica Neue,sans-serif;background:#07080c;color:#e5e7eb;-webkit-font-smoothing:antialiased}.shell{position:relative;min-height:100vh;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-app-region:drag}.shell--app{align-items:stretch;justify-content:stretch;flex-direction:row;background:linear-gradient(180deg,#0b0d13,#07080c);overflow:hidden;-webkit-app-region:no-drag}.shell__left{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden}.helper-aside{flex:0 0 auto;position:relative;background:#0f1115;border-left:1px solid rgba(255,255,255,.06);display:flex;flex-direction:column;min-width:320px;z-index:1}.helper-aside,.helper-aside *,.shell--app .main{-webkit-app-region:no-drag}.shell--app .topbar{-webkit-app-region:drag}.shell--app .topbar .user-chip,.shell--app .topbar .user-chip *,.shell--app .topbar .btn-ghost{-webkit-app-region:no-drag}.glow{position:absolute;top:-40%;right:-40%;bottom:-40%;left:-40%;z-index:0;pointer-events:none;background:radial-gradient(closest-side,rgba(91,141,247,.2),transparent 60%),radial-gradient(closest-side at 30% 70%,rgba(167,139,250,.13),transparent 65%);filter:blur(30px)}.card{-webkit-app-region:no-drag;position:relative;z-index:1;width:380px;padding:36px 36px 28px;background:linear-gradient(180deg,#141820d9,#0d1016d9);border:1px solid rgba(255,255,255,.06);border-radius:16px;box-shadow:0 1px #ffffff0a inset,0 30px 60px #0006;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);display:flex;flex-direction:column;align-items:center;gap:18px}.brand{display:flex;align-items:center;gap:12px;align-self:stretch}.brand-mark{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);box-shadow:0 8px 20px #5b8df759}.brand-mark.sm{width:28px;height:28px;border-radius:8px;box-shadow:none}.brand-mark.sm svg{width:16px;height:16px}.brand-text{line-height:1.2}.brand-name{font-weight:600;font-size:15px}.brand-sub{font-size:12px;color:#9ca3af;margin-top:2px}.tagline{margin:4px 0 0;color:#c7cdd6;font-size:13.5px;text-align:center;line-height:1.55}.btn-primary{-webkit-app-region:no-drag;margin-top:4px;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:42px;display:inline-flex;align-items:center;justify-content:center;gap:8px;background:linear-gradient(180deg,#5b8df7,#4570d8);color:#fff;border:0;border-radius:10px;font-size:14px;font-weight:500;letter-spacing:.2px;cursor:pointer;box-shadow:0 1px #ffffff2e inset,0 -1px #0000002e inset,0 10px 22px #5b8df747;transition:transform 80ms ease,filter .12s ease}.btn-primary:hover{filter:brightness(1.08)}.btn-primary:active{transform:translateY(1px)}.btn-primary:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.btn-ghost{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:6px 14px;font-size:12.5px;cursor:pointer;transition:color .12s ease,border-color .12s ease,background .12s ease}.btn-ghost.sm{padding:4px 10px;font-size:11.5px}.btn-ghost:hover{color:#e5e7eb;background:#ffffff0a;border-color:#ffffff24}.btn-ghost:disabled{opacity:.4;cursor:default}.hint{margin:0;font-size:11.5px;color:#6b7280}.spinner-row{display:inline-flex;align-items:center;gap:8px;color:#c7cdd6;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.error{width:100%;font-size:12px;color:#fca5a5;background:#ef444414;border:1px solid rgba(239,68,68,.18);padding:8px 12px;border-radius:8px;text-align:center;line-height:1.5}.topbar{-webkit-app-region:drag;position:relative;flex:0 0 auto;z-index:10;display:flex;align-items:center;justify-content:space-between;padding:14px 24px 12px;background:#08090e99;border-bottom:1px solid rgba(255,255,255,.05);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}[data-platform=darwin][data-fullscreen="0"] .topbar{padding-top:34px}.brand-mini{display:inline-flex;align-items:center;gap:10px}.brand-mini .brand-name{font-size:14px}.user-chip{-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:10px}.welcome{font-size:12px;color:var(--text-dim);padding:4px 10px;border-radius:999px;background:#ffffff0d;border:1px solid var(--line);animation:fadein .2s ease}@keyframes fadein{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:none}}.avatar{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:linear-gradient(135deg,#5b8df7,#a78bfa);font-size:12px;font-weight:600;color:#fff}.user-name{font-size:13px;color:#c7cdd6}.user-chip{position:relative}.user-chip__trigger{-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:8px;border:1px solid transparent;background:transparent;border-radius:999px;padding:3px 8px 3px 3px;cursor:pointer;transition:.12s}.user-chip__trigger:hover,.user-chip__trigger.is-open{background:#ffffff0f;border-color:#ffffff1f}.user-chip__caret{font-size:10px;color:#8a93a3;transition:transform .12s}.user-chip__trigger.is-open .user-chip__caret{transform:rotate(180deg)}.user-chip__menu{position:absolute;top:38px;right:0;z-index:30;min-width:200px;padding:6px;background:#1b2027;border:1px solid rgba(255,255,255,.12);border-radius:12px;box-shadow:0 12px 34px #00000080;display:flex;flex-direction:column;gap:2px;animation:fadein .14s ease}.user-chip__menu-item{text-align:left;width:100%;border:none;background:transparent;color:#d1d5db;border-radius:8px;padding:9px 11px;font-size:13px;cursor:pointer;transition:.12s}.user-chip__menu-item:hover{background:#ffffff12;color:#fff}.user-chip__menu-item.is-danger{color:#f7a3a3}.user-chip__menu-item.is-danger:hover{background:#ef444429;color:#fff}.user-chip__menu-sep{height:1px;margin:4px 2px;background:#ffffff14}.user-chip__mitm-row{display:flex;align-items:center;justify-content:space-between;gap:10px;cursor:default}.user-chip__mitm-row:hover{background:transparent}.user-chip__mitm-label{font-size:13px;color:var(--text)}.mini-switch{-webkit-app-region:no-drag;position:relative;flex:0 0 auto;width:36px;height:20px;padding:0;border-radius:999px;border:1px solid var(--line);background:#ffffff14;cursor:pointer;transition:background .16s ease,border-color .16s ease}.mini-switch.is-on{background:var(--accent);border-color:var(--accent)}.mini-switch.is-busy{opacity:.6;cursor:default}.mini-switch:disabled{cursor:default}.mini-switch__knob{position:absolute;top:1px;left:1px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px #00000073;transition:transform .16s ease}.mini-switch.is-on .mini-switch__knob{transform:translate(16px)}.mini-switch.is-busy .mini-switch__knob{animation:spin 1s linear infinite}.user-chip__mitm-note{margin:0 4px;padding:2px 4px 6px;font-size:11px;color:var(--text-mute)}.user-chip__mitm-err{margin:2px 4px 6px;padding:5px 8px;font-size:11px;color:var(--danger);background:#ef44441a;border-radius:6px;line-height:1.4}.bcard__top-right{display:inline-flex;align-items:center;gap:6px}.bcard__billing-btn{-webkit-app-region:no-drag;border:1px solid var(--line);background:transparent;color:var(--text-dim);border-radius:7px;padding:3px 9px;font-size:11px;cursor:pointer;transition:color .12s,border-color .12s,background .12s}.bcard__billing-btn:hover{color:var(--accent-text);border-color:var(--accent-line);background:var(--accent-soft)}.glow--app{inset:-10% -10% auto -10%;height:50vh;background:radial-gradient(closest-side at 75% 0%,rgba(91,141,247,.18),transparent 65%),radial-gradient(closest-side at 20% 10%,rgba(167,139,250,.1),transparent 60%);filter:blur(40px)}.main{position:relative;z-index:1;padding:22px 32px 48px;width:100%;display:flex;flex-direction:column;gap:16px;flex:1 1 auto;min-height:0;overflow-y:auto}.main::-webkit-scrollbar{width:8px}.main::-webkit-scrollbar-track{background:transparent}.main::-webkit-scrollbar-thumb{background:#7d87964d;border-radius:999px;border:2px solid transparent;background-clip:padding-box}.main::-webkit-scrollbar-thumb:hover{background-color:#7d87968c}.app__tabs{display:inline-flex;align-items:center;gap:4px;padding:4px;background:#1418208c;border:1px solid rgba(255,255,255,.06);border-radius:10px;align-self:flex-start;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.app__tab{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;color:#9ca3af;border:0;border-radius:7px;padding:6px 14px;font-size:12.5px;font-weight:500;letter-spacing:.15px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:color .12s ease,background .12s ease}.app__tab:hover{color:#e5e7eb;background:#ffffff0a}.app__tab.is-active{color:#fff;background:#5b8df72e;box-shadow:0 0 0 1px #5b8df74d inset}.app__tab-count{font-size:10.5px;color:#9ca3af;background:#ffffff0f;padding:1px 6px;border-radius:999px;min-width:18px;text-align:center}.app__tab.is-active .app__tab-count{background:#5b8df74d;color:#fff}.app__tabsrow{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.app__add-team{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;flex:none;color:#5b8df7;font-weight:600;font-size:12.5px;background:#5b8df71a;border:1px solid rgba(91,141,247,.35);border-radius:9px;padding:7px 14px;transition:color .12s ease,background .12s ease,border-color .12s ease}.app__add-team:hover{color:#fff;background:#5b8df738;border-color:#5b8df7a6}.app__grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.add-card{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:200px;height:200px;background:transparent;border:1.5px dashed rgba(255,255,255,.12);border-radius:14px;color:#9ca3af;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;transition:border-color .16s ease,color .16s ease,background .16s ease}.add-card:hover{border-color:#5b8df780;color:#e5e7eb;background:#5b8df70d}.add-card__plus{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;background:#ffffff0a;font-size:22px;font-weight:300;color:inherit}.add-card__label{font-size:13px;font-weight:500}.bcard--helper{background:linear-gradient(160deg,#5b8df729,#a78bfa24 60%,#1418208c);border-color:#a78bfa40}.bcard--helper .bcard__accent{background:linear-gradient(90deg,#5b8df7,#a78bfa);opacity:1}.bcard--helper:hover{border-color:#a78bfa73}.bcard__pill--helper{background:#a78bfa2e;border-color:#a78bfa4d;color:#c4b5fd;font-size:11px;font-weight:600;letter-spacing:.2px}.bcard__helper-icon{font-size:12px;filter:grayscale(.2)}.bcard__badge--free{background:#34d39926;color:#34d399;border:1px solid rgba(52,211,153,.3)}.bcard__badge--trial{background:#ffffff0f;color:var(--text-dim);border:1px solid var(--line);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard__badge--local{background:#5b8df72e;color:#a5c4ff;border:1px solid rgba(91,141,247,.35);font-size:9.5px;letter-spacing:.3px;padding:2px 7px}.bcard--helper .bcard__name{color:#f3f4f6}.bcard__desc{margin:4px 0 0;font-size:11.5px;line-height:1.5;color:#c7cdd6;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.bcard__fineprint{margin:4px 0 0;font-size:10.5px;color:#9ca3af;opacity:.8}.bcard__cta--helper{background:#ffffff0d;box-shadow:none}.bcard__cta--helper:hover{background:var(--accent-soft)}.helper-aside__top{display:flex;align-items:center;justify-content:space-between;padding:12px 14px 10px;border-bottom:1px solid rgba(255,255,255,.05)}.helper-aside__title{font-size:12.5px;font-weight:600;color:#c7cdd6}.helper-aside__close{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;color:#6b7280;font-size:20px;line-height:1;width:26px;height:26px;border-radius:6px;cursor:pointer;transition:color .12s ease,background .12s ease}.helper-aside__close:hover{color:#e5e7eb;background:#ffffff0f}.helper-modal__backdrop{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:transparent;z-index:20;animation:fadein .14s ease;padding:16px;pointer-events:auto}.helper-modal{width:min(360px,100%);background:linear-gradient(180deg,#1f2733,#1a2029);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:20px 22px 16px;color:#e5e7eb;box-shadow:0 24px 48px -12px #0009,0 1px #ffffff14 inset;animation:fadein .18s ease}.helper-modal__title{font-size:14px;font-weight:600;color:#f3f4f6;margin-bottom:8px}.helper-modal__desc{font-size:12.5px;line-height:1.6;color:#b6bcc6;margin-bottom:18px}.helper-modal__desc code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11.5px;padding:1px 6px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);border-radius:4px;color:#e7eaef}.helper-modal__actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}.helper-modal__btn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:#ffffff0f;color:#d2d6dd;border:1px solid rgba(255,255,255,.1);border-radius:7px;padding:7px 14px;font-size:12.5px;font-weight:500;cursor:pointer;transition:background .12s ease,color .12s ease,transform 80ms ease}.helper-modal__btn:hover{background:#ffffff1a;color:#fff}.helper-modal__btn:active{transform:translateY(1px)}.helper-modal__btn--ghost{background:transparent;border-color:transparent;color:#ffffff8c;margin-right:auto;padding-left:4px;padding-right:4px}.helper-modal__btn--ghost:hover{background:transparent;color:#ffffffd9}.helper-modal__btn--primary{background:linear-gradient(180deg,#5b8df7,#4570d8);border-color:#ffffff2e;color:#fff}.helper-modal__btn--primary:hover{background:linear-gradient(180deg,#6c9af9,#5a82e0)}.helper-modal__btn:disabled{filter:grayscale(.4) brightness(.7);cursor:default}.helper-placeholder{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 40px;text-align:center;color:#9ca3af}.helper-placeholder__mark{font-size:48px;line-height:1;filter:grayscale(.2);opacity:.85}.helper-placeholder__title{margin:0;font-size:16px;font-weight:600;color:#e5e7eb}.helper-placeholder__sub{margin:0;font-size:12.5px;line-height:1.6;color:#9ca3af;max-width:320px}.helper-placeholder__note{margin-top:8px;font-size:11px;color:#6b7280;padding:4px 10px;background:#ffffff08;border:1px solid rgba(255,255,255,.06);border-radius:999px}.helper-placeholder__note code{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#c7cdd6}.section{background:linear-gradient(180deg,#141820b8,#0d1016b8);border:1px solid rgba(255,255,255,.06);border-radius:14px;box-shadow:0 1px #ffffff0a inset,0 14px 30px #00000052;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px 18px 16px}.section-head{display:flex;align-items:center;gap:8px;padding:0 4px 14px;border-bottom:1px solid rgba(255,255,255,.04);margin-bottom:14px}.section-icon{font-size:14px;opacity:.8}.section-head h2{margin:0;font-size:13.5px;font-weight:600;color:#e5e7eb;letter-spacing:.2px}.section-sub{font-size:12px;color:#6b7280}.section-body{display:flex;flex-direction:column;gap:10px}.grid{display:grid;grid-template-columns:repeat(auto-fill,200px);gap:14px}.bcard{position:relative;width:200px;height:200px;background:#1418208c;border:1px solid rgba(255,255,255,.07);border-radius:14px;padding:16px;display:flex;flex-direction:column;overflow:hidden;transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease;--brand: #5b8df7;--accent-cloud: #f59e0b;--accent-custom: #8b5cf6}.bcard:hover{transform:translateY(-2px);border-color:#ffffff24;box-shadow:0 12px 32px -10px #00000073,0 1px #ffffff0a inset}.bcard__accent{position:absolute;top:0;left:0;right:0;height:3px;background:var(--brand);opacity:.9}.bcard--cloud .bcard__accent{background:var(--accent-cloud)}.bcard--custom .bcard__accent{background:var(--accent-custom)}.bcard--online:before{content:"";position:absolute;bottom:-40%;right:-20%;width:140%;height:100%;background:radial-gradient(circle at center,rgba(91,141,247,.22) 0%,transparent 60%);pointer-events:none;opacity:.55}.bcard--cloud.bcard--online:before{background:radial-gradient(circle at center,rgba(245,158,11,.22) 0%,transparent 60%)}.bcard--custom.bcard--online:before{background:radial-gradient(circle at center,rgba(139,92,246,.22) 0%,transparent 60%)}.bcard__top{display:flex;align-items:center;justify-content:space-between;position:relative;z-index:5}.bcard__pill{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;background:#08090eb3;border:1px solid rgba(255,255,255,.07);border-radius:999px;color:#9ca3af}.bcard__pill svg{display:block}.bcard__dot{width:7px;height:7px;border-radius:50%;background:#6b7280;flex-shrink:0}.bcard__dot[data-tone=ok]{background:#34d399;box-shadow:0 0 0 2px #34d39940}.bcard__dot[data-tone=off]{background:#6b7280}.bcard__dot[data-tone=warn]{background:#fbbf24;box-shadow:0 0 0 2px #fbbf2440}.bcard__dot[data-tone=err]{background:#f87171;box-shadow:0 0 0 2px #f8717140}.bcard__badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:var(--accent-soft);color:var(--accent-text);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.bcard__body{flex:1;display:flex;flex-direction:column;gap:4px;padding-top:12px;padding-bottom:12px;position:relative;z-index:1;min-width:0}.bcard__name{margin:0;font-size:17px;font-weight:700;color:#e5e7eb;letter-spacing:-.015em;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__host{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,monospace;font-size:11px;color:#9ca3af;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bcard__meta{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px}.bcard__chip{font-size:10.5px;color:#9ca3af;background:#ffffff08;border:1px solid rgba(255,255,255,.06);padding:2px 7px;border-radius:999px}.bcard__cta{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;gap:8px;width:100%;height:38px;border:1px solid var(--line);border-radius:9px;background:#ffffff0d;color:var(--text);font-size:13px;font-weight:600;letter-spacing:.1px;cursor:pointer;transition:transform 80ms ease,background .14s ease,border-color .14s ease,color .14s ease;box-shadow:none}.bcard--cloud .bcard__cta,.bcard--custom .bcard__cta{background:#ffffff0d;box-shadow:none}.bcard__cta:hover{background:var(--accent-soft);border-color:var(--accent-line);color:var(--accent-text)}.bcard__cta:active{transform:translateY(1px)}.bcard__cta:disabled{background:#ffffff08;color:#6b7280;cursor:not-allowed;box-shadow:none;border:1px solid rgba(255,255,255,.06)}.empty{font-size:12.5px;color:#6b7280;padding:14px 16px;background:#ffffff05;border:1px dashed rgba(255,255,255,.08);border-radius:10px}.bcard__menuwrap{position:relative}.bcard__kebab{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1px solid transparent;border-radius:8px;background:transparent;color:#8b949e;cursor:pointer;transition:.15s}.bcard__kebab:hover:not(:disabled){color:#e6edf3;background:#ffffff0f;border-color:#ffffff1f}.bcard__kebab:disabled{opacity:.5;cursor:default}.bcard__kebab.has-dot{color:#e6edf3}.bcard__kebab.has-dot:after{content:"";position:absolute;top:3px;right:3px;width:7px;height:7px;border-radius:50%;background:#f59e0b;box-shadow:0 0 0 2px #0f1115}.bcard__menu{position:absolute;top:32px;right:0;z-index:20;min-width:150px;padding:5px;background:#1b2027;border:1px solid rgba(255,255,255,.12);border-radius:10px;box-shadow:0 10px 30px #00000073;display:flex;flex-direction:column;gap:2px}.bcard__menu-item{text-align:left;width:100%;border:none;background:transparent;color:#d1d5db;border-radius:7px;padding:7px 10px;font-size:12.5px;cursor:pointer;transition:.12s}.bcard__menu-item:hover{background:#ffffff12;color:#fff}.bcard__menu-item.is-accent{color:var(--accent-text);font-weight:600}.bcard__menu-item.is-accent:hover{background:var(--accent-soft);color:#c7dbff}.bcard__menu-item.is-danger{color:#f7a3a3}.bcard__menu-item.is-danger:hover{background:#ef444429;color:#fff}.bcard__menu--portal{z-index:9990}.bcard__menu--portal .bcard__menu-item{white-space:normal;word-break:break-word}.docker-setup{margin-bottom:14px;padding:14px 16px;background:linear-gradient(180deg,#5b8df714,#14182080);border:1px solid rgba(91,141,247,.22);border-radius:12px}.docker-setup__head{display:flex;flex-direction:column;gap:3px;margin-bottom:12px}.docker-setup__title{font-size:13.5px;font-weight:600;color:#e6edf3}.docker-setup__sub{font-size:11.5px;color:#9ca3af;line-height:1.5}.docker-setup__steps{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.docker-step{display:flex;align-items:center;gap:8px;font-size:12px;color:#9ca3af}.docker-step__dot{width:8px;height:8px;border-radius:50%;flex:none;background:#3a4150;transition:.2s}.docker-step__label{color:#c7cdd6;min-width:96px}.docker-step__msg{color:#8b949e;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.docker-step__pct{margin-left:auto;font-variant-numeric:tabular-nums;color:#5b8df7;flex:none}.docker-step.is-running .docker-step__dot{background:#5b8df7;box-shadow:0 0 0 3px #5b8df72e;animation:dpulse 1.2s ease-in-out infinite}.docker-step.is-running .docker-step__label{color:#e6edf3}.docker-step.is-done .docker-step__dot,.docker-step.is-skip .docker-step__dot{background:#10b981}.docker-step.is-skip{opacity:.7}.docker-step.is-retry .docker-step__dot{background:#f59e0b}.docker-step.is-error .docker-step__dot{background:#ef4444;box-shadow:0 0 0 3px #ef444426}.docker-step.is-error .docker-step__msg{color:#f7a3a3}@keyframes dpulse{0%,to{opacity:1}50%{opacity:.45}}.docker-setup__actions{display:flex;gap:8px;align-items:center}.docker-setup__actions .btn-primary{width:auto;padding:7px 16px;font-size:12.5px}.bcard__ver{font-size:11px;color:#8b949e;font-variant-numeric:tabular-nums}.bcard__chip--new{color:var(--accent-text);background:var(--accent-soft);border-color:var(--accent-line)}.bcard__opmsg{display:flex;align-items:center;gap:6px;margin-top:9px;font-size:11.5px;color:#9ca3af;word-break:break-word}.toast-host{position:fixed;right:16px;bottom:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;max-width:min(360px,calc(100vw - 32px));pointer-events:none}.toast{pointer-events:auto;position:relative;display:flex;flex-direction:column;gap:6px;padding:10px 30px 10px 12px;font-size:12.5px;line-height:1.4;color:var(--text, #e6eaf0);background:#161a21f5;border:1px solid rgba(125,135,150,.22);border-left:3px solid var(--accent, #3b82f6);border-radius:10px;box-shadow:0 8px 28px #0000006b;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);animation:toast-in .18s ease}@keyframes toast-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.toast[data-status=error]{border-left-color:#f87171}.toast[data-status=error] .toast__msg{color:#f87171}.toast[data-status=done]{border-left-color:#4ade80}.toast[data-status=done] .toast__msg{color:#4ade80}.toast__msg{word-break:break-word}.toast__x{position:absolute;top:6px;right:8px;background:none;border:none;cursor:pointer;padding:0;font-size:15px;line-height:1;color:var(--text-dim, #9da7b3)}.toast__x:hover{color:var(--text, #e6eaf0)}.toast__bar{display:block;height:4px;border-radius:2px;background:#7d879640;overflow:hidden}.toast__bar>span{display:block;height:100%;border-radius:2px;background:var(--accent, #3b82f6);transition:width .25s ease}.drawer-scrim{position:fixed;top:0;right:0;bottom:0;left:0;z-index:10000;display:flex;align-items:flex-end;justify-content:center;background:#06080c80;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);animation:drawer-fade .18s ease}@keyframes drawer-fade{0%{opacity:0}to{opacity:1}}.drawer{width:min(560px,calc(100vw - 24px));max-height:76vh;display:flex;flex-direction:column;margin-bottom:12px;background:#14181ffa;border:1px solid rgba(125,135,150,.2);border-radius:16px 16px 12px 12px;box-shadow:0 -10px 44px #00000080;overflow:hidden;animation:drawer-up .24s cubic-bezier(.22,1,.36,1)}@keyframes drawer-up{0%{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}.drawer__head{display:flex;align-items:center;justify-content:space-between;padding:14px 14px 12px 16px;border-bottom:1px solid rgba(125,135,150,.14)}.drawer__title{display:flex;align-items:center;gap:11px}.drawer__spark{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:8px;font-size:14px;font-weight:700;background:#5b8df729;color:var(--brand, #5b8df7)}.drawer__spark--done{background:#4ade8029;color:#4ade80}.drawer__spark--error{background:#f8717129;color:#f87171}.drawer__h{font-size:13.5px;font-weight:650;color:var(--text, #e6eaf0)}.drawer__sub{font-size:11.5px;color:var(--text-dim, #9da7b3);margin-top:1px}.drawer__x{background:none;border:none;cursor:pointer;padding:2px 6px;font-size:19px;line-height:1;color:var(--text-dim, #9da7b3);border-radius:6px}.drawer__x:hover:not(:disabled){color:var(--text, #e6eaf0);background:#7d87961f}.drawer__x:disabled{opacity:.35;cursor:default}.drawer__steps{display:flex;align-items:center;gap:0;padding:14px 18px 4px}.drawer__step{display:flex;align-items:center;gap:7px;color:var(--text-dim, #9da7b3);font-size:12px}.drawer__step-dot{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;font-size:11px;font-weight:700;border:1.5px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3);background:transparent;flex:none}.drawer__step-bar{width:30px;height:1.5px;background:#7d879640;margin:0 8px}.drawer__step.is-active .drawer__step-dot{border-color:var(--brand, #5b8df7);color:var(--brand, #5b8df7);box-shadow:0 0 0 3px #5b8df72e}.drawer__step.is-active .drawer__step-label{color:var(--text, #e6eaf0)}.drawer__step.is-done .drawer__step-dot{border-color:#4ade80;color:#06210f;background:#4ade80}.drawer__step.is-done .drawer__step-bar{background:#4ade8080}.drawer__step.is-error .drawer__step-dot{border-color:#f87171;color:#f87171}.drawer__log{flex:1;min-height:96px;overflow-y:auto;margin:10px 14px;padding:10px 12px;background:#0a0c10b3;border:1px solid rgba(125,135,150,.12);border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;line-height:1.7}.drawer__log-empty{color:var(--text-dim, #9da7b3)}.drawer__line{display:flex;align-items:baseline;gap:8px;padding:1px 0}.drawer__t{color:#6b7686;flex:none;font-variant-numeric:tabular-nums}.drawer__badge{flex:none;padding:0 6px;border-radius:4px;font-size:10px;font-weight:600;background:#5b8df729;color:#8fb0f5}.drawer__badge--swap{background:#f59e0b29;color:#f5b342}.drawer__badge--done{background:#4ade8029;color:#6ee79b}.drawer__linemsg{color:var(--text, #e6eaf0);word-break:break-word}.drawer__line[data-status=error] .drawer__linemsg{color:#f87171}.drawer__line[data-status=done] .drawer__linemsg{color:#6ee79b}.drawer__line[data-status=skip] .drawer__linemsg{color:var(--text-dim, #9da7b3)}.drawer__hint{margin:0 14px 8px;padding:8px 12px;background:#f59e0b1a;border:1px solid rgba(245,158,11,.28);border-radius:8px;color:#f5b342;font-size:11.5px;line-height:1.5}.drawer__foot{display:flex;align-items:center;gap:8px;padding:12px 14px;border-top:1px solid rgba(125,135,150,.14)}.drawer__foot-status{font-size:12.5px;color:var(--text-dim, #9da7b3);margin-right:auto}.drawer__foot-status.is-error{color:#f87171}.drawer__foot-status.is-done{color:#4ade80}.drawer__btn{padding:7px 16px;border-radius:8px;cursor:pointer;font-size:12.5px;font-weight:550;background:#7d879624;border:1px solid rgba(125,135,150,.2);color:var(--text, #e6eaf0)}.drawer__btn:hover{background:#7d879638}.drawer__btn.is-accent{background:var(--brand, #5b8df7);border-color:var(--brand, #5b8df7);color:#fff}.drawer__btn.is-accent:hover{filter:brightness(1.08)}.mitm-card{border:1px solid rgba(125,135,150,.22);border-radius:12px;padding:14px 16px;margin-bottom:14px;background:#1e242e66}.mitm-card--on{border-color:#4ade8059;background:#16281e66}.mitm-pill{position:fixed;top:64px;right:16px;z-index:9998;-webkit-app-region:no-drag;display:inline-flex;align-items:center;gap:8px;padding:4px 6px 4px 11px;border-radius:999px;width:fit-content;max-width:calc(100vw - 32px);background:#141e18d9;border:1px solid rgba(74,222,128,.3);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 4px 14px #0000004d;font-size:12px;line-height:1}.mitm-pill__dot{width:7px;height:7px;border-radius:50%;flex:none;background:#4ade80;box-shadow:0 0 0 3px #4ade8029}.mitm-pill__dot[data-busy="1"]{background:#9da7b3;box-shadow:0 0 0 3px #9da7b329}.mitm-pill__text{color:var(--text-dim, #9da7b3);white-space:nowrap}.mitm-pill__off{background:none;border:none;cursor:pointer;color:#6b7686;font-size:11.5px;padding:3px 7px;border-radius:999px}.mitm-pill__off:hover{color:#f87171;background:#f871711a}.mitm-card__head{display:flex;align-items:center;gap:8px;margin-bottom:6px}.mitm-card__dot{width:8px;height:8px;border-radius:50%;background:#9da7b3;flex:0 0 auto}.mitm-card__dot[data-state=on]{background:#4ade80;box-shadow:0 0 6px #4ade8099}.mitm-card__dot[data-state=warn]{background:#fbbf24}.mitm-card__dot[data-state=off]{background:#60a5fa}.mitm-card__title{font-size:14px;font-weight:600;color:var(--text, #e6edf3)}.mitm-card__desc{font-size:12.5px;line-height:1.5;color:var(--text-dim, #9da7b3);margin:0 0 10px}.mitm-card__note{color:#fbbf24}.mitm-card__error{font-size:12px;color:#f87171;margin-bottom:8px}.mitm-card__actions{display:flex;gap:8px}.mitm-card__btn{font-size:13px;padding:7px 16px;border-radius:8px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:500}.mitm-card__btn:disabled{opacity:.6;cursor:default}.mitm-card__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3)}.mitm-card__sub{color:var(--text-dim, #9da7b3);opacity:.8;font-size:11.5px}.terms-gate{display:flex;align-items:center;justify-content:center;padding:24px}.terms-gate__panel,.terms-gate__panel *{-webkit-app-region:no-drag}.terms-gate__panel{position:relative;z-index:1;width:min(680px,94vw);max-height:90vh;display:flex;flex-direction:column;background:#141921eb;border:1px solid rgba(125,135,150,.22);border-radius:16px;padding:28px 30px;box-shadow:0 24px 60px #00000080}.terms-gate__title{font-size:20px;font-weight:700;margin:0 0 4px;color:var(--text, #e6edf3)}.terms-gate__subtitle{font-size:13px;color:var(--text-dim, #9da7b3);margin:0 0 16px}.terms-gate__body{overflow-y:auto;flex:1 1 auto;min-height:0;padding-right:8px;border-top:1px solid rgba(125,135,150,.15);border-bottom:1px solid rgba(125,135,150,.15);padding-top:14px;padding-bottom:14px}.terms-gate__h2{font-size:14px;font-weight:600;margin:0 0 10px;color:var(--text, #e6edf3)}.terms-gate__summary{margin:0 0 14px;padding-left:20px}.terms-gate__summary li{font-size:13px;line-height:1.6;color:var(--text-dim, #c2cbd6);margin-bottom:8px}.terms-gate__viewfull{background:none;border:none;color:var(--accent, #3b82f6);cursor:pointer;font-size:13px;padding:4px 0;text-decoration:underline}.terms-gate__fulltext{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.6;color:var(--text-dim, #b3bcc8);background:#0003;border-radius:8px;padding:14px;margin:10px 0 0;font-family:inherit}.terms-gate__scrollhint{font-size:12px;color:#fbbf24;text-align:center;margin:12px 0 0}.terms-gate__actions{display:flex;gap:12px;justify-content:flex-end;margin-top:18px}.terms-gate__btn{font-size:14px;padding:10px 22px;border-radius:9px;border:none;cursor:pointer;background:var(--accent, #3b82f6);color:#fff;font-weight:600}.terms-gate__btn:disabled{opacity:.45;cursor:not-allowed}.terms-gate__btn--ghost{background:transparent;border:1px solid rgba(125,135,150,.35);color:var(--text-dim, #9da7b3);font-weight:500}