cicy-desktop 2.1.200 → 2.1.202
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.
- package/package.json +6 -6
- package/src/app-updater.js +164 -94
- package/src/backends/homepage-preload.js +1 -0
- package/src/backends/homepage-react/assets/index-BShnCn32.css +1 -0
- package/src/backends/homepage-react/assets/index-BjgNk0ef.js +377 -0
- package/src/backends/homepage-react/index.html +2 -2
- package/src/backends/ipc.js +1 -0
- package/src/i18n/locales/en.json +12 -0
- package/src/i18n/locales/fr.json +12 -0
- package/src/i18n/locales/ja.json +12 -0
- package/src/i18n/locales/zh-CN.json +12 -0
- package/src/main.js +2 -2
- package/src/sidecar/mirrors.js +10 -5
- package/src/utils/window-utils.js +8 -0
- package/workers/render/src/App.css +33 -0
- package/workers/render/src/App.jsx +65 -0
- package/src/backends/homepage-react/assets/index-BdfzcJ59.css +0 -1
- package/src/backends/homepage-react/assets/index-QFlSWxJR.js +0 -377
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cicy-desktop",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.202",
|
|
4
4
|
"description": "CiCy - AI-powered operating system browser",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": {
|
|
@@ -140,11 +140,11 @@
|
|
|
140
140
|
"//optionalDependencies": "(2026-06 回调): mac/linux 改回 native cicy-code(:8008,colima 在 16G mac 上压垮内存)→ 重新内置 cicy-code-<plat> / cicy-mihomo-<plat>,localbin.fromBundle 零网络 seed(npm 仅作更新通道,带 npmmirror→npmjs 回退)。这些由 scripts/sync-runtime-deps.cjs 在 tag-push 时同步到最新版。npm 的 os/cpu 字段保证每个平台只装自己那份。Windows 仍走 docker(WSL :8009),它那份 cicy-code-windows 用不到但 bundle 着无害。",
|
|
141
141
|
"optionalDependencies": {
|
|
142
142
|
"electron": "41.0.3",
|
|
143
|
-
"cicy-code-darwin-x64": "2.3.
|
|
144
|
-
"cicy-code-darwin-arm64": "2.3.
|
|
145
|
-
"cicy-code-linux-x64": "2.3.
|
|
146
|
-
"cicy-code-linux-arm64": "2.3.
|
|
147
|
-
"cicy-code-windows-x64": "2.3.
|
|
143
|
+
"cicy-code-darwin-x64": "2.3.51",
|
|
144
|
+
"cicy-code-darwin-arm64": "2.3.51",
|
|
145
|
+
"cicy-code-linux-x64": "2.3.51",
|
|
146
|
+
"cicy-code-linux-arm64": "2.3.51",
|
|
147
|
+
"cicy-code-windows-x64": "2.3.51",
|
|
148
148
|
"cicy-mihomo-darwin-x64": "1.10.4",
|
|
149
149
|
"cicy-mihomo-darwin-arm64": "1.10.4",
|
|
150
150
|
"cicy-mihomo-linux-x64": "1.10.4",
|
package/src/app-updater.js
CHANGED
|
@@ -1,131 +1,201 @@
|
|
|
1
|
-
// CiCy Desktop
|
|
1
|
+
// CiCy Desktop 自定义自更新(替掉 electron-updater 的全自动 quitAndInstall —— 它对
|
|
2
|
+
// 未签名 mac / loose 源码版 / 多窗口 tab-shell 都不适配)。
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
4
|
+
// 产品流程:启动后比版本 → 有新版 broadcast「available」→ 渲染层顶部 banner「发现新版
|
|
5
|
+
// [下载]」→ 用户点下载 → 按平台从对应源下安装包(带进度条)→ broadcast「downloading」
|
|
6
|
+
// → 下完「ready」→ 用户点安装 → 拉起原生安装器并退出 app。
|
|
7
|
+
//
|
|
8
|
+
// 源(分网络 + 分平台):
|
|
9
|
+
// 版本清单 CN → OSS releases/latest-version.txt;非 CN → GitHub releases/latest
|
|
10
|
+
// Windows → OSS cicy-desktop-<ver>.exe (GitHub 不发 win 包)
|
|
11
|
+
// macOS → GitHub cicy-desktop-<ver>-<arch>.pkg (CN 经 ghproxy 兜底)
|
|
12
|
+
// Linux → GitHub CiCy-Desktop-<ver>.AppImage (CN 经 ghproxy 兜底)
|
|
6
13
|
|
|
7
|
-
const {
|
|
8
|
-
const { app } = require("electron");
|
|
14
|
+
const { app, shell } = require("electron");
|
|
9
15
|
const path = require("path");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const https = require("https");
|
|
10
18
|
const log = require("electron-log");
|
|
11
|
-
const {
|
|
19
|
+
const { OSS_RELEASES_BASE, MIRRORS, mirrorUrl } = require("./sidecar/mirrors");
|
|
12
20
|
|
|
13
|
-
const
|
|
21
|
+
const REPO = "cicy-ai/cicy-desktop";
|
|
22
|
+
const GH_DL = (ver) => `https://github.com/${REPO}/releases/download/v${ver}`;
|
|
14
23
|
|
|
15
|
-
//
|
|
24
|
+
// ── 网络判定(缓存)──────────────────────────────────────────────────────────
|
|
16
25
|
let _network = null;
|
|
17
26
|
async function detectNetwork() {
|
|
18
27
|
if (_network) return _network;
|
|
19
|
-
try { _network = await require("./sidecar/net-detect")(); }
|
|
20
|
-
catch { _network = "unknown"; }
|
|
28
|
+
try { _network = await require("./sidecar/net-detect")(); } catch { _network = "unknown"; }
|
|
21
29
|
return _network;
|
|
22
30
|
}
|
|
23
31
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} else {
|
|
32
|
-
log.info(`[app-updater] CN — feed: ${feedUrl}`);
|
|
33
|
-
autoUpdater.setFeedURL({ provider: "generic", url: feedUrl });
|
|
32
|
+
// ── 版本比较:>0 a 新于 b ─────────────────────────────────────────────────────
|
|
33
|
+
function cmpVer(a, b) {
|
|
34
|
+
const pa = String(a || "0").replace(/^v/, "").split(".");
|
|
35
|
+
const pb = String(b || "0").replace(/^v/, "").split(".");
|
|
36
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
37
|
+
const d = (parseInt(pa[i], 10) || 0) - (parseInt(pb[i], 10) || 0);
|
|
38
|
+
if (d) return d > 0 ? 1 : -1;
|
|
34
39
|
}
|
|
40
|
+
return 0;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
// 小工具:GET 文本(跟随重定向)
|
|
44
|
+
function getText(url, redirects = 0) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
if (redirects > 5) return reject(new Error("too many redirects"));
|
|
47
|
+
const req = https.get(url, { headers: { "User-Agent": "cicy-desktop" }, timeout: 10000 }, (res) => {
|
|
48
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
49
|
+
res.resume(); return getText(res.headers.location, redirects + 1).then(resolve, reject);
|
|
50
|
+
}
|
|
51
|
+
if (res.statusCode !== 200) { res.resume(); return reject(new Error(`HTTP ${res.statusCode}`)); }
|
|
52
|
+
let s = ""; res.setEncoding("utf8");
|
|
53
|
+
res.on("data", (d) => (s += d));
|
|
54
|
+
res.on("end", () => resolve(s));
|
|
55
|
+
});
|
|
56
|
+
req.on("error", reject);
|
|
57
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")); });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
40
60
|
|
|
41
|
-
|
|
42
|
-
|
|
61
|
+
// CN 走 ghproxy 兜底(OSS 没有 mac/linux 包)。
|
|
62
|
+
function mirrored(githubUrl) {
|
|
63
|
+
const m = MIRRORS[0];
|
|
64
|
+
return m ? mirrorUrl(githubUrl, m) : githubUrl;
|
|
65
|
+
}
|
|
43
66
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
67
|
+
// ── 最新版本号 ────────────────────────────────────────────────────────────────
|
|
68
|
+
async function fetchLatestVersion(network) {
|
|
69
|
+
if (network === "cn") {
|
|
70
|
+
// CN:OSS 版本清单(纯文本 "2.1.200")。
|
|
71
|
+
return (await getText(`${OSS_RELEASES_BASE}/latest-version.txt`)).trim().replace(/^v/, "");
|
|
72
|
+
}
|
|
73
|
+
// 非 CN:GitHub releases/latest 的 tag_name。
|
|
74
|
+
const j = JSON.parse(await getText(`https://api.github.com/repos/${REPO}/releases/latest`));
|
|
75
|
+
return String(j.tag_name || "").trim().replace(/^v/, "");
|
|
47
76
|
}
|
|
48
77
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
autoUpdater.updateConfigPath = devCfg;
|
|
58
|
-
autoUpdater.forceDevUpdateConfig = true;
|
|
59
|
-
log.info("[app-updater] dev mode — using dev-app-update.yml");
|
|
78
|
+
// ── 安装包 URL(分平台 + 分网络)+ 本地文件名 ────────────────────────────────
|
|
79
|
+
function assetFor(version, network) {
|
|
80
|
+
const plat = process.platform;
|
|
81
|
+
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
82
|
+
if (plat === "win32") {
|
|
83
|
+
// win 包只在 OSS(GitHub 不发);CN/非 CN 都用 OSS。
|
|
84
|
+
return { url: `${OSS_RELEASES_BASE}/cicy-desktop-${version}.exe`, file: `cicy-desktop-${version}.exe` };
|
|
60
85
|
}
|
|
86
|
+
if (plat === "darwin") {
|
|
87
|
+
const gh = `${GH_DL(version)}/cicy-desktop-${version}-${arch}.pkg`;
|
|
88
|
+
return { url: network === "cn" ? mirrored(gh) : gh, file: `cicy-desktop-${version}-${arch}.pkg` };
|
|
89
|
+
}
|
|
90
|
+
// linux
|
|
91
|
+
const gh = `${GH_DL(version)}/CiCy-Desktop-${version}.AppImage`;
|
|
92
|
+
return { url: network === "cn" ? mirrored(gh) : gh, file: `CiCy-Desktop-${version}.AppImage` };
|
|
93
|
+
}
|
|
61
94
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
log.info("[app-updater] update downloaded, applying now");
|
|
72
|
-
setTimeout(() => {
|
|
73
|
-
try { autoUpdater.quitAndInstall(true, true); } catch (e) {
|
|
74
|
-
log.warn("[app-updater] quitAndInstall failed:", e.message);
|
|
95
|
+
// ── 下载(带进度,跟随重定向)──────────────────────────────────────────────────
|
|
96
|
+
function download(url, dest, onProgress, redirects = 0) {
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
if (redirects > 6) return reject(new Error("too many redirects"));
|
|
99
|
+
const f = fs.createWriteStream(dest);
|
|
100
|
+
const req = https.get(url, { headers: { "User-Agent": "cicy-desktop" }, timeout: 30000 }, (res) => {
|
|
101
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
102
|
+
f.close(); try { fs.unlinkSync(dest); } catch {}
|
|
103
|
+
return download(res.headers.location, dest, onProgress, redirects + 1).then(resolve, reject);
|
|
75
104
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
105
|
+
if (res.statusCode !== 200) { f.close(); try { fs.unlinkSync(dest); } catch {} return reject(new Error(`HTTP ${res.statusCode}`)); }
|
|
106
|
+
const total = parseInt(res.headers["content-length"] || "0", 10) || 0;
|
|
107
|
+
let transferred = 0, lastPct = -1;
|
|
108
|
+
res.on("data", (chunk) => {
|
|
109
|
+
transferred += chunk.length;
|
|
110
|
+
const percent = total ? Math.floor((transferred / total) * 100) : 0;
|
|
111
|
+
if (percent !== lastPct) { lastPct = percent; try { onProgress && onProgress({ percent, transferred, total }); } catch {} }
|
|
112
|
+
});
|
|
113
|
+
res.pipe(f);
|
|
114
|
+
f.on("finish", () => f.close(() => resolve()));
|
|
115
|
+
f.on("error", (e) => { try { fs.unlinkSync(dest); } catch {} reject(e); });
|
|
116
|
+
});
|
|
117
|
+
req.on("error", (e) => { try { fs.unlinkSync(dest); } catch {} reject(e); });
|
|
118
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")); });
|
|
81
119
|
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── 状态机 + 广播 ─────────────────────────────────────────────────────────────
|
|
123
|
+
let _win = null;
|
|
124
|
+
let _state = { status: "idle", version: null, current: null, progress: null, filePath: null, error: null };
|
|
125
|
+
let _downloading = false;
|
|
82
126
|
|
|
83
|
-
|
|
84
|
-
|
|
127
|
+
function broadcast(patch) {
|
|
128
|
+
Object.assign(_state, patch);
|
|
129
|
+
try { if (_win && !_win.isDestroyed()) _win.webContents.send("app:update-state", _state); } catch {}
|
|
85
130
|
}
|
|
131
|
+
function getState() { return _state; }
|
|
86
132
|
|
|
133
|
+
function init(mainWin) {
|
|
134
|
+
_win = mainWin;
|
|
135
|
+
_state.current = app.getVersion();
|
|
136
|
+
setTimeout(() => check().catch(() => {}), 15_000); // 启动后探一次
|
|
137
|
+
setInterval(() => check().catch(() => {}), 30 * 60 * 1000); // 每 30 分钟
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 比版本;有新版 → available,否则 up-to-date。不自动下载(等用户点)。
|
|
87
141
|
async function check() {
|
|
88
142
|
try {
|
|
89
|
-
|
|
90
|
-
await
|
|
143
|
+
broadcast({ status: "checking", error: null });
|
|
144
|
+
const net = await detectNetwork();
|
|
145
|
+
const latest = await fetchLatestVersion(net);
|
|
146
|
+
const current = app.getVersion();
|
|
147
|
+
if (latest && cmpVer(latest, current) > 0) {
|
|
148
|
+
broadcast({ status: "available", version: latest, current, progress: null, filePath: null });
|
|
149
|
+
} else {
|
|
150
|
+
broadcast({ status: "up-to-date", version: latest || current, current });
|
|
151
|
+
}
|
|
152
|
+
return _state;
|
|
91
153
|
} catch (e) {
|
|
92
154
|
log.warn("[app-updater] check failed:", e.message);
|
|
155
|
+
broadcast({ status: "error", error: e.message });
|
|
156
|
+
return _state;
|
|
93
157
|
}
|
|
94
158
|
}
|
|
95
159
|
|
|
96
|
-
|
|
97
|
-
|
|
160
|
+
// 用户点「下载」:按平台 + 网络下安装包到临时目录,带进度。
|
|
161
|
+
async function downloadUpdate() {
|
|
162
|
+
if (_downloading) return _state;
|
|
163
|
+
const version = _state.version;
|
|
164
|
+
if (!version) { broadcast({ status: "error", error: "no version" }); return _state; }
|
|
165
|
+
_downloading = true;
|
|
166
|
+
try {
|
|
167
|
+
const net = await detectNetwork();
|
|
168
|
+
const { url, file } = assetFor(version, net);
|
|
169
|
+
// 下到 ~/Downloads(用户能直接找到安装包);目录不存在则建。
|
|
170
|
+
const dir = app.getPath("downloads");
|
|
171
|
+
try { fs.mkdirSync(dir, { recursive: true }); } catch {}
|
|
172
|
+
const dest = path.join(dir, file);
|
|
173
|
+
log.info(`[app-updater] downloading ${url} → ${dest}`);
|
|
174
|
+
broadcast({ status: "downloading", progress: { percent: 0, transferred: 0, total: 0 }, filePath: null });
|
|
175
|
+
await download(url, dest, (p) => broadcast({ status: "downloading", progress: p }));
|
|
176
|
+
broadcast({ status: "ready", filePath: dest, progress: { percent: 100 } });
|
|
177
|
+
return _state;
|
|
178
|
+
} catch (e) {
|
|
179
|
+
log.warn("[app-updater] download failed:", e.message);
|
|
180
|
+
broadcast({ status: "error", error: e.message });
|
|
181
|
+
return _state;
|
|
182
|
+
} finally {
|
|
183
|
+
_downloading = false;
|
|
184
|
+
}
|
|
98
185
|
}
|
|
99
186
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (done) return;
|
|
113
|
-
done = true;
|
|
114
|
-
autoUpdater.removeListener("update-available", onAvailable);
|
|
115
|
-
autoUpdater.removeListener("update-not-available", onUpToDate);
|
|
116
|
-
autoUpdater.removeListener("error", onError);
|
|
117
|
-
resolve(payload);
|
|
118
|
-
};
|
|
119
|
-
const onAvailable = (info) => finish({ status: "available", info });
|
|
120
|
-
const onUpToDate = (info) => finish({ status: "up-to-date", info });
|
|
121
|
-
const onError = (err) => finish({ status: "error", error: err.message });
|
|
122
|
-
autoUpdater.on("update-available", onAvailable);
|
|
123
|
-
autoUpdater.on("update-not-available", onUpToDate);
|
|
124
|
-
autoUpdater.on("error", onError);
|
|
125
|
-
setTimeout(() => finish({ status: "error", error: "timeout" }), timeoutMs);
|
|
126
|
-
try { await applyFeedUrl(); await autoUpdater.checkForUpdates(); }
|
|
127
|
-
catch (e) { finish({ status: "error", error: e.message }); }
|
|
128
|
-
});
|
|
187
|
+
// 用户点「安装」:拉起原生安装器(win NSIS / mac pkg)并退出 app;linux AppImage 在
|
|
188
|
+
// 文件管理器里定位(AppImage 非安装器,用户自行替换运行)。
|
|
189
|
+
function installNow() {
|
|
190
|
+
const f = _state.filePath;
|
|
191
|
+
if (!f) return;
|
|
192
|
+
try {
|
|
193
|
+
if (process.platform === "linux") { shell.showItemInFolder(f); return; }
|
|
194
|
+
shell.openPath(f).then((err) => {
|
|
195
|
+
if (err) log.warn("[app-updater] openPath failed:", err);
|
|
196
|
+
else setTimeout(() => { try { app.quit(); } catch {} }, 800);
|
|
197
|
+
});
|
|
198
|
+
} catch (e) { log.warn("[app-updater] install failed:", e.message); }
|
|
129
199
|
}
|
|
130
200
|
|
|
131
|
-
module.exports = { init, check,
|
|
201
|
+
module.exports = { init, check, getState, downloadUpdate, installNow };
|
|
@@ -224,6 +224,7 @@ contextBridge.exposeInMainWorld("cicy", {
|
|
|
224
224
|
getVersion: () => logInvoke("app:get-version"),
|
|
225
225
|
updateState: () => logInvoke("app:update-state"),
|
|
226
226
|
checkUpdate: () => logInvoke("app:check-update"),
|
|
227
|
+
downloadUpdate:() => logInvoke("app:download-update"),
|
|
227
228
|
installUpdate: () => logInvoke("app:install-update"),
|
|
228
229
|
onUpdateState: (cb) => {
|
|
229
230
|
const handler = (_e, state) => { try { cb(state); } catch {} };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@font-face{font-family:Noto Sans Mono CJK SC;src:url(./NotoMonoSC-Regular-CYHoGOBB.woff2) format("woff2");font-weight:400;font-style:normal;font-display:swap}:root{--mono: "Noto Sans Mono CJK SC", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace}: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}.modal-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:18px}.modal-actions .btn-primary{width:auto;margin-top:0;padding:0 18px}.modal-actions .btn-ghost,.modal-actions .btn-primary{flex:0 0 auto;white-space:nowrap}.login-divider{display:flex;align-items:center;gap:10px;margin:4px 0;color:#6b7280;font-size:11.5px}.login-divider:before,.login-divider:after{content:"";flex:1;height:1px;background:#ffffff14}.login-email-input{-webkit-app-region:no-drag;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;box-sizing:border-box;background:#ffffff08;color:#e5e7eb;border:1px solid rgba(255,255,255,.1);border-radius:8px;padding:8px 12px;font-size:13px;transition:border-color .12s ease,background .12s ease}.login-email-input::placeholder{color:#6b7280}.login-email-input:focus{outline:none;border-color:#6366f18c;background:#ffffff0d}.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}.topbar .user-chip{margin-left:auto}.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__menu-version{margin-top:4px;padding:8px 11px 4px;font-size:11px;color:#6b7280;text-align:center;font-variant-numeric:tabular-nums;-webkit-user-select:text;user-select:text;border-top:1px solid rgba(255,255,255,.06)}.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}*::-webkit-scrollbar{width:8px;height:8px}*::-webkit-scrollbar-track{background:transparent}*::-webkit-scrollbar-thumb{background:#7d87964d;border-radius:999px;border:2px solid transparent;background-clip:padding-box}*::-webkit-scrollbar-thumb:hover{background:#7d87968c;background-clip:padding-box}*::-webkit-scrollbar-corner{background:transparent}*{scrollbar-width:thin;scrollbar-color:rgba(125,135,150,.35) transparent}@keyframes cicy-shimmer{0%{background-position:-240px 0}to{background-position:calc(240px + 100%) 0}}.skel{background-color:#ffffff0d;background-image:linear-gradient(90deg,#fff0 0,#ffffff14,#fff0);background-size:240px 100%;background-repeat:no-repeat;animation:cicy-shimmer 1.25s ease-in-out infinite;border-radius:8px}.skel--avatar{width:30px;height:30px;border-radius:50%;flex:0 0 auto}.skel--name{width:72px;height:12px;border-radius:6px}.skel--line{height:12px;border-radius:6px}.skel--cta{height:34px;border-radius:9px;margin-top:16px}.user-chip__trigger--skel{display:inline-flex;align-items:center;gap:10px;cursor:default}.bcard--skeleton{pointer-events:none}.bcard--skeleton__head{display:flex;align-items:center;gap:12px}.bcard--skeleton__lines{flex:1;min-width:0}.update-banner{position:relative;flex:0 0 auto;padding:9px 14px 9px 16px;font-size:13px;border-bottom:1px solid rgba(255,255,255,.06);background:#3b82f61a}.update-banner--ready{background:#22c55e1f}.update-banner--error{background:#ef44441f}.update-banner--downloading{background:#ffffff0d}.update-banner__row{display:flex;align-items:center;gap:10px}.update-banner__icon{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:50%;flex:0 0 auto;font-size:12px;font-weight:700;color:#fff;background:linear-gradient(135deg,#3b82f6,#8b5cf6)}.update-banner--ready .update-banner__icon{background:#22c55e}.update-banner--error .update-banner__icon{background:#ef4444}.update-banner--downloading .update-banner__icon{background:transparent;color:#7aa2f7}.update-banner__text{flex:1;min-width:0;color:#e6edf3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.update-banner__actions{display:flex;align-items:center;gap:6px;flex:0 0 auto}.update-banner__btn{border:none;border-radius:7px;padding:5px 14px;font-size:12px;font-weight:600;cursor:pointer;color:#fff;background:#ffffff1f}.update-banner__btn.is-accent{background:linear-gradient(135deg,#3b82f6,#8b5cf6)}.update-banner__btn:hover{filter:brightness(1.12)}.update-banner__x{border:none;background:transparent;color:#9aa0a6;font-size:18px;line-height:1;cursor:pointer;padding:0 4px;border-radius:6px}.update-banner__x:hover{color:#e6edf3;background:#ffffff14}.update-banner__bar{position:absolute;left:0;right:0;bottom:0;height:3px;background:#ffffff14;overflow:hidden}.update-banner__bar>span{display:block;height:100%;background:linear-gradient(90deg,#3b82f6,#8b5cf6);transition:width .2s ease}.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:var(--mono);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:var(--mono);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:var(--mono);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-sep{height:1px;margin:5px 10px;background:#ffffff14}.bcard__menu-item--switch{display:flex;align-items:center;justify-content:space-between;gap:10px}.bcard__switch{flex:none;width:30px;height:17px;border-radius:999px;background:#ffffff29;position:relative;transition:background .15s}.bcard__switch:after{content:"";position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;background:#fff;transition:transform .15s}.bcard__switch.is-on{background:var(--accent-text, #4f8cff)}.bcard__switch.is-on:after{transform:translate(13px)}.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:var(--mono);font-size:11.5px;line-height:1.7}.drawer__log--scroll{max-height:168px;scrollbar-width:thin;scrollbar-color:rgba(125,135,150,.4) transparent}.drawer__log--scroll::-webkit-scrollbar{width:7px}.drawer__log--scroll::-webkit-scrollbar-track{background:transparent;margin:4px 0}.drawer__log--scroll::-webkit-scrollbar-thumb{background:#7d879652;border-radius:6px;border:1.5px solid transparent;background-clip:padding-box}.drawer__log--scroll::-webkit-scrollbar-thumb:hover{background:#7d87968c;background-clip:padding-box}.drawer__now{display:flex;align-items:center;gap:9px;margin:12px 14px 2px;padding:9px 12px;background:#5b8df714;border:1px solid rgba(91,141,247,.2);border-radius:10px;font-size:12.5px;color:var(--text, #e6eaf0)}.drawer__now span{flex:1;min-width:0}.drawer__dlbars{display:flex;flex-direction:column;gap:10px;margin:12px 14px 2px}.dlbar{padding:10px 12px;border-radius:10px;background:#0a0c108c;border:1px solid rgba(125,135,150,.14)}.dlbar__head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:7px}.dlbar__name{font-size:12.5px;font-weight:600;color:var(--text, #e6eaf0)}.dlbar__pct{font-size:11px;color:var(--text-dim, #9da7b3);font-variant-numeric:tabular-nums}.dlbar__track{height:6px;border-radius:4px;background:#7d879629;overflow:hidden}.dlbar__fill{height:100%;border-radius:4px;background:linear-gradient(90deg,#5b8df7,#2496ed);transition:width .2s ease}.dlbar__fill.is-done{background:#4ade80}.dlbar__url{margin-top:6px;font-size:10.5px;color:#6b7686;font-family:var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dlbar__urlk{display:inline-block;min-width:16px;text-align:center;margin-right:5px;padding:0 4px;border-radius:4px;font-family:inherit;font-size:9.5px;color:#8fb0f5;background:#5b8df729}.drawer__headbtns{display:flex;align-items:center;gap:2px}.drawer-min{position:fixed;right:20px;bottom:20px;z-index:2001;display:inline-flex;align-items:center;gap:9px;padding:10px 15px;border-radius:999px;cursor:pointer;background:#16181d;border:1px solid rgba(125,135,150,.22);box-shadow:0 10px 30px #00000080;color:var(--text, #e6eaf0);font-size:12.5px;font-weight:550;animation:drawer-min-in .18s cubic-bezier(.2,.8,.2,1)}.drawer-min:hover{border-color:#7d879666;background:#1b1e24}@keyframes drawer-min-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.drawer-min__spark{display:inline-flex;width:16px;height:16px;align-items:center;justify-content:center;font-weight:700}.drawer-min--done .drawer-min__spark{color:#4ade80}.drawer-min--error .drawer-min__spark{color:#f87171}.drawer-min__label{font-variant-numeric:tabular-nums}.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__foot-status.is-reboot,.drawer__spark--reboot,.drawer-min--reboot .drawer-min__spark{color:#f5b342}.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:10px;border-top:1px solid rgba(125,135,150,.15);border-bottom:1px solid rgba(125,135,150,.15);padding-top:14px;padding-bottom:14px;overscroll-behavior:contain}.terms-gate__body{scrollbar-width:thin;scrollbar-color:rgba(125,135,150,.35) transparent}.terms-gate__body::-webkit-scrollbar{width:8px}.terms-gate__body::-webkit-scrollbar-track{background:transparent}.terms-gate__body::-webkit-scrollbar-thumb{background:#7d87964d;border-radius:8px;border:2px solid transparent;background-clip:padding-box}.terms-gate__body::-webkit-scrollbar-thumb:hover{background:#7d87968c;background-clip:padding-box;border:2px solid transparent}.terms-gate--review{padding:24px}.terms-gate--review .glow{display:none}.terms-gate--review .terms-gate__panel{width:min(640px,92vw);max-height:min(82vh,760px);background:#12161efa;box-shadow:0 28px 80px #0009;animation:terms-pop .16s ease}@keyframes terms-pop{0%{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:none}}.terms-md{font-size:13px;line-height:1.65;color:var(--text-dim, #c2cbd6);word-break:break-word}.terms-md>:first-child{margin-top:0}.terms-md h1{font-size:17px;font-weight:700;color:var(--text, #e6edf3);margin:0 0 6px}.terms-md h2{font-size:15px;font-weight:700;color:var(--text, #e6edf3);margin:22px 0 8px;padding-top:14px;border-top:1px solid rgba(125,135,150,.12)}.terms-md h3{font-size:13.5px;font-weight:600;color:var(--text, #e6edf3);margin:16px 0 6px}.terms-md p{margin:8px 0}.terms-md strong{color:var(--text, #e6edf3);font-weight:600}.terms-md code{font-family:var(--mono);font-size:12px;background:#7d879629;padding:1px 5px;border-radius:4px;color:#d7dee8}.terms-md ul,.terms-md ol{margin:8px 0;padding-left:22px}.terms-md li{margin:4px 0}.terms-md hr{border:none;border-top:1px solid rgba(125,135,150,.14);margin:18px 0}.terms-md blockquote{margin:10px 0;padding:8px 14px;border-left:3px solid rgba(91,141,247,.55);background:#5b8df712;border-radius:0 8px 8px 0;color:var(--text-dim, #b9c2cf)}.terms-md table{width:100%;border-collapse:collapse;margin:10px 0;font-size:12px;display:block;overflow-x:auto}.terms-md th,.terms-md td{border:1px solid rgba(125,135,150,.18);padding:7px 10px;text-align:left;vertical-align:top}.terms-md th{background:#7d87961a;color:var(--text, #e6edf3);font-weight:600;white-space:nowrap}.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}
|